Restrict playback proxy to direct https streams, prefer adaptive

Newer yt-dlp surfaces HLS (m3u8) and DASH-segment formats whose url is a
manifest, not media bytes; proxying those served the video element an
unplayable m3u8 and broke Range seeking (416). Keep only protocol=https
formats, and prefer adaptive video-only streams (paired with the separate
audio) over progressive muxed formats, which are more reliable.
This commit is contained in:
Jonathan Sykes
2026-08-23 15:15:02 +08:00
parent defc8a7f1a
commit c8a9755e93

View File

@@ -347,6 +347,12 @@ async function resolveStreams(videoId) {
let soonest = Infinity;
for (const f of raw) {
if (!f.url) continue;
// Only keep direct https byte streams. Newer yt-dlp also surfaces HLS
// (m3u8/m3u8_native) and DASH-segment formats whose `url` is a manifest,
// not media bytes — proxying those hands the <video> element an m3u8 it
// can't play (Chrome) and defeats Range seeking (416). https formats are
// exactly the ones the dual-stream engine relied on before this change.
if (f.protocol && f.protocol !== 'https') continue;
const exp = parseExpiry(f.url);
if (exp) soonest = Math.min(soonest, exp);
formats.push({
@@ -392,7 +398,7 @@ function pickFormat(formats, { formatId, wantAudio, wantHeight }) {
}
if (wantAudio) return pickBestAudio(formats);
if (wantHeight) {
for (const wantProg of [true, false]) {
for (const wantProg of [false, true]) {
for (const f of formats) {
if (!isVideoFmt(f)) continue;
if (isAudioFmt(f) !== wantProg) continue;
@@ -417,12 +423,15 @@ app.get('/api/streams', async (c) => {
? `/api/play?v=${videoId}&audio=1&f=${encodeURIComponent(bestAudio.formatId)}`
: null;
// Quality list — progressive (single-file) first, then adaptive; one entry
// per height. Each URL points at our proxy, carrying the format id so the
// proxy serves the exact same stream this list advertised.
// Quality list — one entry per height. ADAPTIVE (video-only, paired with
// the separate audioUrl for dual-stream playback) is preferred over
// progressive single-file: adaptive https streams proxy reliably, while
// progressive muxed formats (itag 18 and friends) are increasingly
// SABR-gated and flaky. Progressive is kept only when no adaptive stream
// exists at that height.
const qualities = [];
const seen = new Set();
for (const wantProg of [true, false]) {
for (const wantProg of [false, true]) {
for (const f of formats) {
if (!isVideoFmt(f)) continue;
if (isAudioFmt(f) !== wantProg) continue;