-
-
- {title}
-
- Copy
- Add
- Save
-
-
- {yamlPrefix}
- {Object.keys(points).map((mainkey) => {
- if (isMulti) {
- return (
-
- {` ${mainkey}:\n mask:\n`}
- {onAdd && showButtons ? (
-
- {`Add to ${mainkey}`}
-
- ) : null}
- {points[mainkey].map((item, subkey) => (
-
- ))}
-
- );
- }
- return (
-
- );
- })}
-
-
- );
-}
-
-function Item({ mainkey, subkey, editing, handleEdit, points, showButtons, _handleAdd, handleRemove, yamlKeyPrefix }) {
- return (
-
-
Events
-
-
onToggleNamedFilter('cameras', item)}
- onShowAll={() => onFilter('cameras', ['all'])}
- onSelectSingle={(item) => onFilter('cameras', item)}
- />
- onToggleNamedFilter('labels', item)}
- onShowAll={() => onFilter('labels', ['all'])}
- onSelectSingle={(item) => onFilter('labels', item)}
- />
- onToggleNamedFilter('zones', item)}
- onShowAll={() => onFilter('zones', ['all'])}
- onSelectSingle={(item) => onFilter('zones', item)}
- />
- {filterValues.sub_labels.length > 0 && (
- onToggleNamedFilter('sub_labels', item)}
- onShowAll={() => onFilter('sub_labels', ['all'])}
- onSelectSingle={(item) => onFilter('sub_labels', item)}
- />
- )}
- {searchParams.event && (
- onFilter('event', null)} type="text">
- View All
-
- )}
-
-
- {config.plus.enabled && (
- onClickFilterSubmitted()}
- inner_fill={searchParams.is_submitted == 1 ? 'currentColor' : 'gray'}
- outer_stroke={searchParams.is_submitted >= 0 ? 'currentColor' : 'gray'}
- />
- )}
-
- onFilter('favorites', searchParams.favorites ? 0 : 1)}
- fill={searchParams.favorites == 1 ? 'currentColor' : 'none'}
- />
-
-
-
- setState({ ...state, showDatePicker: true })}
- />
-
-
- {state.showDownloadMenu && (
-
setState({ ...state, showDownloadMenu: false })} relativeTo={downloadButton}>
- {downloadEvent.has_snapshot && (
-
- )}
- {downloadEvent.has_clip && (
-
- )}
- {(event?.data?.type || 'object') == 'object' &&
- downloadEvent.end_time &&
- downloadEvent.has_snapshot &&
- !downloadEvent.plus_id && (
- showSubmitToPlus(downloadEvent.id, downloadEvent.label, downloadEvent.box)}
- />
- )}
- {downloadEvent.plus_id && (
- setState({ ...state, showDownloadMenu: false })}
- />
- )}
-
- )}
- {state.showDatePicker && (
-
setState({ ...state, setShowDatePicker: false })}
- relativeTo={datePicker}
- >
-
-
-
-
-
-
- {
- setState({ ...state, showCalendar: true, showDatePicker: false });
- }}
- />
-
- )}
-
- {state.showCalendar && (
-
- setState({ ...state, showCalendar: false })}
- relativeTo={datePicker}
- >
- setState({ ...state, showCalendar: false })}
- >
-
-
-
-
- )}
- {state.showPlusSubmit && (
-
- {config.plus.enabled ? (
- <>
-
-
Submit to Frigate+
-
-
-
- {plusSubmitEvent.validBox ? (
-
- Objects in locations you want to avoid are not false positives. Submitting them as false positives
- will confuse the model.
-
- ) : (
-
- Events prior to version 0.13 can only be submitted to Frigate+ without annotations.
-
- )}
-
- {plusSubmitEvent.validBox ? (
-
- setState({ ...state, showPlusSubmit: false })} type="text">
- {uploading.includes(plusSubmitEvent.id) ? 'Close' : 'Cancel'}
-
- onSendToPlus(plusSubmitEvent.id, true, plusSubmitEvent.validBox)}
- disabled={uploading.includes(plusSubmitEvent.id)}
- type="text"
- >
- This is not a {plusSubmitEvent.label}
-
- onSendToPlus(plusSubmitEvent.id, false, plusSubmitEvent.validBox)}
- disabled={uploading.includes(plusSubmitEvent.id)}
- type="text"
- >
- This is a {plusSubmitEvent.label}
-
-
- ) : (
-
- setState({ ...state, showPlusSubmit: false })}
- disabled={uploading.includes(plusSubmitEvent.id)}
- type="text"
- >
- {uploading.includes(plusSubmitEvent.id) ? 'Close' : 'Cancel'}
-
- onSendToPlus(plusSubmitEvent.id, false, plusSubmitEvent.validBox)}
- disabled={uploading.includes(plusSubmitEvent.id)}
- type="text"
- >
- Submit to Frigate+
-
-
- )}
- >
- ) : (
- <>
-
-
- setState({ ...state, showPlusSubmit: false })} type="text">
- Close
-
-
- >
- )}
-
- )}
- {deleteFavoriteState.showDeleteFavorite && (
-
-
-
Delete Saved Event?
-
Confirm deletion of saved event.
-
-
- setDeleteFavoriteState({ ...state, showDeleteFavorite: false })}
- type="text"
- >
- Cancel
-
- {
- setDeleteFavoriteState({ ...state, showDeleteFavorite: false });
- onDelete(e, deleteFavoriteState.deletingFavoriteEventId, false);
- }}
- type="text"
- >
- Delete
-
-
-
- )}
-
- {ongoingEvents ? (
-
-
-
- Ongoing Events
-
-
-
-
-
setShowInProgress(!showInProgress)}
- >
- {showInProgress ? : }
-
-
- {showInProgress &&
- ongoingEvents.map((event, _) => {
- return (
-
{
- this.player = null;
- }}
- onDownloadClick={onDownloadClick}
- onReady={(player) => {
- this.player = player;
- this.player.on('playing', () => {
- setEventOverlay(undefined);
- });
- }}
- onSave={onSave}
- showSubmitToPlus={showSubmitToPlus}
- />
- );
- })}
-
- ) : null}
-
- Past Events
-
- {eventPages ? (
- eventPages.map((page, i) => {
- const lastPage = eventPages.length === i + 1;
- return page.map((event, j) => {
- const lastEvent = lastPage && page.length === j + 1;
- return (
-
{
- this.player = null;
- }}
- onDownloadClick={onDownloadClick}
- onReady={(player) => {
- this.player = player;
- this.player.on('playing', () => {
- setEventOverlay(undefined);
- });
- }}
- onSave={onSave}
- showSubmitToPlus={showSubmitToPlus}
- />
- );
- });
- })
- ) : (
-
- )}
-
-
-
- );
-}
-
-function Event({
- className = '',
- config,
- event,
- eventDetailType,
- eventOverlay,
- viewEvent,
- setViewEvent,
- lastEvent,
- lastEventRef,
- uploading,
- uploadErrors,
- handleEventDetailTabChange,
- onEventFrameSelected,
- onDelete,
- onDispose,
- onDownloadClick,
- onReady,
- onSave,
- showSubmitToPlus,
-}) {
- const getUploadButtonState = (eventId) => {
- const isUploading = uploading.includes(eventId);
- const hasUploadError = uploadErrors.find((event) => event.id === eventId);
- if (hasUploadError) {
- if (hasUploadError.isUnsupported) {
- return { isDisabled: true, label: 'Unsupported label' };
- }
- return { isDisabled: isUploading, label: 'Upload error' };
- }
-
- const label = isUploading ? 'Uploading...' : 'Send to Frigate+';
- return { isDisabled: isUploading, label };
- };
- const apiHost = useApiHost();
-
- return (
-
-
Export
-
- {message.text && (
-
{message.text}
- )}
-
- {selectedClip && (
-
-
- Playback
- {
- this.player = player;
- }}
- onDispose={() => {
- this.player = null;
- }}
- />
-
-
- setSelectedClip('')} type="text">
- Close
-
-
-
- )}
-
- {deleteClip && (
-
-
-
Delete Export?
-
Confirm deletion of {deleteClip}.
-
-
- setDeleteClip('')} type="text">
- Close
-
- onHandleDelete(deleteClip)} type="text">
- Delete
-
-
-
- )}
-
-
-
-
- {exports && (
-
- Exports
- setSelectedClip(clip)}
- onDeleteClip={(clip) => setDeleteClip(clip)}
- />
-
- )}
-
-
- );
-}
-
-function Exports({ exports, onSetClip, onDeleteClip }) {
- return (
-
-
Button
-
- Default
- Danger
- Save
- Gray
- Disabled
-
-
- Default
-
- Danger
-
-
- Save
-
-
- Gray
-
-
- Disabled
-
-
-
- Default
-
- Danger
-
-
- Save
-
-
- Gray
-
-
- Disabled
-
-
-
-
Dialog
-
{
- setShowDialog(true);
- }}
- >
- Show Dialog
-
- {showDialog ? (
-
- ) : null}
-
-
Switch
-
-
-
-
-
-
-
-
-
-
-
Select
-
-
-
-
-
TextField
-
-
-
-
-
-
-
-
- );
-}
diff --git a/web-old/src/routes/System.jsx b/web-old/src/routes/System.jsx
deleted file mode 100644
index 77b37686f..000000000
--- a/web-old/src/routes/System.jsx
+++ /dev/null
@@ -1,487 +0,0 @@
-import { h, Fragment } from 'preact';
-import ActivityIndicator from '../components/ActivityIndicator';
-import Button from '../components/Button';
-import Heading from '../components/Heading';
-import Link from '../components/Link';
-import { useWs } from '../api/ws';
-import useSWR from 'swr';
-import axios from 'axios';
-import { Table, Tbody, Thead, Tr, Th, Td } from '../components/Table';
-import { useState } from 'preact/hooks';
-import Dialog from '../components/Dialog';
-import TimeAgo from '../components/TimeAgo';
-import copy from 'copy-to-clipboard';
-import { About } from '../icons/About';
-import { WebUI } from '../icons/WebUI';
-
-const emptyObject = Object.freeze({});
-
-export default function System() {
- const [state, setState] = useState({ showFfprobe: false, ffprobe: '' });
- const { data: config } = useSWR('config');
-
- const {
- value: { payload: stats },
- } = useWs('stats');
- const { data: initialStats } = useSWR('stats');
-
- const {
- cpu_usages,
- gpu_usages,
- bandwidth_usages,
- detectors,
- service = {},
- detection_fps: _,
- processes,
- cameras,
- } = stats || initialStats || emptyObject;
-
- const detectorNames = Object.keys(detectors || emptyObject);
- const gpuNames = Object.keys(gpu_usages || emptyObject);
- const cameraNames = Object.keys(cameras || emptyObject);
- const processesNames = Object.keys(processes || emptyObject);
-
- const { data: go2rtc } = useSWR('go2rtc/api');
-
- const onHandleFfprobe = async (camera, e) => {
- if (e) {
- e.stopPropagation();
- }
-
- setState({ ...state, showFfprobe: true });
- const response = await axios.get('ffprobe', {
- params: {
- paths: `camera:${camera}`,
- },
- });
-
- if (response.status === 200) {
- setState({ ...state, showFfprobe: true, ffprobe: response.data });
- } else {
- setState({ ...state, showFfprobe: true, ffprobe: 'There was an error getting the ffprobe output.' });
- }
- };
-
- const onCopyFfprobe = async () => {
- copy(JSON.stringify(state.ffprobe).replace(/[\\\s]+/gi, ''));
- setState({ ...state, ffprobe: '', showFfprobe: false });
- };
-
- const onHandleVainfo = async (e) => {
- if (e) {
- e.stopPropagation();
- }
-
- const response = await axios.get('vainfo');
-
- if (response.status === 200) {
- setState({
- ...state,
- showVainfo: true,
- vainfo: response.data,
- });
- } else {
- setState({ ...state, showVainfo: true, vainfo: 'There was an error getting the vainfo output.' });
- }
- };
-
- const onCopyVainfo = async () => {
- copy(JSON.stringify(state.vainfo).replace(/[\\\s]+/gi, ''));
- setState({ ...state, vainfo: '', showVainfo: false });
- };
-
- return (
-
-
-
- System {service.version}
-
- {config && (
-
- go2rtc {go2rtc && `${go2rtc.version} `}
-
- streams info
-
-
- )}
-
-
- {service.last_updated && (
-
-
- Last refreshed:
-
-
- )}
-
- {state.showFfprobe && (
-
-
-
Ffprobe Output
- {state.ffprobe != '' ? (
-
- {state.ffprobe.map((stream, idx) => (
-
-
Stream {idx}:
-
Return Code: {stream.return_code}
-
- {stream.return_code == 0 ? (
-
- {stream.stdout.streams.map((codec, idx) => (
-
- {codec.width ? (
-
-
Video:
-
-
Codec: {codec.codec_long_name}
-
- Resolution: {codec.width}x{codec.height}
-
-
FPS: {codec.avg_frame_rate == '0/0' ? 'Unknown' : codec.avg_frame_rate}
-
-
- ) : (
-
-
Audio:
-
-
Codec: {codec.codec_long_name}
-
-
- )}
-
- ))}
-
- ) : (
-
-
Error: {stream.stderr}
-
- )}
-
- ))}
-
- ) : (
-
- )}
-
-
- onCopyFfprobe()} type="text">
- Copy
-
- setState({ ...state, ffprobe: '', showFfprobe: false })}
- type="text"
- >
- Close
-
-
-
- )}
-
- {state.showVainfo && (
-
-
-
Vainfo Output
- {state.vainfo != '' ? (
-
-
Return Code: {state.vainfo.return_code}
-
-
Process {state.vainfo.return_code == 0 ? 'Output' : 'Error'}:
-
-
{state.vainfo.return_code == 0 ? state.vainfo.stdout : state.vainfo.stderr}
-
- ) : (
-
- )}
-
-
- onCopyVainfo()} type="text">
- Copy
-
- setState({ ...state, vainfo: '', showVainfo: false })} type="text">
- Close
-
-
-
- )}
-
- {!detectors ? (
-
- ) : (
-
-
-
- {detectorNames.map((detector) => (
-
-
{detector}
-
-
-
-
- P-ID
- Inference Speed
- CPU %
- Memory %
- {config.telemetry.network_bandwidth && Network Bandwidth }
-
-
-
-
- {detectors[detector]['pid']}
- {detectors[detector]['inference_speed']} ms
- {cpu_usages[detectors[detector]['pid']]?.['cpu'] || '- '}%
- {cpu_usages[detectors[detector]['pid']]?.['mem'] || '- '}%
- {config.telemetry.network_bandwidth && (
- {bandwidth_usages[detectors[detector]['pid']]?.['bandwidth'] || '- '}KB/s
- )}
-
-
-
-
-
- ))}
-
-
-
-
-
onHandleVainfo(e)}>vainfo
-
-
- {!gpu_usages ? (
-
-
- Hardware acceleration has not been setup, see the docs to setup hardware acceleration.
-
-
- ) : (
-
- {gpuNames.map((gpu) => (
-
-
{gpu}
-
- {gpu_usages[gpu]['gpu'] == -1 ? (
-
- There was an error getting usage stats. This does not mean hardware acceleration is not working.
- Either your GPU does not support this or Frigate does not have proper access to get statistics.
- This is expected for the Home Assistant addon.
-
- ) : (
-
-
-
- GPU %
- Memory %
- {'dec' in gpu_usages[gpu] && Decoder % }
- {'enc' in gpu_usages[gpu] && Encoder % }
-
-
-
-
- {gpu_usages[gpu]['gpu']}
- {gpu_usages[gpu]['mem']}
- {'dec' in gpu_usages[gpu] && {gpu_usages[gpu]['dec']} }
- {'enc' in gpu_usages[gpu] && {gpu_usages[gpu]['enc']} }
-
-
-
- )}
-
-
- ))}
-
- )}
-
-
- {!cameras ? (
-
- ) : (
-
- {cameraNames.map(
- (camera) =>
- config.cameras[camera]['enabled'] && (
-
-
-
{camera.replaceAll('_', ' ')}
-
- {config.cameras[camera]['webui_url'] && (
-
- Web UI
-
-
- )}
- onHandleFfprobe(camera, e)}>
- ffprobe
-
-
-
-
-
-
-
- Process
- P-ID
- FPS
- CPU %
- Memory %
- {config.telemetry.network_bandwidth && Network Bandwidth }
-
-
-
-
-
- ffmpeg
- copy(cpu_usages[cameras[camera]['ffmpeg_pid']]?.['cmdline'])}
- >
-
-
-
- {cameras[camera]['ffmpeg_pid'] || '- '}
- {cameras[camera]['camera_fps'] || '- '}
- {cpu_usages[cameras[camera]['ffmpeg_pid']]?.['cpu'] || '- '}%
- {cpu_usages[cameras[camera]['ffmpeg_pid']]?.['mem'] || '- '}%
- {config.telemetry.network_bandwidth && (
- {bandwidth_usages[cameras[camera]['ffmpeg_pid']]?.['bandwidth'] || '- '}KB/s
- )}
-
-
- Capture
- {cameras[camera]['capture_pid'] || '- '}
- {cameras[camera]['process_fps'] || '- '}
- {cpu_usages[cameras[camera]['capture_pid']]?.['cpu'] || '- '}%
- {cpu_usages[cameras[camera]['capture_pid']]?.['mem'] || '- '}%
- {config.telemetry.network_bandwidth && - }
-
-
- Detect
- {cameras[camera]['pid'] || '- '}
-
- {(() => {
- if (cameras[camera]['pid'] && cameras[camera]['detection_enabled'] == 1)
- return (
-
- {cameras[camera]['detection_fps']} ({cameras[camera]['skipped_fps']} skipped)
-
- );
- else if (cameras[camera]['pid'] && cameras[camera]['detection_enabled'] == 0)
- return disabled ;
-
- return - ;
- })()}
-
- {cpu_usages[cameras[camera]['pid']]?.['cpu'] || '- '}%
- {cpu_usages[cameras[camera]['pid']]?.['mem'] || '- '}%
- {config.telemetry.network_bandwidth && - }
-
-
-
-
-
- )
- )}
-
- )}
-
-
-
- Other Processes
-
-
-
-
-
-
- {processesNames.map((process) => (
-
-
-
-
-
-
- P-ID
- CPU %
- Avg CPU %
- Memory %
- {config.telemetry.network_bandwidth && Network Bandwidth }
-
-
-
-
- {processes[process]['pid'] || '- '}
- {cpu_usages[processes[process]['pid']]?.['cpu'] || '- '}%
- {cpu_usages[processes[process]['pid']]?.['cpu_average'] || '- '}%
- {cpu_usages[processes[process]['pid']]?.['mem'] || '- '}%
- {config.telemetry.network_bandwidth && (
- {bandwidth_usages[processes[process]['pid']]?.['bandwidth'] || '- '}KB/s
- )}
-
-
-
-
-
- ))}
-
-
- System stats update automatically every {config.mqtt.stats_interval} seconds.
-
- )}
-
- );
-}
diff --git a/web-old/src/routes/__tests__/Camera.test.jsx b/web-old/src/routes/__tests__/Camera.test.jsx
deleted file mode 100644
index ef679681c..000000000
--- a/web-old/src/routes/__tests__/Camera.test.jsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import { h } from 'preact';
-import * as AutoUpdatingCameraImage from '../../components/AutoUpdatingCameraImage';
-import * as WS from '../../api/ws';
-import Camera from '../Camera';
-import { set as setData } from 'idb-keyval';
-import * as JSMpegPlayer from '../../components/JSMpegPlayer';
-import { fireEvent, render, screen, waitForElementToBeRemoved } from 'testing-library';
-
-describe('Camera Route', () => {
- beforeEach(() => {
- vi.spyOn(AutoUpdatingCameraImage, 'default').mockImplementation(({ searchParams }) => {
- return