jbilcke-hf's picture
jbilcke-hf HF staff
upgrade libs
7fd8bde
raw
history blame
7.08 kB
"use client"
import { ClapProject, parseClap, serializeClap, ClapMediaOrientation } from "@aitube/clap"
import { create } from "zustand"
import { GlobalStatus, TaskStatus } from "@/types"
import { getVideoOrientation } from "@/lib/utils/getVideoOrientation"
import { RESOLUTION_LONG, RESOLUTION_SHORT } from "./server/aitube/config"
export const useStore = create<{
mainCharacterImage: string
mainCharacterVoice: string
storyPromptDraft: string
storyPrompt: string
// the desired orientation for the next video
// but this won't impact the actual orientation of the fake device container
orientation: ClapMediaOrientation
status: GlobalStatus
parseGenerationStatus: TaskStatus
storyGenerationStatus: TaskStatus
assetGenerationStatus: TaskStatus
voiceGenerationStatus: TaskStatus
imageGenerationStatus: TaskStatus
videoGenerationStatus: TaskStatus
currentClap?: ClapProject
currentVideo: string
// orientation of the currently loaded video (which can be different from `orientation`)
// it will impact the actual orientation of the fake device container
currentVideoOrientation: ClapMediaOrientation
progress: number
error: string
toggleOrientation: () => void
setCurrentVideoOrientation: (currentVideoOrientation: ClapMediaOrientation) => void
setMainCharacterImage: (mainCharacterImage: string) => void
setMainCharacterVoice: (mainCharacterVoice: string) => void
setStoryPromptDraft: (storyPromptDraft: string) => void
setStoryPrompt: (storyPrompt: string) => void
setStatus: (status: GlobalStatus) => void
setParseGenerationStatus: (parseGenerationStatus: TaskStatus) => void
setStoryGenerationStatus: (storyGenerationStatus: TaskStatus) => void
setAssetGenerationStatus: (assetGenerationStatus: TaskStatus) => void
setVoiceGenerationStatus: (voiceGenerationStatus: TaskStatus) => void
setImageGenerationStatus: (imageGenerationStatus: TaskStatus) => void
setVideoGenerationStatus: (videoGenerationStatus: TaskStatus) => void
setCurrentClap: (currentClap?: ClapProject) => void
// note: this will preload the video, and compute the orientation too
setGeneratedVideo: (generatedVideo: string) => Promise<void>
setProgress: (progress: number) => void
setError: (error: string) => void
saveClap: () => Promise<void>
loadClap: (blob: Blob, fileName?: string) => Promise<ClapProject>
}>((set, get) => ({
mainCharacterImage: "",
mainCharacterVoice: "",
storyPromptDraft: "Yesterday I was at my favorite pizza place and..",
storyPrompt: "",
orientation: ClapMediaOrientation.PORTRAIT,
status: "idle",
parseGenerationStatus: "idle",
storyGenerationStatus: "idle",
assetGenerationStatus: "idle",
voiceGenerationStatus: "idle",
imageGenerationStatus: "idle",
videoGenerationStatus: "idle",
currentClap: undefined,
currentVideo: "",
currentVideoOrientation: ClapMediaOrientation.PORTRAIT,
progress: 0,
error: "",
toggleOrientation: () => {
const { orientation: previousOrientation, currentVideoOrientation, currentVideo } = get()
const orientation =
previousOrientation === ClapMediaOrientation.LANDSCAPE
? ClapMediaOrientation.PORTRAIT
: ClapMediaOrientation.LANDSCAPE
set({
orientation,
// we normally don't touch the currentVideoOrientation since it will already contain a video
currentVideoOrientation:
currentVideo
? currentVideoOrientation
: orientation
})
},
setCurrentVideoOrientation: (currentVideoOrientation: ClapMediaOrientation) => { set({ currentVideoOrientation }) },
setMainCharacterImage: (mainCharacterImage: string) => { set({ mainCharacterImage }) },
setMainCharacterVoice: (mainCharacterVoice: string) => { set({ mainCharacterVoice }) },
setStoryPromptDraft: (storyPromptDraft: string) => { set({ storyPromptDraft }) },
setStoryPrompt: (storyPrompt: string) => { set({ storyPrompt }) },
setStatus: (status: GlobalStatus) => { set({ status }) },
setParseGenerationStatus: (parseGenerationStatus: TaskStatus) => { set({ parseGenerationStatus }) },
setStoryGenerationStatus: (storyGenerationStatus: TaskStatus) => { set({ storyGenerationStatus }) },
setAssetGenerationStatus: (assetGenerationStatus: TaskStatus) => { set({ assetGenerationStatus }) },
setVoiceGenerationStatus: (voiceGenerationStatus: TaskStatus) => { set({ voiceGenerationStatus }) },
setImageGenerationStatus: (imageGenerationStatus: TaskStatus) => { set({ imageGenerationStatus }) },
setVideoGenerationStatus: (videoGenerationStatus: TaskStatus) => { set({ videoGenerationStatus }) },
setCurrentClap: (currentClap?: ClapProject) => { set({ currentClap }) },
setGeneratedVideo: async (currentVideo: string): Promise<void> => {
const currentVideoOrientation = await getVideoOrientation(currentVideo)
set({
currentVideo,
currentVideoOrientation
})
},
setProgress: (progress: number) => { set({ progress }) },
setError: (error: string) => { set({ error }) },
saveClap: async (): Promise<void> => {
const { currentClap , storyPrompt } = get()
if (!currentClap) { throw new Error(`cannot save a clap.. if there is no clap`) }
const currentClapBlob: Blob = await serializeClap(currentClap)
// Create an object URL for the compressed clap blob
const objectUrl = URL.createObjectURL(currentClapBlob)
// Create an anchor element and force browser download
const anchor = document.createElement("a")
anchor.href = objectUrl
const firstPartOfStoryPrompt = storyPrompt // .split(",").shift() || ""
const cleanStoryPrompt = firstPartOfStoryPrompt.replace(/([^a-z0-9, ]+)/gi, "_")
const cleanName = `${cleanStoryPrompt.slice(0, 50)}`
anchor.download = `${cleanName}.clap`
document.body.appendChild(anchor) // Append to the body (could be removed once clicked)
anchor.click() // Trigger the download
// Cleanup: revoke the object URL and remove the anchor element
URL.revokeObjectURL(objectUrl)
document.body.removeChild(anchor)
},
loadClap: async (blob: Blob, fileName: string = "untitled_story.clap"): Promise<ClapProject> => {
if (!blob) {
throw new Error(`missing blob`)
}
const currentClap: ClapProject | undefined = await parseClap(blob)
if (!currentClap) { throw new Error(`failed to import the clap`) }
const storyPrompt = currentClap.meta.description.split("||").pop() || ""
// TODO: parseVideoOrientation should be put inside @aitube/clap (in the utils)
// const orientation = parseVideoOrientation(currentClap.meta.orientation)
// let's use the UI settings for now
const { orientation } = get()
currentClap.meta.height = orientation === ClapMediaOrientation.LANDSCAPE ? RESOLUTION_SHORT : RESOLUTION_LONG
currentClap.meta.width = orientation === ClapMediaOrientation.PORTRAIT ? RESOLUTION_SHORT : RESOLUTION_LONG
set({
currentClap,
storyPrompt,
storyPromptDraft: storyPrompt,
orientation,
currentVideoOrientation: orientation,
})
return currentClap
},
}))