"use client" import React, { MouseEventHandler, useEffect, useRef, useState } from "react" import { useLocalStorage } from "usehooks-ts" import { cn } from "@/lib/utils/cn" import { MediaInfo } from "@/types/general" import { useLatentEngine } from "./useLatentEngine" import { PlayPauseButton } from "../components/play-pause-button" import { StaticOrInteractiveTag } from "../../static-or-interactive-tag" import { ContentLayer } from "../components/content-layer" import { localStorageKeys } from "@/app/state/localStorageKeys" import { defaultSettings } from "@/app/state/defaultSettings" import { useStore } from "@/app/state/useStore" import { ClapProject, generateClapFromSimpleStory, serializeClap } from "@aitube/clap" function LatentEngine({ media, width, height, className = "" }: { media: MediaInfo width?: number height?: number className?: string }) { // used to prevent people from opening multiple sessions at the same time // note: this should also be enforced with the Hugging Face ID const [multiTabsLock, setMultiTabsLock] = useLocalStorage( "AI_TUBE_ENGINE_MULTI_TABS_LOCK", Date.now() ) const [huggingfaceApiKey, setHuggingfaceApiKey] = useLocalStorage( localStorageKeys.huggingfaceApiKey, defaultSettings.huggingfaceApiKey ) // note here how we transfer the info from one store to another const jwtToken = useStore(s => s.jwtToken) const setJwtToken = useLatentEngine(s => s.setJwtToken) useEffect(() => { setJwtToken(jwtToken) }, [jwtToken]) const setContainerDimension = useLatentEngine(s => s.setContainerDimension) const isLoaded = useLatentEngine(s => s.isLoaded) const imagine = useLatentEngine(s => s.imagine) const open = useLatentEngine(s => s.open) const videoSimulationVideoPlaybackFPS = useLatentEngine(s => s.videoSimulationVideoPlaybackFPS) const videoSimulationRenderingTimeFPS = useLatentEngine(s => s.videoSimulationRenderingTimeFPS) const isLoop = useLatentEngine(s => s.isLoop) const isStatic = useLatentEngine(s => s.isStatic) const isLive = useLatentEngine(s => s.isLive) const isInteractive = useLatentEngine(s => s.isInteractive) const isPlaying = useLatentEngine(s => s.isPlaying) const togglePlayPause = useLatentEngine(s => s.togglePlayPause) const videoLayers = useLatentEngine(s => s.videoLayers) const interfaceLayers = useLatentEngine(s => s.interfaceLayers) const onClickOnSegmentationLayer = useLatentEngine(s => s.onClickOnSegmentationLayer) const stateRef = useRef({ isInitialized: false }) const [isOverlayVisible, setOverlayVisible] = useState(true) const overlayTimerRef = useRef() const videoLayerRef = useRef(null) const segmentationLayerRef = useRef(null) const mediaUrl = media.clapUrl || media.assetUrlHd || media.assetUrl useEffect(() => { if (!stateRef.current.isInitialized && mediaUrl) { stateRef.current.isInitialized = true const fn = async () => { // TODO julian // there is a bug, we can't unpack the .clap when it's from a data-uri :/ // open(mediaUrl) const mockClap: ClapProject = generateClapFromSimpleStory() const mockArchive: Blob = await serializeClap(mockClap) // for some reason conversion to data uri doesn't work // const mockDataUri = await blobToDataUri(mockArchive, "application/x-gzip") // console.log("mockDataUri:", mockDataUri) open(mockArchive) } fn() } }, [mediaUrl]) const isPlayingRef = useRef(isPlaying) isPlayingRef.current = isPlaying const scheduleOverlayInvisibility = () => { clearTimeout(overlayTimerRef.current) overlayTimerRef.current = setTimeout(() => { if (isPlayingRef.current) { setOverlayVisible(!isPlayingRef.current) } clearTimeout(overlayTimerRef.current) }, 3000) } /* useEffect(() => { if (isPlaying) { scheduleOverlayInvisibility() } else { clearTimeout(overlayTimerRef.current) setOverlayVisible(true) } return () => { clearTimeout(overlayTimerRef.current) } }, [isPlaying]) */ useEffect(() => { setContainerDimension({ width: width || 256, height: height || 256 }) }, [width, height]) return (
{/* */} {/* main content container */} {videoLayers.map(({ id }) => ( {/* {interfaceLayers.map(({ id, element }) => (
{element}
))}
*/} {/* content overlay, with the gradient, buttons etc */}
{ setOverlayVisible(true) scheduleOverlayInvisibility() }} style={{ width, height, boxShadow: "rgba(0, 0, 0, 1) 0px -77px 100px 15px inset" }}> {/* bottom slider and button bar */}
{/* the (optional) timeline slider bar */}
{/* button bar */}
{/* left-side buttons */}
{/* right-side buttons */}
{/* TODO: put a fullscreen button (and mode) here */}
playback: {Math.round(videoSimulationVideoPlaybackFPS * 100) / 100} FPS
rendering: {Math.round(videoSimulationRenderingTimeFPS * 100) / 100} FPS
); } export default LatentEngine