From e9ce494ccdcdf8c82eee46c0ee66823bed7c19f3 Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Sat, 3 Jan 2026 09:11:26 -0500 Subject: [PATCH] Remove watch history metadata caching --- js/feedEngine/watchHistoryFeed.js | 5 +- js/historyView.js | 49 +-- js/services/watchHistoryTelemetry.js | 89 +----- js/watchHistoryService.js | 398 +----------------------- tests/profile-modal-controller.test.mjs | 102 ------ 5 files changed, 18 insertions(+), 625 deletions(-) diff --git a/js/feedEngine/watchHistoryFeed.js b/js/feedEngine/watchHistoryFeed.js index cd0d4428..3af85393 100644 --- a/js/feedEngine/watchHistoryFeed.js +++ b/js/feedEngine/watchHistoryFeed.js @@ -210,10 +210,7 @@ function createWatchHistoryHydratorStage({ const resolver = metadataResolver || createWatchHistoryMetadataResolver(); return async function watchHistoryHydratorStage(items = [], context = {}) { - const shouldStoreMetadata = - typeof service?.shouldStoreMetadata === "function" - ? service.shouldStoreMetadata() !== false - : true; + const shouldStoreMetadata = false; const results = []; diff --git a/js/historyView.js b/js/historyView.js index f2065840..d49cb0d3 100644 --- a/js/historyView.js +++ b/js/historyView.js @@ -1450,10 +1450,7 @@ export function createWatchHistoryRenderer(config = {}) { privacyDismissed: false, metadataCache: new Map(), feedMetadata: null, - metadataStorageEnabled: - typeof watchHistoryService.shouldStoreMetadata === "function" - ? watchHistoryService.shouldStoreMetadata() !== false - : true, + metadataStorageEnabled: false, sessionFallbackActive: false, syncEnabled, localSupported, @@ -1681,20 +1678,8 @@ export function createWatchHistoryRenderer(config = {}) { } function subscribeToMetadataPreference() { - if (typeof watchHistoryService.subscribe !== "function") { - return; - } - const unsubscribe = watchHistoryService.subscribe( - "metadata-preference", - (payload) => { - const enabled = payload?.enabled !== false; - state.metadataStorageEnabled = enabled; - updateMetadataToggle(); - } - ); - if (typeof unsubscribe === "function") { - subscriptions.add(unsubscribe); - } + state.metadataStorageEnabled = false; + updateMetadataToggle(); } function subscribeToRepublishEvents() { @@ -2489,23 +2474,7 @@ export function createWatchHistoryRenderer(config = {}) { } boundMetadataToggleHandler = (event) => { event.preventDefault(); - const nextValue = !state.metadataStorageEnabled; - try { - if (typeof watchHistoryService.setMetadataPreference === "function") { - watchHistoryService.setMetadataPreference(nextValue); - } - } catch (error) { - if (isDevEnv) { - userLogger.warn( - "[historyView] Failed to set metadata preference:", - error - ); - } - } - state.metadataStorageEnabled = - typeof watchHistoryService.shouldStoreMetadata === "function" - ? watchHistoryService.shouldStoreMetadata() !== false - : nextValue; + state.metadataStorageEnabled = false; updateMetadataToggle(); if (!state.metadataStorageEnabled) { try { @@ -2675,10 +2644,7 @@ export function createWatchHistoryRenderer(config = {}) { subscribeToFingerprintUpdates(); subscribeToModerationEvents(); updateInfoCallout(); - state.metadataStorageEnabled = - typeof watchHistoryService.shouldStoreMetadata === "function" - ? watchHistoryService.shouldStoreMetadata() !== false - : true; + state.metadataStorageEnabled = false; updateMetadataToggle(); const storedPreference = readPreference( WATCH_HISTORY_METADATA_PREF_KEY, @@ -2718,10 +2684,7 @@ export function createWatchHistoryRenderer(config = {}) { return; } bindMetadataToggle(); - state.metadataStorageEnabled = - typeof watchHistoryService.shouldStoreMetadata === "function" - ? watchHistoryService.shouldStoreMetadata() !== false - : true; + state.metadataStorageEnabled = false; updateMetadataToggle(); }, async refresh(options = {}) { diff --git a/js/services/watchHistoryTelemetry.js b/js/services/watchHistoryTelemetry.js index 6c3fc5dd..fd07bb9d 100644 --- a/js/services/watchHistoryTelemetry.js +++ b/js/services/watchHistoryTelemetry.js @@ -72,7 +72,7 @@ export default class WatchHistoryTelemetry { ? Math.max(0, viewThresholdSeconds) : DEFAULT_VIEW_THRESHOLD_SECONDS; - this.watchHistoryMetadataEnabled = null; + this.watchHistoryMetadataEnabled = false; this.watchHistoryPreferenceUnsubscribe = null; this.playbackTelemetryState = null; this.loggedViewPointerKeys = new Set(); @@ -81,98 +81,21 @@ export default class WatchHistoryTelemetry { destroy() { this.resetPlaybackLoggingState(); this._clearPreferenceSubscription(); - this.watchHistoryMetadataEnabled = null; + this.watchHistoryMetadataEnabled = false; } async initPreferenceSync() { - if (!this.watchHistoryService?.isEnabled?.()) { - this.watchHistoryMetadataEnabled = false; - return null; - } - - this.refreshPreferenceSettings(); - - if ( - typeof this.watchHistoryService.subscribe === "function" && - !this.watchHistoryPreferenceUnsubscribe - ) { - try { - const unsubscribe = this.watchHistoryService.subscribe( - "metadata-preference", - (payload) => { - const previous = this.watchHistoryMetadataEnabled; - const enabled = payload?.enabled !== false; - this.watchHistoryMetadataEnabled = enabled; - if (previous === true && enabled === false) { - try { - this.watchHistoryService?.clearLocalMetadata?.(); - } catch (error) { - devLogger.warn( - "[watchHistoryTelemetry] Failed to clear cached metadata after toggle off:", - error, - ); - } - } - }, - ); - if (typeof unsubscribe === "function") { - this.watchHistoryPreferenceUnsubscribe = unsubscribe; - } - } catch (error) { - devLogger.warn( - "[watchHistoryTelemetry] Failed to subscribe to metadata preference changes:", - error, - ); - } - } - - return this.watchHistoryPreferenceUnsubscribe || null; + this.watchHistoryMetadataEnabled = false; + return null; } refreshPreferenceSettings() { - if (!this.watchHistoryService?.isEnabled?.()) { - this.watchHistoryMetadataEnabled = false; - return this.watchHistoryMetadataEnabled; - } - - const previous = this.watchHistoryMetadataEnabled; - let enabled = true; - - try { - if (typeof this.watchHistoryService.getSettings === "function") { - const settings = this.watchHistoryService.getSettings(); - enabled = settings?.metadata?.storeLocally !== false; - } else if ( - typeof this.watchHistoryService.shouldStoreMetadata === "function" - ) { - enabled = this.watchHistoryService.shouldStoreMetadata() !== false; - } - } catch (error) { - devLogger.warn( - "[watchHistoryTelemetry] Failed to read metadata settings:", - error, - ); - enabled = true; - } - - this.watchHistoryMetadataEnabled = enabled; - - if (enabled === false && previous !== false) { - try { - this.watchHistoryService?.clearLocalMetadata?.(); - } catch (error) { - devLogger.warn( - "[watchHistoryTelemetry] Failed to purge metadata cache while preference disabled:", - error, - ); - } - } - + this.watchHistoryMetadataEnabled = false; return this.watchHistoryMetadataEnabled; } isMetadataPreferenceEnabled() { - return this.watchHistoryMetadataEnabled !== false; + return false; } persistMetadataForVideo(video, pointerInfo) { diff --git a/js/watchHistoryService.js b/js/watchHistoryService.js index dbf89c48..004d7552 100644 --- a/js/watchHistoryService.js +++ b/js/watchHistoryService.js @@ -24,21 +24,12 @@ import { devLogger, userLogger } from "./utils/logger.js"; const SESSION_STORAGE_KEY = "bitvid:watch-history:session:v1"; const SESSION_STORAGE_VERSION = 1; const POINTER_THROTTLE_MS = 60 * 1000; -const METADATA_STORAGE_KEY = "bitvid:watch-history:metadata-cache:v1"; -const METADATA_STORAGE_VERSION = 1; -const METADATA_PREFERENCE_KEY = "bitvid:watch-history:metadata:store-locally"; - const state = { restored: false, queues: new Map(), inflightSnapshots: new Map(), fingerprintCache: new Map(), listeners: new Map(), - metadata: { - restored: false, - cache: new Map(), - preference: null, - }, }; function resolveFlagEnabled() { @@ -234,345 +225,6 @@ function getLocalStorage() { return null; } -function ensureMetadataPreference() { - if (typeof state.metadata.preference === "boolean") { - return state.metadata.preference; - } - const storage = getLocalStorage(); - if (!storage) { - state.metadata.preference = true; - return state.metadata.preference; - } - try { - const stored = storage.getItem(METADATA_PREFERENCE_KEY); - if (stored === null) { - state.metadata.preference = true; - } else { - state.metadata.preference = stored === "true"; - } - } catch (error) { - devLogger.warn( - "[watchHistoryService] Failed to read metadata preference:", - error, - ); - state.metadata.preference = true; - } - return state.metadata.preference; -} - -function persistMetadataPreference(value) { - const storage = getLocalStorage(); - if (!storage) { - return; - } - try { - if (value === true) { - storage.setItem(METADATA_PREFERENCE_KEY, "true"); - } else { - storage.setItem(METADATA_PREFERENCE_KEY, "false"); - } - } catch (error) { - devLogger.warn( - "[watchHistoryService] Failed to persist metadata preference:", - error, - ); - } -} - -function sanitizeVideoForStorage(video) { - if (!video || typeof video !== "object") { - return null; - } - const createdAt = Number.isFinite(video.created_at) - ? Math.floor(Number(video.created_at)) - : null; - return { - id: typeof video.id === "string" ? video.id : "", - title: typeof video.title === "string" ? video.title : "", - thumbnail: typeof video.thumbnail === "string" ? video.thumbnail : "", - url: typeof video.url === "string" ? video.url : "", - magnet: typeof video.magnet === "string" ? video.magnet : "", - pubkey: typeof video.pubkey === "string" ? video.pubkey : "", - created_at: createdAt, - infoHash: typeof video.infoHash === "string" ? video.infoHash : "", - legacyInfoHash: - typeof video.legacyInfoHash === "string" ? video.legacyInfoHash : "", - mode: typeof video.mode === "string" ? video.mode : "", - isPrivate: video?.isPrivate === true, - description: - typeof video.description === "string" ? video.description : "", - }; -} - -function sanitizeVideoForHistory(video) { - const sanitized = sanitizeVideoForStorage(video); - if (!sanitized) { - return null; - } - - return { - id: sanitized.id, - title: sanitized.title, - thumbnail: sanitized.thumbnail, - pubkey: sanitized.pubkey, - created_at: sanitized.created_at, - url: sanitized.url, - magnet: sanitized.magnet, - infoHash: sanitized.infoHash, - legacyInfoHash: - typeof video?.legacyInfoHash === "string" - ? video.legacyInfoHash - : typeof sanitized.legacyInfoHash === "string" - ? sanitized.legacyInfoHash - : "", - }; -} - -function sanitizeProfileForStorage(profile) { - if (!profile || typeof profile !== "object") { - return null; - } - return { - pubkey: typeof profile.pubkey === "string" ? profile.pubkey : "", - name: typeof profile.name === "string" ? profile.name : "", - display_name: - typeof profile.display_name === "string" ? profile.display_name : "", - picture: typeof profile.picture === "string" ? profile.picture : "", - nip05: typeof profile.nip05 === "string" ? profile.nip05 : "", - }; -} - -function restoreMetadataCache() { - if (state.metadata.restored) { - return; - } - state.metadata.restored = true; - - const storage = getLocalStorage(); - if (!storage) { - return; - } - - let raw = null; - try { - raw = storage.getItem(METADATA_STORAGE_KEY); - } catch (error) { - devLogger.warn( - "[watchHistoryService] Failed to read metadata cache:", - error, - ); - return; - } - - if (!raw || typeof raw !== "string") { - return; - } - - let parsed = null; - try { - parsed = JSON.parse(raw); - } catch (error) { - devLogger.warn( - "[watchHistoryService] Failed to parse metadata cache:", - error, - ); - try { - storage.removeItem(METADATA_STORAGE_KEY); - } catch (cleanupError) { - devLogger.warn( - "[watchHistoryService] Failed to clear corrupt metadata cache:", - cleanupError, - ); - } - return; - } - - if (!parsed || parsed.version !== METADATA_STORAGE_VERSION) { - try { - storage.removeItem(METADATA_STORAGE_KEY); - } catch (cleanupError) { - devLogger.warn( - "[watchHistoryService] Failed to clear outdated metadata cache:", - cleanupError, - ); - } - return; - } - - state.metadata.cache.clear(); - const entries = parsed.entries && typeof parsed.entries === "object" - ? parsed.entries - : {}; - for (const [key, entry] of Object.entries(entries)) { - if (typeof key !== "string" || !key) { - continue; - } - const sanitizedVideo = sanitizeVideoForStorage(entry?.video); - const sanitizedProfile = sanitizeProfileForStorage(entry?.profile); - state.metadata.cache.set(key, { - video: sanitizedVideo, - profile: sanitizedProfile, - storedAt: Number.isFinite(entry?.storedAt) ? entry.storedAt : Date.now(), - }); - } -} - -function sanitizePointerMetadata(metadata) { - if (!metadata || typeof metadata !== "object") { - return null; - } - - const pointerMetadata = {}; - const video = sanitizeVideoForHistory(metadata.video); - if (video) { - pointerMetadata.video = video; - } - - const profile = sanitizeProfileForStorage(metadata.profile); - if (profile) { - pointerMetadata.profile = profile; - } - - const resumeCandidates = [ - metadata.resumeAt, - metadata.resume, - metadata.resumeSeconds, - ]; - for (const candidate of resumeCandidates) { - if (Number.isFinite(candidate)) { - pointerMetadata.resumeAt = Math.max(0, Math.floor(candidate)); - break; - } - } - - if (metadata.completed === true) { - pointerMetadata.completed = true; - } - - return Object.keys(pointerMetadata).length ? pointerMetadata : null; -} - -function persistMetadataCache() { - const storage = getLocalStorage(); - if (!storage) { - return; - } - const shouldStore = shouldStoreMetadataLocally(); - if (!shouldStore || !state.metadata.cache.size) { - try { - storage.removeItem(METADATA_STORAGE_KEY); - } catch (error) { - devLogger.warn( - "[watchHistoryService] Failed to clear metadata cache:", - error, - ); - } - return; - } - - const payload = { version: METADATA_STORAGE_VERSION, entries: {} }; - for (const [key, entry] of state.metadata.cache.entries()) { - payload.entries[key] = { - video: sanitizeVideoForStorage(entry?.video) || null, - profile: sanitizeProfileForStorage(entry?.profile) || null, - storedAt: Number.isFinite(entry?.storedAt) ? entry.storedAt : Date.now(), - }; - } - try { - storage.setItem(METADATA_STORAGE_KEY, JSON.stringify(payload)); - } catch (error) { - devLogger.warn( - "[watchHistoryService] Failed to persist metadata cache:", - error, - ); - } -} - -function shouldStoreMetadataLocally() { - return ensureMetadataPreference() === true; -} - -function setMetadataPreference(enabled) { - const normalized = enabled === false ? false : true; - const previous = ensureMetadataPreference(); - state.metadata.preference = normalized; - persistMetadataPreference(normalized); - if (!normalized) { - state.metadata.cache.clear(); - persistMetadataCache(); - } else { - persistMetadataCache(); - } - if (previous !== normalized) { - emit("metadata-preference", { enabled: normalized }); - } -} - -function getMetadataPreference() { - return ensureMetadataPreference(); -} - -function getLocalMetadata(pointerKeyValue) { - restoreMetadataCache(); - const key = typeof pointerKeyValue === "string" ? pointerKeyValue : ""; - if (!key) { - return null; - } - const stored = state.metadata.cache.get(key); - if (!stored) { - return null; - } - return { - video: stored.video ? { ...stored.video } : null, - profile: stored.profile ? { ...stored.profile } : null, - storedAt: stored.storedAt, - }; -} - -function setLocalMetadata(pointerKeyValue, metadata) { - if (!shouldStoreMetadataLocally()) { - return; - } - const key = typeof pointerKeyValue === "string" ? pointerKeyValue : ""; - if (!key) { - return; - } - restoreMetadataCache(); - if (!metadata || typeof metadata !== "object") { - state.metadata.cache.delete(key); - persistMetadataCache(); - return; - } - const video = sanitizeVideoForStorage(metadata.video); - const profile = sanitizeProfileForStorage(metadata.profile); - state.metadata.cache.set(key, { - video, - profile, - storedAt: Date.now(), - }); - persistMetadataCache(); -} - -function removeLocalMetadata(pointerKeyValue) { - const key = typeof pointerKeyValue === "string" ? pointerKeyValue : ""; - if (!key) { - return; - } - restoreMetadataCache(); - if (state.metadata.cache.delete(key)) { - persistMetadataCache(); - } -} - -function clearLocalMetadata() { - restoreMetadataCache(); - if (!state.metadata.cache.size) { - return; - } - state.metadata.cache.clear(); - persistMetadataCache(); -} - function emit(eventName, payload) { const listeners = state.listeners.get(eventName); if (listeners && listeners.size) { @@ -1133,32 +785,11 @@ async function publishView(pointerInput, createdAt, metadata = {}) { viewResult?.event?.created_at ); - const pointerMetadata = sanitizePointerMetadata(metadata); - if (pointerMetadata) { - const existingMetadata = - typeof normalizedPointer.metadata === "object" - ? normalizedPointer.metadata - : {}; - normalizedPointer.metadata = { ...existingMetadata, ...pointerMetadata }; - if (pointerMetadata.video) { - normalizedPointer.video = pointerMetadata.video; - } - if (pointerMetadata.profile) { - normalizedPointer.profile = pointerMetadata.profile; - } - if ( - Number.isFinite(pointerMetadata.resumeAt) && - !Number.isFinite(normalizedPointer.resumeAt) - ) { - normalizedPointer.resumeAt = Math.max( - 0, - Math.floor(pointerMetadata.resumeAt), - ); - } - if (pointerMetadata.completed === true) { - normalizedPointer.completed = true; - } - } + delete normalizedPointer.metadata; + delete normalizedPointer.video; + delete normalizedPointer.profile; + delete normalizedPointer.resumeAt; + delete normalizedPointer.completed; const key = pointerKey(normalizedPointer); devLogger.info( @@ -1608,17 +1239,6 @@ function getAllQueues() { return summary; } -function getSettings() { - const preference = getMetadataPreference(); - return { - metadata: { - preference, - storeLocally: preference === true, - cacheSize: state.metadata.cache.size, - }, - }; -} - const watchHistoryService = { isEnabled: isFeatureEnabled, supportsLocalHistory, @@ -1630,14 +1250,6 @@ const watchHistoryService = { resetProgress, getQueuedPointers, getAllQueues, - getMetadataPreference, - setMetadataPreference, - shouldStoreMetadata: shouldStoreMetadataLocally, - getLocalMetadata, - setLocalMetadata, - removeLocalMetadata, - clearLocalMetadata, - getSettings, subscribe, }; diff --git a/tests/profile-modal-controller.test.mjs b/tests/profile-modal-controller.test.mjs index c56b951e..33222082 100644 --- a/tests/profile-modal-controller.test.mjs +++ b/tests/profile-modal-controller.test.mjs @@ -1293,105 +1293,3 @@ test('history pane lazily initializes the watch history renderer', async () => { assert.equal(controller.boundProfileHistoryVisibility, null); }); -test('history metadata toggle updates stored preference', async (t) => { - let storedPreference = true; - let clearCalls = 0; - - const originalSetPreference = watchHistoryService.setMetadataPreference; - const originalShouldStore = watchHistoryService.shouldStoreMetadata; - const originalClearMetadata = watchHistoryService.clearLocalMetadata; - const originalSubscribe = watchHistoryService.subscribe; - const originalIsEnabled = watchHistoryService.isEnabled; - const originalSupportsLocal = watchHistoryService.supportsLocalHistory; - const originalIsLocalOnly = watchHistoryService.isLocalOnly; - - watchHistoryService.setMetadataPreference = (value) => { - storedPreference = value !== false; - }; - watchHistoryService.shouldStoreMetadata = () => storedPreference; - watchHistoryService.clearLocalMetadata = () => { - clearCalls += 1; - }; - watchHistoryService.subscribe = () => () => {}; - watchHistoryService.isEnabled = () => true; - watchHistoryService.supportsLocalHistory = () => true; - watchHistoryService.isLocalOnly = () => false; - - t.after(() => { - watchHistoryService.setMetadataPreference = originalSetPreference; - watchHistoryService.shouldStoreMetadata = originalShouldStore; - watchHistoryService.clearLocalMetadata = originalClearMetadata; - watchHistoryService.subscribe = originalSubscribe; - watchHistoryService.isEnabled = originalIsEnabled; - watchHistoryService.supportsLocalHistory = originalSupportsLocal; - watchHistoryService.isLocalOnly = originalIsLocalOnly; - }); - - const controller = createController({ - createWatchHistoryRenderer: (config) => - createWatchHistoryRenderer({ - ...config, - fetchHistory: async () => ({ items: [], metadata: {} }), - snapshot: async () => ({}), - }), - services: { - nostrClient: { sessionActor: { pubkey: defaultActorHex } }, - getCurrentUserNpub: () => defaultActorNpub, - }, - }); - - await controller.load(); - applyDesignSystemAttributes(document); - - controller.refreshAdminPaneState = async () => {}; - controller.populateProfileRelays = () => {}; - controller.populateBlockedList = () => {}; - controller.refreshWalletPaneState = () => {}; - - controller.setActivePubkey(defaultActorHex); - - let cleanupRan = false; - const cleanup = () => { - try { - controller.hide({ silent: true }); - } catch {} - cleanupRan = true; - }; - - try { - await controller.show('history'); - await waitForAnimationFrame(window, 3); - - const toggle = document.getElementById('profileHistoryMetadataToggle'); - assert.ok(toggle); - assert.equal(toggle.getAttribute('aria-checked'), 'true'); - assert.equal(toggle.getAttribute('data-enabled'), 'true'); - - toggle.dispatchEvent( - new window.MouseEvent('click', { bubbles: true, cancelable: true }), - ); - await waitForAnimationFrame(window, 2); - - assert.equal(storedPreference, false); - assert.equal(toggle.getAttribute('aria-checked'), 'false'); - assert.equal(toggle.getAttribute('data-enabled'), 'false'); - assert.equal(clearCalls, 1); - - toggle.dispatchEvent( - new window.MouseEvent('click', { bubbles: true, cancelable: true }), - ); - await waitForAnimationFrame(window, 2); - - assert.equal(storedPreference, true); - assert.equal(toggle.getAttribute('aria-checked'), 'true'); - assert.equal(toggle.getAttribute('data-enabled'), 'true'); - } finally { - cleanup(); - } - - t.after(() => { - if (!cleanupRan) { - cleanup(); - } - }); -});