#!/usr/bin/env tsx import path from "node:path"; import { readdir, stat } from "node:fs/promises"; import { getPaths } from "../lib/paths"; import { reconcileVideoDirs, type ReconcileResult, } from "../controller/reconcileVideoDirs"; import { parseFlags } from "./_parseFlags"; // Migrate / repair video data dirs so they match the app's canonical id layout // (`data//`). See common/controller/reconcileVideoDirs.ts. // // Usage: // reconcile-video-dirs.ts [--channel ] [--dry-run] [--verbose] // // --channel reconcile only that channel (default: every channel with a data/) // --dry-run report what would change without touching the filesystem // --verbose print each rename/merge/skip line const flags = parseFlags(process.argv.slice(2)); const dryRun = flags["dry-run"] === "true"; const verbose = flags.verbose === "true"; const paths = getPaths(); async function channelHasData(slug: string): Promise { try { return ( await stat(path.join(paths.channelsDir, slug, "data")) ).isDirectory(); } catch { return false; } } async function listChannels(): Promise { const entries = await readdir(paths.channelsDir, { withFileTypes: true, }).catch(() => []); const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name); const withData: string[] = []; for (const slug of dirs) if (await channelHasData(slug)) withData.push(slug); return withData.sort(); } function summarize(slug: string, r: ReconcileResult): void { console.log( `${slug}: renamed ${r.renamed.length}, merged ${r.merged.length}, ` + `conflicts ${r.conflicts.length}, skipped ${r.skipped.length}`, ); for (const c of r.conflicts) { console.error(` CONFLICT ${c.from} -> ${c.to}: ${c.reason}`); } } async function main(): Promise { const channels = flags.channel ? [flags.channel] : await listChannels(); if (channels.length === 0) { console.error("No channels with a data/ directory found."); process.exit(2); } if (dryRun) console.log("(dry-run: no files will be moved)\n"); let totalConflicts = 0; for (const slug of channels) { const result = await reconcileVideoDirs({ channelDir: path.join(paths.channelsDir, slug), dryRun, onLog: verbose ? (s) => process.stdout.write(` ${s}`) : undefined, }); summarize(slug, result); totalConflicts += result.conflicts.length; } if (totalConflicts > 0) process.exit(1); } main().catch((err) => { console.error(err); process.exit(1); });