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..e48728a3d7 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,8 +97,8 @@ 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 }); @@ -107,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; @@ -125,6 +128,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 b3f3937599..19c6bbf023 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,16 @@ 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 + ) { + // 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 ( args.asset.maybe_media_subtype === MediaFileSubtype.StorytellerScene || args.asset.media_type === MediaFileType.SceneRon ) { diff --git a/packages/fakeyou.com/src/components/common/VideoPlayer/VideoPlayer.tsx b/packages/fakeyou.com/src/components/common/VideoPlayer/VideoPlayer.tsx index 125abbdac3..67104ba64d 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 debug = false; // || propsDebug; + if(debug) console.log(`reRENDER VideoPlayer @${String(Date.now()).slice(-5)}`) + const [compState, dispatchCompState] = useReducer(reducer, initialState); const videoRef = useRef(null); 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( +