From 326dec9ffa133c1bde4cbee0425ac6d6702f4a96 Mon Sep 17 00:00:00 2001 From: wil Date: Mon, 18 Mar 2024 11:20:16 -0400 Subject: [PATCH 01/11] pusing the videoplayer to studio vst onboarding --- .../src/components/common/VideoPlayer/VideoPlayer.tsx | 8 +------- packages/fakeyou.com/src/components/common/index.tsx | 4 ++-- .../storyteller_studio_intro/StudioVST/StudioVSTPage.tsx | 8 +++----- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/packages/fakeyou.com/src/components/common/VideoPlayer/VideoPlayer.tsx b/packages/fakeyou.com/src/components/common/VideoPlayer/VideoPlayer.tsx index 125abbdac3..dc1acc97a6 100644 --- a/packages/fakeyou.com/src/components/common/VideoPlayer/VideoPlayer.tsx +++ b/packages/fakeyou.com/src/components/common/VideoPlayer/VideoPlayer.tsx @@ -6,7 +6,6 @@ import React, { import { VideoFakeyou } from "components/common"; import { VideoFakeyouProps } from "../VideoFakeyou/VideoFakeyou"; -// import { QuickTrimData } from "./utilities"; import { reducer, @@ -27,21 +26,16 @@ import './styles.scss'; interface VideoPlayerProps extends VideoFakeyouProps { debug?: boolean height?: number - // onSelectTrim: (values: QuickTrimData) => void; - // trimStartSeconds: number; - // trimEndSeconds: number; } export const VideoPlayer = ({ debug: propsDebug = false, height=500, - // onSelectTrim, - // trimStartSeconds: propsTrimStartSeconds, - // trimEndSeconds : propsTrimEndSeconds, ...rest }: VideoPlayerProps) => { const debug = true || propsDebug; if(debug) console.log(`reRENDER VideoQuickTrim @${String(Date.now()).slice(-5)}`) + const [compState, dispatchCompState] = useReducer(reducer, initialState); const videoRef = useRef(null); diff --git a/packages/fakeyou.com/src/components/common/index.tsx b/packages/fakeyou.com/src/components/common/index.tsx index 1338990794..b6026317b8 100644 --- a/packages/fakeyou.com/src/components/common/index.tsx +++ b/packages/fakeyou.com/src/components/common/index.tsx @@ -51,7 +51,7 @@ import TintSpinner from "./TintSpinner"; import VideoBasic from "./VideoBasic"; import VideoFakeyou from "./VideoFakeyou"; import VideoInput from "./VideoInput"; -// import VideoQuickTrim, {QuickTrimType} from "./VideoQuickTrim"; +import { VideoPlayer } from "./VideoPlayer"; import WeightCoverImage from "./WeightCoverImage"; export type { SelectModalData }; @@ -114,6 +114,6 @@ export { VideoBasic, VideoFakeyou, VideoInput, + VideoPlayer, WeightCoverImage, - // VideoQuickTrim, }; 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 index ae917bb0b2..ee6d7015d4 100644 --- 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 @@ -2,10 +2,8 @@ import React, { useRef, useState } from "react"; import { v4 as uuidv4 } from "uuid"; import { NavLink, useParams, useHistory } from "react-router-dom"; import { Button, Container, Panel, TextArea } from "components/common"; -// import VideoQuickTrim, { -// QuickTrimData, -// } from "components/common/VideoQuickTrim"; -import { VideoFakeyou } from "components/common"; + +import { VideoPlayer } from "components/common"; import { useJobStatus, useInferenceJobs } from "hooks"; import { EnqueueVST, EnqueueVSTResponse } from "@storyteller/components/src/api/video_styleTransfer/Enqueue_VST"; import { initialValues } from "./defaultValues"; @@ -145,7 +143,7 @@ export default function PageVSTApp() {
{job.isSuccessful && job.maybe_result ? ( - ) : ( From 477007be868f4bf5387bcaff985f4abb663a27fa Mon Sep 17 00:00:00 2001 From: wil Date: Mon, 18 Mar 2024 13:47:12 -0400 Subject: [PATCH 02/11] common index is broken --- packages/fakeyou.com/src/components/common/index.tsx | 2 -- .../pages/storyteller_studio_intro/StudioVST/StudioVSTPage.tsx | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/fakeyou.com/src/components/common/index.tsx b/packages/fakeyou.com/src/components/common/index.tsx index b6026317b8..f70ec4689c 100644 --- a/packages/fakeyou.com/src/components/common/index.tsx +++ b/packages/fakeyou.com/src/components/common/index.tsx @@ -51,7 +51,6 @@ import TintSpinner from "./TintSpinner"; import VideoBasic from "./VideoBasic"; import VideoFakeyou from "./VideoFakeyou"; import VideoInput from "./VideoInput"; -import { VideoPlayer } from "./VideoPlayer"; import WeightCoverImage from "./WeightCoverImage"; export type { SelectModalData }; @@ -114,6 +113,5 @@ export { VideoBasic, VideoFakeyou, VideoInput, - VideoPlayer, WeightCoverImage, }; 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 index ee6d7015d4..2c91b42e0f 100644 --- 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 @@ -3,7 +3,7 @@ import { v4 as uuidv4 } from "uuid"; import { NavLink, useParams, useHistory } from "react-router-dom"; import { Button, Container, Panel, TextArea } from "components/common"; -import { VideoPlayer } from "components/common"; +import { VideoPlayer } from "components/common/VideoPlayer"; import { useJobStatus, useInferenceJobs } from "hooks"; import { EnqueueVST, EnqueueVSTResponse } from "@storyteller/components/src/api/video_styleTransfer/Enqueue_VST"; import { initialValues } from "./defaultValues"; From fb3ae40899428e9d94c21241cfe1b647298ba966 Mon Sep 17 00:00:00 2001 From: wil Date: Mon, 18 Mar 2024 16:14:38 -0400 Subject: [PATCH 03/11] trim player now scrub properly --- .../common/VideoPlayer/VideoPlayer.tsx | 4 +- .../VideoPlayerQuickTrim.tsx | 104 ++++++++ .../components/ControlBar/ButtonMute.tsx | 38 +++ .../components/ControlBar/ButtonPlaypause.tsx | 46 ++++ .../components/ControlBar/ControlBar.tsx | 36 +++ .../ControlBar/LabelTimeDuration.tsx | 42 ++++ .../components/ControlBar/index.tsx | 1 + .../components/LoadingDots/index.tsx | 4 + .../components/LoadingDots/styles.scss | 45 ++++ .../components/ScrubberBar/ProgressBar.tsx | 54 +++++ .../components/ScrubberBar/ScrubberBar.tsx | 34 +++ .../components/ScrubberBar/TimeCursor.tsx | 96 ++++++++ .../components/ScrubberBar/TrimScrubber.tsx | 111 +++++++++ .../components/ScrubberBar/index.tsx | 1 + .../components/ScrubberBar}/withScrubbing.tsx | 48 ++-- .../components/VideoTakeover.tsx | 86 +++++++ .../common/VideoPlayerQuickTrim/contexts.tsx | 15 ++ .../common/VideoPlayerQuickTrim/index.tsx | 2 + .../common/VideoPlayerQuickTrim/reducer.tsx | 59 +++++ .../styles.scss | 170 +++++++------- .../utilities.tsx | 0 .../common/VideoQuickTrim/VideoQuickTrim.tsx | 222 ------------------ .../VideoQuickTrim/components/ControlBar.tsx | 127 ---------- .../VideoQuickTrim/components/PlayCursor.tsx | 58 ----- .../VideoQuickTrim/components/ProgressBar.tsx | 142 ----------- .../components/TrimScrubber.tsx | 48 ---- .../common/VideoQuickTrim/contexts.tsx | 3 - .../components/common/VideoQuickTrim/index.ts | 5 - .../common/VideoQuickTrim/reducer.tsx | 218 ----------------- .../components/highorder/withScrubbing.tsx | 176 ++++++++++++++ .../view/pages/components_library/sandbox.tsx | 40 ++-- .../components/pageVSTApp/pageVSTApp.tsx | 19 +- 32 files changed, 1096 insertions(+), 958 deletions(-) create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/VideoPlayerQuickTrim.tsx create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/ButtonMute.tsx create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/ButtonPlaypause.tsx create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/ControlBar.tsx create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/LabelTimeDuration.tsx create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/index.tsx create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/LoadingDots/index.tsx create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/LoadingDots/styles.scss create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ScrubberBar/ProgressBar.tsx create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ScrubberBar/ScrubberBar.tsx create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ScrubberBar/TimeCursor.tsx create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ScrubberBar/TrimScrubber.tsx create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ScrubberBar/index.tsx rename packages/fakeyou.com/src/components/common/{VideoQuickTrim/components => VideoPlayerQuickTrim/components/ScrubberBar}/withScrubbing.tsx (78%) create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/VideoTakeover.tsx create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/contexts.tsx create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/index.tsx create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/reducer.tsx rename packages/fakeyou.com/src/components/common/{VideoQuickTrim => VideoPlayerQuickTrim}/styles.scss (50%) rename packages/fakeyou.com/src/components/common/{VideoQuickTrim => VideoPlayerQuickTrim}/utilities.tsx (100%) delete mode 100644 packages/fakeyou.com/src/components/common/VideoQuickTrim/VideoQuickTrim.tsx delete mode 100644 packages/fakeyou.com/src/components/common/VideoQuickTrim/components/ControlBar.tsx delete mode 100644 packages/fakeyou.com/src/components/common/VideoQuickTrim/components/PlayCursor.tsx delete mode 100644 packages/fakeyou.com/src/components/common/VideoQuickTrim/components/ProgressBar.tsx delete mode 100644 packages/fakeyou.com/src/components/common/VideoQuickTrim/components/TrimScrubber.tsx delete mode 100644 packages/fakeyou.com/src/components/common/VideoQuickTrim/contexts.tsx delete mode 100644 packages/fakeyou.com/src/components/common/VideoQuickTrim/index.ts delete mode 100644 packages/fakeyou.com/src/components/common/VideoQuickTrim/reducer.tsx create mode 100644 packages/fakeyou.com/src/components/highorder/withScrubbing.tsx diff --git a/packages/fakeyou.com/src/components/common/VideoPlayer/VideoPlayer.tsx b/packages/fakeyou.com/src/components/common/VideoPlayer/VideoPlayer.tsx index dc1acc97a6..67104ba64d 100644 --- a/packages/fakeyou.com/src/components/common/VideoPlayer/VideoPlayer.tsx +++ b/packages/fakeyou.com/src/components/common/VideoPlayer/VideoPlayer.tsx @@ -33,8 +33,8 @@ export const VideoPlayer = ({ height=500, ...rest }: VideoPlayerProps) => { - const debug = true || propsDebug; - if(debug) console.log(`reRENDER VideoQuickTrim @${String(Date.now()).slice(-5)}`) + const debug = false; // || propsDebug; + if(debug) console.log(`reRENDER VideoPlayer @${String(Date.now()).slice(-5)}`) const [compState, dispatchCompState] = useReducer(reducer, initialState); diff --git a/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/VideoPlayerQuickTrim.tsx b/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/VideoPlayerQuickTrim.tsx new file mode 100644 index 0000000000..479665a3e2 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/VideoPlayerQuickTrim.tsx @@ -0,0 +1,104 @@ +import React, { + useRef, + useCallback, + useReducer, +} from "react"; + +import { VideoFakeyou } from "components/common"; +import { VideoFakeyouProps } from "../VideoFakeyou/VideoFakeyou"; + +import { + reducer, + initialState, + ACTION_TYPES +} from "./reducer"; +import { + VideoElementContext, + TrimContext +} from "./contexts"; + +import { VideoTakeover } from "./components/VideoTakeover"; +import { ScrubberBar } from "./components/ScrubberBar"; +import { ControlBar } from "./components/ControlBar"; + +import './styles.scss'; + + +interface Props extends VideoFakeyouProps { + debug?: boolean; + height?: number; + trimStartMs?: number; + trimEndMs?: number; + onSelectTrim: ({ + trimStartMs,trimEndMs, + }:{ + trimStartMs: number, + trimEndMs:number, + })=>void +} + +export const VideoPlayerQuickTrim = ({ + debug: propsDebug = false, + height=500, + trimStartMs, + trimEndMs, + onSelectTrim, + ...rest +}: Props) => { + const debug = true || propsDebug; + if(debug) console.log(`reRENDER VP_QuickTrim start@${trimStartMs} end@${trimEndMs}`) + + const [compState, dispatchCompState] = useReducer(reducer, initialState); + + const videoRef = useRef(null); + const videoRefCallback = useCallback(node => { + function handleLoadedmetadata (){ + dispatchCompState({ + type: ACTION_TYPES.ON_LOADED_METADATA, + payload:{ videoDuration: node.duration,} + }); + }; + if (node !== null) { + // DOM node referenced by ref has changed and exists + videoRef.current = node; + node.addEventListener("loadedmetadata", handleLoadedmetadata); + } // else{} DOM node referenced by ref has been unmounted + + return()=>{ + node?.removeEventListener("loadedmetadata",handleLoadedmetadata); + } + + }, [ + // No Dependency ! + ]); //END videoRefCallback\ + + return( + + +
+
+ + + {/* components that takes over for spinner and warning message */} + +
{/* END of Video Wrapper */} + + +
+
+
+ ); +} + diff --git a/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/ButtonMute.tsx b/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/ButtonMute.tsx new file mode 100644 index 0000000000..d1e5d268c3 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/ButtonMute.tsx @@ -0,0 +1,38 @@ +import React, { + useContext, + useEffect, + useState +} from "react"; + +import { + faVolumeSlash, + faVolume +} from "@fortawesome/pro-solid-svg-icons"; + +import { Button } from "components/common"; +import { VideoElementContext } from '../../contexts'; + +export function ButtonMute (){ + const vidEl = useContext(VideoElementContext); + const [isMuted, setIsMuted] = useState(vidEl?.muted || false); + + const toggleMute = ()=>{ + setIsMuted((curr)=>(!curr)); + } + + useEffect(()=>{ + if(vidEl !==null){ + if (isMuted) vidEl.muted = true; + else vidEl.muted = false; + } + },[isMuted, vidEl]); + + return( +
- -
- ); - }else{ - console.log('TODO: ControlBar should rendering loading state instead of null'); - return null; - } - -}; - -function TimeLabel(){ - const vidEl = useContext(VideoElementContext); - const [currentTime, setCurrentTime] = useState(0); - const [duration, setDuration] = useState(0); - - useEffect(()=>{ - function handleTimeStamp(){ - setCurrentTime(vidEl?.currentTime ||0); - }; - function handleDuration(){ - setDuration(vidEl?.duration ||0) - }; - vidEl?.addEventListener("timeupdate", handleTimeStamp); - vidEl?.addEventListener("loadmetadata", handleDuration); - return()=>{ - vidEl?.removeEventListener("timeupdate",handleTimeStamp); - vidEl?.addEventListener("loadmetadata", handleDuration); - } - },[vidEl]); - return( -
- -

- {`${formatSecondsToHHMMSSCS(currentTime)}`} -

-
-
/
- -

- {`${formatSecondsToHHMMSSCS(duration)}`} -

-
-
- ); -} \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/PlayCursor.tsx b/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/PlayCursor.tsx deleted file mode 100644 index eb7af26f2f..0000000000 --- a/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/PlayCursor.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import React, { - useContext, - useState, - useEffect -} from 'react'; -import { withScrubbing, withScrubbingPropsI } from './withScrubbing'; - -import { VideoElementContext } from '../contexts'; -import { ONE_MS } from '../utilities'; - -interface PlayCursorPropsI extends withScrubbingPropsI{ - isRepeatOn: boolean; - playBoundStart: number; - playBoundEnd: number; - onChanged: (posPercent: number)=>void; -} - -export const PlayCursor = ({ - isRepeatOn, - playBoundStart, - playBoundEnd, - onChanged, - ...rest -}:PlayCursorPropsI)=>{ - const videoElement = useContext(VideoElementContext); - const [timeCursorOffset, setTimeCursorOffset] = useState(0); - const PlayCursorWithScrubbing = withScrubbing(()=>{ - return( -
- ); - }); - useEffect(()=>{ - const handleTimeCursorPosition = ()=>{ - if(videoElement!==null){ - if(isRepeatOn && ( - videoElement.currentTime < playBoundStart || - videoElement.currentTime > playBoundEnd - )){ - videoElement.currentTime = playBoundStart + ONE_MS - } - const newOffset = (videoElement.currentTime / videoElement.duration) * (rest.boundingWidth); - setTimeCursorOffset(newOffset); - } - }; - videoElement?.addEventListener("timeupdate", handleTimeCursorPosition); - return()=>{ - videoElement?.removeEventListener("timeupdate",handleTimeCursorPosition); - } - },[videoElement, isRepeatOn, playBoundStart, playBoundEnd, rest.boundingWidth]); - - return( - - ); -}; diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/ProgressBar.tsx b/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/ProgressBar.tsx deleted file mode 100644 index d3c1856d28..0000000000 --- a/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/ProgressBar.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import React, { - useState, - useEffect, - useContext, - useCallback, - useLayoutEffect, - useRef -} from 'react'; -import { - Action, - ACTION_TYPES, -} from '../reducer'; -import { TrimScrubber } from './TrimScrubber'; -import { PlayCursor } from './PlayCursor'; -import { VideoElementContext } from '../contexts'; - -export const ProgressBar = ({ - debug: propsDebug = false, - readyToMount, - isRepeatOn, - trimStartSeconds, - trimEndSeconds, - trimDuration, - playbarWidth, - scrubberWidth, - videoDuration, - onPlayCursorChanged, - handlePlaypause, - dispatchCompState -}:{ - debug?: boolean; - readyToMount: boolean; - isRepeatOn: boolean; - trimStartSeconds: number; - trimEndSeconds: number; - trimDuration: number; - playbarWidth: number; - scrubberWidth: number; - videoDuration: number; - onPlayCursorChanged: (newPos: number) => void; - handlePlaypause: (shouldPlay:boolean)=>void; - dispatchCompState: (action: Action) => void; -})=>{ - console.log(`ProgressBAR reRENDERING`) - const debug = false || propsDebug; - if(debug) console.log("reRENDERING --- Progress Bar"); - const playbarRef = useRef(null); - - const playbarRefCallback = useCallback(node => { - if (node !== null) { - playbarRef.current = node; - dispatchCompState({ - type: ACTION_TYPES.SET_PLAYBAR_LAYOUT, - payload: { - playbarWidth: node.getBoundingClientRect().width, - } - }) - }else{ - // console.log(node); - } - }, [dispatchCompState]); - - const handleWindowResize = useCallback(()=> { - if (playbarRef.current !== null && readyToMount){ - dispatchCompState({ - type: ACTION_TYPES.SET_PLAYBAR_LAYOUT, - payload: { - playbarWidth: playbarRef.current.getBoundingClientRect().width, - } - }); - } - },[dispatchCompState, readyToMount]); - useLayoutEffect(() => { - window.addEventListener("resize", handleWindowResize); - return () => { - window.removeEventListener("resize", handleWindowResize); - }; - }, [handleWindowResize]); - - if(readyToMount){ - // console.log('progress bar rendering'); - return( -
- - handlePlaypause(false)} - onChange={(newPos: number)=>{ - const newTrimStartSeconds = newPos / (playbarWidth) * videoDuration; - dispatchCompState({ - type: ACTION_TYPES.MOVE_TRIM, - payload: { - trimStartSeconds: newTrimStartSeconds, - trimEndSeconds: newTrimStartSeconds + trimDuration - } - }); - // handlePlaypause(true); - }} - /> - - {/* END of Playbar */}
- ); - }else{ - console.log('TODO: ProgressBar should rendering loading state instead of null'); - return null; - } -}; - -function Progress (){ - const vidEl = useContext(VideoElementContext); - const [buffered, setBuffered] = useState(0); - useEffect(()=>{ - function handleBuffer(){ - if(vidEl && vidEl.buffered.length > 0){ - setBuffered(vidEl.buffered.end(0)) - } - } - vidEl?.addEventListener("progress", handleBuffer); - },[vidEl]); - - if(vidEl){ - return( -
- - {/* */} -
- ); - } - else return null; -} \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/TrimScrubber.tsx b/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/TrimScrubber.tsx deleted file mode 100644 index 4101b22007..0000000000 --- a/packages/fakeyou.com/src/components/common/VideoQuickTrim/components/TrimScrubber.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React, { - memo, -} from 'react'; -import { - faGripDots -} from "@fortawesome/pro-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { withScrubbing, withScrubbingPropsI } from './withScrubbing'; - -interface TrimScrubberPropsI extends withScrubbingPropsI { - trimStartSeconds:number; - trimDuration: number; - videoDuration: number; - handlePlaypause: ()=> void; - onChange:(newPos:number)=>void -} -export const TrimScrubber = memo(({ - debug: propsDebug = false, - trimStartSeconds, - trimDuration, - videoDuration, - handlePlaypause, - onChange, - ...rest -}:TrimScrubberPropsI)=>{ - const debug = false || propsDebug; - console.log("trimScrubber reRENDERING"); - const TrimScrubberWithScrubbing = withScrubbing(() => { - return( -
- -
- ); - }); - - return ( - - ); -}); diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/contexts.tsx b/packages/fakeyou.com/src/components/common/VideoQuickTrim/contexts.tsx deleted file mode 100644 index bab30c20eb..0000000000 --- a/packages/fakeyou.com/src/components/common/VideoQuickTrim/contexts.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import {createContext} from 'react'; - -export const VideoElementContext = createContext(null); \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/index.ts b/packages/fakeyou.com/src/components/common/VideoQuickTrim/index.ts deleted file mode 100644 index 8f25d05b73..0000000000 --- a/packages/fakeyou.com/src/components/common/VideoQuickTrim/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import {VideoQuickTrim} from './VideoQuickTrim' -import { QuickTrimData } from './utilities'; - -export type { QuickTrimData }; -export default VideoQuickTrim; diff --git a/packages/fakeyou.com/src/components/common/VideoQuickTrim/reducer.tsx b/packages/fakeyou.com/src/components/common/VideoQuickTrim/reducer.tsx deleted file mode 100644 index beb8d5811d..0000000000 --- a/packages/fakeyou.com/src/components/common/VideoQuickTrim/reducer.tsx +++ /dev/null @@ -1,218 +0,0 @@ -import { - MIN_VID_DURATION, - TRIM_OPTIONS, - roundToMilliseconds -} from "./utilities"; - -export enum STATE_STATUSES { - INIT = "init", - VIDEO_METADATA_LOADED = "video_metadata_loaded", - LOAD_ORDER_ERROR = "load_order_error", - ACTION_TYPE_ERROR = "no_such_action_type" -}; - -export enum PLAYPUASE_STATES{ - NOT_READY = "not_ready", - READY = "ready", - PLAYING = "playing", - PAUSED = "paused", - ENDED = "ended", -} - -export type State = { - status: string; - errorMessage: string[]; - - playpause: string; - - canNotTrim: boolean | undefined; - trimDuration: number | undefined; - trimStartSeconds: number | undefined; - trimEndSeconds: number | undefined; - videoLoadProgress: TimeRanges | undefined; - videoDuration: number | undefined; - - playbarWidth: number | undefined; - scrubberWidth: number | undefined; - - isMuted: boolean; - isRepeatOn: boolean; -}; - -export const initialState = { - status: STATE_STATUSES.INIT, - errorMessage : [], - playpause: PLAYPUASE_STATES.NOT_READY, - canNotTrim: undefined, - trimDuration: undefined, - trimStartSeconds: undefined, - trimEndSeconds: undefined, - videoLoadProgress: undefined, - videoDuration: undefined, - playbarWidth: undefined, - scrubberWidth: undefined, - timeCursorOffset: undefined, - isMuted: false, - isRepeatOn: true, -} - -export enum ACTION_TYPES { - RESET = "reset", - TOGGLE_REPEAT = "toggle_repeat", - TOGGLE_MUTE = "toggle_mute", - ON_LOADED_METADATA = "on_loaded_metadata", - MOVE_TIMECURSOR = "move_timecursor", - SET_PLAYPUASE = "set_playpause", - SET_PLAYBAR_LAYOUT = "set_playbar_layout", - SET_TRIM_DURATION = "set_trim_duration", - SET_VIDEO_LOAD_PROGRESS = "set_video_load_progress", - MOVE_TRIM = "move_trim" -} - -export type Action = - | {type: ACTION_TYPES.RESET} - | {type: ACTION_TYPES.TOGGLE_REPEAT, payload?:{isRepeatOn: boolean}} - | {type: ACTION_TYPES.TOGGLE_MUTE} - | {type: ACTION_TYPES.ON_LOADED_METADATA, payload: {videoDuration: number}} - | {type: ACTION_TYPES.MOVE_TIMECURSOR, payload:{ timeCursorOffset: number}} - | {type: ACTION_TYPES.SET_PLAYPUASE, payload:{ playpause: string}} - | {type: ACTION_TYPES.SET_PLAYBAR_LAYOUT, payload:{ playbarWidth: number}} - | {type: ACTION_TYPES.SET_TRIM_DURATION, payload:{ trimDurationString: string}} - | {type: ACTION_TYPES.SET_VIDEO_LOAD_PROGRESS, payload: { - videoLoadProgress: TimeRanges - }} - | {type: ACTION_TYPES.MOVE_TRIM, payload:{ - trimStartSeconds: number, - trimEndSeconds: number, - }} -; - -export function reducer(state: State, action: Action): State { - console.log("VideoQuickTrim State Dispatch"); - console.log(action); - - switch(action.type){ - case ACTION_TYPES.SET_PLAYPUASE:{ - return {...state, ...action.payload}; - } - case ACTION_TYPES.TOGGLE_REPEAT:{ - if(action.payload){ - return {...state, isRepeatOn: action.payload.isRepeatOn} - } - return {...state, isRepeatOn: !state.isRepeatOn} - } - case ACTION_TYPES.TOGGLE_MUTE:{ - return {...state, isMuted: !state.isMuted} - } - case ACTION_TYPES.ON_LOADED_METADATA:{ - if(action.payload.videoDuration >= MIN_VID_DURATION){ - return{ - ...state, - status: STATE_STATUSES.VIDEO_METADATA_LOADED, - videoDuration: action.payload.videoDuration, - canNotTrim: false, - trimDuration: 3, - trimStartSeconds: 0, - trimEndSeconds: 3, - } - }else{ - return { - ...state, - videoDuration: action.payload.videoDuration, - canNotTrim: true - } - } - } - case ACTION_TYPES.SET_VIDEO_LOAD_PROGRESS:{ - return{ - ...state, - videoLoadProgress: action.payload.videoLoadProgress - }; - } - case ACTION_TYPES.SET_PLAYBAR_LAYOUT:{ - const newWidth = action.payload.playbarWidth; - // const prevOffset = state.timeCursorOffset || 0; - if(state.trimDuration && state.videoDuration){ - return { - ...state, - playpause: PLAYPUASE_STATES.READY, - playbarWidth: newWidth, - scrubberWidth: newWidth * (state.trimDuration / state.videoDuration), - // timeCursorOffset: (newWidth-TIME_CURSOR_WIDTH)*(prevOffset/ state.videoDuration) - }; - }else{ - return{ - ...state, - status: STATE_STATUSES.LOAD_ORDER_ERROR, - errorMessage: [...state.errorMessage, 'Setting playbar layout before video and trim are loaded'] - } - } - } - case ACTION_TYPES.SET_TRIM_DURATION:{ - const selected = action.payload.trimDurationString; - if( state.canNotTrim !== undefined - && state.canNotTrim === false - && state.playbarWidth !==undefined - && state.videoDuration !== undefined - && TRIM_OPTIONS[selected] <= state.videoDuration - && state.trimStartSeconds !== undefined - ){ - let newTrimStart = state.trimStartSeconds; - let newTrimEnd = state.trimStartSeconds + TRIM_OPTIONS[selected]; - if (newTrimEnd > state.videoDuration){ - newTrimEnd = state.videoDuration; - if (state.videoDuration - TRIM_OPTIONS[selected] >=0) { - newTrimStart = roundToMilliseconds(state.videoDuration - TRIM_OPTIONS[selected]); - }else{ - newTrimStart = 0; - } - } - return{ - ...state, - trimDuration: newTrimEnd-newTrimStart, - trimStartSeconds: newTrimStart, - trimEndSeconds: newTrimEnd, - scrubberWidth: (newTrimEnd-newTrimStart)/state.videoDuration * state.playbarWidth, - } - }else{ - return { - ...state, - status: STATE_STATUSES.LOAD_ORDER_ERROR, - errorMessage: [...state.errorMessage, 'Setting trim duration before video and trim are loaded'] - } - } - } - case ACTION_TYPES.MOVE_TRIM:{ - return{ - ...state, - trimStartSeconds: roundToMilliseconds(action.payload.trimStartSeconds), - trimEndSeconds: roundToMilliseconds(action.payload.trimEndSeconds), - } - } - // case ACTION_TYPES.MOVE_TIMECURSOR:{ - // return { - // ...state, - // timeCursorOffset: action.payload.timeCursorOffset - // } - // } - case ACTION_TYPES.RESET:{ - return initialState - } - default:{ - return { - ...state, - status: STATE_STATUSES.ACTION_TYPE_ERROR, - errorMessage: [...state.errorMessage, "Reducer Action switch's default case is reached"] - } - } - } -}; - -/* - const trimScrubberWidth = videoRef.current && playbarWidth > 0 - ? trimDuration > 0 && trimDuration < videoRef.current.duration - ? (trimDuration / videoRef.current.duration * playbarWidth) - : playbarWidth - : 0; -*/ - diff --git a/packages/fakeyou.com/src/components/highorder/withScrubbing.tsx b/packages/fakeyou.com/src/components/highorder/withScrubbing.tsx new file mode 100644 index 0000000000..aa9ece22fc --- /dev/null +++ b/packages/fakeyou.com/src/components/highorder/withScrubbing.tsx @@ -0,0 +1,176 @@ +import React,{ + memo, + useEffect, + useRef, + useState, + useLayoutEffect, +} from 'react'; + +export interface withScrubbingPropsI { + debug?: boolean + boundingWidth: number; + scrubberWidth: number; + hitboxPadding?: number; + scrubPosition?: number; // scrubber location as px + styleOverride?: {[key: string]: string|number }; + onScrubStart?: ()=>void; + //only deal with event side effecrts + onScrubEnd?: (newPos: number)=>void; + //return scrubber location as px +} + +type withSrcubbingStates = { + currLeftOffset: number, + prevLeftOffset: number; + pointerStartPos: number; +} + +export const withScrubbing =

(Component: React.ComponentType

) => memo(({ + debug: propsDebug = false, + boundingWidth, + scrubberWidth, + hitboxPadding = 0, + scrubPosition: propsLeftOffset = 0, + styleOverride = {}, + onScrubStart, + onScrubEnd, + ...rest +}: withScrubbingPropsI) => { + // const debug = true || propsDebug; + + + const refEl = useRef(null); + // const refListener = useRef(Date.now()); + + // if (debug) console.log(`${refListener.current} withSCRUBBING reRENDERING!! `); + + const [{ + currLeftOffset, pointerStartPos, + prevLeftOffset + }, setStates] = useState({ + currLeftOffset: propsLeftOffset, + prevLeftOffset: propsLeftOffset, + pointerStartPos: -1 // negative denotes pointer not engaged + }); + + useLayoutEffect(() => { + // if (debug) console.log(`withSCRUBBING useLAYOUTeffect!! `); + function handleScrubStart (e: PointerEvent) { + // if (debug) console.log(`${refListener.current} withSCRUBBING SCRUB_START!! `); + e.preventDefault(); + e.stopPropagation(); + if(refEl.current){ + if(refEl.current.contains(e.target as Node)){ + setStates((curr)=>({ + ...curr, + pointerStartPos: e.clientX + })); + + if(onScrubStart) onScrubStart(); + window.addEventListener("pointermove", handleScrubMove); + return true; + } + } + }; + function handleScrubEnd (e: PointerEvent){ + // if (debug) console.log(`${refListener.current} withSCRUBBING SCRUB_END!! `); + e.preventDefault(); + e.stopPropagation(); + + window.removeEventListener("pointermove", handleScrubMove); + setStates((curr)=>({ + ...curr, + pointerStartPos: -1, + prevLeftOffset: curr.currLeftOffset, + setBySelf: Date.now(), + })); + }; + function handleScrubMove (e: PointerEvent){ + // if (debug) console.log(`${refListener.current} withSCRUBBING SCRUB_MOVE!! `); + e.preventDefault(); + e.stopPropagation(); + + setStates((curr)=>{ + if(curr.pointerStartPos >= 0 && curr.pointerStartPos!==null){ + let newLeftOffset = curr.prevLeftOffset + e.clientX - curr.pointerStartPos; + if (newLeftOffset + scrubberWidth > boundingWidth) { + newLeftOffset = boundingWidth - scrubberWidth; + }else if(newLeftOffset < 0) { + newLeftOffset = 0; + } + if(newLeftOffset !== curr.currLeftOffset){ + return{ + ...curr, + currLeftOffset: newLeftOffset, + } + } + } + return curr; + }); + }; + + // if(!(window as any)[`listener-id-${refListener.current}`]){ + // (window as any)[`listender-id-${refListener.current}`] = true; + window.addEventListener("pointerdown", handleScrubStart); + window.addEventListener("pointerup", handleScrubEnd); + return () => { + // (window as any)[`listener-id-${refListener.current}`] = false; + window.removeEventListener("pointerdown", handleScrubStart); + window.removeEventListener("pointerup", handleScrubEnd); + window.removeEventListener("pointermove", handleScrubMove); + }; + // } + }, [scrubberWidth, boundingWidth, onScrubStart]); + + // useEffect(()=>{ + // // if (debug) console.log(`${refListener.current} withSCRUBBING useEFFECT for scrubStart!! `); + // if(onScrubStart && pointerStartPos > 0){ + // onScrubStart(); + // } + // },[pointerStartPos, onScrubStart]); + + useEffect(()=>{ + // if (debug) console.log(`${refListener.current} withSCRUBBING useEFFECT for scrubEend!! `); + if(onScrubEnd && boundingWidth > 0 && prevLeftOffset >= 0 && propsLeftOffset !== prevLeftOffset){ + onScrubEnd(prevLeftOffset); + } + },[propsLeftOffset, prevLeftOffset, boundingWidth, onScrubEnd]); + + useEffect(()=>{ + // this takes a forced reset on leftoffset + setStates((curr)=>{ + if(curr.prevLeftOffset === curr.currLeftOffset + && propsLeftOffset !== curr.prevLeftOffset + && propsLeftOffset !== curr.prevLeftOffset + ){ + return { + ...curr, + currLeftOffset: propsLeftOffset, // in pixels + prevLeftOffset: propsLeftOffset, //in pixels + } + }else{ + return curr + } + }); + }, [propsLeftOffset]) + + return( +

=0 ? 'grabbing': 'grab', + ...styleOverride + }} + > + +
+ ); +}); diff --git a/packages/fakeyou.com/src/v2/view/pages/components_library/sandbox.tsx b/packages/fakeyou.com/src/v2/view/pages/components_library/sandbox.tsx index f1694cfe2f..d3c90db8f7 100644 --- a/packages/fakeyou.com/src/v2/view/pages/components_library/sandbox.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/components_library/sandbox.tsx @@ -1,7 +1,9 @@ import React, { - // useState + useState } from 'react'; import { VideoPlayer } from 'components/common/VideoPlayer'; +import { VideoPlayerQuickTrim } from 'components/common/VideoPlayerQuickTrim'; + import { NavLink } from 'react-router-dom'; import {Container, Panel} from 'components/common'; @@ -9,37 +11,33 @@ import {Container, Panel} from 'components/common'; export default function ComponentSandbox( {parentPath}:{parentPath:string} ){ - // const [state1, setState1] = useState(undefined); - // const [state2, setState2] = useState(undefined); - // const handleCallback = (val:any)=>{ - // console.log('onChange is triggered') - // console.log(val); - // setState1(val.trimStartSeconds); - // setState2(val.trimEndSeconds); - // } + const [state1, setState1] = useState(undefined); + const [state2, setState2] = useState(undefined); + const handleCallback = (val:any)=>{ + console.log('onChange is triggered') + console.log(val); + setState1(val.trimStartMs); + setState2(val.trimEndMs); + } return ( ← Back

Sandbox

-

- {/*

{`Start Seconds: ${state1}`}

-

{`Start Seconds: ${state2}`}

*/} - {/*
-
- -
*/} +

{`Start Seconds: ${state1}`}

+

{`Start Seconds: ${state2}`}

); } \ No newline at end of file diff --git a/packages/fakeyou.com/src/v2/view/pages/video_styletransfer/components/pageVSTApp/pageVSTApp.tsx b/packages/fakeyou.com/src/v2/view/pages/video_styletransfer/components/pageVSTApp/pageVSTApp.tsx index 0c42975b65..5a8b636695 100644 --- a/packages/fakeyou.com/src/v2/view/pages/video_styletransfer/components/pageVSTApp/pageVSTApp.tsx +++ b/packages/fakeyou.com/src/v2/view/pages/video_styletransfer/components/pageVSTApp/pageVSTApp.tsx @@ -3,9 +3,7 @@ import { NavLink } from "react-router-dom"; import { useParams, useHistory } from "react-router-dom"; import { Button, Panel, TextArea } from "components/common"; -import VideoQuickTrim, { - QuickTrimData, -} from "components/common/VideoQuickTrim"; +import {VideoPlayerQuickTrim} from "components/common/VideoPlayerQuickTrim"; import EnqueueVideoStyleTransfer from "@storyteller/components/src/api/video_styleTransfer"; import { Action, State } from "../../reducer"; @@ -75,14 +73,17 @@ export default function PageVSTApp({
- { + onSelectTrim={(val: { + trimStartMs: number; + trimEndMs: number; + }) =>{ handleOnChange({ - trimStart: val.trimStartSeconds, - trimEnd: val.trimEndSeconds, + trimStart: val.trimStartMs, + trimEnd: val.trimEndMs, }) }} onResponse={res => { From a5665b7444edf676e42c5f4521dffd9be3ea0f5c Mon Sep 17 00:00:00 2001 From: wil Date: Mon, 18 Mar 2024 16:32:13 -0400 Subject: [PATCH 04/11] even more optimized and smoothed rerendering on the trim scrubber --- .../components/ScrubberBar/TrimScrubber.tsx | 48 +++++++------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ScrubberBar/TrimScrubber.tsx b/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ScrubberBar/TrimScrubber.tsx index 40ca8bee87..07740a9b5a 100644 --- a/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ScrubberBar/TrimScrubber.tsx +++ b/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ScrubberBar/TrimScrubber.tsx @@ -1,10 +1,10 @@ import React, { useCallback, useContext, - useEffect, + // useEffect, useLayoutEffect, useState, - // useRef, + useRef, } from 'react'; import { faGripDots @@ -45,22 +45,14 @@ export const TrimScrubber = ({ } return 0 }, [videoElement, trimValues]); - const [{scrubberWidth, scrubberPosition}, setScrubberValues] = useState<{ - scrubberWidth:number; - scrubberPosition: number; - }>({ - scrubberWidth: calcScrubberWidth(), - scrubberPosition: calcScrubberPosition() - }); + const [scrubberWidth, setScrubberWidth] = useState(calcScrubberWidth()); + const scrubberPosition = useRef(calcScrubberPosition()); + const handleWindowResize = useCallback(()=> { - if (videoElement !== null){ - setScrubberValues((curr)=>({ - ...curr, - scrubberWidth:calcScrubberWidth() - })); - } - },[videoElement, calcScrubberWidth]); + setScrubberWidth(calcScrubberWidth()); + },[calcScrubberWidth]); + useLayoutEffect(()=>{ window.addEventListener("resize", handleWindowResize); return () => { @@ -68,13 +60,6 @@ export const TrimScrubber = ({ }; }, [handleWindowResize]) - useEffect(()=>{ - setScrubberValues((curr)=>({ - scrubberPosition:calcScrubberPosition(), - scrubberWidth:calcScrubberWidth() - })); - },[videoElement, calcScrubberPosition, calcScrubberWidth]); - const TrimScrubberWithScrubbing = withScrubbing(() => { return(
@@ -84,16 +69,19 @@ export const TrimScrubber = ({ }); const handleOnScrubEnd = useCallback((newPos: number)=>{ - // console.log("onScrubEnd"); + scrubberPosition.current = newPos; + if(videoElement !== null && trimValues !== null){ const boundingWidth = videoElement.getBoundingClientRect().width const newStartTime = Math.round(newPos / boundingWidth * videoElement.duration * 1000); - trimValues.onChange({ - trimStartMs: newStartTime, - trimEndMs: newStartTime + trimValues.trimDurationMs, - }); + if(trimValues.trimStartMs !== newStartTime){ + trimValues.onChange({ + trimStartMs: newStartTime, + trimEndMs: newStartTime + trimValues.trimDurationMs, + }); + } } - }, [videoElement, trimValues]) + }, []) return ( From 90b0e9b88d6cd066eb6d04fce20e71f761d13c75 Mon Sep 17 00:00:00 2001 From: wil Date: Mon, 18 Mar 2024 16:56:52 -0400 Subject: [PATCH 05/11] selection bubbles are doing quite well --- .../components/ControlBar/ControlBar.tsx | 8 +++-- .../components/ControlBar/SelectTrims.tsx | 31 +++++++++++++++++++ .../components/ScrubberBar/TrimScrubber.tsx | 30 ++++++++++++------ .../common/VideoPlayerQuickTrim/utilities.tsx | 13 +++----- .../view/pages/components_library/sandbox.tsx | 4 +-- 5 files changed, 62 insertions(+), 24 deletions(-) create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/SelectTrims.tsx diff --git a/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/ControlBar.tsx b/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/ControlBar.tsx index d16d2b9117..ea6be72e49 100644 --- a/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/ControlBar.tsx +++ b/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/ControlBar.tsx @@ -1,10 +1,10 @@ -import React, { -} from 'react'; +import React from 'react'; import { STATE_STATUSES } from "../../reducer"; import { ButtonPlaypause } from './ButtonPlaypause'; import { ButtonMute } from './ButtonMute'; import { LabelTimeDuration } from "./LabelTimeDuration" +import { SelectTrim } from './SelectTrims'; import { LoadingDots } from '../LoadingDots'; export const ControlBar = ({ debug: propsDebug = false, @@ -16,14 +16,16 @@ export const ControlBar = ({ const debug = false || propsDebug; if (debug) console.log("reRENDERING ------ ControlBar"); + if (status === STATE_STATUSES.VIDEO_METADATA_LOADED){ return(
+
- +
); } diff --git a/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/SelectTrims.tsx b/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/SelectTrims.tsx new file mode 100644 index 0000000000..830d5f2911 --- /dev/null +++ b/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/SelectTrims.tsx @@ -0,0 +1,31 @@ +import React, { + useCallback, + useContext, +} from 'react'; + +import { TrimContext } from '../../contexts'; +import { TRIM_OPTIONS } from '../../utilities'; +import { SelectionBubbles } from 'components/common'; + +export const SelectTrim = ()=>{ + const trimValues = useContext(TrimContext); + + const handleSetTrimDuration = useCallback((selected: string)=>{ + console.log(`trimDurationString: ${selected}`); + if (trimValues!==null) { + trimValues.onChange({ + trimStartMs: trimValues.trimStartMs, + trimEndMs: trimValues.trimStartMs + TRIM_OPTIONS[selected], + // trimDurationMs: TRIM_OPTIONS[selected], + }); + } + }, [trimValues]); + + return( + + ); +} \ No newline at end of file diff --git a/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ScrubberBar/TrimScrubber.tsx b/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ScrubberBar/TrimScrubber.tsx index 07740a9b5a..a79ef3f2f6 100644 --- a/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ScrubberBar/TrimScrubber.tsx +++ b/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ScrubberBar/TrimScrubber.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useContext, - // useEffect, + useEffect, useLayoutEffect, useState, useRef, @@ -30,12 +30,6 @@ export const TrimScrubber = ({ const videoElement = useContext(VideoElementContext); const trimValues = useContext(TrimContext); - const calcScrubberPosition = useCallback(()=>{ - if (videoElement !== null && trimValues !== null){ - return (trimValues.trimStartMs / (videoElement.duration * 1000) * videoElement.getBoundingClientRect().width); - } - return 0; - }, [videoElement, trimValues]); const calcScrubberWidth = useCallback(()=>{ if (videoElement!==null && trimValues !==null){ const bound = videoElement.getBoundingClientRect().width; @@ -46,8 +40,16 @@ export const TrimScrubber = ({ return 0 }, [videoElement, trimValues]); const [scrubberWidth, setScrubberWidth] = useState(calcScrubberWidth()); - const scrubberPosition = useRef(calcScrubberPosition()); - + useEffect(()=>{ + if (trimValues !== null){ + const newWidth = calcScrubberWidth(); + if (newWidth !== scrubberWidth){ + setScrubberWidth(newWidth); + } + } + },[trimValues]); + + const handleWindowResize = useCallback(()=> { setScrubberWidth(calcScrubberWidth()); @@ -68,6 +70,14 @@ export const TrimScrubber = ({ ); }); + + const calcScrubberPosition = useCallback(()=>{ + if (videoElement !== null && trimValues !== null){ + return (trimValues.trimStartMs / (videoElement.duration * 1000) * videoElement.getBoundingClientRect().width); + } + return 0; + }, [videoElement, trimValues]); + const scrubberPosition = useRef(calcScrubberPosition()); const handleOnScrubEnd = useCallback((newPos: number)=>{ scrubberPosition.current = newPos; @@ -81,7 +91,7 @@ export const TrimScrubber = ({ }); } } - }, []) + }, [videoElement, trimValues]) return ( (undefined); const [state2, setState2] = useState(undefined); const handleCallback = (val:any)=>{ - console.log('onChange is triggered') - console.log(val); + // console.log('onChange is triggered') + // console.log(val); setState1(val.trimStartMs); setState2(val.trimEndMs); } From 6445d1204dda76186fb3a386f296f09075249d9d Mon Sep 17 00:00:00 2001 From: wil Date: Mon, 18 Mar 2024 17:02:23 -0400 Subject: [PATCH 06/11] fix netlify gates --- .../components/ScrubberBar/TrimScrubber.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ScrubberBar/TrimScrubber.tsx b/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ScrubberBar/TrimScrubber.tsx index a79ef3f2f6..579aa3fb7c 100644 --- a/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ScrubberBar/TrimScrubber.tsx +++ b/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ScrubberBar/TrimScrubber.tsx @@ -43,11 +43,12 @@ export const TrimScrubber = ({ useEffect(()=>{ if (trimValues !== null){ const newWidth = calcScrubberWidth(); - if (newWidth !== scrubberWidth){ - setScrubberWidth(newWidth); - } + setScrubberWidth((currWidth)=>{ + if (newWidth !== currWidth) return newWidth; + else return currWidth; + }); } - },[trimValues]); + },[trimValues, calcScrubberWidth]); From 7dd8b473bed48720f424a9216aa98b51618c4577 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Mon, 18 Mar 2024 18:49:13 -0400 Subject: [PATCH 07/11] repair behavior of scene files with glb type --- .../components/src/api/media_files/GetMedia.ts | 2 ++ .../components/src/api/media_files/GetMediaFile.ts | 2 ++ .../components/common/Scene3D/GetEngineUrl.test.ts | 14 ++++++++++++-- .../src/components/common/Scene3D/GetEngineUrl.ts | 13 ++++++++++++- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/packages/components/src/api/media_files/GetMedia.ts b/packages/components/src/api/media_files/GetMedia.ts index 6ac38903fe..175bd099c8 100644 --- a/packages/components/src/api/media_files/GetMedia.ts +++ b/packages/components/src/api/media_files/GetMedia.ts @@ -3,11 +3,13 @@ import { UserDetailsLight } from "../_common/UserDetailsLight"; import { MediaFileType } from "../_common/enums/MediaFileType"; import { WeightCategory } from "../_common/enums/WeightCategory"; import { WeightType } from "../_common/enums/WeightType"; +import { MediaFileClass } from "../enums/MediaFileClass"; import { MediaFileSubtype } from "../enums/MediaFileSubtype"; export interface MediaFile { token: string; media_type: MediaFileType; + media_class: MediaFileClass | null; maybe_media_subtype: MediaFileSubtype | null; public_bucket_path: string; maybe_batch_token: string; diff --git a/packages/components/src/api/media_files/GetMediaFile.ts b/packages/components/src/api/media_files/GetMediaFile.ts index 2930b87778..427a47a783 100644 --- a/packages/components/src/api/media_files/GetMediaFile.ts +++ b/packages/components/src/api/media_files/GetMediaFile.ts @@ -3,12 +3,14 @@ import { UserDetailsLight } from "../_common/UserDetailsLight"; import { MediaFileType } from "../_common/enums/MediaFileType"; import { WeightCategory } from "../_common/enums/WeightCategory"; import { WeightType } from "../_common/enums/WeightType"; +import { MediaFileClass } from "../enums/MediaFileClass"; import { MediaFileSubtype } from "../enums/MediaFileSubtype"; // import { Weight } from "../weights/GetWeight"; export interface MediaFile { token: string; media_type: MediaFileType; + media_class: MediaFileClass | null; maybe_media_subtype: MediaFileSubtype | null; public_bucket_path: string; maybe_creator_user: UserDetailsLight | null; diff --git a/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts b/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts index 12da61f571..f60ab56ce2 100644 --- a/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts +++ b/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts @@ -5,6 +5,7 @@ import { WeightCategory } from "@storyteller/components/src/api/_common/enums/We import { WeightType } from "@storyteller/components/src/api/_common/enums/WeightType"; import { MediaFileSubtype } from "@storyteller/components/src/api/enums/MediaFileSubtype"; import { MediaFile } from "@storyteller/components/src/api/media_files/GetMedia"; +import { MediaFileClass } from "@storyteller/components/src/api/enums/MediaFileClass"; // These tests are important - constructing Storyteller Engine URLs is complicated, // and we want to make sure we test the various cases that occur in production. @@ -55,6 +56,7 @@ describe('media files', () => { mediaFile = { token: "MEDIA_FILE_TOKEN", media_type: MediaFileType.GLB, + media_class: MediaFileClass.Unknown, maybe_media_subtype: null, maybe_prompt_token: null, public_bucket_path: "path/to/file", @@ -95,13 +97,21 @@ describe('media files', () => { expect(url).toEqual("https://engine.fakeyou.com/?mode=studio&scene=remote://MEDIA_FILE_TOKEN.scn.ron"); }); - test('from media subtype', () => { - mediaFile.media_type = MediaFileType.Audio; // NB: Not the real time; forcing test to act on subtype. + test('from media subtype and media type (.scn.ron)', () => { + mediaFile.media_type = MediaFileType.SceneRon; mediaFile.maybe_media_subtype = MediaFileSubtype.StorytellerScene; const url = GetEngineUrl({mode: EngineMode.Studio, asset: mediaFile }); expect(url).toEqual("https://engine.fakeyou.com/?mode=studio&scene=remote://MEDIA_FILE_TOKEN.scn.ron"); }); + + test('from media subtype and media type (.glb)', () => { + mediaFile.media_type = MediaFileType.GLB; + mediaFile.maybe_media_subtype = MediaFileSubtype.StorytellerScene; + + const url = GetEngineUrl({mode: EngineMode.Studio, asset: mediaFile }); + expect(url).toEqual("https://engine.fakeyou.com/?mode=studio&scene=remote://MEDIA_FILE_TOKEN.glb"); + }); }); // Production examples: diff --git a/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.ts b/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.ts index b3f3937599..49d735f680 100644 --- a/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.ts +++ b/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.ts @@ -59,7 +59,18 @@ export function GetEngineUrl(args: GetEngineUrlArgs) : string { // NB: Storyteller Engine makes the API call to load the scene. // We don't need to pass the bucket path. // The engine, does, however, need a `.scn.ron` file extension. - const sceneUrlRef = `remote://${args.asset.token}.scn.ron`; + let extension; + + switch (args.asset.media_type) { + case MediaFileType.SceneRon: + extension = "scn.ron"; + break; + case MediaFileType.GLB: + default: + extension = "glb"; + break; + } + const sceneUrlRef = `remote://${args.asset.token}.${extension}`; engineUrl += `&scene=${sceneUrlRef}`; } else if ( args.asset.maybe_media_subtype === MediaFileSubtype.Mixamo From cf2871c129da67d35a5de601aa27b58d718e95f8 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Mon, 18 Mar 2024 19:17:48 -0400 Subject: [PATCH 08/11] fix glb + scene urls --- .../src/components/common/Scene3D/GetEngineUrl.test.ts | 3 ++- .../src/components/common/Scene3D/GetEngineUrl.ts | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts b/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts index f60ab56ce2..b2d08a08be 100644 --- a/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts +++ b/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts @@ -107,10 +107,11 @@ describe('media files', () => { test('from media subtype and media type (.glb)', () => { mediaFile.media_type = MediaFileType.GLB; + mediaFile.media_class = MediaFileClass.Scene; mediaFile.maybe_media_subtype = MediaFileSubtype.StorytellerScene; const url = GetEngineUrl({mode: EngineMode.Studio, asset: mediaFile }); - expect(url).toEqual("https://engine.fakeyou.com/?mode=studio&scene=remote://MEDIA_FILE_TOKEN.glb"); + expect(url).toEqual("https://engine.fakeyou.com/?mode=studio&sceneImport=remote://MEDIA_FILE_TOKEN.glb"); }); }); diff --git a/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.ts b/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.ts index 49d735f680..170bc38e20 100644 --- a/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.ts +++ b/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.ts @@ -4,6 +4,7 @@ import { EngineMode } from "./EngineMode"; import { MediaFileSubtype } from "@storyteller/components/src/api/enums/MediaFileSubtype"; import { MediaFileType } from "@storyteller/components/src/api/_common/enums/MediaFileType"; import { BucketConfig } from "@storyteller/components/src/api/BucketConfig"; +import { MediaFileClass } from "@storyteller/components/src/api/enums/MediaFileClass"; // Storyteller Engine parameters // These are documented here: @@ -52,6 +53,15 @@ export function GetEngineUrl(args: GetEngineUrlArgs) : string { const bucketAssetUrl = new BucketConfig().getGcsUrl(args.asset.public_bucket_path); if ( + args.asset.media_type === MediaFileType.GLB && + args.asset.media_class === MediaFileClass.Scene && + args.asset.maybe_media_subtype === MediaFileSubtype.StorytellerScene + ) { + // TODO: GLB docs + // NB: Not sure if `maybe_media_subtype` may or may not be required? + const sceneUrlRef = `remote://${args.asset.token}.glb`; + engineUrl += `&sceneImport=${sceneUrlRef}`; + } else if ( args.asset.maybe_media_subtype === MediaFileSubtype.StorytellerScene || args.asset.media_type === MediaFileType.SceneRon ) { From 41e8a418e71cfb4ccdb2edfc922b417ca07a56fb Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Mon, 18 Mar 2024 19:34:54 -0400 Subject: [PATCH 09/11] remove an invalid URL type from engine url mapping --- .../common/Scene3D/GetEngineUrl.test.ts | 18 +++++++++--------- .../components/common/Scene3D/GetEngineUrl.ts | 18 ++++-------------- 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts b/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts index b2d08a08be..14fb4514b8 100644 --- a/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts +++ b/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts @@ -104,15 +104,6 @@ describe('media files', () => { const url = GetEngineUrl({mode: EngineMode.Studio, asset: mediaFile }); expect(url).toEqual("https://engine.fakeyou.com/?mode=studio&scene=remote://MEDIA_FILE_TOKEN.scn.ron"); }); - - test('from media subtype and media type (.glb)', () => { - mediaFile.media_type = MediaFileType.GLB; - mediaFile.media_class = MediaFileClass.Scene; - mediaFile.maybe_media_subtype = MediaFileSubtype.StorytellerScene; - - const url = GetEngineUrl({mode: EngineMode.Studio, asset: mediaFile }); - expect(url).toEqual("https://engine.fakeyou.com/?mode=studio&sceneImport=remote://MEDIA_FILE_TOKEN.glb"); - }); }); // Production examples: @@ -136,6 +127,15 @@ describe('media files', () => { const url = GetEngineUrl({mode: EngineMode.Viewer, asset: mediaFile }); expect(url).toEqual("https://engine.fakeyou.com/?mode=viewer&sceneImport=https://storage.googleapis.com/dev-vocodes-public/path/to/file.gltf"); }); + + test('glb with scene media_class and storyteller_scene subtype', () => { + mediaFile.media_type = MediaFileType.GLB; + mediaFile.media_class = MediaFileClass.Scene; + mediaFile.maybe_media_subtype = MediaFileSubtype.StorytellerScene; + + const url = GetEngineUrl({mode: EngineMode.Studio, asset: mediaFile }); + expect(url).toEqual("https://engine.fakeyou.com/?mode=studio&sceneImport=remote://MEDIA_FILE_TOKEN.glb"); + }); }); // Production examples: diff --git a/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.ts b/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.ts index 170bc38e20..19c6bbf023 100644 --- a/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.ts +++ b/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.ts @@ -57,8 +57,9 @@ export function GetEngineUrl(args: GetEngineUrlArgs) : string { args.asset.media_class === MediaFileClass.Scene && args.asset.maybe_media_subtype === MediaFileSubtype.StorytellerScene ) { - // TODO: GLB docs - // NB: Not sure if `maybe_media_subtype` may or may not be required? + // GLB Scenes + // NB: Not sure if `maybe_media_subtype` is required to be "storyteller_scene". + // We'll require it in the file for now, but may loosen this restriction. const sceneUrlRef = `remote://${args.asset.token}.glb`; engineUrl += `&sceneImport=${sceneUrlRef}`; } else if ( @@ -69,18 +70,7 @@ export function GetEngineUrl(args: GetEngineUrlArgs) : string { // NB: Storyteller Engine makes the API call to load the scene. // We don't need to pass the bucket path. // The engine, does, however, need a `.scn.ron` file extension. - let extension; - - switch (args.asset.media_type) { - case MediaFileType.SceneRon: - extension = "scn.ron"; - break; - case MediaFileType.GLB: - default: - extension = "glb"; - break; - } - const sceneUrlRef = `remote://${args.asset.token}.${extension}`; + const sceneUrlRef = `remote://${args.asset.token}.scn.ron`; engineUrl += `&scene=${sceneUrlRef}`; } else if ( args.asset.maybe_media_subtype === MediaFileSubtype.Mixamo From 54714634c7c9fb30f9424419937b77eeb5ccb6c3 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Mon, 18 Mar 2024 19:38:23 -0400 Subject: [PATCH 10/11] docs --- .../src/components/common/Scene3D/GetEngineUrl.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts b/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts index 14fb4514b8..e48728a3d7 100644 --- a/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts +++ b/packages/fakeyou.com/src/components/common/Scene3D/GetEngineUrl.test.ts @@ -109,6 +109,7 @@ describe('media files', () => { // Production examples: // [without subtype] https://feature-mvp--fakeyou.netlify.app/media/m_tcj2zzncmvmn32f0yavmys5jdrc8cc (Majora's Mask GLB) // [scene_import subtype] https://feature-mvp--fakeyou.netlify.app/media/m_a504ma0n7vv3y80bw7bvgx7q2cecmb (Goron GLB) + // [scene media_class] https://feature-mvp--fakeyou.netlify.app/media/m_2yw1ytwec9wj8y74k3kc26grn4q341 (Joel's island GLB) describe('generic scene file (not storyteller studio scene)', () => { test('glb without subtype', () => { mediaFile.media_type = MediaFileType.GLB; From 3d9cab35786cbe6d6cf410f50c70f5a30afd216e Mon Sep 17 00:00:00 2001 From: wil Date: Mon, 18 Mar 2024 21:29:32 -0400 Subject: [PATCH 11/11] repeat button done --- .../components/ControlBar/ButtonRepeat.tsx | 53 +++++++++++++++++++ .../components/ControlBar/ControlBar.tsx | 2 + .../components/ControlBar/SelectTrims.tsx | 2 +- .../components/ScrubberBar/ProgressBar.tsx | 2 +- .../components/ScrubberBar/TimeCursor.tsx | 13 ++--- 5 files changed, 60 insertions(+), 12 deletions(-) create mode 100644 packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/ButtonRepeat.tsx diff --git a/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/ButtonRepeat.tsx b/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/ButtonRepeat.tsx new file mode 100644 index 0000000000..dc8dcd05cd --- /dev/null +++ b/packages/fakeyou.com/src/components/common/VideoPlayerQuickTrim/components/ControlBar/ButtonRepeat.tsx @@ -0,0 +1,53 @@ +import React, { + useContext, + useEffect, + useState +} from "react"; + +import { + faArrowsRepeat +} from "@fortawesome/pro-solid-svg-icons"; + +import { Button } from "components/common"; +import { ONE_MS } from "../../utilities"; +import { + VideoElementContext, + TrimContext +} from '../../contexts'; + + +export function ButtonRepeat (){ + const vidEl = useContext(VideoElementContext); + const trimVals = useContext(TrimContext); + const [isRepeatOn, setIsRepeat] = useState(true); + + const toggleRepeat = ()=>{ + setIsRepeat((curr)=>(!curr)); + } + + useEffect(()=>{ + function handleRepeat(){ + if(isRepeatOn && vidEl !==null && trimVals!==null){ + const currentMs = Math.round(vidEl.currentTime * 1000) + if(currentMs > trimVals.trimEndMs + || currentMs < trimVals.trimStartMs + ){ + vidEl.currentTime = trimVals.trimStartMs/1000 + ONE_MS + } + } + } + vidEl?.addEventListener("timeupdate", handleRepeat); + return ()=>{ + vidEl?.removeEventListener("timeupdate", handleRepeat); + }; + },[isRepeatOn, vidEl, trimVals]); + + return( +