Merge pull request #523 from storytold/feature/comp/playbar-w-trim

Feature/comp/playbar w trim
This commit is contained in:
wil authored and GitHub committed 2024-02-29 10:59:35 -05:00
commit d7b53c82b1
6 files changed
+157 -151

No files matched your search

@@ -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<HTMLDivElement>(null);
return(
<div className="trim-scrubber"
ref={ref}
style={{width: width + "%"}}
// onPointerDown={()=>{
// 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<HTMLDivElement>)=>{
// if(trimZoneRef.current && playbarRef.current && isScrubbingTrim){
// // console.log(fractionToPercentage(
// // (e.clientX - trimZoneRef.current.getBoundingClientRect().left) / (playbarRef.current.getBoundingClientRect().width)
// // ) + "%");
// }
// }}
>
<FontAwesomeIcon icon={faGripDots} />
</div>
);
}
@@ -4,12 +4,12 @@ import React, {
useLayoutEffect,
useState,
useRef,
PointerEvent,
// PointerEvent,
} from "react";
import {
faArrowsRepeat,
faPlay,
faPause,
faGripDots,
faVolume,
faVolumeSlash,
} from "@fortawesome/pro-solid-svg-icons";
@@ -19,7 +19,8 @@ import { VideoFakeyouProps } from "../VideoFakeyou/VideoFakeyou";
import { QuickTrimData, TrimStates, PlaybarStates } from "./types";
import { formatSecondsToHHMMSSCS } from "./helpers";
import "./styles.scss";
import TrimScrbber from "./TrimScrubber";
import './styles.scss'
interface VideoQuickTrimProps extends VideoFakeyouProps {
onSelect: (values: QuickTrimData) => void;
@@ -54,58 +55,65 @@ export default memo(function VideoQuickTrim({
const videoRef = useRef<HTMLVideoElement | null>(null);
const playbarRef = useRef<HTMLDivElement | null>(null);
const trimZoneRef = useRef<HTMLDivElement>(null);
const [{ playbarWidth, timeCursorOffset }, setPlaybarState] =
useState<PlaybarStates>(initialPlaybarState);
const [playpause, setPlaypause] = useState<"playing" | "paused" | "stopped">(
"paused"
);
const [{
playbarWidth,
timeCursorOffset
}, setPlaybarState] = useState<PlaybarStates>(initialPlaybarState);
const [playpause, setPlaypause] = useState<'playing'|'paused'|'ended'>('paused');
const [isMuted, setIsMuted] = useState<boolean>(false);
const [
{
canNotTrim,
trimStart,
// trimEnd,
trimDuration,
// isScrubbingTrim,
},
setTrimState,
] = useState<TrimStates>(initialTrimState);
const [isRepeatOn, setIsRepatOn] = useState<boolean>(true);
const [{
canNotTrim,
trimStart,
trimEnd,
trimDuration,
// isScrubbingTrim,
}, setTrimState] = useState<TrimStates>(initialTrimState);
const videoRefCallback = useCallback(
node => {
if (node !== null) {
// DOM node referenced by ref has changed and exists
videoRef.current = node;
node.onloadedmetadata = () => {
if (node.duration >= 3) {
setTrimState(curr => ({
...curr,
canNotTrim: false,
trimDuration: 3,
trimEnd: 3,
maxDuration: node.duration,
}));
onSelect({
trimStartSeconds: 0,
trimEndSeconds: 3,
});
}
};
node.ontimeupdate = (e: PointerEvent) => {
setPlaybarState(curr => ({
const videoRefCallback = useCallback(node => {
console.log("videoRefCallback");
if (node !== null) {
// DOM node referenced by ref has changed and exists
videoRef.current = node;
node.onloadedmetadata = ()=>{
if (node.duration >= 3){
setTrimState((curr)=>({
...curr,
timeCursorOffset:
(node.currentTime / node.duration) * (playbarWidth - 8),
canNotTrim: false,
trimDuration: 3,
trimEnd: 3,
maxDuration: node.duration
}));
};
} // else{} DOM node referenced by ref has been unmounted
},
[playbarWidth, onSelect]
); //END videoRefCallback
//TODO: this sbould be set in USEEFFECT
//IFF USEEFFECT starts working again
onSelect({
trimStartSeconds: 0,
trimEndSeconds: 3,
})
}
};
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, isRepeatOn, trimEnd]); //END videoRefCallback
function handleWindowResize() {
if (playbarRef.current !== null) {
@@ -133,33 +141,38 @@ export default memo(function VideoQuickTrim({
};
}, []);
const handleChangeTrimDuration = (selected: string) => {
if (!canNotTrim) {
setTrimState(curr => ({
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") {
const handlePlaypause = ()=>{
if (playpause === 'paused' || playpause === 'ended'){
videoRef.current?.play();
setPlaypause("playing");
} else {
}else{
videoRef.current?.pause();
setPlaypause("paused");
}
};
const trimZoneWidth = videoRef.current
? trimDuration > 0 && trimDuration < videoRef.current.duration
? (trimDuration / videoRef.current.duration) * 100
const trimScrubberWidth = videoRef.current
? trimDuration > 0 && trimDuration < videoRef.current.duration
? (trimDuration / videoRef.current.duration * 100)
: 100
: 0;
@@ -199,41 +212,10 @@ export default memo(function VideoQuickTrim({
{/* END of Video Wrapper */}
<div className="playbar" ref={playbarRefCallback}>
<div className="playbar-bg" />
<div
className="trimzone"
ref={trimZoneRef}
style={{ width: trimZoneWidth + "%" }}
// onPointerDown={()=>{
// 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<HTMLDivElement>)=>{
// if(trimZoneRef.current && playbarRef.current && isScrubbingTrim){
// // console.log(fractionToPercentage(
// // (e.clientX - trimZoneRef.current.getBoundingClientRect().left) / (playbarRef.current.getBoundingClientRect().width)
// // ) + "%");
// }
// }}
>
<FontAwesomeIcon icon={faGripDots} />
</div>
<div className="playcursor" style={{ left: timeCursorOffset + "px" }} />
</div>{" "}
{/* END of Playbar */}
<TrimScrbber width={trimScrubberWidth} />
<div className="playcursor" style={{left: timeCursorOffset+"px"}}/>
</div> {/* END of Playbar */}
<div className="d-flex w-100 justify-content-between mt-3 flex-wrap">
<div className="playpause-external d-flex align-items-center flex-wrap mb-2">
<Button
@@ -242,14 +224,20 @@ export default memo(function VideoQuickTrim({
variant="secondary"
onClick={handlePlaypause}
/>
<Button
className="button-repeat"
icon={faArrowsRepeat}
variant={isRepeatOn ? "primary":"secondary"}
onClick={()=>setIsRepatOn((curr)=>(!curr))}
/>
<Button
className="button-mute"
icon={isMuted ? faVolumeSlash : faVolume}
variant="secondary"
onClick={() => setIsMuted(curr => !curr)}
/>
<div className="playtime ms-2 d-flex">
<span>
<div className="playtime d-flex">
<span >
<p>
{`${formatSecondsToHHMMSSCS(
videoRef.current?.currentTime || 0
@@ -78,7 +78,7 @@ $playbar-height: 2rem;
background-color: $panel-inner-border;
border-radius: 0 0 0.5rem 0.5rem;
}
.trimzone {
.trim-scrubber{
position: absolute;
top: -1rem;
height: 3rem;
@@ -111,13 +111,15 @@ $playbar-height: 2rem;
height: 40px;
margin-right: 0.5rem;
}
.button-mute {
.button-mute, .button-repeat{
width: 40px;
height: 40px;
margin-right: 0.5rem;
}
.playtime {
span {
width: 60px;
.playtime{
font-size: smaller;
span{
width: 50px;
}
div {
width: 10px;
@@ -20,10 +20,10 @@ interface Props {
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, setMediaToken] = useState(
// "m_f5kp3hm74qeq16eq7536jb73jkbvkh"
// ); // Set the media token after polling success
const mediaToken = "m_f5kp3hm74qeq16eq7536jb73jkbvkh"
// const [jobExists, setJobExists] = useState<boolean | null>(null);
const [mediaFile, setMediaFile] = useState<MediaFileType>();
@@ -20,9 +20,10 @@ export default function PageVSTApp() {
const { jobToken } = useParams<{ jobToken: string }>();
const videoRef = useRef<HTMLVideoElement>(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, 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<boolean | null>(null);
const [vstValues, setVstValues] = useState<VSTType>({
...initialValues,
@@ -1,4 +1,7 @@
import React, { useRef, useState } from "react";
import React, {
// useRef,
useState,
} from "react";
import { NavLink } from 'react-router-dom';
import { useParams, useHistory } from "react-router-dom";
@@ -14,7 +17,6 @@ import EnqueueVideoStyleTransfer from "@storyteller/components/src/api/video_sty
import { Action, State } from "../../reducer";
// import { TableOfKeyValues } from "../../commons";
import { initialValues } from "./defaultValues";
import {
mapRequest,
@@ -33,7 +35,6 @@ export default function PageVSTApp({
parentPath: string;
dispatchPageState: (action: Action) => void;
}) {
const videoRef = useRef<HTMLVideoElement>(null);
const { mediaToken } = useParams<any>();
const [vstValues, setVstValues] = useState<VSTType>({
@@ -41,34 +42,6 @@ export default function PageVSTApp({
fileToken: pageState.mediaFileToken || mediaToken,
});
if(videoRef?.current){
if(debug) console.log("set-up video element listeners");
const ve = videoRef.current
ve.onloadedmetadata = () =>{
const newValues : {
width?: number;
height?: number;
maxDuration?: number;
} = {};
// if (ve.videoWidth && ve.videoHeight) {
// const aspectRatio = ve.videoWidth/ve.videoHeight
// if (aspectRatio > 1) newValues.width = 960;
// else if (aspectRatio < 1) newValues.height = 960
// else if (debug) console.log(`aspectRaio: ${aspectRatio}`);
// }
if(ve.duration){
newValues.maxDuration = ve.duration
// newValues.framesCap = newValues.maxFrames;
}
setVstValues((curr)=>({
...curr,
...newValues,
}))
}
}
const handleOnChange = (val:{[key: string]: number|string|boolean|undefined}) => {
setVstValues((curr)=>({...curr, ...val}));
}
@@ -119,13 +92,6 @@ export default function PageVSTApp({
}}
/>
</div>
{/* <div className="col-12 col-md-6">
<Label label={t("image.label.preview")}/>
{debug && <TableOfKeyValues keyValues={vstValues} height={400}/>}
</div>
</div>
<div className="row g-3 mb-4"> */}
<div className="col-12 col-md-6">
<TextArea
{...{