Use constant aspect ratio for review grid

This commit is contained in:
Nick Mowen 2023-12-13 09:16:32 -07:00
parent 80428a8cc1
commit 8278d9abd8
3 changed files with 76 additions and 62 deletions

View File

@ -35,7 +35,7 @@ export default function HistoryCard({
} }
return ( return (
<Card className="my-2 mr-2 bg-secondary"> <Card className="my-2 mr-2 bg-secondary w-[284px]">
<PreviewThumbnailPlayer <PreviewThumbnailPlayer
camera={timeline.camera} camera={timeline.camera}
allPreviews={allPreviews || []} allPreviews={allPreviews || []}

View File

@ -4,62 +4,78 @@ import useSWR from "swr";
import { useCallback, useMemo, useRef } from "react"; import { useCallback, useMemo, useRef } from "react";
import { useApiHost } from "@/api"; import { useApiHost } from "@/api";
import Player from "video.js/dist/types/player"; import Player from "video.js/dist/types/player";
import { AspectRatio } from "../ui/aspect-ratio";
type PreviewPlayerProps = { type PreviewPlayerProps = {
camera: string, camera: string;
allPreviews: Preview[], allPreviews: Preview[];
startTs: number, startTs: number;
} };
type Preview = { type Preview = {
camera: string, camera: string;
src: string, src: string;
type: string, type: string;
start: number, start: number;
end: number, end: number;
} };
export default function PreviewThumbnailPlayer({ camera, allPreviews, startTs }: PreviewPlayerProps) { export default function PreviewThumbnailPlayer({
const { data: config } = useSWR('config'); camera,
const playerRef = useRef<Player | null>(null); allPreviews,
const apiHost = useApiHost(); startTs,
}: PreviewPlayerProps) {
const { data: config } = useSWR("config");
const playerRef = useRef<Player | null>(null);
const apiHost = useApiHost();
const relevantPreview = useMemo(() => { const relevantPreview = useMemo(() => {
return Object.values(allPreviews || []).find( return Object.values(allPreviews || []).find(
(preview) => preview.camera == camera && preview.start < startTs && preview.end > startTs (preview) =>
); preview.camera == camera &&
}, [allPreviews, camera, startTs]); preview.start < startTs &&
preview.end > startTs
const onHover = useCallback((isHovered: Boolean) => {
if (!relevantPreview || !playerRef.current) {
return;
}
if (isHovered) {
playerRef.current.play();
} else {
playerRef.current.pause();
playerRef.current.currentTime(startTs - relevantPreview.start);
}
},
[relevantPreview, startTs]
); );
}, [allPreviews, camera, startTs]);
if (!relevantPreview) { const onHover = useCallback(
return ( (isHovered: Boolean) => {
<img className={getThumbWidth(camera, config)} src={`${apiHost}api/preview/${camera}/${startTs}/thumbnail.jpg`} /> if (!relevantPreview || !playerRef.current) {
); return;
} }
if (isHovered) {
playerRef.current.play();
} else {
playerRef.current.pause();
playerRef.current.currentTime(startTs - relevantPreview.start);
}
},
[relevantPreview, startTs]
);
if (!relevantPreview) {
return ( return (
<div <AspectRatio
className={getThumbWidth(camera, config)} ratio={16 / 9}
onMouseEnter={() => onHover(true)} className="bg-black flex justify-center items-center"
onMouseLeave={() => onHover(false)}
> >
<img src={`${apiHost}api/preview/${camera}/${startTs}/thumbnail.jpg`} />
</AspectRatio>
);
}
return (
<AspectRatio
ratio={16 / 9}
className="bg-black flex justify-center items-center"
onMouseEnter={() => onHover(true)}
onMouseLeave={() => onHover(false)}
>
<div className={`${getThumbWidth(camera, config)}`}>
<VideoPlayer <VideoPlayer
options={{ options={{
preload: 'auto', preload: "auto",
autoplay: false, autoplay: false,
controls: false, controls: false,
muted: true, muted: true,
@ -67,7 +83,7 @@ export default function PreviewThumbnailPlayer({ camera, allPreviews, startTs }:
sources: [ sources: [
{ {
src: `${relevantPreview.src}`, src: `${relevantPreview.src}`,
type: 'video/mp4', type: "video/mp4",
}, },
], ],
}} }}
@ -82,18 +98,16 @@ export default function PreviewThumbnailPlayer({ camera, allPreviews, startTs }:
}} }}
/> />
</div> </div>
); </AspectRatio>
} );
}
function getThumbWidth(camera: string, config: FrigateConfig) { function getThumbWidth(camera: string, config: FrigateConfig) {
const detect = config.cameras[camera].detect; const detect = config.cameras[camera].detect;
if (detect.width / detect.height > 2) {
return 'w-[320px]';
}
if (detect.width / detect.height < 1.4) { if (detect.width / detect.height < 1.4) {
return 'w-[200px]'; return "w-[200px]";
} }
return 'w-[240px]'; return "w-full";
} }

View File

@ -12,24 +12,24 @@ export default defineConfig({
server: { server: {
proxy: { proxy: {
'/api': { '/api': {
target: 'http://localhost:5000', target: 'http://192.168.50.106:5000',
ws: true, ws: true,
}, },
'/vod': { '/vod': {
target: 'http://localhost:5000' target: 'http://192.168.50.106:5000'
}, },
'/clips': { '/clips': {
target: 'http://localhost:5000' target: 'http://192.168.50.106:5000'
}, },
'/exports': { '/exports': {
target: 'http://localhost:5000' target: 'http://192.168.50.106:5000'
}, },
'/ws': { '/ws': {
target: 'ws://localhost:5000', target: 'ws://192.168.50.106:5000',
ws: true, ws: true,
}, },
'/live': { '/live': {
target: 'ws://localhost:5000', target: 'ws://192.168.50.106:5000',
changeOrigin: true, changeOrigin: true,
ws: true, ws: true,
}, },