refactor: Relax H.264 preference for YtDlpDownloader

This commit is contained in:
Peifan Li
2025-12-15 12:45:41 -05:00
parent de7721b66a
commit dc8918bc2f
2 changed files with 30 additions and 7 deletions

View File

@@ -70,10 +70,10 @@ describe('YtDlpDownloader Safari Compatibility', () => {
expect(args.format).toContain('ext=m4a');
});
it('should maintain H.264 preference even when formatSort is provided', async () => {
it('should relax H.264 preference when formatSort is provided to allow higher resolutions', async () => {
// Mock user config with formatSort
mockGetUserYtDlpConfig.mockReturnValue({
S: 'res:1080'
S: 'res:2160'
});
await YtDlpDownloader.downloadVideo('https://www.youtube.com/watch?v=123456');
@@ -81,9 +81,14 @@ describe('YtDlpDownloader Safari Compatibility', () => {
expect(mockExecuteYtDlpSpawn).toHaveBeenCalledTimes(1);
const args = mockExecuteYtDlpSpawn.mock.calls[0][1];
// Should have both formatSort AND the specific format
expect(args.formatSort).toBe('res:1080');
expect(args.format).toContain('vcodec^=avc1');
// Should have formatSort
expect(args.formatSort).toBe('res:2160');
// Should NOT be restricted to avc1/h264 anymore
expect(args.format).not.toContain('vcodec^=avc1');
// Should use the permissive format, but prioritizing VP9/WebM
expect(args.format).toBe('bestvideo[vcodec^=vp9][ext=webm]+bestaudio/bestvideo[ext=webm]+bestaudio/bestvideo+bestaudio/best');
// Should default to WebM to support VP9/AV1 codecs better than MP4 and compatible with Safari 14+
expect(args.mergeOutputFormat).toBe('webm');
});
it('should NOT force generic avc1 string if user provides custom format', async () => {

View File

@@ -377,7 +377,14 @@ export class YtDlpDownloader extends BaseDownloader {
const formatSortValue = userFormatSort || userFormatSort2;
// Determine merge output format: use user's choice or default to mp4
const mergeOutputFormat = userMergeOutputFormat || "mp4";
// However, if user is sorting by resolution (likely demanding 4K/VP9), default to MKV
// because VP9/AV1 in MP4 (mp4v2) is often problematic for Safari/QuickTime.
let defaultMergeFormat = "mp4";
if (formatSortValue && formatSortValue.includes("res")) {
// Use WebM for high-res (likely VP9/AV1) as it's supported by Safari 14+ and Chrome
defaultMergeFormat = "webm";
}
const mergeOutputFormat = userMergeOutputFormat || defaultMergeFormat;
// Update the video path to use the correct extension based on merge format
const videoExtension = mergeOutputFormat;
@@ -422,7 +429,18 @@ export class YtDlpDownloader extends BaseDownloader {
// Add YouTube specific flags if it's a YouTube URL
// Always apply preferred formats for YouTube to ensure codec compatibility (H.264/AAC for Safari)
if (videoUrl.includes("youtube.com") || videoUrl.includes("youtu.be")) {
flags.format = youtubeFormat;
// If the user hasn't specified a format (-f), but HAS specified a sorting order (-S),
// we should assume they want to prioritize their sort order (e.g. resolution) over
// our default strictly-compatible codec constraints.
// This fixes the issue where -S res:2160 fails because the default format restricts to H.264 (max 1080p).
if (!userConfig.f && !userConfig.format && formatSortValue) {
// Allow any video codec (including VP9/AV1 for 4K), but try to keep audio good
// Prioritize VP9 in WebM for Safari 14+ compatibility (AV1 is less supported)
flags.format =
"bestvideo[vcodec^=vp9][ext=webm]+bestaudio/bestvideo[ext=webm]+bestaudio/bestvideo+bestaudio/best";
} else {
flags.format = youtubeFormat;
}
// Use user's extractor args if provided, otherwise let yt-dlp use its defaults
// Modern yt-dlp (2025.11+) has built-in JS challenge solvers that work without PO tokens