diff options
author | Ajay <[email protected]> | 2022-02-06 23:17:34 -0500 |
---|---|---|
committer | Ajay <[email protected]> | 2022-02-06 23:17:34 -0500 |
commit | 4d60dec7f997bb076bfe7dc9d2b7079b96df3e95 (patch) | |
tree | 6c53156c3c5f466ae0080c4fdf64cd4af04ded05 /src | |
parent | 53d0ac8677cd6b5664a9fabe2036f5650a4303c6 (diff) | |
download | SponsorBlock-4d60dec7f997bb076bfe7dc9d2b7079b96df3e95.tar.gz SponsorBlock-4d60dec7f997bb076bfe7dc9d2b7079b96df3e95.zip |
Save downvotes and segment hides in local storage
Diffstat (limited to 'src')
-rw-r--r-- | src/components/SkipNoticeComponent.tsx | 2 | ||||
-rw-r--r-- | src/components/SponsorTimeEditComponent.tsx | 4 | ||||
-rw-r--r-- | src/config.ts | 61 | ||||
-rw-r--r-- | src/content.ts | 41 | ||||
-rw-r--r-- | src/options.ts | 4 | ||||
-rw-r--r-- | src/utils.ts | 43 |
6 files changed, 119 insertions, 36 deletions
diff --git a/src/components/SkipNoticeComponent.tsx b/src/components/SkipNoticeComponent.tsx index b985d481..6b3246d3 100644 --- a/src/components/SkipNoticeComponent.tsx +++ b/src/components/SkipNoticeComponent.tsx @@ -520,7 +520,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta const segmentTimes = Config.config.unsubmittedSegments[sponsorVideoID] || []; segmentTimes.push(sponsorTimesSubmitting); Config.config.unsubmittedSegments[sponsorVideoID] = segmentTimes; - Config.forceUpdate("unsubmittedSegments"); + Config.forceSyncUpdate("unsubmittedSegments"); this.props.contentContainer().sponsorTimesSubmitting.push(sponsorTimesSubmitting); this.props.contentContainer().updatePreviewBar(); diff --git a/src/components/SponsorTimeEditComponent.tsx b/src/components/SponsorTimeEditComponent.tsx index 6269cb34..8681c87f 100644 --- a/src/components/SponsorTimeEditComponent.tsx +++ b/src/components/SponsorTimeEditComponent.tsx @@ -510,7 +510,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo sponsorTimesSubmitting[this.props.index].actionType = this.getNextActionType(category, inputActionType); Config.config.unsubmittedSegments[this.props.contentContainer().sponsorVideoID] = sponsorTimesSubmitting; - Config.forceUpdate("unsubmittedSegments"); + Config.forceSyncUpdate("unsubmittedSegments"); this.props.contentContainer().updatePreviewBar(); @@ -561,7 +561,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo } else { delete Config.config.unsubmittedSegments[this.props.contentContainer().sponsorVideoID]; } - Config.forceUpdate("unsubmittedSegments"); + Config.forceSyncUpdate("unsubmittedSegments"); this.props.contentContainer().updatePreviewBar(); diff --git a/src/config.ts b/src/config.ts index c8e1c75b..fe4b60d6 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,6 +1,6 @@ import * as CompileConfig from "../config.json"; import * as invidiousList from "../ci/invidiouslist.json"; -import { Category, CategorySelection, CategorySkipOption, NoticeVisbilityMode, PreviewBarOption, SponsorTime, StorageChangesObject, UnEncodedSegmentTimes as UnencodedSegmentTimes, Keybind } from "./types"; +import { Category, CategorySelection, CategorySkipOption, NoticeVisbilityMode, PreviewBarOption, SponsorTime, StorageChangesObject, UnEncodedSegmentTimes as UnencodedSegmentTimes, Keybind, HashedValue, VideoID, SponsorHideType } from "./types"; import { keybindEquals } from "./utils/configUtils"; interface SBConfig { @@ -96,17 +96,23 @@ interface SBConfig { } } +export type VideoDownvotes = { segments: { uuid: HashedValue, hidden: SponsorHideType }[] , lastAccess: number }; + interface SBStorage { + /* VideoID prefixes to UUID prefixes */ + downvotedSegments: Record<VideoID & HashedValue, VideoDownvotes>, } export interface SBObject { configSyncListeners: Array<(changes: StorageChangesObject) => unknown>; - defaults: SBConfig; + syncDefaults: SBConfig; + localDefaults: SBStorage; cachedSyncConfig: SBConfig; cachedLocalStorage: SBStorage; config: SBConfig; local: SBStorage; - forceUpdate(prop: string): void; + forceSyncUpdate(prop: string): void; + forceLocalUpdate(prop: string): void; } const Config: SBObject = { @@ -114,7 +120,7 @@ const Config: SBObject = { * Callback function when an option is updated */ configSyncListeners: [], - defaults: { + syncDefaults: { userID: null, isVip: false, lastIsVipUpdate: 0, @@ -275,11 +281,15 @@ const Config: SBObject = { } } }, + localDefaults: { + downvotedSegments: {} + }, cachedSyncConfig: null, cachedLocalStorage: null, config: null, local: null, - forceUpdate + forceSyncUpdate, + forceLocalUpdate }; // Function setup @@ -343,7 +353,7 @@ function configProxy(): { sync: SBConfig, local: SBStorage } { return obj[prop] || data; }, - deleteProperty(obj: SBConfig, prop: keyof SBStorage) { + deleteProperty(obj: SBStorage, prop: keyof SBStorage) { chrome.storage.local.remove(<string> prop); return true; @@ -352,24 +362,35 @@ function configProxy(): { sync: SBConfig, local: SBStorage } { }; return { - sync: new Proxy<SBConfig>({handler: syncHandler} as unknown as SBConfig, syncHandler), - local: new Proxy<SBStorage>({handler: localHandler} as unknown as SBConfig, localHandler) + sync: new Proxy<SBConfig>({ handler: syncHandler } as unknown as SBConfig, syncHandler), + local: new Proxy<SBStorage>({ handler: localHandler } as unknown as SBStorage, localHandler) }; } -function forceUpdate(prop: string): void { +function forceSyncUpdate(prop: string): void { chrome.storage.sync.set({ [prop]: Config.cachedSyncConfig[prop] }); } -function fetchConfig(): Promise<void> { - return new Promise((resolve) => { +function forceLocalUpdate(prop: string): void { + chrome.storage.local.set({ + [prop]: Config.cachedLocalStorage[prop] + }); +} + +async function fetchConfig(): Promise<void> { + await Promise.all([new Promise<void>((resolve) => { chrome.storage.sync.get(null, function(items) { - Config.cachedSyncConfig = <SBConfig> <unknown> items; // Data is ready + Config.cachedSyncConfig = <SBConfig> <unknown> items; resolve(); }); - }); + }), new Promise<void>((resolve) => { + chrome.storage.local.get(null, function(items) { + Config.cachedLocalStorage = <SBStorage> <unknown> items; + resolve(); + }); + })]); } function migrateOldSyncFormats(config: SBConfig) { @@ -477,17 +498,23 @@ async function setupConfig() { // Add defaults function addDefaults() { - for (const key in Config.defaults) { + for (const key in Config.syncDefaults) { if(!Object.prototype.hasOwnProperty.call(Config.cachedSyncConfig, key)) { - Config.cachedSyncConfig[key] = Config.defaults[key]; + Config.cachedSyncConfig[key] = Config.syncDefaults[key]; } else if (key === "barTypes") { - for (const key2 in Config.defaults[key]) { + for (const key2 in Config.syncDefaults[key]) { if(!Object.prototype.hasOwnProperty.call(Config.cachedSyncConfig[key], key2)) { - Config.cachedSyncConfig[key][key2] = Config.defaults[key][key2]; + Config.cachedSyncConfig[key][key2] = Config.syncDefaults[key][key2]; } } } } + + for (const key in Config.localDefaults) { + if(!Object.prototype.hasOwnProperty.call(Config.cachedLocalStorage, key)) { + Config.cachedLocalStorage[key] = Config.localDefaults[key]; + } + } } // Sync config diff --git a/src/content.ts b/src/content.ts index 40c2a74c..0e5eb6d4 100644 --- a/src/content.ts +++ b/src/content.ts @@ -1,5 +1,5 @@ import Config from "./config"; -import { SponsorTime, CategorySkipOption, VideoID, SponsorHideType, VideoInfo, StorageChangesObject, ChannelIDInfo, ChannelIDStatus, SponsorSourceType, SegmentUUID, Category, SkipToTimeParams, ToggleSkippable, ActionType, ScheduledTime } from "./types"; +import { SponsorTime, CategorySkipOption, VideoID, SponsorHideType, VideoInfo, StorageChangesObject, ChannelIDInfo, ChannelIDStatus, SponsorSourceType, SegmentUUID, Category, SkipToTimeParams, ToggleSkippable, ActionType, ScheduledTime, HashedValue } from "./types"; import { ContentContainer, Keybind } from "./types"; import Utils from "./utils"; @@ -209,6 +209,7 @@ function messageListener(request: Message, sender: unknown, sendResponse: (respo return true; case "hideSegment": utils.getSponsorTimeFromUUID(sponsorTimes, request.UUID).hidden = request.type; + utils.addHiddenSegment(sponsorVideoID, request.UUID, request.type); updatePreviewBar(); break; @@ -698,7 +699,7 @@ async function sponsorsLookup(id: string, keepOldSubmissions = true) { if (hashParams.requiredSegment) extraRequestData.requiredSegment = hashParams.requiredSegment; // Check for hashPrefix setting - const hashPrefix = (await utils.getHash(id, 1)).slice(0, 4); + const hashPrefix = (await utils.getHash(id, 1)).slice(0, 4) as VideoID & HashedValue; const response = await utils.asyncRequestToServer('GET', "/api/skipSegments/" + hashPrefix, { categories, actionTypes: getEnabledActionTypes(), @@ -752,6 +753,18 @@ async function sponsorsLookup(id: string, keepOldSubmissions = true) { } } + // See if some segments should be hidden + const downvotedData = Config.local.downvotedSegments[hashPrefix]; + if (downvotedData) { + for (const segment of sponsorTimes) { + const hashedUUID = await utils.getHash(segment.UUID, 1); + const segmentDownvoteData = downvotedData.segments.find((downvote) => downvote.uuid === hashedUUID); + if (segmentDownvoteData) { + segment.hidden = segmentDownvoteData.hidden; + } + } + } + startSkipScheduleCheckingForStartSponsors(); //update the preview bar @@ -1516,7 +1529,7 @@ function startOrEndTimingNewSegment() { // Save the newly created segment Config.config.unsubmittedSegments[sponsorVideoID] = sponsorTimesSubmitting; - Config.forceUpdate("unsubmittedSegments"); + Config.forceSyncUpdate("unsubmittedSegments"); // Make sure they know if someone has already submitted something it while they were watching sponsorsLookup(sponsorVideoID); @@ -1539,7 +1552,7 @@ function cancelCreatingSegment() { if (isSegmentCreationInProgress()) { sponsorTimesSubmitting.splice(sponsorTimesSubmitting.length - 1, 1); Config.config.unsubmittedSegments[sponsorVideoID] = sponsorTimesSubmitting; - Config.forceUpdate("unsubmittedSegments"); + Config.forceSyncUpdate("unsubmittedSegments"); if (sponsorTimesSubmitting.length <= 0) resetSponsorSubmissionNotice(); } @@ -1689,7 +1702,7 @@ function clearSponsorTimes() { //clear the sponsor times delete Config.config.unsubmittedSegments[currentVideoID]; - Config.forceUpdate("unsubmittedSegments"); + Config.forceSyncUpdate("unsubmittedSegments"); //clear sponsor times submitting sponsorTimesSubmitting = []; @@ -1771,7 +1784,11 @@ async function voteAsync(type: number, UUID: SegmentUUID, category?: Category): } else if (type === 1) { segment.hidden = SponsorHideType.Visible; } - + + if (!category) { + utils.addHiddenSegment(sponsorVideoID, segment.UUID, segment.hidden); + } + updatePreviewBar(); } } @@ -1837,7 +1854,7 @@ async function sendSubmitMessage() { //update sponsorTimes Config.config.unsubmittedSegments[sponsorVideoID] = sponsorTimesSubmitting; - Config.forceUpdate("unsubmittedSegments"); + Config.forceSyncUpdate("unsubmittedSegments"); // Check to see if any of the submissions are below the minimum duration set if (Config.config.minDuration > 0) { @@ -1865,7 +1882,7 @@ async function sendSubmitMessage() { // Remove segments from storage since they've already been submitted delete Config.config.unsubmittedSegments[sponsorVideoID]; - Config.forceUpdate("unsubmittedSegments"); + Config.forceSyncUpdate("unsubmittedSegments"); const newSegments = sponsorTimesSubmitting; try { @@ -1972,12 +1989,12 @@ function hotkeyListener(e: KeyboardEvent): void { } //legacy - to preserve keybinds for skipKey, startSponsorKey and submitKey for people who set it before the update. (shouldn't be changed for future keybind options) - if (key.key == skipKey?.key && skipKey.code == null && !keybindEquals(Config.defaults.skipKeybind, skipKey)) { + if (key.key == skipKey?.key && skipKey.code == null && !keybindEquals(Config.syncDefaults.skipKeybind, skipKey)) { if (activeSkipKeybindElement) activeSkipKeybindElement.toggleSkip.call(activeSkipKeybindElement); - } else if (key.key == startSponsorKey?.key && startSponsorKey.code == null && !keybindEquals(Config.defaults.startSponsorKeybind, startSponsorKey)) { + } else if (key.key == startSponsorKey?.key && startSponsorKey.code == null && !keybindEquals(Config.syncDefaults.startSponsorKeybind, startSponsorKey)) { startOrEndTimingNewSegment(); - } else if (key.key == submitKey?.key && submitKey.code == null && !keybindEquals(Config.defaults.submitKeybind, submitKey)) { + } else if (key.key == submitKey?.key && submitKey.code == null && !keybindEquals(Config.syncDefaults.submitKeybind, submitKey)) { submitSponsorTimes(); } } @@ -2090,6 +2107,6 @@ function checkForPreloadedSegment() { if (pushed) { Config.config.unsubmittedSegments[sponsorVideoID] = sponsorTimesSubmitting; - Config.forceUpdate("unsubmittedSegments"); + Config.forceSyncUpdate("unsubmittedSegments"); } }
\ No newline at end of file diff --git a/src/options.ts b/src/options.ts index f759e066..bcf3f824 100644 --- a/src/options.ts +++ b/src/options.ts @@ -195,7 +195,7 @@ async function init() { textChangeResetButton.addEventListener("click", () => { if (!confirm(chrome.i18n.getMessage("areYouSureReset"))) return; - Config.config[option] = Config.defaults[option]; + Config.config[option] = Config.syncDefaults[option]; textChangeInput.value = Config.config[option]; }); @@ -247,7 +247,7 @@ async function init() { const numberInput = optionsElements[i].querySelector("input"); if (isNaN(configValue) || configValue < 0) { - numberInput.value = Config.defaults[option]; + numberInput.value = Config.syncDefaults[option]; } else { numberInput.value = configValue; } diff --git a/src/utils.ts b/src/utils.ts index 087f40ab..f593253f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,5 @@ -import Config from "./config"; -import { CategorySelection, SponsorTime, FetchResponse, BackgroundScriptContainer, Registration, HashedValue } from "./types"; +import Config, { VideoDownvotes } from "./config"; +import { CategorySelection, SponsorTime, FetchResponse, BackgroundScriptContainer, Registration, HashedValue, VideoID, SponsorHideType } from "./types"; import * as CompileConfig from "../config.json"; import { findValidElementFromSelector } from "./utils/pageUtils"; @@ -488,4 +488,43 @@ export default class Utils { return hashHex as T & HashedValue; } + + async addHiddenSegment(videoID: VideoID, segmentUUID: string, hidden: SponsorHideType) { + const hashedVideoID = (await this.getHash(videoID, 1)).slice(0, 4) as VideoID & HashedValue; + const UUIDHash = await this.getHash(segmentUUID, 1); + + const allDownvotes = Config.local.downvotedSegments; + const currentVideoData = allDownvotes[hashedVideoID] || { segments: [], lastAccess: 0 }; + + currentVideoData.lastAccess = Date.now(); + const existingData = currentVideoData.segments.find((segment) => segment.uuid === UUIDHash); + if (hidden === SponsorHideType.Visible) { + delete allDownvotes[hashedVideoID]; + } else { + if (existingData) { + existingData.hidden = hidden; + } else { + currentVideoData.segments.push({ + uuid: UUIDHash, + hidden + }); + } + + allDownvotes[hashedVideoID] = currentVideoData; + } + + const entries = Object.entries(allDownvotes); + if (entries.length > 10000) { + let min: [string, VideoDownvotes] = null; + for (let i = 0; i < entries[0].length; i++) { + if (min === null || entries[i][1].lastAccess < min[1].lastAccess) { + min = entries[i]; + } + } + + delete allDownvotes[min[0]]; + } + + Config.forceLocalUpdate("downvotedSegments"); + } } |