Compare commits

...
4 Commits
Author SHA1 Message Date
Josh Hawkins d315e0874a match link color with other sections 2026-04-28 22:42:10 -05:00
Josh HawkinsandCopilot 7cb29420c1 fix color
Co-authored-by: Copilot <copilot@github.com>
2026-04-28 22:39:14 -05:00
Josh Hawkins a92215480e add docs link to media sync settings 2026-04-28 22:36:44 -05:00
Josh Hawkins 24b7653ea8 ensure embeddings process restarts after maintainer thread crash 2026-04-28 14:29:54 -05:00
3 changed files with 31 additions and 4 deletions
+9
View File
@@ -4,6 +4,7 @@ import base64
import json import json
import logging import logging
import os import os
import sys
import threading import threading
from json.decoder import JSONDecodeError from json.decoder import JSONDecodeError
from multiprocessing.synchronize import Event as MpEvent from multiprocessing.synchronize import Event as MpEvent
@@ -52,6 +53,14 @@ class EmbeddingProcess(FrigateProcess):
self.stop_event, self.stop_event,
) )
maintainer.start() maintainer.start()
maintainer.join()
# If the maintainer thread exited but no shutdown was requested, it
# crashed. Surface as a non-zero exit so the watchdog restarts us
# instead of treating the silent thread death as a clean shutdown.
if not self.stop_event.is_set():
logger.error("Embeddings maintainer thread exited unexpectedly")
sys.exit(1)
class EmbeddingsContext: class EmbeddingsContext:
+4 -1
View File
@@ -28,6 +28,7 @@ class MonitoredProcess:
restart_timestamps: deque[float] = field( restart_timestamps: deque[float] = field(
default_factory=lambda: deque(maxlen=MAX_RESTARTS) default_factory=lambda: deque(maxlen=MAX_RESTARTS)
) )
clean_exit_logged: bool = False
def is_restarting_too_fast(self, now: float) -> bool: def is_restarting_too_fast(self, now: float) -> bool:
while ( while (
@@ -72,7 +73,9 @@ class FrigateWatchdog(threading.Thread):
exitcode = entry.process.exitcode exitcode = entry.process.exitcode
if exitcode == 0: if exitcode == 0:
logger.info("Process %s exited cleanly, not restarting", entry.name) if not entry.clean_exit_logged:
logger.info("Process %s exited cleanly, not restarting", entry.name)
entry.clean_exit_logged = True
return return
logger.warning( logger.warning(
@@ -10,13 +10,16 @@ import axios from "axios";
import { toast } from "sonner"; import { toast } from "sonner";
import { useJobStatus } from "@/api/ws"; import { useJobStatus } from "@/api/ws";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { LuCheck, LuX } from "react-icons/lu"; import { LuCheck, LuExternalLink, LuX } from "react-icons/lu";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil"; import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
import { MediaSyncResults, MediaSyncStats } from "@/types/ws"; import { MediaSyncResults, MediaSyncStats } from "@/types/ws";
import { useDocDomain } from "@/hooks/use-doc-domain";
import { Link } from "react-router-dom";
export default function MediaSyncSettingsView() { export default function MediaSyncSettingsView() {
const { t } = useTranslation("views/settings"); const { t } = useTranslation("views/settings");
const { getLocaleDocUrl } = useDocDomain();
const [selectedMediaTypes, setSelectedMediaTypes] = useState<string[]>([ const [selectedMediaTypes, setSelectedMediaTypes] = useState<string[]>([
"all", "all",
]); ]);
@@ -109,13 +112,25 @@ export default function MediaSyncSettingsView() {
<Heading as="h4" className="mb-2 hidden md:block"> <Heading as="h4" className="mb-2 hidden md:block">
{t("maintenance.sync.title")} {t("maintenance.sync.title")}
</Heading> </Heading>
<div className="max-w-6xl"> <div className="max-w-6xl">
<div className="mb-5 mt-2 flex max-w-5xl flex-col gap-2 text-sm text-muted-foreground"> <div className="mb-5 mt-2 flex max-w-5xl flex-col gap-2 text-sm text-muted-foreground">
<p>{t("maintenance.sync.desc")}</p> <p>{t("maintenance.sync.desc")}</p>
<div className="flex items-center text-primary-variant">
<Link
to={getLocaleDocUrl(
"configuration/record#syncing-media-files-with-disk",
)}
target="_blank"
rel="noopener noreferrer"
className="inline"
>
{t("readTheDocumentation", { ns: "common" })}
<LuExternalLink className="ml-2 inline-flex size-3" />
</Link>
</div>
</div> </div>
</div> </div>
<div className="space-y-6"> <div className="space-y-6">
{/* Media Types Selection */} {/* Media Types Selection */}
<div> <div>