diff options
author | Ajay <[email protected]> | 2023-12-23 13:16:19 -0500 |
---|---|---|
committer | Ajay <[email protected]> | 2023-12-23 13:16:19 -0500 |
commit | 8e366b1450f464f6c1046d0644d8d0a16bf2aeb3 (patch) | |
tree | 3a32d1856f9979823c427281d046cbccedb90eaf | |
parent | 58d503636324b29269957410e060571e387c24b9 (diff) | |
download | SponsorBlock-8e366b1450f464f6c1046d0644d8d0a16bf2aeb3.tar.gz SponsorBlock-8e366b1450f464f6c1046d0644d8d0a16bf2aeb3.zip |
Move unsubmitted segments to local storage to remove limits
Also add a way to export local storage
Fixes #1933
m--------- | maze-utils | 0 | ||||
m--------- | public/_locales | 0 | ||||
-rw-r--r-- | public/options/options.html | 25 | ||||
-rw-r--r-- | src/components/SkipNoticeComponent.tsx | 4 | ||||
-rw-r--r-- | src/components/SponsorTimeEditComponent.tsx | 6 | ||||
-rw-r--r-- | src/components/SubmissionNoticeComponent.tsx | 2 | ||||
-rw-r--r-- | src/components/options/UnsubmittedVideoListComponent.tsx | 4 | ||||
-rw-r--r-- | src/components/options/UnsubmittedVideoListItem.tsx | 8 | ||||
-rw-r--r-- | src/components/options/UnsubmittedVideosComponent.tsx | 6 | ||||
-rw-r--r-- | src/config.ts | 31 | ||||
-rw-r--r-- | src/content.ts | 20 | ||||
-rw-r--r-- | src/options.ts | 48 | ||||
-rw-r--r-- | src/popup.ts | 10 |
13 files changed, 107 insertions, 57 deletions
diff --git a/maze-utils b/maze-utils -Subproject 04699c3634f2dc0661db8c87afe5c6882ddd28f +Subproject 69d771d0f3b96951c970a9eacb046b808907bb1 diff --git a/public/_locales b/public/_locales -Subproject 0c9f99989481a29522dcd338e821295f15eeb13 +Subproject 7f2d4e63dc53facfeed96aae1086c2bc3329b51 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 @@ </div> </div> + <div data-type="private-text-change" data-sync-type="local" data-sync="*" data-confirm-message="exportOptionsWarning"> + <h2>__MSG_exportOtherData__</h2> + + <div> + <div class="option-button trigger-button inline"> + __MSG_exportOptionsCopy__ + </div> + <div class="option-button download-button inline"> + __MSG_exportOptionsDownload__ + </div> + <label for="importLocalOptions" class="option-button inline"> + __MSG_exportOptionsUpload__ + </label> + <input id="importLocalOptions" type="file" class="upload-button hidden" /> + </div> + + <div class="option-hidden-section hidden spacing indent"> + <textarea class="option-text-box" rows="10" style="width:80%"></textarea> + + <div class="option-button text-change-set"> + __MSG_setOptions__ + </div> + </div> + </div> + <div data-type="button-press" data-sync="resetToDefault" data-confirm-message="confirmResetToDefault"> <div class="option-button trigger-button"> __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<SkipNoticeProps, SkipNoticeSta source: SponsorSourceType.Local }; - const segmentTimes = Config.config.unsubmittedSegments[sponsorVideoID] || []; + const segmentTimes = Config.local.unsubmittedSegments[sponsorVideoID] || []; segmentTimes.push(sponsorTimesSubmitting); - Config.config.unsubmittedSegments[sponsorVideoID] = segmentTimes; + Config.local.unsubmittedSegments[sponsorVideoID] = segmentTimes; Config.forceSyncUpdate("unsubmittedSegments"); this.props.contentContainer().sponsorTimesSubmitting.push(sponsorTimesSubmitting); diff --git a/src/components/SponsorTimeEditComponent.tsx b/src/components/SponsorTimeEditComponent.tsx index 80b71f1c..63f03cf9 100644 --- a/src/components/SponsorTimeEditComponent.tsx +++ b/src/components/SponsorTimeEditComponent.tsx @@ -636,7 +636,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo const description = actionType === ActionType.Chapter ? this.descriptionOptionRef?.current?.value : ""; sponsorTimesSubmitting[this.props.index].description = description; - Config.config.unsubmittedSegments[this.props.contentContainer().sponsorVideoID] = sponsorTimesSubmitting; + Config.local.unsubmittedSegments[this.props.contentContainer().sponsorVideoID] = sponsorTimesSubmitting; Config.forceSyncUpdate("unsubmittedSegments"); this.props.contentContainer().updatePreviewBar(); @@ -687,9 +687,9 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo //save this if (sponsorTimes.length > 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<SubmissionNoticeProps, S let sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting; sponsorTimesSubmitting = sponsorTimesSubmitting.sort((a, b) => 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<UnsubmittedVideoList render(): React.ReactElement { // Render nothing if there are no unsubmitted segments - if (Object.keys(Config.config.unsubmittedSegments).length == 0) + if (Object.keys(Config.local.unsubmittedSegments).length == 0) return <></>; return ( @@ -58,7 +58,7 @@ class UnsubmittedVideoListComponent extends React.Component<UnsubmittedVideoList getUnsubmittedVideos(): JSX.Element[] { const elements: JSX.Element[] = []; - for (const videoID of Object.keys(Config.config.unsubmittedSegments)) { + for (const videoID of Object.keys(Config.local.unsubmittedSegments)) { elements.push( <UnsubmittedVideoListItem videoID={videoID} key={videoID}> </UnsubmittedVideoListItem> 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<UnsubmittedVideosListItem } render(): React.ReactElement { - const segmentCount = Config.config.unsubmittedSegments[this.props.videoID]?.length ?? 0; + const segmentCount = Config.local.unsubmittedSegments[this.props.videoID]?.length ?? 0; return ( <> @@ -69,17 +69,17 @@ class UnsubmittedVideoListItem extends React.Component<UnsubmittedVideosListItem clearSegments(): void { if (confirm(chrome.i18n.getMessage("clearThis"))) { - delete Config.config.unsubmittedSegments[this.props.videoID]; + delete Config.local.unsubmittedSegments[this.props.videoID]; Config.forceSyncUpdate("unsubmittedSegments"); } } exportSegments(): void { - this.copyToClipboard(exportTimes(Config.config.unsubmittedSegments[this.props.videoID])); + this.copyToClipboard(exportTimes(Config.local.unsubmittedSegments[this.props.videoID])); } exportSegmentsAsURL(): void { - this.copyToClipboard(`https://youtube.com/watch?v=${this.props.videoID}${exportTimesAsHashParam(Config.config.unsubmittedSegments[this.props.videoID])}`) + this.copyToClipboard(`https://youtube.com/watch?v=${this.props.videoID}${exportTimesAsHashParam(Config.local.unsubmittedSegments[this.props.videoID])}`) } copyToClipboard(text: string): void { diff --git a/src/components/options/UnsubmittedVideosComponent.tsx b/src/components/options/UnsubmittedVideosComponent.tsx index 3806b881..0e642ab6 100644 --- a/src/components/options/UnsubmittedVideosComponent.tsx +++ b/src/components/options/UnsubmittedVideosComponent.tsx @@ -21,8 +21,8 @@ class UnsubmittedVideosComponent extends React.Component<UnsubmittedVideosProps, } render(): React.ReactElement { - const videoCount = Object.keys(Config.config.unsubmittedSegments).length; - const segmentCount = Object.values(Config.config.unsubmittedSegments).reduce((acc: number, vid: Array<unknown>) => acc + vid.length, 0); + const videoCount = Object.keys(Config.local.unsubmittedSegments).length; + const segmentCount = Object.values(Config.local.unsubmittedSegments).reduce((acc: number, vid: Array<unknown>) => acc + vid.length, 0); return <> <div style={{marginBottom: "10px"}}> @@ -48,7 +48,7 @@ class UnsubmittedVideosComponent extends React.Component<UnsubmittedVideosProps, clearAllSegments(): void { if (confirm(chrome.i18n.getMessage("clearUnsubmittedSegmentsConfirm"))) - Config.config.unsubmittedSegments = {}; + Config.local.unsubmittedSegments = {}; } } diff --git a/src/config.ts b/src/config.ts index 14387a89..a8a8a682 100644 --- a/src/config.ts +++ b/src/config.ts @@ -12,8 +12,6 @@ interface SBConfig { userID: string; isVip: boolean; permissions: Record<Category, Permission>; - /* Contains unsubmitted segments that the user has created. */ - unsubmittedSegments: Record<string, SponsorTime[]>; 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<string, SponsorTime[]>; } class ConfigClass extends ProtoConfig<SBConfig, SBStorage> { @@ -153,6 +154,10 @@ class ConfigClass extends ProtoConfig<SBConfig, SBStorage> { 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 = <HTMLInputElement> 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 = <HTMLInputElement> 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<void> { } 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<void> { 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<void> { } 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<void> { 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<void> { break; case "videoChanged": currentVideoID = msg.videoID - sponsorTimes = Config.config.unsubmittedSegments[currentVideoID] ?? []; + sponsorTimes = Config.local.unsubmittedSegments[currentVideoID] ?? []; updateSegmentEditingUI(); if (msg.whitelisted) { |