jbilcke-hf HF staff commited on
Commit
51365b6
·
1 Parent(s): c8c2f6c
Files changed (2) hide show
  1. src/app/main.tsx +33 -7
  2. src/app/store.ts +30 -0
src/app/main.tsx CHANGED
@@ -3,6 +3,7 @@
3
  import React, { useEffect, useRef, useTransition } from 'react'
4
  import { IoMdPhonePortrait } from 'react-icons/io'
5
  import { GiRollingDices } from 'react-icons/gi'
 
6
  import { useLocalStorage } from "usehooks-ts"
7
  import { ClapProject, ClapMediaOrientation, ClapSegmentCategory, updateClap } from '@aitube/clap'
8
  import Image from 'next/image'
@@ -79,6 +80,7 @@ export function Main() {
79
  const setCurrentVideo = useStore(s => s.setCurrentVideo)
80
  const progress = useStore(s => s.progress)
81
  const setProgress = useStore(s => s.setProgress)
 
82
  const saveClap = useStore(s => s.saveClap)
83
  const loadClap = useStore(s => s.loadClap)
84
 
@@ -254,7 +256,7 @@ export function Main() {
254
 
255
  clap = await editClapVideos({
256
  clap,
257
- turbo: true
258
  }).then(r => r.promise)
259
 
260
  if (!clap) { throw new Error(`failed to edit the videos`) }
@@ -313,9 +315,12 @@ export function Main() {
313
  turbo: true
314
  })
315
 
 
 
 
 
316
  console.log(`handleSubmit(): received a video: ${assetUrl.slice(0, 60)}...`)
317
  setFinalGenerationStatus("finished")
318
- setCurrentVideo(assetUrl)
319
  return assetUrl
320
  } catch (err) {
321
  setFinalGenerationStatus("error")
@@ -873,10 +878,10 @@ export function Main() {
873
  )
874
  : status === "error"
875
  ? <span>{error || ""}</span>
876
- : <span>&nbsp;</span> // to prevent layout changes
877
  }</p>
878
  </div>
879
- : currentVideo ? <video
880
  src={currentVideo}
881
  controls
882
  playsInline
@@ -917,9 +922,30 @@ export function Main() {
917
  </div>
918
  </DeviceFrameset>
919
 
920
- {/*
921
- <div className={handleDownload}>Download</div>
922
- */}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
923
  </div>
924
  </div>
925
  </div>
 
3
  import React, { useEffect, useRef, useTransition } from 'react'
4
  import { IoMdPhonePortrait } from 'react-icons/io'
5
  import { GiRollingDices } from 'react-icons/gi'
6
+ import { FaCloudDownloadAlt } from "react-icons/fa"
7
  import { useLocalStorage } from "usehooks-ts"
8
  import { ClapProject, ClapMediaOrientation, ClapSegmentCategory, updateClap } from '@aitube/clap'
9
  import Image from 'next/image'
 
80
  const setCurrentVideo = useStore(s => s.setCurrentVideo)
81
  const progress = useStore(s => s.progress)
82
  const setProgress = useStore(s => s.setProgress)
83
+ const saveVideo = useStore(s => s.saveVideo)
84
  const saveClap = useStore(s => s.saveClap)
85
  const loadClap = useStore(s => s.loadClap)
86
 
 
256
 
257
  clap = await editClapVideos({
258
  clap,
259
+ turbo: false
260
  }).then(r => r.promise)
261
 
262
  if (!clap) { throw new Error(`failed to edit the videos`) }
 
315
  turbo: true
316
  })
317
 
318
+ setCurrentVideo(assetUrl)
319
+
320
+ if (assetUrl.length < 128) { throw new Error(`handleSubmit(): the generated video is too small, so we failed`) }
321
+
322
  console.log(`handleSubmit(): received a video: ${assetUrl.slice(0, 60)}...`)
323
  setFinalGenerationStatus("finished")
 
324
  return assetUrl
325
  } catch (err) {
326
  setFinalGenerationStatus("error")
 
878
  )
879
  : status === "error"
