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;
+};