mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-07 03:35:26 +03:00
Separate to separate event function
This commit is contained in:
parent
bea592bff9
commit
cbe39caa3b
@ -633,189 +633,29 @@ export default function Events({ path, ...props }) {
|
|||||||
{showInProgress ? <MenuOpen className="w-6" /> : <MenuIcon className="w-6" />}
|
{showInProgress ? <MenuOpen className="w-6" /> : <MenuIcon className="w-6" />}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
{showInProgress && ongoingEvents.map((event, _) => {
|
{showInProgress &&
|
||||||
return (
|
ongoingEvents.map((event, _) => {
|
||||||
<Fragment key={event.id}>
|
return (
|
||||||
<div
|
<Event
|
||||||
className="my-2 flex bg-slate-100 dark:bg-slate-800 rounded cursor-pointer min-w-[330px]"
|
className="my-2"
|
||||||
onClick={() => (viewEvent === event.id ? setViewEvent(null) : setViewEvent(event.id))}
|
key={event.id}
|
||||||
>
|
config={config}
|
||||||
<div
|
event={event}
|
||||||
className="relative rounded-l flex-initial min-w-[125px] h-[125px] bg-contain bg-no-repeat bg-center"
|
eventDetailType={eventDetailType}
|
||||||
style={{
|
eventOverlay={eventOverlay}
|
||||||
'background-image': `url(${apiHost}api/events/${event.id}/thumbnail.jpg)`,
|
setEventOverlay={setEventOverlay}
|
||||||
}}
|
viewEvent={viewEvent}
|
||||||
>
|
setViewEvent={setViewEvent}
|
||||||
<StarRecording
|
uploading={uploading}
|
||||||
className="h-6 w-6 text-yellow-300 absolute top-1 right-1 cursor-pointer"
|
handleEventDetailTabChange={handleEventDetailTabChange}
|
||||||
onClick={(e) => onSave(e, event.id, !event.retain_indefinitely)}
|
onEventFrameSelected={onEventFrameSelected}
|
||||||
fill={event.retain_indefinitely ? 'currentColor' : 'none'}
|
onDelete={onDelete}
|
||||||
/>
|
onDownloadClick={onDownloadClick}
|
||||||
{event.end_time ? null : (
|
onSave={onSave}
|
||||||
<div className="bg-slate-300 dark:bg-slate-700 absolute bottom-0 text-center w-full uppercase text-sm rounded-bl">
|
showSubmitToPlus={showSubmitToPlus}
|
||||||
In progress
|
/>
|
||||||
</div>
|
);
|
||||||
)}
|
})}
|
||||||
</div>
|
|
||||||
<div className="m-2 flex grow">
|
|
||||||
<div className="flex flex-col grow">
|
|
||||||
<div className="capitalize text-lg font-bold">
|
|
||||||
{event.label.replaceAll('_', ' ')}
|
|
||||||
{event.sub_label ? `: ${event.sub_label.replaceAll('_', ' ')}` : null}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-sm flex">
|
|
||||||
<Clock className="h-5 w-5 mr-2 inline" />
|
|
||||||
{formatUnixTimestampToDateTime(event.start_time, { ...config.ui })}
|
|
||||||
<div className="hidden md:inline">
|
|
||||||
<span className="m-1">-</span>
|
|
||||||
<TimeAgo time={event.start_time * 1000} dense />
|
|
||||||
</div>
|
|
||||||
<div className="hidden md:inline">
|
|
||||||
<span className="m-1" />( {getDurationFromTimestamps(event.start_time, event.end_time)} )
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="capitalize text-sm flex align-center mt-1">
|
|
||||||
<Camera className="h-5 w-5 mr-2 inline" />
|
|
||||||
{event.camera.replaceAll('_', ' ')}
|
|
||||||
</div>
|
|
||||||
{event.zones.length ? (
|
|
||||||
<div className="capitalize text-sm flex align-center">
|
|
||||||
<Zone className="w-5 h-5 mr-2 inline" />
|
|
||||||
{event.zones.join(', ').replaceAll('_', ' ')}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
<div className="capitalize text-sm flex align-center">
|
|
||||||
<Score className="w-5 h-5 mr-2 inline" />
|
|
||||||
{(event?.data?.top_score || event.top_score || 0) == 0
|
|
||||||
? null
|
|
||||||
: `${event.label}: ${((event?.data?.top_score || event.top_score) * 100).toFixed(0)}%`}
|
|
||||||
{(event?.data?.sub_label_score || 0) == 0
|
|
||||||
? null
|
|
||||||
: `, ${event.sub_label}: ${(event?.data?.sub_label_score * 100).toFixed(0)}%`}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="hidden sm:flex flex-col justify-end mr-2">
|
|
||||||
{event.end_time && event.has_snapshot && (event?.data?.type || 'object') == 'object' && (
|
|
||||||
<Fragment>
|
|
||||||
{event.plus_id ? (
|
|
||||||
<div className="uppercase text-xs underline">
|
|
||||||
<Link
|
|
||||||
href={`https://plus.frigate.video/dashboard/edit-image/?id=${event.plus_id}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="nofollow"
|
|
||||||
>
|
|
||||||
Edit in Frigate+
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Button
|
|
||||||
color="gray"
|
|
||||||
disabled={uploading.includes(event.id)}
|
|
||||||
onClick={(e) =>
|
|
||||||
showSubmitToPlus(event.id, event.label, event?.data?.box || event.box, e)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{uploading.includes(event.id) ? 'Uploading...' : 'Send to Frigate+'}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</Fragment>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<Delete
|
|
||||||
className="h-6 w-6 cursor-pointer"
|
|
||||||
stroke="#f87171"
|
|
||||||
onClick={(e) => onDelete(e, event.id, event.retain_indefinitely)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Download
|
|
||||||
className="h-6 w-6 mt-auto"
|
|
||||||
stroke={event.has_clip || event.has_snapshot ? '#3b82f6' : '#cbd5e1'}
|
|
||||||
onClick={(e) => onDownloadClick(e, event)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{viewEvent !== event.id ? null : (
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="mx-auto max-w-7xl">
|
|
||||||
<div className="flex justify-center w-full py-2">
|
|
||||||
<Tabs
|
|
||||||
selectedIndex={event.has_clip && eventDetailType == 'clip' ? 0 : 1}
|
|
||||||
onChange={handleEventDetailTabChange}
|
|
||||||
className="justify"
|
|
||||||
>
|
|
||||||
<TextTab text="Clip" disabled={!event.has_clip} />
|
|
||||||
<TextTab text={event.has_snapshot ? 'Snapshot' : 'Thumbnail'} />
|
|
||||||
</Tabs>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{eventDetailType == 'clip' && event.has_clip ? (
|
|
||||||
<div>
|
|
||||||
<TimelineSummary
|
|
||||||
event={event}
|
|
||||||
onFrameSelected={(frame, seekSeconds) =>
|
|
||||||
onEventFrameSelected(event, frame, seekSeconds)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<VideoPlayer
|
|
||||||
options={{
|
|
||||||
preload: 'auto',
|
|
||||||
autoplay: true,
|
|
||||||
sources: [
|
|
||||||
{
|
|
||||||
src: `${apiHost}vod/event/${event.id}/master.m3u8`,
|
|
||||||
type: 'application/vnd.apple.mpegurl',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}}
|
|
||||||
seekOptions={{ forward: 10, backward: 5 }}
|
|
||||||
onReady={(player) => {
|
|
||||||
this.player = player;
|
|
||||||
this.player.on('playing', () => {
|
|
||||||
setEventOverlay(undefined);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
onDispose={() => {
|
|
||||||
this.player = null;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{eventOverlay ? (
|
|
||||||
<TimelineEventOverlay
|
|
||||||
eventOverlay={eventOverlay}
|
|
||||||
cameraConfig={config.cameras[event.camera]}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</VideoPlayer>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{eventDetailType == 'image' || !event.has_clip ? (
|
|
||||||
<div className="flex justify-center">
|
|
||||||
<img
|
|
||||||
className="flex-grow-0"
|
|
||||||
src={
|
|
||||||
event.has_snapshot
|
|
||||||
? `${apiHost}api/events/${event.id}/snapshot.jpg`
|
|
||||||
: `${apiHost}api/events/${event.id}/thumbnail.jpg`
|
|
||||||
}
|
|
||||||
alt={`${event.label} at ${((event?.data?.top_score || event.top_score) * 100).toFixed(
|
|
||||||
0
|
|
||||||
)}% confidence`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<Heading className="py-4" size="sm">
|
<Heading className="py-4" size="sm">
|
||||||
@ -827,186 +667,25 @@ export default function Events({ path, ...props }) {
|
|||||||
return page.map((event, j) => {
|
return page.map((event, j) => {
|
||||||
const lastEvent = lastPage && page.length === j + 1;
|
const lastEvent = lastPage && page.length === j + 1;
|
||||||
return (
|
return (
|
||||||
<Fragment key={event.id}>
|
<Event
|
||||||
<div
|
key={event.id}
|
||||||
ref={lastEvent ? lastEventRef : false}
|
config={config}
|
||||||
className="flex bg-slate-100 dark:bg-slate-800 rounded cursor-pointer min-w-[330px]"
|
event={event}
|
||||||
onClick={() => (viewEvent === event.id ? setViewEvent(null) : setViewEvent(event.id))}
|
eventDetailType={eventDetailType}
|
||||||
>
|
eventOverlay={eventOverlay}
|
||||||
<div
|
setEventOverlay={setEventOverlay}
|
||||||
className="relative rounded-l flex-initial min-w-[125px] h-[125px] bg-contain bg-no-repeat bg-center"
|
viewEvent={viewEvent}
|
||||||
style={{
|
setViewEvent={setViewEvent}
|
||||||
'background-image': `url(${apiHost}api/events/${event.id}/thumbnail.jpg)`,
|
lastEvent={lastEvent}
|
||||||
}}
|
lastEventRef={lastEventRef}
|
||||||
>
|
uploading={uploading}
|
||||||
<StarRecording
|
handleEventDetailTabChange={handleEventDetailTabChange}
|
||||||
className="h-6 w-6 text-yellow-300 absolute top-1 right-1 cursor-pointer"
|
onEventFrameSelected={onEventFrameSelected}
|
||||||
onClick={(e) => onSave(e, event.id, !event.retain_indefinitely)}
|
onDelete={onDelete}
|
||||||
fill={event.retain_indefinitely ? 'currentColor' : 'none'}
|
onDownloadClick={onDownloadClick}
|
||||||
/>
|
onSave={onSave}
|
||||||
{event.end_time ? null : (
|
showSubmitToPlus={showSubmitToPlus}
|
||||||
<div className="bg-slate-300 dark:bg-slate-700 absolute bottom-0 text-center w-full uppercase text-sm rounded-bl">
|
/>
|
||||||
In progress
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="m-2 flex grow">
|
|
||||||
<div className="flex flex-col grow">
|
|
||||||
<div className="capitalize text-lg font-bold">
|
|
||||||
{event.label.replaceAll('_', ' ')}
|
|
||||||
{event.sub_label ? `: ${event.sub_label.replaceAll('_', ' ')}` : null}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-sm flex">
|
|
||||||
<Clock className="h-5 w-5 mr-2 inline" />
|
|
||||||
{formatUnixTimestampToDateTime(event.start_time, { ...config.ui })}
|
|
||||||
<div className="hidden md:inline">
|
|
||||||
<span className="m-1">-</span>
|
|
||||||
<TimeAgo time={event.start_time * 1000} dense />
|
|
||||||
</div>
|
|
||||||
<div className="hidden md:inline">
|
|
||||||
<span className="m-1" />( {getDurationFromTimestamps(event.start_time, event.end_time)} )
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="capitalize text-sm flex align-center mt-1">
|
|
||||||
<Camera className="h-5 w-5 mr-2 inline" />
|
|
||||||
{event.camera.replaceAll('_', ' ')}
|
|
||||||
</div>
|
|
||||||
{event.zones.length ? (
|
|
||||||
<div className="capitalize text-sm flex align-center">
|
|
||||||
<Zone className="w-5 h-5 mr-2 inline" />
|
|
||||||
{event.zones.join(', ').replaceAll('_', ' ')}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
<div className="capitalize text-sm flex align-center">
|
|
||||||
<Score className="w-5 h-5 mr-2 inline" />
|
|
||||||
{(event?.data?.top_score || event.top_score || 0) == 0
|
|
||||||
? null
|
|
||||||
: `${event.label}: ${((event?.data?.top_score || event.top_score) * 100).toFixed(0)}%`}
|
|
||||||
{(event?.data?.sub_label_score || 0) == 0
|
|
||||||
? null
|
|
||||||
: `, ${event.sub_label}: ${(event?.data?.sub_label_score * 100).toFixed(0)}%`}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="hidden sm:flex flex-col justify-end mr-2">
|
|
||||||
{event.end_time && event.has_snapshot && (event?.data?.type || 'object') == 'object' && (
|
|
||||||
<Fragment>
|
|
||||||
{event.plus_id ? (
|
|
||||||
<div className="uppercase text-xs underline">
|
|
||||||
<Link
|
|
||||||
href={`https://plus.frigate.video/dashboard/edit-image/?id=${event.plus_id}`}
|
|
||||||
target="_blank"
|
|
||||||
rel="nofollow"
|
|
||||||
>
|
|
||||||
Edit in Frigate+
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Button
|
|
||||||
color="gray"
|
|
||||||
disabled={uploading.includes(event.id)}
|
|
||||||
onClick={(e) =>
|
|
||||||
showSubmitToPlus(event.id, event.label, event?.data?.box || event.box, e)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{uploading.includes(event.id) ? 'Uploading...' : 'Send to Frigate+'}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</Fragment>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<Delete
|
|
||||||
className="h-6 w-6 cursor-pointer"
|
|
||||||
stroke="#f87171"
|
|
||||||
onClick={(e) => onDelete(e, event.id, event.retain_indefinitely)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Download
|
|
||||||
className="h-6 w-6 mt-auto"
|
|
||||||
stroke={event.has_clip || event.has_snapshot ? '#3b82f6' : '#cbd5e1'}
|
|
||||||
onClick={(e) => onDownloadClick(e, event)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{viewEvent !== event.id ? null : (
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="mx-auto max-w-7xl">
|
|
||||||
<div className="flex justify-center w-full py-2">
|
|
||||||
<Tabs
|
|
||||||
selectedIndex={event.has_clip && eventDetailType == 'clip' ? 0 : 1}
|
|
||||||
onChange={handleEventDetailTabChange}
|
|
||||||
className="justify"
|
|
||||||
>
|
|
||||||
<TextTab text="Clip" disabled={!event.has_clip} />
|
|
||||||
<TextTab text={event.has_snapshot ? 'Snapshot' : 'Thumbnail'} />
|
|
||||||
</Tabs>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
{eventDetailType == 'clip' && event.has_clip ? (
|
|
||||||
<div>
|
|
||||||
<TimelineSummary
|
|
||||||
event={event}
|
|
||||||
onFrameSelected={(frame, seekSeconds) =>
|
|
||||||
onEventFrameSelected(event, frame, seekSeconds)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<div>
|
|
||||||
<VideoPlayer
|
|
||||||
options={{
|
|
||||||
preload: 'auto',
|
|
||||||
autoplay: true,
|
|
||||||
sources: [
|
|
||||||
{
|
|
||||||
src: `${apiHost}vod/event/${event.id}/master.m3u8`,
|
|
||||||
type: 'application/vnd.apple.mpegurl',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}}
|
|
||||||
seekOptions={{ forward: 10, backward: 5 }}
|
|
||||||
onReady={(player) => {
|
|
||||||
this.player = player;
|
|
||||||
this.player.on('playing', () => {
|
|
||||||
setEventOverlay(undefined);
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
onDispose={() => {
|
|
||||||
this.player = null;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{eventOverlay ? (
|
|
||||||
<TimelineEventOverlay
|
|
||||||
eventOverlay={eventOverlay}
|
|
||||||
cameraConfig={config.cameras[event.camera]}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</VideoPlayer>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{eventDetailType == 'image' || !event.has_clip ? (
|
|
||||||
<div className="flex justify-center">
|
|
||||||
<img
|
|
||||||
className="flex-grow-0"
|
|
||||||
src={
|
|
||||||
event.has_snapshot
|
|
||||||
? `${apiHost}api/events/${event.id}/snapshot.jpg`
|
|
||||||
: `${apiHost}api/events/${event.id}/thumbnail.jpg`
|
|
||||||
}
|
|
||||||
alt={`${event.label} at ${((event?.data?.top_score || event.top_score) * 100).toFixed(
|
|
||||||
0
|
|
||||||
)}% confidence`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</Fragment>
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@ -1018,3 +697,201 @@ export default function Events({ path, ...props }) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Event({
|
||||||
|
className = '',
|
||||||
|
config,
|
||||||
|
event,
|
||||||
|
eventDetailType,
|
||||||
|
eventOverlay,
|
||||||
|
setEventOverlay,
|
||||||
|
viewEvent,
|
||||||
|
setViewEvent,
|
||||||
|
lastEvent,
|
||||||
|
lastEventRef,
|
||||||
|
uploading,
|
||||||
|
handleEventDetailTabChange,
|
||||||
|
onEventFrameSelected,
|
||||||
|
onDelete,
|
||||||
|
onDownloadClick,
|
||||||
|
onSave,
|
||||||
|
showSubmitToPlus,
|
||||||
|
}) {
|
||||||
|
const apiHost = useApiHost();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={className}>
|
||||||
|
<div
|
||||||
|
ref={lastEvent ? lastEventRef : false}
|
||||||
|
className="flex bg-slate-100 dark:bg-slate-800 rounded cursor-pointer min-w-[330px]"
|
||||||
|
onClick={() => (viewEvent === event.id ? setViewEvent(null) : setViewEvent(event.id))}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="relative rounded-l flex-initial min-w-[125px] h-[125px] bg-contain bg-no-repeat bg-center"
|
||||||
|
style={{
|
||||||
|
'background-image': `url(${apiHost}api/events/${event.id}/thumbnail.jpg)`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<StarRecording
|
||||||
|
className="h-6 w-6 text-yellow-300 absolute top-1 right-1 cursor-pointer"
|
||||||
|
onClick={(e) => onSave(e, event.id, !event.retain_indefinitely)}
|
||||||
|
fill={event.retain_indefinitely ? 'currentColor' : 'none'}
|
||||||
|
/>
|
||||||
|
{event.end_time ? null : (
|
||||||
|
<div className="bg-slate-300 dark:bg-slate-700 absolute bottom-0 text-center w-full uppercase text-sm rounded-bl">
|
||||||
|
In progress
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="m-2 flex grow">
|
||||||
|
<div className="flex flex-col grow">
|
||||||
|
<div className="capitalize text-lg font-bold">
|
||||||
|
{event.label.replaceAll('_', ' ')}
|
||||||
|
{event.sub_label ? `: ${event.sub_label.replaceAll('_', ' ')}` : null}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-sm flex">
|
||||||
|
<Clock className="h-5 w-5 mr-2 inline" />
|
||||||
|
{formatUnixTimestampToDateTime(event.start_time, { ...config.ui })}
|
||||||
|
<div className="hidden md:inline">
|
||||||
|
<span className="m-1">-</span>
|
||||||
|
<TimeAgo time={event.start_time * 1000} dense />
|
||||||
|
</div>
|
||||||
|
<div className="hidden md:inline">
|
||||||
|
<span className="m-1" />( {getDurationFromTimestamps(event.start_time, event.end_time)} )
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="capitalize text-sm flex align-center mt-1">
|
||||||
|
<Camera className="h-5 w-5 mr-2 inline" />
|
||||||
|
{event.camera.replaceAll('_', ' ')}
|
||||||
|
</div>
|
||||||
|
{event.zones.length ? (
|
||||||
|
<div className="capitalize text-sm flex align-center">
|
||||||
|
<Zone className="w-5 h-5 mr-2 inline" />
|
||||||
|
{event.zones.join(', ').replaceAll('_', ' ')}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
<div className="capitalize text-sm flex align-center">
|
||||||
|
<Score className="w-5 h-5 mr-2 inline" />
|
||||||
|
{(event?.data?.top_score || event.top_score || 0) == 0
|
||||||
|
? null
|
||||||
|
: `${event.label}: ${((event?.data?.top_score || event.top_score) * 100).toFixed(0)}%`}
|
||||||
|
{(event?.data?.sub_label_score || 0) == 0
|
||||||
|
? null
|
||||||
|
: `, ${event.sub_label}: ${(event?.data?.sub_label_score * 100).toFixed(0)}%`}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hidden sm:flex flex-col justify-end mr-2">
|
||||||
|
{event.end_time && event.has_snapshot && (event?.data?.type || 'object') == 'object' && (
|
||||||
|
<Fragment>
|
||||||
|
{event.plus_id ? (
|
||||||
|
<div className="uppercase text-xs underline">
|
||||||
|
<Link
|
||||||
|
href={`https://plus.frigate.video/dashboard/edit-image/?id=${event.plus_id}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="nofollow"
|
||||||
|
>
|
||||||
|
Edit in Frigate+
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
color="gray"
|
||||||
|
disabled={uploading.includes(event.id)}
|
||||||
|
onClick={(e) => showSubmitToPlus(event.id, event.label, event?.data?.box || event.box, e)}
|
||||||
|
>
|
||||||
|
{uploading.includes(event.id) ? 'Uploading...' : 'Send to Frigate+'}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Fragment>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<Delete
|
||||||
|
className="h-6 w-6 cursor-pointer"
|
||||||
|
stroke="#f87171"
|
||||||
|
onClick={(e) => onDelete(e, event.id, event.retain_indefinitely)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Download
|
||||||
|
className="h-6 w-6 mt-auto"
|
||||||
|
stroke={event.has_clip || event.has_snapshot ? '#3b82f6' : '#cbd5e1'}
|
||||||
|
onClick={(e) => onDownloadClick(e, event)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{viewEvent !== event.id ? null : (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="mx-auto max-w-7xl">
|
||||||
|
<div className="flex justify-center w-full py-2">
|
||||||
|
<Tabs
|
||||||
|
selectedIndex={event.has_clip && eventDetailType == 'clip' ? 0 : 1}
|
||||||
|
onChange={handleEventDetailTabChange}
|
||||||
|
className="justify"
|
||||||
|
>
|
||||||
|
<TextTab text="Clip" disabled={!event.has_clip} />
|
||||||
|
<TextTab text={event.has_snapshot ? 'Snapshot' : 'Thumbnail'} />
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{eventDetailType == 'clip' && event.has_clip ? (
|
||||||
|
<div>
|
||||||
|
<TimelineSummary
|
||||||
|
event={event}
|
||||||
|
onFrameSelected={(frame, seekSeconds) => onEventFrameSelected(event, frame, seekSeconds)}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<VideoPlayer
|
||||||
|
options={{
|
||||||
|
preload: 'auto',
|
||||||
|
autoplay: true,
|
||||||
|
sources: [
|
||||||
|
{
|
||||||
|
src: `${apiHost}vod/event/${event.id}/master.m3u8`,
|
||||||
|
type: 'application/vnd.apple.mpegurl',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
seekOptions={{ forward: 10, backward: 5 }}
|
||||||
|
onReady={(player) => {
|
||||||
|
this.player = player;
|
||||||
|
this.player.on('playing', () => {
|
||||||
|
setEventOverlay(undefined);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
onDispose={() => {
|
||||||
|
this.player = null;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{eventOverlay ? (
|
||||||
|
<TimelineEventOverlay eventOverlay={eventOverlay} cameraConfig={config.cameras[event.camera]} />
|
||||||
|
) : null}
|
||||||
|
</VideoPlayer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{eventDetailType == 'image' || !event.has_clip ? (
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<img
|
||||||
|
className="flex-grow-0"
|
||||||
|
src={
|
||||||
|
event.has_snapshot
|
||||||
|
? `${apiHost}api/events/${event.id}/snapshot.jpg`
|
||||||
|
: `${apiHost}api/events/${event.id}/thumbnail.jpg`
|
||||||
|
}
|
||||||
|
alt={`${event.label} at ${((event?.data?.top_score || event.top_score) * 100).toFixed(
|
||||||
|
0
|
||||||
|
)}% confidence`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user