From ae8932e27ebcb200dfb45c5371af4718b826cb65 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Sat, 10 Feb 2024 12:33:37 -0700 Subject: [PATCH] Show animated gif for event thumb --- frigate/http.py | 28 ++++++++++--------- .../image/AnimatedEventThumbnail.tsx | 13 +++++---- web/src/components/player/LivePlayer.tsx | 11 ++++---- web/src/pages/Live.tsx | 4 +-- 4 files changed, 31 insertions(+), 25 deletions(-) diff --git a/frigate/http.py b/frigate/http.py index 69cfd272f..c7d9b979c 100644 --- a/frigate/http.py +++ b/frigate/http.py @@ -597,8 +597,6 @@ def event_thumbnail(id, max_cache_age=2592000): def event_preview(id: str, max_cache_age=2592000): try: event: Event = Event.get(Event.id == id) - if event.end_time is not None: - event_complete = True except DoesNotExist: return make_response( jsonify({"success": False, "message": "Event not found"}), 404 @@ -660,7 +658,6 @@ def event_preview(id: str, max_cache_age=2592000): "gif", "-", ] - logger.info(f"running previous gif creation {' '.join(ffmpeg_cmd)}") process = sp.run( ffmpeg_cmd, @@ -685,9 +682,14 @@ def event_preview(id: str, max_cache_age=2592000): if file > end_file: break - selected_previews.append(f"file '{file}'") + selected_previews.append(f"file '/tmp/cache/preview_frames/{file}'") selected_previews.append("duration 0.12") + if not selected_previews: + return make_response( + jsonify({"success": False, "message": "Preview not found"}), 404 + ) + last_file = selected_previews[-2] selected_previews.append(last_file) @@ -713,24 +715,24 @@ def event_preview(id: str, max_cache_age=2592000): "gif", "-", ] - logger.info( - f"running current gif creation {' '.join(ffmpeg_cmd)} with files {' '.join(selected_previews)}" - ) process = sp.run( ffmpeg_cmd, - input="\n".join(selected_previews), - encoding="ascii", + input=str.encode("\n".join(selected_previews)), capture_output=True, ) + + if process.returncode != 0: + return make_response( + jsonify({"success": False, "message": "Unable to create preview gif"}), + 500, + ) + gif_bytes = process.stdout response = make_response(gif_bytes) response.headers["Content-Type"] = "image/gif" - if event_complete: - response.headers["Cache-Control"] = f"private, max-age={max_cache_age}" - else: - response.headers["Cache-Control"] = "no-store" + response.headers["Cache-Control"] = f"private, max-age={max_cache_age}" return response diff --git a/web/src/components/image/AnimatedEventThumbnail.tsx b/web/src/components/image/AnimatedEventThumbnail.tsx index 7c187230c..e397d2eb4 100644 --- a/web/src/components/image/AnimatedEventThumbnail.tsx +++ b/web/src/components/image/AnimatedEventThumbnail.tsx @@ -4,18 +4,21 @@ import { LuStar } from "react-icons/lu"; import TimeAgo from "../dynamic/TimeAgo"; import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; -type EventThumbnailProps = { +type AnimatedEventThumbnailProps = { event: FrigateEvent; onFavorite?: (e: Event, event: FrigateEvent) => void; }; -export function EventThumbnail({ event, onFavorite }: EventThumbnailProps) { +export function AnimatedEventThumbnail({ + event, + onFavorite, +}: AnimatedEventThumbnailProps) { return (
(onFavorite ? onFavorite(e, event) : null)} fill={event.retain_indefinitely ? "currentColor" : "none"} /> -
+
diff --git a/web/src/components/player/LivePlayer.tsx b/web/src/components/player/LivePlayer.tsx index a13a43707..1c41f9ca5 100644 --- a/web/src/components/player/LivePlayer.tsx +++ b/web/src/components/player/LivePlayer.tsx @@ -40,22 +40,23 @@ export default function LivePlayer({ const { activeMotion, activeAudio, activeTracking } = useCameraActivity(cameraConfig); + const cameraActive = useMemo(() => activeMotion || activeTracking, [activeMotion, activeTracking]) const liveMode = useCameraLiveMode(cameraConfig, preferredLiveMode); const [liveReady, setLiveReady] = useState(false); useEffect(() => { if (!liveReady) { - if (activeMotion && liveMode == "jsmpeg") { + if (cameraActive && liveMode == "jsmpeg") { setLiveReady(true); } return; } - if (!activeMotion && !activeTracking) { + if (!cameraActive) { setLiveReady(false); } - }, [activeMotion, activeTracking, liveReady]); + }, [cameraActive, liveReady]); const { payload: recording } = useRecordingsState(cameraConfig.name); @@ -167,7 +168,7 @@ export default function LivePlayer({ : "outline-0" } transition-all duration-500 ${className}`} > - {(showStillWithoutActivity == false || activeMotion || activeTracking) && + {(showStillWithoutActivity == false || cameraActive) && player}
diff --git a/web/src/pages/Live.tsx b/web/src/pages/Live.tsx index 2f4270bb1..6f6d315d9 100644 --- a/web/src/pages/Live.tsx +++ b/web/src/pages/Live.tsx @@ -1,4 +1,4 @@ -import { EventThumbnail } from "@/components/image/EventThumbnail"; +import { AnimatedEventThumbnail } from "@/components/image/AnimatedEventThumbnail"; import LivePlayer from "@/components/player/LivePlayer"; import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"; import { TooltipProvider } from "@/components/ui/tooltip"; @@ -62,7 +62,7 @@ function Live() {
{events.map((event) => { return ( -