Ask the provider again for a favorite yt-dlp can't resolve

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MPZFnNdHbPGDTqQUNiE4ZN
This commit is contained in:
Simon
2026-11-02 22:29:00 +00:00
parent 2fcc17877d
commit 93463bce3a
2 changed files with 107 additions and 4 deletions

View File

@@ -2767,6 +2767,39 @@ App.videos = App.videos || {};
}
};
// Some providers (rule34video among them) hand the listing their media
// formats themselves, and yt-dlp can't extract their pages. A favorite keeps
// no formats, so for those /api/resolve comes back empty -- ask the
// providers again instead, the way the listing did: search the channel for
// the title and take the item that is this video.
const formatsFromProvider = async function(video) {
const idHead = String(video.id || '').split(':')[0];
const channelId = (idHead && idHead !== String(video.id) ? idHead : String(video.channel || '')).toLowerCase();
if (!channelId) return null;
const servers = (App.storage.getConfig().servers || []).map((entry) => Object.keys(entry)[0]);
const wanted = App.favorites && App.favorites.urlKey ? App.favorites.urlKey(video.url) : video.url;
const same = (item) => item && (item.id === video.id ||
(item.url && (App.favorites && App.favorites.urlKey ? App.favorites.urlKey(item.url) : item.url) === wanted));
for (const server of servers) {
try {
const response = await fetch('/api/videos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ channel: channelId, query: video.title || '', page: 1, perPage: 24, server })
});
if (!response.ok) continue;
const data = await response.json();
const found = (data && Array.isArray(data.items) ? data.items : []).find(same);
if (found && hasFormats(found)) {
return { url: found.url, formats: found.formats, http_headers: found.http_headers || {}, isLive: !!found.isLive };
}
} catch (err) {
// Next server.
}
}
return null;
};
App.videos.ensureFormats = function(video) {
if (!video || typeof video !== 'object') return Promise.resolve(null);
if (hasFormats(video.meta)) return Promise.resolve(video.meta);
@@ -2793,13 +2826,14 @@ App.videos = App.videos || {};
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url: video.url })
});
if (!response.ok) return null;
const data = await response.json();
return hasFormats(data) ? data : null;
if (response.ok) {
const data = await response.json();
if (hasFormats(data)) return data;
}
} catch (err) {
// Best-effort: playback still works through the proxy.
return null;
}
return await formatsFromProvider(video);
})();
metaCache.set(cacheKey, promise);
}