import type { DatabaseSync } from "node:sqlite"; // Backfills the `start` parameter on stored YouTube embed URLs. // // `rich_content.embedUrl` is computed once, at dump creation, and previously // dropped the timestamp carried by the source URL (`?t=1265`, `?t=1h2m3s`), so // those videos always opened at 0:00 in the global player. The provider now // translates `t`/`start` into the `start` param the iframe player honours; this // migration applies the same translation to rows written before that fix. // // Purely local — it re-derives the param from the already-stored source URL and // makes no network calls. Self-contained (no imports from api/services) so it // stays reproducible even if the provider's parsing evolves later. // // Idempotent: rows whose embed URL already carries a `start` are skipped, so a // fresh database built from schema.sql is simply a no-op. /** Parse `t`/`start` — plain seconds, `90s`, or `1h2m3s` / `20m5s`. */ function extractStartSeconds(url: string): number | null { let raw: string | null; try { const u = new URL(url); raw = u.searchParams.get("t") ?? u.searchParams.get("start"); } catch { return null; } if (!raw) return null; if (/^\d+$/.test(raw)) return Number(raw); const hms = /^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/i.exec(raw); if (!hms || (!hms[1] && !hms[2] && !hms[3])) return null; return Number(hms[1] ?? 0) * 3600 + Number(hms[2] ?? 0) * 60 + Number(hms[3] ?? 0); } export function up(db: DatabaseSync): void { const rows = db.prepare( `SELECT id, url, rich_content FROM dumps WHERE kind = 'url' AND url IS NOT NULL AND rich_content IS NOT NULL;`, ).all() as { id: string; url: string; rich_content: string }[]; const update = db.prepare( `UPDATE dumps SET rich_content = ? WHERE id = ?;`, ); let patched = 0; for (const row of rows) { let rich: { type?: string; embedUrl?: string }; try { rich = JSON.parse(row.rich_content); } catch { continue; // malformed payload — leave it untouched } if (rich.type !== "youtube" || !rich.embedUrl) continue; const start = extractStartSeconds(row.url); if (!start) continue; let embed: URL; try { embed = new URL(rich.embedUrl); } catch { continue; } if (embed.searchParams.has("start")) continue; embed.searchParams.set("start", String(start)); update.run(JSON.stringify({ ...rich, embedUrl: embed.toString() }), row.id); patched++; } if (patched > 0) { console.log(`[migrate] 0010: added start offset to ${patched} embed(s)`); } }