add recordings
This commit is contained in:
parent
b4a380aba7
commit
f369692133
@ -43,6 +43,7 @@
|
||||
"strftime": "0.10.1",
|
||||
"typescript": "^4.4.2",
|
||||
"validator": "^13.9.0",
|
||||
"video.js": "^8.10.0",
|
||||
"web-vitals": "^2.1.0",
|
||||
"zod": "^3.21.4"
|
||||
},
|
||||
@ -74,6 +75,7 @@
|
||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||
"@types/strftime": "^0.9.8",
|
||||
"@types/uuid": "^9.0.2",
|
||||
"@types/video.js": "^7.3.56",
|
||||
"uuid": "^9.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,6 +35,7 @@ const MainBody = observer(() => {
|
||||
if (isError) return <RetryError onRetry={refetch} />
|
||||
|
||||
const cards = () => {
|
||||
// return cameras.filter(cam => cam.frigateHost?.host.includes('5001')).slice(0,1).map(camera => (
|
||||
return cameras.map(camera => (
|
||||
<CameraCard
|
||||
key={camera.id}
|
||||
|
||||
@ -1,32 +1,175 @@
|
||||
// // import { route } from 'preact-router';
|
||||
// import { Fragment, useState, useRef, useCallback, useMemo } from 'react';
|
||||
// import VideoPlayer from '../shared/components/frigate/VideoPlayer';
|
||||
// import CogwheelLoader from '../shared/components/CogwheelLoader';
|
||||
// import { Grid, Text } from '@mantine/core';
|
||||
// import MultiSelect from '../shared/components/frigate/MultiSelect';
|
||||
// import Button from '../shared/components/frigate/Button';
|
||||
// import StarRecording from '../shared/components/frigate/icons/StarRecording';
|
||||
// import Submitted from '../shared/components/frigate/icons/Submitted';
|
||||
// import CalendarIcon from '../shared/components/frigate/icons/CalendarIcon';
|
||||
// import Menu, { MenuItem } from '../shared/components/frigate/Menu';
|
||||
// import Dialog from '../shared/components/frigate/Dialog';
|
||||
// import TimelineSummary from '../shared/components/frigate/TimelineSummary';
|
||||
// import TimelineEventOverlay from '../shared/components/frigate/TimelineEventOverlay';
|
||||
// import { Tabs, TextTab } from '../shared/components/frigate/Tabs';
|
||||
// import Clock from '../shared/components/frigate/icons/Clock';
|
||||
// import TimeAgo from '../shared/components/frigate/TimeAgo';
|
||||
// import Camera from '../shared/components/frigate/icons/Camera';
|
||||
// import Zone from '../shared/components/frigate/icons/Zone';
|
||||
// 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';
|
||||
import MultiSelect from '../shared/components/frigate/MultiSelect';
|
||||
import StarRecording from '../shared/components/frigate/icons/StarRecording';
|
||||
import Submitted from '../shared/components/frigate/icons/Submitted';
|
||||
import CalendarIcon from '../shared/components/frigate/icons/CalendarIcon';
|
||||
import Menu, { MenuItem } from '../shared/components/frigate/Menu';
|
||||
import Dialog from '../shared/components/frigate/Dialog';
|
||||
import TimelineSummary from '../shared/components/frigate/TimelineSummary';
|
||||
import TimelineEventOverlay from '../shared/components/frigate/TimelineEventOverlay';
|
||||
import { Tabs, TextTab } from '../shared/components/frigate/Tabs';
|
||||
import Clock from '../shared/components/frigate/icons/Clock';
|
||||
import TimeAgo from '../shared/components/frigate/TimeAgo';
|
||||
import Camera from '../shared/components/frigate/icons/Camera';
|
||||
import Zone from '../shared/components/frigate/icons/Zone';
|
||||
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';
|
||||
import { IconDownload, IconStar, IconStarFilled } from '@tabler/icons-react';
|
||||
// ↑↑↑ from frigate ↑↑↑
|
||||
|
||||
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 (
|
||||
<div />
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
export default RecordingsPage
|
||||
|
||||
// const API_LIMIT = 25;
|
||||
|
||||
@ -45,7 +188,7 @@ export default function EventsPage() {
|
||||
// export default function Events({ path, ...props }) {
|
||||
// // const apiHost = useApiHost();
|
||||
// // 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({
|
||||
// before: null,
|
||||
// after: null,
|
||||
@ -284,12 +427,12 @@ export default function EventsPage() {
|
||||
// [path, searchParams, setSearchParams]
|
||||
// );
|
||||
|
||||
// const onClickFilterSubmitted = useCallback(() => {
|
||||
// if (++searchParams.is_submitted > 1) {
|
||||
// searchParams.is_submitted = -1;
|
||||
// }
|
||||
// onFilter('is_submitted', searchParams.is_submitted);
|
||||
// }, [searchParams, onFilter]);
|
||||
// // const onClickFilterSubmitted = useCallback(() => {
|
||||
// // if (++searchParams.is_submitted > 1) {
|
||||
// // searchParams.is_submitted = -1;
|
||||
// // }
|
||||
// // onFilter('is_submitted', searchParams.is_submitted);
|
||||
// // }, [searchParams, onFilter]);
|
||||
|
||||
// const isDone = (eventPages?.[eventPages.length - 1]?.length ?? 0) < API_LIMIT;
|
||||
|
||||
@ -405,14 +548,14 @@ export default function EventsPage() {
|
||||
// )}
|
||||
|
||||
// <div className="ml-auto flex">
|
||||
// {config.plus.enabled && (
|
||||
// {/* {config.plus.enabled && (
|
||||
// <Submitted
|
||||
// className="h-10 w-10 text-yellow-300 cursor-pointer ml-auto"
|
||||
// onClick={() => onClickFilterSubmitted()}
|
||||
// inner_fill={searchParams.is_submitted == 1 ? 'currentColor' : 'gray'}
|
||||
// outer_stroke={searchParams.is_submitted >= 0 ? 'currentColor' : 'gray'}
|
||||
// />
|
||||
// )}
|
||||
// )} */}
|
||||
|
||||
// <StarRecording
|
||||
// className="h-10 w-10 text-yellow-300 cursor-pointer ml-auto"
|
||||
@ -773,14 +916,17 @@ export default function EventsPage() {
|
||||
// <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)`,
|
||||
// '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"
|
||||
// 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
|
||||
@ -825,7 +971,7 @@ export default function EventsPage() {
|
||||
// : `, ${event.sub_label}: ${(event?.data?.sub_label_score * 100).toFixed(0)}%`}
|
||||
// </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' && (
|
||||
// <Fragment>
|
||||
// {event.plus_id ? (
|
||||
@ -849,19 +995,11 @@ export default function EventsPage() {
|
||||
// )}
|
||||
// </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 className="flex flex-col">
|
||||
// {event.has_clip || event.has_snapshot ?
|
||||
// <IconDownload onClick={(e) => onDownloadClick(e, event)} />
|
||||
// : <></>}
|
||||
// </div>
|
||||
// </div>
|
||||
// </div>
|
||||
@ -1,7 +1,6 @@
|
||||
export const routesPath = {
|
||||
MAIN_PATH: '/',
|
||||
BIRDSEYE_PATH: '/birdseye',
|
||||
EVENTS_PATH: '/events',
|
||||
RECORDINGS_PATH: '/recordings',
|
||||
SETTINGS_PATH: '/settings',
|
||||
HOSTS_PATH: '/hosts',
|
||||
|
||||
@ -11,6 +11,7 @@ import HostConfigPage from "../pages/HostConfigPage";
|
||||
import HostSystemPage from "../pages/HostSystemPage";
|
||||
import HostStoragePage from "../pages/HostStoragePage";
|
||||
import LiveCameraPage from "../pages/LiveCameraPage";
|
||||
import RecordingsPage from "../pages/RecordingsPage";
|
||||
|
||||
interface IRoute {
|
||||
path: string,
|
||||
@ -30,6 +31,10 @@ export const routes: IRoute[] = [
|
||||
path: routesPath.HOSTS_PATH,
|
||||
component: <FrigateHostsPage />,
|
||||
},
|
||||
{
|
||||
path: routesPath.RECORDINGS_PATH,
|
||||
component: <RecordingsPage />,
|
||||
},
|
||||
{
|
||||
path: routesPath.HOST_CONFIG_PATH,
|
||||
component: <HostConfigPage />,
|
||||
|
||||
@ -4,6 +4,7 @@ import { z } from "zod"
|
||||
import { GetConfig, DeleteFrigateHost, GetFrigateHost, PutConfig, PutFrigateHost, GetFrigateHostWithCameras, GetCameraWHost, GetCameraWHostWConfig } from "./frigate.schema";
|
||||
import { FrigateConfig } from "../../types/frigateConfig";
|
||||
import { url } from "inspector";
|
||||
import { RecordSummary } from "../../types/record";
|
||||
|
||||
|
||||
const instanceApi = axios.create({
|
||||
@ -17,7 +18,7 @@ export const frigateApi = {
|
||||
getHosts: () => instanceApi.get<GetFrigateHost[]>('apiv1/frigate-hosts').then(res => {
|
||||
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
|
||||
}),
|
||||
getHost: (id: string) => instanceApi.get<GetFrigateHostWithCameras>(`apiv1/frigate-hosts/${id}`).then(res => {
|
||||
@ -34,8 +35,8 @@ export const frigateApi = {
|
||||
}
|
||||
|
||||
export const proxyApi = {
|
||||
getHostConfigRaw: (hostName: string) => instanceApi.get('proxy/api/config/raw', { params: { hostName: hostName } }).then(res => res.data),
|
||||
getHostConfig: (hostName: string) => instanceApi.get('proxy/api/config', { 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/${hostName}/api/config`).then(res => res.data),
|
||||
getImageFrigate: async (imageUrl: string) => {
|
||||
const response = await fetch(imageUrl);
|
||||
if (!response.ok) {
|
||||
@ -43,11 +44,59 @@ export const proxyApi = {
|
||||
}
|
||||
return response.blob();
|
||||
},
|
||||
cameraWsURL: (hostName: string, cameraName: string) => {
|
||||
return `ws://${proxyURL.host}/proxy-ws/live/jsmpeg/${cameraName}?hostName=${hostName}`
|
||||
},
|
||||
cameraImageURL: (hostName: string, cameraName: string) => {
|
||||
return `http://${proxyURL.host}/proxy/api/${cameraName}/latest.jpg?hostName=${hostName}`
|
||||
getHostRestart: (hostName: string) => instanceApi.get(`proxy/${hostName}/api/restart`).then(res => res.data),
|
||||
|
||||
getRecordings: (
|
||||
hostName: string,
|
||||
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',
|
||||
getCameraWHost: 'camera-frigate-host',
|
||||
getHostConfig: 'host-config',
|
||||
getRecordingsSummary: 'recordings-frigate-summary',
|
||||
getRecordings: 'recordings-frigate',
|
||||
}
|
||||
|
||||
@ -52,9 +52,6 @@ const CameraCard = ({
|
||||
const handleOpenRecordings = () => {
|
||||
throw Error('Not yet implemented')
|
||||
}
|
||||
const handleOpenEvents = () => {
|
||||
throw Error('Not yet implemented')
|
||||
}
|
||||
return (
|
||||
<Grid.Col md={6} lg={3} p='0.2rem'>
|
||||
<Card h='100%' radius="lg" padding='0.5rem' className={classes.mainCard}>
|
||||
@ -66,7 +63,6 @@ const CameraCard = ({
|
||||
className={classes.bottomGroup}>
|
||||
<Flex justify='space-evenly' mt='0.5rem' w='100%'>
|
||||
<Button size='sm' onClick={handleOpenRecordings}>Recordings</Button>
|
||||
<Button size='sm' onClick={handleOpenEvents}>Events</Button>
|
||||
</Flex>
|
||||
</Group>
|
||||
</Card>
|
||||
|
||||
72
src/shared/components/accordion/CameraAccordion.tsx
Normal file
72
src/shared/components/accordion/CameraAccordion.tsx
Normal 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;
|
||||
94
src/shared/components/accordion/DayAccordion.tsx
Normal file
94
src/shared/components/accordion/DayAccordion.tsx
Normal 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;
|
||||
46
src/shared/components/accordion/PlayControl.tsx
Normal file
46
src/shared/components/accordion/PlayControl.tsx
Normal 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;
|
||||
11
src/shared/components/accordion/TestAccordion.tsx
Normal file
11
src/shared/components/accordion/TestAccordion.tsx
Normal file
@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
|
||||
const TestAccordion = ({ camera }: { camera: any }) => {
|
||||
return (
|
||||
<div>
|
||||
TEST ACCORDION {camera.name}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TestAccordion;
|
||||
@ -2,9 +2,18 @@ import { SelectItem, SystemProp, SpacingValue, SelectProps, Box, Flex, CloseButt
|
||||
import React, { CSSProperties } from 'react';
|
||||
import CloseWithTooltip from '../CloseWithTooltip';
|
||||
import { strings } from '../../strings/strings';
|
||||
|
||||
|
||||
export interface OneSelectItem {
|
||||
value: string;
|
||||
label: string;
|
||||
selected?: boolean;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
interface OneSelectFilterProps {
|
||||
id: string
|
||||
data: SelectItem[]
|
||||
data: OneSelectItem[]
|
||||
spaceBetween?: SystemProp<SpacingValue>
|
||||
label?: string
|
||||
defaultValue?: string
|
||||
|
||||
@ -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>
|
||||
);
|
||||
}
|
||||
72
src/shared/components/frigate/VideoPlayer.tsx
Normal file
72
src/shared/components/frigate/VideoPlayer.tsx
Normal 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;
|
||||
@ -1,7 +1,9 @@
|
||||
import { h } from 'preact';
|
||||
import { memo } from 'preact/compat';
|
||||
import { memo } from 'react';
|
||||
|
||||
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 (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
||||
33
src/shared/stores/recordings.store.ts
Normal file
33
src/shared/stores/recordings.store.ts
Normal 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)
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ import { FiltersStore } from "./filters/filters.store";
|
||||
import { ModalStore } from "./modal.store";
|
||||
import { OrdersStore } from "./orders.store";
|
||||
import { ProductStore } from "./product.store";
|
||||
import { RecordingsStore } from "./recordings.store";
|
||||
import { SettingsStore } from "./settings.store";
|
||||
import { SideBarsStore } from "./sidebars.store";
|
||||
import PostStore from "./test.store";
|
||||
@ -18,6 +19,7 @@ class RootStore {
|
||||
categoryStore: CategoryStore
|
||||
filtersStore: FiltersStore
|
||||
sideBarsStore: SideBarsStore
|
||||
recordingsStore: RecordingsStore
|
||||
ordersStore: OrdersStore
|
||||
settingsStore: SettingsStore
|
||||
constructor() {
|
||||
@ -29,6 +31,7 @@ class RootStore {
|
||||
this.categoryStore = new CategoryStore()
|
||||
this.filtersStore = new FiltersStore()
|
||||
this.sideBarsStore = new SideBarsStore()
|
||||
this.recordingsStore = new RecordingsStore()
|
||||
this.ordersStore = new OrdersStore()
|
||||
this.settingsStore = new SettingsStore()
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ export const headerMenu = {
|
||||
home:"На главную",
|
||||
test:"Тест",
|
||||
settings:"Настройки",
|
||||
recordings:"Записи",
|
||||
acessSettings:" Настройка доступа",
|
||||
hostsConfig:"Серверы Frigate",
|
||||
}
|
||||
|
||||
26
src/types/event.ts
Normal file
26
src/types/event.ts
Normal 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
46
src/types/record.ts
Normal 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
|
||||
}
|
||||
@ -8,6 +8,7 @@ export const testHeaderLinks: HeaderActionProps =
|
||||
{link: routesPath.MAIN_PATH, label: headerMenu.home, links: []},
|
||||
{link: routesPath.TEST_PATH, label: headerMenu.test, 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.ROLES_PATH, label: headerMenu.acessSettings, links: []},
|
||||
]
|
||||
|
||||
205
yarn.lock
205
yarn.lock
@ -1140,6 +1140,13 @@
|
||||
dependencies:
|
||||
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":
|
||||
version "7.22.5"
|
||||
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"
|
||||
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":
|
||||
version "8.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb"
|
||||
@ -2716,6 +2728,47 @@
|
||||
"@typescript-eslint/types" "5.61.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":
|
||||
version "1.11.6"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24"
|
||||
@ -2837,6 +2890,11 @@
|
||||
"@webassemblyjs/ast" "1.11.6"
|
||||
"@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":
|
||||
version "1.2.0"
|
||||
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"
|
||||
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:
|
||||
version "6.0.2"
|
||||
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"
|
||||
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:
|
||||
version "1.3.1"
|
||||
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"
|
||||
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:
|
||||
version "11.12.0"
|
||||
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"
|
||||
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:
|
||||
version "1.0.6"
|
||||
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"
|
||||
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:
|
||||
version "2.1.0"
|
||||
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"
|
||||
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:
|
||||
version "6.0.3"
|
||||
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"
|
||||
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:
|
||||
version "0.25.9"
|
||||
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"
|
||||
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:
|
||||
version "1.0.1"
|
||||
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"
|
||||
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:
|
||||
version "2.0.0"
|
||||
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"
|
||||
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:
|
||||
version "2.7.0"
|
||||
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"
|
||||
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:
|
||||
version "4.2.0"
|
||||
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"
|
||||
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:
|
||||
version "8.3.0"
|
||||
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"
|
||||
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:
|
||||
version "0.15.1"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56"
|
||||
@ -8580,6 +8727,13 @@ run-parallel@^1.1.9:
|
||||
dependencies:
|
||||
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:
|
||||
version "1.0.0"
|
||||
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"
|
||||
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:
|
||||
version "1.0.0"
|
||||
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"
|
||||
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:
|
||||
version "1.3.0"
|
||||
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"
|
||||
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:
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz#f43dfa35fb51e763d17cd94dcca0c9458f35abf9"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user