#!/usr/bin/env tsx // On-demand duplicate detection. Runs AFTER build:index + build:stats (it reads // the cues + statsByPath those populate), so it is deliberately not chained into // build:data. // // Flags: // --threshold N duration cutoff in seconds (default 180) // --all-durations no length filter; enables the containment sweep // --blocking S how candidate pairs are nominated: title | duration | // both. Defaults to `title` corpus-wide and `duration` in // shorts mode. Nomination is never evidence — every pair is // then judged on transcript content — so this trades recall // against runtime, not correctness. // --near F Jaccard near-duplicate threshold (default 0.6) // --tolerance N duration bucketing window in seconds (default 2) import { getPaths } from "../lib/paths"; import { detectDuplicateShorts } from "../controller/duplicateShorts"; import type { DuplicateBlocking } from "../lib/duplicates"; import { parseFlags } from "./_parseFlags"; const flags = parseFlags(process.argv.slice(2)); const thresholdSeconds = flags["all-durations"] === "true" ? null : flags.threshold !== undefined ? Number(flags.threshold) : undefined; const BLOCKING: ReadonlyArray = ["title", "duration", "both"]; const blockingFlag = flags.blocking; if ( blockingFlag !== undefined && !BLOCKING.includes(blockingFlag as DuplicateBlocking) ) { console.error( `--blocking must be one of: ${BLOCKING.join(", ")} (got "${blockingFlag}")`, ); process.exit(1); } detectDuplicateShorts({ paths: getPaths(), thresholdSeconds, blocking: blockingFlag as DuplicateBlocking | undefined, nearThreshold: flags.near !== undefined ? Number(flags.near) : undefined, durationToleranceSeconds: flags.tolerance !== undefined ? Number(flags.tolerance) : undefined, }).catch((err) => { console.error(err); process.exit(1); });