authorgravatar for git@paperclover.netclover caruso <git@paperclover.net> 2025-07-03 10:34:19-07:00
committergravatar for git@paperclover.netclover caruso <git@paperclover.net> 2025-10-14 02:40:45-07:00
log5e2a792bb470e4c9f1d4a97391400b4c0b711b14
tree1f6c9adf44a9a769bc728d8ed19d5c2ffd752944
parentcd82bee436de52183b6310615e651ff0561ebe36
signature Commit is signed but in an unrecognized format.

actually run ffmpeg fr fr


3 files changed, 59 insertions(+), 22 deletions(-)

framework/lib/async.ts+1-1
......@@ -74,7 +74,7 @@ export class Queue<T, R> {
7474 addReturn(args: T) {
7575 this.#total += 1;
7676 this.updateTotal();
77 if (this.#active.length > this.#maxJobs) {
77 if (this.#active.length >= this.#maxJobs) {
7878 const { promise, resolve, reject } = Promise.withResolvers<R>();
7979 this.#queue.push([args, resolve, reject]);
8080 return promise;
src/file-viewer/bin/scan3.ts+53-19
......@@ -136,7 +136,7 @@ export async function main() {
136136 },
137137 getItemText: ({ mediaFile, processor }) =>
138138 `${mediaFile.path.slice(1)} - ${processor.name}`,
139 maxJobs: 2,
139 maxJobs: 1,
140140 });
141141
142142 function decodeProcessors(input: string) {
......@@ -430,46 +430,79 @@ const procImageSubsets: Process = {
430430 }
431431 },
432432};
433
434const procVideos = transcodeRules.videoFormats.map((preset) => ({
435 name: "encode image subsets",
436 include: rules.extsImage,
433const qualityMap: Record<string, string> = {
434 u: "ultra-high",
435 h: "high",
436 m: "medium",
437 l: "low",
438 d: "data-saving",
439};
440const procVideos = transcodeRules.videoFormats.map<Process>((preset) => ({
441 name: `encode ${preset.codec} ${UNWRAP(qualityMap[preset.id[1]])}`,
442 include: rules.extsVideo,
437443 enable: ffmpegBin != null,
438 async run({ absPath, mediaFile, stat, spin }) {
439 await produceAsset(`${mediaFile}/${preset.id}`, async (base) => {
444 async run({ absPath, mediaFile, spin }) {
445 await produceAsset(`${mediaFile.hash}/${preset.id}`, async (base) => {
440446 base = path.dirname(base);
447 await fs.mkdir(base);
448
449 let inputArgs = ["-i", absPath];
450 try {
451 const config = await fs.readJson<any>(
452 path.join(
453 path.dirname(absPath),
454 path.basename(absPath, path.extname(absPath)) + ".json",
455 ),
456 );
457 if (config.encoder && typeof config.encoder.videoSrc === "string") {
458 const { videoSrc, audioSrc, rate } = config.encoder;
459 inputArgs = [
460 ...rate ? ["-r", String(rate)] : [],
461 "-i",
462 videoSrc,
463 ...audioSrc ? ["-i", audioSrc] : [],
464 ];
465 }
466 } catch (err: any) {
467 if (err?.code !== "ENOENT") throw err;
468 }
469
441470 const args = transcodeRules.getVideoArgs(
442471 preset,
443472 base,
444 ["-i", absPath],
473 inputArgs,
445474 );
446475 try {
447 const quality = {
448 u: "ultra-high",
449 h: "high",
450 m: "medium",
451 l: "low",
452 d: "data-saving",
453 }[preset.id[1]] ?? preset.id;
476 const fakeProgress = new Progress({ text: spin.text, spinner: null });
477 fakeProgress.stop();
478 spin.format = (now: number) => fakeProgress.format(now);
479 // @ts-expect-error
480 fakeProgress.redraw = () => spin.redraw();
481
454482 await ffmpeg.spawn({
455483 ffmpeg: ffmpegBin!,
456 title: `${mediaFile.path.slice(1)} (${preset.codec} ${quality})`,
484 title: fakeProgress.text,
485 progress: fakeProgress,
457486 args,
458487 });
459488 return await collectFiles();
460489 } catch (err) {
461490 for (const file of await collectFiles()) {
462 // TODO: delete assets off disk
491 try {
492 fs.rm(file);
493 } catch {}
463494 }
464495 throw err;
465496 }
466497
467498 async function collectFiles(): Promise<string[]> {
468 throw new Error("!");
499 return (await fs.readdir(base))
500 .filter((basename) => basename.startsWith(preset.id))
501 .map((basename) => path.join(base, basename));
469502 }
470503 });
471504 },
472} satisfies Process));
505}));
473506
474507const processors = [
475508 procDimensions,
......@@ -596,6 +629,7 @@ export function testProgram(name: string, helpArgument: string) {
596629
597630const monthMilliseconds = 30 * 24 * 60 * 60 * 1000;
598631
632import { Progress } from "@paperclover/console/Progress";
599633import { Spinner } from "@paperclover/console/Spinner";
600634import * as async from "#sitegen/async";
601635import * as fs from "#sitegen/fs";
src/file-viewer/ffmpeg.ts+5-2
......@@ -23,13 +23,15 @@ export interface SpawnOptions {
2323 title: string;
2424 ffmpeg?: string;
2525 progress?: Progress;
26 cwd: string;
2627}
2728
2829export async function spawn(options: SpawnOptions) {
29 const { ffmpeg = "ffmpeg", args, title } = options;
30 const { ffmpeg = "ffmpeg", args, title, cwd } = options;
3031 const proc = child_process.spawn(ffmpeg, args, {
3132 stdio: ["ignore", "inherit", "pipe"],
3233 env: { ...process.env, SVT_LOG: "2" },
34 cwd,
3335 });
3436 const parser = new Parse();
3537 const bar = options.progress ?? new Progress({ text: title });
......@@ -63,8 +65,10 @@ export async function spawn(options: SpawnOptions) {
6365 e.args = [ffmpeg, ...args].join(" ");
6466 e.code = code;
6567 e.signal = signal;
68 bar.error(e.message);
6669 return e;
6770 }
71 bar.success(title);
6872}
6973
7074export class Parse {
......@@ -158,4 +162,3 @@ import * as process from "node:process";
158162import events from "node:events";
159163import * as path from "node:path";
160164import { Progress } from "@paperclover/console/Progress";
161