mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-05-10 07:25:27 +03:00
Compare commits
No commits in common. "5a214eb0d1634f87388174f96aa262edd282f23e" and "687fefb343ea597df420ea44998474da66911720" have entirely different histories.
5a214eb0d1
...
687fefb343
@ -153,9 +153,6 @@ Each line represents a detection state, not necessarily unique individuals. Pare
|
||||
if "other_concerns" in schema.get("required", []):
|
||||
schema["required"].remove("other_concerns")
|
||||
|
||||
# OpenAI strict mode requires additionalProperties: false on all objects
|
||||
schema["additionalProperties"] = False
|
||||
|
||||
response_format = {
|
||||
"type": "json_schema",
|
||||
"json_schema": {
|
||||
|
||||
@ -36,12 +36,19 @@ def _to_jpeg(img_bytes: bytes) -> bytes | None:
|
||||
class LlamaCppClient(GenAIClient):
|
||||
"""Generative AI client for Frigate using llama.cpp server."""
|
||||
|
||||
LOCAL_OPTIMIZED_OPTIONS = {
|
||||
"temperature": 0.7,
|
||||
"repeat_penalty": 1.05,
|
||||
"top_p": 0.8,
|
||||
}
|
||||
|
||||
provider: str # base_url
|
||||
provider_options: dict[str, Any]
|
||||
|
||||
def _init_provider(self):
|
||||
"""Initialize the client."""
|
||||
self.provider_options = {
|
||||
**self.LOCAL_OPTIMIZED_OPTIONS,
|
||||
**self.genai_config.provider_options,
|
||||
}
|
||||
return (
|
||||
|
||||
@ -38,7 +38,6 @@ const localeMap: Record<string, () => Promise<Locale>> = {
|
||||
th: () => import("date-fns/locale/th").then((module) => module.th),
|
||||
ca: () => import("date-fns/locale/ca").then((module) => module.ca),
|
||||
hr: () => import("date-fns/locale/hr").then((module) => module.hr),
|
||||
sl: () => import("date-fns/locale/sl").then((module) => module.sl),
|
||||
};
|
||||
|
||||
export function useDateLocale(): Locale {
|
||||
|
||||
@ -106,11 +106,13 @@ export default function useStats(stats: FrigateStats | undefined) {
|
||||
|
||||
const cameraName = config?.cameras?.[name]?.friendly_name ?? name;
|
||||
|
||||
// Skip ffmpeg warnings for replay cameras
|
||||
// Skip ffmpeg warnings for replay cameras when debug replay is active
|
||||
if (
|
||||
!isNaN(ffmpegAvg) &&
|
||||
ffmpegAvg >= CameraFfmpegThreshold.error &&
|
||||
!isReplayCamera(name)
|
||||
!(
|
||||
debugReplayStatus?.active && debugReplayStatus?.replay_camera === name
|
||||
)
|
||||
) {
|
||||
problems.push({
|
||||
text: t("stats.ffmpegHighCpuUsage", {
|
||||
|
||||
@ -26,7 +26,6 @@ export const supportedLanguageKeys = [
|
||||
"pl",
|
||||
"hr",
|
||||
"sk",
|
||||
"sl",
|
||||
"lt",
|
||||
"uk",
|
||||
"cs",
|
||||
|
||||
@ -381,7 +381,7 @@ export default function Replay() {
|
||||
</div>
|
||||
|
||||
{/* Side panel */}
|
||||
<div className="order-last mb-2 mt-2 flex h-full w-full flex-col overflow-hidden rounded-lg border-[1px] border-secondary-foreground bg-background_alt p-2 md:order-none md:mb-0 md:mr-2 md:mt-0 md:w-4/12">
|
||||
<div className="scrollbar-container order-last mb-2 mt-2 flex h-full w-full flex-col overflow-y-auto rounded-lg border-[1px] border-secondary-foreground bg-background_alt p-2 md:order-none md:mb-0 md:mr-2 md:mt-0 md:w-4/12">
|
||||
<div className="mb-5 flex flex-col space-y-2">
|
||||
<Heading as="h3" className="mb-0">
|
||||
{t("title")}
|
||||
@ -399,10 +399,7 @@ export default function Replay() {
|
||||
<p>{t("description")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<Tabs
|
||||
defaultValue="debug"
|
||||
className="flex min-h-0 w-full flex-1 flex-col"
|
||||
>
|
||||
<Tabs defaultValue="debug" className="flex h-full w-full flex-col">
|
||||
<TabsList className="grid w-full grid-cols-3">
|
||||
<TabsTrigger value="debug">
|
||||
{t("debug.debugging", { ns: "views/settings" })}
|
||||
@ -412,10 +409,7 @@ export default function Replay() {
|
||||
{t("websocket_messages")}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent
|
||||
value="debug"
|
||||
className="scrollbar-container mt-2 overflow-y-auto"
|
||||
>
|
||||
<TabsContent value="debug" className="mt-2">
|
||||
<div className="mt-2 space-y-6">
|
||||
<div className="my-2.5 flex flex-col gap-2.5">
|
||||
{DEBUG_OPTION_KEYS.map((key) => {
|
||||
@ -560,10 +554,7 @@ export default function Replay() {
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent
|
||||
value="objects"
|
||||
className="scrollbar-container mt-2 overflow-y-auto"
|
||||
>
|
||||
<TabsContent value="objects" className="mt-2">
|
||||
<ObjectList
|
||||
cameraConfig={replayCameraConfig}
|
||||
objects={objects}
|
||||
|
||||
@ -9,7 +9,6 @@ import {
|
||||
useState,
|
||||
} from "react";
|
||||
import { isCurrentHour } from "@/utils/dateUtil";
|
||||
import { isFirefox, isMobile, isSafari } from "react-device-detect";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { CameraConfig } from "@/types/frigateConfig";
|
||||
import useSWR from "swr";
|
||||
@ -306,46 +305,31 @@ function MotionPreviewClip({
|
||||
);
|
||||
}, [clipStart, preview, range.end_time]);
|
||||
|
||||
const compatIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (compatIntervalRef.current) {
|
||||
clearInterval(compatIntervalRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const resetPlayback = useCallback(() => {
|
||||
if (!videoRef.current || !preview) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (compatIntervalRef.current) {
|
||||
clearInterval(compatIntervalRef.current);
|
||||
compatIntervalRef.current = null;
|
||||
}
|
||||
|
||||
videoRef.current.currentTime = clipStart;
|
||||
videoRef.current.playbackRate = playbackRate;
|
||||
}, [clipStart, playbackRate, preview]);
|
||||
|
||||
if (isSafari || (isFirefox && isMobile)) {
|
||||
// Safari / iOS can't play at speeds > 2x, so manually step through frames
|
||||
videoRef.current.pause();
|
||||
compatIntervalRef.current = setInterval(() => {
|
||||
if (!videoRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
videoRef.current.currentTime += 1;
|
||||
|
||||
if (videoRef.current.currentTime >= clipEnd) {
|
||||
videoRef.current.currentTime = clipStart;
|
||||
}
|
||||
}, 1000 / playbackRate);
|
||||
} else {
|
||||
videoRef.current.playbackRate = playbackRate;
|
||||
useEffect(() => {
|
||||
if (!videoRef.current || !preview) {
|
||||
return;
|
||||
}
|
||||
}, [clipStart, clipEnd, playbackRate, preview]);
|
||||
|
||||
if (!isVisible) {
|
||||
videoRef.current.pause();
|
||||
videoRef.current.currentTime = clipStart;
|
||||
return;
|
||||
}
|
||||
|
||||
if (videoRef.current.readyState >= 2) {
|
||||
resetPlayback();
|
||||
void videoRef.current.play().catch(() => undefined);
|
||||
}
|
||||
}, [clipStart, isVisible, preview, resetPlayback]);
|
||||
|
||||
const drawDimOverlay = useCallback(() => {
|
||||
if (!dimOverlayCanvasRef.current) {
|
||||
@ -479,17 +463,15 @@ function MotionPreviewClip({
|
||||
{showLoadingIndicator && (
|
||||
<Skeleton className="absolute inset-0 z-10 rounded-lg md:rounded-2xl" />
|
||||
)}
|
||||
{preview && isVisible ? (
|
||||
{preview ? (
|
||||
<>
|
||||
<video
|
||||
ref={videoRef}
|
||||
className="size-full bg-black object-contain"
|
||||
preload="auto"
|
||||
autoPlay
|
||||
playsInline
|
||||
preload={isVisible ? "metadata" : "none"}
|
||||
muted
|
||||
disableRemotePlayback
|
||||
loop
|
||||
autoPlay={isVisible}
|
||||
onLoadedMetadata={() => {
|
||||
setVideoLoaded(true);
|
||||
|
||||
@ -499,21 +481,36 @@ function MotionPreviewClip({
|
||||
height: videoRef.current.videoHeight,
|
||||
});
|
||||
}
|
||||
|
||||
if (!isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
resetPlayback();
|
||||
|
||||
if (videoRef.current) {
|
||||
void videoRef.current.play().catch(() => undefined);
|
||||
}
|
||||
}}
|
||||
onCanPlay={() => {
|
||||
setVideoLoaded(true);
|
||||
|
||||
if (!isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (videoRef.current) {
|
||||
void videoRef.current.play().catch(() => undefined);
|
||||
}
|
||||
}}
|
||||
onPlay={() => {
|
||||
setVideoPlaying(true);
|
||||
resetPlayback();
|
||||
}}
|
||||
onPlay={() => setVideoPlaying(true)}
|
||||
onLoadedData={() => setVideoLoaded(true)}
|
||||
onError={() => {
|
||||
setVideoLoaded(true);
|
||||
setVideoPlaying(true);
|
||||
}}
|
||||
onTimeUpdate={() => {
|
||||
if (!videoRef.current || !preview) {
|
||||
if (!videoRef.current || !preview || !isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -522,10 +519,12 @@ function MotionPreviewClip({
|
||||
}
|
||||
}}
|
||||
>
|
||||
<source
|
||||
src={`${baseUrl}${preview.src.substring(1)}`}
|
||||
type={preview.type}
|
||||
/>
|
||||
{isVisible && (
|
||||
<source
|
||||
src={`${baseUrl}${preview.src.substring(1)}`}
|
||||
type={preview.type}
|
||||
/>
|
||||
)}
|
||||
</video>
|
||||
{motionHeatmap && (
|
||||
<canvas
|
||||
|
||||
@ -4,7 +4,7 @@ import useSWR from "swr";
|
||||
import axios from "axios";
|
||||
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
||||
import AutoUpdatingCameraImage from "@/components/camera/AutoUpdatingCameraImage";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { Slider } from "@/components/ui/slider";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
@ -63,8 +63,6 @@ export default function MotionTunerView({
|
||||
improve_contrast: undefined,
|
||||
});
|
||||
|
||||
const userInteractedRef = useRef(false);
|
||||
|
||||
const cameraConfig = useMemo(() => {
|
||||
if (config && selectedCamera) {
|
||||
return config.cameras[selectedCamera];
|
||||
@ -72,7 +70,6 @@ export default function MotionTunerView({
|
||||
}, [config, selectedCamera]);
|
||||
|
||||
useEffect(() => {
|
||||
userInteractedRef.current = false;
|
||||
if (cameraConfig) {
|
||||
setMotionSettings({
|
||||
threshold: cameraConfig.motion.threshold,
|
||||
@ -90,29 +87,24 @@ export default function MotionTunerView({
|
||||
}, [selectedCamera]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!motionSettings.threshold || !userInteractedRef.current) return;
|
||||
if (!motionSettings.threshold) return;
|
||||
|
||||
sendMotionThreshold(motionSettings.threshold);
|
||||
}, [motionSettings.threshold, sendMotionThreshold]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!motionSettings.contour_area || !userInteractedRef.current) return;
|
||||
if (!motionSettings.contour_area) return;
|
||||
|
||||
sendMotionContourArea(motionSettings.contour_area);
|
||||
}, [motionSettings.contour_area, sendMotionContourArea]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
motionSettings.improve_contrast === undefined ||
|
||||
!userInteractedRef.current
|
||||
)
|
||||
return;
|
||||
if (motionSettings.improve_contrast === undefined) return;
|
||||
|
||||
sendImproveContrast(motionSettings.improve_contrast ? "ON" : "OFF");
|
||||
}, [motionSettings.improve_contrast, sendImproveContrast]);
|
||||
|
||||
const handleMotionConfigChange = (newConfig: Partial<MotionSettings>) => {
|
||||
userInteractedRef.current = true;
|
||||
setMotionSettings((prevConfig) => ({ ...prevConfig, ...newConfig }));
|
||||
setUnsavedChanges(true);
|
||||
setChangedValue(true);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user