"use client"; import type { TranscriptDetail } from "../lib/transcripts"; import { channelRef, readerFor } from "../lib/archive/readers"; import { idbGet, idbPutBatch } from "./transcriptStore"; import { makeId, splitId } from "./originId"; // The manifest -> slugToPage -> page walk is the ArchiveReader's // (lib/archive/reader.ts), including the per-channel manifest PromiseMap and // the byte-budgeted page LRU. What stays here is what is genuinely the // viewer's: the per-id memo, the in-flight dedupe, and warming IndexedDB with // every record of a page we already paid for. const resolved = new Map(); const inFlight = new Map>(); // `id` is an OriginId: a bare "channelSlug/videoId" for same-origin content, or // "origin\tchannelSlug/videoId" for a federated cross-origin video. Maps and // the IDB store are keyed by the full id, so origins never collide. export function fetchTranscript(id: string): Promise { const hit = resolved.get(id); if (hit) return Promise.resolve(hit); const flying = inFlight.get(id); if (flying) return flying; const p = load(id).then((detail) => { resolved.set(id, detail); inFlight.delete(id); return detail; }); p.catch(() => { inFlight.delete(id); }); inFlight.set(id, p); return p; } async function load(id: string): Promise { const stored = await idbGet(id); if (stored) return stored; const { origin, slug } = splitId(id); const slashIdx = slug.indexOf("/"); if (slashIdx < 0) throw new Error(`Malformed transcript slug: ${slug}`); const channelSlug = slug.slice(0, slashIdx); const videoId = slug.slice(slashIdx + 1); const reader = readerFor(origin); const ch = channelRef(channelSlug, origin); const manifest = await reader.transcriptsManifest(ch); const pageIndex = manifest.slugToPage[videoId]; if (pageIndex === undefined) throw new Error(`Unknown transcript slug: ${slug}`); const page = await reader.transcriptPage(ch, pageIndex); let found: TranscriptDetail | undefined; for (const entry of page) { // Re-key warmed entries by their OriginId so a cross-origin // channelSlug/videoId can't shadow a same-origin one with the same slug. const entryId = makeId(origin, entry.slug); if (entry.slug === slug) found = entry; // Opportunistically warm the per-id memory + IDB caches so subsequent // fetches for other videos in this page hit without a network round-trip. // Huge win for searchPipeline which iterates many slugs from the same // channel/page. resolved.set(entryId, entry); idbPutBatch(entryId, entry); } if (!found) throw new Error(`Transcript ${slug} missing from page ${pageIndex}`); return found; }