@ignix-social/editor 0.1.18 → 0.1.19

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/transcribe.mjs +29 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ignix-social/editor",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "Ignix Editor: render HTML video compositions (reels, edits, clips) on your own computer. Based on HyperFrames.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -5,19 +5,40 @@
5
5
  import { spawnSync } from 'node:child_process';
6
6
  import { mkdtempSync, readFileSync, rmSync, statSync } from 'node:fs';
7
7
  import { tmpdir } from 'node:os';
8
- import { join } from 'node:path';
8
+ import { basename, dirname, join, relative } from 'node:path';
9
+
10
+ /**
11
+ * Dónde trabaja whisper. En Windows, whisper.cpp NO abre rutas con caracteres fuera del ASCII
12
+ * (pasó con «C:\Users\José María»: el modelo, el audio y la salida cuelgan todos de esa
13
+ * carpeta, %TEMP% incluido). Así que se le ejecuta DESDE la carpeta del modelo y se le pasan
14
+ * solo rutas relativas, que son ASCII: el modelo por su nombre y el audio y la salida en una
15
+ * subcarpeta temporal de ahí. ffmpeg y el arranque del proceso sí entienden Unicode.
16
+ */
17
+ function whisperWorkspace(model) {
18
+ if (process.platform === 'win32') {
19
+ try {
20
+ const cwd = dirname(model);
21
+ const tmp = mkdtempSync(join(cwd, 'asr-'));
22
+ return { tmp, cwd, modelArg: basename(model), arg: (p) => relative(cwd, p) };
23
+ } catch {
24
+ /* carpeta del modelo sin permiso de escritura: como en el resto de sistemas */
25
+ }
26
+ }
27
+ return { tmp: mkdtempSync(join(tmpdir(), 'ignix-editor-asr-')), cwd: undefined, modelArg: model, arg: (p) => p };
28
+ }
9
29
 
10
30
  const norm = (s) => s.toLowerCase().normalize('NFD').replace(/[̀-ͯ]/g, '').replace(/[^\p{L}\p{N}]/gu, '');
11
31
 
12
32
  export function transcribe({ ffmpeg, whisper, model, input, language = 'es', threads = 8 }) {
13
- const tmp = mkdtempSync(join(tmpdir(), 'ignix-editor-asr-'));
33
+ const ws = whisperWorkspace(model);
34
+ const tmp = ws.tmp;
14
35
  try {
15
36
  const wav = join(tmp, 'audio.wav');
16
37
  const a = spawnSync(ffmpeg, ['-nostdin', '-hide_banner', '-loglevel', 'error', '-y', '-i', input, '-vn', '-ar', '16000', '-ac', '1', '-c:a', 'pcm_s16le', wav], { encoding: 'utf8' });
17
38
  if (a.status !== 0) throw new Error(`Could not read the audio of ${input}: ${a.stderr.slice(-300)}`);
18
39
  const out = join(tmp, 'out');
19
- const args = ['-m', model, '-l', language, '-ml', '1', '-sow', '-oj', '-of', out, '-t', String(threads), '-f', wav];
20
- const run = (extra) => spawnSync(whisper, [...extra, ...args], { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });
40
+ const args = ['-m', ws.modelArg, '-l', language, '-ml', '1', '-sow', '-oj', '-of', ws.arg(out), '-t', String(threads), '-f', ws.arg(wav)];
41
+ const run = (extra) => spawnSync(whisper, [...extra, ...args], { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024, cwd: ws.cwd });
21
42
  // En Mac Intel la GPU (Metal) se cuelga con whisper: CPU directamente. En el resto, GPU y,
22
43
  // si falla, otra vez por CPU.
23
44
  const intelMac = process.platform === 'darwin' && process.arch === 'x64';
@@ -75,15 +96,16 @@ export function alignToScript(script, tokens, end) {
75
96
  * confianza (media de sus tokens) y las frases (cortadas en un punto o en una pausa).
76
97
  */
77
98
  export function transcribeDetailed({ ffmpeg, whisper, model, input, language = null, threads = 8, maxWords = 6000, maxSegments = 800 }) {
78
- const tmp = mkdtempSync(join(tmpdir(), 'ignix-editor-asr-'));
99
+ const ws = whisperWorkspace(model);
100
+ const tmp = ws.tmp;
79
101
  try {
80
102
  const wav = join(tmp, 'audio.wav');
81
103
  const a = spawnSync(ffmpeg, ['-nostdin', '-hide_banner', '-loglevel', 'error', '-y', '-i', input, '-vn', '-ar', '16000', '-ac', '1', '-c:a', 'pcm_s16le', wav], { encoding: 'utf8' });
82
104
  if (a.status !== 0) throw new Error(`Could not read the audio of ${input}: ${a.stderr.slice(-300)}`);
83
105
  const duration = statSync(wav).size > 44 ? (statSync(wav).size - 44) / 32000 : 0;
84
106
  const out = join(tmp, 'out');
85
- const args = ['-m', model, '-l', language || 'auto', '-ml', '1', '-sow', '-ojf', '-of', out, '-t', String(threads), '-f', wav];
86
- const run = (extra) => spawnSync(whisper, [...extra, ...args], { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 });
107
+ const args = ['-m', ws.modelArg, '-l', language || 'auto', '-ml', '1', '-sow', '-ojf', '-of', ws.arg(out), '-t', String(threads), '-f', ws.arg(wav)];
108
+ const run = (extra) => spawnSync(whisper, [...extra, ...args], { encoding: 'utf8', maxBuffer: 64 * 1024 * 1024, cwd: ws.cwd });
87
109
  const intelMac = process.platform === 'darwin' && process.arch === 'x64';
88
110
  let w = run(intelMac ? ['-ng'] : []);
89
111
  let device = intelMac ? 'cpu' : 'gpu';