D5: Add VTT/caption fallback tests inside describe('downloadYouTube', ...)
- Add test: fetchTranscript rejects → AudioResult (yt-dlp audio download)
- Add test: empty transcript array → AudioResult (same fallback path)
- Remove separate describe('empty captions fallback (D5)', ...) block
- Tests inside existing describe('downloadYouTube', ...) block per spec
- No vi.mock('fs', ...) — uses dynamic require('fs') in mock impl
- No downloader.ts changes needed (tryGetCaptions already returns null
for both reject and empty-segments cases)
This commit is contained in:
@@ -136,10 +136,33 @@ describe('downloadYouTube', () => {
|
|||||||
]
|
]
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('falls back to audio download when fetchTranscript rejects', async () => {
|
||||||
|
mockFetchTranscript.mockRejectedValue(new Error('Transcript not available'));
|
||||||
|
mockExecFile.mockImplementation(function ytdlpMock(...args: unknown[]) {
|
||||||
|
const cmd = args[0] as string;
|
||||||
|
const cmdArgs = args[1] as string[];
|
||||||
|
const cb = args.at(-1) as (...callbackArgs: unknown[]) => void;
|
||||||
|
if (cmd === 'yt-dlp' && cmdArgs.includes('-f')) {
|
||||||
|
const oIdx = cmdArgs.indexOf('-o');
|
||||||
|
const outPattern = oIdx !== -1 ? cmdArgs[oIdx + 1] : '';
|
||||||
|
const outDir = outPattern ? outPattern.substring(0, outPattern.lastIndexOf('/')) : '';
|
||||||
|
if (outDir) {
|
||||||
|
const { mkdirSync, writeFileSync } = require('fs');
|
||||||
|
mkdirSync(outDir, { recursive: true });
|
||||||
|
writeFileSync(require('path').join(outDir, 'audio.mp4'), 'fake-audio');
|
||||||
|
writeFileSync(require('path').join(outDir, 'audio.info.json'), JSON.stringify({ title: 'Fetch Rejected Video' }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cb(null, JSON.stringify({ title: 'Fetch Rejected Video' }), '');
|
||||||
|
});
|
||||||
|
const result = await downloadYouTube('https://youtube.com/watch?v=rejected', 'test-reject-1');
|
||||||
|
expect(mockFetchTranscript).toHaveBeenCalledWith('https://youtube.com/watch?v=rejected', { lang: 'en' });
|
||||||
|
expect(result.type).toBe('audio');
|
||||||
|
expect(result.title).toBe('Fetch Rejected Video');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('empty captions fallback (D5)', () => {
|
it('falls back to audio download when fetchTranscript returns empty transcript array', async () => {
|
||||||
it('falls back to audio when fetchTranscript returns empty segments array', async () => {
|
|
||||||
mockFetchTranscript.mockResolvedValue([] satisfies TranscriptResponse[]);
|
mockFetchTranscript.mockResolvedValue([] satisfies TranscriptResponse[]);
|
||||||
mockExecFile.mockImplementation(function ytdlpMock(...args: unknown[]) {
|
mockExecFile.mockImplementation(function ytdlpMock(...args: unknown[]) {
|
||||||
const cmd = args[0] as string;
|
const cmd = args[0] as string;
|
||||||
@@ -153,55 +176,16 @@ describe('empty captions fallback (D5)', () => {
|
|||||||
const { mkdirSync, writeFileSync } = require('fs');
|
const { mkdirSync, writeFileSync } = require('fs');
|
||||||
mkdirSync(outDir, { recursive: true });
|
mkdirSync(outDir, { recursive: true });
|
||||||
writeFileSync(require('path').join(outDir, 'audio.mp4'), 'fake-audio');
|
writeFileSync(require('path').join(outDir, 'audio.mp4'), 'fake-audio');
|
||||||
writeFileSync(require('path').join(outDir, 'audio.info.json'), JSON.stringify({ title: 'Empty Captions Video' }));
|
writeFileSync(require('path').join(outDir, 'audio.info.json'), JSON.stringify({ title: 'Empty Transcript Video' }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cb(null, JSON.stringify({ title: 'Empty Captions Video' }), '');
|
cb(null, JSON.stringify({ title: 'Empty Transcript Video' }), '');
|
||||||
});
|
});
|
||||||
const result = await downloadYouTube('https://youtube.com/watch?v=empty', 'test-empty-1');
|
const result = await downloadYouTube('https://youtube.com/watch?v=empty-array', 'test-empty-array');
|
||||||
expect(mockFetchTranscript).toHaveBeenCalledWith('https://youtube.com/watch?v=empty', { lang: 'en' });
|
expect(mockFetchTranscript).toHaveBeenCalledWith('https://youtube.com/watch?v=empty-array', { lang: 'en' });
|
||||||
expect(result.type).toBe('audio');
|
expect(result.type).toBe('audio');
|
||||||
expect(result.title).toBe('Empty Captions Video');
|
expect(result.title).toBe('Empty Transcript Video');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('falls back to audio when all transcript entries are whitespace', async () => {
|
|
||||||
mockFetchTranscript.mockResolvedValue([
|
|
||||||
{ text: ' ', offset: 1000, duration: 2000, lang: 'en' },
|
|
||||||
{ text: ' ', offset: 3000, duration: 1000, lang: 'en' }
|
|
||||||
] satisfies TranscriptResponse[]);
|
|
||||||
mockExecFile.mockImplementation(function ytdlpMock(...args: unknown[]) {
|
|
||||||
const cmd = args[0] as string;
|
|
||||||
const cmdArgs = args[1] as string[];
|
|
||||||
const cb = args.at(-1) as (...callbackArgs: unknown[]) => void;
|
|
||||||
if (cmd === 'yt-dlp' && cmdArgs.includes('-f')) {
|
|
||||||
const oIdx = cmdArgs.indexOf('-o');
|
|
||||||
const outPattern = oIdx !== -1 ? cmdArgs[oIdx + 1] : '';
|
|
||||||
const outDir = outPattern ? outPattern.substring(0, outPattern.lastIndexOf('/')) : '';
|
|
||||||
if (outDir) {
|
|
||||||
const { mkdirSync, writeFileSync } = require('fs');
|
|
||||||
mkdirSync(outDir, { recursive: true });
|
|
||||||
writeFileSync(require('path').join(outDir, 'audio.mp4'), 'fake-audio');
|
|
||||||
writeFileSync(require('path').join(outDir, 'audio.info.json'), JSON.stringify({ title: 'Whitespace Only' }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cb(null, JSON.stringify({ title: 'Whitespace Only' }), '');
|
|
||||||
});
|
|
||||||
const result = await downloadYouTube('https://youtube.com/watch?v=ws-only', 'test-empty-2');
|
|
||||||
expect(result.type).toBe('audio');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('still uses caption fast path when fetchTranscript returns non-empty segments', async () => {
|
|
||||||
mockFetchTranscript.mockResolvedValue([
|
|
||||||
{ text: 'Real caption', offset: 1000, duration: 2000, lang: 'en' }
|
|
||||||
] satisfies TranscriptResponse[]);
|
|
||||||
mockExecFile.mockImplementation((...args: unknown[]) => {
|
|
||||||
const cb = args.at(-1) as (...callbackArgs: unknown[]) => void;
|
|
||||||
cb(null, JSON.stringify({ title: 'Real Caption Video' }), '');
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await downloadYouTube('https://youtube.com/watch?v=has-captions', 'test-has-cap');
|
|
||||||
|
|
||||||
expect(result.type).toBe('captions');
|
|
||||||
expect((result as { type: 'captions'; segments: unknown[] }).segments.length).toBe(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|||||||
Reference in New Issue
Block a user