diff --git a/web/src/components/overlay/ExportDialog.tsx b/web/src/components/overlay/ExportDialog.tsx index a04fcaed2..8bf7f13a9 100644 --- a/web/src/components/overlay/ExportDialog.tsx +++ b/web/src/components/overlay/ExportDialog.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useCallback, useState } from "react"; import { Dialog, DialogClose, @@ -11,6 +11,10 @@ import { import { Label } from "../ui/label"; import { RadioGroup, RadioGroupItem } from "../ui/radio-group"; import { Button } from "../ui/button"; +import { ExportMode } from "@/types/filter"; +import { FaArrowDown } from "react-icons/fa"; +import axios from "axios"; +import { toast } from "sonner"; const EXPORT_OPTIONS = [ "1", @@ -23,13 +27,87 @@ const EXPORT_OPTIONS = [ ] as const; type ExportOption = (typeof EXPORT_OPTIONS)[number]; -export default function ExportDialog() { +type ExportDialogProps = { + camera: string; + mode: ExportMode; + setMode: (mode: ExportMode) => void; +}; +export default function ExportDialog({ + camera, + mode, + setMode, +}: ExportDialogProps) { const [selectedOption, setSelectedOption] = useState("1"); + const onStartExport = useCallback(() => { + const now = new Date(); + let end = now.getTime() / 1000; + + let start; + switch (selectedOption) { + case "1": + now.setHours(now.getHours() - 1); + start = now.getTime() / 1000; + break; + case "4": + now.setHours(now.getHours() - 4); + start = now.getTime() / 1000; + break; + case "8": + now.setHours(now.getHours() - 8); + start = now.getTime() / 1000; + break; + case "12": + now.setHours(now.getHours() - 12); + start = now.getTime() / 1000; + break; + case "24": + now.setHours(now.getHours() - 24); + start = now.getTime() / 1000; + break; + case "custom": + end = 0; + break; + } + + axios + .post(`export/${camera}/start/${start}/end/${end}`, { + playback: "realtime", + }) + .then((response) => { + if (response.status == 200) { + toast.success( + "Successfully started export. View the file in the /exports folder.", + { position: "top-center" }, + ); + } + }) + .catch((error) => { + if (error.response?.data?.message) { + toast.error( + `Failed to start export: ${error.response.data.message}`, + { position: "top-center" }, + ); + } else { + toast.error(`Failed to start export: ${error.message}`, { + position: "top-center", + }); + } + }); + }, [camera, selectedOption]); + return ( - - -
+ + + @@ -61,8 +139,19 @@ export default function ExportDialog() { })} - Cancel - diff --git a/web/src/pages/Export.tsx b/web/src/pages/Export.tsx index 30148ba57..ff6f5765f 100644 --- a/web/src/pages/Export.tsx +++ b/web/src/pages/Export.tsx @@ -10,7 +10,6 @@ import { AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; -import { Toaster } from "@/components/ui/sonner"; import axios from "axios"; import { useCallback, useState } from "react"; import useSWR from "swr"; @@ -42,8 +41,6 @@ function Export() { return (
- - setDeleteClip(undefined)} diff --git a/web/src/types/filter.ts b/web/src/types/filter.ts index 722057fa1..228aea98f 100644 --- a/web/src/types/filter.ts +++ b/web/src/types/filter.ts @@ -1,3 +1,5 @@ // allow any // eslint-disable-next-line @typescript-eslint/no-explicit-any export type FilterType = { [searchKey: string]: any }; + +export type ExportMode = "select" | "timeline" | "none"; diff --git a/web/src/views/events/RecordingView.tsx b/web/src/views/events/RecordingView.tsx index a7cb9f614..7b672a91b 100644 --- a/web/src/views/events/RecordingView.tsx +++ b/web/src/views/events/RecordingView.tsx @@ -12,6 +12,7 @@ import { Button } from "@/components/ui/button"; import { Drawer, DrawerContent, DrawerTrigger } from "@/components/ui/drawer"; import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; import { useOverlayState } from "@/hooks/use-overlay-state"; +import { ExportMode } from "@/types/filter"; import { FrigateConfig } from "@/types/frigateConfig"; import { Preview } from "@/types/preview"; import { @@ -33,6 +34,7 @@ import { isDesktop, isMobile } from "react-device-detect"; import { FaCircle, FaVideo } from "react-icons/fa"; import { IoMdArrowRoundBack } from "react-icons/io"; import { useNavigate } from "react-router-dom"; +import { Toaster } from "@/components/ui/sonner"; import useSWR from "swr"; const SEGMENT_DURATION = 30; @@ -75,6 +77,10 @@ export function RecordingView({ [reviewItems, mainCamera], ); + // export + + const [exportMode, setExportMode] = useState("none"); + // timeline const [timelineType, setTimelineType] = useOverlayState( @@ -211,6 +217,7 @@ export function RecordingView({ return (
+