voxflow 1.18.0 → 1.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -29,7 +29,7 @@ const path = require('path');
|
|
|
29
29
|
const { execFile } = require('child_process');
|
|
30
30
|
|
|
31
31
|
const { parseFlag } = require('../core/args');
|
|
32
|
-
const { runCommand, checkFfmpeg } = require('../core/ffmpeg');
|
|
32
|
+
const { runCommand, checkFfmpeg, resolveFfmpegStaticBin } = require('../core/ffmpeg');
|
|
33
33
|
|
|
34
34
|
// ── ffmpeg binary capability probe ────────────────────────────────────────────
|
|
35
35
|
|
|
@@ -62,10 +62,14 @@ function probeSubtitlesCapableFfmpeg() {
|
|
|
62
62
|
|
|
63
63
|
// 1. System ffmpeg via PATH (or whatever core/ffmpeg.js already resolved)
|
|
64
64
|
tryBinary('ffmpeg', 'system', () => {
|
|
65
|
-
// 2. Bundled ffmpeg-static
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
// 2. Bundled ffmpeg-static.
|
|
66
|
+
// NOTE: cannot use `require('ffmpeg-static')` directly here — when this
|
|
67
|
+
// module is ncc-bundled into dist/index.js, ffmpeg-static's own
|
|
68
|
+
// `path.join(__dirname, ...)` collapses to the bundle's directory and
|
|
69
|
+
// returns a non-existent path. resolveFfmpegStaticBin() handles the
|
|
70
|
+
// ncc-safe recovery via require.resolve('ffmpeg-static/package.json').
|
|
71
|
+
const staticPath = resolveFfmpegStaticBin();
|
|
72
|
+
if (!staticPath) {
|
|
69
73
|
return reject(new Error(
|
|
70
74
|
'No ffmpeg with libass / `subtitles` filter found.\n' +
|
|
71
75
|
' System ffmpeg lacks libass (e.g. Homebrew default formula).\n' +
|
package/lib/core/ffmpeg.js
CHANGED
|
@@ -14,6 +14,43 @@ const fs = require('fs');
|
|
|
14
14
|
|
|
15
15
|
let _resolvedFfmpegPath = null;
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Resolve the bundled `ffmpeg-static` binary path.
|
|
19
|
+
*
|
|
20
|
+
* Naively, `require('ffmpeg-static')` returns the path of its sibling `ffmpeg`
|
|
21
|
+
* binary. But ffmpeg-static's index.js uses `path.join(__dirname, ...)` to
|
|
22
|
+
* compute that path — and when we ncc-bundle this CLI into a single
|
|
23
|
+
* `dist/index.js`, `__dirname` inside the inlined ffmpeg-static module
|
|
24
|
+
* collapses to the *bundle*'s directory (`<install>/voxflow/dist/`), not its
|
|
25
|
+
* real `<install>/voxflow/node_modules/ffmpeg-static/` location. The returned
|
|
26
|
+
* string then points at a file that doesn't exist.
|
|
27
|
+
*
|
|
28
|
+
* Recovery: `require.resolve('ffmpeg-static/package.json')` honors Node's
|
|
29
|
+
* runtime module resolution (not the `__dirname` baked in at bundle time), so
|
|
30
|
+
* it finds the real package directory. The binary lives next to that
|
|
31
|
+
* package.json. This works in both source-mode (npm test) and ncc-bundled
|
|
32
|
+
* mode (the published CLI).
|
|
33
|
+
*
|
|
34
|
+
* Returns: a path string (may or may not exist; caller should fs.existsSync).
|
|
35
|
+
* Or null when ffmpeg-static is not installed at all.
|
|
36
|
+
*/
|
|
37
|
+
function resolveFfmpegStaticBin() {
|
|
38
|
+
// 1. Naive path — works in source mode and any non-ncc context.
|
|
39
|
+
let direct = null;
|
|
40
|
+
try { direct = require('ffmpeg-static'); } catch { /* not installed */ }
|
|
41
|
+
if (direct && fs.existsSync(direct)) return direct;
|
|
42
|
+
|
|
43
|
+
// 2. ncc-safe recovery via package.json resolution.
|
|
44
|
+
try {
|
|
45
|
+
const pkgJson = require.resolve('ffmpeg-static/package.json');
|
|
46
|
+
const exe = process.platform === 'win32' ? 'ffmpeg.exe' : 'ffmpeg';
|
|
47
|
+
const recovered = path.join(path.dirname(pkgJson), exe);
|
|
48
|
+
if (fs.existsSync(recovered)) return recovered;
|
|
49
|
+
} catch { /* not installed */ }
|
|
50
|
+
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
17
54
|
/**
|
|
18
55
|
* Resolve the ffmpeg binary path. Priority:
|
|
19
56
|
* 1. System `ffmpeg` on PATH
|
|
@@ -30,13 +67,11 @@ function resolveFfmpegBin() {
|
|
|
30
67
|
} catch { /* not on PATH */ }
|
|
31
68
|
|
|
32
69
|
// Fall back to ffmpeg-static
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
} catch { /* not installed */ }
|
|
70
|
+
const staticPath = resolveFfmpegStaticBin();
|
|
71
|
+
if (staticPath) {
|
|
72
|
+
_resolvedFfmpegPath = staticPath;
|
|
73
|
+
return _resolvedFfmpegPath;
|
|
74
|
+
}
|
|
40
75
|
|
|
41
76
|
// Nothing found — return 'ffmpeg' and let it fail with a helpful error
|
|
42
77
|
_resolvedFfmpegPath = 'ffmpeg';
|
|
@@ -536,4 +571,6 @@ module.exports = {
|
|
|
536
571
|
concatVideos,
|
|
537
572
|
normalizeVideo,
|
|
538
573
|
detectCjkFont,
|
|
574
|
+
// ffmpeg-static path resolution (used by card-subtitle's libass fallback)
|
|
575
|
+
resolveFfmpegStaticBin,
|
|
539
576
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "voxflow",
|
|
3
|
-
"version": "1.18.
|
|
3
|
+
"version": "1.18.1",
|
|
4
4
|
"description": "AI voice CLI bundled as 6 skills (hub, podcast, transcribe, video, slice, card). Synthesize speech in 200+ voices across 40+ languages, generate multi-speaker AI podcasts, transcribe audio/video with word-level timestamps, dub videos from SRT subtitles, run end-to-end video translation, turn long articles into vertical card video reels via Remotion, and turn text into polished shareable card images or narrated card videos. Backed by a hosted TTS/ASR/LLM/render service with per-user quota (free tier 10K/mo).",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "VoxFlow",
|