import { CombinedStorageGraph } from "@/components/graph/CombinedStorageGraph"; import { StorageGraph } from "@/components/graph/StorageGraph"; import { FrigateStats } from "@/types/stats"; import { useMemo } from "react"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import useSWR from "swr"; import { CiCircleAlert } from "react-icons/ci"; import { FrigateConfig } from "@/types/frigateConfig"; import { useFormattedTimestamp, useTimezone } from "@/hooks/use-date-utils"; import { RecordingsSummary } from "@/types/review"; import { useTranslation } from "react-i18next"; import { TZDate } from "react-day-picker"; import { Link } from "react-router-dom"; import { useDocDomain } from "@/hooks/use-doc-domain"; import { LuExternalLink } from "react-icons/lu"; import { FaExclamationTriangle } from "react-icons/fa"; type CameraStorage = { [key: string]: { bandwidth: number; usage: number; usage_percent: number; }; }; type StorageMetricsProps = { setLastUpdated: (last: number) => void; }; export default function StorageMetrics({ setLastUpdated, }: StorageMetricsProps) { const { data: cameraStorage } = useSWR("recordings/storage"); const { data: stats } = useSWR("stats"); const { data: config } = useSWR("config", { revalidateOnFocus: false, }); const { t } = useTranslation(["views/system"]); const timezone = useTimezone(config); const { getLocaleDocUrl } = useDocDomain(); const totalStorage = useMemo(() => { if (!cameraStorage || !stats) { return undefined; } const totalStorage = { used: stats.service.storage["/media/frigate/recordings"]["used"], camera: 0, total: stats.service.storage["/media/frigate/recordings"]["total"], }; Object.values(cameraStorage).forEach( (cam) => (totalStorage.camera += cam.usage), ); setLastUpdated(Date.now() / 1000); return totalStorage; }, [cameraStorage, stats, setLastUpdated]); // recordings summary const { data: recordingsSummary } = useSWR([ "recordings/summary", { timezone: timezone, }, ]); const earliestDate = useMemo(() => { const keys = Object.keys(recordingsSummary || {}); return keys.length ? new TZDate(keys[keys.length - 1] + "T00:00:00", timezone).getTime() / 1000 : null; }, [recordingsSummary, timezone]); const timeFormat = config?.ui.time_format === "24hour" ? "24hour" : "12hour"; const format = useMemo(() => { return t(`time.formattedTimestampMonthDayYear.${timeFormat}`, { ns: "common", }); }, [t, timeFormat]); const formattedEarliestDate = useFormattedTimestamp( earliestDate || 0, format, timezone, ); if (!cameraStorage || !stats || !totalStorage || !config) { return; } return (
{t("storage.overview")}
{t("storage.recordings.title")}
{t("storage.recordings.tips")}
{earliestDate && (
{t("storage.recordings.earliestRecording")} {" "} {formattedEarliestDate}
)}
/tmp/cache
/dev/shm {stats.service.storage["/dev/shm"]["total"] < (stats.service.storage["/dev/shm"]["min_shm"] ?? 0) && (
{t("storage.shm.warning", { total: stats.service.storage["/dev/shm"]["total"], min_shm: stats.service.storage["/dev/shm"]["min_shm"], })}
{t("storage.shm.readTheDocumentation")}
)}
{t("storage.cameraStorage.title")}
); }