mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-08 20:25:26 +03:00
Show animated gif for event thumb
This commit is contained in:
parent
a83f07d8c8
commit
ae8932e27e
@ -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
|
||||
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user