diff --git a/frigate/api/export.py b/frigate/api/export.py index 812a1b4b2..c2cf66a34 100644 --- a/frigate/api/export.py +++ b/frigate/api/export.py @@ -62,7 +62,7 @@ router = APIRouter(tags=[Tags.export]) def get_exports( allowed_cameras: List[str] = Depends(get_allowed_cameras_for_filter), export_case_id: Optional[str] = None, - camera: Optional[List[str]] = Query(default=None), + cameras: Optional[str] = Query(default="all"), start_date: Optional[float] = None, end_date: Optional[float] = None, ): @@ -74,8 +74,9 @@ def get_exports( else: query = query.where(Export.export_case == export_case_id) - if camera: - filtered_cameras = [c for c in camera if c in allowed_cameras] + if cameras and cameras != "all": + requested = set(cameras.split(",")) + filtered_cameras = list(requested.intersection(allowed_cameras)) if not filtered_cameras: return JSONResponse(content=[]) query = query.where(Export.camera << filtered_cameras) diff --git a/web/src/pages/Exports.tsx b/web/src/pages/Exports.tsx index 5faf0c08f..2416a37cd 100644 --- a/web/src/pages/Exports.tsx +++ b/web/src/pages/Exports.tsx @@ -16,8 +16,14 @@ import { Input } from "@/components/ui/input"; import { Toaster } from "@/components/ui/sonner"; import useKeyboardListener from "@/hooks/use-keyboard-listener"; import { useOverlayState, useSearchEffect } from "@/hooks/use-overlay-state"; +import { useApiFilterArgs } from "@/hooks/use-api-filter"; import { cn } from "@/lib/utils"; -import { DeleteClipType, Export, ExportCase } from "@/types/export"; +import { + DeleteClipType, + Export, + ExportCase, + ExportFilter, +} from "@/types/export"; import OptionAndInputDialog from "@/components/overlay/dialog/OptionAndInputDialog"; import axios from "axios"; @@ -37,6 +43,9 @@ import { toast } from "sonner"; import useSWR from "swr"; import ExportFilterGroup from "@/components/filter/ExportFilterGroup"; +// always parse these as string arrays +const EXPORT_FILTER_ARRAY_KEYS = ["cameras"]; + function Exports() { const { t } = useTranslation(["views/exports"]); @@ -44,11 +53,19 @@ function Exports() { document.title = t("documentTitle"); }, [t]); + // Filters + + const [exportFilter, setExportFilter, exportSearchParams] = + useApiFilterArgs(EXPORT_FILTER_ARRAY_KEYS); + // Data const { data: cases, mutate: updateCases } = useSWR("cases"); - const { data: rawExports, mutate: updateExports } = - useSWR("exports"); + const { data: rawExports, mutate: updateExports } = useSWR( + exportSearchParams && Object.keys(exportSearchParams).length > 0 + ? ["exports", exportSearchParams] + : "exports", + ); const exports = useMemo( () => (rawExports ?? []).filter((e) => !e.export_case), @@ -241,17 +258,17 @@ function Exports() { >
setSearch(e.target.value)} />
{}} + onUpdateFilter={setExportFilter} />