diff --git a/web/src/api/ws.tsx b/web/src/api/ws.tsx index 44d45ea2f..6bb2fdc32 100644 --- a/web/src/api/ws.tsx +++ b/web/src/api/ws.tsx @@ -11,6 +11,7 @@ import { TrackedObjectUpdateReturnType, TriggerStatus, FrigateAudioDetections, + Job, } from "@/types/ws"; import { FrigateStats } from "@/types/stats"; import { createContainer } from "react-tracked"; @@ -651,3 +652,40 @@ export function useTriggers(): { payload: TriggerStatus } { : { name: "", camera: "", event_id: "", type: "", score: 0 }; return { payload: useDeepMemo(parsed) }; } + +export function useJobStatus( + jobType: string, + revalidateOnFocus: boolean = true, +): { payload: Job | null } { + const { + value: { payload }, + send: sendCommand, + } = useWs("job_state", "jobState"); + + const jobData = useDeepMemo( + payload && typeof payload === "string" ? JSON.parse(payload) : {}, + ); + const currentJob = jobData[jobType] || null; + + useEffect(() => { + let listener: (() => void) | undefined; + if (revalidateOnFocus) { + sendCommand("jobState"); + listener = () => { + if (document.visibilityState === "visible") { + sendCommand("jobState"); + } + }; + addEventListener("visibilitychange", listener); + } + + return () => { + if (listener) { + removeEventListener("visibilitychange", listener); + } + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [revalidateOnFocus]); + + return { payload: currentJob as Job | null }; +} diff --git a/web/src/types/ws.ts b/web/src/types/ws.ts index 1d98b7b01..6e22345eb 100644 --- a/web/src/types/ws.ts +++ b/web/src/types/ws.ts @@ -126,3 +126,32 @@ export type TriggerStatus = { type: string; score: number; }; + +export type MediaSyncStats = { + files_checked: number; + orphans_found: number; + orphans_deleted: number; + aborted: boolean; + error: string | null; +}; + +export type MediaSyncTotals = { + files_checked: number; + orphans_found: number; + orphans_deleted: number; +}; + +export type MediaSyncResults = { + [mediaType: string]: MediaSyncStats | MediaSyncTotals; + totals: MediaSyncTotals; +}; + +export type Job = { + id: string; + job_type: string; + status: string; + results?: MediaSyncResults; + start_time?: number; + end_time?: number; + error_message?: string; +};