Recover related videos with bounded discovery and retryable fallbacks
This commit is contained in:
@@ -572,6 +572,9 @@ const API = {
|
||||
search: (query, { refresh = false } = {}) => WEB
|
||||
? webFetch(`/api/search?q=${encodeURIComponent(query)}${refresh ? '&refresh=1' : ''}`)
|
||||
: call('yt.search', 'yt_search', { query }),
|
||||
related: (meta, { refresh = false } = {}) => WEB && /^[\w-]{11}$/.test(meta.id)
|
||||
? fetch(`/api/related?${new URLSearchParams({ videoId: meta.id, title: meta.title || '', channel: meta.channel || meta.artist || '', ...(refresh ? { refresh: '1' } : {}) })}`).then(response => response.json())
|
||||
: API.search(meta.title || meta.channel, { refresh }),
|
||||
// Videos the server already knows — answers fast while the real search loads.
|
||||
searchLocal: (query) => WEB
|
||||
? webFetch(`/api/search/local?q=${encodeURIComponent(query)}`)
|
||||
@@ -11835,23 +11838,33 @@ function updateAbUI() {
|
||||
// ============================================================================
|
||||
// Related videos
|
||||
// ============================================================================
|
||||
async function loadRelated() {
|
||||
if (!current || !current.meta) return;
|
||||
$('relatedPanel').classList.add('hidden');
|
||||
relatedVideos = [];
|
||||
try {
|
||||
const res = await API.search(current.meta.title);
|
||||
if (!res || !res.ok || !current) return;
|
||||
relatedVideos = (res.results || []).filter((v) => v.id !== current.meta.id).slice(0, 8);
|
||||
renderRelated();
|
||||
} catch { /* ignore */ }
|
||||
let relatedState = { status: 'idle', message: '' };
|
||||
const fetchRelatedVideos = RelatedVideos.createLoader({
|
||||
fetchRelated: (meta, options) => API.related(meta, options),
|
||||
update(state) {
|
||||
if (current?.meta?.id !== state.id) return;
|
||||
relatedState = state; relatedVideos = state.cards; renderRelated();
|
||||
},
|
||||
});
|
||||
async function loadRelated(options = {}) {
|
||||
if (current?.meta) return fetchRelatedVideos(current.meta, options);
|
||||
}
|
||||
function renderRelated() {
|
||||
const panel = $('relatedPanel'), list = $('relatedList');
|
||||
if (!panel || !list || !relatedVideos.length) { if (panel) panel.classList.add('hidden'); return; }
|
||||
if (!panel || !list || (relatedState.status === 'idle' && !relatedVideos.length)) { if (panel) panel.classList.add('hidden'); return; }
|
||||
panel.classList.remove('hidden');
|
||||
list.classList.toggle('hidden', relatedCollapsed);
|
||||
list.innerHTML = '';
|
||||
if (relatedState.message) {
|
||||
const notice = document.createElement('div'); notice.className = 'related-notice'; notice.setAttribute('role', 'status');
|
||||
const message = document.createElement('p'); message.textContent = relatedState.message; notice.append(message);
|
||||
if (relatedState.status === 'error') {
|
||||
const retry = document.createElement('button'); retry.type = 'button'; retry.className = 'related-retry'; retry.setAttribute('aria-label', 'Retry related videos');
|
||||
retry.innerHTML = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 11a9 9 0 1 1 2.6 7M3 3v8h8"/></svg><span>Retry</span>';
|
||||
retry.onclick = () => loadRelated({ refresh: true }); notice.append(retry);
|
||||
}
|
||||
list.append(notice);
|
||||
}
|
||||
relatedVideos.forEach((v) => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'related-item';
|
||||
|
||||
Reference in New Issue
Block a user