Search: answer on the first YouTube page, fill the rest in background; never cache empty results; gzip search JSON
This commit is contained in:
@@ -64,6 +64,7 @@ async function webFetch(path, opts = {}) {
|
||||
return AsyncGuard.getJson(path, opts, {
|
||||
// Some upstream failures are represented by HTTP 200 + {ok:false}; do
|
||||
// not expose those transient responses as a manual Retry state either.
|
||||
...(/^\/api\/search\?/.test(path) ? { timeoutMs: 40000, attempts: 2 } : {}),
|
||||
retryResult: value => value?.ok === false && /\b(?:408|425|429|5\d\d)\b|temporar|timed?\s*out|network|connection|upstream|unavailable|available right now/i.test(String(value.error || value.message || '')),
|
||||
});
|
||||
const res = await fetch(path, opts);
|
||||
@@ -170,10 +171,12 @@ const RecentSearches = (() => {
|
||||
},
|
||||
clear() { save([]); }, // saved results stay in SearchLibrary (cleared only from Settings)
|
||||
// Cache the just-fetched result set for `q` (called on every successful search).
|
||||
cacheResults(q, results, ts) { SearchLibrary.put(q, results, ts); },
|
||||
// Promise of the cached {query, results, ts} for `q`, or null.
|
||||
getResults(q) {
|
||||
return SearchLibrary.get(q);
|
||||
cacheResults(q, results, ts) { if (results && results.length) SearchLibrary.put(q, results, ts); },
|
||||
// Promise of the cached {query, results, ts} for `q`, or null. An empty
|
||||
// saved set (left by an earlier failed search) counts as no copy.
|
||||
async getResults(q) {
|
||||
const hit = await SearchLibrary.get(q);
|
||||
return hit && hit.results && hit.results.length ? hit : null;
|
||||
},
|
||||
// Normalized queries that have a cached result set — one localStorage
|
||||
// read for the whole dropdown, rather than one per row.
|
||||
@@ -7893,6 +7896,19 @@ function wireUI() {
|
||||
warmStreams(results.slice(0, 3).map((r) => r.id));
|
||||
if (refresh) toast('Search updated');
|
||||
perfMeasure('ytp:search', 'ytp:search-start');
|
||||
// The server answered with YouTube's first page and is still fetching
|
||||
// the rest; pick up the full list quietly once it has landed.
|
||||
if (res.partial) setTimeout(async () => {
|
||||
if (mySeq !== searchSeq || view.type !== 'search') return;
|
||||
try {
|
||||
const more = await API.search(q);
|
||||
if (mySeq !== searchSeq || !more?.ok || (more.results || []).length <= results.length) return;
|
||||
searchResults = more.results;
|
||||
renderList();
|
||||
RecentSearches.cacheResults(q, more.results, Date.now());
|
||||
showSearchMeta(q, Date.now());
|
||||
} catch { /* keep the first page */ }
|
||||
}, 10000);
|
||||
} catch (err) {
|
||||
networkDone = true;
|
||||
if (mySeq !== searchSeq) return;
|
||||
|
||||
Reference in New Issue
Block a user