Handle onclick for video

This commit is contained in:
Nicolas Mowen 2024-03-04 09:56:11 -07:00
parent 0e22e39be2
commit d7ed3ab4e8
2 changed files with 70 additions and 22 deletions

View File

@ -30,6 +30,7 @@ type DynamicVideoPlayerProps = {
cameraPreviews: Preview[];
previewOnly?: boolean;
onControllerReady?: (controller: DynamicVideoController) => void;
onClick?: () => void;
};
export default function DynamicVideoPlayer({
className,
@ -38,6 +39,7 @@ export default function DynamicVideoPlayer({
cameraPreviews,
previewOnly = false,
onControllerReady,
onClick,
}: DynamicVideoPlayerProps) {
const apiHost = useApiHost();
const { data: config } = useSWR<FrigateConfig>("config");
@ -83,6 +85,8 @@ export default function DynamicVideoPlayer({
);
}, [camera, config, previewOnly]);
const [hasRecordingAtTime, setHasRecordingAtTime] = useState(true);
// keyboard control
const onKeyboardShortcut = useCallback(
@ -145,28 +149,35 @@ export default function DynamicVideoPlayer({
// we only want to calculate this once
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const initialPreviewSource = useMemo(() => {
const preview = cameraPreviews.find(
const initialPreview = useMemo(() => {
return cameraPreviews.find(
(preview) =>
preview.camera == camera &&
Math.round(preview.start) >= timeRange.start &&
Math.floor(preview.end) <= timeRange.end,
);
if (preview) {
return {
src: preview.src,
type: preview.type,
};
} else {
return undefined;
}
// we only want to calculate this once
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const [currentPreview, setCurrentPreview] = useState(initialPreviewSource);
const [currentPreview, setCurrentPreview] = useState(initialPreview);
const onPreviewSeeked = useCallback(() => {
if (!controller) {
return;
}
controller.finishedSeeking();
if (currentPreview && previewOnly && previewRef.current && onClick) {
setHasRecordingAtTime(
controller.hasRecordingAtTime(
currentPreview.start + previewRef.current.currentTime,
),
);
}
}, [controller, currentPreview, onClick, previewOnly]);
// state of playback player
@ -177,7 +188,9 @@ export default function DynamicVideoPlayer({
};
}, [timeRange]);
const { data: recordings } = useSWR<Recording[]>(
previewOnly ? null : [`${camera}/recordings`, recordingParams],
previewOnly && onClick == undefined
? null
: [`${camera}/recordings`, recordingParams],
{ revalidateOnFocus: false },
);
@ -217,7 +230,9 @@ export default function DynamicVideoPlayer({
}
return (
<div className={className}>
<div
className={`relative ${className ?? ""} ${onClick ? (hasRecordingAtTime ? "cursor-pointer" : "") : ""}`}
>
{!previewOnly && (
<div
className={`w-full relative ${
@ -272,7 +287,7 @@ export default function DynamicVideoPlayer({
autoPlay
playsInline
muted
onSeeked={() => controller.finishedSeeking()}
onSeeked={onPreviewSeeked}
onLoadedData={() => controller.previewReady()}
onLoadStart={
previewOnly && onControllerReady
@ -286,6 +301,9 @@ export default function DynamicVideoPlayer({
<source src={currentPreview.src} type={currentPreview.type} />
)}
</video>
{onClick && !hasRecordingAtTime && (
<div className="absolute inset-0 z-10 bg-black bg-opacity-60" />
)}
</div>
);
}
@ -508,4 +526,16 @@ export class DynamicVideoController {
this.previewRef.current?.pause();
this.readyToScrub = true;
}
hasRecordingAtTime(time: number): boolean {
if (!this.recordings || this.recordings.length == 0) {
return false;
}
return (
this.recordings.find(
(segment) => segment.start_time <= time && segment.end_time >= time,
) != undefined
);
}
}

View File

@ -98,9 +98,22 @@ export function DesktopRecordingView({
videoPlayersRef.current[mainCamera].onPlayerTimeUpdate(undefined);
videoPlayersRef.current[mainCamera].scrubToTimestamp(currentTime);
videoPlayersRef.current[newCam].seekToTimestamp(currentTime, true);
videoPlayersRef.current[newCam].onPlayerTimeUpdate(
(timestamp: number) => {
setCurrentTime(timestamp);
allCameras.forEach((cam) => {
if (cam != newCam) {
videoPlayersRef.current[cam]?.scrubToTimestamp(
Math.floor(timestamp),
);
}
});
},
);
setMainCamera(newCam);
},
[currentTime, mainCamera],
[allCameras, currentTime, mainCamera],
);
return (
@ -130,6 +143,14 @@ export function DesktopRecordingView({
setPlayerReady(true);
controller.onPlayerTimeUpdate((timestamp: number) => {
setCurrentTime(timestamp);
allCameras.forEach((otherCam) => {
if (cam != otherCam) {
videoPlayersRef.current[otherCam]?.scrubToTimestamp(
Math.floor(timestamp),
);
}
});
});
controller.seekToTimestamp(startTime, true);
@ -140,11 +161,7 @@ export function DesktopRecordingView({
}
return (
<div
key={cam}
className="aspect-video flex items-center"
onClick={() => onSelectCamera(cam)}
>
<div key={cam} className="aspect-video flex items-center">
<DynamicVideoPlayer
className="size-full"
camera={cam}
@ -156,13 +173,14 @@ export function DesktopRecordingView({
setPlayerReady(true);
controller.scrubToTimestamp(startTime);
}}
onClick={() => onSelectCamera(cam)}
/>
</div>
);
})}
</div>
<div className="absolute overflow-hidden w-56 inset-y-0 right-0 cursor-pointer">
<div className="absolute overflow-hidden w-56 inset-y-0 right-0">
<EventReviewTimeline
segmentDuration={30}
timestampSpread={15}