@effectnode/media 0.9.0 → 0.10.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/dist/backend/movie-backend/agent/prompt/script.md +111 -1
- package/dist/backend/movie-backend/core.js +33 -0
- package/dist/backend/movie-backend/generation-queue.d.ts +1 -1
- package/dist/backend/movie-backend/generation-queue.js +75 -8
- package/dist/backend/movie-backend/render-media.d.ts +79 -2
- package/dist/backend/movie-backend/render-media.js +722 -418
- package/frontend/src/movie-app/components/EditorTabs/AdvancedVoiceCloneTab.tsx +366 -0
- package/frontend/src/movie-app/components/EditorTabs/AudioToVideoTab.tsx +406 -0
- package/frontend/src/movie-app/components/EditorTabs/FastImageEditTab.tsx +47 -0
- package/frontend/src/movie-app/components/EditorTabs/GenerateVideoTab.tsx +155 -1
- package/frontend/src/movie-app/components/EditorTabs/MovieStudioTab.tsx +33 -13
- package/frontend/src/movie-app/components/EditorTabs/SetupAiModelTab.tsx +26 -5
- package/frontend/src/movie-app/components/EditorTabs/UpscaleTab.tsx +342 -0
- package/frontend/src/movie-app/components/EditorTabs/VoiceCloneTab.tsx +365 -0
- package/frontend/src/movie-app/components/ProjectEditorPage.tsx +130 -125
- package/frontend/src/movie-app/stores/advancedVoiceCloneStore.ts +207 -0
- package/frontend/src/movie-app/stores/aiModelStore.ts +25 -2
- package/frontend/src/movie-app/stores/audioToVideoStore.ts +274 -0
- package/frontend/src/movie-app/stores/generationStore.ts +156 -255
- package/frontend/src/movie-app/stores/movieStudioStore.ts +10 -3
- package/frontend/src/movie-app/stores/queueStore.ts +47 -1
- package/frontend/src/movie-app/stores/upscaleStore.ts +118 -0
- package/frontend/src/movie-app/stores/voiceCloneStore.ts +227 -0
- package/package.json +1 -1
- package/frontend/src/movie-app/components/EditorTabs/BatchVoiceVideoTab.tsx +0 -913
- package/frontend/src/movie-app/components/EditorTabs/ExtendVideoTab.tsx +0 -305
- package/frontend/src/movie-app/components/EditorTabs/ExtractImageTab.tsx +0 -249
- package/frontend/src/movie-app/components/EditorTabs/SceneVisualTab.tsx +0 -267
- package/frontend/src/movie-app/lib/batchVoiceStorage.ts +0 -75
- package/frontend/src/movie-app/stores/batchVoiceStore.ts +0 -990
- package/frontend/src/movie-app/stores/sceneVisualStore.ts +0 -251
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
```bash
|
|
4
4
|
|
|
5
|
+
uv run mlx_audio.tts.generate \
|
|
6
|
+
--model Qwen/Qwen3-TTS-12Hz-1.7B-Base \
|
|
7
|
+
--text "Ghost reporting. ... 你好嗎?" \
|
|
8
|
+
--ref_audio ./reference_voice.wav \
|
|
9
|
+
--play --output ./out --audio_format mp3 --stream --save --instruct "slow down"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
5
13
|
uv run mlx_audio.tts.generate \
|
|
6
14
|
--model Qwen/Qwen3-TTS-12Hz-1.7B-Base \
|
|
7
15
|
--text "Ghost reporting. ... 你好嗎?" \
|
|
@@ -10,13 +18,15 @@ uv run mlx_audio.tts.generate \
|
|
|
10
18
|
|
|
11
19
|
```
|
|
12
20
|
|
|
21
|
+
<!-- mlx-community/Kokoro-82M-bf16 -->
|
|
22
|
+
|
|
13
23
|
<!-- Low quality -->
|
|
14
24
|
|
|
15
25
|
```bash
|
|
16
26
|
|
|
17
27
|
uv run mlx_audio.tts.generate \
|
|
18
28
|
--model Qwen/Qwen3-TTS-12Hz-0.6B-Base \
|
|
19
|
-
--text "Ghost reporting. 明月幾時有?把酒問青天。不知天上宮闕,今夕是何年。我欲乘風歸去,又恐瓊樓玉宇,高處不勝寒。起舞弄清影,何似在人間!轉朱閣,低綺戶,照無眠。不應有恨,何事長向別時圓?人有悲歡離合,月有陰晴圓缺,此事古難全。但願人長久,千里共嬋娟。 中秋節快樂!" \
|
|
29
|
+
--text "Ghost reporting. 中秋節快樂! 明月幾時有?把酒問青天。不知天上宮闕,今夕是何年。我欲乘風歸去,又恐瓊樓玉宇,高處不勝寒。起舞弄清影,何似在人間!轉朱閣,低綺戶,照無眠。不應有恨,何事長向別時圓?人有悲歡離合,月有陰晴圓缺,此事古難全。但願人長久,千里共嬋娟。 中秋節快樂!" \
|
|
20
30
|
--ref_audio ./reference_voice.wav \
|
|
21
31
|
--play --output ./out --audio_format mp3 --stream --save --instruct "slow down"
|
|
22
32
|
|
|
@@ -70,6 +80,15 @@ mlxgen upscale \
|
|
|
70
80
|
--output input_upscaled_2048.png
|
|
71
81
|
|
|
72
82
|
|
|
83
|
+
mlxgen upscale \
|
|
84
|
+
--model AbstractFramework/seedvr2-7b-8bit \
|
|
85
|
+
--image-path input.png \
|
|
86
|
+
--resolution 1x \
|
|
87
|
+
--seed 42 \
|
|
88
|
+
--mlx-cache-limit-gb 100 \
|
|
89
|
+
--output input_refined_1x.png
|
|
90
|
+
|
|
91
|
+
|
|
73
92
|
```
|
|
74
93
|
|
|
75
94
|
# upscale video
|
|
@@ -100,4 +119,95 @@ mlxgen upscale \
|
|
|
100
119
|
--output upscalde_video.mp4
|
|
101
120
|
|
|
102
121
|
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
##
|
|
128
|
+
##
|
|
129
|
+
##
|
|
130
|
+
|
|
131
|
+
cd to workspace/python-src/dots-tts-mlx
|
|
132
|
+
|
|
133
|
+
# 1. install the quant-aware runtime (>= v0.2.0)
|
|
134
|
+
pip install "git+https://github.com/sb1992/dots-tts-mlx.git@v0.2.0"
|
|
135
|
+
|
|
136
|
+
# 2. download the variant you want (use "mf-int4/*" for the faster MeanFlow decoder)
|
|
137
|
+
hf download shraey/dots-tts-mlx --include "mf-int4/*" --local-dir ./dots-tts-mlx-weights
|
|
138
|
+
|
|
139
|
+
# 1. install the quant-aware runtime (>= v0.2.0)
|
|
140
|
+
pip install "git+https://github.com/sb1992/dots-tts-mlx.git@v0.2.0"
|
|
141
|
+
|
|
142
|
+
# 2. download the variant you want (use "mf-int4/*" for the faster MeanFlow decoder)
|
|
143
|
+
hf download shraey/dots-tts-mlx --include "int4/*" --local-dir ./dots-tts-mlx-weights
|
|
144
|
+
|
|
145
|
+
# 2. download the variant you want (use "mf-int4/*" for the faster MeanFlow decoder)
|
|
146
|
+
hf download shraey/dots-tts-mlx --include "mf-int4/*" --local-dir ./dots-tts-mlx-weights
|
|
147
|
+
|
|
148
|
+
# 3. run (files land in ./dots-tts-mlx-weights/int4/)
|
|
149
|
+
dots-tts --model ./dots-tts-mlx-weights/int4 \
|
|
150
|
+
--text "Hello from MLX." --ref-audio reference.wav --language YUE \
|
|
151
|
+
--out-path out --out-prefix clone
|
|
152
|
+
|
|
153
|
+
dots-tts --model ./dots-tts-mlx-weights/mf-int4 \
|
|
154
|
+
--text "我講一個笑話比你聽. 有一日,個阿伯去茶餐廳食飯,嗌咗個「揚州炒飯」。食食吓,阿伯叫住伙計:「哥仔!你呢個揚州炒飯裡面,點解連一隻蝦都冇嘅?」伙計好冷靜咁答佢:「阿伯,咁你食『煲仔飯』嗰陣,裡面又有冇煲仔呀?」" \
|
|
155
|
+
--ref-audio reference.wav --language YUE \
|
|
156
|
+
--out-path out --out-prefix cantonese; \
|
|
157
|
+
afplay ./out/cantonese_000.wav
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
dots-tts --model ./dots-tts-mlx-weights/int4 \
|
|
161
|
+
--text "Ghost reporting. 我講一個笑話比你聽. 有一日,個阿伯去茶餐廳食飯,嗌咗個「揚州炒飯」。食食吓,阿伯叫住伙計:「哥仔!你呢個揚州炒飯裡面,點解連一隻蝦都冇嘅?」伙計好冷靜咁答佢:「阿伯,咁你食『煲仔飯』嗰陣,裡面又有冇煲仔呀?」" \
|
|
162
|
+
--ref-audio reference.wav --language YUE \
|
|
163
|
+
--out-path out --out-prefix fast; \
|
|
164
|
+
|
|
165
|
+
afplay ./out/fast_000.wav
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
dots-tts --model ./dots-tts-mlx-weights/int4 \
|
|
169
|
+
--text "Ghost reporting. 今日有d dry. 等我講一個笑話比你聽啦! 有一日,個阿伯去茶餐廳食飯,嗌咗個「揚州炒飯」。食食吓,阿伯叫住伙計:「哥仔!你呢個揚州炒飯裡面,點解連一隻蝦都冇嘅?」伙計好冷靜咁答佢:「阿伯,咁你食『煲仔飯』嗰陣,裡面又有冇煲仔呀?」 哈哈哈哈" \
|
|
170
|
+
--ref-audio reference.wav --language YUE \
|
|
171
|
+
--out-path out --out-prefix good_int4; \
|
|
172
|
+
afplay ./out/good_int4_000.wav
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
dots-tts --model ./dots-tts-mlx-weights/mf-int4 \
|
|
176
|
+
--text "Ghost reporting. 今日有d dry. 等我講一個笑話比你聽啦! 有一日,個阿伯去茶餐廳食飯,嗌咗個「揚州炒飯」。食食吓,阿伯叫住伙計:「哥仔!你呢個揚州炒飯裡面,點解連一隻蝦都冇嘅?」伙計好冷靜咁答佢:「阿伯,咁你食『煲仔飯』嗰陣,裡面又有冇煲仔呀?」 哈哈哈哈" \
|
|
177
|
+
--ref-audio reference.wav --language YUE \
|
|
178
|
+
--out-path out --out-prefix good_mf-int4; \
|
|
179
|
+
|
|
180
|
+
afplay ./out/good_mf-int4_000.wav
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
## not very good.
|
|
184
|
+
|
|
185
|
+
# Faster 15 steps
|
|
186
|
+
# cwd at the git folder
|
|
187
|
+
uv run ltx-2-mlx a2v --image /Users/loklok/coder-workspace/cantonese/food.png --audio /Users/loklok/coder-workspace/cantonese/food-short.wav --frame-rate 24 --output ./out-mp4 --prompt "scene at restarurant" --stage1-steps 15 --stage2-steps 3
|
|
188
|
+
|
|
189
|
+
# Standard 30 steps
|
|
190
|
+
# cwd at the git folder
|
|
191
|
+
|
|
192
|
+
uv run ltx-2-mlx a2v --image /Users/loklok/coder-workspace/cantonese/food.png --audio /Users/loklok/coder-workspace/cantonese/food-short.wav --frame-rate 24 --output ./out-mp4 --prompt "scene at restarurant" --stage1-steps 30 --stage2-steps 3
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
uv run ltx-2-mlx a2v --image /Users/loklok/coder-workspace/cantonese/food.png --audio /Users/loklok/coder-workspace/cantonese/food-short.wav --frame-rate 24 --frames 481 --output ./out/yo.mp4 --prompt "happy food ondering story at the restarurant" --stage1-steps 8 --stage2-steps 3
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
mkdir dots-tts-mlx
|
|
199
|
+
git clone https://github.com/sb1992/dots-tts-mlx.git
|
|
200
|
+
cd dots-tts-mlx
|
|
201
|
+
uv run pip install -e .
|
|
202
|
+
|
|
203
|
+
##
|
|
204
|
+
##
|
|
205
|
+
##
|
|
206
|
+
## dgrauet/ltx-2.3-mlx (High Quality)
|
|
207
|
+
## dgrauet/ltx-2.3-mlx-q8 (Standard Quality)
|
|
208
|
+
##
|
|
209
|
+
##
|
|
210
|
+
##
|
|
211
|
+
|
|
212
|
+
|
|
103
213
|
```
|
|
@@ -568,6 +568,39 @@ async function installPythonDependencies() {
|
|
|
568
568
|
return false;
|
|
569
569
|
}
|
|
570
570
|
}
|
|
571
|
+
{
|
|
572
|
+
const pythonAppSrcDir = join(APP_DATA_DIR, "python-src");
|
|
573
|
+
if (!existsSync(pythonAppSrcDir)) {
|
|
574
|
+
mkdirSync(pythonAppSrcDir, { recursive: true });
|
|
575
|
+
}
|
|
576
|
+
const dotsTtsFolder = join(pythonAppSrcDir, "dots-tts-mlx");
|
|
577
|
+
if (!existsSync(dotsTtsFolder)) {
|
|
578
|
+
const cloneCMD = await runCommand("git", ["clone", "https://github.com/sb1992/dots-tts-mlx.git", "dots-tts-mlx"], { cwd: pythonAppSrcDir });
|
|
579
|
+
if (!cloneCMD.success) {
|
|
580
|
+
console.error("Failed to clone dots-tts-mlx:", cloneCMD.error);
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
// Install the dots-tts CLI (into ~/.local/bin/dots-tts, which is where
|
|
585
|
+
// `getDotsTtsBin()` resolves it). `uv tool install` does not require a venv.
|
|
586
|
+
const uvPath = await getUvPath();
|
|
587
|
+
const installResult = await runCommand(uvPath, ["tool", "install", "--editable", "."], { cwd: dotsTtsFolder });
|
|
588
|
+
if (!installResult.success) {
|
|
589
|
+
console.error("Failed to install dots-tts:", installResult.error || installResult.output);
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
// Install mlx-whisper (MLX-powered speech-to-text for transcription) as a
|
|
593
|
+
// `uv tool` so its `mlx_whisper` CLI lands in ~/.local/bin, mirroring dots-tts.
|
|
594
|
+
const whisperResult = await runCommand(uvPath, [
|
|
595
|
+
"tool",
|
|
596
|
+
"install",
|
|
597
|
+
"mlx-whisper",
|
|
598
|
+
]);
|
|
599
|
+
if (!whisperResult.success) {
|
|
600
|
+
console.error("Failed to install mlx-whisper:", whisperResult.error || whisperResult.output);
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
}
|
|
571
604
|
// {
|
|
572
605
|
// const pythonAppSrcDir = join(APP_DATA_DIR, "python-src");
|
|
573
606
|
// if (!existsSync(pythonAppSrcDir)) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Application } from "express";
|
|
2
|
-
export type QueueTaskType = "generate" | "render" | "render-assets" | "render-videos" | "render-scene-images" | "render-asset" | "render-scene-image" | "render-video" | "regenerate-asset" | "regenerate-video" | "regenerate-scene-image" | "fast-image-edit";
|
|
2
|
+
export type QueueTaskType = "generate" | "render" | "render-assets" | "render-videos" | "render-scene-images" | "render-asset" | "render-scene-image" | "render-video" | "regenerate-asset" | "regenerate-video" | "regenerate-scene-image" | "fast-image-edit" | "image-to-video" | "upscale" | "voice-clone" | "audio-to-video" | "advanced-voice-clone";
|
|
3
3
|
export type QueueTaskStatus = "pending" | "running" | "completed" | "failed" | "cancelled" | "paused";
|
|
4
4
|
export interface QueueTask {
|
|
5
5
|
id: string;
|
|
@@ -2,7 +2,7 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync, readdirSync, statS
|
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
|
-
import { generateAssetImage, generateSceneImage, generateSceneVideo, generateFastImageEditImage, cancelActiveRender, } from "./render-media.js";
|
|
5
|
+
import { generateAssetImage, generateSceneImage, generateSceneVideo, generateFastImageEditImage, generateImageToVideo, generateUpscale, generateVoiceClone, generateAudioToVideo, generateAdvancedVoiceClone, cancelActiveRender, } from "./render-media.js";
|
|
6
6
|
import { generateMovieStudioBible } from "./agent/agent-backend.js";
|
|
7
7
|
import { movieStudioStateFile } from "./agent/workspace.js";
|
|
8
8
|
const APP_DATA_DIR = join(homedir(), "media-studio");
|
|
@@ -319,7 +319,7 @@ async function runRenderSceneImages(ctx, scenes) {
|
|
|
319
319
|
entry = { slug, ...existing };
|
|
320
320
|
}
|
|
321
321
|
else {
|
|
322
|
-
const r = await generateSceneImage(ctx.projectId, sc, ctx.log);
|
|
322
|
+
const r = await generateSceneImage(ctx.projectId, sc, undefined, ctx.log);
|
|
323
323
|
throwIfAborted(ctx.signal);
|
|
324
324
|
if ("error" in r)
|
|
325
325
|
throw new Error(r.error);
|
|
@@ -413,9 +413,76 @@ const handlers = {
|
|
|
413
413
|
},
|
|
414
414
|
// Generate a composite image via fast-image-edit (FLUX.2 Klein).
|
|
415
415
|
"fast-image-edit": async (ctx) => {
|
|
416
|
-
const { prompt, images } = ctx.task.payload || {};
|
|
416
|
+
const { prompt, images, steps, upscale } = ctx.task.payload || {};
|
|
417
417
|
ctx.log("Generating composite image (FLUX.2 Klein)…\n");
|
|
418
|
-
const r = await generateFastImageEditImage(ctx.projectId, String(prompt || ""), Array.isArray(images) ? images : [], ctx.log);
|
|
418
|
+
const r = await generateFastImageEditImage(ctx.projectId, String(prompt || ""), Array.isArray(images) ? images : [], Number(steps), String(upscale || "none"), ctx.log);
|
|
419
|
+
if ("error" in r)
|
|
420
|
+
throw new Error(r.error);
|
|
421
|
+
return r;
|
|
422
|
+
},
|
|
423
|
+
// Generate a video from a project image via LTX-2.3 (Scene Video Generation).
|
|
424
|
+
"image-to-video": async (ctx, getUvPath) => {
|
|
425
|
+
const { prompt, imagePath, width, height, frames, frameRate, mode, stage1Steps, stage2Steps, } = ctx.task.payload || {};
|
|
426
|
+
ctx.log("Generating scene video (LTX-2.3)…\n");
|
|
427
|
+
const r = await generateImageToVideo(await getUvPath(), ctx.projectId, {
|
|
428
|
+
prompt: String(prompt || ""),
|
|
429
|
+
imagePath: String(imagePath || ""),
|
|
430
|
+
width,
|
|
431
|
+
height,
|
|
432
|
+
frames,
|
|
433
|
+
frameRate,
|
|
434
|
+
mode,
|
|
435
|
+
stage1Steps,
|
|
436
|
+
stage2Steps,
|
|
437
|
+
}, ctx.log);
|
|
438
|
+
if ("error" in r)
|
|
439
|
+
throw new Error(r.error);
|
|
440
|
+
return r;
|
|
441
|
+
},
|
|
442
|
+
// Upscale/refine a project image via mlxgen (SeedVR2).
|
|
443
|
+
"upscale": async (ctx) => {
|
|
444
|
+
const { imagePath, resolution } = ctx.task.payload || {};
|
|
445
|
+
ctx.log(`Upscaling image (${resolution || "1x"})…\n`);
|
|
446
|
+
const r = await generateUpscale(ctx.projectId, String(imagePath || ""), String(resolution || "1x"), ctx.log);
|
|
447
|
+
if ("error" in r)
|
|
448
|
+
throw new Error(r.error);
|
|
449
|
+
return r;
|
|
450
|
+
},
|
|
451
|
+
// Clone a reference voice and speak the transcript.
|
|
452
|
+
"voice-clone": async (ctx, getUvPath) => {
|
|
453
|
+
const { text, refAudioPath, quality } = ctx.task.payload || {};
|
|
454
|
+
ctx.log("Cloning voice…\n");
|
|
455
|
+
const r = await generateVoiceClone(await getUvPath(), ctx.projectId, String(text || ""), String(refAudioPath || ""), String(quality || "high"), ctx.log);
|
|
456
|
+
if ("error" in r)
|
|
457
|
+
throw new Error(r.error);
|
|
458
|
+
return r;
|
|
459
|
+
},
|
|
460
|
+
// Clone a reference voice via dots-tts.
|
|
461
|
+
"advanced-voice-clone": async (ctx) => {
|
|
462
|
+
const { text, refAudioPath, language, outPrefix, model } = ctx.task.payload || {};
|
|
463
|
+
ctx.log("Cloning voice (dots-tts)…\n");
|
|
464
|
+
const r = await generateAdvancedVoiceClone(ctx.projectId, {
|
|
465
|
+
text: String(text || ""),
|
|
466
|
+
refAudioPath: String(refAudioPath || ""),
|
|
467
|
+
language: String(language || "YUE"),
|
|
468
|
+
outPrefix: String(outPrefix || ""),
|
|
469
|
+
model: String(model || ""),
|
|
470
|
+
}, ctx.log);
|
|
471
|
+
if ("error" in r)
|
|
472
|
+
throw new Error(r.error);
|
|
473
|
+
return r;
|
|
474
|
+
},
|
|
475
|
+
// Generate a video from an image + audio via ltx-2-mlx a2v.
|
|
476
|
+
"audio-to-video": async (ctx, getUvPath) => {
|
|
477
|
+
const { imagePath, audioPath, prompt, stage1Steps, frames } = ctx.task.payload || {};
|
|
478
|
+
ctx.log("Generating audio-to-video…\n");
|
|
479
|
+
const r = await generateAudioToVideo(await getUvPath(), ctx.projectId, {
|
|
480
|
+
imagePath: String(imagePath || ""),
|
|
481
|
+
audioPath: String(audioPath || ""),
|
|
482
|
+
prompt: String(prompt || ""),
|
|
483
|
+
stage1Steps: Number(stage1Steps),
|
|
484
|
+
frames: Number(frames),
|
|
485
|
+
}, ctx.log);
|
|
419
486
|
if ("error" in r)
|
|
420
487
|
throw new Error(r.error);
|
|
421
488
|
return r;
|
|
@@ -463,7 +530,7 @@ const handlers = {
|
|
|
463
530
|
},
|
|
464
531
|
// Generate a single scene image, skipping if it already exists.
|
|
465
532
|
"render-scene-image": async (ctx) => {
|
|
466
|
-
const { scene } = ctx.task.payload || {};
|
|
533
|
+
const { scene, steps } = ctx.task.payload || {};
|
|
467
534
|
if (!scene || typeof scene !== "object")
|
|
468
535
|
throw new Error("scene is required");
|
|
469
536
|
const s = slugify(scene?.slug);
|
|
@@ -474,7 +541,7 @@ const handlers = {
|
|
|
474
541
|
ctx.log(`Already generated: scene-${s}.png — skipping\n`);
|
|
475
542
|
return { slug: s, ...existing };
|
|
476
543
|
}
|
|
477
|
-
const r = await generateSceneImage(ctx.projectId, scene, ctx.log);
|
|
544
|
+
const r = await generateSceneImage(ctx.projectId, scene, Number(steps), ctx.log);
|
|
478
545
|
if ("error" in r)
|
|
479
546
|
throw new Error(r.error);
|
|
480
547
|
return {
|
|
@@ -542,10 +609,10 @@ const handlers = {
|
|
|
542
609
|
};
|
|
543
610
|
},
|
|
544
611
|
"regenerate-scene-image": async (ctx) => {
|
|
545
|
-
const { scene } = ctx.task.payload || {};
|
|
612
|
+
const { scene, steps } = ctx.task.payload || {};
|
|
546
613
|
if (!scene || typeof scene !== "object")
|
|
547
614
|
throw new Error("scene is required");
|
|
548
|
-
const r = await generateSceneImage(ctx.projectId, scene, ctx.log);
|
|
615
|
+
const r = await generateSceneImage(ctx.projectId, scene, Number(steps), ctx.log);
|
|
549
616
|
if ("error" in r)
|
|
550
617
|
throw new Error(r.error);
|
|
551
618
|
return {
|
|
@@ -32,11 +32,88 @@ export declare function generateSceneVideo(uvPath: string, projectId: string, sc
|
|
|
32
32
|
} | {
|
|
33
33
|
error: string;
|
|
34
34
|
}>;
|
|
35
|
+
/**
|
|
36
|
+
* Generate a video from a project image via LTX-2.3. `imagePath` must be a bare
|
|
37
|
+
* filename previously uploaded/generated for this project. Used by the queue
|
|
38
|
+
* worker for the "Scene Video Generation" tab.
|
|
39
|
+
*/
|
|
40
|
+
export declare function generateImageToVideo(uvPath: string, projectId: string, params: {
|
|
41
|
+
prompt: string;
|
|
42
|
+
imagePath: string;
|
|
43
|
+
width?: number;
|
|
44
|
+
height?: number;
|
|
45
|
+
frames?: number;
|
|
46
|
+
frameRate?: number;
|
|
47
|
+
mode?: string;
|
|
48
|
+
model?: string;
|
|
49
|
+
stage1Steps?: number;
|
|
50
|
+
stage2Steps?: number;
|
|
51
|
+
}, onLog?: (text: string) => void): Promise<{
|
|
52
|
+
filename: string;
|
|
53
|
+
url: string;
|
|
54
|
+
} | {
|
|
55
|
+
error: string;
|
|
56
|
+
}>;
|
|
35
57
|
/**
|
|
36
58
|
* Generate a single scene image via fast-image-edit (FLUX.2 Klein), using the
|
|
37
59
|
* already-generated character/place images referenced by the scene's slugs.
|
|
38
60
|
*/
|
|
39
|
-
export declare function generateSceneImage(projectId: string, scene: any, onLog?: (text: string) => void): Promise<{
|
|
61
|
+
export declare function generateSceneImage(projectId: string, scene: any, steps?: number, onLog?: (text: string) => void): Promise<{
|
|
62
|
+
filename: string;
|
|
63
|
+
url: string;
|
|
64
|
+
} | {
|
|
65
|
+
error: string;
|
|
66
|
+
}>;
|
|
67
|
+
/**
|
|
68
|
+
* Upscale/refine a project image via mlxgen (SeedVR2). `imagePath` must be a
|
|
69
|
+
* bare filename previously uploaded/generated for this project. `resolution` is
|
|
70
|
+
* either "1x" (refine at native resolution) or "2048" (upscale to 2048px).
|
|
71
|
+
*/
|
|
72
|
+
export declare function generateUpscale(projectId: string, imagePath: string, resolution: string, onLog?: (text: string) => void): Promise<{
|
|
73
|
+
filename: string;
|
|
74
|
+
url: string;
|
|
75
|
+
} | {
|
|
76
|
+
error: string;
|
|
77
|
+
}>;
|
|
78
|
+
/**
|
|
79
|
+
* Clone a reference voice and speak `text` via mlx_audio.tts.generate. `refAudioPath`
|
|
80
|
+
* must be a bare filename previously uploaded to this project. `quality` is "low"
|
|
81
|
+
* or "high" (maps to a TTS model). Output is saved under <output>/voices/.
|
|
82
|
+
*/
|
|
83
|
+
export declare function generateVoiceClone(uvPath: string, projectId: string, text: string, refAudioPath: string, quality: string, onLog?: (text: string) => void): Promise<{
|
|
84
|
+
filename: string;
|
|
85
|
+
url: string;
|
|
86
|
+
} | {
|
|
87
|
+
error: string;
|
|
88
|
+
}>;
|
|
89
|
+
/**
|
|
90
|
+
* Generate a video from an image + audio via ltx-2-mlx `a2v` (audio-to-video).
|
|
91
|
+
* `imagePath` and `audioPath` must be bare filenames previously uploaded/generated
|
|
92
|
+
* for this project. `stage1Steps` maps to `--stage1-steps` (15 default, 30 HD).
|
|
93
|
+
*/
|
|
94
|
+
export declare function generateAudioToVideo(uvPath: string, projectId: string, params: {
|
|
95
|
+
imagePath: string;
|
|
96
|
+
audioPath: string;
|
|
97
|
+
prompt: string;
|
|
98
|
+
stage1Steps: number;
|
|
99
|
+
frames: number;
|
|
100
|
+
}, onLog?: (text: string) => void): Promise<{
|
|
101
|
+
filename: string;
|
|
102
|
+
url: string;
|
|
103
|
+
} | {
|
|
104
|
+
error: string;
|
|
105
|
+
}>;
|
|
106
|
+
/**
|
|
107
|
+
* Clone a reference voice via dots-tts. Output is written to
|
|
108
|
+
* <output>/<projectId>/dots-tts/ as `<prefix>_000.wav`.
|
|
109
|
+
*/
|
|
110
|
+
export declare function generateAdvancedVoiceClone(projectId: string, params: {
|
|
111
|
+
text: string;
|
|
112
|
+
refAudioPath: string;
|
|
113
|
+
language: string;
|
|
114
|
+
outPrefix: string;
|
|
115
|
+
model: string;
|
|
116
|
+
}, onLog?: (text: string) => void): Promise<{
|
|
40
117
|
filename: string;
|
|
41
118
|
url: string;
|
|
42
119
|
} | {
|
|
@@ -47,7 +124,7 @@ export declare function generateSceneImage(projectId: string, scene: any, onLog?
|
|
|
47
124
|
* base64 data URLs that are decoded into temp files and passed to the model as
|
|
48
125
|
* separate `--image` inputs. Used by the generation queue worker.
|
|
49
126
|
*/
|
|
50
|
-
export declare function generateFastImageEditImage(projectId: string, prompt: string, images: string[], onLog?: (text: string) => void): Promise<{
|
|
127
|
+
export declare function generateFastImageEditImage(projectId: string, prompt: string, images: string[], steps?: number, upscaleResolution?: string, onLog?: (text: string) => void): Promise<{
|
|
51
128
|
filename: string;
|
|
52
129
|
url: string;
|
|
53
130
|
} | {
|