From 8e366b1450f464f6c1046d0644d8d0a16bf2aeb3 Mon Sep 17 00:00:00 2001 From: Ajay Date: Sat, 23 Dec 2023 13:16:19 -0500 Subject: [PATCH] Move unsubmitted segments to local storage to remove limits Also add a way to export local storage Fixes #1933 --- maze-utils | 2 +- public/_locales | 2 +- public/options/options.html | 25 ++++++++++ src/components/SkipNoticeComponent.tsx | 4 +- src/components/SponsorTimeEditComponent.tsx | 6 +-- src/components/SubmissionNoticeComponent.tsx | 2 +- .../options/UnsubmittedVideoListComponent.tsx | 4 +- .../options/UnsubmittedVideoListItem.tsx | 8 ++-- .../options/UnsubmittedVideosComponent.tsx | 6 +-- src/config.ts | 31 +++++++----- src/content.ts | 20 ++++---- src/options.ts | 48 +++++++++++++------ src/popup.ts | 10 ++-- 13 files changed, 109 insertions(+), 59 deletions(-) diff --git a/maze-utils b/maze-utils index 04699c36..69d771d0 160000 --- a/maze-utils +++ b/maze-utils @@ -1 +1 @@ -Subproject commit 04699c3634f2dc0661db8c87afe5c6882ddd28fc +Subproject commit 69d771d0f3b96951c970a9eacb046b808907bb1c diff --git a/public/_locales b/public/_locales index 0c9f9998..7f2d4e63 160000 --- a/public/_locales +++ b/public/_locales @@ -1 +1 @@ -Subproject commit 0c9f99989481a29522dcd338e821295f15eeb136 +Subproject commit 7f2d4e63dc53facfeed96aae1086c2bc3329b519 diff --git a/public/options/options.html b/public/options/options.html index 545b0fdc..01a3b345 100644 --- a/public/options/options.html +++ b/public/options/options.html @@ -512,6 +512,31 @@ +
+

__MSG_exportOtherData__

