From 93c88704e0a49a405faf4b5b86a31d35ac329f3c Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 13:11:39 +0000 Subject: [PATCH 1/3] Fix watch history hydration by prioritizing readRelays The `createWatchHistoryHydrationStage` in the feed engine was incorrectly relying solely on `nostrClient.relays` for network fetches. This could cause hydration failures if `relays` contained write-only servers or was otherwise restricted. This commit updates the logic to prioritize `nostrClient.readRelays` if available, falling back to `nostrClient.relays`. This ensures that hydration attempts query the correct relays configured for reading data. Fixes an issue where watch history items would display as "Untitled video" due to failed metadata resolution. --- js/feedEngine/watchHistoryFeed.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/js/feedEngine/watchHistoryFeed.js b/js/feedEngine/watchHistoryFeed.js index a1860609..4f5dc2bb 100644 --- a/js/feedEngine/watchHistoryFeed.js +++ b/js/feedEngine/watchHistoryFeed.js @@ -318,11 +318,18 @@ function createWatchHistoryHydrationStage() { const uniqueIds = Array.from(new Set(missing)); const uniqueAddresses = Array.from(new Set(missingAddresses)); - const relays = + const readRelays = + Array.isArray(nostrClient?.readRelays) && nostrClient.readRelays.length + ? nostrClient.readRelays + : null; + + const fallbackRelays = Array.isArray(nostrClient?.relays) && nostrClient.relays.length ? nostrClient.relays : null; + const relays = readRelays || fallbackRelays; + if (!relays || !nostrClient?.pool) { return items; } From b03d96f041772c6f1cbf5d133f6179eddb0f46a6 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 13:18:32 +0000 Subject: [PATCH 2/3] Add verbose logging and fix relay selection for watch history hydration The watch history hydration stage was failing silently when relay connections were unstable or misconfigured. This commit: 1. Replaces the conditional debug logger with `devLogger.info` and `devLogger.warn` to ensure visibility of critical hydration steps. 2. Adds detailed logging for item counts, missing ID/address lists, filter generation, and fetch results. 3. Updates the relay selection logic to prioritize `nostrClient.readRelays` over `nostrClient.relays`. This ensures that hydration attempts query the user's specific read-configured relays, which is crucial for fetching metadata when generic relays may be write-only or restricted. These changes aid in diagnosing "Untitled video" issues and improve the robustness of the metadata fetch process. --- js/feedEngine/watchHistoryFeed.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/js/feedEngine/watchHistoryFeed.js b/js/feedEngine/watchHistoryFeed.js index 4f5dc2bb..c1d42452 100644 --- a/js/feedEngine/watchHistoryFeed.js +++ b/js/feedEngine/watchHistoryFeed.js @@ -9,6 +9,7 @@ import { isWatchHistoryDebugEnabled, logWatchHistoryDebug, } from "../watchHistoryDebug.js"; +import { devLogger } from "../utils/logger.js"; import { createBlacklistFilterStage, createWatchHistorySuppressionStage, @@ -18,11 +19,11 @@ import { isPlainObject } from "./utils.js"; const WATCH_HISTORY_FEED_LOG_NAMESPACE = "watchHistoryFeed"; function debugInfo(message, details) { - logWatchHistoryDebug(WATCH_HISTORY_FEED_LOG_NAMESPACE, "info", message, details); + devLogger.info(`[${WATCH_HISTORY_FEED_LOG_NAMESPACE}] ${message}`, details || ""); } function debugWarn(message, details) { - logWatchHistoryDebug(WATCH_HISTORY_FEED_LOG_NAMESPACE, "warn", message, details); + devLogger.warn(`[${WATCH_HISTORY_FEED_LOG_NAMESPACE}] ${message}`, details || ""); } function normalizeActorCandidate(...values) { @@ -249,11 +250,13 @@ function checkLocalCacheForVideo(pointer) { function createWatchHistoryHydrationStage() { return async function watchHistoryHydrationStage(items = [], context = {}) { + debugInfo(`Starting hydration stage for ${items.length} items.`); const missing = []; const missingAddresses = []; const idMap = new Map(); const itemsToHydrate = items.filter(item => !item.video && item.pointer); + debugInfo(`Items needing hydration: ${itemsToHydrate.length}`); let addressScanNeeded = false; // First pass: try to resolve from cache for IDs, and collect addresses @@ -312,12 +315,15 @@ function createWatchHistoryHydrationStage() { } if (!missing.length && !missingAddresses.length) { + debugInfo("No missing items to fetch from relays."); return items; } const uniqueIds = Array.from(new Set(missing)); const uniqueAddresses = Array.from(new Set(missingAddresses)); + debugInfo(`Fetching from relays. Missing IDs: ${uniqueIds.length}, Missing Addresses: ${uniqueAddresses.length}`); + const readRelays = Array.isArray(nostrClient?.readRelays) && nostrClient.readRelays.length ? nostrClient.readRelays @@ -330,7 +336,10 @@ function createWatchHistoryHydrationStage() { const relays = readRelays || fallbackRelays; + debugInfo("Selected relays for hydration:", relays); + if (!relays || !nostrClient?.pool) { + debugWarn("Aborting hydration: No relays or pool available."); return items; } @@ -361,9 +370,13 @@ function createWatchHistoryHydrationStage() { } } + debugInfo("Generated filters:", filters); + try { const events = await nostrClient.pool.list(relays, filters); + debugInfo(`Hydration fetch returned ${events.length} events.`); + let matchCount = 0; for (const event of events) { if (!event) { continue; @@ -380,6 +393,7 @@ function createWatchHistoryHydrationStage() { if (target.metadata) { target.metadata.video = video; } + matchCount++; } } } @@ -395,6 +409,7 @@ function createWatchHistoryHydrationStage() { if (target.metadata) { target.metadata.video = video; } + matchCount++; } } } @@ -403,6 +418,7 @@ function createWatchHistoryHydrationStage() { debugWarn(`[watchHistoryFeed] Failed to convert event ${event.id}:`, err); } } + debugInfo(`Hydrated ${matchCount} items from fetched events.`); } catch (error) { debugWarn("[watchHistoryFeed] Hydration fetch failed:", error); } From 386ddea44af1845cf93a2e50643bbcdd92b93142 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 13:30:51 +0000 Subject: [PATCH 3/3] Enhance watch history hydration with relay hints and verbose logging The watch history hydration process was sometimes failing to retrieve video metadata, resulting in "Untitled video" cards. This was primarily due to only querying the generic relay list and potentially missing content hosted on specific relays. This commit improves the robustness of `createWatchHistoryHydrationStage` in `js/feedEngine/watchHistoryFeed.js` by: 1. Collecting unique relay hints from the pointer objects (`item.pointer.relay`) of items needing hydration. 2. Merging these hints with the user's `readRelays` (or fallback `relays`) list. This ensures that if a specific video is known to exist on a niche relay, that relay is queried during the metadata fetch. 3. Replacing conditional debug logging with `devLogger.info` and `devLogger.warn` to provide detailed insights into the hydration process (item counts, selected relays, generated filters, and fetch results), aiding in future diagnosis. These changes ensure the application attempts to fetch metadata from all relevant sources indicated by the history data itself. --- js/feedEngine/watchHistoryFeed.js | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/js/feedEngine/watchHistoryFeed.js b/js/feedEngine/watchHistoryFeed.js index c1d42452..098b1ea8 100644 --- a/js/feedEngine/watchHistoryFeed.js +++ b/js/feedEngine/watchHistoryFeed.js @@ -334,11 +334,23 @@ function createWatchHistoryHydrationStage() { ? nostrClient.relays : null; - const relays = readRelays || fallbackRelays; + const baseRelays = readRelays || fallbackRelays || []; + const relayHints = new Set(); - debugInfo("Selected relays for hydration:", relays); + for (const item of itemsToHydrate) { + if (item.pointer && typeof item.pointer.relay === "string") { + const hint = item.pointer.relay.trim(); + if (hint) { + relayHints.add(hint); + } + } + } - if (!relays || !nostrClient?.pool) { + const mergedRelays = Array.from(new Set([...baseRelays, ...relayHints])); + + debugInfo("Selected relays for hydration:", mergedRelays); + + if (!mergedRelays.length || !nostrClient?.pool) { debugWarn("Aborting hydration: No relays or pool available."); return items; } @@ -370,10 +382,10 @@ function createWatchHistoryHydrationStage() { } } - debugInfo("Generated filters:", filters); + debugInfo("Generated filters:", JSON.stringify(filters)); try { - const events = await nostrClient.pool.list(relays, filters); + const events = await nostrClient.pool.list(mergedRelays, filters); debugInfo(`Hydration fetch returned ${events.length} events.`); let matchCount = 0;