@lalalic/markcut 1.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/.env.example +27 -0
- package/.github/user-steer.md +9 -0
- package/.vscode/settings.json +3 -0
- package/AGENTS.md +271 -0
- package/README.md +219 -0
- package/SKILL.md +209 -0
- package/docs/dynamic-components.md +191 -0
- package/docs/edit-mode.md +220 -0
- package/docs/json-descriptive.md +539 -0
- package/docs/label-mode.md +110 -0
- package/docs/markdown-descriptive.md +751 -0
- package/docs/templates.md +52 -0
- package/package.json +64 -0
- package/remotion.config.ts +5 -0
- package/scripts/artlist-dl.mjs +190 -0
- package/scripts/build-pipeline.sh +19 -0
- package/scripts/build-player.sh +20 -0
- package/src/Root.tsx +55 -0
- package/src/config.mjs +88 -0
- package/src/context/EventContext.tsx +168 -0
- package/src/context/index.tsx +42 -0
- package/src/descriptive/compiler.test.ts +1135 -0
- package/src/descriptive/compiler.ts +1230 -0
- package/src/descriptive/dsl.ts +455 -0
- package/src/descriptive/markdown.test.ts +866 -0
- package/src/descriptive/markdown.ts +674 -0
- package/src/descriptive/resolve.test.ts +951 -0
- package/src/descriptive/resolve.ts +891 -0
- package/src/entry.tsx +163 -0
- package/src/index.ts +4 -0
- package/src/player/browser.tsx +356 -0
- package/src/player/bundle/player.js +60259 -0
- package/src/player/bundler.mjs +269 -0
- package/src/player/label-server.mjs +599 -0
- package/src/player/pipeline.mjs +11123 -0
- package/src/player/pipeline.ts +117 -0
- package/src/player/server-shared.mjs +144 -0
- package/src/player/server.mjs +1006 -0
- package/src/render/cli-tools.ts +177 -0
- package/src/render/cli.mjs +628 -0
- package/src/schema/index.ts +259 -0
- package/src/types/Audio.tsx +56 -0
- package/src/types/Component.tsx +135 -0
- package/src/types/Effect.tsx +88 -0
- package/src/types/Folder.tsx +180 -0
- package/src/types/FrameSyncStyle.tsx +51 -0
- package/src/types/Image.tsx +51 -0
- package/src/types/Include.tsx +394 -0
- package/src/types/Map.tsx +252 -0
- package/src/types/Rhythm.tsx +58 -0
- package/src/types/Scene.tsx +42 -0
- package/src/types/Subtitle.tsx +218 -0
- package/src/types/Video.tsx +70 -0
- package/src/types/keyframes.ts +454 -0
- package/src/utils/__tests__/vtt.test.ts +129 -0
- package/src/utils/index.ts +168 -0
- package/src/utils/tween.ts +118 -0
- package/src/vision/cli.mjs +1187 -0
- package/src/vision/vision_prompts.md +67 -0
- package/tests/dsl.test.ts +317 -0
- package/tests/fixtures/audio.json +25 -0
- package/tests/fixtures/basic.json +27 -0
- package/tests/fixtures/component-all.json +38 -0
- package/tests/fixtures/components.json +38 -0
- package/tests/fixtures/effects.json +64 -0
- package/tests/fixtures/full.json +51 -0
- package/tests/fixtures/map.json +23 -0
- package/tests/fixtures/md/all-nodes.md +28 -0
- package/tests/fixtures/md/basic.md +6 -0
- package/tests/fixtures/md/component-imports.md +20 -0
- package/tests/fixtures/md/edge-cases.md +33 -0
- package/tests/fixtures/md/effects.md +17 -0
- package/tests/fixtures/md/frontmatter.md +20 -0
- package/tests/fixtures/md/full-feature.md +58 -0
- package/tests/fixtures/md/imports-block.md +19 -0
- package/tests/fixtures/md/include-main.md +11 -0
- package/tests/fixtures/md/include-sub.md +25 -0
- package/tests/fixtures/md/jsx-code-fence.md +21 -0
- package/tests/fixtures/md/map.md +11 -0
- package/tests/fixtures/md/nested-scenes.md +25 -0
- package/tests/fixtures/md/rhythm.md +17 -0
- package/tests/fixtures/md/scenes.md +16 -0
- package/tests/fixtures/md/tween.md +11 -0
- package/tests/fixtures/md/vars-test.md +6 -0
- package/tests/fixtures/scenes.json +40 -0
- package/tests/fixtures/subtitle.json +59 -0
- package/tests/fixtures/subvideo.json +59 -0
- package/tests/fixtures/templates/courseware.md +351 -0
- package/tests/fixtures/tween-visual.json +28 -0
- package/tests/fixtures/video-series.json +54 -0
- package/tests/md-descriptive.test.ts +742 -0
- package/tests/render.test.ts +985 -0
- package/tests/schema.test.ts +68 -0
- package/tests/server.test.ts +308 -0
- package/tests/utils.ts +391 -0
- package/tests/vitest.config.ts +18 -0
- package/tests/vitest.integration.config.ts +16 -0
- package/tsconfig.json +20 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 4 CLI wrappers for generation tasks: TTS, STT, TTI, TTV.
|
|
3
|
+
*
|
|
4
|
+
* Each function is a simple one-shot command wrapper — substitute {input}/{output}
|
|
5
|
+
* in the template, run the CLI, return the output path (or empty string on failure).
|
|
6
|
+
*
|
|
7
|
+
* This module exists so tests can mock each function individually instead of
|
|
8
|
+
* mocking low-level execSync/exec, which is brittle and slow.
|
|
9
|
+
*
|
|
10
|
+
* Default CLI templates are imported from src/config.mjs — the single source of truth.
|
|
11
|
+
* Override via MARKCUT_TTS_CLI / MARKCUT_STT_CLI / MARKCUT_TTI_CLI / MARKCUT_TTV_CLI.
|
|
12
|
+
*/
|
|
13
|
+
import { execSync, exec } from "node:child_process";
|
|
14
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
15
|
+
import { dirname } from "node:path";
|
|
16
|
+
import {
|
|
17
|
+
DEFAULT_TTS_CLI,
|
|
18
|
+
DEFAULT_STT_CLI,
|
|
19
|
+
DEFAULT_TTI_CLI,
|
|
20
|
+
DEFAULT_TTV_CLI,
|
|
21
|
+
} from "../config.mjs";
|
|
22
|
+
|
|
23
|
+
export { DEFAULT_TTS_CLI, DEFAULT_STT_CLI, DEFAULT_TTI_CLI, DEFAULT_TTV_CLI };
|
|
24
|
+
|
|
25
|
+
// ── Helpers ───────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Smart placeholder substitution with quote-aware escaping.
|
|
29
|
+
*
|
|
30
|
+
* Detects whether {input}/{output} is wrapped in single or double quotes
|
|
31
|
+
* in the template and applies the correct escaping strategy:
|
|
32
|
+
*
|
|
33
|
+
* - Double-quoted: escape `"` → `\"` and keep `'` literal
|
|
34
|
+
* - Single-quoted: escape `'` → `'\''` (end quote, escaped quote, resume)
|
|
35
|
+
* - Unquoted: escape both `'` and `"`
|
|
36
|
+
*
|
|
37
|
+
* Examples:
|
|
38
|
+
* template: --text "{input}" input: it's "great"
|
|
39
|
+
* → --text "it's \"great\""
|
|
40
|
+
*
|
|
41
|
+
* template: --text '{input}' input: it's "great"
|
|
42
|
+
* → --text 'it'\''s "great"'
|
|
43
|
+
*/
|
|
44
|
+
function substituteCli(template: string, input: string, output: string): string {
|
|
45
|
+
// Escape the output value (file paths rarely have quotes, but be safe)
|
|
46
|
+
const safeOutput = output;
|
|
47
|
+
|
|
48
|
+
// For input, detect quote context and escape accordingly
|
|
49
|
+
let safeInput: string;
|
|
50
|
+
// Look for {input} preceded by a quote character (ignoring whitespace)
|
|
51
|
+
const inputMatch = template.match(/(['"])\{input\}/);
|
|
52
|
+
if (inputMatch) {
|
|
53
|
+
const quote = inputMatch[1]!;
|
|
54
|
+
if (quote === '"') {
|
|
55
|
+
// Double-quoted: escape double quotes
|
|
56
|
+
safeInput = input.replace(/"/g, '\\"');
|
|
57
|
+
} else {
|
|
58
|
+
// Single-quoted: escape single quotes using '\'' sequence
|
|
59
|
+
safeInput = input.replace(/'/g, "'\\''");
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
// Unquoted: escape both
|
|
63
|
+
safeInput = input.replace(/"/g, '\\"').replace(/'/g, "'\\''");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return template
|
|
67
|
+
.replace(/\{input\}/g, safeInput)
|
|
68
|
+
.replace(/\{output\}/g, safeOutput);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// ── Exported functions ────────────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Generate TTS audio from text.
|
|
75
|
+
* @returns output path on success, empty string on failure.
|
|
76
|
+
*/
|
|
77
|
+
export function generateTTS(text: string, outputPath: string, cli?: string): string {
|
|
78
|
+
mkdirSync(dirname(outputPath), { recursive: true });
|
|
79
|
+
const cmd = substituteCli(cli ?? DEFAULT_TTS_CLI, text, outputPath);
|
|
80
|
+
try {
|
|
81
|
+
execSync(cmd, { stdio: "pipe" });
|
|
82
|
+
} catch (e: any) {
|
|
83
|
+
console.warn(` ⚠ TTS failed: ${e.message}`);
|
|
84
|
+
return "";
|
|
85
|
+
}
|
|
86
|
+
return existsSync(outputPath) ? outputPath : "";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Run STT on an audio file to generate VTT subtitles.
|
|
91
|
+
* Completes when the CLI exits (VTT will be in outputDir).
|
|
92
|
+
*/
|
|
93
|
+
export async function generateSTT(audioPath: string, outputDir: string, cli?: string): Promise<void> {
|
|
94
|
+
const cmd = substituteCli(cli ?? DEFAULT_STT_CLI, audioPath, outputDir);
|
|
95
|
+
try {
|
|
96
|
+
execSync(cmd, { stdio: "pipe" });
|
|
97
|
+
} catch (e: any) {
|
|
98
|
+
console.warn(` ⚠ STT failed: ${e.message}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Generate an image from a text prompt.
|
|
104
|
+
* @returns output path on success, empty string on failure.
|
|
105
|
+
*/
|
|
106
|
+
export function generateTTI(prompt: string, outputPath: string, cli?: string): string {
|
|
107
|
+
mkdirSync(dirname(outputPath), { recursive: true });
|
|
108
|
+
const cmd = substituteCli(cli ?? DEFAULT_TTI_CLI, prompt, outputPath);
|
|
109
|
+
try {
|
|
110
|
+
execSync(cmd, {
|
|
111
|
+
encoding: "utf-8",
|
|
112
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
113
|
+
timeout: 300_000,
|
|
114
|
+
env: { ...process.env, TTI_CMD: cli ?? DEFAULT_TTI_CLI },
|
|
115
|
+
});
|
|
116
|
+
} catch (e: any) {
|
|
117
|
+
console.error(` ✗ TTI failed: ${e.message}`);
|
|
118
|
+
return "";
|
|
119
|
+
}
|
|
120
|
+
return existsSync(outputPath) ? outputPath : "";
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Generate a video from a text prompt.
|
|
125
|
+
*
|
|
126
|
+
* When a custom `cli` template is provided, it's used as-is (template substitution
|
|
127
|
+
* + execSync). When no CLI is given (default), the function calls `generateTTI`
|
|
128
|
+
* to create an image from the prompt, then uses ffmpeg to produce a 3-second MP4.
|
|
129
|
+
*
|
|
130
|
+
* @param ttiCmd Optional TTI CLI template for the image step (defaults to DEFAULT_TTI_CLI).
|
|
131
|
+
* @returns output path on success, empty string on failure.
|
|
132
|
+
*/
|
|
133
|
+
export function generateTTV(
|
|
134
|
+
prompt: string,
|
|
135
|
+
outputPath: string,
|
|
136
|
+
cli?: string,
|
|
137
|
+
ttiCmd?: string,
|
|
138
|
+
): string {
|
|
139
|
+
mkdirSync(dirname(outputPath), { recursive: true });
|
|
140
|
+
|
|
141
|
+
if (!cli) {
|
|
142
|
+
// Default: use generateTTI to create an image, then ffmpeg to make a 3s MP4
|
|
143
|
+
const pngPath = outputPath.replace(/\.mp4$/, ".png");
|
|
144
|
+
const imageResult = generateTTI(prompt, pngPath, ttiCmd);
|
|
145
|
+
if (!imageResult || !existsSync(imageResult)) {
|
|
146
|
+
console.error(` ✗ TTV: TTI step produced no image for "${prompt.slice(0, 50)}..."`);
|
|
147
|
+
return "";
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
execSync(
|
|
151
|
+
`ffmpeg -y -loop 1 -i "${pngPath}" -c:v libx264 -t 3 -pix_fmt yuv420p "${outputPath}"`,
|
|
152
|
+
{ stdio: ["pipe", "pipe", "pipe"], timeout: 60_000 },
|
|
153
|
+
);
|
|
154
|
+
} catch (e: any) {
|
|
155
|
+
console.error(` ✗ TTV: ffmpeg failed: ${e.message}`);
|
|
156
|
+
return "";
|
|
157
|
+
}
|
|
158
|
+
// Clean up intermediate PNG
|
|
159
|
+
try { execSync(`rm "${pngPath}"`, { stdio: "pipe" }); } catch { /* best-effort */ }
|
|
160
|
+
return existsSync(outputPath) ? outputPath : "";
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Custom CLI mode: substitute template and run
|
|
164
|
+
const cmd = substituteCli(cli, prompt, outputPath);
|
|
165
|
+
try {
|
|
166
|
+
execSync(cmd, {
|
|
167
|
+
encoding: "utf-8",
|
|
168
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
169
|
+
timeout: 300_000,
|
|
170
|
+
env: { ...process.env, TTI_CMD: ttiCmd ?? DEFAULT_TTI_CLI },
|
|
171
|
+
});
|
|
172
|
+
} catch (e: any) {
|
|
173
|
+
console.error(` ✗ TTV failed: ${e.message}`);
|
|
174
|
+
return "";
|
|
175
|
+
}
|
|
176
|
+
return existsSync(outputPath) ? outputPath : "";
|
|
177
|
+
}
|