+ +
+
+ __MSG_exportOptionsCopy__ +
+
+ __MSG_exportOptionsDownload__ +
+ + +
+ + +
+
__MSG_resetToDefault__ diff --git a/src/components/SkipNoticeComponent.tsx b/src/components/SkipNoticeComponent.tsx index f571035e..b8cf7acd 100644 --- a/src/components/SkipNoticeComponent.tsx +++ b/src/components/SkipNoticeComponent.tsx @@ -563,9 +563,9 @@ class SkipNoticeComponent extends React.Component 0) { - Config.config.unsubmittedSegments[this.props.contentContainer().sponsorVideoID] = sponsorTimes; + Config.local.unsubmittedSegments[this.props.contentContainer().sponsorVideoID] = sponsorTimes; } else { - delete Config.config.unsubmittedSegments[this.props.contentContainer().sponsorVideoID]; + delete Config.local.unsubmittedSegments[this.props.contentContainer().sponsorVideoID]; } Config.forceSyncUpdate("unsubmittedSegments"); diff --git a/src/components/SubmissionNoticeComponent.tsx b/src/components/SubmissionNoticeComponent.tsx index abe32d6d..8f29bcb4 100644 --- a/src/components/SubmissionNoticeComponent.tsx +++ b/src/components/SubmissionNoticeComponent.tsx @@ -237,7 +237,7 @@ class SubmissionNoticeComponent extends React.Component a.segment[0] - b.segment[0]); - Config.config.unsubmittedSegments[this.props.contentContainer().sponsorVideoID] = sponsorTimesSubmitting; + Config.local.unsubmittedSegments[this.props.contentContainer().sponsorVideoID] = sponsorTimesSubmitting; Config.forceSyncUpdate("unsubmittedSegments"); this.forceUpdate(); diff --git a/src/components/options/UnsubmittedVideoListComponent.tsx b/src/components/options/UnsubmittedVideoListComponent.tsx index e63feb99..8bca8cf4 100644 --- a/src/components/options/UnsubmittedVideoListComponent.tsx +++ b/src/components/options/UnsubmittedVideoListComponent.tsx @@ -24,7 +24,7 @@ class UnsubmittedVideoListComponent extends React.Component; return ( @@ -58,7 +58,7 @@ class UnsubmittedVideoListComponent extends React.Component diff --git a/src/components/options/UnsubmittedVideoListItem.tsx b/src/components/options/UnsubmittedVideoListItem.tsx index b2150457..8ff92911 100644 --- a/src/components/options/UnsubmittedVideoListItem.tsx +++ b/src/components/options/UnsubmittedVideoListItem.tsx @@ -23,7 +23,7 @@ class UnsubmittedVideoListItem extends React.Component @@ -69,17 +69,17 @@ class UnsubmittedVideoListItem extends React.Component) => acc + vid.length, 0); + const videoCount = Object.keys(Config.local.unsubmittedSegments).length; + const segmentCount = Object.values(Config.local.unsubmittedSegments).reduce((acc: number, vid: Array) => acc + vid.length, 0); return <>
@@ -48,7 +48,7 @@ class UnsubmittedVideosComponent extends React.Component; - /* Contains unsubmitted segments that the user has created. */ - unsubmittedSegments: Record; defaultCategory: Category; renderSegmentsAsChapters: boolean; whitelistedChannels: string[]; @@ -142,6 +140,9 @@ interface SBStorage { // Used when sync storage disbaled alreadyInstalled: boolean; + + /* Contains unsubmitted segments that the user has created. */ + unsubmittedSegments: Record; } class ConfigClass extends ProtoConfig { @@ -153,6 +154,10 @@ class ConfigClass extends ProtoConfig { skipCount: this.config.skipCount, sponsorTimesContributed: this.config.sponsorTimesContributed }); + + chrome.storage.local.set({ + ...this.localDefaults, + }); } } @@ -161,6 +166,14 @@ function migrateOldSyncFormats(config: SBConfig) { chrome.storage.sync.remove("showZoomToFillError"); } + if (config["unsubmittedSegments"] && Object.keys(config["unsubmittedSegments"]).length > 0) { + chrome.storage.local.set({ + unsubmittedSegments: config["unsubmittedSegments"] + }, () => { + chrome.storage.sync.remove("unsubmittedSegments"); + }); + } + if (!config["chapterCategoryAdded"]) { config["chapterCategoryAdded"] = true; @@ -174,15 +187,6 @@ function migrateOldSyncFormats(config: SBConfig) { } } - if (config["segmentTimes"]) { - const unsubmittedSegments = {}; - for (const item of config["segmentTimes"]) { - unsubmittedSegments[item[0]] = item[1]; - } - - chrome.storage.sync.remove("segmentTimes", () => config.unsubmittedSegments = unsubmittedSegments); - } - if (config["exclusive_accessCategoryAdded"] !== undefined) { chrome.storage.sync.remove("exclusive_accessCategoryAdded"); } @@ -268,7 +272,6 @@ const syncDefaults = { userID: null, isVip: false, permissions: {}, - unsubmittedSegments: {}, defaultCategory: "chooseACategory" as Category, renderSegmentsAsChapters: false, whitelistedChannels: [], @@ -464,7 +467,9 @@ const syncDefaults = { const localDefaults = { downvotedSegments: {}, navigationApiAvailable: null, - alreadyInstalled: false + alreadyInstalled: false, + + unsubmittedSegments: {} }; const Config = new ConfigClass(syncDefaults, localDefaults, migrateOldSyncFormats); diff --git a/src/content.ts b/src/content.ts index bda3b4b7..76201831 100644 --- a/src/content.ts +++ b/src/content.ts @@ -307,7 +307,7 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo } if (addedSegments) { - Config.config.unsubmittedSegments[getVideoID()] = sponsorTimesSubmitting; + Config.local.unsubmittedSegments[getVideoID()] = sponsorTimesSubmitting; Config.forceSyncUpdate("unsubmittedSegments"); updateEditButtonsOnPlayer(); @@ -1925,7 +1925,7 @@ function startOrEndTimingNewSegment() { } // Save the newly created segment - Config.config.unsubmittedSegments[getVideoID()] = sponsorTimesSubmitting; + Config.local.unsubmittedSegments[getVideoID()] = sponsorTimesSubmitting; Config.forceSyncUpdate("unsubmittedSegments"); // Make sure they know if someone has already submitted something it while they were watching @@ -1958,11 +1958,11 @@ function cancelCreatingSegment() { if (isSegmentCreationInProgress()) { if (sponsorTimesSubmitting.length > 1) { // If there's more than one segment: remove last sponsorTimesSubmitting.pop(); - Config.config.unsubmittedSegments[getVideoID()] = sponsorTimesSubmitting; + Config.local.unsubmittedSegments[getVideoID()] = sponsorTimesSubmitting; } else { // Otherwise delete the video entry & close submission menu resetSponsorSubmissionNotice(); sponsorTimesSubmitting = []; - delete Config.config.unsubmittedSegments[getVideoID()]; + delete Config.local.unsubmittedSegments[getVideoID()]; } Config.forceSyncUpdate("unsubmittedSegments"); } @@ -1972,7 +1972,7 @@ function cancelCreatingSegment() { } function updateSponsorTimesSubmitting(getFromConfig = true) { - const segmentTimes = Config.config.unsubmittedSegments[getVideoID()]; + const segmentTimes = Config.local.unsubmittedSegments[getVideoID()]; //see if this data should be saved in the sponsorTimesSubmitting variable if (getFromConfig && segmentTimes != undefined) { @@ -2102,7 +2102,7 @@ function closeInfoMenu() { function clearSponsorTimes() { const currentVideoID = getVideoID(); - const sponsorTimes = Config.config.unsubmittedSegments[currentVideoID]; + const sponsorTimes = Config.local.unsubmittedSegments[currentVideoID]; if (sponsorTimes != undefined && sponsorTimes.length > 0) { const confirmMessage = chrome.i18n.getMessage("clearThis") + getSegmentsMessage(sponsorTimes) @@ -2112,7 +2112,7 @@ function clearSponsorTimes() { resetSponsorSubmissionNotice(); //clear the sponsor times - delete Config.config.unsubmittedSegments[currentVideoID]; + delete Config.local.unsubmittedSegments[currentVideoID]; Config.forceSyncUpdate("unsubmittedSegments"); //clear sponsor times submitting @@ -2276,7 +2276,7 @@ async function sendSubmitMessage() { } //update sponsorTimes - Config.config.unsubmittedSegments[getVideoID()] = sponsorTimesSubmitting; + Config.local.unsubmittedSegments[getVideoID()] = sponsorTimesSubmitting; Config.forceSyncUpdate("unsubmittedSegments"); // Check to see if any of the submissions are below the minimum duration set @@ -2304,7 +2304,7 @@ async function sendSubmitMessage() { stopAnimation(); // Remove segments from storage since they've already been submitted - delete Config.config.unsubmittedSegments[getVideoID()]; + delete Config.local.unsubmittedSegments[getVideoID()]; Config.forceSyncUpdate("unsubmittedSegments"); const newSegments = sponsorTimesSubmitting; @@ -2610,7 +2610,7 @@ function checkForPreloadedSegment() { } if (pushed) { - Config.config.unsubmittedSegments[getVideoID()] = sponsorTimesSubmitting; + Config.local.unsubmittedSegments[getVideoID()] = sponsorTimesSubmitting; Config.forceSyncUpdate("unsubmittedSegments"); } } diff --git a/src/options.ts b/src/options.ts index 6b088538..6e963288 100644 --- a/src/options.ts +++ b/src/options.ts @@ -62,6 +62,10 @@ async function init() { Config.configSyncListeners.push(optionsConfigUpdateListener); } + if (!Config.configLocalListeners.includes(optionsLocalConfigUpdateListener)) { + Config.configLocalListeners.push(optionsLocalConfigUpdateListener); + } + await utils.wait(() => Config.config !== null); if (!Config.config.darkMode) { @@ -253,10 +257,10 @@ async function init() { if (option == "*") { const downloadButton = optionsElements[i].querySelector(".download-button"); - downloadButton.addEventListener("click", downloadConfig); + downloadButton.addEventListener("click", () => downloadConfig(optionsElements[i])); const uploadButton = optionsElements[i].querySelector(".upload-button"); - uploadButton.addEventListener("change", (e) => uploadConfig(e)); + uploadButton.addEventListener("change", (e) => uploadConfig(e, optionsElements[i] as HTMLElement)); } const privateTextChangeOption = optionsElements[i].getAttribute("data-sync"); @@ -406,7 +410,11 @@ function optionsConfigUpdateListener(changes: StorageChangesObject) { for (const chooser of categoryChoosers) { chooser.update(); } - } else if (changes.unsubmittedSegments) { + } +} + +function optionsLocalConfigUpdateListener(changes: StorageChangesObject) { + if (changes.unsubmittedSegments) { for (const chooser of unsubmittedVideos) { chooser.update(); } @@ -540,6 +548,7 @@ function activatePrivateTextChange(element: HTMLElement) { const textBox = element.querySelector(".option-text-box"); const option = element.getAttribute("data-sync"); + const optionType = element.getAttribute("data-sync-type"); // See if anything extra must be done switch (option) { @@ -552,7 +561,11 @@ function activatePrivateTextChange(element: HTMLElement) { // See if anything extra must be done switch (option) { case "*": { - result = JSON.stringify(Config.cachedSyncConfig); + if (optionType === "local") { + result = JSON.stringify(Config.cachedLocalStorage); + } else { + result = JSON.stringify(Config.cachedSyncConfig); + } break; } } @@ -595,6 +608,7 @@ function activatePrivateTextChange(element: HTMLElement) { */ async function setTextOption(option: string, element: HTMLElement, value: string, callbackOnError?: () => void) { const confirmMessage = element.getAttribute("data-confirm-message"); + const optionType = element.getAttribute("data-sync-type"); if (confirmMessage === null || confirm(chrome.i18n.getMessage(confirmMessage))) { @@ -604,10 +618,14 @@ async function setTextOption(option: string, element: HTMLElement, value: string try { const newConfig = JSON.parse(value); for (const key in newConfig) { - Config.config[key] = newConfig[key]; + if (optionType === "local") { + Config.local[key] = newConfig[key]; + } else { + Config.config[key] = newConfig[key]; + } } - if (newConfig.supportInvidious) { + if (optionType !== "local" && newConfig.supportInvidious) { const checkbox = document.querySelector("#support-invidious > div > label > input"); checkbox.checked = true; @@ -630,25 +648,27 @@ async function setTextOption(option: string, element: HTMLElement, value: string } } -function downloadConfig() { +function downloadConfig(element: Element) { + const optionType = element.getAttribute("data-sync-type"); + const file = document.createElement("a"); - const jsonData = JSON.parse(JSON.stringify(Config.cachedSyncConfig)); + const jsonData = JSON.parse(JSON.stringify(optionType === "local" ? Config.cachedLocalStorage : Config.cachedSyncConfig)); const dateTimeString = new Date().toJSON().replace("T", "_").replace(/:/g, ".").replace(/.\d+Z/g, "") file.setAttribute("href", `data:text/json;charset=utf-8,${encodeURIComponent(JSON.stringify(jsonData))}`); - file.setAttribute("download", `SponsorBlockConfig_${dateTimeString}.json`); + file.setAttribute("download", `SponsorBlock${optionType === "local" ? "OtherData" : "Config"}_${dateTimeString}.json`); document.body.append(file); file.click(); file.remove(); } -function uploadConfig(e) { - if (e.target.files.length == 1) { - const file = e.target.files[0]; +function uploadConfig(e: Event, element: HTMLElement) { + const target = e.target as HTMLInputElement; + if (target.files.length == 1) { + const file = target.files[0]; const reader = new FileReader(); - const element = document.querySelector("[data-sync='*']") as HTMLElement; reader.onload = function(ev) { setTextOption("*", element, ev.target.result as string, () => { - e.target.value = null; + target.value = null; }); }; reader.readAsText(file); diff --git a/src/popup.ts b/src/popup.ts index 562fbfcb..e1e5757f 100644 --- a/src/popup.ts +++ b/src/popup.ts @@ -435,7 +435,7 @@ async function runThePopup(messageListener?: MessageListener): Promise { } await utils.wait(() => Config.config !== null, 5000, 10); - sponsorTimes = Config.config.unsubmittedSegments[currentVideoID] ?? []; + sponsorTimes = Config.local.unsubmittedSegments[currentVideoID] ?? []; updateSegmentEditingUI(); messageHandler.sendMessage( @@ -527,7 +527,7 @@ async function runThePopup(messageListener?: MessageListener): Promise { function startSponsorCallback(response: SponsorStartResponse) { // Only update the segments after a segment was created if (!response.creatingSegment) { - sponsorTimes = Config.config.unsubmittedSegments[currentVideoID] || []; + sponsorTimes = Config.local.unsubmittedSegments[currentVideoID] || []; } // Update the UI @@ -769,7 +769,7 @@ async function runThePopup(messageListener?: MessageListener): Promise { } function isCreatingSegment(): boolean { - const segments = Config.config.unsubmittedSegments[currentVideoID]; + const segments = Config.local.unsubmittedSegments[currentVideoID]; if (!segments) return false; const lastSegment = segments[segments.length - 1]; return lastSegment && lastSegment?.segment?.length !== 2; @@ -1094,7 +1094,7 @@ async function runThePopup(messageListener?: MessageListener): Promise { for (const key in changes) { switch(key) { case "unsubmittedSegments": - sponsorTimes = Config.config.unsubmittedSegments[currentVideoID] ?? []; + sponsorTimes = Config.local.unsubmittedSegments[currentVideoID] ?? []; updateSegmentEditingUI(); break; } @@ -1144,7 +1144,7 @@ async function runThePopup(messageListener?: MessageListener): Promise { break; case "videoChanged": currentVideoID = msg.videoID - sponsorTimes = Config.config.unsubmittedSegments[currentVideoID] ?? []; + sponsorTimes = Config.local.unsubmittedSegments[currentVideoID] ?? []; updateSegmentEditingUI(); if (msg.whitelisted) {