@lalalic/markcut 2.2.8 → 2.2.9
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/package.json +2 -1
- package/skills/markcut/SKILL.md +0 -2
- package/skills/markcut/docs/markdown-descriptive.md +1 -1
- package/src/config.mjs +60 -63
- package/src/descriptive/resolve.ts +3 -2
- package/src/player/pipeline.mjs +64 -21
- package/src/render/cli.mjs +42 -69
- package/src/vision/cli.mjs +21 -45
- package/skills/markcut/docs/template.md +0 -212
- package/skills/markcut/docs/templates/courseware/TEMPLATE.md +0 -526
- package/skills/markcut/docs/templates/courseware/agents/reviewer.md +0 -84
- package/skills/markcut/docs/templates/courseware/prompts/fix.md +0 -30
- package/skills/markcut/docs/templates/courseware/prompts/outline.md +0 -58
- package/skills/markcut/docs/templates/courseware/prompts/scene.md +0 -70
- package/skills/markcut/docs/templates/deep-dive/TEMPLATE.md +0 -354
- package/skills/markcut/docs/templates/deep-dive/agents/researcher.md +0 -69
- package/skills/markcut/docs/templates/deep-dive/agents/reviewer.md +0 -108
- package/skills/markcut/docs/templates/deep-dive/prompts/fix.md +0 -28
- package/skills/markcut/docs/templates/deep-dive/prompts/outline.md +0 -102
- package/skills/markcut/docs/templates/deep-dive/prompts/script.md +0 -64
- package/skills/markcut/docs/templates/illustrated-book/TEMPLATE.md +0 -356
- package/skills/markcut/docs/templates/illustrated-book/agents/reviewer.md +0 -115
- package/skills/markcut/docs/templates/illustrated-book/prompts/fix.md +0 -29
- package/skills/markcut/docs/templates/illustrated-book/prompts/illustration.md +0 -35
- package/skills/markcut/docs/templates/illustrated-book/prompts/story.md +0 -69
- package/skills/markcut/docs/templates/short-film/TEMPLATE.md +0 -385
- package/skills/markcut/docs/templates/short-film/agents/reviewer.md +0 -115
- package/skills/markcut/docs/templates/short-film/prompts/fix.md +0 -29
- package/skills/markcut/docs/templates/short-film/prompts/screenplay.md +0 -101
- package/skills/markcut/docs/templates/short-film/prompts/storyboard.md +0 -54
- package/skills/markcut/docs/templates/short-video/TEMPLATE.md +0 -301
- package/skills/markcut/docs/templates/short-video/agents/reviewer.md +0 -94
- package/skills/markcut/docs/templates/short-video/prompts/fix.md +0 -27
- package/skills/markcut/docs/templates/short-video/prompts/script.md +0 -65
- package/skills/markcut/docs/templates/vlog/TEMPLATE.md +0 -326
- package/skills/markcut/docs/templates/vlog/agents/reviewer.md +0 -89
- package/skills/markcut/docs/templates/vlog/agents/story-writer.md +0 -100
- package/skills/markcut/docs/templates/vlog/prompts/bgm-select.md +0 -38
- package/skills/markcut/docs/templates/vlog/prompts/group-clips.md +0 -93
- package/skills/markcut/docs/templates/vlog/prompts/local-context.md +0 -59
- package/skills/markcut/docs/templates/vlog/prompts/outline.md +0 -56
- package/skills/markcut/docs/templates/vlog/prompts/review-fix.md +0 -27
- package/skills/markcut/docs/templates/vlog/prompts/storyboard.md +0 -41
- package/src/render/cli.test.ts +0 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lalalic/markcut",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.9",
|
|
4
4
|
"description": "Markdown-to-video engine. Describe scenes in markdown, get a rendered video.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"markcut": "bin/markcut"
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"test:watch": "vitest --config tests/vitest.config.ts",
|
|
32
32
|
"build": "scripts/build-pipeline.sh && scripts/build-player.sh",
|
|
33
33
|
"vision": "node src/render/cli.mjs vision",
|
|
34
|
+
"markcut": "bin/markcut",
|
|
34
35
|
"push": "GIT_SSH_COMMAND=\"ssh -i ~/.ssh/id_ed25519_1 -o IdentitiesOnly=yes\" git push"
|
|
35
36
|
},
|
|
36
37
|
"dependencies": {
|
package/skills/markcut/SKILL.md
CHANGED
|
@@ -62,8 +62,6 @@ some common issues (photo or video can't be displayed, audio missing), take belo
|
|
|
62
62
|
| Markdown descriptive format (primary authoring format) | [docs/markdown-descriptive.md](docs/markdown-descriptive.md) |
|
|
63
63
|
| Label system (browse, label, export labels.json) | [docs/label-mode.md](docs/label-mode.md) |
|
|
64
64
|
| Player servers (label + edit mode) | [docs/edit-mode.md](docs/edit-mode.md) |
|
|
65
|
-
| How to make video template | [docs/template.md](docs/template.md) |
|
|
66
|
-
| Templates | [docs/templates/](docs/templates/) |
|
|
67
65
|
|
|
68
66
|
|
|
69
67
|
## common used components
|
|
@@ -529,7 +529,7 @@ layout:series width:1920 height:1080 fps:30
|
|
|
529
529
|
tts:"edge-tts --voice 'en-US-GuyNeural' --text '{input}' --write-media '{output}'"
|
|
530
530
|
|
|
531
531
|
## Hook
|
|
532
|
-
- image
|
|
532
|
+
- image prompt:"..."
|
|
533
533
|
|
|
534
534
|
## Title
|
|
535
535
|
- script "Welcome to the course"
|
package/src/config.mjs
CHANGED
|
@@ -8,6 +8,48 @@
|
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
|
|
12
|
+
export const args=(function parseArgs(argv) {
|
|
13
|
+
const CLI_OVERRIDE_FLAGS = {
|
|
14
|
+
"--itt": "itt",
|
|
15
|
+
"--vtt": "vtt",
|
|
16
|
+
"--stt": "stt",
|
|
17
|
+
"--tts": "tts",
|
|
18
|
+
"--agent": "agent",
|
|
19
|
+
"--edit-cli": "editCli",
|
|
20
|
+
"--tti": "tti",
|
|
21
|
+
"--ttv": "ttv"
|
|
22
|
+
};
|
|
23
|
+
const args = { command: "", file: "", output: "", forceNew: false, verbose: false, label: false, edit: false, noBrowser: false, chat: false, port: 3001, compile: false, cli: false, showClis: false, scriptOutputDir: "", mediaOutputDir: "", variant: [], cliOverrides: {} };
|
|
24
|
+
let i = 2;
|
|
25
|
+
if (argv[i]) args.command = argv[i++];
|
|
26
|
+
if (argv[i] && !argv[i].startsWith("--")) args.file = argv[i++];
|
|
27
|
+
while (i < argv.length) {
|
|
28
|
+
const flag = argv[i++];
|
|
29
|
+
if (flag === "--output" && argv[i]) args.output = argv[i++];
|
|
30
|
+
else if (flag === "--script-output-dir" && argv[i]) args.scriptOutputDir = argv[i++];
|
|
31
|
+
else if (flag === "--media-output-dir" && argv[i]) args.mediaOutputDir = argv[i++];
|
|
32
|
+
else if (flag === "--cli") args.cli = true;
|
|
33
|
+
else if (flag === "--show-clis") args.showClis = true;
|
|
34
|
+
else if (flag === "--compile") args.compile = true;
|
|
35
|
+
else if (flag === "--force-new") args.forceNew = true;
|
|
36
|
+
else if (flag === "--verbose") args.verbose = true;
|
|
37
|
+
else if (flag === "--label") args.label = true;
|
|
38
|
+
else if (flag === "--edit") args.edit = true;
|
|
39
|
+
else if (flag === "--no-browser") args.noBrowser = true;
|
|
40
|
+
else if (flag === "--port" && argv[i]) args.port = parseInt(argv[i], 10);
|
|
41
|
+
else if (flag.startsWith("--port=")) args.port = parseInt(flag.split("=")[1], 10);
|
|
42
|
+
else if (flag === "--variant" && argv[i]) args.variant.push(argv[i++]);
|
|
43
|
+
else if (flag.startsWith("--variant=")) args.variant.push(flag.split("=")[1]);
|
|
44
|
+
else if (CLI_OVERRIDE_FLAGS[flag] && argv[i]) {
|
|
45
|
+
const key = CLI_OVERRIDE_FLAGS[flag];
|
|
46
|
+
args.cliOverrides[key] = argv[i++];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return args;
|
|
50
|
+
})(process.argv);
|
|
51
|
+
|
|
52
|
+
|
|
11
53
|
// ── Media type sets ───────────────────────────────────────────────────────
|
|
12
54
|
|
|
13
55
|
export const IMAGE_EXTS = new Set([".jpg", ".jpeg", ".png", ".webp", ".gif", ".bmp", ".tiff", ".heic", ".avif"]);
|
|
@@ -25,14 +67,8 @@ export const MAX_VIDEO_DIMENSION = Number(process.env.MARKCUT_MAX_VIDEO_DIMENSIO
|
|
|
25
67
|
// ── Vision pipeline CLI templates ─────────────────────────────────────────
|
|
26
68
|
|
|
27
69
|
/**
|
|
28
|
-
* Image-to-text (ITT) CLI.
|
|
29
|
-
* Reads an image file and returns a text description.
|
|
30
|
-
* Placeholders: {input}=image path(s), {prompt}=description prompt.
|
|
31
|
-
* Override with MARKCUT_ITT_CLI env var.
|
|
70
|
+
* Image-to-text (ITT) CLI placeholder (now defined below via export let).
|
|
32
71
|
*/
|
|
33
|
-
export const DEFAULT_ITT_CLI =
|
|
34
|
-
process.env.MARKCUT_ITT_CLI ||
|
|
35
|
-
'uvx --from mlx-vlm mlx_vlm.generate --model mlx-community/MiniCPM-V-4.6-bf16 --max-tokens 2048 --prompt "{prompt}" --image {input} --temperature 0.0 --thinking-mode disabled';
|
|
36
72
|
|
|
37
73
|
/**
|
|
38
74
|
* Seconds between sampled video frames when using the default ITT-based VTT.
|
|
@@ -42,62 +78,23 @@ export const GOOGLE_MAPS_API_KEY = process.env.GOOGLE_MAPS_API_KEY || "";
|
|
|
42
78
|
export const DEFAULT_VTT_SAMPLE_INTERVAL = Number(process.env.MARKCUT_VTT_SAMPLE_INTERVAL) || 5;
|
|
43
79
|
|
|
44
80
|
/**
|
|
45
|
-
* Video-to-text (VTT) CLI
|
|
46
|
-
* When set to null/empty, falls back to ITT via frame extraction.
|
|
47
|
-
* Placeholders: {input}=video path, {prompt}=description prompt.
|
|
48
|
-
* Override with MARKCUT_VTT_CLI env var.
|
|
49
|
-
*/
|
|
50
|
-
export const DEFAULT_VTT_CLI =
|
|
51
|
-
process.env.MARKCUT_VTT_CLI ||
|
|
52
|
-
'uvx --from mlx-vlm mlx_vlm.generate --model mlx-community/MiniCPM-V-4.6-bf16 --max-tokens 2048 --prompt "{prompt}" --video {input} --temperature 0.0 --processor-kwargs \'{"max_num_frames": 32, "stack_frames": 1, "max_slice_nums": 1, "use_image_id": false}\'';
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Speech-to-text (STT) CLI.
|
|
56
|
-
* Extracts audio and generates VTT subtitles.
|
|
57
|
-
* Placeholders: {input}=audio path, {output}=output directory.
|
|
58
|
-
* Override with MARKCUT_STT_CLI env var.
|
|
59
|
-
* Used by both vision and render pipelines.
|
|
60
|
-
*/
|
|
61
|
-
export const DEFAULT_STT_CLI =
|
|
62
|
-
process.env.MARKCUT_STT_CLI ||
|
|
63
|
-
'uvx --from openai-whisper whisper "{input}" --output_format vtt --output_dir "{output}"';
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Text-to-speech (TTS) CLI.
|
|
67
|
-
* Generates audio from text.
|
|
68
|
-
* Placeholders: {input}=text, {output}=audio file path.
|
|
69
|
-
* Override with MARKCUT_TTS_CLI env var.
|
|
70
|
-
* Used by both vision and render pipelines.
|
|
71
|
-
*/
|
|
72
|
-
export const DEFAULT_TTS_CLI =
|
|
73
|
-
process.env.MARKCUT_TTS_CLI ||
|
|
74
|
-
'uvx edge-tts --voice "en-US-GuyNeural" --text "{input}" --write-media "{output}"';
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Default agent CLI — general-purpose text LLM.
|
|
78
|
-
* Used for text-only tasks like detect-scenes.
|
|
79
|
-
* Placeholder:
|
|
80
|
-
* {prompt} = user prompt (current context + edit request)
|
|
81
|
-
* Override with MARKCUT_AGENT_CLI env var.
|
|
82
|
-
*/
|
|
83
|
-
export const DEFAULT_AGENT_CLI = process.env.MARKCUT_AGENT_CLI || 'npx pi -p {prompt}';
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Default edit agent CLI — used by the edit-mode persistent rpc session.
|
|
87
|
-
* Placeholders:
|
|
88
|
-
* {systemprompt} = system prompt (role, instructions, knowledge)
|
|
89
|
-
* {prompt} = user prompt (current context + edit request)
|
|
90
|
-
* {sessionid} = unique session ID for conversation continuity
|
|
91
|
-
* Override with MARKCUT_EDIT_CLI env var.
|
|
81
|
+
* Video-to-text (VTT) CLI placeholder (now defined below via export let).
|
|
92
82
|
*/
|
|
93
|
-
export const DEFAULT_EDIT_CLI = process.env.MARKCUT_EDIT_CLI || 'npx pi --session-id {sessionid} --system-prompt {systemprompt} -p {prompt}';
|
|
94
83
|
|
|
95
|
-
// ── Render-only pipeline CLI templates ─────────────────────────────────────
|
|
96
|
-
// These are specific to the render pipeline (no vision pipeline equivalent).
|
|
97
|
-
// All templates support {input}, {output}, and {seed} placeholders.
|
|
98
|
-
// {seed} is substituted with root.seed (or empty string if not set).
|
|
99
84
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
export const
|
|
85
|
+
/** Speech-to-text CLI. Override via --stt flag or MARKCUT_STT_CLI env var. */
|
|
86
|
+
export const DEFAULT_STT_CLI = args.cliOverrides.stt || process.env.MARKCUT_STT_CLI || 'uvx --from openai-whisper whisper "{input}" --output_format vtt --output_dir "{output}"';
|
|
87
|
+
/** Text-to-speech CLI. Override via --tts flag or MARKCUT_TTS_CLI env var. */
|
|
88
|
+
export const DEFAULT_TTS_CLI = args.cliOverrides.tts || process.env.MARKCUT_TTS_CLI || 'uvx edge-tts --voice "en-US-GuyNeural" --text "{input}" --write-media "{output}"';
|
|
89
|
+
/** Default agent CLI. Override via --agent flag or MARKCUT_AGENT_CLI env var. */
|
|
90
|
+
export const DEFAULT_AGENT_CLI = args.cliOverrides.agent || process.env.MARKCUT_AGENT_CLI || 'npx pi -p {prompt}';
|
|
91
|
+
/** Default edit agent CLI. Override via --edit-cli flag or MARKCUT_EDIT_CLI env var. */
|
|
92
|
+
export const DEFAULT_EDIT_CLI = args.cliOverrides.editCli || process.env.MARKCUT_EDIT_CLI || 'npx pi --session-id {sessionid} --system-prompt {systemprompt} -p {prompt}';
|
|
93
|
+
/** Text-to-image CLI. Override via --tti flag or MARKCUT_TTI_CLI env var. */
|
|
94
|
+
export const DEFAULT_TTI_CLI = args.cliOverrides.tti || process.env.MARKCUT_TTI_CLI || 'uvx --from mflux mflux-generate-flux2 --model flux2-klein-4b --steps 5 --prompt "{input}" --output "{output}" --seed {seed}';
|
|
95
|
+
/** Text-to-video CLI. Override via --ttv flag or MARKCUT_TTV_CLI env var. */
|
|
96
|
+
export const DEFAULT_TTV_CLI = args.cliOverrides.ttv || process.env.MARKCUT_TTV_CLI || '';
|
|
97
|
+
/** Image-to-text CLI. Override via --itt flag or MARKCUT_ITT_CLI env var. */
|
|
98
|
+
export const DEFAULT_ITT_CLI = args.cliOverrides.itt || process.env.MARKCUT_ITT_CLI || 'uvx --from mlx-vlm mlx_vlm.generate --model mlx-community/MiniCPM-V-4.6-bf16 --max-tokens 2048 --prompt "{prompt}" --image {input} --temperature 0.0 --thinking-mode disabled';;
|
|
99
|
+
/** Video-to-text CLI. Override via --vtt flag or MARKCUT_VTT_CLI env var. */
|
|
100
|
+
export const DEFAULT_VTT_CLI = args.cliOverrides.vtt || process.env.MARKCUT_VTT_CLI || 'uvx --from mlx-vlm mlx_vlm.generate --model mlx-community/MiniCPM-V-4.6-bf16 --max-tokens 2048 --prompt "{prompt}" --video {input} --temperature 0.0 --processor-kwargs \'{"max_num_frames": 32, "stack_frames": 1, "max_slice_nums": 1, "use_image_id": false}\'';
|
|
@@ -713,8 +713,9 @@ export async function resolveGeneratedMedia(
|
|
|
713
713
|
walkDown(clone as any, (node) => {
|
|
714
714
|
if ((node.type !== "image" && node.type !== "video")) return;
|
|
715
715
|
if (!node.prompt || typeof node.prompt !== "string") return;
|
|
716
|
-
// Skip if src is already set (prompt is just metadata)
|
|
717
|
-
|
|
716
|
+
// Skip if src is already set to a real path (prompt is just metadata).
|
|
717
|
+
// "auto" is a placeholder meaning "auto-generate via TTI/TTV".
|
|
718
|
+
if (node.src && node.src !== "auto") return;
|
|
718
719
|
const id = node.id ?? `${node.type}-${genNodes.length}`;
|
|
719
720
|
genNodes.push({ node, id, type: node.type as "image" | "video", prompt: node.prompt });
|
|
720
721
|
});
|
package/src/player/pipeline.mjs
CHANGED
|
@@ -883,8 +883,8 @@ var require_format = __commonJS({
|
|
|
883
883
|
return format.apply(null, [fmt].concat(replacements));
|
|
884
884
|
}
|
|
885
885
|
function format(fmt) {
|
|
886
|
-
var argIndex = 1,
|
|
887
|
-
return
|
|
886
|
+
var argIndex = 1, args2 = [].slice.call(arguments), i = 0, n = fmt.length, result = "", c, escaped = false, arg, tmp, leadingZero = false, precision, nextArg = function() {
|
|
887
|
+
return args2[argIndex++];
|
|
888
888
|
}, slurpNumber = function() {
|
|
889
889
|
var digits = "";
|
|
890
890
|
while (/\d/.test(fmt[i])) {
|
|
@@ -972,22 +972,61 @@ import { existsSync, mkdirSync } from "node:fs";
|
|
|
972
972
|
import { dirname } from "node:path";
|
|
973
973
|
|
|
974
974
|
// src/config.mjs
|
|
975
|
+
var args = (function parseArgs(argv) {
|
|
976
|
+
const CLI_OVERRIDE_FLAGS = {
|
|
977
|
+
"--itt": "itt",
|
|
978
|
+
"--vtt": "vtt",
|
|
979
|
+
"--stt": "stt",
|
|
980
|
+
"--tts": "tts",
|
|
981
|
+
"--agent": "agent",
|
|
982
|
+
"--edit-cli": "editCli",
|
|
983
|
+
"--tti": "tti",
|
|
984
|
+
"--ttv": "ttv"
|
|
985
|
+
};
|
|
986
|
+
const args2 = { command: "", file: "", output: "", forceNew: false, verbose: false, label: false, edit: false, noBrowser: false, chat: false, port: 3001, compile: false, cli: false, showClis: false, scriptOutputDir: "", mediaOutputDir: "", variant: [], cliOverrides: {} };
|
|
987
|
+
let i = 2;
|
|
988
|
+
if (argv[i]) args2.command = argv[i++];
|
|
989
|
+
if (argv[i] && !argv[i].startsWith("--")) args2.file = argv[i++];
|
|
990
|
+
while (i < argv.length) {
|
|
991
|
+
const flag = argv[i++];
|
|
992
|
+
if (flag === "--output" && argv[i]) args2.output = argv[i++];
|
|
993
|
+
else if (flag === "--script-output-dir" && argv[i]) args2.scriptOutputDir = argv[i++];
|
|
994
|
+
else if (flag === "--media-output-dir" && argv[i]) args2.mediaOutputDir = argv[i++];
|
|
995
|
+
else if (flag === "--cli") args2.cli = true;
|
|
996
|
+
else if (flag === "--show-clis") args2.showClis = true;
|
|
997
|
+
else if (flag === "--compile") args2.compile = true;
|
|
998
|
+
else if (flag === "--force-new") args2.forceNew = true;
|
|
999
|
+
else if (flag === "--verbose") args2.verbose = true;
|
|
1000
|
+
else if (flag === "--label") args2.label = true;
|
|
1001
|
+
else if (flag === "--edit") args2.edit = true;
|
|
1002
|
+
else if (flag === "--no-browser") args2.noBrowser = true;
|
|
1003
|
+
else if (flag === "--port" && argv[i]) args2.port = parseInt(argv[i], 10);
|
|
1004
|
+
else if (flag.startsWith("--port=")) args2.port = parseInt(flag.split("=")[1], 10);
|
|
1005
|
+
else if (flag === "--variant" && argv[i]) args2.variant.push(argv[i++]);
|
|
1006
|
+
else if (flag.startsWith("--variant=")) args2.variant.push(flag.split("=")[1]);
|
|
1007
|
+
else if (CLI_OVERRIDE_FLAGS[flag] && argv[i]) {
|
|
1008
|
+
const key = CLI_OVERRIDE_FLAGS[flag];
|
|
1009
|
+
args2.cliOverrides[key] = argv[i++];
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
return args2;
|
|
1013
|
+
})(process.argv);
|
|
975
1014
|
var MAX_IMAGE_DIMENSION = Number(process.env.MARKCUT_MAX_IMAGE_DIMENSION) || 384;
|
|
976
1015
|
var MAX_VIDEO_DURATION = Number(process.env.MARKCUT_MAX_VIDEO_DURATION) || 60;
|
|
977
1016
|
var MAX_VIDEO_DIMENSION = Number(process.env.MARKCUT_MAX_VIDEO_DIMENSION) || 360;
|
|
978
|
-
var DEFAULT_ITT_CLI = process.env.MARKCUT_ITT_CLI || 'uvx --from mlx-vlm mlx_vlm.generate --model mlx-community/MiniCPM-V-4.6-bf16 --max-tokens 2048 --prompt "{prompt}" --image {input} --temperature 0.0 --thinking-mode disabled';
|
|
979
1017
|
var GOOGLE_MAPS_API_KEY = process.env.GOOGLE_MAPS_API_KEY || "";
|
|
980
1018
|
var DEFAULT_VTT_SAMPLE_INTERVAL = Number(process.env.MARKCUT_VTT_SAMPLE_INTERVAL) || 5;
|
|
981
|
-
var
|
|
982
|
-
var
|
|
983
|
-
var
|
|
984
|
-
var
|
|
985
|
-
var
|
|
986
|
-
var
|
|
987
|
-
var
|
|
1019
|
+
var DEFAULT_STT_CLI = args.cliOverrides.stt || process.env.MARKCUT_STT_CLI || 'uvx --from openai-whisper whisper "{input}" --output_format vtt --output_dir "{output}"';
|
|
1020
|
+
var DEFAULT_TTS_CLI = args.cliOverrides.tts || process.env.MARKCUT_TTS_CLI || 'uvx edge-tts --voice "en-US-GuyNeural" --text "{input}" --write-media "{output}"';
|
|
1021
|
+
var DEFAULT_AGENT_CLI = args.cliOverrides.agent || process.env.MARKCUT_AGENT_CLI || "npx pi -p {prompt}";
|
|
1022
|
+
var DEFAULT_EDIT_CLI = args.cliOverrides.editCli || process.env.MARKCUT_EDIT_CLI || "npx pi --session-id {sessionid} --system-prompt {systemprompt} -p {prompt}";
|
|
1023
|
+
var DEFAULT_TTI_CLI = args.cliOverrides.tti || process.env.MARKCUT_TTI_CLI || 'uvx --from mflux mflux-generate-flux2 --model flux2-klein-4b --steps 5 --prompt "{input}" --output "{output}" --seed {seed}';
|
|
1024
|
+
var DEFAULT_TTV_CLI = args.cliOverrides.ttv || process.env.MARKCUT_TTV_CLI || "";
|
|
1025
|
+
var DEFAULT_ITT_CLI = args.cliOverrides.itt || process.env.MARKCUT_ITT_CLI || 'uvx --from mlx-vlm mlx_vlm.generate --model mlx-community/MiniCPM-V-4.6-bf16 --max-tokens 2048 --prompt "{prompt}" --image {input} --temperature 0.0 --thinking-mode disabled';
|
|
1026
|
+
var DEFAULT_VTT_CLI = args.cliOverrides.vtt || process.env.MARKCUT_VTT_CLI || `uvx --from mlx-vlm mlx_vlm.generate --model mlx-community/MiniCPM-V-4.6-bf16 --max-tokens 2048 --prompt "{prompt}" --video {input} --temperature 0.0 --processor-kwargs '{"max_num_frames": 32, "stack_frames": 1, "max_slice_nums": 1, "use_image_id": false}'`;
|
|
988
1027
|
|
|
989
1028
|
// src/render/cli-tools.ts
|
|
990
|
-
function substituteCli(template, input, output) {
|
|
1029
|
+
function substituteCli(template, input, output, seed) {
|
|
991
1030
|
const safeOutput = output;
|
|
992
1031
|
let safeInput;
|
|
993
1032
|
const inputMatch = template.match(/(['"])\{input\}/);
|
|
@@ -1001,7 +1040,8 @@ function substituteCli(template, input, output) {
|
|
|
1001
1040
|
} else {
|
|
1002
1041
|
safeInput = input.replace(/"/g, '\\"').replace(/'/g, "'\\''");
|
|
1003
1042
|
}
|
|
1004
|
-
|
|
1043
|
+
const safeSeed = seed !== void 0 ? String(seed) : "";
|
|
1044
|
+
return template.replace(/\{input\}/g, safeInput).replace(/\{output\}/g, safeOutput).replace(/\{seed\}/g, safeSeed);
|
|
1005
1045
|
}
|
|
1006
1046
|
function generateTTS(text3, outputPath, cli) {
|
|
1007
1047
|
mkdirSync(dirname(outputPath), { recursive: true });
|
|
@@ -1022,9 +1062,9 @@ async function generateSTT(audioPath, outputDir, cli) {
|
|
|
1022
1062
|
console.warn(` \u26A0 STT failed: ${e.message}`);
|
|
1023
1063
|
}
|
|
1024
1064
|
}
|
|
1025
|
-
function generateTTI(prompt, outputPath, cli) {
|
|
1065
|
+
function generateTTI(prompt, outputPath, cli, seed) {
|
|
1026
1066
|
mkdirSync(dirname(outputPath), { recursive: true });
|
|
1027
|
-
const cmd = substituteCli(cli ?? DEFAULT_TTI_CLI, prompt, outputPath);
|
|
1067
|
+
const cmd = substituteCli(cli ?? DEFAULT_TTI_CLI, prompt, outputPath, seed);
|
|
1028
1068
|
try {
|
|
1029
1069
|
execSync(cmd, {
|
|
1030
1070
|
encoding: "utf-8",
|
|
@@ -1038,11 +1078,11 @@ function generateTTI(prompt, outputPath, cli) {
|
|
|
1038
1078
|
}
|
|
1039
1079
|
return existsSync(outputPath) ? outputPath : "";
|
|
1040
1080
|
}
|
|
1041
|
-
function generateTTV(prompt, outputPath, cli, ttiCmd) {
|
|
1081
|
+
function generateTTV(prompt, outputPath, cli, ttiCmd, seed) {
|
|
1042
1082
|
mkdirSync(dirname(outputPath), { recursive: true });
|
|
1043
1083
|
if (!cli) {
|
|
1044
1084
|
const pngPath = outputPath.replace(/\.mp4$/, ".png");
|
|
1045
|
-
const imageResult = generateTTI(prompt, pngPath, ttiCmd);
|
|
1085
|
+
const imageResult = generateTTI(prompt, pngPath, ttiCmd, seed);
|
|
1046
1086
|
if (!imageResult || !existsSync(imageResult)) {
|
|
1047
1087
|
console.error(` \u2717 TTV: TTI step produced no image for "${prompt.slice(0, 50)}..."`);
|
|
1048
1088
|
return "";
|
|
@@ -1062,7 +1102,7 @@ function generateTTV(prompt, outputPath, cli, ttiCmd) {
|
|
|
1062
1102
|
}
|
|
1063
1103
|
return existsSync(outputPath) ? outputPath : "";
|
|
1064
1104
|
}
|
|
1065
|
-
const cmd = substituteCli(cli, prompt, outputPath);
|
|
1105
|
+
const cmd = substituteCli(cli, prompt, outputPath, seed);
|
|
1066
1106
|
try {
|
|
1067
1107
|
execSync(cmd, {
|
|
1068
1108
|
encoding: "utf-8",
|
|
@@ -10977,14 +11017,15 @@ async function resolveGeneratedMedia(root, options) {
|
|
|
10977
11017
|
walkDown(clone, (node2) => {
|
|
10978
11018
|
if (node2.type !== "image" && node2.type !== "video") return;
|
|
10979
11019
|
if (!node2.prompt || typeof node2.prompt !== "string") return;
|
|
10980
|
-
if (node2.src) return;
|
|
11020
|
+
if (node2.src && node2.src !== "auto") return;
|
|
10981
11021
|
const id = node2.id ?? `${node2.type}-${genNodes.length}`;
|
|
10982
11022
|
genNodes.push({ node: node2, id, type: node2.type, prompt: node2.prompt });
|
|
10983
11023
|
});
|
|
10984
11024
|
for (const { node: node2, id, type, prompt } of genNodes) {
|
|
10985
11025
|
const ext = type === "image" ? "png" : "mp4";
|
|
10986
11026
|
const cli = type === "image" ? clone.tti ?? options.ttiCli ?? DEFAULT_TTI_CLI : clone.ttv ?? options.ttvCli ?? DEFAULT_TTV_CLI;
|
|
10987
|
-
const
|
|
11027
|
+
const seed = options.seed;
|
|
11028
|
+
const cacheKey = computeCacheKey({ prompt, cli, type, seed });
|
|
10988
11029
|
const outputPath = join(options.outputDir, `${cacheKey}.${ext}`);
|
|
10989
11030
|
const cached = checkCache(cache, `gen:${cacheKey}`, cacheKey);
|
|
10990
11031
|
const label = type === "image" ? "TTI" : "TTV";
|
|
@@ -10997,7 +11038,7 @@ async function resolveGeneratedMedia(root, options) {
|
|
|
10997
11038
|
try {
|
|
10998
11039
|
console.log(` \u{1F50A} ${label}: ${labelText}...`);
|
|
10999
11040
|
const ttiCmd = clone.tti ?? options.ttiCli ?? DEFAULT_TTI_CLI;
|
|
11000
|
-
const result = type === "image" ? generateTTI(prompt, outputPath, cli) : generateTTV(prompt, outputPath, cli, ttiCmd);
|
|
11041
|
+
const result = type === "image" ? generateTTI(prompt, outputPath, cli, seed) : generateTTV(prompt, outputPath, cli, ttiCmd, seed);
|
|
11001
11042
|
if (result) {
|
|
11002
11043
|
node2.src = outputPath;
|
|
11003
11044
|
updateCache(cache, `gen:${cacheKey}`, cacheKey, outputPath);
|
|
@@ -11107,11 +11148,13 @@ async function resolveAll2(root, options = {}) {
|
|
|
11107
11148
|
variant: options.variants?.[0] ?? "video"
|
|
11108
11149
|
});
|
|
11109
11150
|
result = await resolveMediaSrcs(result, { baseDir: options.baseDir });
|
|
11151
|
+
const generationSeed = result.seed ?? options.seed;
|
|
11110
11152
|
if (options.mediaOutputDir) {
|
|
11111
11153
|
result = await resolveGeneratedMedia(result, {
|
|
11112
11154
|
outputDir: options.mediaOutputDir,
|
|
11113
11155
|
ttiCli: options.ttiCli,
|
|
11114
|
-
ttvCli: options.ttvCli
|
|
11156
|
+
ttvCli: options.ttvCli,
|
|
11157
|
+
seed: generationSeed
|
|
11115
11158
|
});
|
|
11116
11159
|
}
|
|
11117
11160
|
result = await resolveMediaDurations(result, {
|
package/src/render/cli.mjs
CHANGED
|
@@ -11,6 +11,11 @@ import { readFileSync, writeFileSync, mkdirSync, existsSync, copyFileSync } from
|
|
|
11
11
|
import { createHash } from "node:crypto";
|
|
12
12
|
import { resolve, dirname, join, extname } from "node:path";
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
|
+
import {
|
|
15
|
+
DEFAULT_TTS_CLI, DEFAULT_STT_CLI, DEFAULT_TTI_CLI, DEFAULT_TTV_CLI,
|
|
16
|
+
DEFAULT_ITT_CLI, DEFAULT_VTT_CLI, DEFAULT_AGENT_CLI, DEFAULT_EDIT_CLI,
|
|
17
|
+
args,
|
|
18
|
+
} from "../config.mjs";
|
|
14
19
|
|
|
15
20
|
const __filename = fileURLToPath(import.meta.url);
|
|
16
21
|
const __dirname = dirname(__filename);
|
|
@@ -44,13 +49,27 @@ function usage() {
|
|
|
44
49
|
console.log(`
|
|
45
50
|
markcut CLI — Markdown/JSON → video pipeline
|
|
46
51
|
|
|
52
|
+
npx @lalalic/markcut <command> [options]
|
|
53
|
+
|
|
54
|
+
global options:
|
|
55
|
+
--show-clis Print all default CLI templates and exit
|
|
56
|
+
|
|
57
|
+
--tti <template> Override default TTI CLI template
|
|
58
|
+
--tts <template> Override default TTS CLI template
|
|
59
|
+
--stt <template> Override default STT CLI template
|
|
60
|
+
--ttv <template> Override default TTV CLI template
|
|
61
|
+
--itt <template> Override default ITT CLI template
|
|
62
|
+
--vtt <template> Override default VTT CLI template
|
|
63
|
+
--agent <template> Override default agent CLI template
|
|
64
|
+
--edit-cli <template> Override default edit agent CLI template
|
|
65
|
+
|
|
66
|
+
--help Show this help
|
|
67
|
+
|
|
47
68
|
Commands:
|
|
48
69
|
|
|
49
70
|
verify <file> Parse + validate descriptive file
|
|
50
|
-
--cli Check required CLI tools are installed
|
|
51
71
|
|
|
52
72
|
preview <file.json|.md> Open player with live preview
|
|
53
|
-
--edit Auto-reload on file change
|
|
54
73
|
--label Open label input overlay
|
|
55
74
|
--no-browser Skip opening browser automatically
|
|
56
75
|
--port <num> Port for the player server (default: 3001)
|
|
@@ -61,47 +80,16 @@ Commands:
|
|
|
61
80
|
|
|
62
81
|
vision <folder> Extract metadata into metadata.json
|
|
63
82
|
--label Full pipeline: preview → label → normalize → percept → segments
|
|
64
|
-
--agent <template> Custom text LLM CLI for detect-scenes ({prompt})
|
|
65
|
-
--itt <template> Custom ITT CLI template with {input}, {prompt}
|
|
66
|
-
--vtt <template> Custom VTT CLI template with {input}, {prompt}
|
|
67
|
-
--stt <template> Custom STT CLI template with {input}, {output}
|
|
68
83
|
--prompts-file <path> Path to prompts markdown file (default: vision_prompts.md)
|
|
69
84
|
--vtt-sample-interval <n> Sample one video frame every N seconds (default: 5)
|
|
70
85
|
--context "text" Background context about people/places (injected into prompts)
|
|
71
|
-
--pick <files> Comma-separated filenames to process
|
|
72
86
|
--skip-stt Skip speech-to-text for videos
|
|
73
87
|
--dry-run Show what would be processed without running AI
|
|
74
88
|
--show-prompts Print the prompts file and exit
|
|
75
|
-
--show-clis Print the default ITT/VTT/STT CLI templates
|
|
76
|
-
--help Show this help
|
|
77
89
|
--<prompt-name> "text" Override any prompt template from vision_prompts.md
|
|
78
90
|
`);
|
|
79
91
|
}
|
|
80
92
|
|
|
81
|
-
export function parseArgs(argv) {
|
|
82
|
-
const args = { command: "", file: "", output: "", forceNew: false, verbose: false, label: false, edit: false, noBrowser: false, chat: false, port: 3001, compile: false, cli: false, scriptOutputDir: "", mediaOutputDir: "", variant: [] };
|
|
83
|
-
let i = 2;
|
|
84
|
-
if (argv[i]) args.command = argv[i++];
|
|
85
|
-
if (argv[i] && !argv[i].startsWith("--")) args.file = argv[i++];
|
|
86
|
-
while (i < argv.length) {
|
|
87
|
-
const flag = argv[i++];
|
|
88
|
-
if (flag === "--output" && argv[i]) args.output = argv[i++];
|
|
89
|
-
else if (flag === "--script-output-dir" && argv[i]) args.scriptOutputDir = argv[i++];
|
|
90
|
-
else if (flag === "--media-output-dir" && argv[i]) args.mediaOutputDir = argv[i++];
|
|
91
|
-
else if (flag === "--cli") args.cli = true;
|
|
92
|
-
else if (flag === "--compile") args.compile = true;
|
|
93
|
-
else if (flag === "--force-new") args.forceNew = true;
|
|
94
|
-
else if (flag === "--verbose") args.verbose = true;
|
|
95
|
-
else if (flag === "--label") args.label = true;
|
|
96
|
-
else if (flag === "--edit") args.edit = true;
|
|
97
|
-
else if (flag === "--no-browser") args.noBrowser = true;
|
|
98
|
-
else if (flag === "--port" && argv[i]) args.port = parseInt(argv[i], 10);
|
|
99
|
-
else if (flag.startsWith("--port=")) args.port = parseInt(flag.split("=")[1], 10);
|
|
100
|
-
else if (flag === "--variant" && argv[i]) args.variant.push(argv[i++]);
|
|
101
|
-
else if (flag.startsWith("--variant=")) args.variant.push(flag.split("=")[1]);
|
|
102
|
-
}
|
|
103
|
-
return args;
|
|
104
|
-
}
|
|
105
93
|
|
|
106
94
|
/**
|
|
107
95
|
* Render one aspect ratio with compact progress output.
|
|
@@ -222,13 +210,32 @@ function renderOne(streamTree, aspect, outputPath, verbose) {
|
|
|
222
210
|
}
|
|
223
211
|
|
|
224
212
|
async function main() {
|
|
225
|
-
const args = parseArgs(process.argv);
|
|
226
|
-
|
|
227
213
|
if (!args.command || args.command === "help" || args.command === "--help") {
|
|
228
214
|
usage();
|
|
229
215
|
process.exit(0);
|
|
230
216
|
}
|
|
231
217
|
|
|
218
|
+
// --show-clis works as a command or a flag
|
|
219
|
+
if (args.showClis || args.command === "--show-clis") {
|
|
220
|
+
console.log(`Default CLI
|
|
221
|
+
tti=${DEFAULT_TTI_CLI}
|
|
222
|
+
|
|
223
|
+
ttv=${DEFAULT_TTV_CLI || "(empty — falls back to TTI + ffmpeg to produce a 3s MP4)"}
|
|
224
|
+
|
|
225
|
+
tts=${DEFAULT_TTS_CLI}
|
|
226
|
+
|
|
227
|
+
stt=${DEFAULT_STT_CLI}
|
|
228
|
+
|
|
229
|
+
itt=${DEFAULT_ITT_CLI}
|
|
230
|
+
|
|
231
|
+
vtt=${DEFAULT_VTT_CLI || "(empty — uses ITT via frame extraction)"}
|
|
232
|
+
|
|
233
|
+
agent=${DEFAULT_AGENT_CLI}
|
|
234
|
+
|
|
235
|
+
edit=${DEFAULT_EDIT_CLI}`);
|
|
236
|
+
process.exit(0);
|
|
237
|
+
}
|
|
238
|
+
|
|
232
239
|
if (args.command === "vision") {
|
|
233
240
|
const { main: visionMain } = await import("../vision/cli.mjs");
|
|
234
241
|
await visionMain(process.argv);
|
|
@@ -487,40 +494,6 @@ function hasScript(root) {
|
|
|
487
494
|
}
|
|
488
495
|
walkMedia(descriptive.children);
|
|
489
496
|
|
|
490
|
-
// ── Check 2: CLI tool availability (--cli flag) ────────────────────
|
|
491
|
-
if (args.cli) {
|
|
492
|
-
function extractCmd(configStr) {
|
|
493
|
-
if (!configStr) return null;
|
|
494
|
-
return configStr.trim().split(/\s+/)[0] || null;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
const pipelineConfigs = [
|
|
498
|
-
{ type: "TTS", config: descriptive.tts, defaultCmd: "edge-tts", needsCheck: hasScript(descriptive) },
|
|
499
|
-
{ type: "STT", config: descriptive.stt, defaultCmd: "whisper", needsCheck: hasScript(descriptive) },
|
|
500
|
-
{ type: "TTI", config: descriptive.tti, defaultCmd: "pi", needsCheck: needsTti },
|
|
501
|
-
{ type: "TTV", config: descriptive.ttv, defaultCmd: "pi", needsCheck: needsTtv },
|
|
502
|
-
];
|
|
503
|
-
|
|
504
|
-
const checked = new Set();
|
|
505
|
-
for (const p of pipelineConfigs) {
|
|
506
|
-
if (!p.needsCheck) continue;
|
|
507
|
-
const cmd = extractCmd(p.config) || p.defaultCmd;
|
|
508
|
-
if (checked.has(cmd)) continue;
|
|
509
|
-
checked.add(cmd);
|
|
510
|
-
const { execSync } = await import("node:child_process");
|
|
511
|
-
try {
|
|
512
|
-
execSync(`which ${cmd}`, { stdio: "ignore" });
|
|
513
|
-
} catch {
|
|
514
|
-
const hints = {
|
|
515
|
-
"pi": " (pip install pi-sdk)",
|
|
516
|
-
"edge-tts": " (pip install edge-tts)",
|
|
517
|
-
"whisper": " (pip install openai-whisper)",
|
|
518
|
-
};
|
|
519
|
-
errors.push(`CLI tool "${cmd}" not found${hints[cmd] || ""}. Required by ${p.type}`);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
|
|
524
497
|
// ── Results ─────────────────────────────────────────────────────────
|
|
525
498
|
for (const w of warnings) emitWarn(w);
|
|
526
499
|
if (errors.length > 0) {
|