Play favorites from freshly resolved formats, not the page URL
Streaming a page URL makes /api/stream re-run yt-dlp on every request -- slow, and a 500 on some sites -- so the player and the reels feed now wait for App.videos.ensureFormats() when an item has no formats (favorites, or a card clicked before its hover-resolve landed) and play a real media URL with the extractor's headers, the same path a hovered card takes. Favorites' download does the same. The page URL survives only as a last resort when resolution yields nothing. Two things in the proxy kept this site broken either way: heavyfetish serves media from paths with a trailing slash (/get_file/.../11097_720p.mp4/), which missed every extension test in stream_video and sent even a resolved media URL down the yt-dlp branch -- a full extraction per request, including every seek. Its CDN (st17.heavyfetish.com) also serves a certificate that expired 2026-02-16, so the upstream fetch failed verification and returned 500. A browser can't play such a host at all, which is much of why this proxy exists, so impersonate_get() now retries once without verification, logs it, and remembers the host so the doomed handshake isn't repeated for every range request. STREAM_TLS_VERIFY_ONLY=1 restores the hard failure. Verified in headless Chrome against the real site: a favorite holding only the page URL now resolves, streams (206, video/mp4, duration 3167.8s, readyState 4, no error) and shows "720p mp4 | 480p mp4" in the quality menu. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBDkEXP4htyXTCZUwMLphd
This commit is contained in:
@@ -815,19 +815,28 @@ App.player = App.player || {};
|
||||
});
|
||||
let destroyFormatMenu = bindFormats();
|
||||
addCleanup(() => destroyFormatMenu());
|
||||
const meta = (source && typeof source === 'object') ? (source.meta || source) : null;
|
||||
const hasFormats = !!(meta && Array.isArray(meta.formats) && meta.formats.length);
|
||||
// Items that arrive without formats -- a listing card clicked before its
|
||||
// hover-resolve finished, or a favorite (which deliberately stores no
|
||||
// resolved formats, since their URLs expire) -- would otherwise show an
|
||||
// empty quality menu. Playback already starts from the page URL via the
|
||||
// proxy, so resolve in the background and rebuild the menu with the real
|
||||
// qualities as soon as they land.
|
||||
if (App.videos && typeof App.videos.ensureFormats === 'function') {
|
||||
App.videos.ensureFormats(source).then((meta) => {
|
||||
// A later open() may have taken over in the meantime; its own
|
||||
// bindFormats() owns the menu then.
|
||||
if (!meta || cp.source !== source) return;
|
||||
destroyFormatMenu();
|
||||
destroyFormatMenu = bindFormats();
|
||||
// resolved formats, since their URLs expire) -- carry only a page URL,
|
||||
// and they'd also show an empty quality menu. Resolve them first: the
|
||||
// page-URL fallback makes /api/stream re-run yt-dlp on every single
|
||||
// request, which is slow and fails outright on some sites, whereas a
|
||||
// resolved format is a real media URL with the extractor's headers --
|
||||
// the same path a hovered card plays through.
|
||||
let deferredStart = false;
|
||||
if (!hasFormats && App.videos && typeof App.videos.ensureFormats === 'function') {
|
||||
deferredStart = true;
|
||||
App.videos.ensureFormats(source).then((resolved) => {
|
||||
// A later open() (or a close) may have taken over in the
|
||||
// meantime; that session owns the player now.
|
||||
if (cp.source !== source) return;
|
||||
if (resolved) {
|
||||
destroyFormatMenu();
|
||||
destroyFormatMenu = bindFormats();
|
||||
}
|
||||
playSources(source, { originEl: cp.originEl });
|
||||
});
|
||||
}
|
||||
bindGestures(cp.video);
|
||||
@@ -840,7 +849,9 @@ App.player = App.player || {};
|
||||
document.body.style.overflow = 'hidden';
|
||||
wakeHud();
|
||||
|
||||
playSources(source, { originEl: cp.originEl });
|
||||
// Already-resolved sources start immediately; unresolved ones start from
|
||||
// the ensureFormats() callback above (the spinner is already up).
|
||||
if (!deferredStart) playSources(source, { originEl: cp.originEl });
|
||||
};
|
||||
|
||||
App.player.close = function(opts) {
|
||||
|
||||
Reference in New Issue
Block a user