fix open recordings from host

This commit is contained in:
NlightN22 2024-05-03 14:39:29 +07:00
parent 2f72689f4e
commit 9472c5224c
10 changed files with 103 additions and 45 deletions

View File

@ -101,7 +101,7 @@ export const proxyApi = {
) => ) =>
instanceApi.get<RecordSummary[]>(`proxy/${hostName}/api/${cameraName}/recordings/summary`, { instanceApi.get<RecordSummary[]>(`proxy/${hostName}/api/${cameraName}/recordings/summary`, {
params: { timezone }, params: { timezone },
timeout: 5 * 60 * 1000 timeout: 5 * 60 * 1000,
}).then(res => res.data), }).then(res => res.data),
// E.g. http://127.0.0.1:5000/api/events?before=1708534799&after=1708448400&camera=CameraName&has_clip=1&include_thumbnails=0&limit=5000 // E.g. http://127.0.0.1:5000/api/events?before=1708534799&after=1708448400&camera=CameraName&has_clip=1&include_thumbnails=0&limit=5000
@ -130,7 +130,8 @@ export const proxyApi = {
limit: limit, limit: limit,
min_score: minScore, min_score: minScore,
max_score: maxScore, max_score: maxScore,
} },
timeout: 5 * 60 * 1000,
}).then(res => res.data), }).then(res => res.data),
getEventsSummary: (hostName: string, cameraName: string) => getEventsSummary: (hostName: string, cameraName: string) =>

View File

