mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-07 11:45:24 +03:00
Use constant aspect ratio for review grid
This commit is contained in:
parent
80428a8cc1
commit
8278d9abd8
@ -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 || []}
|
||||||
|
|||||||
@ -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";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user