Show animated gif for event thumb

This commit is contained in:
Nicolas Mowen 2024-02-10 12:33:37 -07:00
parent a83f07d8c8
commit ae8932e27e
4 changed files with 31 additions and 25 deletions

View File

@ -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

View File

@ -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 (
<Tooltip>
<TooltipTrigger asChild>
<div
className="relative rounded bg-cover aspect-square h-24 bg-no-repeat bg-center mr-4"
className="relative rounded bg-cover aspect-video h-24 bg-no-repeat bg-center mr-4"
style={{
backgroundImage: `url(${baseUrl}api/events/${event.id}/thumbnail.jpg)`,
backgroundImage: `url(${baseUrl}api/events/${event.id}/preview.gif)`,
}}
>
<LuStar
@ -23,7 +26,7 @@ export function EventThumbnail({ event, onFavorite }: EventThumbnailProps) {
onClick={(e: Event) => (onFavorite ? onFavorite(e, event) : null)}
fill={event.retain_indefinitely ? "currentColor" : "none"}
/>
<div className="absolute bottom-0 w-full h-6 bg-gradient-to-t from-slate-900/50 to-transparent">
<div className="absolute bottom-0 w-full h-6 bg-gradient-to-t from-slate-900/50 to-transparent rounded">
<div className="absolute left-1 bottom-0 text-xs text-white w-full">
<TimeAgo time={event.start_time * 1000} dense />
</div>

View File

@ -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}
<div
@ -179,7 +180,7 @@ export default function LivePlayer({
className="w-full h-full"
camera={cameraConfig.name}
showFps={false}
reloadInterval={30000}
reloadInterval={(cameraActive && !liveReady) ? 200 : 30000}
/>
</div>

View File

@ -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() {
<div className="flex">
{events.map((event) => {
return (
<EventThumbnail
<AnimatedEventThumbnail
key={event.id}
event={event}
onFavorite={onFavorite}