import { removeMediaFile } from "../lib/mediaTier-server"; import path from "node:path"; import fs from "fs-extra"; import type { Paths } from "../lib/paths"; import { isDoNotClean } from "../lib/doNotClean-server"; import { audioFilesToRemove } from "../lib/videoStatus"; import { readChannelConfig } from "./channels"; const { pathExists, readdir } = fs; export type RemoveWrongFormatAudioOptions = { channelSlug: string; paths: Paths; onLog?: (msg: string) => void; signal?: AbortSignal; }; export type RemoveWrongFormatAudioResult = { inspected: number; cleanedDirs: number; removedFiles: number; skipped: number; }; // Delete EVERY finalized audio file that isn't the channel's target format, // across all video dirs — even when the dir has no target file at all (the // failed-extract orphans cleanExtraAudioFormats deliberately leaves alone). Used // to clear pre-audio-integrity downloads so they re-download cleanly. Respects // the per-video "do not clean" marker, like the sibling cleanup controllers. export async function removeWrongFormatAudio({ channelSlug, paths, onLog, signal, }: RemoveWrongFormatAudioOptions): Promise { const log = onLog ?? ((m: string) => console.log(m)); const config = await readChannelConfig(paths, channelSlug); if (!config?.audioFormat) { log( `No audioFormat configured for ${channelSlug}; nothing to clean (set the channel's audio format first).`, ); return { inspected: 0, cleanedDirs: 0, removedFiles: 0, skipped: 0 }; } const targetAudioFile = `audio.${config.audioFormat}`; const dataDir = path.join(paths.channelsDir, channelSlug, "data"); if (!(await pathExists(dataDir))) { log(`No data directory for ${channelSlug}`); return { inspected: 0, cleanedDirs: 0, removedFiles: 0, skipped: 0 }; } const dirs = await readdir(dataDir); let cleanedDirs = 0; let removedFiles = 0; let skipped = 0; for (const id of dirs) { if (signal?.aborted) { log("Cancelled"); break; } const videoDir = path.join(dataDir, id); const entries = await readdir(videoDir).catch(() => [] as string[]); const wrongFormat = audioFilesToRemove(entries, { targetAudioFile, wrongFormatOnly: true, }); if (wrongFormat.length === 0) continue; if (await isDoNotClean(videoDir)) { log(`Skipped ${id} (marked do not clean)`); skipped++; continue; } for (const f of wrongFormat) { await removeMediaFile(videoDir, f); // derefs a tiered link (release 17) log(`Removed ${id}/${f}`); removedFiles++; } cleanedDirs++; } const skippedNote = skipped > 0 ? ` Skipped ${skipped} (do not clean).` : ""; log( `Removed ${removedFiles} wrong-format audio file(s) from ${cleanedDirs} of ${dirs.length} video dir(s) (target ${targetAudioFile}).${skippedNote}`, ); return { inspected: dirs.length, cleanedDirs, removedFiles, skipped }; }