···1515//
1616// Usage: node bin/build-filter.mjs <totalSec> (writes graph to stdout)
17171818+import { resolve, dirname } from "node:path";
1919+import { fileURLToPath } from "node:url";
2020+2121+const HERE = dirname(fileURLToPath(import.meta.url));
2222+const ROOT = resolve(HERE, "..");
2323+const REPO = resolve(ROOT, "..");
1824const TOTAL = process.argv[2];
1925if (!TOTAL) {
2026 console.error("usage: build-filter.mjs <totalSec>");
···3743// codec dance. fontsdir picks up the YWFT face shipped in the repo so
3844// the .ass `Style: YWFTProcessing` resolves. The .ass path is escaped
3945// for ffmpeg's filter parser (`:` and `\` need it inside subtitles=...).
4040-const ASS = `${ROOT}/out/subs.ass`.replace(/:/g, "\\:").replace(/\\/g, "/");
4141-const FONTSDIR = `${REPO}/system/public/type/webfonts`.replace(/:/g, "\\:");
4646+// Inside ffmpeg's `filter_complex_script`, the `subtitles=` argument
4747+// uses `:` as a filter-arg separator. Path colons must be escaped with
4848+// `\:`. Backslashes don't appear in Linux paths so we don't worry about
4949+// those. Single-quote the values for safety.
5050+const escFilter = (p) => p.replace(/:/g, "\\:");
5151+const ASS = escFilter(`${ROOT}/out/subs.ass`);
5252+const FONTSDIR = escFilter(`${REPO}/system/public/type/webfonts`);
4253lines.push(`[v0]subtitles='${ASS}':fontsdir='${FONTSDIR}'[final]`);
43544455process.stdout.write(lines.join(";\n") + "\n");