( inputPath: string, dir: string, to: CaptionExportFormat, output: string | undefined, json: boolean, preserveCues: boolean, )
| 176 | // --------------------------------------------------------------------------- |
| 177 | |
| 178 | async function exportTranscript( |
| 179 | inputPath: string, |
| 180 | dir: string, |
| 181 | to: CaptionExportFormat, |
| 182 | output: string | undefined, |
| 183 | json: boolean, |
| 184 | preserveCues: boolean, |
| 185 | ): Promise<void> { |
| 186 | const { loadTranscript, formatSrt, formatVtt } = await import("../whisper/normalize.js"); |
| 187 | const { words, format } = loadTranscript(inputPath); |
| 188 | |
| 189 | if (words.length === 0) exitNoWords(json); |
| 190 | |
| 191 | // A .srt/.vtt source is already phrase-level; keep its cue boundaries 1:1. |
| 192 | // --preserve-cues forces the same for an already-cued transcript.json whose |
| 193 | // entries have no internal whitespace (single-word or CJK captions), which |
| 194 | // the automatic whitespace heuristic in wordsToCues can't detect. |
| 195 | const preGrouped = preserveCues || format === "srt" || format === "vtt" || undefined; |
| 196 | const outPath = resolve(output ?? join(dir, `transcript.${to}`)); |
| 197 | const content = |
| 198 | to === "srt" ? formatSrt(words, { preGrouped }) : formatVtt(words, { preGrouped }); |
| 199 | writeFileSync(outPath, content); |
| 200 | |
| 201 | if (json) { |
| 202 | console.log( |
| 203 | JSON.stringify({ ok: true, format: to, wordCount: words.length, outputPath: outPath }), |
| 204 | ); |
| 205 | } else { |
| 206 | console.log( |
| 207 | `${c.success("◇")} Exported ${c.accent(String(words.length))} words to ${c.accent(to.toUpperCase())} → ${c.accent(outPath)}`, |
| 208 | ); |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | // --------------------------------------------------------------------------- |
| 213 | // Transcribe audio/video with whisper |
no test coverage detected