diff --git a/src/lib/server/downloader.ts b/src/lib/server/downloader.ts index 00eb893..9f0c7b3 100644 --- a/src/lib/server/downloader.ts +++ b/src/lib/server/downloader.ts @@ -48,11 +48,13 @@ export interface AudioResult { export type DownloadResult = CaptionResult | AudioResult; -/** Try to get auto-generated captions from YouTube. Returns null if unavailable. */ +/** Try to get auto-generated captions from YouTube. Returns null if unavailable + * (fetch error OR parsed segments are empty/unusable). */ async function tryGetCaptions(url: string, _outDir: string): Promise { try { const transcript = await fetchTranscript(url, { lang: 'en' }); const segments = transcriptEntriesToSegments(transcript); + if (segments.length === 0) return null; const title = await getYouTubeTitle(url); return { type: 'captions', segments, title }; } catch { @@ -100,15 +102,20 @@ async function downloadAudio(url: string, outDir: string): Promise<{ audioPath: return { audioPath: join(outDir, audioFile), title }; } -/** Download a YouTube URL: try captions first, fall back to audio. */ +/** Download a YouTube URL: try captions first, fall back to audio. + * + * VTT fallback: when tryGetCaptions returns null (parse failure or + * empty/unusable segments) we fall through to downloadAudio instead + * of returning an empty CaptionResult. */ export async function downloadYouTube(url: string, jobId: string): Promise { await ensureTmpDir(); const outDir = join(TMP_DIR, jobId); await mkdir(outDir, { recursive: true }); const captions = await tryGetCaptions(url, outDir); - if (captions && captions.segments.length > 0) return captions; + if (captions) return captions; + /* VTT fallback: captions unavailable — download full audio for whisper transcription */ const { audioPath, title } = await downloadAudio(url, outDir); return { type: 'audio', audioPath, title }; }