Use preview thumbnails

This commit is contained in:
Nicolas Mowen 2024-02-17 16:02:37 -07:00
parent 9b9808ca38
commit 7b1c7f9be9
2 changed files with 79 additions and 63 deletions

View File

@ -1,14 +1,7 @@
import VideoPlayer from "./VideoPlayer";
import React, {
useCallback,
useEffect,
useRef,
useState,
} from "react";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { useApiHost } from "@/api";
import Player from "video.js/dist/types/player";
import { AspectRatio } from "../ui/aspect-ratio";
import { LuPlayCircle } from "react-icons/lu";
import { isCurrentHour } from "@/utils/dateUtil";
import { isSafari } from "@/utils/browserUtil";
@ -116,10 +109,9 @@ export default function PreviewThumbnailPlayer({
);
return (
<AspectRatio
<div
ref={relevantPreview ? inViewRef : null}
ratio={16 / 9}
className="bg-black flex justify-center items-center"
className="relative w-full h-full"
onMouseEnter={() => onPlayback(true)}
onMouseLeave={() => onPlayback(false)}
>
@ -134,7 +126,9 @@ export default function PreviewThumbnailPlayer({
isMobile={isMobile}
onClick={onClick}
/>
</AspectRatio>
<div className="absolute top-0 left-0 right-0 rounded-2xl z-10 w-full h-[30%] bg-gradient-to-b from-black/20 to-transparent pointer-events-none" />
<div className="absolute bottom-0 left-0 right-0 rounded-2xl z-10 w-full h-[10%] bg-gradient-to-t from-black/20 to-transparent pointer-events-none" />
</div>
);
}
@ -198,55 +192,48 @@ function PreviewContent({
);
} else {
return (
<>
<div className="w-full">
<VideoPlayer
options={{
preload: "auto",
aspectRatio: "16:9",
autoplay: true,
controls: false,
muted: true,
loadingSpinner: false,
poster: relevantPreview
? ""
: `${apiHost}api/preview/${camera}/${startTs}/thumbnail.jpg`,
sources: relevantPreview
? [
{
src: `${relevantPreview.src}`,
type: "video/mp4",
},
]
: [],
}}
seekOptions={{}}
onReady={(player) => {
playerRef.current = player;
<VideoPlayer
options={{
preload: "auto",
autoplay: true,
controls: false,
muted: true,
fluid: true,
loadingSpinner: false,
poster: relevantPreview
? ""
: `${apiHost}api/preview/${camera}/${startTs}/thumbnail.jpg`,
sources: relevantPreview
? [
{
src: `${relevantPreview.src}`,
type: "video/mp4",
},
]
: [],
}}
seekOptions={{}}
onReady={(player) => {
playerRef.current = player;
if (!relevantPreview) {
return;
}
if (!relevantPreview) {
return;
}
if (!isInitiallyVisible) {
player.pause(); // autoplay + pause is required for iOS
}
if (!isInitiallyVisible) {
player.pause(); // autoplay + pause is required for iOS
}
player.playbackRate(slowPlayack ? 2 : 8);
player.currentTime(startTs - relevantPreview.start);
if (isMobile && onClick) {
player.on("touchstart", handleTouchStart);
}
}}
onDispose={() => {
playerRef.current = null;
}}
/>
</div>
{relevantPreview && (
<LuPlayCircle className="absolute z-10 left-1 bottom-1 w-4 h-4 text-white text-opacity-60" />
)}
</>
player.playbackRate(slowPlayack ? 2 : 8);
player.currentTime(startTs - relevantPreview.start);
if (isMobile && onClick) {
player.on("touchstart", handleTouchStart);
}
}}
onDispose={() => {
playerRef.current = null;
}}
/>
);
}
}

View File

@ -1,4 +1,5 @@
import TimeAgo from "@/components/dynamic/TimeAgo";
import PreviewThumbnailPlayer from "@/components/player/PreviewThumbnailPlayer";
import ActivityIndicator from "@/components/ui/activity-indicator";
import { Button } from "@/components/ui/button";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
@ -14,7 +15,10 @@ export default function Events() {
const { data: config } = useSWR<FrigateConfig>("config");
const [severity, setSeverity] = useState<ReviewSeverity>("alert");
const { data: reviewSegments } = useSWR<ReviewSegment[]>("review");
const { data: reviewSegments } = useSWR<ReviewSegment[]>([
"review",
{ limit: 500 },
]);
const previewTimes = useMemo(() => {
if (!reviewSegments) {
@ -89,7 +93,7 @@ export default function Events() {
</Button>
<Button className="mx-1" variant="secondary">
<LuCalendar className=" mr-[10px]" />
Fab 13
Fab 17
</Button>
<Button className="mx-1" variant="secondary">
<LuFilter className=" mr-[10px]" />
@ -101,10 +105,35 @@ export default function Events() {
<div className="flex flex-wrap gap-2 mt-2">
{reviewSegments?.map((value) => {
if (value.severity == severity) {
const detectConfig = config.cameras[value.camera].detect;
const relevantPreview = Object.values(allPreviews || []).find(
(preview) =>
preview.camera == value.camera &&
preview.start < value.start_time &&
preview.end > value.end_time
);
return (
<div className="relative h-[234px] w-[416px] bg-blue-500 rounded-lg">
{value.camera} {value.data.objects}
<div className="absolute left-1 right-1 bottom-0 flex justify-between">
<div
className="relative h-[234px] rounded-2xl overflow-hidden"
style={{
aspectRatio: detectConfig.width / detectConfig.height,
}}
>
{relevantPreview ? (
<PreviewThumbnailPlayer
relevantPreview={relevantPreview}
camera={value.camera}
startTs={value.start_time}
isMobile={false}
eventId=""
/>
) : (
<div>
{value.camera} {value.data.objects}
</div>
)}
<div className="absolute left-1 right-1 bottom-1 flex justify-between">
<TimeAgo time={value.start_time * 1000} />
{formatUnixTimestampToDateTime(value.start_time, {
strftime_fmt: