// `archilyzer media attach [--items ] [--match ] // [--create-records] [--replace] [--dry-run]` — a channel's held videos given // their media from a local archive, offline. The work and its rules are // controller/attachMedia.ts; the editor runs the same function as the // `attach-media` job (`POST /api/ops/attach-media`). // // Nothing on the network. It copies into the saved-video store and writes // pointers (and, with --create-records, records) atomically; it does not see // the editor's queues, so not beside a job on the same channel. The channel's // media guard is asked here, as the job kind's `needsMedia` asks it there. import { readFile } from "node:fs/promises"; import { getPaths } from "../lib/paths"; import { assertChannelMediaReachable } from "../lib/channelMedia"; import { isValidChannelSlug, readChannelConfig } from "../controller/channels"; import { attachMedia } from "../controller/attachMedia"; import type { AttachItem } from "../lib/attachMediaPlan"; function parseItems(text: string): AttachItem[] { const raw = JSON.parse(text) as unknown; if (!Array.isArray(raw) || raw.length === 0) throw new Error("--items: a non-empty JSON array of {id, path}"); return raw.map((e, i) => { const r = e as Record; if (typeof r?.id !== "string" || !r.id || /[/\\\0]/.test(r.id) || typeof r.path !== "string" || !r.path) { throw new Error(`--items[${i}]: an object {"id": "