Fix recording roots UI usage and filesystem labels

This commit is contained in:
ibs0d 2026-03-07 20:45:17 +11:00
parent 22c957fca4
commit c5ba7aae08
3 changed files with 30 additions and 16 deletions

View File

@ -128,15 +128,15 @@
"storage": { "storage": {
"title": "Storage", "title": "Storage",
"overview": "Overview", "overview": "Overview",
"recordings": { "recordings": {
"title": "Recordings", "title": "Recordings",
"tips": "This value represents the total storage used by the recordings in Frigate's database. Frigate does not track storage usage for all files on your disk.", "tips": "This value represents the total storage used by the recordings in Frigate's database. Frigate does not track storage usage for all files on your disk.",
"earliestRecording": "Earliest recording available:", "earliestRecording": "Earliest recording available:",
"roots": "Recording Roots", "roots": "Recording Roots",
"nonDefault": "Non-default root", "rootSummary": "Disk used: {{used}} MiB \u2022 Free: {{free}} MiB \u2022 Usage: {{usage_percent}}%",
"rootSummary": "Disk used: {{used}} MiB • Free: {{free}} MiB • Usage: {{usage_percent}}%", "rootCameras": "Cameras: {{cameras}}",
"rootCameras": "Cameras: {{cameras}}" "recordingsTracked": "Frigate recordings tracked: {{recordings_size}} MiB"
}, },
"shm": { "shm": {
"title": "SHM (shared memory) allocation", "title": "SHM (shared memory) allocation",
"warning": "The current SHM size of {{total}}MB is too small. Increase it to at least {{min_shm}}MB." "warning": "The current SHM size of {{total}}MB is too small. Increase it to at least {{min_shm}}MB."

View File

@ -20,6 +20,7 @@ export type RecordingRootStorage = {
cameras: string[]; cameras: string[];
camera_usages: RootCameraStorage; camera_usages: RootCameraStorage;
is_default: boolean; is_default: boolean;
filesystem?: string;
}; };
export function RecordingsRoots({ roots }: { roots: RecordingRootStorage[] }) { export function RecordingsRoots({ roots }: { roots: RecordingRootStorage[] }) {
@ -36,15 +37,15 @@ export function RecordingsRoots({ roots }: { roots: RecordingRootStorage[] }) {
> >
<div className="mb-2 flex items-center justify-between gap-2"> <div className="mb-2 flex items-center justify-between gap-2">
<div className="break-all text-sm font-medium">{root.path}</div> <div className="break-all text-sm font-medium">{root.path}</div>
{!root.is_default && ( {root.filesystem && (
<div className="rounded-md bg-primary/15 px-2 py-1 text-xs text-primary"> <div className="rounded-md bg-primary/15 px-2 py-1 text-xs text-primary">
{t("storage.recordings.nonDefault")} {root.filesystem}
</div> </div>
)} )}
</div> </div>
<StorageGraph <StorageGraph
graphId={`recordings-root-${root.path}`} graphId={`recordings-root-${root.path}`}
used={root.recordings_size} used={root.used}
total={root.total} total={root.total}
/> />
<div className="mt-2 text-xs text-primary-variant"> <div className="mt-2 text-xs text-primary-variant">
@ -54,6 +55,11 @@ export function RecordingsRoots({ roots }: { roots: RecordingRootStorage[] }) {
usage_percent: root.usage_percent.toFixed(2), usage_percent: root.usage_percent.toFixed(2),
})} })}
</div> </div>
<div className="mt-2 text-xs text-primary-variant">
{t("storage.recordings.recordingsTracked", {
recordings_size: root.recordings_size,
})}
</div>
<div className="mt-2 text-xs text-muted-foreground"> <div className="mt-2 text-xs text-muted-foreground">
{t("storage.recordings.rootCameras", { {t("storage.recordings.rootCameras", {
cameras: cameras:

View File

@ -5,11 +5,13 @@ import { RecordingsRoots, type RecordingRootStorage } from "../RecordingsRoots";
vi.mock("react-i18next", () => ({ vi.mock("react-i18next", () => ({
useTranslation: () => ({ useTranslation: () => ({
t: (key: string, opts?: Record<string, string>) => { t: (key: string, opts?: Record<string, string | number>) => {
if (key === "storage.recordings.nonDefault") return "Non-default root";
if (key === "storage.recordings.rootSummary") { if (key === "storage.recordings.rootSummary") {
return `Disk used: ${opts?.used} MiB • Free: ${opts?.free} MiB • Usage: ${opts?.usage_percent}%`; return `Disk used: ${opts?.used} MiB • Free: ${opts?.free} MiB • Usage: ${opts?.usage_percent}%`;
} }
if (key === "storage.recordings.recordingsTracked") {
return `Frigate recordings tracked: ${opts?.recordings_size} MiB`;
}
if (key === "storage.recordings.rootCameras") { if (key === "storage.recordings.rootCameras") {
return `Cameras: ${opts?.cameras}`; return `Cameras: ${opts?.cameras}`;
} }
@ -20,7 +22,7 @@ vi.mock("react-i18next", () => ({
})); }));
describe("RecordingsRoots", () => { describe("RecordingsRoots", () => {
it("renders multiple roots and per-camera usage", () => { it("renders multiple roots, filesystem details, and per-camera usage", () => {
const roots: RecordingRootStorage[] = [ const roots: RecordingRootStorage[] = [
{ {
path: "/media/frigate/recordings", path: "/media/frigate/recordings",
@ -31,6 +33,7 @@ describe("RecordingsRoots", () => {
recordings_size: 600, recordings_size: 600,
cameras: ["front_door"], cameras: ["front_door"],
is_default: true, is_default: true,
filesystem: "ext4 • /media/frigate",
camera_usages: { camera_usages: {
front_door: { bandwidth: 5, usage: 600, usage_percent: 100 }, front_door: { bandwidth: 5, usage: 600, usage_percent: 100 },
}, },
@ -44,6 +47,7 @@ describe("RecordingsRoots", () => {
recordings_size: 800, recordings_size: 800,
cameras: ["back_yard", "garage"], cameras: ["back_yard", "garage"],
is_default: false, is_default: false,
filesystem: "xfs • /mnt",
camera_usages: { camera_usages: {
back_yard: { bandwidth: 4, usage: 300, usage_percent: 37.5 }, back_yard: { bandwidth: 4, usage: 300, usage_percent: 37.5 },
garage: { bandwidth: 6, usage: 500, usage_percent: 62.5 }, garage: { bandwidth: 6, usage: 500, usage_percent: 62.5 },
@ -55,7 +59,11 @@ describe("RecordingsRoots", () => {
expect(html).toContain("/media/frigate/recordings"); expect(html).toContain("/media/frigate/recordings");
expect(html).toContain("/mnt/custom-recordings"); expect(html).toContain("/mnt/custom-recordings");
expect(html).toContain("Non-default root"); expect(html).toContain("ext4 • /media/frigate");
expect(html).toContain("xfs • /mnt");
expect(html).not.toContain("Non-default root");
expect(html).toContain("Disk used: 700 MiB • Free: 300 MiB • Usage: 70.00%");
expect(html).toContain("Frigate recordings tracked: 600 MiB");
expect(html).toContain("Cameras: back yard, garage"); expect(html).toContain("Cameras: back yard, garage");
expect(html).toContain("garage"); expect(html).toContain("garage");
}); });