mirror of
https://git.pumpin.online/funkpower/catena.git
synced 2026-10-23 03:07:13 +00:00
avplan: whether a recording plays, and the one call that fixes it
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Decide playability and build the ffmpeg call the way the media server did,
|
||||
* for comparison.
|
||||
*
|
||||
* The functions below are the originals, copied out of the transcode service
|
||||
* verbatim and with their type annotations dropped — the module they live in
|
||||
* opens a database connection when it loads, which an oracle must not do. The
|
||||
* surrounding job bookkeeping (Prisma writes, progress parsing, the temporary
|
||||
* file it renames from) is not copied, because none of it decides anything.
|
||||
*
|
||||
* node oracle/compatible.mjs < cases.ndjson
|
||||
*
|
||||
* One case per line: {path, video, audio, encoder, output}. Writes one JSON
|
||||
* verdict per line.
|
||||
*/
|
||||
|
||||
import { extname } from "node:path";
|
||||
import { createInterface } from "node:readline";
|
||||
|
||||
const APPLE_NATIVE_VIDEO = /^(avc1|h264|hvc1|hev1|hevc)/i;
|
||||
const APPLE_NATIVE_AUDIO = /^(aac|mp4a)/i;
|
||||
const MP4_CONTAINERS = new Set([".mp4", ".m4v", ".mov"]);
|
||||
|
||||
function isAppleCompatible(videoCodec, audioCodec, videoPath) {
|
||||
if (!videoCodec || !audioCodec || !videoPath) return false;
|
||||
return (
|
||||
APPLE_NATIVE_VIDEO.test(videoCodec) &&
|
||||
APPLE_NATIVE_AUDIO.test(audioCodec) &&
|
||||
MP4_CONTAINERS.has(extname(videoPath).toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The encoder table. The original reads COMPATIBLE_VIDEO_ENCODER here; the
|
||||
* name is passed in instead, so one run can cover every branch.
|
||||
*/
|
||||
function videoEncodePlan(name, device) {
|
||||
switch ((name || "h264_nvenc").toLowerCase()) {
|
||||
case "h264_videotoolbox":
|
||||
return {
|
||||
preInput: [],
|
||||
video: ["-c:v", "h264_videotoolbox", "-q:v", "60", "-profile:v", "high", "-pix_fmt", "yuv420p"],
|
||||
};
|
||||
case "h264_vaapi":
|
||||
return {
|
||||
preInput: ["-init_hw_device", `vaapi=va:${device || "/dev/dri/renderD128"}`, "-filter_hw_device", "va"],
|
||||
video: ["-vf", "format=nv12,hwupload", "-c:v", "h264_vaapi", "-qp", "23"],
|
||||
};
|
||||
case "libx264":
|
||||
return {
|
||||
preInput: [],
|
||||
video: ["-c:v", "libx264", "-preset", "veryfast", "-crf", "23", "-pix_fmt", "yuv420p"],
|
||||
};
|
||||
default:
|
||||
return {
|
||||
preInput: [],
|
||||
video: ["-c:v", "h264_nvenc", "-preset", "p5", "-rc", "vbr", "-cq", "23", "-b:v", "0"],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function verdict(c) {
|
||||
const vcodec = c.video || "";
|
||||
const acodec = c.audio || "";
|
||||
|
||||
if (isAppleCompatible(vcodec, acodec, c.path)) {
|
||||
return { path: c.path, playable: true, encoder: null, copies_audio: true, argv: [] };
|
||||
}
|
||||
|
||||
const copyVideo = APPLE_NATIVE_VIDEO.test(vcodec);
|
||||
const copyAudio = APPLE_NATIVE_AUDIO.test(acodec);
|
||||
const base = c.path.slice(0, c.path.length - extname(c.path).length);
|
||||
const out = c.output || `${base}.h264.mp4`;
|
||||
|
||||
const plan = copyVideo
|
||||
? { preInput: [], video: ["-c:v", "copy"] }
|
||||
: videoEncodePlan(c.encoder, c.vaapi_device);
|
||||
const encoder = copyVideo ? "copy" : (c.encoder || "h264_nvenc");
|
||||
|
||||
const argv = [
|
||||
"-nostdin",
|
||||
"-y",
|
||||
...plan.preInput,
|
||||
"-i",
|
||||
c.path,
|
||||
...plan.video,
|
||||
...(copyAudio ? ["-c:a", "copy"] : ["-c:a", "aac", "-b:a", "160k", "-ac", "2"]),
|
||||
"-movflags",
|
||||
"+faststart",
|
||||
out,
|
||||
];
|
||||
return { path: c.path, playable: false, encoder, copies_audio: copyAudio, argv };
|
||||
}
|
||||
|
||||
const lines = createInterface({ input: process.stdin, crlfDelay: Infinity });
|
||||
for await (const line of lines) {
|
||||
if (!line.trim()) continue;
|
||||
process.stdout.write(JSON.stringify(verdict(JSON.parse(line))) + "\n");
|
||||
}
|
||||
Reference in New Issue
Block a user