@effectnode/media 0.8.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.
Files changed (33) hide show
  1. package/dist/backend/movie-backend/agent/prompt/script.md +213 -0
  2. package/dist/backend/movie-backend/core.js +33 -0
  3. package/dist/backend/movie-backend/generation-queue.d.ts +1 -1
  4. package/dist/backend/movie-backend/generation-queue.js +82 -6
  5. package/dist/backend/movie-backend/render-media.d.ts +89 -1
  6. package/dist/backend/movie-backend/render-media.js +772 -476
  7. package/frontend/src/movie-app/components/EditorTabs/AdvancedVoiceCloneTab.tsx +366 -0
  8. package/frontend/src/movie-app/components/EditorTabs/AudioToVideoTab.tsx +406 -0
  9. package/frontend/src/movie-app/components/EditorTabs/FastImageEditTab.tsx +67 -51
  10. package/frontend/src/movie-app/components/EditorTabs/GenerateVideoTab.tsx +155 -1
  11. package/frontend/src/movie-app/components/EditorTabs/MovieStudioTab.tsx +33 -13
  12. package/frontend/src/movie-app/components/EditorTabs/SetupAiModelTab.tsx +26 -5
  13. package/frontend/src/movie-app/components/EditorTabs/UpscaleTab.tsx +342 -0
  14. package/frontend/src/movie-app/components/EditorTabs/VoiceCloneTab.tsx +365 -0
  15. package/frontend/src/movie-app/components/ProjectEditorPage.tsx +131 -126
  16. package/frontend/src/movie-app/stores/advancedVoiceCloneStore.ts +207 -0
  17. package/frontend/src/movie-app/stores/aiModelStore.ts +25 -2
  18. package/frontend/src/movie-app/stores/audioToVideoStore.ts +274 -0
  19. package/frontend/src/movie-app/stores/generationStore.ts +250 -345
  20. package/frontend/src/movie-app/stores/movieStudioStore.ts +10 -3
  21. package/frontend/src/movie-app/stores/projectStore.ts +7 -3
  22. package/frontend/src/movie-app/stores/queueStore.ts +48 -1
  23. package/frontend/src/movie-app/stores/upscaleStore.ts +118 -0
  24. package/frontend/src/movie-app/stores/voiceCloneStore.ts +227 -0
  25. package/package.json +1 -1
  26. package/frontend/src/movie-app/components/EditorTabs/BatchVoiceVideoTab.tsx +0 -913
  27. package/frontend/src/movie-app/components/EditorTabs/CharacterSheet.tsx +0 -234
  28. package/frontend/src/movie-app/components/EditorTabs/ExtendVideoTab.tsx +0 -305
  29. package/frontend/src/movie-app/components/EditorTabs/ExtractImageTab.tsx +0 -249
  30. package/frontend/src/movie-app/components/EditorTabs/SceneVisualTab.tsx +0 -267
  31. package/frontend/src/movie-app/lib/batchVoiceStorage.ts +0 -75
  32. package/frontend/src/movie-app/stores/batchVoiceStore.ts +0 -990
  33. package/frontend/src/movie-app/stores/sceneVisualStore.ts +0 -251
