diff --git a/frigate/record/export.py b/frigate/record/export.py index c6fc8aadb..5b562bd60 100644 --- a/frigate/record/export.py +++ b/frigate/record/export.py @@ -159,7 +159,7 @@ class RecordingExporter(threading.Thread): export_id = f"{self.camera}_{''.join(random.choices(string.ascii_lowercase + string.digits, k=6))}" export_name = ( self.user_provided_name - or f"{self.camera} {self.get_datetime_from_timestamp(self.start_time)} {self.get_datetime_from_timestamp(self.end_time)}" + or f"{self.camera.replace("_", " ")} {self.get_datetime_from_timestamp(self.start_time)} {self.get_datetime_from_timestamp(self.end_time)}" ) video_path = f"{EXPORT_DIR}/{export_id}.mp4" diff --git a/web/src/App.tsx b/web/src/App.tsx index 21ad0200e..a86e79491 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -11,7 +11,7 @@ import { Redirect } from "./components/navigation/Redirect"; const Live = lazy(() => import("@/pages/Live")); const Events = lazy(() => import("@/pages/Events")); -const Export = lazy(() => import("@/pages/Export")); +const Exports = lazy(() => import("@/pages/Exports")); const SubmitPlus = lazy(() => import("@/pages/SubmitPlus")); const ConfigEditor = lazy(() => import("@/pages/ConfigEditor")); const System = lazy(() => import("@/pages/System")); @@ -38,7 +38,7 @@ function App() { } /> } /> } /> - } /> + } /> } /> } /> } /> diff --git a/web/src/components/card/ExportCard.tsx b/web/src/components/card/ExportCard.tsx index dcfea95e3..f4a5c18a5 100644 --- a/web/src/components/card/ExportCard.tsx +++ b/web/src/components/card/ExportCard.tsx @@ -1,8 +1,7 @@ -import { baseUrl } from "@/api/baseUrl"; import ActivityIndicator from "../indicators/activity-indicator"; import { LuPencil, LuTrash } from "react-icons/lu"; import { Button } from "../ui/button"; -import { useMemo, useRef, useState } from "react"; +import { useRef, useState } from "react"; import { isDesktop } from "react-device-detect"; import { FaPlay } from "react-icons/fa"; import Chip from "../indicators/Chip"; @@ -10,19 +9,18 @@ import { Skeleton } from "../ui/skeleton"; import { Dialog, DialogContent, DialogFooter, DialogTitle } from "../ui/dialog"; import { Input } from "../ui/input"; import useKeyboardListener from "@/hooks/use-keyboard-listener"; +import { Export } from "@/types/export"; type ExportProps = { className: string; - file: { - name: string; - }; + exportedRecording: Export; onRename: (original: string, update: string) => void; onDelete: (file: string) => void; }; export default function ExportCard({ className, - file, + exportedRecording, onRename, onDelete, }: ExportProps) { @@ -30,10 +28,6 @@ export default function ExportCard({ const [hovered, setHovered] = useState(false); const [playing, setPlaying] = useState(false); const [loading, setLoading] = useState(true); - const inProgress = useMemo( - () => file.name.startsWith("in_progress"), - [file.name], - ); // editing name @@ -102,13 +96,19 @@ export default function ExportCard({
setHovered(true) : undefined + isDesktop && !exportedRecording.in_progress + ? () => setHovered(true) + : undefined } onMouseLeave={ - isDesktop && !inProgress ? () => setHovered(false) : undefined + isDesktop && !exportedRecording.in_progress + ? () => setHovered(false) + : undefined } onClick={ - isDesktop || inProgress ? undefined : () => setHovered(!hovered) + isDesktop || exportedRecording.in_progress + ? undefined + : () => setHovered(!hovered) } > {hovered && ( @@ -119,13 +119,15 @@ export default function ExportCard({
setEditName({ original: file.name, update: "" })} + onClick={() => + setEditName({ original: exportedRecording.id, update: "" }) + } > onDelete(file.name)} + onClick={() => onDelete(exportedRecording.id)} > @@ -144,20 +146,14 @@ export default function ExportCard({ )} )} - {inProgress ? ( + {exportedRecording.in_progress ? ( ) : ( - + src={exportedRecording.thumb_path.replace("/media/frigate", "")} + onLoad={() => setLoading(false)} + /> )} {loading && ( @@ -165,9 +161,7 @@ export default function ExportCard({ {!playing && (
- {file.name - .substring(0, file.name.length - 4) - .replaceAll("_", " ")} + {exportedRecording.name}
)} diff --git a/web/src/pages/Export.tsx b/web/src/pages/Exports.tsx similarity index 76% rename from web/src/pages/Export.tsx rename to web/src/pages/Exports.tsx index b808ce81e..020212d17 100644 --- a/web/src/pages/Export.tsx +++ b/web/src/pages/Exports.tsx @@ -1,4 +1,3 @@ -import { baseUrl } from "@/api/baseUrl"; import ExportCard from "@/components/card/ExportCard"; import { AlertDialog, @@ -11,19 +10,13 @@ import { } from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; +import { Export } from "@/types/export"; import axios from "axios"; import { useCallback, useEffect, useMemo, useState } from "react"; import useSWR from "swr"; -type ExportItem = { - name: string; -}; - -function Export() { - const { data: allExports, mutate } = useSWR( - "exports/", - (url: string) => axios({ baseURL: baseUrl, url }).then((res) => res.data), - ); +function Exports() { + const { data: exports, mutate } = useSWR("exports"); useEffect(() => { document.title = "Export - Frigate"; @@ -33,15 +26,17 @@ function Export() { const [search, setSearch] = useState(""); - const exports = useMemo(() => { - if (!search || !allExports) { - return allExports; + const filteredExports = useMemo(() => { + if (!search || !exports) { + return exports; } - return allExports.filter((exp) => - exp.name.toLowerCase().includes(search.toLowerCase()), + return exports.filter((exp) => + exp.name + .toLowerCase() + .includes(search.toLowerCase().replaceAll(" ", "_")), ); - }, [allExports, search]); + }, [exports, search]); // Deleting @@ -63,8 +58,8 @@ function Export() { // Renaming const onHandleRename = useCallback( - (original: string, update: string) => { - axios.patch(`export/${original}/${update}`).then((response) => { + (id: string, update: string) => { + axios.patch(`export/${id}/${update}`).then((response) => { if (response.status == 200) { setDeleteClip(undefined); mutate(); @@ -106,15 +101,15 @@ function Export() {
- {allExports && exports && ( + {exports && filteredExports && (
- {Object.values(allExports).map((item) => ( + {Object.values(exports).map((item) => ( setDeleteClip(file)} /> @@ -126,4 +121,4 @@ function Export() { ); } -export default Export; +export default Exports; diff --git a/web/src/types/export.ts b/web/src/types/export.ts new file mode 100644 index 000000000..499e33313 --- /dev/null +++ b/web/src/types/export.ts @@ -0,0 +1,9 @@ +export type Export = { + id: string; + camera: string; + name: string; + date: number; + video_path: string; + thumb_path: string; + in_progress: boolean; +};