// THE WHOLE-RECORDING FETCH DOWNLOADS ANYWAY, AND EVERY METADATA REWRITE KEEPS // THE OLD VERSION (release 10 slice N). // // On a youtube-handling channel the only pass that fetches media is the no-subs // fallback, and its gate is "no transcript and no captions". So "Persist source // video" (and `fetch_clip` with `full: true`, the same function) on a video that // already had a transcript ran two subtitle passes and produced no file — the // live proof of slice M found it on teamrcn. No spec covered it: every persist // spec used a transcribe-handling fixture. This one uses the youtube fixture's // already-downloaded video, transcript and all. // // The second test is the history: two downloads of one video where the source's // metadata changed in between (the fake's `.fake-ytdlp-metadata.json` knob), // and the video page saying so. import { readFile, readdir, stat, writeFile } from "node:fs/promises"; import { test, expect } from "@playwright/test"; import { readJson, resetData, resolvePath, writeSettings } from "./helpers"; import { baseUrl } from "./baseUrl"; const SLUG = "test-youtube"; const rel = (p: string) => `test-transcripts/channels/${SLUG}/${p}`; // The fixture's downloaded video: metadata.info.json + transcript.en.vtt. const HAS_TRANSCRIPT = "fake00000001"; // In the playlist, never downloaded. const UNDOWNLOADED = "fake00000002"; type Outcome = { status: string; startedAt: string; fellBackToTranscribe?: boolean; attempts: Array<{ kind: string; ytdlpExitCode: number | null }>; }; async function outcomeOf(id: string): Promise { try { return await readJson(rel(`data/${id}/download-outcome.json`)); } catch { return null; } } test("Persist source video on a youtube-handling video with a transcript downloads the source and leaves the transcript alone", async ({ page, }) => { test.setTimeout(120_000); await resetData("youtube-with-playlist"); // Inline whisper ON, so the claim "nothing but the container" bites: had the // forced pass extracted audio and fallen through to transcription, the // whisper transcript below would have been rewritten. await writeSettings({ inlineTranscribeOnFallback: true }); const whisper = JSON.stringify({ transcription: [{ text: "the transcript already on disk" }], }); const whisperPath = resolvePath(rel(`data/${HAS_TRANSCRIPT}/transcript.json`)); await writeFile(whisperPath, whisper); await fetch(`${baseUrl}/api/test/invalidate-cache`).catch(() => {}); await page.goto(`/channels/${SLUG}/videos/${HAS_TRANSCRIPT}`); await page.getByLabel("Source video stage summary").click(); // The card used to say persistence was for transcribe-handling channels only // and offered no button here at all. const run = page.getByRole("button", { name: "Persist source video", exact: true, }); await expect(run).toBeEnabled(); await run.click(); const log = page.getByLabel(`Persist source video for ${HAS_TRANSCRIPT} output`); await expect(log).toContainText( "forceMedia: downloading the source although a transcript is on disk", { timeout: 30_000 }, ); await expect(page.getByLabel(`unpersist source video ${HAS_TRANSCRIPT}`)).toBeVisible({ timeout: 30_000, }); await expect(log).toContainText("Persisted source video to"); // yt-dlp was asked for MEDIA: the fake reaches its source-media branch only // for an invocation without --skip-download. const inv = await readFile(resolvePath(rel("fake-ytdlp.invocations")), "utf8"); expect(inv).toContain( `download-source-media:https://www.youtube.com/watch?v=${HAS_TRANSCRIPT}`, ); // The pointer landed, naming a real file in the store. const pointer = await readJson<{ dir: string; file: string; bytes: number; keepReason?: string; }>(rel(`data/${HAS_TRANSCRIPT}/saved-video.json`)); expect(pointer.keepReason).toBe("override"); expect(pointer.file).toMatch(/^source-media\./); expect((await stat(`${pointer.dir}/${pointer.file}`)).size).toBe(pointer.bytes); // The transcript is byte-for-byte what it was, and nothing was extracted // beside it: no audio, no container left in the data dir. expect(await readFile(whisperPath, "utf8")).toBe(whisper); const files = await readdir(resolvePath(rel(`data/${HAS_TRANSCRIPT}`))); expect(files.filter((f) => /^(audio|source-media)\./.test(f))).toEqual([]); // A clean download, and not a fallback to transcription. const outcome = await outcomeOf(HAS_TRANSCRIPT); expect(outcome?.status).toBe("ok"); expect(outcome?.fellBackToTranscribe).toBeUndefined(); expect(outcome?.attempts.map((a) => a.kind)).toEqual([ "metadata-prefetch", "primary", "no-subs-fallback", ]); }); test("a download whose metadata changed upstream appends to metadata.history.json, and the video page says so", async ({ page, }) => { test.setTimeout(120_000); await resetData("youtube-with-playlist"); const knob = resolvePath(rel(".fake-ytdlp-metadata.json")); const historyRel = rel(`data/${UNDOWNLOADED}/metadata.history.json`); // First download: a first write of metadata.info.json is not a rewrite. await writeFile(knob, JSON.stringify({ view_count: 100 })); await page.goto(`/channels/${SLUG}/videos/${UNDOWNLOADED}`); await page.getByRole("button", { name: /^Run download pipeline$/ }).click(); await expect.poll(() => outcomeOf(UNDOWNLOADED), { timeout: 30_000 }).not.toBeNull(); const first = (await outcomeOf(UNDOWNLOADED))!; expect(first.status).toBe("ok"); await expect(readJson(historyRel)).rejects.toThrow(); // The source renames the video and it gains views; download again. await writeFile( knob, JSON.stringify({ title: `Synthetic ${UNDOWNLOADED} (renamed upstream)`, view_count: 150, }), ); await page.reload(); await page.getByRole("button", { name: /^Run download pipeline$/ }).click(); await expect .poll(async () => (await outcomeOf(UNDOWNLOADED))?.startedAt, { timeout: 30_000, }) .not.toBe(first.startedAt); type History = { entries: Array<{ by: string; changed: Record; counters: Record; }>; }; const history = await readJson(historyRel); expect(history.entries).toHaveLength(1); expect(history.entries[0].by).toBe("prefetch"); expect(history.entries[0].changed).toEqual({ title: { from: `Synthetic ${UNDOWNLOADED}`, to: `Synthetic ${UNDOWNLOADED} (renamed upstream)`, }, }); expect(history.entries[0].counters).toEqual({ view_count: [100, 150] }); // The page: one line, and the entry behind it with its from → to. await page.goto(`/channels/${SLUG}/videos/${UNDOWNLOADED}`); const line = page.getByText(/^Metadata rewritten 1× · last .* by prefetch: title$/); await expect(line).toBeVisible(); await line.click(); const entries = page.getByLabel("metadata history entries"); await expect(entries).toContainText(`Synthetic ${UNDOWNLOADED} (renamed upstream)`); await expect(entries).toContainText("view_count"); await expect(entries).toContainText("100 → 150"); }); // A FAILED MEDIA PASS IS NOT A FAILED DOWNLOAD (release 11 slice O3). With the // fake's media download dying part-way, the run fails — the operator asked for // the source and did not get it — but the download outcome stays the subtitle // pass's `ok`, so the video page no longer says "Download failed" over a // transcript that is fine. test("Persist source video whose media download fails: the run fails with yt-dlp's line, the video is not marked Download failed", async ({ page, }) => { test.setTimeout(120_000); await resetData("youtube-with-playlist"); await writeFile(resolvePath(rel(".fake-ytdlp-media-fail")), ""); await page.goto(`/channels/${SLUG}/videos/${HAS_TRANSCRIPT}`); await page.getByLabel("Source video stage summary").click(); await page.getByRole("button", { name: "Persist source video", exact: true }).click(); const log = page.getByLabel(`Persist source video for ${HAS_TRANSCRIPT} output`); await expect(log).toContainText( "forceMedia: the source download failed (yt-dlp exit 1); the transcript on disk is untouched and the download stays ok.", { timeout: 30_000 }, ); await expect(log).toContainText( "[error] The source video was not downloaded: ERROR: [download] Got error: HTTP Error 403: Forbidden", { timeout: 30_000 }, ); const outcome = await outcomeOf(HAS_TRANSCRIPT); expect(outcome?.status).toBe("ok"); expect(outcome?.attempts.map((a) => [a.kind, a.ytdlpExitCode])).toEqual([ ["metadata-prefetch", 0], ["primary", 0], ["no-subs-fallback", 1], ]); // The transcript is there (the subtitle pass re-fetched it, as on any // re-download); the failed media pass wrote none. const files = await readdir(resolvePath(rel(`data/${HAS_TRANSCRIPT}`))); expect(files).toContain("transcript.en.vtt"); expect(files).not.toContain("saved-video.json"); // The page, fresh: no failed-download badge, and nothing persisted. await page.reload(); await expect(page.getByLabel("download outcome")).toHaveCount(0); await page.getByLabel("Source video stage summary").click(); await expect(page.getByLabel(`unpersist source video ${HAS_TRANSCRIPT}`)).toHaveCount(0); });