mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-07 03:35:26 +03:00
Playback recording when clicking on review item
This commit is contained in:
parent
6a400a5f12
commit
d5f57c117e
@ -23,12 +23,14 @@ type HistoryCardProps = {
|
||||
timeline: Card;
|
||||
relevantPreview?: Preview;
|
||||
shouldAutoPlay: boolean;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
export default function HistoryCard({
|
||||
relevantPreview,
|
||||
timeline,
|
||||
shouldAutoPlay,
|
||||
onClick,
|
||||
}: HistoryCardProps) {
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
|
||||
@ -37,7 +39,10 @@ export default function HistoryCard({
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="my-2 xs:mr-2 bg-secondary w-full xs:w-[48%] sm:w-[284px]">
|
||||
<Card
|
||||
className="cursor-pointer my-2 xs:mr-2 bg-secondary w-full xs:w-[48%] sm:w-[284px]"
|
||||
onClick={onClick}
|
||||
>
|
||||
<PreviewThumbnailPlayer
|
||||
camera={timeline.camera}
|
||||
relevantPreview={relevantPreview}
|
||||
|
||||
74
web-new/src/components/card/TimelineCardPlayer.tsx
Normal file
74
web-new/src/components/card/TimelineCardPlayer.tsx
Normal file
@ -0,0 +1,74 @@
|
||||
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog";
|
||||
import useSWR from "swr";
|
||||
import { FrigateConfig } from "@/types/frigateConfig";
|
||||
import VideoPlayer from "../player/VideoPlayer";
|
||||
import { useMemo } from "react";
|
||||
import { useApiHost } from "@/api";
|
||||
|
||||
type TimelinePlayerCardProps = {
|
||||
timeline?: Card;
|
||||
onDismiss: () => void;
|
||||
};
|
||||
|
||||
export default function TimelinePlayerCard({
|
||||
timeline,
|
||||
onDismiss,
|
||||
}: TimelinePlayerCardProps) {
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
const apiHost = useApiHost();
|
||||
|
||||
const recordingParams = useMemo(() => {
|
||||
if (!timeline) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
before: timeline.entries.at(-1)!!.timestamp + 30,
|
||||
after: timeline.entries.at(0)!!.timestamp,
|
||||
};
|
||||
}, [timeline]);
|
||||
const { data: recordings } = useSWR<Recording[]>(
|
||||
timeline ? [`${timeline.camera}/recordings`, recordingParams] : null,
|
||||
{ revalidateOnFocus: false }
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog open={timeline != null} onOpenChange={onDismiss}>
|
||||
<DialogContent className="md:max-w-xl lg:max-w-2xl xl:max-w-3xl 2xl:max-w-4xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="capitalize">
|
||||
{`${timeline?.camera?.replaceAll(
|
||||
"_",
|
||||
" "
|
||||
)} @ ${formatUnixTimestampToDateTime(timeline?.time ?? 0, {
|
||||
strftime_fmt:
|
||||
config?.ui?.time_format == "24hour" ? "%H:%M:%S" : "%I:%M:%S",
|
||||
})}`}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
{recordings && recordings.length > 0 && (
|
||||
<VideoPlayer
|
||||
options={{
|
||||
preload: "auto",
|
||||
autoplay: true,
|
||||
sources: [
|
||||
{
|
||||
src: `${apiHost}vod/${timeline?.camera}/start/${recordings
|
||||
.at(0)
|
||||
?.start_time.toFixed(2)}/end/${recordings
|
||||
.at(-1)
|
||||
?.end_time.toFixed(2)}/master.m3u8`,
|
||||
type: "application/vnd.apple.mpegurl",
|
||||
},
|
||||
],
|
||||
}}
|
||||
seekOptions={{ forward: 10, backward: 5 }}
|
||||
/>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -152,6 +152,10 @@ function isCurrentHour(timestamp: number) {
|
||||
function getPreviewWidth(camera: string, config: FrigateConfig) {
|
||||
const detect = config.cameras[camera].detect;
|
||||
|
||||
if (detect.width / detect.height < 1.0) {
|
||||
return "w-[120px]";
|
||||
}
|
||||
|
||||
if (detect.width / detect.height < 1.4) {
|
||||
return "w-[208px]";
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import ActivityIndicator from "@/components/ui/activity-indicator";
|
||||
import HistoryCard from "@/components/card/HistoryCard";
|
||||
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
|
||||
import axios from "axios";
|
||||
import TimelinePlayerCard from "@/components/card/TimelineCardPlayer";
|
||||
|
||||
const API_LIMIT = 100;
|
||||
|
||||
@ -44,15 +45,17 @@ function History() {
|
||||
isValidating,
|
||||
} = useSWRInfinite<HourlyTimeline>(getKey, timelineFetcher);
|
||||
const { data: allPreviews } = useSWR<Preview[]>(
|
||||
`preview/all/start/${(timelinePages ?? [])?.at(0)?.start ?? 0}/end/${
|
||||
(timelinePages ?? [])?.at(-1)?.end ?? 0
|
||||
}`,
|
||||
timelinePages
|
||||
? `preview/all/start/${timelinePages?.at(0)
|
||||
?.start}/end/${timelinePages?.at(-1)?.end}`
|
||||
: null,
|
||||
{ revalidateOnFocus: false }
|
||||
);
|
||||
|
||||
const [detailLevel, setDetailLevel] = useState<"normal" | "extra" | "full">(
|
||||
"normal"
|
||||
);
|
||||
const [playback, setPlayback] = useState<Card | undefined>();
|
||||
|
||||
const timelineCards: CardsData | never[] = useMemo(() => {
|
||||
if (!timelinePages) {
|
||||
@ -161,7 +164,7 @@ function History() {
|
||||
[size, setSize, isValidating, isDone]
|
||||
);
|
||||
|
||||
if (!config || !timelineCards ||timelineCards.length == 0) {
|
||||
if (!config || !timelineCards || timelineCards.length == 0) {
|
||||
return <ActivityIndicator />;
|
||||
}
|
||||
|
||||
@ -172,6 +175,11 @@ function History() {
|
||||
Dates and times are based on the timezone {timezone}
|
||||
</div>
|
||||
|
||||
<TimelinePlayerCard
|
||||
timeline={playback}
|
||||
onDismiss={() => setPlayback(undefined)}
|
||||
/>
|
||||
|
||||
<div>
|
||||
{Object.entries(timelineCards)
|
||||
.reverse()
|
||||
@ -225,6 +233,9 @@ function History() {
|
||||
timeline={timeline}
|
||||
shouldAutoPlay={shouldAutoPlay}
|
||||
relevantPreview={relevantPreview}
|
||||
onClick={() => {
|
||||
setPlayback(timeline);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
11
web-new/src/types/record.ts
Normal file
11
web-new/src/types/record.ts
Normal file
@ -0,0 +1,11 @@
|
||||
type Recording = {
|
||||
id: string,
|
||||
camera: string,
|
||||
start_time: number,
|
||||
end_time: number,
|
||||
path: string,
|
||||
segment_size: number,
|
||||
motion: number,
|
||||
objects: number,
|
||||
dBFS: number,
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user