@ -35,14 +35,22 @@ const CameraAccordion = ({
} }
}) })
const recodItem = (record: RecordSummary) => ( const recodItem = (record: RecordSummary) => {
if (host) {
return (
<Accordion.Item key={record.day} value={record.day}> <Accordion.Item key={record.day} value={record.day}>
<Accordion.Control key={record.day + 'control'}>{t('day')}: {record.day}</Accordion.Control> <Accordion.Control key={record.day + 'control'}>{t('day')}: {record.day}</Accordion.Control>
<Accordion.Panel key={record.day + 'panel'}> <Accordion.Panel key={record.day + 'panel'}>
<DayAccordion key={record.day + 'day'} recordSummary={record} /> <DayAccordion
host={host}
camera={camera}
key={record.day + 'day'}
recordSummary={record} />
</Accordion.Panel> </Accordion.Panel>
</Accordion.Item> </Accordion.Item>
) )
}
}
const days = useMemo(() => { const days = useMemo(() => {
if (recordings && camera) { if (recordings && camera) {

View File

@ -6,21 +6,25 @@ import { mapHostToHostname } from '../../../services/frigate.proxy/frigate.api';
import { RecordSummary } from '../../../types/record'; import { RecordSummary } from '../../../types/record';
import { isProduction } from '../../env.const'; import { isProduction } from '../../env.const';
import DayAccordionItem from './DayAccordionItem'; import DayAccordionItem from './DayAccordionItem';
import { GetCameraWHostWConfig, GetFrigateHost } from '../../../services/frigate.proxy/frigate.schema';
interface RecordingAccordionProps { interface RecordingAccordionProps {
recordSummary?: RecordSummary, recordSummary?: RecordSummary
host: GetFrigateHost
camera: GetCameraWHostWConfig
} }
const DayAccordionItemMemo = React.memo(DayAccordionItem) const DayAccordionItemMemo = React.memo(DayAccordionItem)
const DayAccordion = ({ const DayAccordion = ({
recordSummary, recordSummary,
host,
camera
}: RecordingAccordionProps) => { }: RecordingAccordionProps) => {
const { recordingsStore: recStore } = useContext(Context) const { recordingsStore: recStore } = useContext(Context)
const [openedValue, setOpenedValue] = useState<string>() const [openedValue, setOpenedValue] = useState<string>()
const camera = recStore.filteredCamera const hostName = mapHostToHostname(host)
const hostName = mapHostToHostname(recStore.filteredHost)
const handleOpenPlayer = useCallback((value?: string) => { const handleOpenPlayer = useCallback((value?: string) => {
if (recStore.playedItem !== value) { if (recStore.playedItem !== value) {
@ -32,11 +36,13 @@ const DayAccordion = ({
}, [openedValue, recStore]); }, [openedValue, recStore]);
const dayItems = useMemo(() => { const dayItems = useMemo(() => {
if (recordSummary && recordSummary.hours.length > 0) { if (recordSummary && recordSummary.hours.length > 0 && hostName && host) {
return recordSummary.hours.map(hour => { return recordSummary.hours.map(hour => {
const played = recordSummary.day + hour.hour === recStore.playedItem; const played = recordSummary.day + hour.hour === recStore.playedItem;
return ( return (
<DayAccordionItemMemo <DayAccordionItemMemo
camera={camera}
host={host}
key={recordSummary.day + hour.hour} key={recordSummary.day + hour.hour}
recordSummary={recordSummary} recordSummary={recordSummary}
recordHour={hour} recordHour={hour}

View File

@ -12,14 +12,17 @@ import AccordionControlButton from '../buttons/AccordionControlButton';
import AccordionShareButton from '../buttons/AccordionShareButton'; import AccordionShareButton from '../buttons/AccordionShareButton';
import PlayControl from '../buttons/PlayControl'; import PlayControl from '../buttons/PlayControl';
import DayPanel from './DayPanel'; import DayPanel from './DayPanel';
import { GetCameraWHostWConfig, GetFrigateHost } from '../../../services/frigate.proxy/frigate.schema';
interface DayAccordionItemProps { interface DayAccordionItemProps {
recordSummary: RecordSummary, recordSummary: RecordSummary,
recordHour: RecordHour, recordHour: RecordHour,
hostName?: string, hostName: string,
cameraName?: string, cameraName: string,
played?: boolean, played?: boolean,
openPlayer?: (value?: string) => void, openPlayer?: (value?: string) => void,
camera: GetCameraWHostWConfig
host: GetFrigateHost
} }
const DayAccordionItem = ({ const DayAccordionItem = ({
@ -28,7 +31,9 @@ const DayAccordionItem = ({
hostName, hostName,
cameraName, cameraName,
played, played,
openPlayer openPlayer,
camera,
host
}: DayAccordionItemProps) => { }: DayAccordionItemProps) => {
const { t } = useTranslation() const { t } = useTranslation()
const navigate = useNavigate() const navigate = useNavigate()
@ -92,6 +97,8 @@ const DayAccordionItem = ({
</Flex> </Flex>
</Accordion.Control> </Accordion.Control>
<DayPanel <DayPanel
camera={camera}
host={host}
day={recordSummary.day} day={recordSummary.day}
hour={hour} hour={hour}
events={recordHour.events} events={recordHour.events}

View File

@ -1,18 +1,23 @@
import { Accordion, Text } from '@mantine/core'; import { Accordion, Text } from '@mantine/core';
import { Suspense, lazy, useState } from 'react'; import { Suspense, lazy, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { GetCameraWHostWConfig, GetFrigateHost } from '../../../services/frigate.proxy/frigate.schema';
const EventsAccordion = lazy(() => import('./EventsAccordion')) const EventsAccordion = lazy(() => import('./EventsAccordion'))
interface DayEventsAccordionProps { interface DayEventsAccordionProps {
day: string, day: string,
hour: string, hour: string,
qty?: number, qty?: number,
camera: GetCameraWHostWConfig
host: GetFrigateHost
} }
const DayEventsAccordion = ({ const DayEventsAccordion = ({
day, day,
hour, hour,
qty, qty,
camera,
host,
}: DayEventsAccordionProps) => { }: DayEventsAccordionProps) => {
const { t } = useTranslation() const { t } = useTranslation()
const [openedItem, setOpenedItem] = useState<string>() const [openedItem, setOpenedItem] = useState<string>()
@ -27,7 +32,11 @@ const DayEventsAccordion = ({
<Accordion.Panel> <Accordion.Panel>
{openedItem === hour ? {openedItem === hour ?
<Suspense> <Suspense>
<EventsAccordion day={day} hour={hour} /> <EventsAccordion
camera={camera}
host={host}
day={day}
hour={hour} />
</Suspense> </Suspense>
: <></> : <></>
} }

View File

@ -3,6 +3,7 @@ import VideoDownloader from '../../../widgets/VideoDownloader';
import VideoPlayer from '../players/VideoPlayer'; import VideoPlayer from '../players/VideoPlayer';
import DayEventsAccordion from './DayEventsAccordion'; import DayEventsAccordion from './DayEventsAccordion';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { GetCameraWHostWConfig, GetFrigateHost } from '../../../services/frigate.proxy/frigate.schema';
interface DayPanelProps { interface DayPanelProps {
day: string, day: string,
@ -14,6 +15,8 @@ interface DayPanelProps {
playedURL?: string, playedURL?: string,
startUnixTime: number, startUnixTime: number,
endUnixTime: number, endUnixTime: number,
camera: GetCameraWHostWConfig
host: GetFrigateHost
} }
const DayPanel = ({ const DayPanel = ({
@ -26,6 +29,8 @@ const DayPanel = ({
playedURL, playedURL,
startUnixTime, startUnixTime,
endUnixTime, endUnixTime,
camera,
host
}: DayPanelProps) => { }: DayPanelProps) => {
const { t } = useTranslation() const { t } = useTranslation()
return ( return (
@ -42,7 +47,12 @@ const DayPanel = ({
</Flex> </Flex>
: ''} : ''}
{events > 0 ? {events > 0 ?
<DayEventsAccordion day={day} hour={hour} qty={events} /> <DayEventsAccordion
camera={camera}
host={host}
day={day}
hour={hour}
qty={events} />
: :
<Center><Text>{t('notHaveEvents')}</Text></Center> <Center><Text>{t('notHaveEvents')}</Text></Center>
} }

View File

@ -1,4 +1,4 @@
import { Button, Flex, Text } from '@mantine/core'; import { Button, Flex, Group, Text } from '@mantine/core';
import { IconExternalLink } from '@tabler/icons-react'; import { IconExternalLink } from '@tabler/icons-react';
import { proxyApi } from '../../../services/frigate.proxy/frigate.api'; import { proxyApi } from '../../../services/frigate.proxy/frigate.api';
import { EventFrigate } from '../../../types/event'; import { EventFrigate } from '../../../types/event';
@ -26,12 +26,23 @@ const EventPanel = ({
<> <>
{playedURL && playedURL === videoURL ? <VideoPlayer videoUrl={playedURL} /> : <></>} {playedURL && playedURL === videoURL ? <VideoPlayer videoUrl={playedURL} /> : <></>}
<Flex w='100%' justify='space-between'> <Flex w='100%' justify='space-between'>
{!hostName ? <></> : <Group spacing='xs'>
<BlobImage <Text fw={700}>{t('camera')}:</Text>
maw={200} <Text>{event.camera}</Text>
fit="contain" </Group>
withPlaceholder <Group>
src={proxyApi.eventThumbnailUrl(hostName, event.id)} /> <Text fw={700}>{t('player.startTime')}:</Text>
<Text>{unixTimeToDate(event.start_time)}</Text>
</Group>
<Group>
<Text fw={700}>{t('player.duration')}:</Text>
<Text>{getDurationFromTimestamps(event.start_time, event.end_time)}</Text>
</Group>
{!event.data?.score ? <></> :
<Group>
<Text fw={700}>{t('player.rating')}:</Text>
<Text>{(event.data.score * 100).toFixed(2)}%</Text>
</Group>
} }
<Flex direction='column' align='end' justify='center'> <Flex direction='column' align='end' justify='center'>
{!hostName ? '' : {!hostName ? '' :
@ -46,13 +57,6 @@ const EventPanel = ({
</Button> </Button>
</Flex> </Flex>
} }
<Text mt='1rem'>{t('camera')}: {event.camera}</Text>
<Text>{t('player.object')}: {event.label}</Text>
<Text>{t('player.startTime')}: {unixTimeToDate(event.start_time)}</Text>
<Text>{t('player.duration')}: {getDurationFromTimestamps(event.start_time, event.end_time)}</Text>
{!event.data?.score? <></> :
<Text>{t('player.rating')}: {(event.data.score * 100).toFixed(2)}%</Text>
}
</Flex> </Flex>
</Flex> </Flex>
</> </>

View File

@ -4,7 +4,7 @@ import { observer } from 'mobx-react-lite';
import { useContext, useState } from 'react'; import { useContext, useState } from 'react';
import { Context } from '../../..'; import { Context } from '../../..';
import { frigateQueryKeys, mapHostToHostname, proxyApi } from '../../../services/frigate.proxy/frigate.api'; import { frigateQueryKeys, mapHostToHostname, proxyApi } from '../../../services/frigate.proxy/frigate.api';
import { getEventsQuerySchema } from '../../../services/frigate.proxy/frigate.schema'; import { GetCameraWHostWConfig, GetFrigateHost, getEventsQuerySchema } from '../../../services/frigate.proxy/frigate.schema';
import { getUnixTime } from '../../utils/dateUtil'; import { getUnixTime } from '../../utils/dateUtil';
import RetryError from '../RetryError'; import RetryError from '../RetryError';
import EventsAccordionItem from './EventsAccordionItem'; import EventsAccordionItem from './EventsAccordionItem';
@ -16,10 +16,10 @@ import EventsAccordionItem from './EventsAccordionItem';
* @param hostName proxy format, e.g hostName: localhost:4000 * @param hostName proxy format, e.g hostName: localhost:4000
*/ */
interface EventsAccordionProps { interface EventsAccordionProps {
day?: string, day: string,
hour?: string, hour: string,
cameraName?: string camera: GetCameraWHostWConfig
hostName?: string host: GetFrigateHost
} }
/** /**
@ -31,13 +31,13 @@ interface EventsAccordionProps {
const EventsAccordion = ({ const EventsAccordion = ({
day, day,
hour, hour,
camera,
host,
}: EventsAccordionProps) => { }: EventsAccordionProps) => {
const { recordingsStore: recStore } = useContext(Context) const { recordingsStore: recStore } = useContext(Context)
const [openedItem, setOpenedItem] = useState<string>() const [openedItem, setOpenedItem] = useState<string>()
const host = recStore.filteredHost
const hostName = mapHostToHostname(host) const hostName = mapHostToHostname(host)
const camera = recStore.filteredCamera
const isRequiredParams = host && camera const isRequiredParams = host && camera
const { data, isPending, isError, refetch } = useQuery({ const { data, isPending, isError, refetch } = useQuery({

View File

@ -11,6 +11,7 @@ import { useNavigate } from 'react-router-dom';
import { routesPath } from '../../../router/routes.path'; import { routesPath } from '../../../router/routes.path';
import { proxyApi } from '../../../services/frigate.proxy/frigate.api'; import { proxyApi } from '../../../services/frigate.proxy/frigate.api';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import BlobImage from '../images/BlobImage';
interface EventsAccordionItemProps { interface EventsAccordionItemProps {
@ -44,7 +45,8 @@ const EventsAccordionItem = ({
const duration = getDurationFromTimestamps(event.start_time, event.end_time) const duration = getDurationFromTimestamps(event.start_time, event.end_time)
return ( return (
<Group> <Group>
<Text>{t('player.object')}: {event.label}</Text> <Text fw={700}>{t('player.object')}:</Text>
<Text >{event.label}</Text>
<Text>{time}</Text> <Text>{time}</Text>
{duration ? {duration ?
<Text>{duration}</Text> <Text>{duration}</Text>
@ -77,6 +79,14 @@ const EventsAccordionItem = ({
<Accordion.Item key={event.id + 'Item'} value={event.id}> <Accordion.Item key={event.id + 'Item'} value={event.id}>
<Accordion.Control key={event.id + 'Control'}> <Accordion.Control key={event.id + 'Control'}>
<Flex justify='space-between'> <Flex justify='space-between'>
{!hostName ? <></> :
<BlobImage
maw={200}
mr='1rem'
fit="contain"
withPlaceholder
src={proxyApi.eventThumbnailUrl(hostName, event.id)} />
}
{eventLabel(event)} {eventLabel(event)}
<Group> <Group>
<AccordionShareButton recordUrl={eventVideoURL} /> <AccordionShareButton recordUrl={eventVideoURL} />

View File

@ -51,7 +51,10 @@ const SelectedDayList = ({
return ( return (
<Flex w='100%' h='100%' direction='column' align='center'> <Flex w='100%' h='100%' direction='column' align='center'>
<Text>{host.name} / {camera.name} / {stringDay}</Text> <Text>{host.name} / {camera.name} / {stringDay}</Text>
<DayAccordion recordSummary={recordingsDay} /> <DayAccordion
host={host}
camera={camera}
recordSummary={recordingsDay} />
</Flex> </Flex>
); );
}; };