forked from Github/frigate
Fix linter and fix lint issues (#10141)
This commit is contained in:
@@ -14,6 +14,9 @@ import useSWR from "swr";
|
||||
import { FrigateConfig } from "@/types/frigateConfig";
|
||||
import ActivityIndicator from "../ui/activity-indicator";
|
||||
import useKeyboardListener from "@/hooks/use-keyboard-listener";
|
||||
import { Recording } from "@/types/record";
|
||||
import { Preview } from "@/types/preview";
|
||||
import { DynamicPlayback } from "@/types/playback";
|
||||
|
||||
/**
|
||||
* Dynamically switches between video playback and scrubbing preview player.
|
||||
@@ -37,7 +40,7 @@ export default function DynamicVideoPlayer({
|
||||
const timezone = useMemo(
|
||||
() =>
|
||||
config?.ui?.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
[config]
|
||||
[config],
|
||||
);
|
||||
|
||||
// playback behavior
|
||||
@@ -51,7 +54,7 @@ export default function DynamicVideoPlayer({
|
||||
config.cameras[camera].detect.height <
|
||||
1.7
|
||||
);
|
||||
}, [config]);
|
||||
}, [camera, config]);
|
||||
|
||||
// controlling playback
|
||||
|
||||
@@ -60,7 +63,7 @@ export default function DynamicVideoPlayer({
|
||||
const [isScrubbing, setIsScrubbing] = useState(false);
|
||||
const [hasPreview, setHasPreview] = useState(false);
|
||||
const [focusedItem, setFocusedItem] = useState<Timeline | undefined>(
|
||||
undefined
|
||||
undefined,
|
||||
);
|
||||
const controller = useMemo(() => {
|
||||
if (!config) {
|
||||
@@ -72,9 +75,9 @@ export default function DynamicVideoPlayer({
|
||||
previewRef,
|
||||
(config.cameras[camera]?.detect?.annotation_offset || 0) / 1000,
|
||||
setIsScrubbing,
|
||||
setFocusedItem
|
||||
setFocusedItem,
|
||||
);
|
||||
}, [config]);
|
||||
}, [camera, config]);
|
||||
|
||||
// keyboard control
|
||||
|
||||
@@ -115,11 +118,11 @@ export default function DynamicVideoPlayer({
|
||||
break;
|
||||
}
|
||||
},
|
||||
[playerRef]
|
||||
[playerRef],
|
||||
);
|
||||
useKeyboardListener(
|
||||
["ArrowLeft", "ArrowRight", "m", " "],
|
||||
onKeyboardShortcut
|
||||
onKeyboardShortcut,
|
||||
);
|
||||
|
||||
// initial state
|
||||
@@ -131,16 +134,18 @@ export default function DynamicVideoPlayer({
|
||||
date.getMonth() + 1
|
||||
}/${date.getDate()}/${date.getHours()}/${camera}/${timezone.replaceAll(
|
||||
"/",
|
||||
","
|
||||
",",
|
||||
)}/master.m3u8`,
|
||||
type: "application/vnd.apple.mpegurl",
|
||||
};
|
||||
// we only want to calculate this once
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
const initialPreviewSource = useMemo(() => {
|
||||
const preview = cameraPreviews.find(
|
||||
(preview) =>
|
||||
Math.round(preview.start) >= timeRange.start &&
|
||||
Math.floor(preview.end) <= timeRange.end
|
||||
Math.floor(preview.end) <= timeRange.end,
|
||||
);
|
||||
|
||||
if (preview) {
|
||||
@@ -153,6 +158,9 @@ export default function DynamicVideoPlayer({
|
||||
setHasPreview(false);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// we only want to calculate this once
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// state of playback player
|
||||
@@ -165,7 +173,7 @@ export default function DynamicVideoPlayer({
|
||||
}, [timeRange]);
|
||||
const { data: recordings } = useSWR<Recording[]>(
|
||||
[`${camera}/recordings`, recordingParams],
|
||||
{ revalidateOnFocus: false }
|
||||
{ revalidateOnFocus: false },
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -178,13 +186,13 @@ export default function DynamicVideoPlayer({
|
||||
date.getMonth() + 1
|
||||
}/${date.getDate()}/${date.getHours()}/${camera}/${timezone.replaceAll(
|
||||
"/",
|
||||
","
|
||||
",",
|
||||
)}/master.m3u8`;
|
||||
|
||||
const preview = cameraPreviews.find(
|
||||
(preview) =>
|
||||
Math.round(preview.start) >= timeRange.start &&
|
||||
Math.floor(preview.end) <= timeRange.end
|
||||
Math.floor(preview.end) <= timeRange.end,
|
||||
);
|
||||
setHasPreview(preview != undefined);
|
||||
|
||||
@@ -193,6 +201,9 @@ export default function DynamicVideoPlayer({
|
||||
playbackUri,
|
||||
preview,
|
||||
});
|
||||
|
||||
// we only want this to change when recordings update
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [controller, recordings]);
|
||||
|
||||
if (!controller) {
|
||||
@@ -300,7 +311,7 @@ export class DynamicVideoController {
|
||||
previewRef: MutableRefObject<Player | undefined>,
|
||||
annotationOffset: number,
|
||||
setScrubbing: (isScrubbing: boolean) => void,
|
||||
setFocusedItem: (timeline: Timeline) => void
|
||||
setFocusedItem: (timeline: Timeline) => void,
|
||||
) {
|
||||
this.playerRef = playerRef;
|
||||
this.previewRef = previewRef;
|
||||
@@ -437,7 +448,7 @@ export class DynamicVideoController {
|
||||
this.timeToSeek = time;
|
||||
} else {
|
||||
this.previewRef.current?.currentTime(
|
||||
Math.max(0, time - this.preview.start)
|
||||
Math.max(0, time - this.preview.start),
|
||||
);
|
||||
this.seeking = true;
|
||||
}
|
||||
@@ -453,7 +464,7 @@ export class DynamicVideoController {
|
||||
this.timeToSeek != this.previewRef.current?.currentTime()
|
||||
) {
|
||||
this.previewRef.current?.currentTime(
|
||||
this.timeToSeek - this.preview.start
|
||||
this.timeToSeek - this.preview.start,
|
||||
);
|
||||
} else {
|
||||
this.seeking = false;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { baseUrl } from "@/api/baseUrl";
|
||||
import { useResizeObserver } from "@/hooks/resize-observer";
|
||||
// @ts-ignore we know this doesn't have types
|
||||
// @ts-expect-error we know this doesn't have types
|
||||
import JSMpeg from "@cycjimmy/jsmpeg-player";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
|
||||
@@ -47,10 +47,10 @@ export default function JSMpegPlayer({
|
||||
}
|
||||
|
||||
return 100;
|
||||
}, [availableWidth, aspectRatio, height]);
|
||||
}, [availableWidth, aspectRatio, containerHeight, height]);
|
||||
const scaledWidth = useMemo(
|
||||
() => Math.ceil(scaledHeight * aspectRatio - scrollBarWidth),
|
||||
[scaledHeight, aspectRatio, scrollBarWidth]
|
||||
[scaledHeight, aspectRatio, scrollBarWidth],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -62,7 +62,7 @@ export default function JSMpegPlayer({
|
||||
playerRef.current,
|
||||
url,
|
||||
{},
|
||||
{ protocols: [], audio: false, videoBufferSize: 1024 * 1024 * 4 }
|
||||
{ protocols: [], audio: false, videoBufferSize: 1024 * 1024 * 4 },
|
||||
);
|
||||
|
||||
const fullscreen = () => {
|
||||
@@ -79,6 +79,7 @@ export default function JSMpegPlayer({
|
||||
if (playerRef.current) {
|
||||
try {
|
||||
video.destroy();
|
||||
// eslint-disable-next-line no-empty
|
||||
} catch (e) {}
|
||||
playerRef.current = null;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export default function LivePlayer({
|
||||
|
||||
const cameraActive = useMemo(
|
||||
() => windowVisible && (activeMotion || activeTracking),
|
||||
[activeMotion, activeTracking, windowVisible]
|
||||
[activeMotion, activeTracking, windowVisible],
|
||||
);
|
||||
|
||||
// camera live state
|
||||
@@ -56,6 +56,8 @@ export default function LivePlayer({
|
||||
if (!cameraActive) {
|
||||
setLiveReady(false);
|
||||
}
|
||||
// live mode won't change
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [cameraActive, liveReady]);
|
||||
|
||||
const { payload: recording } = useRecordingsState(cameraConfig.name);
|
||||
@@ -76,7 +78,7 @@ export default function LivePlayer({
|
||||
}
|
||||
|
||||
return 30000;
|
||||
}, []);
|
||||
}, [liveReady, cameraActive, windowVisible]);
|
||||
|
||||
if (!cameraConfig) {
|
||||
return <ActivityIndicator />;
|
||||
|
||||
@@ -37,8 +37,10 @@ function MSEPlayer({
|
||||
const videoRef = useRef<HTMLVideoElement>(null);
|
||||
const wsRef = useRef<WebSocket | null>(null);
|
||||
const reconnectTIDRef = useRef<number | null>(null);
|
||||
const ondataRef = useRef<((data: any) => void) | null>(null);
|
||||
const onmessageRef = useRef<{ [key: string]: (msg: any) => void }>({});
|
||||
const ondataRef = useRef<((data: ArrayBufferLike) => void) | null>(null);
|
||||
const onmessageRef = useRef<{
|
||||
[key: string]: (msg: { value: string; type: string }) => void;
|
||||
}>({});
|
||||
const msRef = useRef<MediaSource | null>(null);
|
||||
|
||||
const wsURL = useMemo(() => {
|
||||
@@ -49,7 +51,7 @@ function MSEPlayer({
|
||||
const currentVideo = videoRef.current;
|
||||
|
||||
if (currentVideo) {
|
||||
currentVideo.play().catch((er: any) => {
|
||||
currentVideo.play().catch((er: { name: string }) => {
|
||||
if (er.name === "NotAllowedError" && !currentVideo.muted) {
|
||||
currentVideo.muted = true;
|
||||
currentVideo.play().catch(() => {});
|
||||
@@ -59,16 +61,19 @@ function MSEPlayer({
|
||||
};
|
||||
|
||||
const send = useCallback(
|
||||
(value: any) => {
|
||||
(value: object) => {
|
||||
if (wsRef.current) wsRef.current.send(JSON.stringify(value));
|
||||
},
|
||||
[wsRef]
|
||||
[wsRef],
|
||||
);
|
||||
|
||||
const codecs = useCallback((isSupported: (type: string) => boolean) => {
|
||||
return CODECS.filter((codec) =>
|
||||
isSupported(`video/mp4; codecs="${codec}"`)
|
||||
isSupported(`video/mp4; codecs="${codec}"`),
|
||||
).join();
|
||||
|
||||
// we know that these deps are correct
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const onConnect = useCallback(() => {
|
||||
@@ -76,6 +81,8 @@ function MSEPlayer({
|
||||
|
||||
setWsState(WebSocket.CONNECTING);
|
||||
|
||||
// TODO may need to check this later
|
||||
// eslint-disable-next-line
|
||||
connectTS = Date.now();
|
||||
|
||||
wsRef.current = new WebSocket(wsURL);
|
||||
@@ -110,6 +117,8 @@ function MSEPlayer({
|
||||
onmessageRef.current = {};
|
||||
|
||||
onMse();
|
||||
// only run once
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const onClose = useCallback(() => {
|
||||
@@ -135,11 +144,11 @@ function MSEPlayer({
|
||||
() => {
|
||||
send({
|
||||
type: "mse",
|
||||
// @ts-ignore
|
||||
// @ts-expect-error for typing
|
||||
value: codecs(MediaSource.isTypeSupported),
|
||||
});
|
||||
},
|
||||
{ once: true }
|
||||
{ once: true },
|
||||
);
|
||||
|
||||
if (videoRef.current) {
|
||||
@@ -156,7 +165,7 @@ function MSEPlayer({
|
||||
value: codecs(MediaSource.isTypeSupported),
|
||||
});
|
||||
},
|
||||
{ once: true }
|
||||
{ once: true },
|
||||
);
|
||||
videoRef.current!.src = URL.createObjectURL(msRef.current!);
|
||||
videoRef.current!.srcObject = null;
|
||||
@@ -184,6 +193,7 @@ function MSEPlayer({
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug(e);
|
||||
}
|
||||
});
|
||||
@@ -201,6 +211,7 @@ function MSEPlayer({
|
||||
try {
|
||||
sb?.appendBuffer(data);
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.debug(e);
|
||||
}
|
||||
}
|
||||
@@ -217,7 +228,7 @@ function MSEPlayer({
|
||||
const MediaSourceConstructor =
|
||||
"ManagedMediaSource" in window ? window.ManagedMediaSource : MediaSource;
|
||||
|
||||
// @ts-ignore
|
||||
// @ts-expect-error for typing
|
||||
msRef.current = new MediaSourceConstructor();
|
||||
|
||||
if ("hidden" in document && visibilityCheck) {
|
||||
@@ -241,7 +252,7 @@ function MSEPlayer({
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: visibilityThreshold }
|
||||
{ threshold: visibilityThreshold },
|
||||
);
|
||||
observer.observe(videoRef.current!);
|
||||
}
|
||||
@@ -251,6 +262,8 @@ function MSEPlayer({
|
||||
return () => {
|
||||
onDisconnect();
|
||||
};
|
||||
// we know that these deps are correct
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [playbackEnabled, onDisconnect, onConnect]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -78,13 +78,13 @@ export default function PreviewThumbnailPlayer({
|
||||
const playingBack = useMemo(() => playback, [playback]);
|
||||
|
||||
const onPlayback = useCallback(
|
||||
(isHovered: Boolean) => {
|
||||
(isHovered: boolean) => {
|
||||
if (isHovered) {
|
||||
setHoverTimeout(
|
||||
setTimeout(() => {
|
||||
setPlayback(true);
|
||||
setHoverTimeout(null);
|
||||
}, 500)
|
||||
}, 500),
|
||||
);
|
||||
} else {
|
||||
if (hoverTimeout) {
|
||||
@@ -95,14 +95,17 @@ export default function PreviewThumbnailPlayer({
|
||||
setProgress(0);
|
||||
}
|
||||
},
|
||||
[hoverTimeout, review]
|
||||
|
||||
// we know that these deps are correct
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[hoverTimeout, review],
|
||||
);
|
||||
|
||||
// date
|
||||
|
||||
const formattedDate = useFormattedTimestamp(
|
||||
review.start_time,
|
||||
config?.ui.time_format == "24hour" ? "%b %-d, %H:%M" : "%b %-d, %I:%M %p"
|
||||
config?.ui.time_format == "24hour" ? "%b %-d, %H:%M" : "%b %-d, %I:%M %p",
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -134,7 +137,7 @@ export default function PreviewThumbnailPlayer({
|
||||
}`}
|
||||
src={`${apiHost}${review.thumb_path.replace(
|
||||
"/media/frigate/",
|
||||
""
|
||||
"",
|
||||
)}`}
|
||||
loading={isSafari ? "eager" : "lazy"}
|
||||
onLoad={() => {
|
||||
@@ -215,8 +218,11 @@ function PreviewContent({
|
||||
// start with a bit of padding
|
||||
return Math.max(
|
||||
0,
|
||||
review.start_time - relevantPreview.start - PREVIEW_PADDING
|
||||
review.start_time - relevantPreview.start - PREVIEW_PADDING,
|
||||
);
|
||||
|
||||
// we know that these deps are correct
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
const [lastPercent, setLastPercent] = useState(0.0);
|
||||
|
||||
@@ -234,6 +240,9 @@ function PreviewContent({
|
||||
playerRef.current.currentTime = playerStartTime;
|
||||
playerRef.current.playbackRate = 8;
|
||||
}
|
||||
|
||||
// we know that these deps are correct
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [playerRef]);
|
||||
|
||||
// time progress update
|
||||
@@ -269,6 +278,9 @@ function PreviewContent({
|
||||
} else {
|
||||
setProgress(playerPercent);
|
||||
}
|
||||
|
||||
// we know that these deps are correct
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [setProgress, lastPercent]);
|
||||
|
||||
// manual playback
|
||||
@@ -289,6 +301,9 @@ function PreviewContent({
|
||||
}
|
||||
}, 125);
|
||||
return () => clearInterval(intervalId);
|
||||
|
||||
// we know that these deps are correct
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [manualPlayback, playerRef]);
|
||||
|
||||
// preview
|
||||
@@ -333,7 +348,7 @@ function InProgressPreview({
|
||||
const { data: previewFrames } = useSWR<string[]>(
|
||||
`preview/${review.camera}/start/${Math.floor(review.start_time) - 4}/end/${
|
||||
Math.ceil(review.end_time) + 4
|
||||
}/frames`
|
||||
}/frames`,
|
||||
);
|
||||
const [key, setKey] = useState(0);
|
||||
|
||||
@@ -361,6 +376,9 @@ function InProgressPreview({
|
||||
|
||||
setKey(key + 1);
|
||||
}, MIN_LOAD_TIMEOUT_MS);
|
||||
|
||||
// we know that these deps are correct
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [key, previewFrames]);
|
||||
|
||||
if (!previewFrames || previewFrames.length == 0) {
|
||||
@@ -394,7 +412,7 @@ function PreviewContextItems({
|
||||
const exportReview = useCallback(() => {
|
||||
axios.post(
|
||||
`export/${review.camera}/start/${review.start_time}/end/${review.end_time}`,
|
||||
{ playback: "realtime" }
|
||||
{ playback: "realtime" },
|
||||
);
|
||||
}, [review]);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import Player from "video.js/dist/types/player";
|
||||
type VideoPlayerProps = {
|
||||
children?: ReactElement | ReactElement[];
|
||||
options?: {
|
||||
[key: string]: any;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
seekOptions?: {
|
||||
forward?: number;
|
||||
@@ -23,7 +23,7 @@ export default function VideoPlayer({
|
||||
options,
|
||||
seekOptions = { forward: 30, backward: 10 },
|
||||
remotePlayback = false,
|
||||
onReady = (_) => {},
|
||||
onReady = () => {},
|
||||
onDispose = () => {},
|
||||
}: VideoPlayerProps) {
|
||||
const videoRef = useRef<HTMLDivElement | null>(null);
|
||||
@@ -47,7 +47,7 @@ export default function VideoPlayer({
|
||||
if (!playerRef.current) {
|
||||
// The Video.js player needs to be _inside_ the component el for React 18 Strict Mode.
|
||||
const videoElement = document.createElement(
|
||||
"video-js"
|
||||
"video-js",
|
||||
) as HTMLVideoElement;
|
||||
videoElement.controls = true;
|
||||
videoElement.playsInline = true;
|
||||
@@ -62,9 +62,12 @@ export default function VideoPlayer({
|
||||
{ ...defaultOptions, ...options },
|
||||
() => {
|
||||
onReady && onReady(player);
|
||||
}
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
// we know that these deps are correct
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [options, videoRef]);
|
||||
|
||||
// Dispose the Video.js player when the functional component unmounts
|
||||
@@ -78,6 +81,9 @@ export default function VideoPlayer({
|
||||
onDispose();
|
||||
}
|
||||
};
|
||||
|
||||
// we know that these deps are correct
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [playerRef]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -58,7 +58,7 @@ export default function WebRtcPlayer({
|
||||
.filter((kind) => media.indexOf(kind) >= 0)
|
||||
.map(
|
||||
(kind) =>
|
||||
pc.addTransceiver(kind, { direction: "recvonly" }).receiver.track
|
||||
pc.addTransceiver(kind, { direction: "recvonly" }).receiver.track,
|
||||
);
|
||||
localTracks.push(...tracks);
|
||||
}
|
||||
@@ -66,12 +66,12 @@ export default function WebRtcPlayer({
|
||||
videoRef.current.srcObject = new MediaStream(localTracks);
|
||||
return pc;
|
||||
},
|
||||
[videoRef]
|
||||
[videoRef],
|
||||
);
|
||||
|
||||
async function getMediaTracks(
|
||||
media: string,
|
||||
constraints: MediaStreamConstraints
|
||||
constraints: MediaStreamConstraints,
|
||||
) {
|
||||
try {
|
||||
const stream =
|
||||
@@ -126,7 +126,7 @@ export default function WebRtcPlayer({
|
||||
}
|
||||
});
|
||||
},
|
||||
[]
|
||||
[],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -140,7 +140,7 @@ export default function WebRtcPlayer({
|
||||
|
||||
const url = `${baseUrl.replace(
|
||||
/^http/,
|
||||
"ws"
|
||||
"ws",
|
||||
)}live/webrtc/api/ws?src=${camera}`;
|
||||
const ws = new WebSocket(url);
|
||||
const aPc = PeerConnection("video+audio");
|
||||
|
||||
Reference in New Issue
Block a user