From 502786b689fcc087ca2f866d90ca6e893a97f918 Mon Sep 17 00:00:00 2001 From: chloe caruso Date: Thu, 3 Jul 2025 10:34:19 -0700 Subject: [PATCH] actually run ffmpeg fr fr --- framework/lib/async.ts | 2 +- src/file-viewer/bin/scan3.ts | 72 ++++++++++++++++++++++++++---------- src/file-viewer/ffmpeg.ts | 7 +++- 3 files changed, 59 insertions(+), 22 deletions(-) diff --git a/framework/lib/async.ts b/framework/lib/async.ts index c213ce7..522e156 100644 --- a/framework/lib/async.ts +++ b/framework/lib/async.ts @@ -74,7 +74,7 @@ export class Queue { addReturn(args: T) { this.#total += 1; this.updateTotal(); - if (this.#active.length > this.#maxJobs) { + if (this.#active.length >= this.#maxJobs) { const { promise, resolve, reject } = Promise.withResolvers(); this.#queue.push([args, resolve, reject]); return promise; diff --git a/src/file-viewer/bin/scan3.ts b/src/file-viewer/bin/scan3.ts index b5fbeef..1ffae57 100644 --- a/src/file-viewer/bin/scan3.ts +++ b/src/file-viewer/bin/scan3.ts @@ -136,7 +136,7 @@ export async function main() { }, getItemText: ({ mediaFile, processor }) => `${mediaFile.path.slice(1)} - ${processor.name}`, - maxJobs: 2, + maxJobs: 1, }); function decodeProcessors(input: string) { @@ -430,46 +430,79 @@ const procImageSubsets: Process = { } }, }; - -const procVideos = transcodeRules.videoFormats.map((preset) => ({ - name: "encode image subsets", - include: rules.extsImage, +const qualityMap: Record = { + u: "ultra-high", + h: "high", + m: "medium", + l: "low", + d: "data-saving", +}; +const procVideos = transcodeRules.videoFormats.map((preset) => ({ + name: `encode ${preset.codec} ${UNWRAP(qualityMap[preset.id[1]])}`, + include: rules.extsVideo, enable: ffmpegBin != null, - async run({ absPath, mediaFile, stat, spin }) { - await produceAsset(`${mediaFile}/${preset.id}`, async (base) => { + async run({ absPath, mediaFile, spin }) { + await produceAsset(`${mediaFile.hash}/${preset.id}`, async (base) => { base = path.dirname(base); + await fs.mkdir(base); + + let inputArgs = ["-i", absPath]; + try { + const config = await fs.readJson( + path.join( + path.dirname(absPath), + path.basename(absPath, path.extname(absPath)) + ".json", + ), + ); + if (config.encoder && typeof config.encoder.videoSrc === "string") { + const { videoSrc, audioSrc, rate } = config.encoder; + inputArgs = [ + ...rate ? ["-r", String(rate)] : [], + "-i", + videoSrc, + ...audioSrc ? ["-i", audioSrc] : [], + ]; + } + } catch (err: any) { + if (err?.code !== "ENOENT") throw err; + } + const args = transcodeRules.getVideoArgs( preset, base, - ["-i", absPath], + inputArgs, ); try { - const quality = { - u: "ultra-high", - h: "high", - m: "medium", - l: "low", - d: "data-saving", - }[preset.id[1]] ?? preset.id; + const fakeProgress = new Progress({ text: spin.text, spinner: null }); + fakeProgress.stop(); + spin.format = (now: number) => fakeProgress.format(now); + // @ts-expect-error + fakeProgress.redraw = () => spin.redraw(); + await ffmpeg.spawn({ ffmpeg: ffmpegBin!, - title: `${mediaFile.path.slice(1)} (${preset.codec} ${quality})`, + title: fakeProgress.text, + progress: fakeProgress, args, }); return await collectFiles(); } catch (err) { for (const file of await collectFiles()) { - // TODO: delete assets off disk + try { + fs.rm(file); + } catch {} } throw err; } async function collectFiles(): Promise { - throw new Error("!"); + return (await fs.readdir(base)) + .filter((basename) => basename.startsWith(preset.id)) + .map((basename) => path.join(base, basename)); } }); }, -} satisfies Process)); +})); const processors = [ procDimensions, @@ -596,6 +629,7 @@ export function testProgram(name: string, helpArgument: string) { const monthMilliseconds = 30 * 24 * 60 * 60 * 1000; +import { Progress } from "@paperclover/console/Progress"; import { Spinner } from "@paperclover/console/Spinner"; import * as async from "#sitegen/async"; import * as fs from "#sitegen/fs"; diff --git a/src/file-viewer/ffmpeg.ts b/src/file-viewer/ffmpeg.ts index f67caaf..4546f90 100644 --- a/src/file-viewer/ffmpeg.ts +++ b/src/file-viewer/ffmpeg.ts @@ -23,13 +23,15 @@ export interface SpawnOptions { title: string; ffmpeg?: string; progress?: Progress; + cwd: string; } export async function spawn(options: SpawnOptions) { - const { ffmpeg = "ffmpeg", args, title } = options; + const { ffmpeg = "ffmpeg", args, title, cwd } = options; const proc = child_process.spawn(ffmpeg, args, { stdio: ["ignore", "inherit", "pipe"], env: { ...process.env, SVT_LOG: "2" }, + cwd, }); const parser = new Parse(); const bar = options.progress ?? new Progress({ text: title }); @@ -63,8 +65,10 @@ export async function spawn(options: SpawnOptions) { e.args = [ffmpeg, ...args].join(" "); e.code = code; e.signal = signal; + bar.error(e.message); return e; } + bar.success(title); } export class Parse { @@ -158,4 +162,3 @@ import * as process from "node:process"; import events from "node:events"; import * as path from "node:path"; import { Progress } from "@paperclover/console/Progress"; -