diff --git a/mods/features/adblock.js b/mods/features/adblock.js index 18e8fa82..7ca59452 100644 --- a/mods/features/adblock.js +++ b/mods/features/adblock.js @@ -672,6 +672,15 @@ function processResponsePayload(payload, detectedPage) { hasMore: !!plc.continuations, }); } + // Same accumulator hook as the object-root handler above. Confirmed + // necessary on-device: three continuations arrived through that path, all + // with hasContinuation:true, and the FINAL batch (8 of 68 items, the one + // carrying no continuation token) only ever came through here. Without + // this the accumulator never sees a completion and full_cache.from_native + // never fires, so the slow re-download stays on the critical path. + // Must run BEFORE filterContinuationItems, which reduces an all-watched + // batch to the single kept helper. + noteContinuationBatch(String(window.location?.hash || ''), plc.contents, !!plc?.continuations); plc.contents = filterContinuationItems(plc.contents, detectedPage, !!plc?.continuations, 'arrayPayload.playlist.continuation'); } const arrayTopPlaylistRenderer = payload?.contents?.tvBrowseRenderer?.content?.tvSurfaceContentRenderer?.content?.twoColumnRenderer?.rightColumn?.playlistVideoListRenderer; diff --git a/mods/features/playlistBatchCollect.js b/mods/features/playlistBatchCollect.js index 6e8d175a..0c6ac422 100644 --- a/mods/features/playlistBatchCollect.js +++ b/mods/features/playlistBatchCollect.js @@ -94,7 +94,7 @@ * Load → "Load All at Once". */ -import { appendFileOnlyLog } from './hideWatched.js'; +import { appendFileOnlyLog, getItemVideoId } from './hideWatched.js'; import { configRead } from '../config.js'; function _log(label, payload) { @@ -490,11 +490,20 @@ export function noteContinuationBatch(key, contents, hasMore) { if (!Array.isArray(contents) || !contents.length) return; if (getCachedFullPlaylist(key)) return; // already complete; this is the reloaded pass + // Dedupe by video id. Two handlers feed this (object-root and array-root), + // and a batch that reached both would otherwise be counted twice, putting + // duplicate tiles in the cached playlist. + if (!window.__ttContinuationAcc || window.__ttContinuationAcc.key !== key) { + window.__ttContinuationAcc = { key, contents: [], seen: {} }; + } const acc = window.__ttContinuationAcc; - if (!acc || acc.key !== key) { - window.__ttContinuationAcc = { key, contents: contents.slice() }; - } else { - acc.contents = acc.contents.concat(contents); + for (const item of contents) { + const id = getItemVideoId(item); + if (id) { + if (acc.seen[id]) continue; + acc.seen[id] = true; + } + acc.contents.push(item); } if (hasMore) return;