add recordings

This commit is contained in:
NlightN22 2024-02-24 02:47:24 +07:00
parent b4a380aba7
commit f369692133
22 changed files with 881 additions and 168 deletions

View File

@ -43,6 +43,7 @@
"strftime": "0.10.1", "strftime": "0.10.1",
"typescript": "^4.4.2", "typescript": "^4.4.2",
"validator": "^13.9.0", "validator": "^13.9.0",
"video.js": "^8.10.0",
"web-vitals": "^2.1.0", "web-vitals": "^2.1.0",
"zod": "^3.21.4" "zod": "^3.21.4"
}, },
@ -74,6 +75,7 @@
"@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@types/strftime": "^0.9.8", "@types/strftime": "^0.9.8",
"@types/uuid": "^9.0.2", "@types/uuid": "^9.0.2",
"@types/video.js": "^7.3.56",
"uuid": "^9.0.0" "uuid": "^9.0.0"
} }
} }

View File

@ -35,6 +35,7 @@ const MainBody = observer(() => {
if (isError) return <RetryError onRetry={refetch} /> if (isError) return <RetryError onRetry={refetch} />
const cards = () => { const cards = () => {
// return cameras.filter(cam => cam.frigateHost?.host.includes('5001')).slice(0,1).map(camera => (
return cameras.map(camera => ( return cameras.map(camera => (
<CameraCard <CameraCard
key={camera.id} key={camera.id}

View File

@ -1,32 +1,175 @@
// // import { route } from 'preact-router'; import MultiSelect from '../shared/components/frigate/MultiSelect';
// import { Fragment, useState, useRef, useCallback, useMemo } from 'react'; import StarRecording from '../shared/components/frigate/icons/StarRecording';
// import VideoPlayer from '../shared/components/frigate/VideoPlayer'; import Submitted from '../shared/components/frigate/icons/Submitted';
// import CogwheelLoader from '../shared/components/CogwheelLoader'; import CalendarIcon from '../shared/components/frigate/icons/CalendarIcon';
// import { Grid, Text } from '@mantine/core'; import Menu, { MenuItem } from '../shared/components/frigate/Menu';
// import MultiSelect from '../shared/components/frigate/MultiSelect'; import Dialog from '../shared/components/frigate/Dialog';
// import Button from '../shared/components/frigate/Button'; import TimelineSummary from '../shared/components/frigate/TimelineSummary';
// import StarRecording from '../shared/components/frigate/icons/StarRecording'; import TimelineEventOverlay from '../shared/components/frigate/TimelineEventOverlay';
// import Submitted from '../shared/components/frigate/icons/Submitted'; import { Tabs, TextTab } from '../shared/components/frigate/Tabs';
// import CalendarIcon from '../shared/components/frigate/icons/CalendarIcon'; import Clock from '../shared/components/frigate/icons/Clock';
// import Menu, { MenuItem } from '../shared/components/frigate/Menu'; import TimeAgo from '../shared/components/frigate/TimeAgo';
// import Dialog from '../shared/components/frigate/Dialog'; import Camera from '../shared/components/frigate/icons/Camera';
// import TimelineSummary from '../shared/components/frigate/TimelineSummary'; import Zone from '../shared/components/frigate/icons/Zone';
// import TimelineEventOverlay from '../shared/components/frigate/TimelineEventOverlay'; import Score from '../shared/components/frigate/icons/Score';
// import { Tabs, TextTab } from '../shared/components/frigate/Tabs'; import Link from '../shared/components/frigate/Link';
// import Clock from '../shared/components/frigate/icons/Clock'; import Delete from '../shared/components/frigate/icons/Delete';
// import TimeAgo from '../shared/components/frigate/TimeAgo'; import Download from '../shared/components/frigate/icons/Download';
// import Camera from '../shared/components/frigate/icons/Camera'; import { IconDownload, IconStar, IconStarFilled } from '@tabler/icons-react';
// import Zone from '../shared/components/frigate/icons/Zone'; // ↑↑↑ from frigate ↑↑↑
// import Score from '../shared/components/frigate/icons/Score';
// import Link from '../shared/components/frigate/Link';
// import Delete from '../shared/components/frigate/icons/Delete';
// import Download from '../shared/components/frigate/icons/Download';
export default function EventsPage() { import { Fragment, useState, useRef, useCallback, useMemo, useContext, useEffect, lazy, Suspense } from 'react';
import VideoPlayer from '../shared/components/frigate/VideoPlayer';
import CogwheelLoader from '../shared/components/CogwheelLoader';
import { Accordion, Button, Center, Flex, Grid, SelectItem, Text } from '@mantine/core';
import { frigateApi, frigateQueryKeys, mapHostToHostname, proxyApi } from '../services/frigate.proxy/frigate.api';
import { useLocation, useParams } from 'react-router-dom';
import { useQuery } from '@tanstack/react-query';
import { observer } from 'mobx-react-lite';
import { Context } from '..';
import OneSelectFilter, { OneSelectItem } from '../shared/components/filters.aps/OneSelectFilter';
import CenterLoader from '../shared/components/CenterLoader';
import RetryError from './RetryError';
const CameraAccordion = lazy(() => import('../shared/components/accordion/CameraAccordion'));
const RecordingsPage = observer(() => {
const { sideBarsStore } = useContext(Context)
const filterData: OneSelectItem[] = [
{ value: 'dsfgdfg', label: 'fasdfsdf' },
{ value: 'dsfgsfgnjcv', label: 'frteh' },
{ value: 'rthsdfgh', label: 'dftghdfgjn' },
]
const hostSelector = () => (
<OneSelectFilter
id='dsfgds54'
label='HostSelector'
spaceBetween='1rem'
data={filterData}
/>
)
useEffect(() => {
sideBarsStore.rightVisible = true
sideBarsStore.setRightChildren(hostSelector())
}, [])
const location = useLocation()
const queryParams = new URLSearchParams(location.search)
const cameraId = queryParams.get('cameraId');
const hostId = queryParams.get('hostId')
const date = queryParams.get('date');
const time = queryParams.get('time');
const { data: camera, isPending: cameraPending, isError: cameraError, refetch: cameraRefetch } = useQuery({
queryKey: [frigateQueryKeys.getCameraWHost, cameraId],
queryFn: async () => {
if (cameraId) {
return frigateApi.getCameraWHost(cameraId)
}
return null
}
})
const { data: host, isPending: hostPending, isError: hostError, refetch: hostRefetch } = useQuery({
queryKey: [frigateQueryKeys.getFrigateHost, hostId],
queryFn: async () => {
if (hostId) {
return frigateApi.getHost(hostId)
}
return null
}
})
const [openCameraId, setOpenCameraId] = useState<string | null>(null)
const handleOnChange = (cameraId: string | null) => {
console.log('Camera id', cameraId)
setOpenCameraId(openCameraId === cameraId ? null : cameraId)
}
const handleRetry = () => {
if (cameraId) cameraRefetch()
else if (hostId) hostRefetch()
}
if (hostPending || cameraPending) return <CenterLoader />
if (hostError || cameraError) return <RetryError onRetry={handleRetry} />
// Camera selected
if (camera && camera.frigateHost) {
return (
<Flex w='100%' h='100%' direction='column' align='center'>
<Text>{camera.frigateHost.name}</Text>
<Suspense>
<CameraAccordion camera={camera} host={camera.frigateHost} />
</Suspense>
</Flex>
)
}
// Host selected
if (host && host.cameras.length > 0) {
const cameras = host.cameras.map(camera => {
return (
<Accordion.Item key={camera.id + 'Item'} value={camera.id}>
<Accordion.Control key={camera.id + 'Control'}>{camera.name}</Accordion.Control>
<Accordion.Panel key={camera.id + 'Panel'}>
{openCameraId === camera.id && (
<Suspense>
<CameraAccordion camera={camera} host={host} />
</Suspense>
)}
</Accordion.Panel>
</Accordion.Item>
)
})
return (
<Flex w='100%' h='100%' direction='column' align='center'>
<Text>{host.name}</Text>
<Accordion
mt='1rem'
variant='separated'
radius="md" w='100%'
onChange={(value) => handleOnChange(value)}>
{cameras}
</Accordion>
</Flex>
)
}
return (
<Flex w='100%' h='100%' direction='column' justify='center' align='center'>
<Text size='xl'>Please select host</Text>
</Flex>
)
// const videoUrl = proxyApi.eventURL('localhost:5000', event)
// const recordUrl = 'http://127.0.0.1:5000/vod/2024-02/22/18/Buhgalteria/Asia,Krasnoyarsk/master.m3u8'
// const recordUrl = proxyApi.recordingURL('localhost:5000', 'Buhgalteria', 'Asia,Krasnoyarsk', '2024-02/22/18')
// console.log(recordUrl)
// return (
// <Flex w='100%' h='100%' direction='column'>
// {/* <VideoPlayer videoUrl={recordUrl} /> */}
// </Flex>
// )
// seekOptions={{ forward: 10, backward: 5 }}
// onReady={handlePlayerReady}
// onDispose={onDispose}
{/* {eventOverlay ? (
<TimelineEventOverlay eventOverlay={eventOverlay} cameraConfig={config.cameras[event.camera]} />
) : null} */}
return ( return (
<div /> <div />
) )
} })
export default RecordingsPage
// const API_LIMIT = 25; // const API_LIMIT = 25;
@ -45,7 +188,7 @@ export default function EventsPage() {
// export default function Events({ path, ...props }) { // export default function Events({ path, ...props }) {
// // const apiHost = useApiHost(); // // const apiHost = useApiHost();
// // const { data: config } = useSWR('config'); // // const { data: config } = useSWR('config');
// const timezone = useMemo(() => config?.ui?.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone, [config]); // const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone
// const [searchParams, setSearchParams] = useState({ // const [searchParams, setSearchParams] = useState({
// before: null, // before: null,
// after: null, // after: null,
@ -284,12 +427,12 @@ export default function EventsPage() {
// [path, searchParams, setSearchParams] // [path, searchParams, setSearchParams]
// ); // );
// const onClickFilterSubmitted = useCallback(() => { // // const onClickFilterSubmitted = useCallback(() => {
// if (++searchParams.is_submitted > 1) { // // if (++searchParams.is_submitted > 1) {
// searchParams.is_submitted = -1; // // searchParams.is_submitted = -1;
// } // // }
// onFilter('is_submitted', searchParams.is_submitted); // // onFilter('is_submitted', searchParams.is_submitted);
// }, [searchParams, onFilter]); // // }, [searchParams, onFilter]);
// const isDone = (eventPages?.[eventPages.length - 1]?.length ?? 0) < API_LIMIT; // const isDone = (eventPages?.[eventPages.length - 1]?.length ?? 0) < API_LIMIT;
@ -405,14 +548,14 @@ export default function EventsPage() {
// )} // )}
// <div className="ml-auto flex"> // <div className="ml-auto flex">
// {config.plus.enabled && ( // {/* {config.plus.enabled && (
// <Submitted // <Submitted
// className="h-10 w-10 text-yellow-300 cursor-pointer ml-auto" // className="h-10 w-10 text-yellow-300 cursor-pointer ml-auto"
// onClick={() => onClickFilterSubmitted()} // onClick={() => onClickFilterSubmitted()}
// inner_fill={searchParams.is_submitted == 1 ? 'currentColor' : 'gray'} // inner_fill={searchParams.is_submitted == 1 ? 'currentColor' : 'gray'}
// outer_stroke={searchParams.is_submitted >= 0 ? 'currentColor' : 'gray'} // outer_stroke={searchParams.is_submitted >= 0 ? 'currentColor' : 'gray'}
// /> // />
// )} // )} */}
// <StarRecording // <StarRecording
// className="h-10 w-10 text-yellow-300 cursor-pointer ml-auto" // className="h-10 w-10 text-yellow-300 cursor-pointer ml-auto"
@ -773,14 +916,17 @@ export default function EventsPage() {
// <div // <div
// className="relative rounded-l flex-initial min-w-[125px] h-[125px] bg-contain bg-no-repeat bg-center" // className="relative rounded-l flex-initial min-w-[125px] h-[125px] bg-contain bg-no-repeat bg-center"
// style={{ // style={{
// 'background-image': `url(${apiHost}api/events/${event.id}/thumbnail.jpg)`, // 'backgroundImage': `url(${apiHost}api/events/${event.id}/thumbnail.jpg)`,
// }} // }}
// > // >
// <StarRecording // <IconStar
// onClick={(e) => onSave(e, event.id, !event.retain_indefinitely)}
// fill={event.retain_indefinitely ? 'yellow' : 'none'} />
// {/* <StarRecording
// className="h-6 w-6 text-yellow-300 absolute top-1 right-1 cursor-pointer" // className="h-6 w-6 text-yellow-300 absolute top-1 right-1 cursor-pointer"
// onClick={(e) => onSave(e, event.id, !event.retain_indefinitely)} // onClick={(e) => onSave(e, event.id, !event.retain_indefinitely)}
// fill={event.retain_indefinitely ? 'currentColor' : 'none'} // fill={event.retain_indefinitely ? 'currentColor' : 'none'}
// /> // /> */}
// {event.end_time ? null : ( // {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"> // <div className="bg-slate-300 dark:bg-slate-700 absolute bottom-0 text-center w-full uppercase text-sm rounded-bl">
// In progress // In progress
@ -825,7 +971,7 @@ export default function EventsPage() {
// : `, ${event.sub_label}: ${(event?.data?.sub_label_score * 100).toFixed(0)}%`} // : `, ${event.sub_label}: ${(event?.data?.sub_label_score * 100).toFixed(0)}%`}
// </div> // </div>
// </div> // </div>
// <div class="hidden sm:flex flex-col justify-end mr-2"> // {/* <div class="hidden sm:flex flex-col justify-end mr-2">
// {event.end_time && event.has_snapshot && (event?.data?.type || 'object') == 'object' && ( // {event.end_time && event.has_snapshot && (event?.data?.type || 'object') == 'object' && (
// <Fragment> // <Fragment>
// {event.plus_id ? ( // {event.plus_id ? (
@ -849,19 +995,11 @@ export default function EventsPage() {
// )} // )}
// </Fragment> // </Fragment>
// )} // )}
// </div> // </div> */}
// <div class="flex flex-col"> // <div className="flex flex-col">
// <Delete // {event.has_clip || event.has_snapshot ?
// className="h-6 w-6 cursor-pointer" // <IconDownload onClick={(e) => onDownloadClick(e, event)} />
// 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> // </div>
// </div> // </div>

View File

@ -1,7 +1,6 @@
export const routesPath = { export const routesPath = {
MAIN_PATH: '/', MAIN_PATH: '/',
BIRDSEYE_PATH: '/birdseye', BIRDSEYE_PATH: '/birdseye',
EVENTS_PATH: '/events',
RECORDINGS_PATH: '/recordings', RECORDINGS_PATH: '/recordings',
SETTINGS_PATH: '/settings', SETTINGS_PATH: '/settings',
HOSTS_PATH: '/hosts', HOSTS_PATH: '/hosts',

View File

@ -11,6 +11,7 @@ import HostConfigPage from "../pages/HostConfigPage";
import HostSystemPage from "../pages/HostSystemPage"; import HostSystemPage from "../pages/HostSystemPage";
import HostStoragePage from "../pages/HostStoragePage"; import HostStoragePage from "../pages/HostStoragePage";
import LiveCameraPage from "../pages/LiveCameraPage"; import LiveCameraPage from "../pages/LiveCameraPage";
import RecordingsPage from "../pages/RecordingsPage";
interface IRoute { interface IRoute {
path: string, path: string,
@ -30,6 +31,10 @@ export const routes: IRoute[] = [
path: routesPath.HOSTS_PATH, path: routesPath.HOSTS_PATH,
component: <FrigateHostsPage />, component: <FrigateHostsPage />,
}, },
{
path: routesPath.RECORDINGS_PATH,
component: <RecordingsPage />,
},
{ {
path: routesPath.HOST_CONFIG_PATH, path: routesPath.HOST_CONFIG_PATH,
component: <HostConfigPage />, component: <HostConfigPage />,

View File

@ -4,6 +4,7 @@ import { z } from "zod"
import { GetConfig, DeleteFrigateHost, GetFrigateHost, PutConfig, PutFrigateHost, GetFrigateHostWithCameras, GetCameraWHost, GetCameraWHostWConfig } from "./frigate.schema"; import { GetConfig, DeleteFrigateHost, GetFrigateHost, PutConfig, PutFrigateHost, GetFrigateHostWithCameras, GetCameraWHost, GetCameraWHostWConfig } from "./frigate.schema";
import { FrigateConfig } from "../../types/frigateConfig"; import { FrigateConfig } from "../../types/frigateConfig";
import { url } from "inspector"; import { url } from "inspector";
import { RecordSummary } from "../../types/record";
const instanceApi = axios.create({ const instanceApi = axios.create({
@ -17,7 +18,7 @@ export const frigateApi = {
getHosts: () => instanceApi.get<GetFrigateHost[]>('apiv1/frigate-hosts').then(res => { getHosts: () => instanceApi.get<GetFrigateHost[]>('apiv1/frigate-hosts').then(res => {
return res.data return res.data
}), }),
getHostWithCameras: () => instanceApi.get<GetFrigateHostWithCameras[]>('apiv1/frigate-hosts', { params: { include: 'cameras' } }).then(res => { getHostsWithCameras: () => instanceApi.get<GetFrigateHostWithCameras[]>('apiv1/frigate-hosts', { params: { include: 'cameras' } }).then(res => {
return res.data return res.data
}), }),
getHost: (id: string) => instanceApi.get<GetFrigateHostWithCameras>(`apiv1/frigate-hosts/${id}`).then(res => { getHost: (id: string) => instanceApi.get<GetFrigateHostWithCameras>(`apiv1/frigate-hosts/${id}`).then(res => {
@ -34,8 +35,8 @@ export const frigateApi = {
} }
export const proxyApi = { export const proxyApi = {
getHostConfigRaw: (hostName: string) => instanceApi.get('proxy/api/config/raw', { params: { hostName: hostName } }).then(res => res.data), getHostConfigRaw: (hostName: string) => instanceApi.get(`proxy/${hostName}/api/config/raw`).then(res => res.data),
getHostConfig: (hostName: string) => instanceApi.get('proxy/api/config', { params: { hostName: hostName } }).then(res => res.data), getHostConfig: (hostName: string) => instanceApi.get(`proxy/${hostName}/api/config`).then(res => res.data),
getImageFrigate: async (imageUrl: string) => { getImageFrigate: async (imageUrl: string) => {
const response = await fetch(imageUrl); const response = await fetch(imageUrl);
if (!response.ok) { if (!response.ok) {
@ -43,11 +44,59 @@ export const proxyApi = {
} }
return response.blob(); return response.blob();
}, },
cameraWsURL: (hostName: string, cameraName: string) => { getHostRestart: (hostName: string) => instanceApi.get(`proxy/${hostName}/api/restart`).then(res => res.data),
return `ws://${proxyURL.host}/proxy-ws/live/jsmpeg/${cameraName}?hostName=${hostName}`
}, getRecordings: (
cameraImageURL: (hostName: string, cameraName: string) => { hostName: string,
return `http://${proxyURL.host}/proxy/api/${cameraName}/latest.jpg?hostName=${hostName}` cameraName: string,
after: number,
before: number
) =>
instanceApi.get(`proxy/${hostName}/api/${cameraName}/recordings?after=${after}&before=${before}`).then(res => res.data),
getRecordingsSummary: (
hostName: string,
cameraName: string,
timezone: string,
) =>
instanceApi.get<RecordSummary[]>(`proxy/${hostName}/api/${cameraName}/recordings/summary`, {params: { timezone}}).then(res => res.data),
getEvents: (
hostName: string,
camerasName: string[],
timezone: string,
minScore?: number,
maxScore?: number,
after?: number,
before?: number,
labels?: string[],
) =>
instanceApi.get(`proxy/${hostName}/api/events`, {
params: {
cameras: camerasName,
after: after,
timezone: timezone,
before: before, // @before the last event start_time in list
labels: labels,
min_score: minScore,
max_score: maxScore,
}
}).then(res => res.data),
getEventsSummary: (hostName: string, cameraName: string) =>
instanceApi.get(`proxy/${hostName}/api/${cameraName}/events/summary`).then(res => res.data),
getEventsInProgress: (hostName: string) => instanceApi.get(`proxy/${hostName}/api/events?in_progress=1&include_thumbnails=0`),
cameraWsURL: (hostName: string, cameraName: string) =>
`ws://${proxyURL.host}/proxy-ws/${hostName}/live/jsmpeg/${cameraName}`,
cameraImageURL: (hostName: string, cameraName: string) =>
`${proxyURL.protocol}//${proxyURL.host}/proxy/${hostName}/api/${cameraName}/latest.jpg`,
eventURL: (hostName: string, event: string) =>
`${proxyURL.protocol}//${proxyURL.host}/proxy/${hostName}/vod/event/${event}/master.m3u8`,
// http://127.0.0.1:5000/vod/2024-02/23/19/CameraName/Asia,Krasnoyarsk/master.m3u8
recordingURL: (hostName: string, cameraName: string, timezone: string, day: string, hour: string) => {// day:2024-02-23 hour:19
const parts = day.split('-')
const date = `${parts[0]}-${parts[1]}/${parts[2]}/${hour}`
return `${proxyURL.protocol}//${proxyURL.host}/proxy/${hostName}/vod/${date}/${cameraName}/${timezone}/master.m3u8` // todo add Date/Time
}, },
} }
@ -69,4 +118,6 @@ export const frigateQueryKeys = {
getCamerasWHost: 'cameras-frigate-host', getCamerasWHost: 'cameras-frigate-host',
getCameraWHost: 'camera-frigate-host', getCameraWHost: 'camera-frigate-host',
getHostConfig: 'host-config', getHostConfig: 'host-config',
getRecordingsSummary: 'recordings-frigate-summary',
getRecordings: 'recordings-frigate',
} }

View File

@ -52,9 +52,6 @@ const CameraCard = ({
const handleOpenRecordings = () => { const handleOpenRecordings = () => {
throw Error('Not yet implemented') throw Error('Not yet implemented')
} }
const handleOpenEvents = () => {
throw Error('Not yet implemented')
}
return ( return (
<Grid.Col md={6} lg={3} p='0.2rem'> <Grid.Col md={6} lg={3} p='0.2rem'>
<Card h='100%' radius="lg" padding='0.5rem' className={classes.mainCard}> <Card h='100%' radius="lg" padding='0.5rem' className={classes.mainCard}>
@ -66,7 +63,6 @@ const CameraCard = ({
className={classes.bottomGroup}> className={classes.bottomGroup}>
<Flex justify='space-evenly' mt='0.5rem' w='100%'> <Flex justify='space-evenly' mt='0.5rem' w='100%'>
<Button size='sm' onClick={handleOpenRecordings}>Recordings</Button> <Button size='sm' onClick={handleOpenRecordings}>Recordings</Button>
<Button size='sm' onClick={handleOpenEvents}>Events</Button>
</Flex> </Flex>
</Group> </Group>
</Card> </Card>

View File

@ -0,0 +1,72 @@
import { Accordion, Center, Text } from '@mantine/core';
import React, { useContext, useEffect, useState } from 'react';
import { GetCameraWHostWConfig, GetFrigateHost } from '../../../services/frigate.proxy/frigate.schema';
import { useQuery } from '@tanstack/react-query';
import { frigateQueryKeys, mapHostToHostname, proxyApi } from '../../../services/frigate.proxy/frigate.api';
import CogwheelLoader from '../CogwheelLoader';
import DayAccordion from './DayAccordion';
import { observer } from 'mobx-react-lite';
import { Context } from '../../..';
interface CameraAccordionProps {
camera: GetCameraWHostWConfig,
host: GetFrigateHost
}
const CameraAccordion = observer(({
camera,
host
}: CameraAccordionProps) => {
const { recordingsStore } = useContext(Context)
const { data, isPending, isError } = useQuery({
queryKey: [frigateQueryKeys.getRecordingsSummary, camera?.id],
queryFn: () => {
if (camera && host) {
const hostName = mapHostToHostname(host)
return proxyApi.getRecordingsSummary(hostName, camera.name, 'Asia/Krasnoyarsk')
}
return null
}
})
const [openedDay, setOpenedDay] = useState<string | null>()
useEffect(() => {
if (openedDay) {
recordingsStore.playedRecord.cameraName = camera.name
const hostName = mapHostToHostname(host)
recordingsStore.playedRecord.hostName = hostName
}
}, [openedDay])
const handleClick = (value: string | null) => {
setOpenedDay(value)
}
if (isPending) return (
<Center>
<Text>Loading...</Text>
</Center>
)
if (isError) return <Text>Loading error</Text>
if (!data || !camera) return null
const days = data.map(rec => (
<Accordion.Item key={rec.day} value={rec.day}>
<Accordion.Control key={rec.day + 'control'}>{rec.day}</Accordion.Control>
<Accordion.Panel key={rec.day + 'panel'}>
<DayAccordion key={rec.day + 'day'} recordSummary={rec} />
</Accordion.Panel>
</Accordion.Item>
))
return (
<Accordion variant='separated' radius="md" w='100%' onChange={handleClick}>
{days}
</Accordion>
)
})
export default CameraAccordion;

View File

@ -0,0 +1,94 @@
import { Accordion, Flex, Group, Text } from '@mantine/core';
import React, { useContext, useEffect, useState } from 'react';
import { RecordHour, RecordSummary, Recording } from '../../../types/record';
import Button from '../frigate/Button';
import { IconPlayerPause, IconPlayerPlay, IconPlayerStop } from '@tabler/icons-react';
import { observer } from 'mobx-react-lite';
import PlayControl from './PlayControl';
import { frigateApi, proxyApi } from '../../../services/frigate.proxy/frigate.api';
import { Context } from '../../..';
import VideoPlayer from '../frigate/VideoPlayer';
interface RecordingAccordionProps {
recordSummary?: RecordSummary
}
const DayAccordion = observer(({
recordSummary
}: RecordingAccordionProps) => {
const { recordingsStore } = useContext(Context)
const [openVideoPlayer, setOpenVideoPlayer] = useState<string>()
const [openedValue, setOpenedValue] = useState<string>()
const [playerUrl, setPlayerUrl] = useState<string>()
useEffect(() => {
if (openVideoPlayer) {
console.log('openVideoPlayer', openVideoPlayer)
if (openVideoPlayer) {
recordingsStore.playedRecord.day = recordSummary?.day
recordingsStore.playedRecord.hour = openVideoPlayer
recordingsStore.playedRecord.timezone = 'Asia,Krasnoyarsk'
const parsed = recordingsStore.getFullPlayedRecord(recordingsStore.playedRecord)
console.log('recordingsStore.playedRecord: ', recordingsStore.playedRecord)
if (parsed.success) {
const url = proxyApi.recordingURL(
parsed.data.hostName,
parsed.data.cameraName,
parsed.data.timezone,
parsed.data.day,
parsed.data.hour
)
console.log('GET URL: ', url)
setPlayerUrl(url)
}
}
}else {
setPlayerUrl(undefined)
}
}, [openVideoPlayer])
if (!recordSummary || recordSummary.hours.length < 1) return (<Text>Not have record at that day</Text>)
const handleOpenPlayer = (hour: string) => {
// console.log(`openVideoPlayer day:${recordSummary.day} hour:${hour}`)
if (openVideoPlayer !== hour) {
setOpenedValue(hour)
setOpenVideoPlayer(hour)
} else if (openedValue === hour && openVideoPlayer === hour) {
setOpenVideoPlayer(undefined)
}
}
const handleClick = (value: string) => {
if (openedValue === value) {
setOpenedValue(undefined)
} else {
setOpenedValue(value)
}
setOpenVideoPlayer(undefined)
}
return (
<Accordion
key={recordSummary.day}
variant='separated'
radius="md" w='100%'
value={openedValue}
onChange={handleClick}
>
{recordSummary.hours.map(hour => (
<Accordion.Item key={hour.hour + 'Item'} value={hour.hour}>
<Accordion.Control key={hour.hour + 'Control'}>
<PlayControl hour={hour.hour} openVideoPlayer={openVideoPlayer} onClick={handleOpenPlayer}/>
</Accordion.Control>
<Accordion.Panel key={hour.hour + 'Panel'}>
{openVideoPlayer === hour.hour ? <VideoPlayer videoUrl={playerUrl} /> : <></>}
Events
</Accordion.Panel>
</Accordion.Item>
))}
</Accordion>
)
})
export default DayAccordion;

View File

@ -0,0 +1,46 @@
import { Flex, Group, Text } from '@mantine/core';
import { IconPlayerPlay, IconPlayerStop } from '@tabler/icons-react';
import React from 'react';
interface PlayControlProps {
hour: string,
openVideoPlayer?: string,
onClick?: (value: string) => void
}
const PlayControl = ({
hour,
openVideoPlayer,
onClick
}: PlayControlProps) => {
const handleClick = (value: string) => {
if (onClick) onClick(value)
}
return (
<Flex justify='space-between'>
Hour: {hour}
<Group>
<Text onClick={(event) => {
event.stopPropagation()
handleClick(hour)
}}>
{openVideoPlayer === hour ? 'Stop Video' : 'Play Video'}
</Text>
{openVideoPlayer === hour ?
<IconPlayerStop onClick={(event) => {
event.stopPropagation()
handleClick(hour)
}} />
:
<IconPlayerPlay onClick={(event) => {
event.stopPropagation()
handleClick(hour)
}} />
}
</Group>
</Flex>
)
}
export default PlayControl;

View File

@ -0,0 +1,11 @@
import React from 'react';
const TestAccordion = ({ camera }: { camera: any }) => {
return (
<div>
TEST ACCORDION {camera.name}
</div>
);
};
export default TestAccordion;

View File

@ -2,9 +2,18 @@ import { SelectItem, SystemProp, SpacingValue, SelectProps, Box, Flex, CloseButt
import React, { CSSProperties } from 'react'; import React, { CSSProperties } from 'react';
import CloseWithTooltip from '../CloseWithTooltip'; import CloseWithTooltip from '../CloseWithTooltip';
import { strings } from '../../strings/strings'; import { strings } from '../../strings/strings';
export interface OneSelectItem {
value: string;
label: string;
selected?: boolean;
disabled?: boolean;
}
interface OneSelectFilterProps { interface OneSelectFilterProps {
id: string id: string
data: SelectItem[] data: OneSelectItem[]
spaceBetween?: SystemProp<SpacingValue> spaceBetween?: SystemProp<SpacingValue>
label?: string label?: string
defaultValue?: string defaultValue?: string

View File

@ -1,100 +0,0 @@
import { useRef, useEffect } from 'react';
import videojs from 'video.js';
import 'videojs-playlist';
import 'video.js/dist/video-js.css';
export default function VideoPlayer({
children,
options,
seekOptions = { forward: 30, backward: 10 }, onReady = () => { }, onDispose = () => { }
}) {
const playerRef = useRef();
useEffect(() => {
const defaultOptions = {
controls: true,
controlBar: {
skipButtons: seekOptions,
},
playbackRates: [0.5, 1, 2, 4, 8],
fluid: true,
};
if (!videojs.browser.IS_FIREFOX) {
defaultOptions.playbackRates.push(16);
}
const player = videojs(playerRef.current, { ...defaultOptions, ...options }, () => {
onReady(player);
});
// Allows player to continue on error
player.reloadSourceOnError();
// Disable fullscreen on iOS if we have children
if (
children &&
videojs.browser.IS_IOS &&
videojs.browser.IOS_VERSION > 9 &&
!player.el_.ownerDocument.querySelector('.bc-iframe')
) {
player.tech_.el_.setAttribute('playsinline', 'playsinline');
player.tech_.supportsFullScreen = function () {
return false;
};
}
const screen = window.screen;
const angle = () => {
// iOS
if (typeof window.orientation === 'number') {
return window.orientation;
}
// Android
if (screen && screen.orientation && screen.orientation.angle) {
return window.orientation;
}
videojs.log('angle unknown');
return 0;
};
const rotationHandler = () => {
const currentAngle = angle();
if (currentAngle === 90 || currentAngle === 270 || currentAngle === -90) {
if (player.paused() === false) {
player.requestFullscreen();
}
}
if ((currentAngle === 0 || currentAngle === 180) && player.isFullscreen()) {
player.exitFullscreen();
}
};
if (videojs.browser.IS_IOS) {
window.addEventListener('orientationchange', rotationHandler);
} else if (videojs.browser.IS_ANDROID && screen.orientation) {
// addEventListener('orientationchange') is not a user interaction on Android
screen.orientation.onchange = rotationHandler;
}
return () => {
if (videojs.browser.IS_IOS) {
window.removeEventListener('orientationchange', rotationHandler);
}
player.dispose();
onDispose();
};
}, []); // eslint-disable-line react-hooks/exhaustive-deps
return (
<div data-vjs-player>
{/* Setting an empty data-setup is required to override the default values and allow video to be fit the size of its parent */}
<video ref={playerRef} className="small-player video-js vjs-default-skin" data-setup="{}" controls playsinline />
{children}
</div>
);
}

View File

@ -0,0 +1,72 @@
import React, { useRef, useEffect } from 'react';
import videojs from 'video.js';
import Player from 'video.js/dist/types/player';
import 'video.js/dist/video-js.css'
interface VideoPlayerProps {
videoUrl?: string
}
const VideoPlayer = ({ videoUrl }: VideoPlayerProps) => {
const videoRef = useRef<HTMLVideoElement>(null);
const playerRef = useRef<Player | null>(null);
useEffect(() => {
const defaultOptions = {
preload: 'auto',
autoplay: true,
sources: [
{
src: videoUrl,
type: 'application/vnd.apple.mpegurl',
},
],
controls: true,
controlBar: {
skipButtons: { forward: 10, backward: 5 },
},
playbackRates: [0.5, 1, 2, 4, 8],
fluid: true,
};
if (!videojs.browser.IS_FIREFOX) {
defaultOptions.playbackRates.push(16);
}
//TODO add rotations on IOS and android devices
console.log('playerRef.current', playerRef.current)
if (videoRef.current) {
console.log('mount new player')
playerRef.current = videojs(videoRef.current, { ...defaultOptions }, () => {
console.log('player is ready');
});
}
console.log('VideoPlayer rendered')
return () => {
if (playerRef.current !== null) {
playerRef.current.dispose();
playerRef.current = null;
console.log('unmount player')
}
};
}, []);
useEffect(() => {
if (playerRef.current) {
playerRef.current.src(videoUrl);
console.log('player change src')
}
}, [videoUrl]);
return (
<div data-vjs-player>
{/* Setting an empty data-setup is required to override the default values and allow video to be fit the size of its parent */}
<video ref={videoRef} className="small-player video-js vjs-default-skin" data-setup="{}" controls playsInline />
</div>
);
};
export default VideoPlayer;

View File

@ -1,7 +1,9 @@
import { h } from 'preact'; import { memo } from 'react';
import { memo } from 'preact/compat';
export function Download({ className = 'h-6 w-6', stroke = 'currentColor', fill = 'none', onClick = () => {} }) {
export function Download({ className = 'h-6 w-6', stroke = 'currentColor', fill = 'none', onClick = () => void }) {
return ( return (
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View File

@ -0,0 +1,33 @@
import { z } from "zod"
export type RecordingPlay = {
hostName?: string
cameraName?: string
hour?: string
day?: string
timezone?: string
}
export class RecordingsStore {
recordingSchema = z.object({
hostName: z.string(),
cameraName: z.string(),
hour: z.string(),
day: z.string(),
timezone: z.string(),
})
private _playedRecord: RecordingPlay = {}
public get playedRecord(): RecordingPlay {
return this._playedRecord
}
public set playedRecord(value: RecordingPlay) {
this._playedRecord = value
}
getFullPlayedRecord(value: RecordingPlay) {
return this.recordingSchema.safeParse(value)
}
}

View File

@ -4,6 +4,7 @@ import { FiltersStore } from "./filters/filters.store";
import { ModalStore } from "./modal.store"; import { ModalStore } from "./modal.store";
import { OrdersStore } from "./orders.store"; import { OrdersStore } from "./orders.store";
import { ProductStore } from "./product.store"; import { ProductStore } from "./product.store";
import { RecordingsStore } from "./recordings.store";
import { SettingsStore } from "./settings.store"; import { SettingsStore } from "./settings.store";
import { SideBarsStore } from "./sidebars.store"; import { SideBarsStore } from "./sidebars.store";
import PostStore from "./test.store"; import PostStore from "./test.store";
@ -18,6 +19,7 @@ class RootStore {
categoryStore: CategoryStore categoryStore: CategoryStore
filtersStore: FiltersStore filtersStore: FiltersStore
sideBarsStore: SideBarsStore sideBarsStore: SideBarsStore
recordingsStore: RecordingsStore
ordersStore: OrdersStore ordersStore: OrdersStore
settingsStore: SettingsStore settingsStore: SettingsStore
constructor() { constructor() {
@ -29,6 +31,7 @@ class RootStore {
this.categoryStore = new CategoryStore() this.categoryStore = new CategoryStore()
this.filtersStore = new FiltersStore() this.filtersStore = new FiltersStore()
this.sideBarsStore = new SideBarsStore() this.sideBarsStore = new SideBarsStore()
this.recordingsStore = new RecordingsStore()
this.ordersStore = new OrdersStore() this.ordersStore = new OrdersStore()
this.settingsStore = new SettingsStore() this.settingsStore = new SettingsStore()
} }

View File

@ -2,6 +2,7 @@ export const headerMenu = {
home:"На главную", home:"На главную",
test:"Тест", test:"Тест",
settings:"Настройки", settings:"Настройки",
recordings:"Записи",
acessSettings:" Настройка доступа", acessSettings:" Настройка доступа",
hostsConfig:"Серверы Frigate", hostsConfig:"Серверы Frigate",
} }

26
src/types/event.ts Normal file
View File

@ -0,0 +1,26 @@
export interface Event {
id: string;
label: string;
sub_label?: string;
camera: string;
start_time: number;
end_time?: number;
false_positive: boolean;
zones: string[];
thumbnail: string;
has_clip: boolean;
has_snapshot: boolean;
retain_indefinitely: boolean;
plus_id?: string;
model_hash?: string;
data: {
top_score: number;
score: number;
sub_label_score?: number;
region: number[];
box: number[];
area: number;
ratio: number;
type: "object" | "audio" | "manual";
};
}

46
src/types/record.ts Normal file
View File

@ -0,0 +1,46 @@
export type Recording = {
id: string;
camera: string;
start_time: number;
end_time: number;
path: string;
segment_size: number;
duration: number;
motion: number;
objects: number;
dBFS: number;
};
export type RecordingSegment = {
id: string;
start_time: number;
end_time: number;
motion: number;
objects: number;
segment_size: number;
duration: number;
};
export type RecordingActivity = {
[hour: number]: RecordingSegmentActivity[];
};
export type RecordingSegmentActivity = {
date: number;
count: number;
hasObjects: boolean;
};
export interface RecordSummary {
day: string
events: number
hours: RecordHour[]
}
export interface RecordHour {
duration: number
events: number
hour: string
motion: number
objects: number
}

View File

@ -8,6 +8,7 @@ export const testHeaderLinks: HeaderActionProps =
{link: routesPath.MAIN_PATH, label: headerMenu.home, links: []}, {link: routesPath.MAIN_PATH, label: headerMenu.home, links: []},
{link: routesPath.TEST_PATH, label: headerMenu.test, links: []}, {link: routesPath.TEST_PATH, label: headerMenu.test, links: []},
{link: routesPath.SETTINGS_PATH, label: headerMenu.settings, links: []}, {link: routesPath.SETTINGS_PATH, label: headerMenu.settings, links: []},
{link: routesPath.RECORDINGS_PATH, label: headerMenu.recordings, links: []},
{link: routesPath.HOSTS_PATH, label: headerMenu.hostsConfig, links: []}, {link: routesPath.HOSTS_PATH, label: headerMenu.hostsConfig, links: []},
{link: routesPath.ROLES_PATH, label: headerMenu.acessSettings, links: []}, {link: routesPath.ROLES_PATH, label: headerMenu.acessSettings, links: []},
] ]

205
yarn.lock
View File

@ -1140,6 +1140,13 @@
dependencies: dependencies:
regenerator-runtime "^0.13.11" regenerator-runtime "^0.13.11"
"@babel/runtime@^7.5.5":
version "7.23.9"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7"
integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==
dependencies:
regenerator-runtime "^0.14.0"
"@babel/template@^7.22.5", "@babel/template@^7.3.3": "@babel/template@^7.22.5", "@babel/template@^7.3.3":
version "7.22.5" version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec"
@ -2599,6 +2606,11 @@
resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.17.tgz#0a6d1510395065171e3378a4afc587a3aefa7cc1" resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.17.tgz#0a6d1510395065171e3378a4afc587a3aefa7cc1"
integrity sha512-aqayTNmeWrZcvnG2MG9eGYI6b7S5fl+yKgPs6bAjOTwPS316R5SxBGKvtSExfyoJU7pIeHJfsHI0Ji41RVMkvQ== integrity sha512-aqayTNmeWrZcvnG2MG9eGYI6b7S5fl+yKgPs6bAjOTwPS316R5SxBGKvtSExfyoJU7pIeHJfsHI0Ji41RVMkvQ==
"@types/video.js@^7.3.56":
version "7.3.56"
resolved "https://registry.yarnpkg.com/@types/video.js/-/video.js-7.3.56.tgz#b66001b64055d58fd49ad69543b0e92b9b850adf"
integrity sha512-T3cp/kDuNj8scIzK87u0uomUDKnYQE1uHAA0zFPNGc0qCF3aLxZmMtpWvGxofEPNOpUfvtbsQMvwbjU42q8omw==
"@types/ws@^8.5.5": "@types/ws@^8.5.5":
version "8.5.5" version "8.5.5"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb"
@ -2716,6 +2728,47 @@
"@typescript-eslint/types" "5.61.0" "@typescript-eslint/types" "5.61.0"
eslint-visitor-keys "^3.3.0" eslint-visitor-keys "^3.3.0"
"@videojs/http-streaming@3.10.0":
version "3.10.0"
resolved "https://registry.yarnpkg.com/@videojs/http-streaming/-/http-streaming-3.10.0.tgz#b20eaf7246cc014f2715c967a9cdc4240b6e7e61"
integrity sha512-Lf1rmhTalV4Gw0bJqHmH4lfk/FlepUDs9smuMtorblAYnqDlE2tbUOb7sBXVYoXGdbWbdTW8jH2cnS+6HWYJ4Q==
dependencies:
"@babel/runtime" "^7.12.5"
"@videojs/vhs-utils" "4.0.0"
aes-decrypter "4.0.1"
global "^4.4.0"
m3u8-parser "^7.1.0"
mpd-parser "^1.3.0"
mux.js "7.0.2"
video.js "^7 || ^8"
"@videojs/vhs-utils@4.0.0", "@videojs/vhs-utils@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@videojs/vhs-utils/-/vhs-utils-4.0.0.tgz#4d4dbf5d61a9fbd2da114b84ec747c3a483bc60d"
integrity sha512-xJp7Yd4jMLwje2vHCUmi8MOUU76nxiwII3z4Eg3Ucb+6rrkFVGosrXlMgGnaLjq724j3wzNElRZ71D/CKrTtxg==
dependencies:
"@babel/runtime" "^7.12.5"
global "^4.4.0"
url-toolkit "^2.2.1"
"@videojs/vhs-utils@^3.0.5":
version "3.0.5"
resolved "https://registry.yarnpkg.com/@videojs/vhs-utils/-/vhs-utils-3.0.5.tgz#665ba70d78258ba1ab977364e2fe9f4d4799c46c"
integrity sha512-PKVgdo8/GReqdx512F+ombhS+Bzogiofy1LgAj4tN8PfdBx3HSS7V5WfJotKTqtOWGwVfSWsrYN/t09/DSryrw==
dependencies:
"@babel/runtime" "^7.12.5"
global "^4.4.0"
url-toolkit "^2.2.1"
"@videojs/xhr@2.6.0":
version "2.6.0"
resolved "https://registry.yarnpkg.com/@videojs/xhr/-/xhr-2.6.0.tgz#cd897e0ad54faf497961bcce3fa16dc15a26bb80"
integrity sha512-7J361GiN1tXpm+gd0xz2QWr3xNWBE+rytvo8J3KuggFaLg+U37gZQ2BuPLcnkfGffy2e+ozY70RHC8jt7zjA6Q==
dependencies:
"@babel/runtime" "^7.5.5"
global "~4.4.0"
is-function "^1.0.1"
"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": "@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5":
version "1.11.6" version "1.11.6"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24"
@ -2837,6 +2890,11 @@
"@webassemblyjs/ast" "1.11.6" "@webassemblyjs/ast" "1.11.6"
"@xtuc/long" "4.2.2" "@xtuc/long" "4.2.2"
"@xmldom/xmldom@^0.8.3":
version "0.8.10"
resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz#a1337ca426aa61cef9fe15b5b28e340a72f6fa99"
integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==
"@xtuc/ieee754@^1.2.0": "@xtuc/ieee754@^1.2.0":
version "1.2.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
@ -2906,6 +2964,16 @@ adjust-sourcemap-loader@^4.0.0:
loader-utils "^2.0.0" loader-utils "^2.0.0"
regex-parser "^2.2.11" regex-parser "^2.2.11"
aes-decrypter@4.0.1, aes-decrypter@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/aes-decrypter/-/aes-decrypter-4.0.1.tgz#c1a81d0bde0e96fed0674488d2a31a6d7ab9b7a7"
integrity sha512-H1nh/P9VZXUf17AA5NQfJML88CFjVBDuGkp5zDHa7oEhYN9TTpNLJknRY1ie0iSKWlDf6JRnJKaZVDSQdPy6Cg==
dependencies:
"@babel/runtime" "^7.12.5"
"@videojs/vhs-utils" "^3.0.5"
global "^4.4.0"
pkcs7 "^1.0.4"
agent-base@6: agent-base@6:
version "6.0.2" version "6.0.2"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
@ -4276,6 +4344,11 @@ dom-serializer@^1.0.1:
domhandler "^4.2.0" domhandler "^4.2.0"
entities "^2.0.0" entities "^2.0.0"
dom-walk@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84"
integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==
domelementtype@1: domelementtype@1:
version "1.3.1" version "1.3.1"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
@ -5278,6 +5351,14 @@ global-prefix@^3.0.0:
kind-of "^6.0.2" kind-of "^6.0.2"
which "^1.3.1" which "^1.3.1"
global@4.4.0, global@^4.3.1, global@^4.4.0, global@~4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406"
integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==
dependencies:
min-document "^2.19.0"
process "^0.11.10"
globals@^11.1.0: globals@^11.1.0:
version "11.12.0" version "11.12.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
@ -5612,6 +5693,11 @@ indent-string@^4.0.0:
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
individual@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/individual/-/individual-2.0.0.tgz#833b097dad23294e76117a98fb38e0d9ad61bb97"
integrity sha512-pWt8hBCqJsUWI/HtcfWod7+N9SgAqyPEaF7JQjwzjn5vGrpg6aQ5qeAFQ7dx//UH4J1O+7xqew+gCeeFt6xN/g==
inflight@^1.0.4: inflight@^1.0.4:
version "1.0.6" version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
@ -5739,6 +5825,11 @@ is-fullwidth-code-point@^3.0.0:
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
is-function@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08"
integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==
is-generator-fn@^2.0.0: is-generator-fn@^2.0.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
@ -6630,6 +6721,11 @@ jwt-decode@^3.1.2:
resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59" resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59"
integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A== integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==
keycode@2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.0.tgz#3d0af56dc7b8b8e5cba8d0a97f107204eec22b04"
integrity sha512-ps3I9jAdNtRpJrbBvQjpzyFbss/skHqzS+eu4RxKLaEAtFqkjZaB6TZMSivPbLxf4K7VI4SjR0P5mRCX5+Q25A==
kind-of@^6.0.2: kind-of@^6.0.2:
version "6.0.3" version "6.0.3"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
@ -6792,6 +6888,15 @@ lz-string@^1.5.0:
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"
integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
m3u8-parser@^7.1.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/m3u8-parser/-/m3u8-parser-7.1.0.tgz#fa92ee22fc798150397c297152c879fe09f066c6"
integrity sha512-7N+pk79EH4oLKPEYdgRXgAsKDyA/VCo0qCHlUwacttQA0WqsjZQYmNfywMvjlY9MpEBVZEt0jKFd73Kv15EBYQ==
dependencies:
"@babel/runtime" "^7.12.5"
"@videojs/vhs-utils" "^3.0.5"
global "^4.4.0"
magic-string@^0.25.0, magic-string@^0.25.7: magic-string@^0.25.0, magic-string@^0.25.7:
version "0.25.9" version "0.25.9"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
@ -6894,6 +6999,13 @@ mimic-fn@^2.1.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
min-document@^2.19.0:
version "2.19.0"
resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
integrity sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==
dependencies:
dom-walk "^0.1.0"
min-indent@^1.0.0: min-indent@^1.0.0:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869"
@ -6999,6 +7111,16 @@ monaco-yaml@^5.1.1:
vscode-uri "^3.0.0" vscode-uri "^3.0.0"
yaml "^2.0.0" yaml "^2.0.0"
mpd-parser@^1.2.2, mpd-parser@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/mpd-parser/-/mpd-parser-1.3.0.tgz#38c20f4d73542b4ed554158bc1f0fa571dc61388"
integrity sha512-WgeIwxAqkmb9uTn4ClicXpEQYCEduDqRKfmUdp4X8vmghKfBNXZLYpREn9eqrDx/Tf5LhzRcJLSpi4ohfV742Q==
dependencies:
"@babel/runtime" "^7.12.5"
"@videojs/vhs-utils" "^4.0.0"
"@xmldom/xmldom" "^0.8.3"
global "^4.4.0"
ms@2.0.0: ms@2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@ -7022,6 +7144,14 @@ multicast-dns@^7.2.5:
dns-packet "^5.2.2" dns-packet "^5.2.2"
thunky "^1.0.2" thunky "^1.0.2"
mux.js@7.0.2, mux.js@^7.0.1:
version "7.0.2"
resolved "https://registry.yarnpkg.com/mux.js/-/mux.js-7.0.2.tgz#410641dc922c5d173d7ce45fbdb2bb9e2a69137c"
integrity sha512-CM6+QuyDbc0qW1OfEjkd2+jVKzTXF+z5VOKH0eZxtZtnrG/ilkW/U7l7IXGtBNLASF9sKZMcK1u669cq50Qq0A==
dependencies:
"@babel/runtime" "^7.11.2"
global "^4.4.0"
mz@^2.7.0: mz@^2.7.0:
version "2.7.0" version "2.7.0"
resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
@ -7425,6 +7555,13 @@ pirates@^4.0.1, pirates@^4.0.4:
resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9"
integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==
pkcs7@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/pkcs7/-/pkcs7-1.0.4.tgz#6090b9e71160dabf69209d719cbafa538b00a1cb"
integrity sha512-afRERtHn54AlwaF2/+LFszyAANTCggGilmcmILUzEjvs3XgFZT+xE6+QWQcAGmu4xajy+Xtj7acLOPdx5/eXWQ==
dependencies:
"@babel/runtime" "^7.5.5"
pkg-dir@^4.1.0, pkg-dir@^4.2.0: pkg-dir@^4.1.0, pkg-dir@^4.2.0:
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
@ -8047,6 +8184,11 @@ process-nextick-args@~2.0.0:
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
process@^0.11.10:
version "0.11.10"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
promise@^8.1.0: promise@^8.1.0:
version "8.3.0" version "8.3.0"
resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a" resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a"
@ -8412,6 +8554,11 @@ regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.9:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
regenerator-runtime@^0.14.0:
version "0.14.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f"
integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==
regenerator-transform@^0.15.1: regenerator-transform@^0.15.1:
version "0.15.1" version "0.15.1"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56"
@ -8580,6 +8727,13 @@ run-parallel@^1.1.9:
dependencies: dependencies:
queue-microtask "^1.2.2" queue-microtask "^1.2.2"
rust-result@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/rust-result/-/rust-result-1.0.0.tgz#34c75b2e6dc39fe5875e5bdec85b5e0f91536f72"
integrity sha512-6cJzSBU+J/RJCF063onnQf0cDUOHs9uZI1oroSGnHOph+CQTIJ5Pp2hK5kEQq1+7yE/EEWfulSNXAQ2jikPthA==
dependencies:
individual "^2.0.0"
safe-array-concat@^1.0.0: safe-array-concat@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.0.tgz#2064223cba3c08d2ee05148eedbc563cd6d84060" resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.0.tgz#2064223cba3c08d2ee05148eedbc563cd6d84060"
@ -8600,6 +8754,13 @@ safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
safe-json-parse@4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/safe-json-parse/-/safe-json-parse-4.0.0.tgz#7c0f578cfccd12d33a71c0e05413e2eca171eaac"
integrity sha512-RjZPPHugjK0TOzFrLZ8inw44s9bKox99/0AZW9o/BEQVrJfhI+fIHMErnPyRa89/yRXUUr93q+tiN6zhoVV4wQ==
dependencies:
rust-result "^1.0.0"
safe-regex-test@^1.0.0: safe-regex-test@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295"
@ -9554,6 +9715,11 @@ url-parse@^1.5.3:
querystringify "^2.1.1" querystringify "^2.1.1"
requires-port "^1.0.0" requires-port "^1.0.0"
url-toolkit@^2.2.1:
version "2.2.5"
resolved "https://registry.yarnpkg.com/url-toolkit/-/url-toolkit-2.2.5.tgz#58406b18e12c58803e14624df5e374f638b0f607"
integrity sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg==
use-callback-ref@^1.3.0: use-callback-ref@^1.3.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.0.tgz#772199899b9c9a50526fedc4993fc7fa1f7e32d5" resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.0.tgz#772199899b9c9a50526fedc4993fc7fa1f7e32d5"
@ -9640,6 +9806,45 @@ vary@~1.1.2:
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
"video.js@^7 || ^8", video.js@^8.10.0:
version "8.10.0"
resolved "https://registry.yarnpkg.com/video.js/-/video.js-8.10.0.tgz#603a49909ef33f839264da8b73513f9daf592b57"
integrity sha512-7UeG/flj/pp8tNGW8WKPP1VJb3x2FgLoqUWzpZqkoq5YIyf6MNzmIrKtxprl438T5RVkcj+OzV8IX4jYSAn4Sw==
dependencies:
"@babel/runtime" "^7.12.5"
"@videojs/http-streaming" "3.10.0"
"@videojs/vhs-utils" "^4.0.0"
"@videojs/xhr" "2.6.0"
aes-decrypter "^4.0.1"
global "4.4.0"
keycode "2.2.0"
m3u8-parser "^7.1.0"
mpd-parser "^1.2.2"
mux.js "^7.0.1"
safe-json-parse "4.0.0"
videojs-contrib-quality-levels "4.0.0"
videojs-font "4.1.0"
videojs-vtt.js "0.15.5"
videojs-contrib-quality-levels@4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/videojs-contrib-quality-levels/-/videojs-contrib-quality-levels-4.0.0.tgz#faa8096594cdbfc3ccbefe8572fc20531ba23f3d"
integrity sha512-u5rmd8BjLwANp7XwuQ0Q/me34bMe6zg9PQdHfTS7aXgiVRbNTb4djcmfG7aeSrkpZjg+XCLezFNenlJaCjBHKw==
dependencies:
global "^4.4.0"
videojs-font@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/videojs-font/-/videojs-font-4.1.0.tgz#3ae1dbaac60b4f0f1c4e6f7ff9662a89df176015"
integrity sha512-X1LuPfLZPisPLrANIAKCknZbZu5obVM/ylfd1CN+SsCmPZQ3UMDPcvLTpPBJxcBuTpHQq2MO1QCFt7p8spnZ/w==
videojs-vtt.js@0.15.5:
version "0.15.5"
resolved "https://registry.yarnpkg.com/videojs-vtt.js/-/videojs-vtt.js-0.15.5.tgz#567776eaf2a7a928d88b148a8b401ade2406f2ca"
integrity sha512-yZbBxvA7QMYn15Lr/ZfhhLPrNpI/RmCSCqgIff57GC2gIrV5YfyzLfLyZMj0NnZSAz8syB4N0nHXpZg9MyrMOQ==
dependencies:
global "^4.3.1"
vscode-jsonrpc@8.2.0: vscode-jsonrpc@8.2.0:
version "8.2.0" version "8.2.0"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz#f43dfa35fb51e763d17cd94dcca0c9458f35abf9" resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz#f43dfa35fb51e763d17cd94dcca0c9458f35abf9"