@effectnode/media 0.5.0 → 0.6.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/agent-backend.d.ts +1 -1
- package/dist/backend/movie-backend/agent/agent-backend.js +169 -83
- package/dist/backend/movie-backend/agent/tools/index.js +0 -2
- package/dist/backend/movie-backend/core.js +1 -1
- package/dist/backend/movie-backend/generation-queue.js +8 -1
- package/dist/backend/movie-backend/render-media.js +28 -254
- package/frontend/index.html +3 -5
- package/frontend/src/movie-app/SetupPage.tsx +1 -1
- package/frontend/src/movie-app/components/EditorTabs/MovieStudioTab.tsx +127 -23
- package/frontend/src/movie-app/components/EditorTabs/SetupAiModelTab.tsx +28 -23
- package/frontend/src/movie-app/components/ProjectEditorPage.tsx +0 -33
- package/frontend/src/movie-app/components/ProjectManager.tsx +1 -1
- package/frontend/src/movie-app/index.css +46 -20
- package/frontend/src/movie-app/index.html +1 -1
- package/frontend/src/movie-app/stores/aiModelStore.ts +16 -7
- package/frontend/src/movie-app/stores/generationStore.ts +0 -1
- package/frontend/src/movie-app/stores/movieStudioStore.ts +27 -3
- package/package.json +2 -2
- package/frontend/src/movie-app/components/EditorTabs/CharactersTab.tsx +0 -664
|
@@ -191,9 +191,14 @@ const DOWNLOAD_MODELS: {
|
|
|
191
191
|
desc: "Character, place & scene images",
|
|
192
192
|
},
|
|
193
193
|
{
|
|
194
|
-
id: "
|
|
195
|
-
name: "
|
|
196
|
-
desc: "
|
|
194
|
+
id: "ltx",
|
|
195
|
+
name: "dgrauet/ltx-2.3-mlx-q8",
|
|
196
|
+
desc: "Video generation",
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
id: "tts",
|
|
200
|
+
name: "Qwen/Qwen3-TTS-12Hz-1.7B-Base",
|
|
201
|
+
desc: "Voice-over & speech",
|
|
197
202
|
},
|
|
198
203
|
];
|
|
199
204
|
|
|
@@ -228,7 +233,7 @@ export default function SetupAiModelTab() {
|
|
|
228
233
|
<section>
|
|
229
234
|
<h3 className="mb-2 text-sm font-semibold text-ink-900">Engines</h3>
|
|
230
235
|
<div className="space-y-2">
|
|
231
|
-
<ModelRow name="mlx-gen" desc="Image generation engine (z-image · flux
|
|
236
|
+
<ModelRow name="mlx-gen" desc="Image generation engine (z-image · flux)">
|
|
232
237
|
<StatusBadge
|
|
233
238
|
value={store.mlxgenInstalled}
|
|
234
239
|
okText="Installed"
|
|
@@ -259,6 +264,22 @@ export default function SetupAiModelTab() {
|
|
|
259
264
|
{store.downloading === "mlx-vlm" ? "Installing…" : "Install"}
|
|
260
265
|
</button>
|
|
261
266
|
</ModelRow>
|
|
267
|
+
|
|
268
|
+
<ModelRow name="huggingface-cli" desc="HF CLI for downloading models">
|
|
269
|
+
<StatusBadge
|
|
270
|
+
value={store.hfInstalled}
|
|
271
|
+
okText="Installed"
|
|
272
|
+
missingText="Not installed"
|
|
273
|
+
/>
|
|
274
|
+
<button
|
|
275
|
+
onClick={() => store.installTool("hf-cli")}
|
|
276
|
+
disabled={busy}
|
|
277
|
+
className="flex items-center gap-1.5 whitespace-nowrap rounded-xl border border-tiffany-500 px-3 py-1.5 text-xs font-medium text-tiffany-700 transition-colors hover:bg-tiffany-50 disabled:opacity-50"
|
|
278
|
+
>
|
|
279
|
+
{store.downloading === "hf-cli" ? SpinnerIcon : InstallIcon}
|
|
280
|
+
{store.downloading === "hf-cli" ? "Installing…" : "Install"}
|
|
281
|
+
</button>
|
|
282
|
+
</ModelRow>
|
|
262
283
|
</div>
|
|
263
284
|
</section>
|
|
264
285
|
|
|
@@ -286,7 +307,9 @@ export default function SetupAiModelTab() {
|
|
|
286
307
|
? store.zImageDownloaded
|
|
287
308
|
: m.id === "flux"
|
|
288
309
|
? store.fluxDownloaded
|
|
289
|
-
:
|
|
310
|
+
: m.id === "ltx"
|
|
311
|
+
? store.ltxDownloaded
|
|
312
|
+
: store.ttsDownloaded;
|
|
290
313
|
return (
|
|
291
314
|
<ModelRow key={m.id} name={m.name} desc={m.desc}>
|
|
292
315
|
<StatusBadge value={downloaded} />
|
|
@@ -301,24 +324,6 @@ export default function SetupAiModelTab() {
|
|
|
301
324
|
</ModelRow>
|
|
302
325
|
);
|
|
303
326
|
})}
|
|
304
|
-
|
|
305
|
-
<ModelRow
|
|
306
|
-
name="dgrauet/ltx-2.3-mlx-q8"
|
|
307
|
-
desc="Video generation"
|
|
308
|
-
>
|
|
309
|
-
<span className="text-xs italic text-ink-400">
|
|
310
|
-
Downloads on first render
|
|
311
|
-
</span>
|
|
312
|
-
</ModelRow>
|
|
313
|
-
|
|
314
|
-
<ModelRow
|
|
315
|
-
name="Qwen/Qwen3-TTS-12Hz-1.7B-Base"
|
|
316
|
-
desc="Voice-over & speech"
|
|
317
|
-
>
|
|
318
|
-
<span className="text-xs italic text-ink-400">
|
|
319
|
-
Downloads on first use
|
|
320
|
-
</span>
|
|
321
|
-
</ModelRow>
|
|
322
327
|
</div>
|
|
323
328
|
</section>
|
|
324
329
|
|
|
@@ -8,7 +8,6 @@ import MovieStudioTab from "./EditorTabs/MovieStudioTab";
|
|
|
8
8
|
import GenerateVideoTab from "./EditorTabs/GenerateVideoTab";
|
|
9
9
|
import AgentTab from "./EditorTabs/AgentTab";
|
|
10
10
|
import StoryWriterTab from "./EditorTabs/StoryWriterTab";
|
|
11
|
-
import CharactersTab from "./EditorTabs/CharactersTab";
|
|
12
11
|
import ExtractImageTab from "./EditorTabs/ExtractImageTab";
|
|
13
12
|
import SceneVisualTab from "./EditorTabs/SceneVisualTab";
|
|
14
13
|
import TextToImageTab from "./EditorTabs/TextToImageTab";
|
|
@@ -173,22 +172,6 @@ export default function ProjectEditorPage() {
|
|
|
173
172
|
</svg>
|
|
174
173
|
);
|
|
175
174
|
|
|
176
|
-
const CharacterIcon = (
|
|
177
|
-
<svg
|
|
178
|
-
width="18"
|
|
179
|
-
height="18"
|
|
180
|
-
viewBox="0 0 24 24"
|
|
181
|
-
fill="none"
|
|
182
|
-
stroke="currentColor"
|
|
183
|
-
strokeWidth="2"
|
|
184
|
-
strokeLinecap="round"
|
|
185
|
-
strokeLinejoin="round"
|
|
186
|
-
>
|
|
187
|
-
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
|
|
188
|
-
<circle cx="12" cy="7" r="4" />
|
|
189
|
-
</svg>
|
|
190
|
-
);
|
|
191
|
-
|
|
192
175
|
const ExtractIcon = (
|
|
193
176
|
<svg
|
|
194
177
|
width="18"
|
|
@@ -471,17 +454,6 @@ export default function ProjectEditorPage() {
|
|
|
471
454
|
{StoryWriterIcon}
|
|
472
455
|
Story Writer
|
|
473
456
|
</button>
|
|
474
|
-
<button
|
|
475
|
-
onClick={() => store.setActiveTab("characters")}
|
|
476
|
-
className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
|
|
477
|
-
store.activeTab === "characters"
|
|
478
|
-
? "bg-tiffany-500/10 text-tiffany-700 shadow-glow-sm"
|
|
479
|
-
: "text-ink-600 hover:bg-ink-100 hover:text-ink-800"
|
|
480
|
-
}`}
|
|
481
|
-
>
|
|
482
|
-
{CharacterIcon}
|
|
483
|
-
Characters
|
|
484
|
-
</button>
|
|
485
457
|
<button
|
|
486
458
|
onClick={() => store.setActiveTab("extract")}
|
|
487
459
|
className={`flex items-center gap-2 px-5 py-2.5 text-sm font-medium rounded-2xl transition-all ${
|
|
@@ -597,11 +569,6 @@ export default function ProjectEditorPage() {
|
|
|
597
569
|
<StoryWriterTab projectId={id!} />
|
|
598
570
|
)}
|
|
599
571
|
|
|
600
|
-
{/* ========== CHARACTERS PANEL ========== */}
|
|
601
|
-
{store.activeTab === "characters" && (
|
|
602
|
-
<CharactersTab projectId={id!} />
|
|
603
|
-
)}
|
|
604
|
-
|
|
605
572
|
{/* ========== EXTRACT IMAGE PANEL ========== */}
|
|
606
573
|
{store.activeTab === "extract" && <ExtractImageTab projectId={id!} />}
|
|
607
574
|
|
|
@@ -168,7 +168,7 @@ export default function ProjectManager() {
|
|
|
168
168
|
<header className="flex flex-col gap-7 md:flex-row md:items-end md:justify-between">
|
|
169
169
|
<div className="max-w-xl">
|
|
170
170
|
<p className="text-[11px] font-semibold uppercase tracking-[0.24em] text-tiffany-600">
|
|
171
|
-
|
|
171
|
+
EffectNode Media
|
|
172
172
|
</p>
|
|
173
173
|
<h1 className="mt-3 font-display text-4xl font-light leading-[1.06] text-ink-900 md:text-5xl">
|
|
174
174
|
Make the movies{" "}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
@import "tailwindcss";
|
|
2
2
|
|
|
3
3
|
/* ==========================================================================
|
|
4
|
-
|
|
4
|
+
EffectNode Media — design tokens
|
|
5
5
|
A dreamy, tiffany-blue film studio. Gradients read like light through
|
|
6
6
|
shallow water: tiffany teal → aqua → periwinkle → blush.
|
|
7
7
|
========================================================================== */
|
|
8
8
|
|
|
9
9
|
@theme {
|
|
10
10
|
/* ---- Type ---- */
|
|
11
|
-
--font-sans:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
--font-sans:
|
|
12
|
+
"Avenir Next", Avenir, "Helvetica Neue", -apple-system, BlinkMacSystemFont,
|
|
13
|
+
"Segoe UI", Roboto, sans-serif;
|
|
14
|
+
--font-display:
|
|
15
|
+
"Cormorant", "Iowan Old Style", "Baskerville", "Georgia", serif;
|
|
16
|
+
--font-mono:
|
|
17
|
+
ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas,
|
|
16
18
|
"Liberation Mono", monospace;
|
|
17
19
|
|
|
18
20
|
/* ---- Tiffany (the brand) ---- */
|
|
@@ -48,13 +50,14 @@
|
|
|
48
50
|
--color-dream-mint: #c8f1e6;
|
|
49
51
|
|
|
50
52
|
/* ---- Shadows ---- */
|
|
51
|
-
--shadow-glow:
|
|
53
|
+
--shadow-glow:
|
|
54
|
+
0 8px 30px -6px rgba(10, 186, 181, 0.55),
|
|
52
55
|
0 2px 10px -2px rgba(10, 186, 181, 0.4);
|
|
53
56
|
--shadow-glow-sm: 0 0 14px -2px rgba(10, 186, 181, 0.45);
|
|
54
|
-
--shadow-card:
|
|
55
|
-
0 10px 30px -12px rgba(13, 25, 28, 0.16);
|
|
56
|
-
--shadow-card-hover:
|
|
57
|
-
0 18px 44px -14px rgba(10, 186, 181, 0.35);
|
|
57
|
+
--shadow-card:
|
|
58
|
+
0 1px 2px rgba(13, 25, 28, 0.04), 0 10px 30px -12px rgba(13, 25, 28, 0.16);
|
|
59
|
+
--shadow-card-hover:
|
|
60
|
+
0 2px 4px rgba(13, 25, 28, 0.05), 0 18px 44px -14px rgba(10, 186, 181, 0.35);
|
|
58
61
|
--shadow-modal: 0 24px 80px -16px rgba(13, 25, 28, 0.35);
|
|
59
62
|
}
|
|
60
63
|
|
|
@@ -65,16 +68,28 @@
|
|
|
65
68
|
body {
|
|
66
69
|
margin: 0;
|
|
67
70
|
background-color: #f3f7f7;
|
|
68
|
-
background-image:
|
|
71
|
+
background-image:
|
|
72
|
+
radial-gradient(
|
|
69
73
|
120% 90% at 85% -10%,
|
|
70
74
|
rgba(183, 225, 251, 0.5),
|
|
71
75
|
transparent 60%
|
|
72
76
|
),
|
|
73
|
-
radial-gradient(
|
|
77
|
+
radial-gradient(
|
|
78
|
+
100% 80% at 0% 0%,
|
|
79
|
+
rgba(255, 214, 234, 0.4),
|
|
80
|
+
transparent 55%
|
|
81
|
+
);
|
|
74
82
|
background-attachment: fixed;
|
|
75
83
|
color: #283a40;
|
|
76
|
-
font-family:
|
|
77
|
-
|
|
84
|
+
font-family:
|
|
85
|
+
"Avenir Next",
|
|
86
|
+
Avenir,
|
|
87
|
+
"Helvetica Neue",
|
|
88
|
+
-apple-system,
|
|
89
|
+
BlinkMacSystemFont,
|
|
90
|
+
"Segoe UI",
|
|
91
|
+
Roboto,
|
|
92
|
+
sans-serif;
|
|
78
93
|
-webkit-font-smoothing: antialiased;
|
|
79
94
|
-moz-osx-font-smoothing: grayscale;
|
|
80
95
|
}
|
|
@@ -82,7 +97,8 @@ body {
|
|
|
82
97
|
code,
|
|
83
98
|
pre,
|
|
84
99
|
.font-mono {
|
|
85
|
-
font-family:
|
|
100
|
+
font-family:
|
|
101
|
+
ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas,
|
|
86
102
|
"Liberation Mono", monospace;
|
|
87
103
|
}
|
|
88
104
|
|
|
@@ -126,7 +142,8 @@ pre,
|
|
|
126
142
|
z-index: 0;
|
|
127
143
|
overflow: hidden;
|
|
128
144
|
pointer-events: none;
|
|
129
|
-
background:
|
|
145
|
+
background:
|
|
146
|
+
radial-gradient(
|
|
130
147
|
120% 90% at 85% -10%,
|
|
131
148
|
rgba(183, 225, 251, 0.55),
|
|
132
149
|
transparent 60%
|
|
@@ -251,7 +268,12 @@ pre,
|
|
|
251
268
|
}
|
|
252
269
|
|
|
253
270
|
.thumb {
|
|
254
|
-
background-image: linear-gradient(
|
|
271
|
+
background-image: linear-gradient(
|
|
272
|
+
135deg,
|
|
273
|
+
#b0e8e3 0%,
|
|
274
|
+
#81d8d0 50%,
|
|
275
|
+
#c3b4f4 100%
|
|
276
|
+
);
|
|
255
277
|
}
|
|
256
278
|
|
|
257
279
|
.btn-primary {
|
|
@@ -263,12 +285,16 @@ pre,
|
|
|
263
285
|
);
|
|
264
286
|
color: #ffffff;
|
|
265
287
|
box-shadow: var(--shadow-glow);
|
|
266
|
-
transition:
|
|
288
|
+
transition:
|
|
289
|
+
filter 150ms ease,
|
|
290
|
+
box-shadow 150ms ease,
|
|
291
|
+
transform 150ms ease;
|
|
267
292
|
}
|
|
268
293
|
|
|
269
294
|
.btn-primary:hover {
|
|
270
295
|
filter: brightness(1.05);
|
|
271
|
-
box-shadow:
|
|
296
|
+
box-shadow:
|
|
297
|
+
0 10px 34px -6px rgba(10, 186, 181, 0.6),
|
|
272
298
|
0 4px 12px -4px rgba(10, 186, 181, 0.45);
|
|
273
299
|
}
|
|
274
300
|
|
|
@@ -3,19 +3,21 @@ import { create } from "zustand";
|
|
|
3
3
|
const API_BASE = `http://localhost:${(window as any).PORT}`;
|
|
4
4
|
|
|
5
5
|
/** Models that have a dedicated download endpoint in the backend. */
|
|
6
|
-
export type AiModelId = "z-image" | "flux" | "
|
|
6
|
+
export type AiModelId = "z-image" | "flux" | "ltx" | "tts";
|
|
7
7
|
|
|
8
8
|
/** Tools whose "install" step must run before their models can be downloaded. */
|
|
9
|
-
export type AiToolId = "mlxgen" | "mlx-vlm";
|
|
9
|
+
export type AiToolId = "mlxgen" | "mlx-vlm" | "hf-cli";
|
|
10
10
|
|
|
11
11
|
interface AiModelStore {
|
|
12
12
|
// Tool installation status
|
|
13
13
|
mlxgenInstalled: boolean | null;
|
|
14
14
|
mlxVlmInstalled: boolean | null;
|
|
15
|
+
hfInstalled: boolean | null;
|
|
15
16
|
// Model download status
|
|
16
17
|
zImageDownloaded: boolean | null;
|
|
17
18
|
fluxDownloaded: boolean | null;
|
|
18
|
-
|
|
19
|
+
ltxDownloaded: boolean | null;
|
|
20
|
+
ttsDownloaded: boolean | null;
|
|
19
21
|
// In-flight install/download (a model id or tool id)
|
|
20
22
|
downloading: string | null;
|
|
21
23
|
logs: string[];
|
|
@@ -65,40 +67,47 @@ async function readSSE(
|
|
|
65
67
|
const DOWNLOAD_ENDPOINTS: Record<AiModelId, string> = {
|
|
66
68
|
"z-image": "/api/mlxgen/download-z-model",
|
|
67
69
|
flux: "/api/mlxgen/download-flux-model",
|
|
68
|
-
|
|
70
|
+
ltx: "/api/hf/download-ltx",
|
|
71
|
+
tts: "/api/hf/download-tts",
|
|
69
72
|
};
|
|
70
73
|
|
|
71
74
|
const TOOL_ENDPOINTS: Record<AiToolId, string> = {
|
|
72
75
|
mlxgen: "/api/mlxgen/install",
|
|
73
76
|
"mlx-vlm": "/api/agent/install",
|
|
77
|
+
"hf-cli": "/api/hf/install",
|
|
74
78
|
};
|
|
75
79
|
|
|
76
80
|
export const useAiModelStore = create<AiModelStore>((set, get) => ({
|
|
77
81
|
mlxgenInstalled: null,
|
|
78
82
|
mlxVlmInstalled: null,
|
|
83
|
+
hfInstalled: null,
|
|
79
84
|
zImageDownloaded: null,
|
|
80
85
|
fluxDownloaded: null,
|
|
81
|
-
|
|
86
|
+
ltxDownloaded: null,
|
|
87
|
+
ttsDownloaded: null,
|
|
82
88
|
downloading: null,
|
|
83
89
|
logs: [],
|
|
84
90
|
error: null,
|
|
85
91
|
|
|
86
92
|
checkStatus: async () => {
|
|
87
93
|
try {
|
|
88
|
-
const [mlxgen, agent] = await Promise.all([
|
|
94
|
+
const [mlxgen, agent, hf] = await Promise.all([
|
|
89
95
|
fetch(`${API_BASE}/api/mlxgen/status`).then((r) =>
|
|
90
96
|
r.ok ? r.json() : null,
|
|
91
97
|
),
|
|
92
98
|
fetch(`${API_BASE}/api/agent/status`).then((r) =>
|
|
93
99
|
r.ok ? r.json() : null,
|
|
94
100
|
),
|
|
101
|
+
fetch(`${API_BASE}/api/hf/status`).then((r) => (r.ok ? r.json() : null)),
|
|
95
102
|
]);
|
|
96
103
|
set({
|
|
97
104
|
mlxgenInstalled: mlxgen ? Boolean(mlxgen.installed) : null,
|
|
98
|
-
qwenDownloaded: mlxgen ? Boolean(mlxgen.modelDownloaded) : null,
|
|
99
105
|
zImageDownloaded: mlxgen ? Boolean(mlxgen.zModelDownloaded) : null,
|
|
100
106
|
fluxDownloaded: mlxgen ? Boolean(mlxgen.fluxModelDownloaded) : null,
|
|
101
107
|
mlxVlmInstalled: agent ? Boolean(agent.installed) : null,
|
|
108
|
+
hfInstalled: hf ? Boolean(hf.installed) : null,
|
|
109
|
+
ltxDownloaded: hf ? Boolean(hf.ltxDownloaded) : null,
|
|
110
|
+
ttsDownloaded: hf ? Boolean(hf.ttsDownloaded) : null,
|
|
102
111
|
});
|
|
103
112
|
} catch {
|
|
104
113
|
// Leave status unknown (null) if the checks fail.
|
|
@@ -143,6 +143,8 @@ interface MovieStudioStore {
|
|
|
143
143
|
projectId: string | null;
|
|
144
144
|
hydrated: boolean;
|
|
145
145
|
generating: boolean;
|
|
146
|
+
generateStatus: string | null;
|
|
147
|
+
generateProgress: { current: number; total: number } | null;
|
|
146
148
|
result: MovieStudioResult | null;
|
|
147
149
|
error: string | null;
|
|
148
150
|
rendering: boolean;
|
|
@@ -197,6 +199,8 @@ export const useMovieStudioStore = create<MovieStudioStore>((set, get) => ({
|
|
|
197
199
|
projectId: null,
|
|
198
200
|
hydrated: false,
|
|
199
201
|
generating: false,
|
|
202
|
+
generateStatus: null,
|
|
203
|
+
generateProgress: null,
|
|
200
204
|
result: null,
|
|
201
205
|
error: null,
|
|
202
206
|
rendering: false,
|
|
@@ -250,6 +254,9 @@ export const useMovieStudioStore = create<MovieStudioStore>((set, get) => ({
|
|
|
250
254
|
assets: Array.isArray(stored.assets) ? stored.assets : [],
|
|
251
255
|
videos: Array.isArray(stored.videos) ? stored.videos : [],
|
|
252
256
|
sceneImages: Array.isArray(stored.sceneImages) ? stored.sceneImages : [],
|
|
257
|
+
renderedScenes: Array.isArray(stored.renderedScenes)
|
|
258
|
+
? stored.renderedScenes
|
|
259
|
+
: [],
|
|
253
260
|
});
|
|
254
261
|
} catch {
|
|
255
262
|
// Ignore — keep in-memory defaults.
|
|
@@ -465,14 +472,28 @@ export const useMovieStudioStore = create<MovieStudioStore>((set, get) => ({
|
|
|
465
472
|
if (task.status === "completed" && task.result) {
|
|
466
473
|
if (!appliedCompleted.has(task.id)) {
|
|
467
474
|
appliedCompleted.add(task.id);
|
|
468
|
-
set({
|
|
475
|
+
set({
|
|
476
|
+
result: task.result,
|
|
477
|
+
generating: false,
|
|
478
|
+
generateStatus: null,
|
|
479
|
+
generateProgress: null,
|
|
480
|
+
});
|
|
469
481
|
persistMovieStudioState();
|
|
470
482
|
playDing3x();
|
|
471
483
|
}
|
|
472
484
|
} else if (err) {
|
|
473
|
-
set({
|
|
485
|
+
set({
|
|
486
|
+
generating: false,
|
|
487
|
+
error: err,
|
|
488
|
+
generateStatus: null,
|
|
489
|
+
generateProgress: null,
|
|
490
|
+
});
|
|
474
491
|
} else {
|
|
475
|
-
set({
|
|
492
|
+
set({
|
|
493
|
+
generating: isActive,
|
|
494
|
+
generateStatus: task.status === "running" ? task.statusText : null,
|
|
495
|
+
generateProgress: task.progress,
|
|
496
|
+
});
|
|
476
497
|
}
|
|
477
498
|
break;
|
|
478
499
|
}
|
|
@@ -708,6 +729,8 @@ export const useMovieStudioStore = create<MovieStudioStore>((set, get) => ({
|
|
|
708
729
|
set({
|
|
709
730
|
idea: "",
|
|
710
731
|
generating: false,
|
|
732
|
+
generateStatus: null,
|
|
733
|
+
generateProgress: null,
|
|
711
734
|
result: null,
|
|
712
735
|
error: null,
|
|
713
736
|
rendering: false,
|
|
@@ -749,6 +772,7 @@ function persistMovieStudioState() {
|
|
|
749
772
|
assets: s.assets,
|
|
750
773
|
videos: s.videos,
|
|
751
774
|
sceneImages: s.sceneImages,
|
|
775
|
+
renderedScenes: s.renderedScenes,
|
|
752
776
|
}),
|
|
753
777
|
}).catch(() => {});
|
|
754
778
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effectnode/media",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Start a full-stack media app: Vite + React + TypeScript frontend, Express backend with REST + WebSocket API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"scripts": {
|
|
20
20
|
"deploy": "npm run build; npm version minor; npm publish --access public",
|
|
21
21
|
"build": "tsc && node -e \"const fs=require('node:fs');fs.cpSync('src/backend/movie-backend/agent/prompt','dist/backend/movie-backend/agent/prompt',{recursive:true})\"",
|
|
22
|
-
"dev": "concurrently -k -n backend,frontend -c blue,green \"bun run dev:backend\" \"bun run dev:frontend\"",
|
|
22
|
+
"dev": "concurrently -k -n backend,frontend -c blue,green \"bun run dev:backend\" \"bun run dev:frontend\"; open http://localhsot:5177",
|
|
23
23
|
"dev:backend": "nodemon",
|
|
24
24
|
"dev:frontend": "vite",
|
|
25
25
|
"start": "node bin/effectnode-media"
|