Prefer h264 formats for saves so cached copies play on every device

This commit is contained in:
Jonathan Sykes
2026-09-01 07:02:57 +08:00
parent a159c1e642
commit 06f80debc0

View File

@@ -760,6 +760,20 @@ async function assertNotLive(videoId) {
}
}
// Format selectors for saves. `[ext=mp4]` alone is NOT enough: YouTube ships
// AV1 in mp4 containers too, and the bot-check fallback clients (web_embedded)
// rank the AV1 ladder as "bestvideo" — an AV1 save plays on almost nothing
// (iOS Safari has no AV1 decoder), so cached copies errored and fell back to
// streaming. Demand h264 (`vcodec^=avc1`) + m4a/aac first; anything exotic is
// a last resort for videos with no avc ladder at all.
const SAVE_MUX_FORMAT =
'bv*[height<=720][vcodec^=avc1]+ba[ext=m4a]/bv*[height<=720][vcodec^=avc1]+ba/'
+ 'b[ext=mp4][vcodec^=avc1]/bv*[height<=720]+ba/b[ext=mp4]/b';
const SAVE_DEFAULT_FORMAT =
'best[ext=mp4][vcodec^=avc1][acodec!=none]/'
+ 'bv*[height<=720][vcodec^=avc1]+ba[ext=m4a]/bv*[height<=720][vcodec^=avc1]+ba/'
+ 'best[ext=mp4][acodec!=none]/bv*[height<=720]+ba/b';
// Same video + same format args requested while a download is already in
// flight (the OPFS worker retries, then the main-thread fallback retries
// again) share ONE yt-dlp run instead of spawning another each time.
@@ -849,7 +863,7 @@ async function ytdlpEditedDownloadResponse(videoId, fp, keep, signal) {
await withSaveSlot(() => runYtdlpResilient([
`https://www.youtube.com/watch?v=${videoId}`,
'--no-warnings', '--no-playlist',
'-f', 'bv*[height<=720][ext=mp4]+ba[ext=m4a]/bv*[height<=720]+ba/b[ext=mp4]/b',
'-f', SAVE_MUX_FORMAT,
'--merge-output-format', 'mp4',
'--limit-rate', DOWNLOAD_RATE,
'-o', srcTmp,
@@ -916,7 +930,7 @@ app.get('/api/download/:videoId', async (c) => {
if (c.req.query('mux') === '1') {
try {
return await ytdlpDownloadResponse(videoId, fp, [
'-f', 'bv*[height<=720][ext=mp4]+ba[ext=m4a]/bv*[height<=720]+ba/b[ext=mp4]/b',
'-f', SAVE_MUX_FORMAT,
'--merge-output-format', 'mp4',
], c.req.raw.signal);
} catch (err) {
@@ -931,8 +945,7 @@ app.get('/api/download/:videoId', async (c) => {
// "Requested format is not available".
try {
return await ytdlpDownloadResponse(videoId, fp, [
'-f', 'bestvideo[ext=mp4][acodec!=none]/bestvideo[acodec!=none]/best[ext=mp4][acodec!=none]/'
+ 'bv*[height<=720][ext=mp4]+ba[ext=m4a]/bv*[height<=720]+ba/b',
'-f', SAVE_DEFAULT_FORMAT,
'--merge-output-format', 'mp4',
], c.req.raw.signal);
} catch (err) {