forked from Github/frigate
Improve graph using pandas (#9234)
* Ensure viewport is always full screen * Protect against hour with no cards and ensure data is consistent * Reduce grouped up image refreshes * Include current hour and fix scrubbing bugginess * Scroll initially selected timeline in to view * Expand timelne class type * Use poster image for preview on video player instead of using separate image view * Fix available streaming modes * Incrase timing for grouping timline items * Fix audio activity listener * Fix player not switching views correctly * Use player time to convert to timeline time * Update sub labels for previous timeline items * Show mini timeline bar for non selected items * Rewrite desktop timeline to use separate dynamic video player component * Extend improvements to mobile as well * Improve time formatting * Fix scroll * Fix no preview case * Mobile fixes * Audio toggle fixes * More fixes for mobile * Improve scaling of graph motion activity * Add keyboard shortcut hook and support shortcuts for playback page * Fix sizing of dialog * Improve height scaling of dialog * simplify and fix layout system for timeline * Fix timeilne items not working * Implement basic Frigate+ submitting from timeline
This commit is contained in:
committed by
Blake Blackshear
parent
9c4b69191b
commit
af3f6dadcb
@@ -6,6 +6,20 @@ import useSWR from "swr";
|
||||
import { FrigateConfig } from "@/types/frigateConfig";
|
||||
import VideoPlayer from "../player/VideoPlayer";
|
||||
import { Card } from "../ui/card";
|
||||
import { useApiHost } from "@/api";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
AlertDialogTrigger,
|
||||
} from "../ui/alert-dialog";
|
||||
import { useCallback } from "react";
|
||||
import axios from "axios";
|
||||
|
||||
type TimelineItemCardProps = {
|
||||
timeline: Timeline;
|
||||
@@ -18,37 +32,55 @@ export default function TimelineItemCard({
|
||||
onSelect,
|
||||
}: TimelineItemCardProps) {
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
const apiHost = useApiHost();
|
||||
|
||||
const onSubmitToPlus = useCallback(
|
||||
async (falsePositive: boolean) => {
|
||||
falsePositive
|
||||
? await axios.put(`events/${timeline.source_id}/false_positive`)
|
||||
: await axios.post(`events/${timeline.source_id}/plus`, {
|
||||
include_annotation: 1,
|
||||
});
|
||||
},
|
||||
[timeline]
|
||||
);
|
||||
|
||||
return (
|
||||
<Card className="relative m-2 flex w-full h-32 cursor-pointer" onClick={onSelect}>
|
||||
<div className="w-1/2 p-2">
|
||||
{relevantPreview && (
|
||||
<VideoPlayer
|
||||
options={{
|
||||
preload: "auto",
|
||||
height: "114",
|
||||
width: "202",
|
||||
autoplay: true,
|
||||
controls: false,
|
||||
fluid: false,
|
||||
muted: true,
|
||||
loadingSpinner: false,
|
||||
sources: [
|
||||
{
|
||||
src: `${relevantPreview.src}`,
|
||||
type: "video/mp4",
|
||||
},
|
||||
],
|
||||
}}
|
||||
seekOptions={{}}
|
||||
onReady={(player) => {
|
||||
<Card
|
||||
className="relative m-2 flex w-full h-20 xl:h-24 3xl:h-28 4xl:h-36 cursor-pointer"
|
||||
onClick={onSelect}
|
||||
>
|
||||
<div className="w-32 xl:w-40 3xl:w-44 4xl:w-60 p-2">
|
||||
<VideoPlayer
|
||||
options={{
|
||||
preload: "auto",
|
||||
autoplay: true,
|
||||
controls: false,
|
||||
aspectRatio: "16:9",
|
||||
muted: true,
|
||||
loadingSpinner: false,
|
||||
poster: relevantPreview
|
||||
? ""
|
||||
: `${apiHost}api/preview/${timeline.camera}/${timeline.timestamp}/thumbnail.jpg`,
|
||||
sources: relevantPreview
|
||||
? [
|
||||
{
|
||||
src: `${relevantPreview.src}`,
|
||||
type: "video/mp4",
|
||||
},
|
||||
]
|
||||
: [],
|
||||
}}
|
||||
seekOptions={{}}
|
||||
onReady={(player) => {
|
||||
if (relevantPreview) {
|
||||
player.pause(); // autoplay + pause is required for iOS
|
||||
player.currentTime(timeline.timestamp - relevantPreview.start);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="px-2 py-1 w-1/2">
|
||||
<div className="py-1">
|
||||
<div className="capitalize font-semibold text-sm">
|
||||
{getTimelineItemDescription(timeline)}
|
||||
</div>
|
||||
@@ -60,16 +92,52 @@ export default function TimelineItemCard({
|
||||
date_style: "medium",
|
||||
})}
|
||||
</div>
|
||||
<Button
|
||||
className="absolute bottom-1 right-1"
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
>
|
||||
<div className="w-8 h-8">
|
||||
<Logo />
|
||||
</div>
|
||||
+
|
||||
</Button>
|
||||
{timeline.source == "tracked_object" && (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<Button
|
||||
className="absolute bottom-1 right-1 hidden xl:flex"
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
>
|
||||
<div className="w-8 h-8">
|
||||
<Logo />
|
||||
</div>
|
||||
+
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Submit To Frigate+</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
Objects in locations you want to avoid are not false
|
||||
positives. Submitting them as false positives will confuse the
|
||||
model.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<img
|
||||
className="flex-grow-0"
|
||||
src={`${apiHost}api/events/${timeline.source_id}/snapshot.jpg`}
|
||||
alt={`${timeline.data.label}`}
|
||||
/>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
className="bg-success"
|
||||
onClick={() => onSubmitToPlus(false)}
|
||||
>
|
||||
This is a {timeline.data.label}
|
||||
</AlertDialogAction>
|
||||
<AlertDialogAction
|
||||
className="bg-danger"
|
||||
onClick={() => onSubmitToPlus(true)}
|
||||
>
|
||||
This is not a {timeline.data.label}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
)}
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user