@higgsfield/video-editor-mcp 1.1.1-dev.0 → 2.0.0
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.
- package/README.md +63 -26
- package/dist/cli.mjs +284 -112
- package/dist/guide.md +2 -3
- package/dist/index.html +1 -1
- package/dist/launcher.mjs +34 -14
- package/dist/server.mjs +209 -265
- package/node_modules/@higgsfield/higgsedit/dist/api.d.ts +481 -0
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-PFQYUJQL.mjs → chunk-3XVMJSV2.mjs} +1 -1
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-F4CMIG5G.mjs → chunk-4VE54E4U.mjs} +3 -1
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-2JYLVSVV.mjs → chunk-55JARHLA.mjs} +205 -65
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-YLVXQIPX.mjs → chunk-E4N76T2R.mjs} +1 -1
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-OCIZ36XD.mjs → chunk-ECRSOIKI.mjs} +1 -1
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-IEPPEDEU.mjs → chunk-ENALCTAD.mjs} +1 -1
- package/node_modules/@higgsfield/higgsedit/dist/chunk-HY2E7IWX.mjs +43 -0
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-NARTAWRN.mjs → chunk-HYCUOCUQ.mjs} +9 -4
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-C3GSB3XZ.mjs → chunk-NLEIBCAK.mjs} +1 -7
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-2WKNH7OL.mjs → chunk-O3AVYZ6U.mjs} +1 -1
- package/node_modules/@higgsfield/higgsedit/dist/chunk-UXO22E3P.mjs +22 -0
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-BH2QF635.mjs → chunk-V5XTCFYK.mjs} +4 -100
- package/node_modules/@higgsfield/higgsedit/dist/chunk-W2TANYEH.mjs +118 -0
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-DVN4LUXV.mjs → chunk-XY5CCUOZ.mjs} +2 -6
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-KIWGVAPJ.mjs → chunk-Y7SAX2P4.mjs} +8 -12
- package/node_modules/@higgsfield/higgsedit/dist/chunk-YNAIRXOP.mjs +22 -0
- package/node_modules/@higgsfield/higgsedit/dist/{chunk-WENFTPXQ.mjs → chunk-ZO3NDQD3.mjs} +1 -1
- package/node_modules/@higgsfield/higgsedit/dist/higgsedit.mjs +13 -13
- package/node_modules/@higgsfield/higgsedit/package.json +4 -1
- package/package.json +4 -4
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/skills/video-editing/SKILL.md +109 -95
- package/plugin/mcp.json +0 -10
- package/plugin/plugin.json +0 -41
- package/plugin/skills/motion-craft/SKILL.md +0 -87
- package/plugin/skills/motion-craft/agents/openai.yaml +0 -6
- package/plugin/skills/motion-craft/references/causality.md +0 -25
- package/plugin/skills/motion-craft/references/composition.md +0 -20
- package/plugin/skills/motion-craft/references/easing.md +0 -19
- package/plugin/skills/motion-craft/references/qc.md +0 -33
- package/plugin/skills/motion-craft/references/recipes.md +0 -31
- package/plugin/skills/motion-craft/references/stepped.md +0 -43
- package/plugin/skills/motion-craft/references/timing.md +0 -60
- package/plugin/skills/motion-craft/references/tsx.md +0 -60
- package/plugin/skills/motion-craft/references/typography.md +0 -22
- package/plugin/skills/video-editing/agents/openai.yaml +0 -6
- package/plugin/skills/video-editing/references/animation-contract.md +0 -45
- package/plugin/skills/video-editing/references/assembly.md +0 -58
- package/plugin/skills/video-editing/references/caption-systems.md +0 -29
- package/plugin/skills/video-editing/references/caption-titling.md +0 -41
- package/plugin/skills/video-editing/references/clip-geometry.md +0 -37
- package/plugin/skills/video-editing/references/compose.md +0 -283
- package/plugin/skills/video-editing/references/editor-measured.md +0 -37
- package/plugin/skills/video-editing/references/failure-modes.md +0 -37
- package/plugin/skills/video-editing/references/local-setup.md +0 -25
- package/plugin/skills/video-editing/references/motion-language.md +0 -27
- package/plugin/skills/video-editing/references/provenance.md +0 -7
- package/plugin/skills/video-editing/references/sandbox.md +0 -31
- package/plugin/skills/video-editing/references/shot-blueprints.md +0 -24
- package/plugin/skills/video-editing/references/title-animation.md +0 -29
- package/plugin/skills/video-editing/references/tsx-projects.md +0 -187
- package/plugin/skills/video-editing/references/workflows.md +0 -41
|
@@ -554,19 +554,23 @@ async function frame(dir, at, out, depth = 8, allowFontFallback = false) {
|
|
|
554
554
|
await session.close();
|
|
555
555
|
}
|
|
556
556
|
}
|
|
557
|
-
async function
|
|
557
|
+
async function sheetPng(dir, times, columns, width, allowFontFallback = false) {
|
|
558
558
|
const session = await openCapture(dir, 8, allowFontFallback);
|
|
559
559
|
try {
|
|
560
560
|
const frames = times.map((t) => toFrame(t, session.composition.fps, 0));
|
|
561
561
|
const png = await api(session.page).sheet(frames, { columns, width });
|
|
562
562
|
await session.assertFonts();
|
|
563
|
-
|
|
564
|
-
writeFileSync(out, Buffer.from(png, "base64"));
|
|
565
|
-
return { out: resolve(out), frames };
|
|
563
|
+
return { png: Buffer.from(png, "base64"), frames };
|
|
566
564
|
} finally {
|
|
567
565
|
await session.close();
|
|
568
566
|
}
|
|
569
567
|
}
|
|
568
|
+
async function sheet(dir, times, out, columns, width, allowFontFallback = false) {
|
|
569
|
+
const { png, frames } = await sheetPng(dir, times, columns, width, allowFontFallback);
|
|
570
|
+
mkdirSync(dirname(resolve(out)), { recursive: true });
|
|
571
|
+
writeFileSync(out, png);
|
|
572
|
+
return { out: resolve(out), frames };
|
|
573
|
+
}
|
|
570
574
|
function shuffle(items, seed = 7) {
|
|
571
575
|
const out = [...items];
|
|
572
576
|
let s = seed;
|
|
@@ -610,6 +614,7 @@ export {
|
|
|
610
614
|
rateFilter,
|
|
611
615
|
render,
|
|
612
616
|
frame,
|
|
617
|
+
sheetPng,
|
|
613
618
|
sheet,
|
|
614
619
|
shuffle,
|
|
615
620
|
verify
|
|
@@ -12,11 +12,6 @@ import {
|
|
|
12
12
|
parseKey,
|
|
13
13
|
sourceFacts
|
|
14
14
|
} from "./chunk-MV2JTTBQ.mjs";
|
|
15
|
-
import "./chunk-UDL5AHKO.mjs";
|
|
16
|
-
import "./chunk-LTKMGZ6T.mjs";
|
|
17
|
-
import "./chunk-P2BGHTRR.mjs";
|
|
18
|
-
import "./chunk-PAHMD6SU.mjs";
|
|
19
|
-
import "./chunk-2VKQHJMM.mjs";
|
|
20
15
|
import {
|
|
21
16
|
Node,
|
|
22
17
|
Sync,
|
|
@@ -28,8 +23,6 @@ import {
|
|
|
28
23
|
syncPlacement,
|
|
29
24
|
walk
|
|
30
25
|
} from "./chunk-BV5COOQZ.mjs";
|
|
31
|
-
import "./chunk-F7YUKEI5.mjs";
|
|
32
|
-
import "./chunk-75WO2NOF.mjs";
|
|
33
26
|
|
|
34
27
|
// src/outline.ts
|
|
35
28
|
import { basename } from "node:path";
|
|
@@ -165,6 +158,7 @@ async function outline(dir) {
|
|
|
165
158
|
};
|
|
166
159
|
return { ...base, text: textOf(base) };
|
|
167
160
|
}
|
|
161
|
+
|
|
168
162
|
export {
|
|
169
163
|
outline
|
|
170
164
|
};
|
|
@@ -6,7 +6,7 @@ const __filename = __higgseditPath(import.meta.url);
|
|
|
6
6
|
const __dirname = __filename.slice(0, __filename.lastIndexOf("/"));
|
|
7
7
|
|
|
8
8
|
// package.json
|
|
9
|
-
var package_default = { name: "@higgsfield/higgsedit", version: "0.
|
|
9
|
+
var package_default = { name: "@higgsfield/higgsedit", version: "1.0.0", devDependencies: { typescript: "7.0.2" } };
|
|
10
10
|
|
|
11
11
|
// src/manifest.ts
|
|
12
12
|
var CLI_MANIFEST = package_default;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __higgseditRequire } from "node:module";
|
|
3
|
+
import { fileURLToPath as __higgseditPath } from "node:url";
|
|
4
|
+
const require = __higgseditRequire(import.meta.url);
|
|
5
|
+
const __filename = __higgseditPath(import.meta.url);
|
|
6
|
+
const __dirname = __filename.slice(0, __filename.lastIndexOf("/"));
|
|
7
|
+
import {
|
|
8
|
+
outline
|
|
9
|
+
} from "./chunk-NLEIBCAK.mjs";
|
|
10
|
+
import "./chunk-OF6RMSM2.mjs";
|
|
11
|
+
import "./chunk-MV2JTTBQ.mjs";
|
|
12
|
+
import "./chunk-UDL5AHKO.mjs";
|
|
13
|
+
import "./chunk-LTKMGZ6T.mjs";
|
|
14
|
+
import "./chunk-P2BGHTRR.mjs";
|
|
15
|
+
import "./chunk-PAHMD6SU.mjs";
|
|
16
|
+
import "./chunk-2VKQHJMM.mjs";
|
|
17
|
+
import "./chunk-BV5COOQZ.mjs";
|
|
18
|
+
import "./chunk-F7YUKEI5.mjs";
|
|
19
|
+
import "./chunk-75WO2NOF.mjs";
|
|
20
|
+
export {
|
|
21
|
+
outline
|
|
22
|
+
};
|
|
@@ -14,7 +14,6 @@ import {
|
|
|
14
14
|
outputDir,
|
|
15
15
|
pool,
|
|
16
16
|
probeMedia,
|
|
17
|
-
resolveMediaPath,
|
|
18
17
|
textHeight,
|
|
19
18
|
textWidth,
|
|
20
19
|
videoSource,
|
|
@@ -23,19 +22,9 @@ import {
|
|
|
23
22
|
import {
|
|
24
23
|
transcribe
|
|
25
24
|
} from "./chunk-L5LWBA6A.mjs";
|
|
26
|
-
import "./chunk-PAHMD6SU.mjs";
|
|
27
|
-
import "./chunk-BV5COOQZ.mjs";
|
|
28
25
|
import {
|
|
29
26
|
parseTime
|
|
30
27
|
} from "./chunk-F7YUKEI5.mjs";
|
|
31
|
-
import {
|
|
32
|
-
flag,
|
|
33
|
-
parseArgs
|
|
34
|
-
} from "./chunk-MCVD2ZQV.mjs";
|
|
35
|
-
import "./chunk-75WO2NOF.mjs";
|
|
36
|
-
|
|
37
|
-
// src/media/cli.ts
|
|
38
|
-
import { resolve } from "node:path";
|
|
39
28
|
|
|
40
29
|
// src/media/time.ts
|
|
41
30
|
function seconds(value, fps, duration) {
|
|
@@ -321,94 +310,9 @@ async function waveform(file, options = {}) {
|
|
|
321
310
|
return { path, silences, width: canvas.width, height: canvas.height, rows };
|
|
322
311
|
}
|
|
323
312
|
|
|
324
|
-
// src/media/cli.ts
|
|
325
|
-
var MEDIA_USAGE = `higgsedit media <command> <path> [options] inspect footage; relative paths resolve against --project <dir> or the cwd
|
|
326
|
-
|
|
327
|
-
probe <path> container, duration, per-track codecs
|
|
328
|
-
grab <path> [-t 0,1.5,45f,-1f | -c 6 [-s t] [-e t]] [-q small|medium|large|fullres]
|
|
329
|
-
[-S|--separate] [--per-sheet n] [--uncapped] [-o dir]
|
|
330
|
-
frames as labelled contact sheets (or one PNG each)
|
|
331
|
-
filmstrip <path> [-s t] [-e t] [-x scale] [-o png] thumbnail grid with a timecode ruler
|
|
332
|
-
waveform <path> [-s t] [-e t] [-x scale] [-o png] [--silence-db -40] [--min-silence 0.4]
|
|
333
|
-
loudness PNG plus silent ranges
|
|
334
|
-
transcribe <path> [-o json] [--model repo] [--language en]
|
|
335
|
-
word-timed transcript JSON (local mlx_whisper)
|
|
336
|
-
add --json for machine-readable output
|
|
337
|
-
`;
|
|
338
|
-
var number = (value, name) => {
|
|
339
|
-
if (value === void 0) return void 0;
|
|
340
|
-
const n = Number(value);
|
|
341
|
-
if (!Number.isFinite(n)) throw new Error(`--${name} takes a number, got "${value}"`);
|
|
342
|
-
return n;
|
|
343
|
-
};
|
|
344
|
-
async function runMedia(argv) {
|
|
345
|
-
const [command, ...rest] = argv;
|
|
346
|
-
const args = parseArgs(rest, /* @__PURE__ */ new Set(["json", "separate", "S", "uncapped"]));
|
|
347
|
-
const target = args.positional[0];
|
|
348
|
-
if (!command || !target || command === "help") {
|
|
349
|
-
console.log(MEDIA_USAGE);
|
|
350
|
-
return command ? 1 : 0;
|
|
351
|
-
}
|
|
352
|
-
const file = resolveMediaPath(target, resolve(flag(args, "project") ?? process.cwd()));
|
|
353
|
-
const output = flag(args, "o", "output");
|
|
354
|
-
const window2 = { ...flag(args, "s", "start") !== void 0 ? { start: flag(args, "s", "start") } : {}, ...flag(args, "e", "end") !== void 0 ? { end: flag(args, "e", "end") } : {} };
|
|
355
|
-
const scale = number(flag(args, "x", "scale"), "scale");
|
|
356
|
-
const print = (value, text) => console.log(args.flags.json === true ? JSON.stringify(value, null, 2) : text());
|
|
357
|
-
switch (command) {
|
|
358
|
-
case "probe": {
|
|
359
|
-
const p = await probeMedia(file);
|
|
360
|
-
print(
|
|
361
|
-
p,
|
|
362
|
-
() => [
|
|
363
|
-
`${p.path} ${p.type}, ${p.format ?? "not media"}, ${p.size} bytes${p.duration !== void 0 ? `, ${p.duration.toFixed(3)} s` : ""}`,
|
|
364
|
-
...p.tracks.map((t) => ` #${t.id} ${t.type} ${t.codec ?? "?"}${t.width ? ` ${t.width}x${t.height}` : ""}${t.fps ? ` ${t.fps} fps` : ""}${t.pixFmt ? ` ${t.pixFmt}` : ""}${t.sampleRate ? ` ${t.sampleRate} Hz` : ""}${t.channels ? ` ${t.channels} ch` : ""} ${t.duration.toFixed(3)} s`)
|
|
365
|
-
].join("\n")
|
|
366
|
-
);
|
|
367
|
-
return 0;
|
|
368
|
-
}
|
|
369
|
-
case "grab": {
|
|
370
|
-
const times = flag(args, "t", "times")?.split(",").map((s) => s.trim()).filter(Boolean);
|
|
371
|
-
const count = number(flag(args, "c", "count"), "count");
|
|
372
|
-
const perSheet = number(flag(args, "per-sheet"), "per-sheet");
|
|
373
|
-
const quality = flag(args, "q", "quality");
|
|
374
|
-
const r = await grab(file, {
|
|
375
|
-
...times ? { times } : {},
|
|
376
|
-
...count !== void 0 ? { count } : {},
|
|
377
|
-
...window2,
|
|
378
|
-
...quality ? { quality } : {},
|
|
379
|
-
...args.flags.separate === true || args.flags.S === true ? { separate: true } : {},
|
|
380
|
-
...perSheet !== void 0 ? { perSheet } : {},
|
|
381
|
-
...args.flags.uncapped === true ? { uncapped: true } : {},
|
|
382
|
-
...output ? { output } : {}
|
|
383
|
-
});
|
|
384
|
-
print(r, () => r.images.map((i) => `${i.timecode} ${i.path}`).join("\n"));
|
|
385
|
-
return 0;
|
|
386
|
-
}
|
|
387
|
-
case "filmstrip": {
|
|
388
|
-
const r = await filmstrip(file, { ...window2, ...scale !== void 0 ? { scale } : {}, ...output ? { output } : {} });
|
|
389
|
-
print({ path: r.path, width: r.width, height: r.height, columns: r.columns, rows: r.rows }, () => `wrote ${r.path} (${r.columns}x${r.rows} thumbnails, ${r.width}x${r.height})`);
|
|
390
|
-
return 0;
|
|
391
|
-
}
|
|
392
|
-
case "waveform": {
|
|
393
|
-
const silenceDb = number(flag(args, "silence-db"), "silence-db");
|
|
394
|
-
const minSilence = number(flag(args, "min-silence"), "min-silence");
|
|
395
|
-
const r = await waveform(file, { ...window2, ...scale !== void 0 ? { scale } : {}, ...output ? { output } : {}, ...silenceDb !== void 0 ? { silenceDb } : {}, ...minSilence !== void 0 ? { minSilence } : {} });
|
|
396
|
-
print({ path: r.path, silences: r.silences }, () => [`wrote ${r.path}`, ...r.silences.length ? r.silences.map((s) => ` silent ${s.start.toFixed(3)}\u2013${s.end.toFixed(3)} s`) : [" no silences"]].join("\n"));
|
|
397
|
-
return 0;
|
|
398
|
-
}
|
|
399
|
-
case "transcribe": {
|
|
400
|
-
const model = flag(args, "model");
|
|
401
|
-
const language = flag(args, "language");
|
|
402
|
-
const r = await transcribeMedia(file, { ...output ? { output } : {}, ...model ? { model } : {}, ...language ? { language } : {} });
|
|
403
|
-
print(r, () => `wrote ${r.path} (${r.segments} segments, ${r.words} words${r.language ? `, ${r.language}` : ""})`);
|
|
404
|
-
return 0;
|
|
405
|
-
}
|
|
406
|
-
default:
|
|
407
|
-
console.log(MEDIA_USAGE);
|
|
408
|
-
return 1;
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
313
|
export {
|
|
412
|
-
|
|
413
|
-
|
|
314
|
+
filmstrip,
|
|
315
|
+
grab,
|
|
316
|
+
transcribeMedia,
|
|
317
|
+
waveform
|
|
414
318
|
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __higgseditRequire } from "node:module";
|
|
3
|
+
import { fileURLToPath as __higgseditPath } from "node:url";
|
|
4
|
+
const require = __higgseditRequire(import.meta.url);
|
|
5
|
+
const __filename = __higgseditPath(import.meta.url);
|
|
6
|
+
const __dirname = __filename.slice(0, __filename.lastIndexOf("/"));
|
|
7
|
+
import {
|
|
8
|
+
filmstrip,
|
|
9
|
+
grab,
|
|
10
|
+
transcribeMedia,
|
|
11
|
+
waveform
|
|
12
|
+
} from "./chunk-V5XTCFYK.mjs";
|
|
13
|
+
import {
|
|
14
|
+
probeMedia,
|
|
15
|
+
resolveMediaPath
|
|
16
|
+
} from "./chunk-QJTILFBS.mjs";
|
|
17
|
+
import "./chunk-L5LWBA6A.mjs";
|
|
18
|
+
import "./chunk-PAHMD6SU.mjs";
|
|
19
|
+
import "./chunk-BV5COOQZ.mjs";
|
|
20
|
+
import "./chunk-F7YUKEI5.mjs";
|
|
21
|
+
import {
|
|
22
|
+
flag,
|
|
23
|
+
parseArgs
|
|
24
|
+
} from "./chunk-MCVD2ZQV.mjs";
|
|
25
|
+
import "./chunk-75WO2NOF.mjs";
|
|
26
|
+
|
|
27
|
+
// src/media/cli.ts
|
|
28
|
+
import { resolve } from "node:path";
|
|
29
|
+
var MEDIA_USAGE = `higgsedit media <command> <path> [options] inspect footage; relative paths resolve against --project <dir> or the cwd
|
|
30
|
+
|
|
31
|
+
probe <path> container, duration, per-track codecs
|
|
32
|
+
grab <path> [-t 0,1.5,45f,-1f | -c 6 [-s t] [-e t]] [-q small|medium|large|fullres]
|
|
33
|
+
[-S|--separate] [--per-sheet n] [--uncapped] [-o dir]
|
|
34
|
+
frames as labelled contact sheets (or one PNG each)
|
|
35
|
+
filmstrip <path> [-s t] [-e t] [-x scale] [-o png] thumbnail grid with a timecode ruler
|
|
36
|
+
waveform <path> [-s t] [-e t] [-x scale] [-o png] [--silence-db -40] [--min-silence 0.4]
|
|
37
|
+
loudness PNG plus silent ranges
|
|
38
|
+
transcribe <path> [-o json] [--model repo] [--language en]
|
|
39
|
+
word-timed transcript JSON (local mlx_whisper)
|
|
40
|
+
add --json for machine-readable output
|
|
41
|
+
`;
|
|
42
|
+
var number = (value, name) => {
|
|
43
|
+
if (value === void 0) return void 0;
|
|
44
|
+
const n = Number(value);
|
|
45
|
+
if (!Number.isFinite(n)) throw new Error(`--${name} takes a number, got "${value}"`);
|
|
46
|
+
return n;
|
|
47
|
+
};
|
|
48
|
+
async function runMedia(argv) {
|
|
49
|
+
const [command, ...rest] = argv;
|
|
50
|
+
const args = parseArgs(rest, /* @__PURE__ */ new Set(["json", "separate", "S", "uncapped"]));
|
|
51
|
+
const target = args.positional[0];
|
|
52
|
+
if (!command || !target || command === "help") {
|
|
53
|
+
console.log(MEDIA_USAGE);
|
|
54
|
+
return command ? 1 : 0;
|
|
55
|
+
}
|
|
56
|
+
const file = resolveMediaPath(target, resolve(flag(args, "project") ?? process.cwd()));
|
|
57
|
+
const output = flag(args, "o", "output");
|
|
58
|
+
const window = { ...flag(args, "s", "start") !== void 0 ? { start: flag(args, "s", "start") } : {}, ...flag(args, "e", "end") !== void 0 ? { end: flag(args, "e", "end") } : {} };
|
|
59
|
+
const scale = number(flag(args, "x", "scale"), "scale");
|
|
60
|
+
const print = (value, text) => console.log(args.flags.json === true ? JSON.stringify(value, null, 2) : text());
|
|
61
|
+
switch (command) {
|
|
62
|
+
case "probe": {
|
|
63
|
+
const p = await probeMedia(file);
|
|
64
|
+
print(
|
|
65
|
+
p,
|
|
66
|
+
() => [
|
|
67
|
+
`${p.path} ${p.type}, ${p.format ?? "not media"}, ${p.size} bytes${p.duration !== void 0 ? `, ${p.duration.toFixed(3)} s` : ""}`,
|
|
68
|
+
...p.tracks.map((t) => ` #${t.id} ${t.type} ${t.codec ?? "?"}${t.width ? ` ${t.width}x${t.height}` : ""}${t.fps ? ` ${t.fps} fps` : ""}${t.pixFmt ? ` ${t.pixFmt}` : ""}${t.sampleRate ? ` ${t.sampleRate} Hz` : ""}${t.channels ? ` ${t.channels} ch` : ""} ${t.duration.toFixed(3)} s`)
|
|
69
|
+
].join("\n")
|
|
70
|
+
);
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
case "grab": {
|
|
74
|
+
const times = flag(args, "t", "times")?.split(",").map((s) => s.trim()).filter(Boolean);
|
|
75
|
+
const count = number(flag(args, "c", "count"), "count");
|
|
76
|
+
const perSheet = number(flag(args, "per-sheet"), "per-sheet");
|
|
77
|
+
const quality = flag(args, "q", "quality");
|
|
78
|
+
const r = await grab(file, {
|
|
79
|
+
...times ? { times } : {},
|
|
80
|
+
...count !== void 0 ? { count } : {},
|
|
81
|
+
...window,
|
|
82
|
+
...quality ? { quality } : {},
|
|
83
|
+
...args.flags.separate === true || args.flags.S === true ? { separate: true } : {},
|
|
84
|
+
...perSheet !== void 0 ? { perSheet } : {},
|
|
85
|
+
...args.flags.uncapped === true ? { uncapped: true } : {},
|
|
86
|
+
...output ? { output } : {}
|
|
87
|
+
});
|
|
88
|
+
print(r, () => r.images.map((i) => `${i.timecode} ${i.path}`).join("\n"));
|
|
89
|
+
return 0;
|
|
90
|
+
}
|
|
91
|
+
case "filmstrip": {
|
|
92
|
+
const r = await filmstrip(file, { ...window, ...scale !== void 0 ? { scale } : {}, ...output ? { output } : {} });
|
|
93
|
+
print({ path: r.path, width: r.width, height: r.height, columns: r.columns, rows: r.rows }, () => `wrote ${r.path} (${r.columns}x${r.rows} thumbnails, ${r.width}x${r.height})`);
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
case "waveform": {
|
|
97
|
+
const silenceDb = number(flag(args, "silence-db"), "silence-db");
|
|
98
|
+
const minSilence = number(flag(args, "min-silence"), "min-silence");
|
|
99
|
+
const r = await waveform(file, { ...window, ...scale !== void 0 ? { scale } : {}, ...output ? { output } : {}, ...silenceDb !== void 0 ? { silenceDb } : {}, ...minSilence !== void 0 ? { minSilence } : {} });
|
|
100
|
+
print({ path: r.path, silences: r.silences }, () => [`wrote ${r.path}`, ...r.silences.length ? r.silences.map((s) => ` silent ${s.start.toFixed(3)}\u2013${s.end.toFixed(3)} s`) : [" no silences"]].join("\n"));
|
|
101
|
+
return 0;
|
|
102
|
+
}
|
|
103
|
+
case "transcribe": {
|
|
104
|
+
const model = flag(args, "model");
|
|
105
|
+
const language = flag(args, "language");
|
|
106
|
+
const r = await transcribeMedia(file, { ...output ? { output } : {}, ...model ? { model } : {}, ...language ? { language } : {} });
|
|
107
|
+
print(r, () => `wrote ${r.path} (${r.segments} segments, ${r.words} words${r.language ? `, ${r.language}` : ""})`);
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
default:
|
|
111
|
+
console.log(MEDIA_USAGE);
|
|
112
|
+
return 1;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export {
|
|
116
|
+
MEDIA_USAGE,
|
|
117
|
+
runMedia
|
|
118
|
+
};
|
|
@@ -10,15 +10,10 @@ import {
|
|
|
10
10
|
} from "./chunk-P2BGHTRR.mjs";
|
|
11
11
|
import {
|
|
12
12
|
selectStatus
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import "./chunk-QJTILFBS.mjs";
|
|
13
|
+
} from "./chunk-ZO3NDQD3.mjs";
|
|
15
14
|
import {
|
|
16
15
|
findTool
|
|
17
16
|
} from "./chunk-PAHMD6SU.mjs";
|
|
18
|
-
import "./chunk-BV5COOQZ.mjs";
|
|
19
|
-
import "./chunk-F7YUKEI5.mjs";
|
|
20
|
-
import "./chunk-MCVD2ZQV.mjs";
|
|
21
|
-
import "./chunk-75WO2NOF.mjs";
|
|
22
17
|
|
|
23
18
|
// src/doctor.ts
|
|
24
19
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -69,6 +64,7 @@ function diagnosisText(d, extra = {}) {
|
|
|
69
64
|
${d.errors.map((e) => ` ${e.replace(/\n/g, "\n ")}`).join("\n")}` : "recent errors: none");
|
|
70
65
|
return lines.join("\n");
|
|
71
66
|
}
|
|
67
|
+
|
|
72
68
|
export {
|
|
73
69
|
diagnose,
|
|
74
70
|
diagnosisText
|
|
@@ -11,8 +11,6 @@ import {
|
|
|
11
11
|
readBody,
|
|
12
12
|
sendFile
|
|
13
13
|
} from "./chunk-7PYXSJS7.mjs";
|
|
14
|
-
import "./chunk-UDL5AHKO.mjs";
|
|
15
|
-
import "./chunk-LTKMGZ6T.mjs";
|
|
16
14
|
import {
|
|
17
15
|
launch
|
|
18
16
|
} from "./chunk-P2BGHTRR.mjs";
|
|
@@ -20,8 +18,6 @@ import {
|
|
|
20
18
|
ffmpeg,
|
|
21
19
|
probe
|
|
22
20
|
} from "./chunk-PAHMD6SU.mjs";
|
|
23
|
-
import "./chunk-NUXTKQNM.mjs";
|
|
24
|
-
import "./chunk-2VKQHJMM.mjs";
|
|
25
21
|
import {
|
|
26
22
|
encodeMaskFile
|
|
27
23
|
} from "./chunk-BV5COOQZ.mjs";
|
|
@@ -32,7 +28,6 @@ import {
|
|
|
32
28
|
flag,
|
|
33
29
|
flags
|
|
34
30
|
} from "./chunk-MCVD2ZQV.mjs";
|
|
35
|
-
import "./chunk-75WO2NOF.mjs";
|
|
36
31
|
|
|
37
32
|
// src/segment.ts
|
|
38
33
|
import { createHash } from "node:crypto";
|
|
@@ -402,16 +397,17 @@ async function runSegment(args, print) {
|
|
|
402
397
|
);
|
|
403
398
|
return 0;
|
|
404
399
|
}
|
|
400
|
+
|
|
405
401
|
export {
|
|
406
|
-
ORT_FILES,
|
|
407
|
-
ensureModel,
|
|
408
|
-
mediaSeconds,
|
|
409
|
-
modelCached,
|
|
410
402
|
modelDir,
|
|
403
|
+
modelCached,
|
|
404
|
+
ensureModel,
|
|
411
405
|
ortDir,
|
|
412
|
-
|
|
406
|
+
ORT_FILES,
|
|
407
|
+
segmentBundle,
|
|
413
408
|
prompts,
|
|
414
|
-
runSegment,
|
|
415
409
|
segment,
|
|
416
|
-
|
|
410
|
+
point,
|
|
411
|
+
mediaSeconds,
|
|
412
|
+
runSegment
|
|
417
413
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire as __higgseditRequire } from "node:module";
|
|
3
|
+
import { fileURLToPath as __higgseditPath } from "node:url";
|
|
4
|
+
const require = __higgseditRequire(import.meta.url);
|
|
5
|
+
const __filename = __higgseditPath(import.meta.url);
|
|
6
|
+
const __dirname = __filename.slice(0, __filename.lastIndexOf("/"));
|
|
7
|
+
import {
|
|
8
|
+
diagnose,
|
|
9
|
+
diagnosisText
|
|
10
|
+
} from "./chunk-XY5CCUOZ.mjs";
|
|
11
|
+
import "./chunk-P2BGHTRR.mjs";
|
|
12
|
+
import "./chunk-ZO3NDQD3.mjs";
|
|
13
|
+
import "./chunk-QJTILFBS.mjs";
|
|
14
|
+
import "./chunk-PAHMD6SU.mjs";
|
|
15
|
+
import "./chunk-BV5COOQZ.mjs";
|
|
16
|
+
import "./chunk-F7YUKEI5.mjs";
|
|
17
|
+
import "./chunk-MCVD2ZQV.mjs";
|
|
18
|
+
import "./chunk-75WO2NOF.mjs";
|
|
19
|
+
export {
|
|
20
|
+
diagnose,
|
|
21
|
+
diagnosisText
|
|
22
|
+
};
|
|
@@ -349,7 +349,7 @@ async function runSelect(args, print) {
|
|
|
349
349
|
const text = flag(args, "text");
|
|
350
350
|
const prompt = parsePrompt({ ...text !== void 0 ? { text } : {}, ...include.length ? { points: include } : {}, ...exclude.length ? { exclude } : {}, ...box ? { box } : {} });
|
|
351
351
|
requireSelectBackend();
|
|
352
|
-
const { mediaSeconds } = await import("./chunk-
|
|
352
|
+
const { mediaSeconds } = await import("./chunk-HY2E7IWX.mjs");
|
|
353
353
|
const fps = await mediaSeconds.fps(media);
|
|
354
354
|
const time = mediaSeconds.parse(flag(args, "at", "t"), fps, "--at") ?? 0;
|
|
355
355
|
const out = flag(args, "o", "out");
|
|
@@ -6,7 +6,7 @@ const __filename = __higgseditPath(import.meta.url);
|
|
|
6
6
|
const __dirname = __filename.slice(0, __filename.lastIndexOf("/"));
|
|
7
7
|
import {
|
|
8
8
|
CLI_MANIFEST
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-O3AVYZ6U.mjs";
|
|
10
10
|
import {
|
|
11
11
|
flag,
|
|
12
12
|
parseArgs
|
|
@@ -63,21 +63,21 @@ async function main(argv) {
|
|
|
63
63
|
return 0;
|
|
64
64
|
}
|
|
65
65
|
if (command === "doctor") {
|
|
66
|
-
const { diagnose, diagnosisText } = await import("./chunk-
|
|
66
|
+
const { diagnose, diagnosisText } = await import("./chunk-YNAIRXOP.mjs");
|
|
67
67
|
const d = await diagnose();
|
|
68
68
|
print(d, () => diagnosisText(d));
|
|
69
69
|
return "path" in d.ffmpeg && "path" in d.ffprobe ? 0 : 1;
|
|
70
70
|
}
|
|
71
71
|
if (command === "media") {
|
|
72
|
-
const { runMedia } = await import("./chunk-
|
|
72
|
+
const { runMedia } = await import("./chunk-W2TANYEH.mjs");
|
|
73
73
|
return runMedia(rest);
|
|
74
74
|
}
|
|
75
75
|
if (command === "segment") {
|
|
76
|
-
const { runSegment } = await import("./chunk-
|
|
76
|
+
const { runSegment } = await import("./chunk-HY2E7IWX.mjs");
|
|
77
77
|
return runSegment(args, print);
|
|
78
78
|
}
|
|
79
79
|
if (command === "select") {
|
|
80
|
-
const { runSelect } = await import("./chunk-
|
|
80
|
+
const { runSelect } = await import("./chunk-ECRSOIKI.mjs");
|
|
81
81
|
return runSelect(args, print);
|
|
82
82
|
}
|
|
83
83
|
if (command === "transcribe") {
|
|
@@ -89,7 +89,7 @@ async function main(argv) {
|
|
|
89
89
|
return runFonts(parseArgs(rest, /* @__PURE__ */ new Set(["json", "cached"])), print);
|
|
90
90
|
}
|
|
91
91
|
if (command === "presets") {
|
|
92
|
-
const { runPresets } = await import("./chunk-
|
|
92
|
+
const { runPresets } = await import("./chunk-ENALCTAD.mjs");
|
|
93
93
|
return runPresets(args, print);
|
|
94
94
|
}
|
|
95
95
|
if (!command || command === "help" || command === "--help" || !dirArg) {
|
|
@@ -97,7 +97,7 @@ async function main(argv) {
|
|
|
97
97
|
return command ? 1 : 0;
|
|
98
98
|
}
|
|
99
99
|
if (command === "init") {
|
|
100
|
-
const { scaffold } = await import("./chunk-
|
|
100
|
+
const { scaffold } = await import("./chunk-3XVMJSV2.mjs");
|
|
101
101
|
const [w, h] = String(flag(args, "size") ?? "1080x1920").split("x").map(Number);
|
|
102
102
|
const target = resolve(dirArg);
|
|
103
103
|
const r = scaffold(target, { width: w, height: h, fps: Number(flag(args, "fps") ?? 30) });
|
|
@@ -139,7 +139,7 @@ async function main(argv) {
|
|
|
139
139
|
const at = args.positional[1];
|
|
140
140
|
const out = flag(args, "o", "out");
|
|
141
141
|
if (!at || !out) throw new Error("usage: higgsedit frame <dir> <t> -o x.png");
|
|
142
|
-
const { frame } = await import("./chunk-
|
|
142
|
+
const { frame } = await import("./chunk-4VE54E4U.mjs");
|
|
143
143
|
const r = await frame(dir, at, out, depth, allowFontFallback);
|
|
144
144
|
print(r, () => `wrote ${r.out} (frame ${r.frame}, ${r.ms.toFixed(0)} ms)`);
|
|
145
145
|
return 0;
|
|
@@ -148,7 +148,7 @@ async function main(argv) {
|
|
|
148
148
|
const times = flag(args, "times")?.split(",").map((s) => s.trim()).filter(Boolean);
|
|
149
149
|
const out = flag(args, "o", "out");
|
|
150
150
|
if (!times?.length || !out) throw new Error("usage: higgsedit sheet <dir> --times 0,1,2 -o sheet.png");
|
|
151
|
-
const { sheet } = await import("./chunk-
|
|
151
|
+
const { sheet } = await import("./chunk-4VE54E4U.mjs");
|
|
152
152
|
const r = await sheet(dir, times, out, Number(flag(args, "columns") ?? Math.min(4, times.length)), Number(flag(args, "width") ?? 480), allowFontFallback);
|
|
153
153
|
print(r, () => `wrote ${r.out} (frames ${r.frames.join(", ")})`);
|
|
154
154
|
return 0;
|
|
@@ -159,7 +159,7 @@ async function main(argv) {
|
|
|
159
159
|
const encoder = flag(args, "encoder");
|
|
160
160
|
if (encoder && !ENCODERS.includes(encoder)) throw new Error(`unknown --encoder ${encoder}; use ${ENCODERS.join(", ")}`);
|
|
161
161
|
const crf = flag(args, "crf");
|
|
162
|
-
const { render } = await import("./chunk-
|
|
162
|
+
const { render } = await import("./chunk-4VE54E4U.mjs");
|
|
163
163
|
const r = await render(dir, {
|
|
164
164
|
out,
|
|
165
165
|
depth,
|
|
@@ -185,13 +185,13 @@ async function main(argv) {
|
|
|
185
185
|
return 0;
|
|
186
186
|
}
|
|
187
187
|
case "verify": {
|
|
188
|
-
const { verify } = await import("./chunk-
|
|
188
|
+
const { verify } = await import("./chunk-4VE54E4U.mjs");
|
|
189
189
|
const r = await verify(dir, Number(flag(args, "count") ?? 12), depth, allowFontFallback);
|
|
190
190
|
print(r, () => [`${r.identical ? "identical" : "DIFFERENT"}: ${r.frames.length} frames, ${r.depth}-bit, shuffled order ${r.shuffledOrder.join(",")}`, ...r.frames.map((f, i) => ` ${String(f).padStart(6)} ${r.forward[i]} ${r.shuffled[i] === r.forward[i] ? "=" : "\u2260"}`)].join("\n"));
|
|
191
191
|
return r.identical ? 0 : 1;
|
|
192
192
|
}
|
|
193
193
|
case "outline": {
|
|
194
|
-
const { outline } = await import("./chunk-
|
|
194
|
+
const { outline } = await import("./chunk-UXO22E3P.mjs");
|
|
195
195
|
const o = await outline(dir);
|
|
196
196
|
print(o, () => o.text);
|
|
197
197
|
return o.ok ? 0 : 1;
|
|
@@ -201,7 +201,7 @@ async function main(argv) {
|
|
|
201
201
|
return runEdit(dir, args, print);
|
|
202
202
|
}
|
|
203
203
|
case "dev": {
|
|
204
|
-
const { dev } = await import("./chunk-
|
|
204
|
+
const { dev } = await import("./chunk-55JARHLA.mjs");
|
|
205
205
|
await dev(dir, Number(flag(args, "port") ?? 4173), flag(args, "presets"));
|
|
206
206
|
return await new Promise(() => {
|
|
207
207
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@higgsfield/higgsedit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Make videos in code: create, preview, edit and render TSX video projects from the command line",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"video",
|
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
"higgsedit": "./dist/higgsedit.mjs"
|
|
20
20
|
},
|
|
21
21
|
"exports": {
|
|
22
|
+
"./api": {
|
|
23
|
+
"types": "./dist/api.d.ts"
|
|
24
|
+
},
|
|
22
25
|
"./jsx": {
|
|
23
26
|
"types": "./dist/jsx/index.d.ts"
|
|
24
27
|
},
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@higgsfield/video-editor-mcp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Higgsfield Video Editor as a local MCP server over stdio: edits TSX video projects on this machine with the higgsedit CLI and serves the editor panel.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
-
"
|
|
8
|
+
"video-editor-mcp": "dist/cli.mjs",
|
|
9
9
|
"fnf-video-editor-mcp": "dist/server.mjs"
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
@@ -22,14 +22,14 @@
|
|
|
22
22
|
"esbuild": "0.28.2",
|
|
23
23
|
"playwright-core": "1.63.0",
|
|
24
24
|
"solid-js": "1.9.16",
|
|
25
|
-
"@higgsfield/higgsedit": "0.
|
|
25
|
+
"@higgsfield/higgsedit": "1.0.0"
|
|
26
26
|
},
|
|
27
27
|
"bundleDependencies": [
|
|
28
28
|
"@higgsfield/higgsedit"
|
|
29
29
|
],
|
|
30
30
|
"publishConfig": {
|
|
31
31
|
"access": "public",
|
|
32
|
-
"tag": "
|
|
32
|
+
"tag": "latest"
|
|
33
33
|
},
|
|
34
34
|
"keywords": [
|
|
35
35
|
"mcp",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "higgsfield-video-editor-local",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Edit and render video projects on your Mac with Higgsfield. Arrange clips, add text, audio and motion graphics, preview your timeline in the interactive editor, and render finished videos. Generate images and videos in the editor by connecting your Higgsfield account.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Higgsfield AI, Inc."
|