diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json index 55bd3744..ff63a4f7 100644 --- a/public/_locales/en/messages.json +++ b/public/_locales/en/messages.json @@ -516,7 +516,7 @@ "message": "Intermission" }, "category_intermission_description": { - "message": "An interval without actual content. Could be a pause, static frame, or repeating animation in the video. Should not be used for pauses or transitions with actual content" + "message": "An interval without actual content. Could be a pause, static frame, or repeating animation in the video. Should not be used for pauses or transitions with actual content." }, "category_music_offtopic": { "message": "Music: Non-Music Section" diff --git a/src/background.ts b/src/background.ts index 28a1051e..ec398d59 100644 --- a/src/background.ts +++ b/src/background.ts @@ -40,21 +40,6 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) { }); }); - return true; - case "addSponsorTime": - addSponsorTime(request.time, request.videoID, callback); - - //this allows the callback to be called later - return true; - - case "getSponsorTimes": - getSponsorTimes(request.videoID, function(sponsorTimes) { - callback({ - sponsorTimes - }); - }); - - //this allows the callback to be called later return true; case "submitVote": submitVote(request.type, request.UUID, request.category).then(callback); @@ -127,38 +112,6 @@ function unregisterFirefoxContentScript(id: string) { delete contentScriptRegistrations[id]; } -//gets the sponsor times from memory -function getSponsorTimes(videoID, callback) { - let sponsorTimes = []; - let sponsorTimesStorage = Config.config.sponsorTimes.get(videoID); - - if (sponsorTimesStorage != undefined && sponsorTimesStorage.length > 0) { - sponsorTimes = sponsorTimesStorage; - } - - callback(sponsorTimes); -} - -function addSponsorTime(time, videoID, callback) { - getSponsorTimes(videoID, function(sponsorTimes) { - //add to sponsorTimes - if (sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].length < 2) { - //it is an end time - sponsorTimes[sponsorTimes.length - 1][1] = time; - } else { - //it is a start time - let sponsorTimesIndex = sponsorTimes.length; - sponsorTimes[sponsorTimesIndex] = []; - - sponsorTimes[sponsorTimesIndex][0] = time; - } - - //save this info - Config.config.sponsorTimes.set(videoID, sponsorTimes); - callback(); - }); -} - async function submitVote(type: number, UUID: string, category: string) { let userID = Config.config.userID; diff --git a/src/components/SponsorTimeEditComponent.tsx b/src/components/SponsorTimeEditComponent.tsx index 52c1da0c..efcf595a 100644 --- a/src/components/SponsorTimeEditComponent.tsx +++ b/src/components/SponsorTimeEditComponent.tsx @@ -322,7 +322,7 @@ class SponsorTimeEditComponent extends React.Component, + // sponsorTimes: SBMap, + segmentTimes: SBMap, whitelistedChannels: string[], forceChannelCheck: boolean, startSponsorKeybind: string, @@ -84,35 +85,39 @@ class SBMap extends Map { } } - set(key, value) { - const result = super.set(key, value); + get(key): U { + return super.get(key); + } + rawSet(key, value) { + return super.set(key, value); + } + + update() { // Store updated SBMap locally chrome.storage.sync.set({ [this.id]: encodeStoredItem(this) }); + } + set(key: T, value: U) { + const result = super.set(key, value); + + this.update(); return result; } delete(key) { const result = super.delete(key); - // Store updated SBMap locally - chrome.storage.sync.set({ - [this.id]: encodeStoredItem(this) - }); - + this.update(); return result; } clear() { const result = super.clear(); - chrome.storage.sync.set({ - [this.id]: encodeStoredItem(this) - }); - + this.update(); return result; } } @@ -124,7 +129,7 @@ var Config: SBObject = { configListeners: [], defaults: { userID: null, - sponsorTimes: new SBMap("sponsorTimes"), + segmentTimes: new SBMap("segmentTimes"), whitelistedChannels: [], forceChannelCheck: false, startSponsorKeybind: ";", @@ -244,24 +249,13 @@ function encodeStoredItem(data: T): T | Array { * * @param {*} data */ -function decodeStoredItem(id: string, data: T): T | SBMap { +function decodeStoredItem(id: string, data: T): T | SBMap { if (!Config.defaults[id]) return data; if (Config.defaults[id] instanceof SBMap) { try { - let jsonData: any = data; - - // Check if data is stored in the old format for SBMap (a JSON string) - if (typeof data === "string") { - try { - jsonData = JSON.parse(data); - } catch(e) { - // Continue normally (out of this if statement) - } - } - - if (!Array.isArray(jsonData)) return data; - return new SBMap(id, jsonData); + if (!Array.isArray(data)) return data; + return new SBMap(id, data); } catch(e) { console.error("Failed to parse SBMap: " + id); } @@ -319,9 +313,9 @@ function fetchConfig() { }); } -async function migrateOldFormats() { - if (Config.config["disableAutoSkip"]) { - for (const selection of Config.config.categorySelections) { +function migrateOldFormats(config: SBConfig) { + if (config["disableAutoSkip"]) { + for (const selection of config.categorySelections) { if (selection.name === "sponsor") { selection.option = CategorySkipOption.ManualSkip; @@ -331,65 +325,108 @@ async function migrateOldFormats() { } // Auto vote removal - if (Config.config["autoUpvote"]) { + if (config["autoUpvote"]) { chrome.storage.sync.remove("autoUpvote"); } // mobileUpdateShowCount removal - if (Config.config["mobileUpdateShowCount"] !== undefined) { + if (config["mobileUpdateShowCount"] !== undefined) { chrome.storage.sync.remove("mobileUpdateShowCount"); } // categoryUpdateShowCount removal - if (Config.config["categoryUpdateShowCount"] !== undefined) { + if (config["categoryUpdateShowCount"] !== undefined) { chrome.storage.sync.remove("categoryUpdateShowCount"); } // Channel URLS - if (Config.config.whitelistedChannels.length > 0 && - (Config.config.whitelistedChannels[0] == null || Config.config.whitelistedChannels[0].includes("/"))) { - let newChannelList: string[] = []; - for (const item of Config.config.whitelistedChannels) { - if (item != null) { - if (item.includes("/channel/")) { - newChannelList.push(item.split("/")[2]); - } else if (item.includes("/user/") && utils.isContentScript()) { - // Replace channel URL with channelID - let response = await utils.asyncRequestToCustomServer("GET", "https://sponsor.ajay.app/invidious/api/v1/channels/" + item.split("/")[2] + "?fields=authorId"); - - if (response.ok) { - newChannelList.push((JSON.parse(response.responseText)).authorId); - } else { - // Add it at the beginning so it gets converted later + if (config.whitelistedChannels.length > 0 && + (config.whitelistedChannels[0] == null || config.whitelistedChannels[0].includes("/"))) { + const channelURLFixer = async() => { + let newChannelList: string[] = []; + for (const item of config.whitelistedChannels) { + if (item != null) { + if (item.includes("/channel/")) { + newChannelList.push(item.split("/")[2]); + } else if (item.includes("/user/") && utils.isContentScript()) { + + + // Replace channel URL with channelID + let response = await utils.asyncRequestToCustomServer("GET", "https://sponsor.ajay.app/invidious/api/v1/channels/" + item.split("/")[2] + "?fields=authorId"); + + if (response.ok) { + newChannelList.push((JSON.parse(response.responseText)).authorId); + } else { + // Add it at the beginning so it gets converted later + newChannelList.unshift(item); + } + } else if (item.includes("/user/")) { + // Add it at the beginning so it gets converted later (The API can only be called in the content script due to CORS issues) newChannelList.unshift(item); + } else { + newChannelList.push(item); } - } else if (item.includes("/user/")) { - // Add it at the beginning so it gets converted later (The API can only be called in the content script due to CORS issues) - newChannelList.unshift(item); - } else { - newChannelList.push(item); } } + + config.whitelistedChannels = newChannelList; } - Config.config.whitelistedChannels = newChannelList; + channelURLFixer(); } // Check if off-topic category needs to be removed - for (let i = 0; i < Config.config.categorySelections.length; i++) { - if (Config.config.categorySelections[i].name === "offtopic") { - Config.config.categorySelections.splice(i, 1); + for (let i = 0; i < config.categorySelections.length; i++) { + if (config.categorySelections[i].name === "offtopic") { + config.categorySelections.splice(i, 1); // Call set listener - Config.config.categorySelections = Config.config.categorySelections; + config.categorySelections = config.categorySelections; break; } } + + // Migrate old "sponsorTimes" + if (config["sponsorTimes"]) { + let jsonData: any = config["sponsorTimes"]; + + // Check if data is stored in the old format for SBMap (a JSON string) + if (typeof jsonData === "string") { + try { + jsonData = JSON.parse(jsonData); + } catch(e) { + // Continue normally (out of this if statement) + } + } + + // Otherwise junk data + if (Array.isArray(jsonData)) { + let oldMap = new Map(jsonData); + oldMap.forEach((sponsorTimes: number[][], key) => { + let segmentTimes: SponsorTime[] = []; + for (const segment of sponsorTimes) { + segmentTimes.push({ + segment: segment, + category: "sponsor", + UUID: null + }); + } + + config.segmentTimes.rawSet(key, segmentTimes); + }); + + config.segmentTimes.update(); + } + + chrome.storage.sync.remove("sponsorTimes"); + } } async function setupConfig() { await fetchConfig(); addDefaults(); convertJSON(); - Config.config = configProxy(); - migrateOldFormats(); + const config = configProxy(); + migrateOldFormats(config); + + Config.config = config; } // Reset config diff --git a/src/content.ts b/src/content.ts index 86ae8342..f8a6ab8b 100644 --- a/src/content.ts +++ b/src/content.ts @@ -323,13 +323,13 @@ async function videoIDChange(id) { //warn them if they had unsubmitted times if (previousVideoID != null) { //get the sponsor times from storage - let sponsorTimes = Config.config.sponsorTimes.get(previousVideoID); + let sponsorTimes = Config.config.segmentTimes.get(previousVideoID); if (sponsorTimes != undefined && sponsorTimes.length > 0) { //warn them that they have unsubmitted sponsor times - chrome.runtime.sendMessage({ - message: "alertPrevious", - previousVideoID: previousVideoID - }) + chrome.runtime.sendMessage({ + message: "alertPrevious", + previousVideoID: previousVideoID + }); } //set the previous video id to the currentID @@ -347,10 +347,10 @@ async function videoIDChange(id) { //make sure everything is properly added updateVisibilityOfPlayerControlsButton().then(() => { //see if the onvideo control image needs to be changed - let segments = Config.config.sponsorTimes.get(sponsorVideoID); - if (segments != null && segments.length > 0 && segments[segments.length - 1].length >= 2) { + let segments = Config.config.segmentTimes.get(sponsorVideoID); + if (segments != null && segments.length > 0 && segments[segments.length - 1].segment.length >= 2) { changeStartSponsorButton(true, true); - } else if (segments != null && segments.length > 0 && segments[segments.length - 1].length < 2) { + } else if (segments != null && segments.length > 0 && segments[segments.length - 1].segment.length < 2) { changeStartSponsorButton(false, true); } else { changeStartSponsorButton(true, false); @@ -1169,31 +1169,24 @@ function startSponsorClicked() { }); } - // Create raw segment list - let segments: number[][] = []; - for (const sponsorTime of sponsorTimesSubmitting) { - segments.push(sponsorTime.segment); - } - //save this info - Config.config.sponsorTimes.set(sponsorVideoID, segments); + Config.config.segmentTimes.set(sponsorVideoID, sponsorTimesSubmitting); updateSponsorTimesSubmitting(false) } function updateSponsorTimesSubmitting(getFromConfig: boolean = true) { - let segments = Config.config.sponsorTimes.get(sponsorVideoID); + let segmentTimes = Config.config.segmentTimes.get(sponsorVideoID); //see if this data should be saved in the sponsorTimesSubmitting variable - if (getFromConfig && segments != undefined) { + if (getFromConfig && segmentTimes != undefined) { sponsorTimesSubmitting = []; - for (const segment of segments) { + for (const segmentTime of segmentTimes) { sponsorTimesSubmitting.push({ - segment: segment, + segment: segmentTime.segment, UUID: null, - // Default to sponsor - category: "sponsor" + category: segmentTime.category }); } } @@ -1317,7 +1310,7 @@ function clearSponsorTimes() { let currentVideoID = sponsorVideoID; - let sponsorTimes = Config.config.sponsorTimes.get(currentVideoID); + let sponsorTimes = Config.config.segmentTimes.get(currentVideoID); if (sponsorTimes != undefined && sponsorTimes.length > 0) { let confirmMessage = chrome.i18n.getMessage("clearThis") + getSegmentsMessage(sponsorTimes) @@ -1325,7 +1318,7 @@ function clearSponsorTimes() { if(!confirm(confirmMessage)) return; //clear the sponsor times - Config.config.sponsorTimes.delete(currentVideoID); + Config.config.segmentTimes.delete(currentVideoID); //clear sponsor times submitting sponsorTimesSubmitting = []; @@ -1447,14 +1440,14 @@ async function sendSubmitMessage(){ } //update sponsorTimes - Config.config.sponsorTimes.set(sponsorVideoID, utils.getSegmentsFromSponsorTimes(sponsorTimesSubmitting)); + Config.config.segmentTimes.set(sponsorVideoID, sponsorTimesSubmitting); // Check to see if any of the submissions are below the minimum duration set if (Config.config.minDuration > 0) { for (let i = 0; i < sponsorTimesSubmitting.length; i++) { if (sponsorTimesSubmitting[i].segment[1] - sponsorTimesSubmitting[i].segment[0] < Config.config.minDuration) { let confirmShort = chrome.i18n.getMessage("shortCheck") + "\n\n" + - getSegmentsMessage(utils.getSegmentsFromSponsorTimes(sponsorTimesSubmitting)); + getSegmentsMessage(sponsorTimesSubmitting); if(!confirm(confirmShort)) return; } @@ -1484,7 +1477,7 @@ async function sendSubmitMessage(){ submitButton.addEventListener("animationend", animationEndListener); //clear the sponsor times - Config.config.sponsorTimes.delete(sponsorVideoID); + Config.config.segmentTimes.delete(sponsorVideoID); //add submissions to current sponsors list if (sponsorTimes === null) sponsorTimes = []; @@ -1512,12 +1505,12 @@ async function sendSubmitMessage(){ } //get the message that visually displays the video times -function getSegmentsMessage(segments: number[][]): string { +function getSegmentsMessage(sponsorTimes: SponsorTime[]): string { let sponsorTimesMessage = ""; - for (let i = 0; i < segments.length; i++) { - for (let s = 0; s < segments[i].length; s++) { - let timeMessage = utils.getFormattedTime(segments[i][s]); + for (let i = 0; i < sponsorTimes.length; i++) { + for (let s = 0; s < sponsorTimes[i].segment.length; s++) { + let timeMessage = utils.getFormattedTime(sponsorTimes[i].segment[s]); //if this is an end time if (s == 1) { timeMessage = " to " + timeMessage; diff --git a/src/options.ts b/src/options.ts index 814fce69..a0d3ccd9 100644 --- a/src/options.ts +++ b/src/options.ts @@ -439,8 +439,8 @@ function activatePrivateTextChange(element: HTMLElement) { case "*": let jsonData = JSON.parse(JSON.stringify(Config.localConfig)); - // Fix sponsorTimes data as it is destroyed from the JSON stringify - jsonData.sponsorTimes = Config.encodeStoredItem(Config.localConfig.sponsorTimes); + // Fix segmentTimes data as it is destroyed from the JSON stringify + jsonData.segmentTimes = Config.encodeStoredItem(Config.localConfig.segmentTimes); result = JSON.stringify(jsonData); break; @@ -522,8 +522,8 @@ function copyDebugOutputToClipboard() { config: JSON.parse(JSON.stringify(Config.localConfig)) // Deep clone config object }; - // Fix sponsorTimes data as it is destroyed from the JSON stringify - output.config.sponsorTimes = Config.encodeStoredItem(Config.localConfig.sponsorTimes); + // Fix segmentTimes data as it is destroyed from the JSON stringify + output.config.segmentTimes = Config.encodeStoredItem(Config.localConfig.segmentTimes); // Sanitise sensitive user config values delete output.config.userID; diff --git a/src/popup.ts b/src/popup.ts index ee5c9930..239fefbd 100644 --- a/src/popup.ts +++ b/src/popup.ts @@ -119,7 +119,7 @@ async function runThePopup(messageListener?: MessageListener) { let startTimeChosen = false; //the start and end time pairs (2d) - let sponsorTimes = []; + let sponsorTimes: SponsorTime[] = []; //current video ID of this tab let currentVideoID = null; @@ -252,9 +252,9 @@ async function runThePopup(messageListener?: MessageListener) { } //load video times for this video - let sponsorTimesStorage = Config.config.sponsorTimes.get(currentVideoID); + let sponsorTimesStorage = Config.config.segmentTimes.get(currentVideoID); if (sponsorTimesStorage != undefined && sponsorTimesStorage.length > 0) { - if (sponsorTimesStorage[sponsorTimesStorage.length - 1] != undefined && sponsorTimesStorage[sponsorTimesStorage.length - 1].length < 2) { + if (sponsorTimesStorage[sponsorTimesStorage.length - 1] != undefined && sponsorTimesStorage[sponsorTimesStorage.length - 1].segment.length < 2) { startTimeChosen = true; PageElements.sponsorStart.innerHTML = chrome.i18n.getMessage("sponsorEnd"); } @@ -336,13 +336,17 @@ async function runThePopup(messageListener?: MessageListener) { let sponsorTimesIndex = sponsorTimes.length - (startTimeChosen ? 1 : 0); if (sponsorTimes[sponsorTimesIndex] == undefined) { - sponsorTimes[sponsorTimesIndex] = []; + sponsorTimes[sponsorTimesIndex] = { + segment: [], + category: "sponsor", + UUID: null + }; } - sponsorTimes[sponsorTimesIndex][startTimeChosen ? 1 : 0] = response.time; + sponsorTimes[sponsorTimesIndex].segment[startTimeChosen ? 1 : 0] = response.time; let localStartTimeChosen = startTimeChosen; - Config.config.sponsorTimes.set(currentVideoID, sponsorTimes); + Config.config.segmentTimes.set(currentVideoID, sponsorTimes); //send a message to the client script if (localStartTimeChosen) { @@ -528,7 +532,7 @@ async function runThePopup(messageListener?: MessageListener) { } function previewSponsorTime(index) { - let skipTime = sponsorTimes[index][0]; + let skipTime = sponsorTimes[index].segment[0]; if (document.getElementById("startTimeMinutes" + index) != null) { //edit is currently open, use that time @@ -575,28 +579,28 @@ async function runThePopup(messageListener?: MessageListener) { startTimeMinutes.id = "startTimeMinutes" + index; startTimeMinutes.className = "sponsorTime popupElement"; startTimeMinutes.type = "text"; - startTimeMinutes.value = String(getTimeInMinutes(sponsorTimes[index][0])); + startTimeMinutes.value = String(getTimeInMinutes(sponsorTimes[index].segment[0])); startTimeMinutes.style.width = "45px"; let startTimeSeconds = document.createElement("input"); startTimeSeconds.id = "startTimeSeconds" + index; startTimeSeconds.className = "sponsorTime popupElement"; startTimeSeconds.type = "text"; - startTimeSeconds.value = getTimeInFormattedSeconds(sponsorTimes[index][0]); + startTimeSeconds.value = getTimeInFormattedSeconds(sponsorTimes[index].segment[0]); startTimeSeconds.style.width = "60px"; let endTimeMinutes = document.createElement("input"); endTimeMinutes.id = "endTimeMinutes" + index; endTimeMinutes.className = "sponsorTime popupElement"; endTimeMinutes.type = "text"; - endTimeMinutes.value = String(getTimeInMinutes(sponsorTimes[index][1])); + endTimeMinutes.value = String(getTimeInMinutes(sponsorTimes[index].segment[1])); endTimeMinutes.style.width = "45px"; let endTimeSeconds = document.createElement("input"); endTimeSeconds.id = "endTimeSeconds" + index; endTimeSeconds.className = "sponsorTime popupElement"; endTimeSeconds.type = "text"; - endTimeSeconds.value = getTimeInFormattedSeconds(sponsorTimes[index][1]); + endTimeSeconds.value = getTimeInFormattedSeconds(sponsorTimes[index].segment[1]); endTimeSeconds.style.width = "60px"; //the button to set the current time @@ -668,11 +672,11 @@ async function runThePopup(messageListener?: MessageListener) { } function saveSponsorTimeEdit(index, closeEditMode = true) { - sponsorTimes[index][0] = getSponsorTimeEditTimes("startTime", index); - sponsorTimes[index][1] = getSponsorTimeEditTimes("endTime", index); + sponsorTimes[index].segment[0] = getSponsorTimeEditTimes("startTime", index); + sponsorTimes[index].segment[1] = getSponsorTimeEditTimes("endTime", index); //save this - Config.config.sponsorTimes.set(currentVideoID, sponsorTimes); + Config.config.segmentTimes.set(currentVideoID, sponsorTimes); messageHandler.query({ active: true, @@ -692,7 +696,7 @@ async function runThePopup(messageListener?: MessageListener) { //deletes the sponsor time submitted at an index function deleteSponsorTime(index) { //if it is not a complete sponsor time - if (sponsorTimes[index].length < 2) { + if (sponsorTimes[index].segment.length < 2) { messageHandler.query({ active: true, currentWindow: true @@ -710,7 +714,7 @@ async function runThePopup(messageListener?: MessageListener) { sponsorTimes.splice(index, 1); //save this - Config.config.sponsorTimes.set(currentVideoID, sponsorTimes); + Config.config.segmentTimes.set(currentVideoID, sponsorTimes); //if they are all removed if (sponsorTimes.length == 0) { @@ -780,7 +784,7 @@ async function runThePopup(messageListener?: MessageListener) { //hides and shows the submit times button when needed function showSubmitTimesIfNecessary() { //check if an end time has been specified for the latest sponsor time - if (sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].length > 1) { + if (sponsorTimes.length > 0 && sponsorTimes[sponsorTimes.length - 1].segment.length > 1) { //show submit times button document.getElementById("submitTimesContainer").style.display = "unset"; } else {