From ced1298fe7c903e9106ddaa70839111350217a6f Mon Sep 17 00:00:00 2001 From: KrX3D Date: Sat, 29 Aug 2026 22:08:16 +0200 Subject: [PATCH] fix: hook the array-root continuation path too, so the native cache completes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The accumulator added in the last PR never fired. On-device with it live, every continuation reaching the handler I hooked carried hasContinuation:true: playlist.continuation.detected itemCount:15 hasContinuation:true playlist.continuation.detected itemCount:15 hasContinuation:true playlist.continuation.detected itemCount:15 hasContinuation:true That is 15 initial + 45 = 60 of 68 items. The final batch — the 8 items carrying no continuation token, the one completion depends on — never appeared there at all. It arrives through processResponsePayload, the array-root JSON.parse path, which handles playlistVideoListContinuation separately and logs nothing. So the accumulator sat at 45 items waiting for a completion that could not come, and the slow re-download stayed on the critical path exactly as before: 20:05:03.141 .. 20:05:11.128 batch_fetched x4 20:05:11.131 full_cache.stored (via the collector, ~10s in) This is the same object-root/array-root split that has bitten this file before, where a feature added to one path silently misses the other. Hooked the array-root path as well, before its filterContinuationItems call for the same reason as the other one — after it, an all-watched batch is down to the single kept helper. Also deduped the accumulator by video id. Two handlers now feed it, and a response reaching both would otherwise put duplicate tiles in the cached playlist. --- mods/features/adblock.js | 9 +++++++++ mods/features/playlistBatchCollect.js | 19 ++++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) 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;