diff --git a/packages/fakeyou.com/src/components/common/LoadingSpinner/LoadingSpinner.tsx b/packages/fakeyou.com/src/components/common/LoadingSpinner/LoadingSpinner.tsx index e5d1697afa..f83b40ff55 100644 --- a/packages/fakeyou.com/src/components/common/LoadingSpinner/LoadingSpinner.tsx +++ b/packages/fakeyou.com/src/components/common/LoadingSpinner/LoadingSpinner.tsx @@ -2,18 +2,23 @@ import React from "react"; interface LoadingSpinnerProps { className?: string; + label?: string; } -export default function LoadingSpinner({ className }: LoadingSpinnerProps) { +export default function LoadingSpinner({ + className, + label, +}: LoadingSpinnerProps) { return (
Loading...
+ {label &&
{label}
}
); } diff --git a/packages/fakeyou.com/src/components/common/Scene3D/Scene3D.scss b/packages/fakeyou.com/src/components/common/Scene3D/Scene3D.scss index 6fe22684fd..7544744324 100644 --- a/packages/fakeyou.com/src/components/common/Scene3D/Scene3D.scss +++ b/packages/fakeyou.com/src/components/common/Scene3D/Scene3D.scss @@ -11,3 +11,9 @@ width: 100%; border-radius: 0.5rem; } + +.fy-scene-3d-fullscreen-intro { + height: calc(100vh - 66px - 66px); + width: 100%; + border-radius: 0rem; +} diff --git a/packages/fakeyou.com/src/components/common/Scene3D/Scene3D.tsx b/packages/fakeyou.com/src/components/common/Scene3D/Scene3D.tsx index 32b6153ea5..92708195c1 100644 --- a/packages/fakeyou.com/src/components/common/Scene3D/Scene3D.tsx +++ b/packages/fakeyou.com/src/components/common/Scene3D/Scene3D.tsx @@ -12,6 +12,7 @@ interface Scene3DProps { urlParams?: string; objectId?: string; sceneMediaFileToken?: string; + className?: string; } export default function Scene3D({ @@ -20,8 +21,8 @@ export default function Scene3D({ objectId, sceneMediaFileToken, urlParams, + className, }: Scene3DProps) { - let engineUrl = `https://engine.fakeyou.com?mode=${mode}`; if (sceneMediaFileToken) { @@ -35,14 +36,9 @@ export default function Scene3D({
- +
); } diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/TrimScrubber.tsx b/packages/fakeyou.com/src/components/common/VideoQuickTrim/TrimScrubber.tsx new file mode 100644 index 0000000000..dc900305eb --- /dev/null +++ b/packages/fakeyou.com/src/components/common/VideoQuickTrim/TrimScrubber.tsx @@ -0,0 +1,49 @@ +import React, { + useRef, + // useState +} from 'react'; +import { + faGripDots +} from "@fortawesome/pro-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +export default function TrimScrubber({ + width +}:{ + width:number +}){ + const ref = useRef(null); + return( +
{ + // if(trimZoneRef.current){ + // trimZoneRef.current.style.cursor = 'grabbing'; + // setTrimState((curr)=>({ + // ...curr, + // isScrubbingTrim: true, + // })) + // } + // }} + // onPointerUp={()=>{ + // if(trimZoneRef.current) { + // trimZoneRef.current.style.cursor = 'grab'; + // setTrimState((curr)=>({ + // ...curr, + // isScrubbingTrim: false, + // })); + // } + // }} + // onPointerMove={(e: PointerEvent)=>{ + // if(trimZoneRef.current && playbarRef.current && isScrubbingTrim){ + // // console.log(fractionToPercentage( + // // (e.clientX - trimZoneRef.current.getBoundingClientRect().left) / (playbarRef.current.getBoundingClientRect().width) + // // ) + "%"); + // } + // }} + > + +
+ ); +} diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/VideoQuickTrim.tsx b/packages/fakeyou.com/src/components/common/VideoQuickTrim/VideoQuickTrim.tsx index 343ee9fa50..2ebbd27fab 100644 --- a/packages/fakeyou.com/src/components/common/VideoQuickTrim/VideoQuickTrim.tsx +++ b/packages/fakeyou.com/src/components/common/VideoQuickTrim/VideoQuickTrim.tsx @@ -4,69 +4,69 @@ import React, { useLayoutEffect, useState, useRef, - PointerEvent, + // PointerEvent, } from "react"; import { + faArrowsRepeat, faPlay, faPause, - faGripDots, faVolume, faVolumeSlash, -} from "@fortawesome/pro-solid-svg-icons" +} from "@fortawesome/pro-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { - Button, - SelectionBubbles, - VideoFakeyou -} from "components/common"; +import { Button, SelectionBubbles, VideoFakeyou } from "components/common"; import { VideoFakeyouProps } from "../VideoFakeyou/VideoFakeyou"; import { QuickTrimData, TrimStates, PlaybarStates } from "./types"; import { formatSecondsToHHMMSSCS } from "./helpers"; +import TrimScrbber from "./TrimScrubber"; import './styles.scss' -interface VideoQuickTrimProps extends VideoFakeyouProps{ - onSelect: (values: QuickTrimData)=>void; +interface VideoQuickTrimProps extends VideoFakeyouProps { + onSelect: (values: QuickTrimData) => void; } const trimOptions: { [key: string]: number } = { "3s": 3, "5s": 5, "10s": 10, - "15s" : 15, + "15s": 15, }; -const initialTrimState:TrimStates = { +const initialTrimState: TrimStates = { canNotTrim: true, isScrubbingTrim: false, trimDuration: 0, trimStart: 0, trimEnd: 0, maxDuration: 0, -} +}; -const initialPlaybarState:PlaybarStates = { +const initialPlaybarState: PlaybarStates = { playbarWidth: 0, timeCursorOffset: 0, -} +}; export default memo(function VideoQuickTrim({ onSelect, ...rest -}: VideoQuickTrimProps){ +}: VideoQuickTrimProps) { // console.log("VideoQuickTrim Rerender!!"); const videoRef = useRef(null); const playbarRef = useRef(null); - const trimZoneRef = useRef(null); - const [{playbarWidth, timeCursorOffset}, setPlaybarState] = useState(initialPlaybarState); - const [playpause, setPlaypause] = useState<'playing'|'paused'|'stopped'>('paused'); + const [{ + playbarWidth, + timeCursorOffset + }, setPlaybarState] = useState(initialPlaybarState); + const [playpause, setPlaypause] = useState<'playing'|'paused'|'ended'>('paused'); const [isMuted, setIsMuted] = useState(false); + const [isRepeatOn, setIsRepatOn] = useState(true); const [{ canNotTrim, trimStart, - // trimEnd, + trimEnd, trimDuration, // isScrubbingTrim, }, setTrimState] = useState(initialTrimState); @@ -74,6 +74,7 @@ export default memo(function VideoQuickTrim({ const videoRefCallback = useCallback(node => { + console.log("videoRefCallback"); if (node !== null) { // DOM node referenced by ref has changed and exists videoRef.current = node; @@ -87,72 +88,89 @@ export default memo(function VideoQuickTrim({ trimEnd: 3, maxDuration: node.duration })); + //TODO: this sbould be set in USEEFFECT + //IFF USEEFFECT starts working again onSelect({ trimStartSeconds: 0, trimEndSeconds: 3, }) } - } + }; - node.ontimeupdate = (e: PointerEvent)=>{ + node.ontimeupdate = ()=>{ + if(isRepeatOn && node.currentTime >= trimEnd){ + node.currentTime = 0; + } setPlaybarState((curr)=>({ ...curr, timeCursorOffset: (node.currentTime / node.duration) * (playbarWidth-8) })); - } + + }; + + node.onplay = ()=>{ setPlaypause("playing");}; + node.onpause = ()=>{setPlaypause("paused");}; + node.onended = ()=>{setPlaypause("ended");}; + } // else{} DOM node referenced by ref has been unmounted - }, [playbarWidth, onSelect]); //END videoRefCallback + }, [playbarWidth, onSelect, isRepeatOn, trimEnd]); //END videoRefCallback function handleWindowResize() { - if(playbarRef.current !== null){ + if (playbarRef.current !== null) { const newWidth = playbarRef.current.getBoundingClientRect().width; - setPlaybarState((curr)=>({ + setPlaybarState(curr => ({ playbarWidth: newWidth, - timeCursorOffset: (curr.timeCursorOffset / (curr.playbarWidth-8)) * (newWidth-8) + timeCursorOffset: + (curr.timeCursorOffset / (curr.playbarWidth - 8)) * (newWidth - 8), })); } } const playbarRefCallback = useCallback(node => { - if(node !== null) { + if (node !== null) { playbarRef.current = node; - setPlaybarState((curr)=>({ + setPlaybarState(curr => ({ ...curr, - playbarWidth: node.getBoundingClientRect().width + playbarWidth: node.getBoundingClientRect().width, })); } }, []); - useLayoutEffect(()=>{ - window.addEventListener('resize', handleWindowResize); + useLayoutEffect(() => { + window.addEventListener("resize", handleWindowResize); return () => { - window.removeEventListener('resize', handleWindowResize); + window.removeEventListener("resize", handleWindowResize); }; - },[]); + }, []); const handleChangeTrimDuration = (selected: string) =>{ if(!canNotTrim){ + const newTrimEnd = trimStart+ trimOptions[selected]; setTrimState((curr)=>({ ...curr, trimDuration: trimOptions[selected], - trimEnd: trimStart+ trimOptions[selected], + trimEnd: newTrimEnd, })) onSelect({ trimStartSeconds: trimStart, - trimEndSeconds: trimStart+ trimOptions[selected], + trimEndSeconds: newTrimEnd, }); + if (isRepeatOn + && videoRef.current + && videoRef.current.currentTime > newTrimEnd + ){ + videoRef.current.currentTime = trimStart; + } } } const handlePlaypause = ()=>{ - if (playpause === 'paused' || playpause === 'stopped'){ + if (playpause === 'paused' || playpause === 'ended'){ videoRef.current?.play(); - setPlaypause('playing'); }else{ videoRef.current?.pause(); - setPlaypause('paused'); } - } + }; - const trimZoneWidth = videoRef.current + const trimScrubberWidth = videoRef.current ? trimDuration > 0 && trimDuration < videoRef.current.duration ? (trimDuration / videoRef.current.duration * 100) : 100 @@ -169,57 +187,32 @@ export default memo(function VideoQuickTrim({ />
- {playpause === 'paused' && - - } - {playpause === 'playing' && - - } + )}
- {videoRef.current && canNotTrim && + {videoRef.current && canNotTrim && (

Warning: Sorry Your Video is TOO Short

- } - {/* END of Video Wrapper */} + )} + + {/* END of Video Wrapper */}
-
{ - // if(trimZoneRef.current){ - // trimZoneRef.current.style.cursor = 'grabbing'; - // setTrimState((curr)=>({ - // ...curr, - // isScrubbingTrim: true, - // })) - // } - // }} - // onPointerUp={()=>{ - // if(trimZoneRef.current) { - // trimZoneRef.current.style.cursor = 'grab'; - // setTrimState((curr)=>({ - // ...curr, - // isScrubbingTrim: false, - // })); - // } - // }} - // onPointerMove={(e: PointerEvent)=>{ - // if(trimZoneRef.current && playbarRef.current && isScrubbingTrim){ - // // console.log(fractionToPercentage( - // // (e.clientX - trimZoneRef.current.getBoundingClientRect().left) / (playbarRef.current.getBoundingClientRect().width) - // // ) + "%"); - // } - // }} - > - -
+
{/* END of Playbar */} @@ -227,20 +220,28 @@ export default memo(function VideoQuickTrim({
+
+ ); +} + +export { StudioIntroPage }; diff --git a/packages/fakeyou.com/src/v2/view/pages/storyteller_studio_intro/StudioIntroResultPage.tsx b/packages/fakeyou.com/src/v2/view/pages/storyteller_studio_intro/StudioIntroResultPage.tsx new file mode 100644 index 0000000000..dde29c9f78 --- /dev/null +++ b/packages/fakeyou.com/src/v2/view/pages/storyteller_studio_intro/StudioIntroResultPage.tsx @@ -0,0 +1,79 @@ +import React, { useState } from "react"; +import { SessionWrapper } from "@storyteller/components/src/session/SessionWrapper"; +import { SessionSubscriptionsWrapper } from "@storyteller/components/src/session/SessionSubscriptionsWrapper"; +import { Button, Container, Panel } from "components/common"; +import { usePrefixedDocumentTitle } from "common/UsePrefixedDocumentTitle"; +import { useHistory, useParams } from "react-router-dom"; +import "./StudioIntro.scss"; +import LoadingSpinner from "components/common/LoadingSpinner"; +import { useMedia } from "hooks"; +import { BucketConfig } from "@storyteller/components/src/api/BucketConfig"; +import { MediaFileType } from "@storyteller/components/src/api/media_files"; + +interface Props { + sessionWrapper: SessionWrapper; + sessionSubscriptionsWrapper: SessionSubscriptionsWrapper; +} + +// This page just needs to poll the job and show the video when it's done. + +function StudioIntroResultPage(props: Props) { + const { jobToken } = useParams<{ jobToken: string }>(); + const history = useHistory(); + // const [mediaToken, setMediaToken] = useState( + // "m_f5kp3hm74qeq16eq7536jb73jkbvkh" + // ); // Set the media token after polling success + const mediaToken = "m_f5kp3hm74qeq16eq7536jb73jkbvkh" + // const [jobExists, setJobExists] = useState(null); + + const [mediaFile, setMediaFile] = useState(); + + useMedia({ + mediaToken: mediaToken, + onSuccess: (res: any) => { + setMediaFile(res); + }, + }); + + const mediaLink = + mediaFile && new BucketConfig().getGcsUrl(mediaFile.public_bucket_path); + + usePrefixedDocumentTitle("Storyteller Studio"); + + if (!jobToken) { + history.push("/"); + } + + // Should also check if job actually exists or not + // + // if (!jobExists) { + // history.push("/"); + // } + + return ( + + +

Generation Result

+
+ +
+ {mediaToken ? ( +
+
+ {mediaToken ? ( +
+
+ ) : null} +
+ ); +} + +export { StudioIntroResultPage }; diff --git a/packages/fakeyou.com/src/v2/view/pages/storyteller_studio_intro/StudioVST/StudioVSTPage.tsx b/packages/fakeyou.com/src/v2/view/pages/storyteller_studio_intro/StudioVST/StudioVSTPage.tsx new file mode 100644 index 0000000000..18954a679e --- /dev/null +++ b/packages/fakeyou.com/src/v2/view/pages/storyteller_studio_intro/StudioVST/StudioVSTPage.tsx @@ -0,0 +1,162 @@ +import React, { useRef, useState } from "react"; +import { NavLink, useParams, useHistory } from "react-router-dom"; +import { + Button, + Container, + Panel, + SelectionBubbles, + TextArea, +} from "components/common"; +import VideoQuickTrim, { + QuickTrimData, +} from "components/common/VideoQuickTrim"; +import EnqueueVideoStyleTransfer from "@storyteller/components/src/api/video_styleTransfer"; +import { initialValues } from "./defaultValues"; +import { mapRequest, VSTType } from "./helpers"; +import LoadingSpinner from "components/common/LoadingSpinner"; +// import SectionAdvanceOptions from "./sectionAdvanceOptions"; + +export default function PageVSTApp() { + const { jobToken } = useParams<{ jobToken: string }>(); + const videoRef = useRef(null); + const history = useHistory(); + // const [mediaToken, setMediaToken] = useState( + // "m_wnnz9jey96dwhss9qk1891v9gmvnj8" // Currently hardcoded video media token value. Let a job polling function set the value when job returns media on success. + // ); + const mediaToken = "m_wnnz9jey96dwhss9qk1891v9gmvnj8"; + // const [jobExists, setJobExists] = useState(null); + const [vstValues, setVstValues] = useState({ + ...initialValues, + fileToken: mediaToken, + }); + + const handleOnChange = (val: { + [key: string]: number | string | boolean | undefined; + }) => { + setVstValues(curr => ({ ...curr, ...val })); + }; + + const handleGenerate = () => { + const request = mapRequest(vstValues); + EnqueueVideoStyleTransfer(request).then(res => { + if (res.success && res.inference_job_token) { + console.log("Job enqueued successfully", res.inference_job_token); + history.push(`/studio-intro/result/${res.inference_job_token}`); + } else { + console.log("Failed to enqueue job", res); + } + }); + }; + + if (videoRef?.current) { + const ve = videoRef.current; + ve.onloadedmetadata = () => { + const newValues: { + width?: number; + height?: number; + maxDuration?: number; + } = {}; + if (ve.duration) { + newValues.maxDuration = ve.duration; + } + setVstValues(curr => ({ + ...curr, + ...newValues, + })); + }; + } + + const styleMap: { [key: string]: string } = { + Anime: "weight_yqexh77ntqyawzgh9fzash798", // set SD Model Weight Tokens, currently hardcoded all to Anime style + Pixel: "weight_yqexh77ntqyawzgh9fzash798", + Painting: "weight_yqexh77ntqyawzgh9fzash798", + }; + + const handleStyleSelection = (selected: string) => { + const selectedSdModelToken = styleMap[selected]; + setVstValues(curr => ({ ...curr, sdModelToken: selectedSdModelToken })); + }; + + const styleOptions = Object.keys(styleMap); + + if (!jobToken) { + history.push("/"); + } + + // Should also check if job actually exists or not + // + // if (!jobExists) { + // history.push("/"); + // } + + return ( + + +

Style Your Scene

+
+ +
+
+ {vstValues.fileToken ? ( + + handleOnChange({ + trimStart: val.trimStartSeconds, + trimEnd: val.trimEndSeconds, + }) + } + /> + ) : ( +
+ +
+ )} +
+
+
+ + +
+ +