refactor: Update default and YouTube video formats for compatibility
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
// Mock dependencies
|
||||
const mockExecuteYtDlpSpawn = vi.fn();
|
||||
const mockExecuteYtDlpJson = vi.fn().mockResolvedValue({
|
||||
title: 'Test Video',
|
||||
uploader: 'Test Author',
|
||||
upload_date: '20230101',
|
||||
thumbnail: 'http://example.com/thumb.jpg',
|
||||
extractor: 'youtube'
|
||||
});
|
||||
const mockGetUserYtDlpConfig = vi.fn().mockReturnValue({});
|
||||
|
||||
vi.mock('../../../utils/ytDlpUtils', () => ({
|
||||
executeYtDlpSpawn: (...args: any[]) => mockExecuteYtDlpSpawn(...args),
|
||||
executeYtDlpJson: (...args: any[]) => mockExecuteYtDlpJson(...args),
|
||||
getUserYtDlpConfig: (...args: any[]) => mockGetUserYtDlpConfig(...args),
|
||||
getNetworkConfigFromUserConfig: () => ({})
|
||||
}));
|
||||
|
||||
vi.mock('../../../services/storageService', () => ({
|
||||
updateActiveDownload: vi.fn(),
|
||||
saveVideo: vi.fn(),
|
||||
getVideoBySourceUrl: vi.fn(),
|
||||
updateVideo: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('fs-extra', () => ({
|
||||
default: {
|
||||
pathExists: vi.fn().mockResolvedValue(false),
|
||||
ensureDirSync: vi.fn(),
|
||||
existsSync: vi.fn().mockReturnValue(false),
|
||||
createWriteStream: vi.fn().mockReturnValue({
|
||||
on: (event: string, cb: any) => {
|
||||
if (event === 'finish') cb();
|
||||
return { on: () => {} };
|
||||
}
|
||||
}),
|
||||
readdirSync: vi.fn().mockReturnValue([]),
|
||||
}
|
||||
}));
|
||||
|
||||
vi.mock('axios', () => ({
|
||||
default: {
|
||||
get: vi.fn().mockResolvedValue({ data: {} }),
|
||||
create: vi.fn().mockReturnValue({ get: vi.fn(), post: vi.fn() }),
|
||||
}
|
||||
}));
|
||||
|
||||
import { YtDlpDownloader } from '../../../services/downloaders/YtDlpDownloader';
|
||||
|
||||
describe('YtDlpDownloader Safari Compatibility', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockExecuteYtDlpSpawn.mockReturnValue({
|
||||
stdout: { on: vi.fn() },
|
||||
kill: vi.fn(),
|
||||
then: (resolve: any) => resolve()
|
||||
});
|
||||
});
|
||||
|
||||
it('should use H.264 compatible format for YouTube videos by default', async () => {
|
||||
await YtDlpDownloader.downloadVideo('https://www.youtube.com/watch?v=123456');
|
||||
|
||||
expect(mockExecuteYtDlpSpawn).toHaveBeenCalledTimes(1);
|
||||
const args = mockExecuteYtDlpSpawn.mock.calls[0][1];
|
||||
|
||||
expect(args.format).toContain('vcodec^=avc1');
|
||||
// Expect m4a audio which implies AAC for YouTube
|
||||
expect(args.format).toContain('ext=m4a');
|
||||
});
|
||||
|
||||
it('should maintain H.264 preference even when formatSort is provided', async () => {
|
||||
// Mock user config with formatSort
|
||||
mockGetUserYtDlpConfig.mockReturnValue({
|
||||
S: 'res:1080'
|
||||
});
|
||||
|
||||
await YtDlpDownloader.downloadVideo('https://www.youtube.com/watch?v=123456');
|
||||
|
||||
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');
|
||||
});
|
||||
|
||||
it('should NOT force generic avc1 string if user provides custom format', async () => {
|
||||
// Mock user config with custom format
|
||||
mockGetUserYtDlpConfig.mockReturnValue({
|
||||
f: 'bestvideo+bestaudio'
|
||||
});
|
||||
|
||||
await YtDlpDownloader.downloadVideo('https://www.youtube.com/watch?v=123456');
|
||||
|
||||
expect(mockExecuteYtDlpSpawn).toHaveBeenCalledTimes(1);
|
||||
const args = mockExecuteYtDlpSpawn.mock.calls[0][1];
|
||||
|
||||
// Should use user's format
|
||||
expect(args.format).toBe('bestvideo+bestaudio');
|
||||
});
|
||||
});
|
||||
@@ -336,9 +336,9 @@ export class YtDlpDownloader {
|
||||
|
||||
// Default format based on user config or fallback
|
||||
let defaultFormat =
|
||||
"bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best";
|
||||
"bestvideo[ext=mp4][vcodec^=avc1]+bestaudio[ext=m4a]/best[ext=mp4]/best";
|
||||
let youtubeFormat =
|
||||
"bestvideo[ext=mp4][vcodec^=avc1]+bestaudio[ext=m4a][acodec=aac]/bestvideo[ext=mp4][vcodec=h264]+bestaudio[ext=m4a]/best[ext=mp4]/best";
|
||||
"bestvideo[ext=mp4][vcodec^=avc1]+bestaudio[ext=m4a]/bestvideo[ext=mp4][vcodec^=h264]+bestaudio[ext=m4a]/best[ext=mp4]/best";
|
||||
|
||||
// If user specified a format, use it (but still apply MP4 container preference)
|
||||
if (userConfig.f || userConfig.format) {
|
||||
@@ -412,6 +412,7 @@ export class YtDlpDownloader {
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user