import type { Metadata } from "next"; import Link from "next/link"; import { getPaths } from "yt-dlp-transcript-common/lib/paths"; import { mediaScanKey, mediaScanTotals, unreviewedFindings, type MediaScanOverrides, type MediaScanReport, } from "yt-dlp-transcript-common/lib/mediaScan"; import type { DuplicateCluster, DuplicateOverrides, DuplicateReport, } from "yt-dlp-transcript-common/lib/duplicates"; import type { AutoPausedRow } from "yt-dlp-transcript-common/views/review"; import { loadReviewSummary } from "./lib/loadReview"; import { ResumeAutoPausedButton } from "./components/ResumeAutoPausedButton"; import { RunDuplicateDetectionButton } from "./components/RunDuplicateDetectionButton"; import { RunMediaScanButton } from "./components/RunMediaScanButton"; import { MediaScanFindingRow } from "./components/MediaScanFindingRow"; import { DuplicateClusterReview } from "./components/DuplicateClusterReview"; export const dynamic = "force-dynamic"; export const metadata: Metadata = { title: "Review" }; // CORPUS REVIEW: the two findings a PERSON decides about. Duplicate clusters // and media-integrity findings are both flag-only, both carry a reviewed state, // and neither is an operation or a pile of reclaimable bytes — which is why // they are here rather than on an operation page or on /cleanup. export default async function ReviewPage() { const review = await loadReviewSummary(getPaths()); return (

Review

); } // CHANNELS THE MACHINE PAUSED WHILE NOBODY WAS LOOKING. // // The storage watch forces a channel to `paused` when the drive its media is on // stops being there, and restores it when the drive comes back. Both halves are // right; what was missing was anywhere that said it had happened. Until this, // the record surfaced as one chip beside one channel's tier on /channels — so // the way you found out a platter had gone away was by noticing that a channel // had quietly stopped doing anything. // // IT IS A REVIEW FINDING, not a storage row: the decision is about a CHANNEL, // and what to do about it is a judgement — resume it and accept that a lane // will keep skipping it, or go plug the drive in. /storage is where the second // one happens, so the section links there rather than duplicating its controls. // // RENDERED ONLY WHEN THERE IS SOMETHING TO SAY. Unlike the two sections below // it, an empty state here would be a permanent all-clear about a condition that // is normally absent — noise on every render of a page whose whole job is to be // worth reading. function AutoPausedSection({ rows }: { rows: AutoPausedRow[] }) { if (rows.length === 0) return null; return (

Auto-paused channels ({rows.length})

Storage

The storage watch paused these because the drive holding their media stopped answering. Each returns to its previous tier on its own when the drive is back — resume one only to override that, which is a decision the watch will then respect until the next time the drive goes away.

); } // Corrupt media already on disk. Flag-only, exactly like the duplicates section // above it — the probes have existed since the audio-checked download landed; // what was missing was ever running them against files that are already here. function MediaScanSection({ report, overrides, }: { report: MediaScanReport | null; overrides: MediaScanOverrides; }) { const findings = report?.findings ?? []; const outstanding = unreviewedFindings(report, overrides); const totals = report ? mediaScanTotals(report) : null; return (

Media integrity{outstanding.length > 0 ? ` (${outstanding.length})` : ""}

Checks the audio and video files still on disk for corruption or truncation. Flag-only — nothing here deletes anything. {totals && totals.scannedAt ? ( <> {" "} Last scan{" "} {" "} · {totals.filesScanned.toLocaleString()} file(s) across{" "} {totals.videosScanned.toLocaleString()} video dir(s) {totals.deepProbed > 0 ? `, ${totals.deepProbed.toLocaleString()} fully decoded` : ""} . ) : ( " Never run." )}

{findings.length === 0 ? (

{/* "Never run" is NOT "all clear", and saying so would be the one way this section could actively mislead. */} {report ? "Every file the scan read was intact." : "Run a scan to check the media on disk."}

) : ( )}
); } function DuplicatesSection({ report, overrides, }: { report: DuplicateReport | null; overrides: DuplicateOverrides; }) { const clusters = report?.clusters ?? []; return (

Duplicate shorts

Cross-platform, cross-channel duplicate detection (metadata pre-cluster → transcript comparison). Flag-only — review and act manually. {report ? ( <> {" "} Last run{" "} {" "} ·{" "} {report.runConfig.thresholdSeconds === null ? "all durations" : `≤ ${report.runConfig.thresholdSeconds}s`} . ) : ( " Never run." )}

{clusters.length === 0 ? (

{report ? "No duplicate clusters found." : "Run detection to scan."}

) : ( )}
); } function DuplicateClusterCard({ cluster, override, }: { cluster: DuplicateCluster; override: DuplicateOverrides["clusters"][string] | undefined; }) { const matchLabel: Record = { "transcript-exact": "exact transcript", "transcript-near": "near transcript", "title-duration": "title + runtime", }; return (
  • {matchLabel[cluster.matchKind]} {/* Nothing compared these videos' content — one side has no transcript. The cluster is a suspect for a human, stays out of the built site, and shares no derived work until someone confirms it. */} {cluster.needsReview && !override?.confirmed && !override?.notDuplicate && ( needs review )} {override?.confirmed && confirmed} {override?.notDuplicate && not a duplicate} {cluster.score !== null && ( score {cluster.score.toFixed(2)} )} {cluster.contained && clip-of-longer} {cluster.crossPlatform && cross-platform} {cluster.crossChannel && cross-channel} {cluster.videoRefs.length} videos · ~{cluster.durationBucket}s {/* The confirm path exists precisely for `needsReview` clusters: clusterMaySharePartial fails closed, so until a human says "confirmed" these share no digest and never reach a built site. Content-confirmed clusters need no confirmation — but they can still be rejected, which is the only way to un-assert a wrong one. */}
  • ); } function Badge({ children }: { children: React.ReactNode }) { return ( {children} ); }