880
  ? <span>{error || ""}</span>
881
+ : <span>{error ? error : <span>&nbsp;</span>}</span> // to prevent layout changes
882
  }</p>
883
  </div>
884
+ : (currentVideo && currentVideo?.length > 128) ? <video
885
  src={currentVideo}
886
  controls
887
  playsInline
 
922
  </div>
923
  </DeviceFrameset>
924
 
925
+ {(currentVideo && currentVideo.length > 128) ? <div
926
+ className={cn(`
927
+ w-full
928
+ flex flex-row
929
+ items-center justify-center
930
+ transition-all duration-150 ease-in-out
931
+
932
+ text-stone-800
933
+
934
+ group
935
+ pt-2 md:pt-4
936
+ `,
937
+ isBusy ? 'opacity-50' : 'cursor-pointer opacity-100 hover:scale-110 active:scale-150 hover:text-stone-950 active:text-black'
938
+ )}
939
+ style={{ textShadow: "rgb(255 255 255 / 19%) 0px 0px 2px" }}
940
+ onClick={isBusy ? undefined : saveVideo}
941
+ >
942
+ <div className="
943
+ text-base md:text-lg lg:text-xl
944
+ transition-all duration-150 ease-out
945
+ group-hover:animate-swing
946
+ "><FaCloudDownloadAlt /></div>
947
+ <div className="text-xs md:text-sm lg:text-base">&nbsp;Download</div>
948
+ </div> : null}
949
  </div>
950
  </div>
951
  </div>
src/app/store.ts CHANGED
@@ -61,6 +61,7 @@ export const useStore = create<{
61
 
62
  setProgress: (progress: number) => void
63
  setError: (error: string) => void
 
64
  saveClap: () => Promise<void>
65
  loadClap: (blob: Blob, fileName?: string) => Promise<ClapProject>
66
  }>((set, get) => ({
@@ -188,6 +189,35 @@ export const useStore = create<{
188
  },
189
  setProgress: (progress: number) => { set({ progress }) },
190
  setError: (error: string) => { set({ error }) },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  saveClap: async (): Promise<void> => {
192
  const { currentClap , storyPrompt } = get()
193
 
 
61
 
62
  setProgress: (progress: number) => void
63
  setError: (error: string) => void
64
+ saveVideo: () => Promise<void>
65
  saveClap: () => Promise<void>
66
  loadClap: (blob: Blob, fileName?: string) => Promise<ClapProject>
67
  }>((set, get) => ({
 
189
  },
190
  setProgress: (progress: number) => { set({ progress }) },
191
  setError: (error: string) => { set({ error }) },
192
+ saveVideo: async (): Promise<void> => {
193
+ const { currentVideo, storyPrompt } = get()
194
+
195
+ if (!currentVideo) { throw new Error(`cannot save a video.. if there is no video`) }
196
+
197
+ const currentClapBlob: Blob = await fetch(currentVideo).then(r => r.blob())
198
+
199
+ // Create an object URL for the compressed clap blob
200
+ const objectUrl = URL.createObjectURL(currentClapBlob)
201
+
202
+ // Create an anchor element and force browser download
203
+ const anchor = document.createElement("a")
204
+ anchor.href = objectUrl
205
+
206
+ const firstPartOfStoryPrompt = storyPrompt // .split(",").shift() || ""
207
+
208
+ const cleanStoryPrompt = firstPartOfStoryPrompt.replace(/([^a-z0-9, ]+)/gi, "_")
209
+
210
+ const cleanName = `${cleanStoryPrompt.slice(0, 50)}`
211
+
212
+ anchor.download = `${cleanName}.mp4`
213
+
214
+ document.body.appendChild(anchor) // Append to the body (could be removed once clicked)
215
+ anchor.click() // Trigger the download
216
+
217
+ // Cleanup: revoke the object URL and remove the anchor element
218
+ URL.revokeObjectURL(objectUrl)
219
+ document.body.removeChild(anchor)
220
+ },
221
  saveClap: async (): Promise<void> => {
222
  const { currentClap , storyPrompt } = get()
223