@@ -0,0 +1,213 @@
1
+ <!-- high quality -->
2
+
3
+ ```bash
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
+
13
+ uv run mlx_audio.tts.generate \
14
+ --model Qwen/Qwen3-TTS-12Hz-1.7B-Base \
15
+ --text "Ghost reporting. ... 你好嗎?" \
16
+ --ref_audio ./reference_voice.wav \
17
+ --play --output ./out --audio_format mp3 --stream --save --instruct "slow down"
18
+
19
+ ```
20
+
21
+ <!-- mlx-community/Kokoro-82M-bf16 -->
22
+
23
+ <!-- Low quality -->
24
+
25
+ ```bash
26
+
27
+ uv run mlx_audio.tts.generate \
28
+ --model Qwen/Qwen3-TTS-12Hz-0.6B-Base \
29
+ --text "Ghost reporting. 中秋節快樂! 明月幾時有?把酒問青天。不知天上宮闕,今夕是何年。我欲乘風歸去,又恐瓊樓玉宇,高處不勝寒。起舞弄清影,何似在人間!轉朱閣,低綺戶,照無眠。不應有恨,何事長向別時圓?人有悲歡離合,月有陰晴圓缺,此事古難全。但願人長久,千里共嬋娟。 中秋節快樂!" \
30
+ --ref_audio ./reference_voice.wav \
31
+ --play --output ./out --audio_format mp3 --stream --save --instruct "slow down"
32
+
33
+
34
+
35
+ uv run mlx_audio.tts.generate \
36
+ --model Qwen/Qwen3-TTS-12Hz-0.6B-Base \
37
+ --text "ghost reporting! Hi how are you?" \
38
+ --ref_audio ./reference_voice.wav \
39
+ --play --output ./out --audio_format mp3 --stream --save --instruct "slow down"
40
+
41
+
42
+ ```
43
+
44
+ # Image editing
45
+
46
+ ## 4B OK FOR COMMERCAIL USE APACHE LICENSE
47
+
48
+ ```bash
49
+
50
+ mlxgen download --model AbstractFramework/flux.2-klein-4b-8bit
51
+
52
+ mlxgen generate \
53
+ --image input.jpeg \
54
+ --prompt "The person and The ninja standing next to each other, in a studio, taking photo." \
55
+ --image person.png \
56
+ --output result.png \
57
+ --model AbstractFramework/flux.2-klein-4b-8bit \
58
+ --mlx-cache-limit-gb 20 \
59
+ --steps 5 --seed 42 --width 1024 --height 1024
60
+
61
+ ```
62
+
63
+ #
64
+
65
+ #
66
+
67
+ # Upscale to 2048
68
+
69
+ ```bash
70
+ ####
71
+
72
+ mlxgen download --model AbstractFramework/seedvr2-7b-8bit
73
+
74
+ mlxgen upscale \
75
+ --model AbstractFramework/seedvr2-7b-8bit \
76
+ --image-path input.png \
77
+ --resolution 2048 \
78
+ --seed 42 \
79
+ --mlx-cache-limit-gb 100 \
80
+ --output input_upscaled_2048.png
81
+
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
+
92
+ ```
93
+
94
+ # upscale video
95
+
96
+ ```bash
97
+
98
+ mlxgen download --model AbstractFramework/seedvr2-7b-8bit
99
+
100
+ mlxgen upscale \
101
+ --model AbstractFramework/seedvr2-7b-8bit \
102
+ --video-path input.mp4 \
103
+ --resolution 720 \
104
+ --temporal-chunk-size 29 \
105
+ --temporal-chunk-overlap 8 \
106
+ --mlx-cache-limit-gb 64 \
107
+ --force-unsafe-video-memory \
108
+ --metadata \
109
+ --output upscalde_video.mp4
110
+
111
+
112
+
113
+ mlxgen upscale \
114
+ --model AbstractFramework/seedvr2-7b-8bit \
115
+ --video-path input.mp4 \
116
+ --resolution 2x \
117
+ --mlx-cache-limit-gb 64 \
118
+ --force-unsafe-video-memory \
119
+ --output upscalde_video.mp4
120
+
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
+
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";
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, 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);
@@ -411,6 +411,82 @@ const handlers = {
411
411
  ctx.log("Production bible ready.\n");
412
412
  return result;
413
413
  },
414
+ // Generate a composite image via fast-image-edit (FLUX.2 Klein).
415
+ "fast-image-edit": async (ctx) => {
416
+ const { prompt, images, steps, upscale } = ctx.task.payload || {};
417
+ ctx.log("Generating composite image (FLUX.2 Klein)…\n");
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);
486
+ if ("error" in r)
487
+ throw new Error(r.error);
488
+ return r;
489
+ },
414
490
  "render-assets": async (ctx) => {
415
491
  const { characters, places } = ctx.task.payload || {};
416
492
  return runRenderAssets(ctx, characters, places);
@@ -454,7 +530,7 @@ const handlers = {
454
530
  },
455
531
  // Generate a single scene image, skipping if it already exists.
456
532
  "render-scene-image": async (ctx) => {
457
- const { scene } = ctx.task.payload || {};
533
+ const { scene, steps } = ctx.task.payload || {};
458
534
  if (!scene || typeof scene !== "object")
459
535
  throw new Error("scene is required");
460
536
  const s = slugify(scene?.slug);
@@ -465,7 +541,7 @@ const handlers = {
465
541
  ctx.log(`Already generated: scene-${s}.png — skipping\n`);
466
542
  return { slug: s, ...existing };
467
543
  }
468
- const r = await generateSceneImage(ctx.projectId, scene, ctx.log);
544
+ const r = await generateSceneImage(ctx.projectId, scene, Number(steps), ctx.log);
469
545
  if ("error" in r)
470
546
  throw new Error(r.error);
471
547
  return {
@@ -533,10 +609,10 @@ const handlers = {
533
609
  };
534
610
  },
535
611
  "regenerate-scene-image": async (ctx) => {
536
- const { scene } = ctx.task.payload || {};
612
+ const { scene, steps } = ctx.task.payload || {};
537
613
  if (!scene || typeof scene !== "object")
538
614
  throw new Error("scene is required");
539
- const r = await generateSceneImage(ctx.projectId, scene, ctx.log);
615
+ const r = await generateSceneImage(ctx.projectId, scene, Number(steps), ctx.log);
540
616
  if ("error" in r)
541
617
  throw new Error(r.error);
542
618
  return {
@@ -32,11 +32,99 @@ 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<{
117
+ filename: string;
118
+ url: string;
119
+ } | {
120
+ error: string;
121
+ }>;
122
+ /**
123
+ * Generate a composite image via fast-image-edit (FLUX.2 Klein). `images` are
124
+ * base64 data URLs that are decoded into temp files and passed to the model as
125
+ * separate `--image` inputs. Used by the generation queue worker.
126
+ */
127
+ export declare function generateFastImageEditImage(projectId: string, prompt: string, images: string[], steps?: number, upscaleResolution?: string, onLog?: (text: string) => void): Promise<{
40
128
  filename: string;
41
129
  url: string;
42
130
  } | {