mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-09 04:35:25 +03:00
Handle onclick for video
This commit is contained in:
parent
0e22e39be2
commit
d7ed3ab4e8
@ -30,6 +30,7 @@ type DynamicVideoPlayerProps = {
|
|||||||
cameraPreviews: Preview[];
|
cameraPreviews: Preview[];
|
||||||
previewOnly?: boolean;
|
previewOnly?: boolean;
|
||||||
onControllerReady?: (controller: DynamicVideoController) => void;
|
onControllerReady?: (controller: DynamicVideoController) => void;
|
||||||
|
onClick?: () => void;
|
||||||
};
|
};
|
||||||
export default function DynamicVideoPlayer({
|
export default function DynamicVideoPlayer({
|
||||||
className,
|
className,
|
||||||
@ -38,6 +39,7 @@ export default function DynamicVideoPlayer({
|
|||||||
cameraPreviews,
|
cameraPreviews,
|
||||||
previewOnly = false,
|
previewOnly = false,
|
||||||
onControllerReady,
|
onControllerReady,
|
||||||
|
onClick,
|
||||||
}: DynamicVideoPlayerProps) {
|
}: DynamicVideoPlayerProps) {
|
||||||
const apiHost = useApiHost();
|
const apiHost = useApiHost();
|
||||||
const { data: config } = useSWR<FrigateConfig>("config");
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
@ -83,6 +85,8 @@ export default function DynamicVideoPlayer({
|
|||||||
);
|
);
|
||||||
}, [camera, config, previewOnly]);
|
}, [camera, config, previewOnly]);
|
||||||
|
|
||||||
|
const [hasRecordingAtTime, setHasRecordingAtTime] = useState(true);
|
||||||
|
|
||||||
// keyboard control
|
// keyboard control
|
||||||
|
|
||||||
const onKeyboardShortcut = useCallback(
|
const onKeyboardShortcut = useCallback(
|
||||||
@ -145,28 +149,35 @@ export default function DynamicVideoPlayer({
|
|||||||
// we only want to calculate this once
|
// we only want to calculate this once
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, []);
|
}, []);
|
||||||
const initialPreviewSource = useMemo(() => {
|
const initialPreview = useMemo(() => {
|
||||||
const preview = cameraPreviews.find(
|
return cameraPreviews.find(
|
||||||
(preview) =>
|
(preview) =>
|
||||||
preview.camera == camera &&
|
preview.camera == camera &&
|
||||||
Math.round(preview.start) >= timeRange.start &&
|
Math.round(preview.start) >= timeRange.start &&
|
||||||
Math.floor(preview.end) <= timeRange.end,
|
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
|
// we only want to calculate this once
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// 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
|
// state of playback player
|
||||||
|
|
||||||
@ -177,7 +188,9 @@ export default function DynamicVideoPlayer({
|
|||||||
};
|
};
|
||||||
}, [timeRange]);
|
}, [timeRange]);
|
||||||
const { data: recordings } = useSWR<Recording[]>(
|
const { data: recordings } = useSWR<Recording[]>(
|
||||||
previewOnly ? null : [`${camera}/recordings`, recordingParams],
|
previewOnly && onClick == undefined
|
||||||
|
? null
|
||||||
|
: [`${camera}/recordings`, recordingParams],
|
||||||
{ revalidateOnFocus: false },
|
{ revalidateOnFocus: false },
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -217,7 +230,9 @@ export default function DynamicVideoPlayer({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div
|
||||||
|
className={`relative ${className ?? ""} ${onClick ? (hasRecordingAtTime ? "cursor-pointer" : "") : ""}`}
|
||||||
|
>
|
||||||
{!previewOnly && (
|
{!previewOnly && (
|
||||||
<div
|
<div
|
||||||
className={`w-full relative ${
|
className={`w-full relative ${
|
||||||
@ -272,7 +287,7 @@ export default function DynamicVideoPlayer({
|
|||||||
autoPlay
|
autoPlay
|
||||||
playsInline
|
playsInline
|
||||||
muted
|
muted
|
||||||
onSeeked={() => controller.finishedSeeking()}
|
onSeeked={onPreviewSeeked}
|
||||||
onLoadedData={() => controller.previewReady()}
|
onLoadedData={() => controller.previewReady()}
|
||||||
onLoadStart={
|
onLoadStart={
|
||||||
previewOnly && onControllerReady
|
previewOnly && onControllerReady
|
||||||
@ -286,6 +301,9 @@ export default function DynamicVideoPlayer({
|
|||||||
<source src={currentPreview.src} type={currentPreview.type} />
|
<source src={currentPreview.src} type={currentPreview.type} />
|
||||||
)}
|
)}
|
||||||
</video>
|
</video>
|
||||||
|
{onClick && !hasRecordingAtTime && (
|
||||||
|
<div className="absolute inset-0 z-10 bg-black bg-opacity-60" />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -508,4 +526,16 @@ export class DynamicVideoController {
|
|||||||
this.previewRef.current?.pause();
|
this.previewRef.current?.pause();
|
||||||
this.readyToScrub = true;
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -98,9 +98,22 @@ export function DesktopRecordingView({
|
|||||||
videoPlayersRef.current[mainCamera].onPlayerTimeUpdate(undefined);
|
videoPlayersRef.current[mainCamera].onPlayerTimeUpdate(undefined);
|
||||||
videoPlayersRef.current[mainCamera].scrubToTimestamp(currentTime);
|
videoPlayersRef.current[mainCamera].scrubToTimestamp(currentTime);
|
||||||
videoPlayersRef.current[newCam].seekToTimestamp(currentTime, true);
|
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);
|
setMainCamera(newCam);
|
||||||
},
|
},
|
||||||
[currentTime, mainCamera],
|
[allCameras, currentTime, mainCamera],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -130,6 +143,14 @@ export function DesktopRecordingView({
|
|||||||
setPlayerReady(true);
|
setPlayerReady(true);
|
||||||
controller.onPlayerTimeUpdate((timestamp: number) => {
|
controller.onPlayerTimeUpdate((timestamp: number) => {
|
||||||
setCurrentTime(timestamp);
|
setCurrentTime(timestamp);
|
||||||
|
|
||||||
|
allCameras.forEach((otherCam) => {
|
||||||
|
if (cam != otherCam) {
|
||||||
|
videoPlayersRef.current[otherCam]?.scrubToTimestamp(
|
||||||
|
Math.floor(timestamp),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
controller.seekToTimestamp(startTime, true);
|
controller.seekToTimestamp(startTime, true);
|
||||||
@ -140,11 +161,7 @@ export function DesktopRecordingView({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div key={cam} className="aspect-video flex items-center">
|
||||||
key={cam}
|
|
||||||
className="aspect-video flex items-center"
|
|
||||||
onClick={() => onSelectCamera(cam)}
|
|
||||||
>
|
|
||||||
<DynamicVideoPlayer
|
<DynamicVideoPlayer
|
||||||
className="size-full"
|
className="size-full"
|
||||||
camera={cam}
|
camera={cam}
|
||||||
@ -156,13 +173,14 @@ export function DesktopRecordingView({
|
|||||||
setPlayerReady(true);
|
setPlayerReady(true);
|
||||||
controller.scrubToTimestamp(startTime);
|
controller.scrubToTimestamp(startTime);
|
||||||
}}
|
}}
|
||||||
|
onClick={() => onSelectCamera(cam)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</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
|
<EventReviewTimeline
|
||||||
segmentDuration={30}
|
segmentDuration={30}
|
||||||
timestampSpread={15}
|
timestampSpread={15}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user