Proxy YouTube playback through server to fix intermittent 403s

Direct googlevideo URLs are bound to the extractor's IP and expire, so the
browser fetching them from a different IP got intermittent 403s on playback.
Route playback through a same-origin /api/play proxy that fetches the stream
server-side (matching the extractor IP) with yt-dlp's own http_headers and
forwards Range headers for seeking; fall back to piping yt-dlp for SABR/itag-18
formats a plain GET can't fetch. Prefer adaptive streams over progressive in
the playback fallback order.
This commit is contained in:
Jonathan Sykes
2026-08-23 15:01:28 +08:00
parent ee78c8561b
commit defc8a7f1a
2 changed files with 227 additions and 31 deletions

View File

@@ -1441,9 +1441,11 @@ const Player = {
}
},
// Ordered list of qualities to try if playback errors out. Progressive
// (single-file, has audio) goes near the front because it's the most reliable;
// then we walk from the lowest resolution up.
// Ordered list of qualities to try if playback errors out. Adaptive
// (video-only + separate audio via /api/play) goes near the front: those
// streams proxy reliably, whereas progressive single-file formats (itag 18
// and friends) are increasingly SABR-gated and 403 even server-side, so they
// now go LAST. We then walk from the lowest resolution up.
fallbackQueue: [],
fbIndex: 0,
buildFallbackQueue(chosen) {
@@ -1452,8 +1454,8 @@ const Player = {
const queue = [];
const push = (x) => { if (x && !seen.has(x.label)) { seen.add(x.label); queue.push(x); } };
push(chosen);
qs.filter((q) => q.hasAudio).forEach(push); // progressive = sturdiest
qs.forEach(push); // then everything, low → high
qs.filter((q) => !q.hasAudio).forEach(push); // adaptive = sturdiest (proxied)
qs.forEach(push); // then everything, incl. progressive
this.fallbackQueue = queue;
this.fbIndex = 0;
},