diff --git a/js/feedEngine/watchHistoryFeed.js b/js/feedEngine/watchHistoryFeed.js index 3af85393..407f082d 100644 --- a/js/feedEngine/watchHistoryFeed.js +++ b/js/feedEngine/watchHistoryFeed.js @@ -3,7 +3,6 @@ import { pointerKey, normalizePointerInput } from "../nostr/watchHistory.js"; import watchHistoryService from "../watchHistoryService.js"; import nostrService from "../services/nostrService.js"; -import { createWatchHistoryMetadataResolver } from "../watchHistoryMetadata.js"; import { isWatchHistoryDebugEnabled, logWatchHistoryDebug, @@ -203,15 +202,8 @@ function createWatchHistorySource({ service = watchHistoryService } = {}) { }; } -function createWatchHistoryHydratorStage({ - service = watchHistoryService, - metadataResolver, -} = {}) { - const resolver = metadataResolver || createWatchHistoryMetadataResolver(); - +function createWatchHistoryHydratorStage({ service = watchHistoryService } = {}) { return async function watchHistoryHydratorStage(items = [], context = {}) { - const shouldStoreMetadata = false; - const results = []; for (const item of Array.isArray(items) ? items : []) { @@ -224,150 +216,12 @@ function createWatchHistoryHydratorStage({ ? item.pointerKey : pointerKey(item.pointer); - const pointerType = - typeof item?.pointer?.type === "string" ? item.pointer.type : null; - const pointerValue = - typeof item?.pointer?.value === "string" ? item.pointer.value : null; - const watchedAtValue = Number.isFinite(item?.watchedAt) ? Math.max(0, Math.floor(Number(item.watchedAt))) : Number.isFinite(item?.metadata?.watchedAt) ? Math.max(0, Math.floor(Number(item.metadata.watchedAt))) : 0; - let video = item.video || null; - let profile = item.metadata?.profile || null; - - if (isWatchHistoryDebugEnabled()) { - debugInfo("Hydrating watch history item.", { - pointerKey: pointerKeyValue || null, - pointerType, - pointerValue, - hasInitialVideo: Boolean(video), - hasInitialProfile: Boolean(profile), - watchedAt: watchedAtValue || null, - }); - } - - if (pointerKeyValue && typeof service?.getLocalMetadata === "function") { - try { - const stored = service.getLocalMetadata(pointerKeyValue); - if (stored) { - const hadVideo = Boolean(video); - const hadProfile = Boolean(profile); - video = video || stored.video || null; - profile = profile || stored.profile || null; - if (isWatchHistoryDebugEnabled()) { - debugInfo("Loaded cached metadata for pointer.", { - pointerKey: pointerKeyValue, - hadVideo, - hadProfile, - cachedVideo: Boolean(stored.video), - cachedProfile: Boolean(stored.profile), - resultingVideo: Boolean(video), - resultingProfile: Boolean(profile), - }); - } - } - } catch (error) { - context?.log?.( - "[watch-history-feed] Failed to read cached metadata", - error, - ); - if (isWatchHistoryDebugEnabled()) { - debugWarn("Failed to read cached metadata for pointer.", { - pointerKey: pointerKeyValue || null, - error, - }); - } - } - } - - if (!video && resolver?.resolveVideo) { - if (isWatchHistoryDebugEnabled()) { - debugInfo("Resolving video via metadata resolver.", { - pointerKey: pointerKeyValue || null, - pointerType, - pointerValue, - }); - } - try { - video = await resolver.resolveVideo(item.pointer); - if (isWatchHistoryDebugEnabled()) { - if (video) { - debugInfo("Metadata resolver returned video candidate.", { - pointerKey: pointerKeyValue || null, - videoId: typeof video.id === "string" ? video.id : null, - title: - typeof video.title === "string" && video.title.trim() - ? video.title - : null, - invalid: Boolean(video.invalid), - }); - } else { - debugWarn("Metadata resolver returned no video candidate.", { - pointerKey: pointerKeyValue || null, - }); - } - } - } catch (error) { - context?.log?.( - "[watch-history-feed] Failed to resolve video from pointer", - error, - ); - if (isWatchHistoryDebugEnabled()) { - debugWarn("Metadata resolver threw while resolving video.", { - pointerKey: pointerKeyValue || null, - error, - }); - } - } - } - - if (video?.pubkey && !profile && resolver?.resolveProfile) { - if (isWatchHistoryDebugEnabled()) { - debugInfo("Resolving profile for video author.", { - pointerKey: pointerKeyValue || null, - pubkey: video.pubkey, - }); - } - try { - profile = resolver.resolveProfile(video.pubkey) || null; - if (isWatchHistoryDebugEnabled()) { - if (profile) { - const label = - profile.display_name || - profile.name || - profile.username || - profile.nip05 || - null; - debugInfo("Resolved profile for video author.", { - pointerKey: pointerKeyValue || null, - pubkey: video.pubkey, - label: label || null, - }); - } else { - debugWarn("Resolver returned no profile for video author.", { - pointerKey: pointerKeyValue || null, - pubkey: video.pubkey, - }); - } - } - } catch (error) { - context?.log?.( - "[watch-history-feed] Failed to resolve profile for pointer", - error, - ); - if (isWatchHistoryDebugEnabled()) { - debugWarn("Profile resolver threw while hydrating metadata.", { - pointerKey: pointerKeyValue || null, - pubkey: video.pubkey, - error, - }); - } - } - } - const metadata = { ...(isPlainObject(item.metadata) ? item.metadata : {}), pointerKey: pointerKeyValue, @@ -375,88 +229,15 @@ function createWatchHistoryHydratorStage({ resumeAt: item.metadata?.resumeAt ?? null, completed: item.metadata?.completed ?? false, session: item.metadata?.session === true, - video: video || null, - profile: profile || null, }; - if (isWatchHistoryDebugEnabled()) { - const summary = { - pointerKey: pointerKeyValue || null, - pointerType, - pointerValue, - watchedAt: watchedAtValue || null, - videoId: typeof video?.id === "string" ? video.id : null, - title: - typeof video?.title === "string" && video.title?.trim() - ? video.title - : null, - invalid: Boolean(video?.invalid), - hasProfile: Boolean(profile), - }; - if (video) { - debugInfo("Hydrated watch history entry metadata.", summary); - } else { - debugWarn("Hydrated entry is missing resolved video metadata.", summary); - } - } - const nextItem = { ...item, pointerKey: pointerKeyValue, watchedAt: watchedAtValue, - video: video || null, metadata, }; results.push(nextItem); - - if (pointerKeyValue) { - if (shouldStoreMetadata) { - try { - service?.setLocalMetadata?.(pointerKeyValue, { - video: metadata.video, - profile: metadata.profile, - }); - if (isWatchHistoryDebugEnabled()) { - debugInfo("Stored metadata cache entry for pointer.", { - pointerKey: pointerKeyValue, - cachedVideo: Boolean(metadata.video), - cachedProfile: Boolean(metadata.profile), - }); - } - } catch (error) { - context?.log?.( - "[watch-history-feed] Failed to persist metadata cache", - error, - ); - if (isWatchHistoryDebugEnabled()) { - debugWarn("Failed to persist metadata cache entry.", { - pointerKey: pointerKeyValue, - error, - }); - } - } - } else if (typeof service?.removeLocalMetadata === "function") { - try { - service.removeLocalMetadata(pointerKeyValue); - if (isWatchHistoryDebugEnabled()) { - debugInfo("Cleared metadata cache entry for pointer.", { - pointerKey: pointerKeyValue, - }); - } - } catch (error) { - context?.log?.( - "[watch-history-feed] Failed to clear cached metadata", - error, - ); - if (isWatchHistoryDebugEnabled()) { - debugWarn("Failed to clear cached metadata entry.", { - pointerKey: pointerKeyValue, - error, - }); - } - } - } - } } return results; @@ -484,11 +265,8 @@ function createWatchHistorySorter() { export function createWatchHistoryFeedDefinition({ service = watchHistoryService, nostr = nostrService, - metadataResolverFactory = createWatchHistoryMetadataResolver, shouldIncludeVideo, } = {}) { - const metadataResolver = metadataResolverFactory({ nostr }); - const blacklistStage = createBlacklistFilterStage({ shouldIncludeVideo: typeof shouldIncludeVideo === "function" @@ -502,7 +280,7 @@ export function createWatchHistoryFeedDefinition({ return { source: createWatchHistorySource({ service }), stages: [ - createWatchHistoryHydratorStage({ service, metadataResolver }), + createWatchHistoryHydratorStage({ service }), blacklistStage, createWatchHistorySuppressionStage(), ], diff --git a/js/nostr/watchHistory.js b/js/nostr/watchHistory.js index d6a61453..5932242c 100644 --- a/js/nostr/watchHistory.js +++ b/js/nostr/watchHistory.js @@ -39,42 +39,6 @@ const WATCH_HISTORY_ENCRYPTION_FALLBACK_ORDER = Object.freeze([ "nip04", ]); -function cloneVideoMetadata(video) { - if (!video || typeof video !== "object") { - return null; - } - try { - return JSON.parse(JSON.stringify(video)); - } catch (error) { - devLogger.warn("[nostr] Failed to clone watch history video metadata", error); - return { ...video }; - } -} - -function cloneProfileMetadata(profile) { - if (!profile || typeof profile !== "object") { - return null; - } - try { - return JSON.parse(JSON.stringify(profile)); - } catch (error) { - devLogger.warn("[nostr] Failed to clone watch history profile metadata", error); - return { ...profile }; - } -} - -function clonePointerMetadata(metadata) { - if (!metadata || typeof metadata !== "object") { - return null; - } - try { - return JSON.parse(JSON.stringify(metadata)); - } catch (error) { - devLogger.warn("[nostr] Failed to clone watch history pointer metadata", error); - return { ...metadata }; - } -} - function mergePointerDetails(target, source) { if (!target || typeof target !== "object" || !source || typeof source !== "object") { return target; @@ -96,19 +60,6 @@ function mergePointerDetails(target, source) { target.relay = source.relay.trim(); } } - if (!target.metadata && source.metadata && typeof source.metadata === "object") { - target.metadata = clonePointerMetadata(source.metadata); - } else if (target.metadata && source.metadata) { - const merged = clonePointerMetadata(target.metadata) || {}; - const additional = clonePointerMetadata(source.metadata) || {}; - target.metadata = { ...merged, ...additional }; - } - if (!target.video && source.video) { - target.video = cloneVideoMetadata(source.video); - } - if (!target.profile && source.profile) { - target.profile = cloneProfileMetadata(source.profile); - } return target; } @@ -146,29 +97,6 @@ function clonePointerItem(pointer) { cloned.session = true; } - const metadata = clonePointerMetadata(pointer.metadata); - if (metadata) { - cloned.metadata = metadata; - } - - const video = cloneVideoMetadata(pointer.video) || metadata?.video || null; - if (video) { - cloned.video = video; - } - - const profile = cloneProfileMetadata(pointer.profile) || metadata?.profile || null; - if (profile) { - cloned.profile = profile; - } - - if (pointer.completed === true || metadata?.completed === true) { - cloned.completed = true; - } - - if (!Number.isFinite(cloned.resumeAt) && Number.isFinite(metadata?.resumeAt)) { - cloned.resumeAt = metadata.resumeAt; - } - return cloned; } @@ -544,15 +472,7 @@ function canonicalizeWatchHistoryItems(rawItems, maxItems = WATCH_HISTORY_MAX_IT } function sanitizeWatchHistoryMetadata(metadata) { - if (!metadata || typeof metadata !== "object") { - return {}; - } - try { - return JSON.parse(JSON.stringify(metadata)); - } catch (error) { - devLogger.warn("[nostr] Failed to sanitize watch history metadata:", error); - return {}; - } + return {}; } function serializeWatchHistoryItems(items) { @@ -580,47 +500,14 @@ function serializeWatchHistoryItems(items) { if (watchedAt !== undefined) { payload.watchedAt = watchedAt; } - const metadata = clonePointerMetadata(item?.metadata); - if (metadata) { - payload.metadata = metadata; - } - const video = metadata?.video || cloneVideoMetadata(item?.video); - if (video) { - if (payload.metadata) { - payload.metadata.video = payload.metadata.video || video; - } else { - payload.video = video; - } - } - const profile = metadata?.profile || cloneProfileMetadata(item?.profile); - if (profile) { - if (payload.metadata) { - payload.metadata.profile = payload.metadata.profile || profile; - } else { - payload.profile = profile; - } - } const resumeAt = Number.isFinite(item?.resumeAt) ? Math.max(0, Math.floor(item.resumeAt)) : undefined; if (resumeAt !== undefined) { - if (payload.metadata) { - if (!Number.isFinite(payload.metadata.resumeAt)) { - payload.metadata.resumeAt = resumeAt; - } - } else { - payload.resumeAt = resumeAt; - } + payload.resumeAt = resumeAt; } if (item?.completed === true) { - if (payload.metadata) { - payload.metadata.completed = true; - } else { - payload.completed = true; - } - } - if (payload.metadata && !Object.keys(payload.metadata).length) { - delete payload.metadata; + payload.completed = true; } return payload; }) @@ -1106,7 +993,6 @@ class WatchHistoryManager { items = Object.values(records).flat(); } - const metadata = sanitizeWatchHistoryMetadata(entry?.metadata); const snapshotId = typeof entry?.snapshotId === "string" ? entry.snapshotId : ""; const fingerprint = typeof entry?.fingerprint === "string" ? entry.fingerprint : ""; sanitizedActors[actorKey] = { @@ -1119,7 +1005,7 @@ class WatchHistoryManager { savedAt, records, items, - metadata, + metadata: {}, }; } const storage = { @@ -1169,7 +1055,6 @@ class WatchHistoryManager { records = canonicalizeWatchHistoryItems(entry.items, WATCH_HISTORY_MAX_ITEMS); } - const metadata = sanitizeWatchHistoryMetadata(entry.metadata); const snapshotId = typeof entry.snapshotId === "string" ? entry.snapshotId : ""; const fingerprint = typeof entry.fingerprint === "string" ? entry.fingerprint : ""; const savedAt = Number.isFinite(entry.savedAt) && entry.savedAt > 0 ? entry.savedAt : now; @@ -1183,7 +1068,7 @@ class WatchHistoryManager { fingerprint, savedAt, records, - metadata, + metadata: {}, }; mutated = true; } @@ -1362,17 +1247,13 @@ class WatchHistoryManager { return fetchResult; } const storageEntry = this.getStorage().actors?.[actorKey]; - const metadata = sanitizeWatchHistoryMetadata(storageEntry?.metadata); - const alreadyAttempted = metadata.autoSnapshotAttempted === true; const records = storageEntry?.records || {}; const flatItems = Object.values(records).flat(); - if (!flatItems.length || alreadyAttempted) { + if (!flatItems.length) { return fetchResult; } - metadata.autoSnapshotAttempted = true; - metadata.autoSnapshotAttemptedAt = Date.now(); // We publish all records const publishResult = await this.publishRecords(records, { @@ -1390,7 +1271,7 @@ class WatchHistoryManager { chunkEvents: [], savedAt: Date.now(), fingerprint, - metadata, + metadata: {}, }; this.cache.set(actorKey, entry); this.persistEntry(actorKey, entry); @@ -1776,16 +1657,6 @@ class WatchHistoryManager { success: !!publishResult.ok, retryable: !!publishResult.retryable, }); - const metadata = sanitizeWatchHistoryMetadata(cachedEntry.metadata); - metadata.updatedAt = Date.now(); - metadata.status = publishResult.ok ? "ok" : "error"; - metadata.lastPublishResults = publishResult.publishResults; // This will likely be array or complex obj - metadata.skippedCount = publishResult.skippedCount || 0; - if (!publishResult.ok) { - metadata.lastError = publishResult.error || "publish-failed"; - } else { - delete metadata.lastError; - } const entry = { actor: resolvedActor, records, @@ -1794,7 +1665,7 @@ class WatchHistoryManager { pointerEvent: publishResult.pointerEvent || cachedEntry.pointerEvent || null, savedAt: Date.now(), fingerprint, - metadata, + metadata: {}, }; this.cache.set(actorKey, entry); this.persistEntry(actorKey, entry); @@ -1938,7 +1809,7 @@ class WatchHistoryManager { pointerEvent: null, savedAt: now, fingerprint, - metadata: sanitizeWatchHistoryMetadata(storageEntry?.metadata), + metadata: {}, }; this.cache.set(actorKey, entry); this.persistEntry(actorKey, entry); @@ -2094,10 +1965,6 @@ class WatchHistoryManager { resolvedActor, records, ); - const metadata = sanitizeWatchHistoryMetadata( - this.getStorage().actors?.[actorKey]?.metadata, - ); - metadata.lastFetchedAt = now; const entry = { actor: resolvedActor, @@ -2107,7 +1974,7 @@ class WatchHistoryManager { pointerEvent: latestEvent, savedAt: now, fingerprint, - metadata, + metadata: {}, }; this.cache.set(actorKey, entry); this.persistEntry(actorKey, entry); @@ -2196,7 +2063,7 @@ class WatchHistoryManager { pointerEvent: fetchResult.pointerEvent || null, savedAt: Date.now(), fingerprint, - metadata: sanitizeWatchHistoryMetadata(storage.actors?.[actorKey]?.metadata), + metadata: {}, }; this.cache.set(actorKey, entry); this.persistEntry(actorKey, entry); diff --git a/js/services/watchHistoryTelemetry.js b/js/services/watchHistoryTelemetry.js index fd07bb9d..bb2c6158 100644 --- a/js/services/watchHistoryTelemetry.js +++ b/js/services/watchHistoryTelemetry.js @@ -72,7 +72,6 @@ export default class WatchHistoryTelemetry { ? Math.max(0, viewThresholdSeconds) : DEFAULT_VIEW_THRESHOLD_SECONDS; - this.watchHistoryMetadataEnabled = false; this.watchHistoryPreferenceUnsubscribe = null; this.playbackTelemetryState = null; this.loggedViewPointerKeys = new Set(); @@ -81,17 +80,14 @@ export default class WatchHistoryTelemetry { destroy() { this.resetPlaybackLoggingState(); this._clearPreferenceSubscription(); - this.watchHistoryMetadataEnabled = false; } async initPreferenceSync() { - this.watchHistoryMetadataEnabled = false; return null; } refreshPreferenceSettings() { - this.watchHistoryMetadataEnabled = false; - return this.watchHistoryMetadataEnabled; + return false; } isMetadataPreferenceEnabled() { @@ -99,54 +95,11 @@ export default class WatchHistoryTelemetry { } persistMetadataForVideo(video, pointerInfo) { - if ( - !this.watchHistoryMetadataEnabled || - !pointerInfo || - !pointerInfo.key || - typeof this.watchHistoryService?.setLocalMetadata !== "function" - ) { - return; - } - - if (!video || typeof video !== "object") { - return; - } - - const metadata = { - // Persist only sanitized metadata so local history never stores playback - // transports or magnet fingerprints. - video: sanitizeVideoMetadata(video), - }; - - try { - this.watchHistoryService.setLocalMetadata(pointerInfo.key, metadata); - } catch (error) { - devLogger.warn( - "[watchHistoryTelemetry] Failed to persist local metadata for pointer:", - pointerInfo.key, - error, - ); - } + // Deprecated } dropMetadata(pointerKey) { - if (!pointerKey || typeof pointerKey !== "string") { - return; - } - - if (typeof this.watchHistoryService?.removeLocalMetadata !== "function") { - return; - } - - try { - this.watchHistoryService.removeLocalMetadata(pointerKey); - } catch (error) { - devLogger.warn( - "[watchHistoryTelemetry] Failed to remove cached metadata for pointer:", - pointerKey, - error, - ); - } + // Deprecated } async handleRemoval(payload = {}) { @@ -272,58 +225,11 @@ export default class WatchHistoryTelemetry { try { const canUseWatchHistoryService = typeof this.watchHistoryService?.publishView === "function"; - const resolveWatchActor = () => { - const normalizedUser = this.normalizeHexPubkey( - this.getActiveUserPubkey(), - ); - if (normalizedUser) { - return normalizedUser; - } - - const normalizedClient = this.normalizeHexPubkey( - this.nostrClient?.pubkey, - ); - if (normalizedClient) { - return normalizedClient; - } - - const normalizedSession = this.normalizeHexPubkey( - this.nostrClient?.sessionActor?.pubkey, - ); - if (normalizedSession) { - return normalizedSession; - } - - if (normalizeString(this.nostrClient?.pubkey)) { - return normalizeString(this.nostrClient.pubkey).toLowerCase(); - } - - if (normalizeString(this.nostrClient?.sessionActor?.pubkey)) { - return normalizeString( - this.nostrClient.sessionActor.pubkey, - ).toLowerCase(); - } - - return ""; - }; - - const activeWatchActor = resolveWatchActor(); - const watchMetadata = {}; - if (activeWatchActor) { - watchMetadata.actor = activeWatchActor; - } - if (state.videoMetadata) { - watchMetadata.video = state.videoMetadata; - } - const metadataPayload = Object.keys(watchMetadata).length - ? watchMetadata - : undefined; if (canUseWatchHistoryService) { viewResult = await this.watchHistoryService.publishView( thresholdPointer, undefined, - metadataPayload, ); } else if (typeof this.nostrClient?.recordVideoView === "function") { viewResult = await this.nostrClient.recordVideoView( diff --git a/js/watchHistoryMetadata.js b/js/watchHistoryMetadata.js deleted file mode 100644 index 0e573066..00000000 --- a/js/watchHistoryMetadata.js +++ /dev/null @@ -1,306 +0,0 @@ -// js/watchHistoryMetadata.js - -import { getApplication } from "./applicationContext.js"; -import nostrService from "./services/nostrService.js"; -import { nostrClient } from "./nostrClientFacade.js"; -import { logWatchHistoryDebug } from "./watchHistoryDebug.js"; - -function logInfo(message, details) { - logWatchHistoryDebug("watchHistoryMetadata", "info", message, details); -} - -function logWarn(message, details) { - logWatchHistoryDebug("watchHistoryMetadata", "warn", message, details); -} - -function normalizeVideoCandidate(candidate) { - if (!candidate || typeof candidate !== "object") { - logInfo("Discarded non-object video candidate while normalizing."); - return null; - } - if (candidate.deleted) { - logInfo("Discarded deleted video candidate while normalizing.", { - eventId: candidate.id || null, - }); - return null; - } - return candidate; -} - -export function createWatchHistoryMetadataResolver({ - appResolver = getApplication, - nostr = nostrService, - client = nostrClient, -} = {}) { - const caches = { - activeVideos: null, - catalogPromise: null, - }; - - function getAppInstance() { - try { - return typeof appResolver === "function" ? appResolver() : getApplication(); - } catch (error) { - logWarn("Failed to resolve application instance.", { error }); - return null; - } - } - - async function resolveVideo(pointer) { - if (!pointer || typeof pointer !== "object") { - logInfo("Received invalid pointer when resolving video metadata.", { - pointerType: typeof pointer, - }); - return null; - } - - const app = getAppInstance(); - const type = pointer.type === "a" ? "a" : "e"; - const value = typeof pointer.value === "string" ? pointer.value.trim() : ""; - logInfo("Attempting to resolve watch history pointer.", { - pointerType: type, - pointerValue: value || null, - hasAppInstance: Boolean(app), - }); - if (!value) { - logInfo("Pointer value was empty after trimming. Skipping resolution."); - return null; - } - - if (type === "e") { - const fromAppCache = app?.videosMap?.get?.(value); - const normalizedAppCache = normalizeVideoCandidate(fromAppCache); - if (normalizedAppCache) { - logInfo("Resolved pointer from application videos map.", { - pointerValue: value, - }); - return normalizedAppCache; - } - - const fromClientCache = client?.allEvents instanceof Map - ? client.allEvents.get(value) - : null; - const normalizedClientCache = normalizeVideoCandidate(fromClientCache); - if (normalizedClientCache) { - logInfo("Resolved pointer from nostr client event cache.", { - pointerValue: value, - }); - return normalizedClientCache; - } - - if (typeof app?.getOldEventById === "function") { - try { - const fetched = await app.getOldEventById(value); - const normalized = normalizeVideoCandidate(fetched); - if (normalized) { - logInfo("Resolved pointer via app.getOldEventById.", { - pointerValue: value, - }); - return normalized; - } - } catch (error) { - logWarn("Failed to fetch old event by id.", { - pointerValue: value, - error, - }); - } - } - - if (typeof client?.getEventById === "function") { - try { - const fetched = await client.getEventById(value); - const normalized = normalizeVideoCandidate(fetched); - if (normalized) { - logInfo("Resolved pointer via nostrClient.getEventById.", { - pointerValue: value, - }); - return normalized; - } - } catch (error) { - logWarn("Failed to fetch event by id via nostrClient.", { - pointerValue: value, - error, - }); - } - } - - logInfo("Failed to resolve pointer by event id.", { pointerValue: value }); - return null; - } - - const compareAddress = (candidate) => { - if (!candidate || typeof candidate !== "object") { - return false; - } - if (typeof app?.getVideoAddressPointer !== "function") { - return false; - } - try { - const address = app.getVideoAddressPointer(candidate); - const isMatch = typeof address === "string" && address.trim() === value; - if (isMatch) { - logInfo("Candidate matched address pointer.", { - pointerValue: value, - candidateId: candidate.id || null, - }); - } - return isMatch; - } catch (error) { - logWarn("Failed to compute address pointer for candidate.", { - pointerValue: value, - error, - }); - return false; - } - }; - - if (app?.videosMap instanceof Map) { - for (const cached of app.videosMap.values()) { - if (compareAddress(cached)) { - const normalized = normalizeVideoCandidate(cached); - if (normalized) { - return normalized; - } - } - } - } - - const blacklist = app?.blacklistedEventIds instanceof Set - ? app.blacklistedEventIds - : undefined; - const filterOptions = { - blacklistedEventIds: blacklist, - isAuthorBlocked: (pubkey) => { - try { - return ( - typeof app?.isAuthorBlocked === "function" && - app.isAuthorBlocked(pubkey) - ); - } catch (error) { - logWarn("Failed to evaluate isAuthorBlocked callback.", { - pointerValue: value, - error, - }); - return false; - } - }, - }; - - if (!Array.isArray(caches.activeVideos)) { - try { - caches.activeVideos = nostr.getFilteredActiveVideos - ? nostr.getFilteredActiveVideos(filterOptions) - : []; - logInfo("Loaded active videos cache for pointer resolution.", { - pointerValue: value, - candidateCount: Array.isArray(caches.activeVideos) - ? caches.activeVideos.length - : 0, - }); - } catch (error) { - logWarn("Failed to read active videos cache.", { - pointerValue: value, - error, - }); - caches.activeVideos = []; - } - } - - for (const candidate of Array.isArray(caches.activeVideos) - ? caches.activeVideos - : []) { - if (compareAddress(candidate)) { - const normalized = normalizeVideoCandidate(candidate); - if (normalized) { - logInfo("Resolved pointer from active videos cache.", { - pointerValue: value, - candidateId: normalized.id || null, - }); - return normalized; - } - } - } - - if (!caches.catalogPromise && typeof nostr.fetchVideos === "function") { - logInfo("Fetching video catalog for pointer resolution.", { - pointerValue: value, - }); - caches.catalogPromise = nostr - .fetchVideos(filterOptions) - .catch((error) => { - logWarn("Failed to fetch video catalog while resolving pointer.", { - pointerValue: value, - error, - }); - return []; - }); - } - - if (caches.catalogPromise) { - try { - const catalog = await caches.catalogPromise; - for (const candidate of Array.isArray(catalog) ? catalog : []) { - if (compareAddress(candidate)) { - const normalized = normalizeVideoCandidate(candidate); - if (normalized) { - logInfo("Resolved pointer from fetched video catalog.", { - pointerValue: value, - candidateId: normalized.id || null, - }); - return normalized; - } - } - } - } catch (error) { - logWarn("Failed to resolve pointer from fetched catalog.", { - pointerValue: value, - error, - }); - } - } - - logInfo("Failed to resolve pointer after exhausting lookup strategies.", { - pointerValue: value, - }); - return null; - } - - function resolveProfile(pubkey) { - if (typeof pubkey !== "string" || !pubkey.trim()) { - return null; - } - const app = getAppInstance(); - if (!app) { - return null; - } - try { - const cacheEntry = - typeof app.getProfileCacheEntry === "function" - ? app.getProfileCacheEntry(pubkey) - : null; - if (cacheEntry && typeof cacheEntry === "object") { - logInfo("Resolved profile from application cache.", { pubkey }); - return cacheEntry.profile || null; - } - } catch (error) { - logWarn("Failed to read profile cache entry.", { pubkey, error }); - } - logInfo("Profile cache miss during watch history hydration.", { pubkey }); - return null; - } - - function reset() { - caches.activeVideos = null; - logInfo("Reset active videos cache for watch history metadata resolver."); - caches.catalogPromise = null; - logInfo("Reset catalog promise for watch history metadata resolver."); - } - - return { - resolveVideo, - resolveProfile, - reset, - caches, - }; -} - diff --git a/js/watchHistoryService.js b/js/watchHistoryService.js index 75ecc9d4..5471b995 100644 --- a/js/watchHistoryService.js +++ b/js/watchHistoryService.js @@ -698,22 +698,11 @@ async function updateFingerprintCache(actorKey, items) { return fingerprint; } -async function publishView(pointerInput, createdAt, metadata = {}) { +async function publishView(pointerInput, createdAt) { const recordOptions = {}; if (Number.isFinite(createdAt)) { recordOptions.created_at = createdAt; } - if (metadata && typeof metadata === "object") { - if (Array.isArray(metadata.additionalTags)) { - recordOptions.additionalTags = metadata.additionalTags; - } - if (Array.isArray(metadata.relays)) { - recordOptions.relays = metadata.relays; - } - if (metadata.content != null) { - recordOptions.content = metadata.content; - } - } const viewResult = await nostrClient.recordVideoView(pointerInput, recordOptions); @@ -722,7 +711,6 @@ async function publishView(pointerInput, createdAt, metadata = {}) { { pointer: pointerInput, createdAt, - hasMetadata: !!metadata, } ); @@ -736,7 +724,6 @@ async function publishView(pointerInput, createdAt, metadata = {}) { } const actorCandidate = - (typeof metadata.actor === "string" && metadata.actor.trim()) || viewResult?.event?.pubkey || nostrClient?.pubkey || nostrClient?.sessionActor?.pubkey || @@ -775,7 +762,6 @@ async function publishView(pointerInput, createdAt, metadata = {}) { viewResult?.event?.created_at ); - delete normalizedPointer.metadata; delete normalizedPointer.video; delete normalizedPointer.profile; delete normalizedPointer.resumeAt; diff --git a/tests/watch-history.test.mjs b/tests/watch-history.test.mjs index c611feb5..aa8dccd9 100644 --- a/tests/watch-history.test.mjs +++ b/tests/watch-history.test.mjs @@ -1160,7 +1160,6 @@ async function testWatchHistoryPartialRelayRetry() { await watchHistoryService.publishView( { type: "e", value: "partial-pointer" }, Math.floor(Date.now() / 1000), - { actor }, ); const queuedBefore = watchHistoryService.getQueuedPointers(actor); @@ -1359,7 +1358,6 @@ async function testWatchHistorySnapshotRetainsNewQueueEntriesDuringPublish() { await watchHistoryService.publishView( { type: "e", value: "inflight-initial" }, createdAt, - { actor }, ); const queuedBefore = watchHistoryService.getQueuedPointers(actor); @@ -1401,7 +1399,6 @@ async function testWatchHistorySnapshotRetainsNewQueueEntriesDuringPublish() { await watchHistoryService.publishView( { type: "e", value: "inflight-new" }, createdAt, - { actor }, ); const queuedDuring = watchHistoryService.getQueuedPointers(actor); @@ -1566,19 +1563,16 @@ async function testWatchHistoryServiceIntegration() { await watchHistoryService.publishView( { type: "e", value: "video-one" }, viewCreatedAt, - { actor, video: pointerVideo }, ); viewCreatedAt += 60; await watchHistoryService.publishView( { type: "e", value: "video-one" }, viewCreatedAt, - { actor, video: pointerVideo }, ); viewCreatedAt += 30; await watchHistoryService.publishView( { type: "a", value: "30023:pub:episode" }, viewCreatedAt, - { actor }, ); const queued = watchHistoryService.getQueuedPointers(actor); @@ -1823,7 +1817,6 @@ async function testWatchHistorySnapshotMergesQueuedWithCachedItems() { await watchHistoryService.publishView( { type: "e", value: "fresh-entry" }, 1_700_000_200, - { actor }, ); const mergeResult = await watchHistoryService.snapshot(null, { @@ -2366,7 +2359,6 @@ async function testWatchHistoryLocalFallbackWhenDisabled() { await watchHistoryService.publishView( { type: "e", value: "local-pointer" }, createdAt, - { actor }, ); const queued = watchHistoryService.getQueuedPointers(actor); diff --git a/tests/watchHistory/watch-history-telemetry.test.mjs b/tests/watchHistory/watch-history-telemetry.test.mjs index 41ca738e..9beaec5c 100644 --- a/tests/watchHistory/watch-history-telemetry.test.mjs +++ b/tests/watchHistory/watch-history-telemetry.test.mjs @@ -83,39 +83,10 @@ async function flushAsyncOperations() { } async function testMetadataPreferenceSync() { - let subscribeTopic = null; - let subscribeHandler = null; - let cleared = false; - - const { telemetry } = createTelemetry({ - serviceOverrides: { - subscribe: (topic, handler) => { - subscribeTopic = topic; - subscribeHandler = handler; - return () => {}; - }, - clearLocalMetadata: () => { - cleared = true; - }, - getSettings: () => ({ metadata: { storeLocally: true } }), - }, - options: { - getActiveUserPubkey: () => null, - }, - }); - + const { telemetry } = createTelemetry(); const unsubscribe = await telemetry.initPreferenceSync(); - assert.equal(subscribeTopic, "metadata-preference"); - assert.equal(typeof unsubscribe, "function"); - assert.equal(telemetry.isMetadataPreferenceEnabled(), true); - - subscribeHandler({ enabled: false }); + assert.equal(unsubscribe, null, "initPreferenceSync should no-op"); assert.equal(telemetry.isMetadataPreferenceEnabled(), false); - assert.equal(cleared, true); - - subscribeHandler({ enabled: true }); - assert.equal(telemetry.isMetadataPreferenceEnabled(), true); - } async function testPreparePlaybackLoggingPublishesOnce() { @@ -127,8 +98,8 @@ async function testPreparePlaybackLoggingPublishesOnce() { const { telemetry } = createTelemetry({ serviceOverrides: { - publishView: async (ptr, _relay, metadata) => { - publishCalls.push({ ptr, metadata }); + publishView: async (ptr, _relay) => { + publishCalls.push({ ptr }); return { ok: true, event: { pubkey: hexPubkey }, @@ -174,7 +145,7 @@ async function testPreparePlaybackLoggingPublishesOnce() { ); } -async function testPreparePlaybackLoggingIncludesVideoMetadata() { +async function testPreparePlaybackLoggingIgnoresMetadata() { const publishCalls = []; const pointer = { type: "e", value: "event-id" }; @@ -182,8 +153,8 @@ async function testPreparePlaybackLoggingIncludesVideoMetadata() { const { telemetry } = createTelemetry({ serviceOverrides: { - publishView: async (ptr, _relay, metadata) => { - publishCalls.push({ ptr, metadata }); + publishView: async (ptr, _relay) => { + publishCalls.push({ ptr }); return { ok: true, event: { pubkey: hexPubkey }, @@ -216,14 +187,7 @@ async function testPreparePlaybackLoggingIncludesVideoMetadata() { assert.equal(publishCalls.length, 1, "publishView should be invoked once"); assert.equal(publishCalls[0].ptr, pointer); - assert.ok(publishCalls[0].metadata, "metadata payload should exist"); - assert.deepEqual(publishCalls[0].metadata.video, { - id: "evt1", - title: "Example Video", - thumbnail: "https://example.com/thumb.jpg", - pubkey: hexPubkey, - created_at: 1_700_000_000, - }); + assert.equal(publishCalls[0].metadata, undefined, "metadata should NOT be passed"); } async function testCancelPlaybackLoggingFlushes() { @@ -254,7 +218,7 @@ async function testCancelPlaybackLoggingFlushes() { await testMetadataPreferenceSync(); await testPreparePlaybackLoggingPublishesOnce(); -await testPreparePlaybackLoggingIncludesVideoMetadata(); +await testPreparePlaybackLoggingIgnoresMetadata(); await testCancelPlaybackLoggingFlushes(); console.log("watch-history-telemetry tests passed");