refactor search detail dialog to use new generic video player component

This commit is contained in:
Josh Hawkins 2024-10-13 11:45:51 -05:00
parent 3a9af74f54
commit 4c35a0b4e6

View File

@ -6,7 +6,7 @@ import { useFormattedTimestamp } from "@/hooks/use-date-utils";
import { getIconForLabel } from "@/utils/iconUtil"; import { getIconForLabel } from "@/utils/iconUtil";
import { useApiHost } from "@/api"; import { useApiHost } from "@/api";
import { Button } from "../../ui/button"; import { Button } from "../../ui/button";
import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import axios from "axios"; import axios from "axios";
import { toast } from "sonner"; import { toast } from "sonner";
import { Textarea } from "../../ui/textarea"; import { Textarea } from "../../ui/textarea";
@ -21,7 +21,6 @@ import {
DialogTitle, DialogTitle,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { Event } from "@/types/event"; import { Event } from "@/types/event";
import HlsVideoPlayer from "@/components/player/HlsVideoPlayer";
import { baseUrl } from "@/api/baseUrl"; import { baseUrl } from "@/api/baseUrl";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import ActivityIndicator from "@/components/indicators/activity-indicator"; import ActivityIndicator from "@/components/indicators/activity-indicator";
@ -62,8 +61,7 @@ import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch";
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import useImageLoaded from "@/hooks/use-image-loaded"; import useImageLoaded from "@/hooks/use-image-loaded";
import ImageLoadingIndicator from "@/components/indicators/ImageLoadingIndicator"; import ImageLoadingIndicator from "@/components/indicators/ImageLoadingIndicator";
import { useResizeObserver } from "@/hooks/resize-observer"; import { GenericVideoPlayer } from "@/components/player/GenericVideoPlayer";
import { VideoResolutionType } from "@/types/live";
const SEARCH_TABS = [ const SEARCH_TABS = [
"details", "details",
@ -597,99 +595,45 @@ function ObjectSnapshotTab({
type VideoTabProps = { type VideoTabProps = {
search: SearchResult; search: SearchResult;
}; };
function VideoTab({ search }: VideoTabProps) {
const [isLoading, setIsLoading] = useState(true);
const videoRef = useRef<HTMLVideoElement | null>(null);
const endTime = useMemo(() => search.end_time ?? Date.now() / 1000, [search]);
export function VideoTab({ search }: VideoTabProps) {
const navigate = useNavigate(); const navigate = useNavigate();
const { data: reviewItem } = useSWR<ReviewSegment>([ const { data: reviewItem } = useSWR<ReviewSegment>([
`review/event/${search.id}`, `review/event/${search.id}`,
]); ]);
const endTime = useMemo(() => search.end_time ?? Date.now() / 1000, [search]);
const containerRef = useRef<HTMLDivElement | null>(null); const source = `${baseUrl}vod/${search.camera}/start/${search.start_time}/end/${endTime}/index.m3u8`;
const [{ width: containerWidth, height: containerHeight }] =
useResizeObserver(containerRef);
const [videoResolution, setVideoResolution] = useState<VideoResolutionType>({
width: 0,
height: 0,
});
const videoAspectRatio = useMemo(() => {
return videoResolution.width / videoResolution.height || 16 / 9;
}, [videoResolution]);
const containerAspectRatio = useMemo(() => {
return containerWidth / containerHeight || 16 / 9;
}, [containerWidth, containerHeight]);
const videoDimensions = useMemo(() => {
if (!containerWidth || !containerHeight)
return { width: "100%", height: "100%" };
if (containerAspectRatio > videoAspectRatio) {
const height = containerHeight;
const width = height * videoAspectRatio;
return { width: `${width}px`, height: `${height}px` };
} else {
const width = containerWidth;
const height = width / videoAspectRatio;
return { width: `${width}px`, height: `${height}px` };
}
}, [containerWidth, containerHeight, videoAspectRatio, containerAspectRatio]);
return ( return (
<div ref={containerRef} className="relative flex h-full w-full flex-col"> <GenericVideoPlayer source={source}>
<div className="relative flex flex-grow items-center justify-center"> {reviewItem && (
{(isLoading || !reviewItem) && (
<ActivityIndicator className="absolute left-1/2 top-1/2 z-10 -translate-x-1/2 -translate-y-1/2" />
)}
<div <div
className="relative flex items-center justify-center" className={cn(
style={videoDimensions} "absolute top-2 z-10 flex items-center",
> isIOS ? "right-8" : "right-2",
<HlsVideoPlayer
videoRef={videoRef}
currentSource={`${baseUrl}vod/${search.camera}/start/${search.start_time}/end/${endTime}/index.m3u8`}
hotKeys
visible
frigateControls={false}
fullscreen={false}
supportsFullscreen={false}
onPlaying={() => setIsLoading(false)}
setFullResolution={setVideoResolution}
/>
{!isLoading && reviewItem && (
<div
className={cn(
"absolute top-2 z-10 flex items-center",
isIOS ? "right-8" : "right-2",
)}
>
<Tooltip>
<TooltipTrigger>
<Chip
className="cursor-pointer rounded-md bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500"
onClick={() => {
if (reviewItem?.id) {
const params = new URLSearchParams({
id: reviewItem.id,
}).toString();
navigate(`/review?${params}`);
}
}}
>
<FaHistory className="size-4 text-white" />
</Chip>
</TooltipTrigger>
<TooltipContent side="left">View in History</TooltipContent>
</Tooltip>
</div>
)} )}
>
<Tooltip>
<TooltipTrigger>
<Chip
className="cursor-pointer rounded-md bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500"
onClick={() => {
if (reviewItem?.id) {
const params = new URLSearchParams({
id: reviewItem.id,
}).toString();
navigate(`/review?${params}`);
}
}}
>
<FaHistory className="size-4 text-white" />
</Chip>
</TooltipTrigger>
<TooltipContent side="left">View in History</TooltipContent>
</Tooltip>
</div> </div>
</div> )}
</div> </GenericVideoPlayer>
); );
} }