import { readFile, writeFile } from "node:fs/promises"; import { test, expect } from "@playwright/test"; import { channelStage, generateReport, pathExists, resetData, resolvePath, writeSettings, } from "./helpers"; const CHANNEL = "test-transcribe"; const DATA = `test-transcripts/channels/${CHANNEL}/data`; // Minimal metadata so transcribeOne's normalize pass runs (it needs // metadata.info.json) and writes transcript.cues.json. const META = JSON.stringify({ id: "vidA", title: "Synthetic parakeet video", upload_date: "20240101", duration: 10, }); async function selectParakeet() { await writeSettings({ adminTitle: "Test Admin", maxTranscriptPageBytes: 8388608, sleepBetweenDownloadsSeconds: 0, transcriptionApp: "parakeet", transcriptionApps: { parakeet: {} }, }); } test("parakeet app transcribes audio and writes a transcript.json", async ({ page, }) => { await resetData("one-transcribe-channel-with-audio"); await selectParakeet(); await generateReport(page, CHANNEL); await page.goto(channelStage(CHANNEL, "transcribe")); await page.getByRole("button", { name: "Transcribe missing" }).click(); await expect(page.getByLabel("Transcribe missing output")).toContainText( "3 succeeded", { timeout: 30_000 }, ); for (const id of ["vidA", "vidB", "vidC"]) { expect(await pathExists(`${DATA}/${id}/transcript.json`)).toBe(true); } // The wrapper emits chough-native JSON (chunk_data in seconds). Reaching this // shape proves the parakeet app's argv ran AND that transcribeOne correctly // renamed the wrapper's exact `--output` path (no ".json" appended) to // transcript.json. const raw = JSON.parse( await readFile(resolvePath(`${DATA}/vidA/transcript.json`), "utf8"), ); expect(Array.isArray(raw.chunk_data)).toBe(true); expect(raw.transcription).toBeUndefined(); }); test("parakeet output normalizes to chough-tagged cues", async ({ page }) => { await resetData("one-transcribe-channel-with-audio"); await selectParakeet(); // Give vidA metadata so the normalize pass produces transcript.cues.json. await writeFile(resolvePath(`${DATA}/vidA/metadata.info.json`), META); await generateReport(page, CHANNEL); await page.goto(channelStage(CHANNEL, "transcribe")); await page.getByRole("button", { name: "Transcribe missing" }).click(); await expect(page.getByLabel("Transcribe missing output")).toContainText( "3 succeeded", { timeout: 30_000 }, ); await expect .poll(() => pathExists(`${DATA}/vidA/transcript.cues.json`), { timeout: 15_000, }) .toBe(true); const cues = JSON.parse( await readFile(resolvePath(`${DATA}/vidA/transcript.cues.json`), "utf8"), ); // The stitched output is chough-shaped, so it carries the chough-json tag… expect(cues.transcriptFormat).toBe("chough-json"); // …and the cues come from seconds-based chunk_data (start 0, end 5), not // misread as whisper milliseconds. expect(cues.cues.length).toBeGreaterThan(0); expect(cues.cues[0].start).toBe(0); expect(cues.cues[0].end).toBe(5); });