Files
ytplayer/frontend/server-backfill.js
2026-10-03 17:56:01 +08:00

35 lines
2.1 KiB
JavaScript

/* Best-effort discovery for playback that never requests server streams. */
(function(root) {
'use strict';
function createReporter({ fetch: send = root.fetch?.bind(root), online = () => root.navigator?.onLine !== false, enabled = () => true } = {}) {
const seen = new Set(), deferred = new Map();
function report(meta) {
if (!enabled() || !send || !meta || !/^[A-Za-z0-9_-]{11}$/.test(meta.id) || meta.custom || meta.upload || !meta.title || meta.title === meta.id || seen.has(meta.id)) return;
// Snapshot only the known metadata, never stream URLs or local blob art.
let thumbnail;
try {
const url = new URL(meta.thumbnail);
if (url.protocol === 'https:' && /^(?:i|i\d)\.ytimg\.com$/.test(url.hostname) && !url.port && !url.username && !url.password) thumbnail = url.href;
} catch { /* server supplies canonical art */ }
const card = { id: meta.id, title: String(meta.title).slice(0,300), channel: String(meta.channel || meta.artist || meta.uploader || '').slice(0,300),
duration: Math.max(0, Math.min(86400, Number(meta.duration) || 0)), ...(thumbnail ? { thumbnail } : {}) };
if (!online()) {
deferred.set(meta.id, card);
if (deferred.size > 256) deferred.delete(deferred.keys().next().value);
return;
}
seen.add(meta.id); deferred.delete(meta.id);
// No synchronous/network failure can interrupt the player event handler.
try {
Promise.resolve(send(`/api/media/${encodeURIComponent(card.id)}/meta`, {
method:'POST', headers:{'Content-Type':'application/json'}, body:JSON.stringify(card),
signal: root.AbortSignal?.timeout?.(10000),
})).catch(() => {});
} catch { /* best effort; a later session can report again */ }
}
return { report, flush: () => { if (online()) for (const card of [...deferred.values()]) report(card); } };
}
if (typeof module !== 'undefined') module.exports = { createReporter };
else { const reporter = createReporter(); root.ServerBackfill = reporter; root.addEventListener('online', reporter.flush); }
})(typeof window !== 'undefined' ? window : globalThis);