| 307 | } |
| 308 | |
| 309 | function patchVideoSrc( |
| 310 | dir: string, |
| 311 | videoFilename: string | undefined, |
| 312 | durationSeconds?: number, |
| 313 | ): void { |
| 314 | const htmlFiles = readdirSync(dir, { withFileTypes: true, recursive: true }) |
| 315 | .filter((e) => e.isFile() && e.name.endsWith(".html")) |
| 316 | .map((e) => join(e.parentPath, e.name)); |
| 317 | |
| 318 | for (const file of htmlFiles) { |
| 319 | let content = readFileSync(file, "utf-8"); |
| 320 | if (videoFilename) { |
| 321 | content = content.replaceAll("__VIDEO_SRC__", videoFilename); |
| 322 | } else { |
| 323 | // Remove video elements with placeholder src |
| 324 | content = content.replace(/<video[^>]*src="__VIDEO_SRC__"[^>]*>[\s\S]*?<\/video>/g, ""); |
| 325 | content = content.replace(/<video[^>]*src="__VIDEO_SRC__"[^>]*>/g, ""); |
| 326 | // Remove audio elements with placeholder src |
| 327 | content = content.replace(/<audio[^>]*src="__VIDEO_SRC__"[^>]*>[\s\S]*?<\/audio>/g, ""); |
| 328 | content = content.replace(/<audio[^>]*src="__VIDEO_SRC__"[^>]*>/g, ""); |
| 329 | } |
| 330 | // Patch duration — use probed duration or default |
| 331 | const dur = durationSeconds ? String(Math.round(durationSeconds * 100) / 100) : "10"; |
| 332 | content = content.replaceAll("__VIDEO_DURATION__", dur); |
| 333 | writeFileSync(file, content, "utf-8"); |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | async function patchTranscript(dir: string, transcriptPath: string): Promise<void> { |
| 338 | const { loadTranscript, patchCaptionHtml } = await import("../whisper/normalize.js"); |