(
page: Page,
outputDir: string,
progress: (stage: string, detail?: string) => void,
opts?: { networkVideoUrls?: Set<string>; sampleMs?: number; downloadBudgetMs?: number },
)
| 394 | */ |
| 395 | // fallow-ignore-next-line complexity |
| 396 | export async function captureVideoManifest( |
| 397 | page: Page, |
| 398 | outputDir: string, |
| 399 | progress: (stage: string, detail?: string) => void, |
| 400 | opts?: { networkVideoUrls?: Set<string>; sampleMs?: number; downloadBudgetMs?: number }, |
| 401 | ): Promise<void> { |
| 402 | const netSet = opts?.networkVideoUrls ?? new Set<string>(); |
| 403 | const sampleMs = opts?.sampleMs ?? 0; |
| 404 | const downloadBudgetMs = opts?.downloadBudgetMs ?? 180000; |
| 405 | |
| 406 | // DOM scan, optionally sampled over time (Layer 2) when videos are present. |
| 407 | const initial = await scanVideoDom(page); |
| 408 | const domVideos = |
| 409 | initial.length > 0 && sampleMs > 0 ? await sampleVideoDom(page, sampleMs, netSet) : initial; |
| 410 | |
| 411 | // Merge DOM (rich) + network-only (thin, Layer 1), deduped by download |
| 412 | // filename so a clip seen in both lands once. netSet is read here — AFTER |
| 413 | // sampling — so rotation fetches that arrived during the window count. |
| 414 | const fileKey = (s: string) => (s.split("/").pop() || s).split("?")[0]!; |
| 415 | const byKey = new Map<string, VideoDescriptor & { rich: boolean }>(); |
| 416 | for (const d of domVideos) { |
| 417 | const k = d.filename || fileKey(d.src); |
| 418 | if (!byKey.has(k)) byKey.set(k, { ...d, rich: true }); |
| 419 | } |
| 420 | for (const url of netSet) { |
| 421 | if (!url.startsWith("http")) continue; |
| 422 | const k = fileKey(url); |
| 423 | if (!byKey.has(k)) { |
| 424 | byKey.set(k, { |
| 425 | src: url, |
| 426 | filename: k, |
| 427 | width: 0, |
| 428 | height: 0, |
| 429 | sourceWidth: 0, |
| 430 | sourceHeight: 0, |
| 431 | top: 0, |
| 432 | left: 0, |
| 433 | heading: "", |
| 434 | caption: "", |
| 435 | ariaLabel: "", |
| 436 | rich: false, |
| 437 | }); |
| 438 | } |
| 439 | } |
| 440 | const merged = [...byKey.values()]; |
| 441 | if (merged.length === 0) return; |
| 442 | |
| 443 | const videoManifestDir = join(outputDir, "assets", "videos"); |
| 444 | mkdirSync(videoManifestDir, { recursive: true }); |
| 445 | const previewDir = join(videoManifestDir, "previews"); |
| 446 | mkdirSync(previewDir, { recursive: true }); |
| 447 | |
| 448 | const videoManifest: Array<{ |
| 449 | index: number; |
| 450 | url: string; |
| 451 | filename: string; |
| 452 | width: number; |
| 453 | height: number; |
no test coverage detected