Commit
•
ca5fb80
1
Parent(s):
5bf59c5
this is neat
Browse files
src/app/main.tsx
CHANGED
@@ -30,6 +30,7 @@ import { Field } from '@/components/form/field'
|
|
30 |
import { Label } from '@/components/form/label'
|
31 |
import { getParam } from '@/lib/utils/getParam'
|
32 |
import { GenerationStage } from '@/types'
|
|
|
33 |
|
34 |
export function Main() {
|
35 |
const [_isPending, startTransition] = useTransition()
|
@@ -79,104 +80,35 @@ export function Main() {
|
|
79 |
|
80 |
const isBusy = useStore(s => s.isBusy)
|
81 |
|
82 |
-
const
|
83 |
-
|
84 |
-
readAs: "ArrayBuffer"
|
85 |
-
})
|
86 |
-
const fileData = filesContent[0]
|
87 |
-
|
88 |
-
useEffect(() => {
|
89 |
-
const fn = async () => {
|
90 |
-
if (!fileData?.name) { return }
|
91 |
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
} = useStore.getState()
|
98 |
|
99 |
-
|
100 |
-
|
101 |
-
setStatus("generating")
|
102 |
-
setProgress(25)
|
103 |
-
setParseGenerationStatus("generating")
|
104 |
|
105 |
-
|
106 |
-
const blob = new Blob([fileData.content])
|
107 |
-
clap = await loadClap(blob, fileData.name)
|
108 |
-
} catch (err) {
|
109 |
-
console.error("failed to load the Clap file:", err)
|
110 |
-
setError(`${err}`)
|
111 |
-
}
|
112 |
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
setProgress(0)
|
117 |
-
return
|
118 |
-
}
|
119 |
|
|
|
120 |
setParseGenerationStatus("finished")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
|
122 |
-
/*
|
123 |
-
try {
|
124 |
-
setProgress(60)
|
125 |
-
setVoiceGenerationStatus("generating")
|
126 |
-
clap = await editClapDialogues({
|
127 |
-
clap,
|
128 |
-
turbo: true,
|
129 |
-
})
|
130 |
-
|
131 |
-
if (!clap) { throw new Error(`failed to edit the dialogues`) }
|
132 |
-
|
133 |
-
console.log(`handleSubmit(): received a clap with dialogues = `, clap)
|
134 |
-
setCurrentClap(clap)
|
135 |
-
setVoiceGenerationStatus("finished")
|
136 |
-
} catch (err) {
|
137 |
-
setVoiceGenerationStatus("error")
|
138 |
-
setStatus("error")
|
139 |
-
setError(`${err}`)
|
140 |
-
return
|
141 |
-
}
|
142 |
-
if (!clap) {
|
143 |
-
return
|
144 |
-
}
|
145 |
-
*/
|
146 |
-
|
147 |
-
setFinalGenerationStatus("generating")
|
148 |
-
|
149 |
-
let assetUrl = ""
|
150 |
-
try {
|
151 |
-
assetUrl = await exportClapToVideo({
|
152 |
-
clap,
|
153 |
-
turbo: true
|
154 |
-
})
|
155 |
-
} catch (err) {
|
156 |
-
console.error("failed to render the Clap file:", err)
|
157 |
-
setError(`${err}`)
|
158 |
-
}
|
159 |
-
|
160 |
-
if (!assetUrl) {
|
161 |
-
setFinalGenerationStatus("error")
|
162 |
-
setStatus("error")
|
163 |
-
setProgress(0)
|
164 |
-
return
|
165 |
-
}
|
166 |
-
|
167 |
-
setFinalGenerationStatus("finished")
|
168 |
|
169 |
-
setProgress(80)
|
170 |
-
|
171 |
-
console.log(`loadClap(): generated a video: ${assetUrl.slice(0, 60)}...`)
|
172 |
-
|
173 |
-
setCurrentVideo(assetUrl)
|
174 |
-
setStatus("finished")
|
175 |
-
setError("")
|
176 |
-
}
|
177 |
-
fn()
|
178 |
-
}, [fileData?.name])
|
179 |
-
|
180 |
const generateStory = async (): Promise<ClapProject> => {
|
181 |
|
182 |
let clap: ClapProject | undefined = undefined
|
@@ -219,7 +151,7 @@ export function Main() {
|
|
219 |
|
220 |
const generateEntities = async (clap: ClapProject): Promise<ClapProject> => {
|
221 |
try {
|
222 |
-
setProgress(20)
|
223 |
setAssetGenerationStatus("generating")
|
224 |
clap = await editClapEntities({
|
225 |
clap,
|
@@ -251,13 +183,13 @@ export function Main() {
|
|
251 |
|
252 |
const generateMusic = async (clap: ClapProject): Promise<ClapProject> => {
|
253 |
try {
|
254 |
-
setProgress(30)
|
255 |
setMusicGenerationStatus("generating")
|
256 |
|
257 |
clap = await editClapMusic({
|
258 |
clap,
|
259 |
turbo: true
|
260 |
-
})
|
261 |
|
262 |
if (!clap) { throw new Error(`failed to edit the music`) }
|
263 |
|
@@ -279,7 +211,7 @@ export function Main() {
|
|
279 |
|
280 |
const generateStoryboards = async (clap: ClapProject): Promise<ClapProject> => {
|
281 |
try {
|
282 |
-
setProgress(40)
|
283 |
setImageGenerationStatus("generating")
|
284 |
clap = await editClapStoryboards({
|
285 |
clap,
|
@@ -287,7 +219,7 @@ export function Main() {
|
|
287 |
// since this uses a model with character consistency,
|
288 |
// which is not the case for the non-turbo one
|
289 |
turbo: true
|
290 |
-
})
|
291 |
|
292 |
if (!clap) { throw new Error(`failed to edit the storyboards`) }
|
293 |
|
@@ -310,13 +242,13 @@ export function Main() {
|
|
310 |
|
311 |
const generateVideos = async (clap: ClapProject): Promise<ClapProject> => {
|
312 |
try {
|
313 |
-
setProgress(50)
|
314 |
setVideoGenerationStatus("generating")
|
315 |
|
316 |
clap = await editClapVideos({
|
317 |
clap,
|
318 |
turbo: true
|
319 |
-
})
|
320 |
|
321 |
if (!clap) { throw new Error(`failed to edit the videos`) }
|
322 |
|
@@ -338,7 +270,7 @@ export function Main() {
|
|
338 |
|
339 |
const generateDialogues = async (clap: ClapProject): Promise<ClapProject> => {
|
340 |
try {
|
341 |
-
setProgress(70)
|
342 |
setVoiceGenerationStatus("generating")
|
343 |
clap = await editClapDialogues({
|
344 |
clap,
|
@@ -367,7 +299,7 @@ export function Main() {
|
|
367 |
|
368 |
let assetUrl = ""
|
369 |
try {
|
370 |
-
setProgress(85)
|
371 |
setFinalGenerationStatus("generating")
|
372 |
assetUrl = await exportClapToVideo({
|
373 |
clap,
|
@@ -397,14 +329,14 @@ export function Main() {
|
|
397 |
generateVideos(clap)
|
398 |
])
|
399 |
|
|
|
|
|
400 |
for (const newerClap of claps) {
|
401 |
-
console.log("newerClap:", newerClap)
|
402 |
clap = await updateClap(clap, newerClap, {
|
403 |
overwriteMeta: false,
|
404 |
inlineReplace: true,
|
405 |
})
|
406 |
}
|
407 |
-
console.log("finalClap: ", clap)
|
408 |
|
409 |
/*
|
410 |
clap = await claps.reduce(async (existingClap, newerClap) =>
|
@@ -447,6 +379,54 @@ export function Main() {
|
|
447 |
})
|
448 |
}
|
449 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
450 |
// note: we are interested in the *current* video orientation,
|
451 |
// not the requested video orientation requested for the next video
|
452 |
const isLandscape = currentVideoOrientation === ClapMediaOrientation.LANDSCAPE
|
@@ -496,10 +476,12 @@ export function Main() {
|
|
496 |
})
|
497 |
*/
|
498 |
useStore.setState({
|
499 |
-
progress: Math.min(maxProgressPerStage[stage], progress + 1)
|
|
|
500 |
})
|
501 |
|
502 |
-
timerRef.current = setTimeout(timerFn, progressDelayInMsPerStage[stage])
|
|
|
503 |
}
|
504 |
|
505 |
useEffect(() => {
|
|
|
30 |
import { Label } from '@/components/form/label'
|
31 |
import { getParam } from '@/lib/utils/getParam'
|
32 |
import { GenerationStage } from '@/types'
|
33 |
+
import { FileContent } from 'use-file-picker/dist/interfaces'
|
34 |
|
35 |
export function Main() {
|
36 |
const [_isPending, startTransition] = useTransition()
|
|
|
80 |
|
81 |
const isBusy = useStore(s => s.isBusy)
|
82 |
|
83 |
+
const importStory = async (fileData: FileContent<ArrayBuffer>): Promise<ClapProject> => {
|
84 |
+
if (!fileData?.name) { throw new Error(`invalid file (missing file name)`) }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
|
86 |
+
const {
|
87 |
+
setStatus,
|
88 |
+
setProgress,
|
89 |
+
setParseGenerationStatus,
|
90 |
+
} = useStore.getState()
|
|
|
91 |
|
92 |
+
let clap: ClapProject | undefined = undefined
|
|
|
|
|
|
|
|
|
93 |
|
94 |
+
setParseGenerationStatus("generating")
|
|
|
|
|
|
|
|
|
|
|
|
|
95 |
|
96 |
+
try {
|
97 |
+
const blob = new Blob([fileData.content])
|
98 |
+
clap = await loadClap(blob, fileData.name)
|
|
|
|
|
|
|
99 |
|
100 |
+
if (!clap) { throw new Error(`failed to load the clap file`) }
|
101 |
setParseGenerationStatus("finished")
|
102 |
+
setCurrentClap(clap)
|
103 |
+
return clap
|
104 |
+
} catch (err) {
|
105 |
+
console.error("failed to load the Clap file:", err)
|
106 |
+
setParseGenerationStatus("error")
|
107 |
+
throw err
|
108 |
+
}
|
109 |
+
}
|
110 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
const generateStory = async (): Promise<ClapProject> => {
|
113 |
|
114 |
let clap: ClapProject | undefined = undefined
|
|
|
151 |
|
152 |
const generateEntities = async (clap: ClapProject): Promise<ClapProject> => {
|
153 |
try {
|
154 |
+
// setProgress(20)
|
155 |
setAssetGenerationStatus("generating")
|
156 |
clap = await editClapEntities({
|
157 |
clap,
|
|
|
183 |
|
184 |
const generateMusic = async (clap: ClapProject): Promise<ClapProject> => {
|
185 |
try {
|
186 |
+
// setProgress(30)
|
187 |
setMusicGenerationStatus("generating")
|
188 |
|
189 |
clap = await editClapMusic({
|
190 |
clap,
|
191 |
turbo: true
|
192 |
+
}).then(r => r.promise)
|
193 |
|
194 |
if (!clap) { throw new Error(`failed to edit the music`) }
|
195 |
|
|
|
211 |
|
212 |
const generateStoryboards = async (clap: ClapProject): Promise<ClapProject> => {
|
213 |
try {
|
214 |
+
// setProgress(40)
|
215 |
setImageGenerationStatus("generating")
|
216 |
clap = await editClapStoryboards({
|
217 |
clap,
|
|
|
219 |
// since this uses a model with character consistency,
|
220 |
// which is not the case for the non-turbo one
|
221 |
turbo: true
|
222 |
+
}).then(r => r.promise)
|
223 |
|
224 |
if (!clap) { throw new Error(`failed to edit the storyboards`) }
|
225 |
|
|
|
242 |
|
243 |
const generateVideos = async (clap: ClapProject): Promise<ClapProject> => {
|
244 |
try {
|
245 |
+
// setProgress(50)
|
246 |
setVideoGenerationStatus("generating")
|
247 |
|
248 |
clap = await editClapVideos({
|
249 |
clap,
|
250 |
turbo: true
|
251 |
+
}).then(r => r.promise)
|
252 |
|
253 |
if (!clap) { throw new Error(`failed to edit the videos`) }
|
254 |
|
|
|
270 |
|
271 |
const generateDialogues = async (clap: ClapProject): Promise<ClapProject> => {
|
272 |
try {
|
273 |
+
// setProgress(70)
|
274 |
setVoiceGenerationStatus("generating")
|
275 |
clap = await editClapDialogues({
|
276 |
clap,
|
|
|
299 |
|
300 |
let assetUrl = ""
|
301 |
try {
|
302 |
+
// setProgress(85)
|
303 |
setFinalGenerationStatus("generating")
|
304 |
assetUrl = await exportClapToVideo({
|
305 |
clap,
|
|
|
329 |
generateVideos(clap)
|
330 |
])
|
331 |
|
332 |
+
console.log("finished processing the 2 tasks in parallel")
|
333 |
+
|
334 |
for (const newerClap of claps) {
|
|
|
335 |
clap = await updateClap(clap, newerClap, {
|
336 |
overwriteMeta: false,
|
337 |
inlineReplace: true,
|
338 |
})
|
339 |
}
|
|
|
340 |
|
341 |
/*
|
342 |
clap = await claps.reduce(async (existingClap, newerClap) =>
|
|
|
379 |
})
|
380 |
}
|
381 |
|
382 |
+
|
383 |
+
const { openFilePicker, filesContent } = useFilePicker({
|
384 |
+
accept: '.clap',
|
385 |
+
readAs: "ArrayBuffer"
|
386 |
+
})
|
387 |
+
const fileData = filesContent[0]
|
388 |
+
|
389 |
+
useEffect(() => {
|
390 |
+
const fn = async () => {
|
391 |
+
if (!fileData?.name) { return }
|
392 |
+
|
393 |
+
const { setStatus, setProgress } = useStore.getState()
|
394 |
+
|
395 |
+
setProgress(0)
|
396 |
+
setStatus("generating")
|
397 |
+
|
398 |
+
try {
|
399 |
+
let clap = await importStory(fileData)
|
400 |
+
|
401 |
+
const claps = await Promise.all([
|
402 |
+
generateMusic(clap),
|
403 |
+
generateVideos(clap)
|
404 |
+
])
|
405 |
+
|
406 |
+
// console.log("finished processing the 2 tasks in parallel")
|
407 |
+
|
408 |
+
for (const newerClap of claps) {
|
409 |
+
clap = await updateClap(clap, newerClap, {
|
410 |
+
overwriteMeta: false,
|
411 |
+
inlineReplace: true,
|
412 |
+
})
|
413 |
+
}
|
414 |
+
|
415 |
+
await generateFinalVideo(clap)
|
416 |
+
|
417 |
+
setStatus("finished")
|
418 |
+
setProgress(100)
|
419 |
+
setError("")
|
420 |
+
} catch (err) {
|
421 |
+
console.error(`failed to import: `, err)
|
422 |
+
setStatus("error")
|
423 |
+
setError(`${err}`)
|
424 |
+
}
|
425 |
+
|
426 |
+
}
|
427 |
+
fn()
|
428 |
+
}, [fileData?.name])
|
429 |
+
|
430 |
// note: we are interested in the *current* video orientation,
|
431 |
// not the requested video orientation requested for the next video
|
432 |
const isLandscape = currentVideoOrientation === ClapMediaOrientation.LANDSCAPE
|
|
|
476 |
})
|
477 |
*/
|
478 |
useStore.setState({
|
479 |
+
// progress: Math.min(maxProgressPerStage[stage], progress + 1)
|
480 |
+
progress: Math.min(100, progress + 1)
|
481 |
})
|
482 |
|
483 |
+
// timerRef.current = setTimeout(timerFn, progressDelayInMsPerStage[stage])
|
484 |
+
timerRef.current = setTimeout(timerFn, 750)
|
485 |
}
|
486 |
|
487 |
useEffect(() => {
|
src/app/server/aitube/editClapMusic.ts
CHANGED
@@ -4,6 +4,7 @@ import { ClapProject } from "@aitube/clap"
|
|
4 |
import { editClapMusic as apiEditClapMusic, ClapCompletionMode } from "@aitube/client"
|
5 |
|
6 |
import { getToken } from "./getToken"
|
|
|
7 |
|
8 |
export async function editClapMusic({
|
9 |
clap,
|
@@ -11,13 +12,16 @@ export async function editClapMusic({
|
|
11 |
}: {
|
12 |
clap: ClapProject
|
13 |
turbo?: boolean
|
14 |
-
}):
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
|
|
|
|
|
|
23 |
}
|
|
|
4 |
import { editClapMusic as apiEditClapMusic, ClapCompletionMode } from "@aitube/client"
|
5 |
|
6 |
import { getToken } from "./getToken"
|
7 |
+
import { Workaround } from "./types"
|
8 |
|
9 |
export async function editClapMusic({
|
10 |
clap,
|
|
|
12 |
}: {
|
13 |
clap: ClapProject
|
14 |
turbo?: boolean
|
15 |
+
}): Workaround<ClapProject> {
|
16 |
+
async function promise() {
|
17 |
+
return await apiEditClapMusic({
|
18 |
+
clap,
|
19 |
+
completionMode: ClapCompletionMode.MERGE,
|
20 |
+
turbo,
|
21 |
+
token: await getToken()
|
22 |
+
})
|
23 |
+
}
|
24 |
+
return {
|
25 |
+
promise: promise()
|
26 |
+
}
|
27 |
}
|
src/app/server/aitube/editClapStoryboards.ts
CHANGED
@@ -4,6 +4,7 @@ import { ClapProject } from "@aitube/clap"
|
|
4 |
import { editClapStoryboards as apiEditClapStoryboards, ClapCompletionMode } from "@aitube/client"
|
5 |
|
6 |
import { getToken } from "./getToken"
|
|
|
7 |
|
8 |
export async function editClapStoryboards({
|
9 |
clap,
|
@@ -11,13 +12,16 @@ export async function editClapStoryboards({
|
|
11 |
}: {
|
12 |
clap: ClapProject
|
13 |
turbo?: boolean
|
14 |
-
}):
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
|
|
|
|
|
|
23 |
}
|
|
|
4 |
import { editClapStoryboards as apiEditClapStoryboards, ClapCompletionMode } from "@aitube/client"
|
5 |
|
6 |
import { getToken } from "./getToken"
|
7 |
+
import { Workaround } from "./types"
|
8 |
|
9 |
export async function editClapStoryboards({
|
10 |
clap,
|
|
|
12 |
}: {
|
13 |
clap: ClapProject
|
14 |
turbo?: boolean
|
15 |
+
}): Workaround<ClapProject> {
|
16 |
+
async function promise() {
|
17 |
+
return await apiEditClapStoryboards({
|
18 |
+
clap,
|
19 |
+
completionMode: ClapCompletionMode.MERGE,
|
20 |
+
turbo,
|
21 |
+
token: await getToken()
|
22 |
+
})
|
23 |
+
}
|
24 |
+
return {
|
25 |
+
promise: promise()
|
26 |
+
}
|
27 |
}
|
src/app/server/aitube/editClapVideos.ts
CHANGED
@@ -4,6 +4,7 @@ import { ClapProject } from "@aitube/clap"
|
|
4 |
import { editClapVideos as apiEditClapVideos, ClapCompletionMode } from "@aitube/client"
|
5 |
|
6 |
import { getToken } from "./getToken"
|
|
|
7 |
|
8 |
export async function editClapVideos({
|
9 |
clap,
|
@@ -11,13 +12,16 @@ export async function editClapVideos({
|
|
11 |
}: {
|
12 |
clap: ClapProject
|
13 |
turbo?: boolean
|
14 |
-
}):
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
|
|
|
|
|
|
23 |
}
|
|
|
4 |
import { editClapVideos as apiEditClapVideos, ClapCompletionMode } from "@aitube/client"
|
5 |
|
6 |
import { getToken } from "./getToken"
|
7 |
+
import { Workaround } from "./types"
|
8 |
|
9 |
export async function editClapVideos({
|
10 |
clap,
|
|
|
12 |
}: {
|
13 |
clap: ClapProject
|
14 |
turbo?: boolean
|
15 |
+
}): Workaround<ClapProject> {
|
16 |
+
async function promise() {
|
17 |
+
return await apiEditClapVideos({
|
18 |
+
clap,
|
19 |
+
completionMode: ClapCompletionMode.MERGE,
|
20 |
+
turbo,
|
21 |
+
token: await getToken()
|
22 |
+
})
|
23 |
+
}
|
24 |
+
return {
|
25 |
+
promise: promise()
|
26 |
+
}
|
27 |
}
|
src/app/server/aitube/types.ts
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// "Server Actions are designed for mutations that update server-side state; they are not recommended for data fetching. Accordingly, frameworks implementing Server Actions typically process one action at a time and do not have a way to cache the return value."
|
2 |
+
// well, lol
|
3 |
+
// https://www.youtube.com/watch?v=CDZg3maL9q0
|
4 |
+
export type Workaround<T> = Promise<{ promise: Promise<T> }>
|