mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-13 06:35:24 +03:00
Fix reloading recordings failing
This commit is contained in:
parent
63984c1bc9
commit
daa6d7672c
@ -168,7 +168,10 @@ export default function DynamicVideoPlayer({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!controller || !recordings) {
|
if (!controller || !recordings) {
|
||||||
setNoRecording(true);
|
if (recordings?.length == 0) {
|
||||||
|
setNoRecording(true);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -101,7 +101,7 @@ export default function Events() {
|
|||||||
|
|
||||||
// review paging
|
// review paging
|
||||||
|
|
||||||
const [beforeTs, setBeforeTs] = useState(Date.now() / 1000);
|
const [beforeTs, setBeforeTs] = useState(Math.ceil(Date.now() / 1000));
|
||||||
const last24Hours = useMemo(() => {
|
const last24Hours = useMemo(() => {
|
||||||
return { before: beforeTs, after: getHoursAgo(24) };
|
return { before: beforeTs, after: getHoursAgo(24) };
|
||||||
}, [beforeTs]);
|
}, [beforeTs]);
|
||||||
@ -455,5 +455,5 @@ export default function Events() {
|
|||||||
function getHoursAgo(hours: number): number {
|
function getHoursAgo(hours: number): number {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
now.setHours(now.getHours() - hours);
|
now.setHours(now.getHours() - hours);
|
||||||
return now.getTime() / 1000;
|
return Math.ceil(now.getTime() / 1000);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,6 +46,7 @@ import { ASPECT_VERTICAL_LAYOUT, ASPECT_WIDE_LAYOUT } from "@/types/record";
|
|||||||
import { useResizeObserver } from "@/hooks/resize-observer";
|
import { useResizeObserver } from "@/hooks/resize-observer";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useFullscreen } from "@/hooks/use-fullscreen";
|
import { useFullscreen } from "@/hooks/use-fullscreen";
|
||||||
|
import ActivityIndicator from "@/components/indicators/activity-indicator";
|
||||||
|
|
||||||
const SEGMENT_DURATION = 30;
|
const SEGMENT_DURATION = 30;
|
||||||
|
|
||||||
@ -107,7 +108,7 @@ export function RecordingView({
|
|||||||
return chunk.after <= startTime && chunk.before >= startTime;
|
return chunk.after <= startTime && chunk.before >= startTime;
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
const currentTimeRange = useMemo(
|
const currentTimeRange = useMemo<TimeRange | undefined>(
|
||||||
() => chunkedTimeRange[selectedRangeIdx],
|
() => chunkedTimeRange[selectedRangeIdx],
|
||||||
[selectedRangeIdx, chunkedTimeRange],
|
[selectedRangeIdx, chunkedTimeRange],
|
||||||
);
|
);
|
||||||
@ -171,6 +172,10 @@ export function RecordingView({
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!currentTimeRange) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (scrubbing || exportRange) {
|
if (scrubbing || exportRange) {
|
||||||
if (
|
if (
|
||||||
currentTime > currentTimeRange.before + 60 ||
|
currentTime > currentTimeRange.before + 60 ||
|
||||||
@ -198,6 +203,10 @@ export function RecordingView({
|
|||||||
|
|
||||||
const manuallySetCurrentTime = useCallback(
|
const manuallySetCurrentTime = useCallback(
|
||||||
(time: number) => {
|
(time: number) => {
|
||||||
|
if (!currentTimeRange) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setCurrentTime(time);
|
setCurrentTime(time);
|
||||||
|
|
||||||
if (currentTimeRange.after <= time && currentTimeRange.before >= time) {
|
if (currentTimeRange.after <= time && currentTimeRange.before >= time) {
|
||||||
@ -210,6 +219,10 @@ export function RecordingView({
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!currentTimeRange) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!scrubbing) {
|
if (!scrubbing) {
|
||||||
if (Math.abs(currentTime - playerTime) > 10) {
|
if (Math.abs(currentTime - playerTime) > 10) {
|
||||||
if (
|
if (
|
||||||
@ -471,143 +484,152 @@ export function RecordingView({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
{currentTimeRange ? (
|
||||||
ref={mainLayoutRef}
|
|
||||||
className={cn(
|
|
||||||
"flex h-full justify-center overflow-hidden",
|
|
||||||
isDesktop ? "" : "flex-col gap-2 landscape:flex-row",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
ref={cameraLayoutRef}
|
ref={mainLayoutRef}
|
||||||
className={cn("flex flex-1 flex-wrap", isDesktop ? "w-[80%]" : "")}
|
className={cn(
|
||||||
|
"flex h-full justify-center overflow-hidden",
|
||||||
|
isDesktop ? "" : "flex-col gap-2 landscape:flex-row",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={cn(
|
ref={cameraLayoutRef}
|
||||||
"flex size-full items-center",
|
className={cn("flex flex-1 flex-wrap", isDesktop ? "w-[80%]" : "")}
|
||||||
mainCameraAspect == "tall"
|
|
||||||
? "flex-row justify-evenly"
|
|
||||||
: "flex-col justify-center gap-2",
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
key={mainCamera}
|
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative",
|
"flex size-full items-center",
|
||||||
isDesktop
|
mainCameraAspect == "tall"
|
||||||
? cn(
|
? "flex-row justify-evenly"
|
||||||
"flex justify-center px-4",
|
: "flex-col justify-center gap-2",
|
||||||
mainCameraAspect == "tall"
|
|
||||||
? "h-[50%] md:h-[60%] lg:h-[75%] xl:h-[90%]"
|
|
||||||
: mainCameraAspect == "wide"
|
|
||||||
? "w-full"
|
|
||||||
: "",
|
|
||||||
)
|
|
||||||
: cn(
|
|
||||||
"pt-2 portrait:w-full",
|
|
||||||
mainCameraAspect == "wide"
|
|
||||||
? "aspect-wide landscape:w-full"
|
|
||||||
: "aspect-video landscape:h-[94%] landscape:xl:h-[65%]",
|
|
||||||
),
|
|
||||||
)}
|
)}
|
||||||
style={{
|
|
||||||
width: mainCameraStyle ? mainCameraStyle.width : undefined,
|
|
||||||
aspectRatio: isDesktop
|
|
||||||
? mainCameraAspect == "tall"
|
|
||||||
? getCameraAspect(mainCamera)
|
|
||||||
: undefined
|
|
||||||
: Math.max(1, getCameraAspect(mainCamera) ?? 0),
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<DynamicVideoPlayer
|
|
||||||
className={grow}
|
|
||||||
camera={mainCamera}
|
|
||||||
timeRange={currentTimeRange}
|
|
||||||
cameraPreviews={allPreviews ?? []}
|
|
||||||
startTimestamp={playbackStart}
|
|
||||||
hotKeys={exportMode != "select"}
|
|
||||||
fullscreen={fullscreen}
|
|
||||||
onTimestampUpdate={(timestamp) => {
|
|
||||||
setPlayerTime(timestamp);
|
|
||||||
setCurrentTime(timestamp);
|
|
||||||
Object.values(previewRefs.current ?? {}).forEach((prev) =>
|
|
||||||
prev.scrubToTimestamp(Math.floor(timestamp)),
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
onClipEnded={onClipEnded}
|
|
||||||
onControllerReady={(controller) => {
|
|
||||||
mainControllerRef.current = controller;
|
|
||||||
}}
|
|
||||||
isScrubbing={scrubbing || exportMode == "timeline"}
|
|
||||||
setFullResolution={setFullResolution}
|
|
||||||
toggleFullscreen={toggleFullscreen}
|
|
||||||
containerRef={mainLayoutRef}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{isDesktop && (
|
|
||||||
<div
|
<div
|
||||||
ref={previewRowRef}
|
key={mainCamera}
|
||||||
className={cn(
|
className={cn(
|
||||||
"scrollbar-container flex gap-2 overflow-auto",
|
"relative",
|
||||||
mainCameraAspect == "tall"
|
isDesktop
|
||||||
? "h-full w-72 flex-col"
|
? cn(
|
||||||
: `h-28 w-full`,
|
"flex justify-center px-4",
|
||||||
previewRowOverflows ? "" : "items-center justify-center",
|
mainCameraAspect == "tall"
|
||||||
|
? "h-[50%] md:h-[60%] lg:h-[75%] xl:h-[90%]"
|
||||||
|
: mainCameraAspect == "wide"
|
||||||
|
? "w-full"
|
||||||
|
: "",
|
||||||
|
)
|
||||||
|
: cn(
|
||||||
|
"pt-2 portrait:w-full",
|
||||||
|
mainCameraAspect == "wide"
|
||||||
|
? "aspect-wide landscape:w-full"
|
||||||
|
: "aspect-video landscape:h-[94%] landscape:xl:h-[65%]",
|
||||||
|
),
|
||||||
)}
|
)}
|
||||||
|
style={{
|
||||||
|
width: mainCameraStyle ? mainCameraStyle.width : undefined,
|
||||||
|
aspectRatio: isDesktop
|
||||||
|
? mainCameraAspect == "tall"
|
||||||
|
? getCameraAspect(mainCamera)
|
||||||
|
: undefined
|
||||||
|
: Math.max(1, getCameraAspect(mainCamera) ?? 0),
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className="w-2" />
|
<DynamicVideoPlayer
|
||||||
{allCameras.map((cam) => {
|
className={grow}
|
||||||
if (cam == mainCamera || cam == "birdseye") {
|
camera={mainCamera}
|
||||||
return;
|
timeRange={currentTimeRange}
|
||||||
}
|
cameraPreviews={allPreviews ?? []}
|
||||||
|
startTimestamp={playbackStart}
|
||||||
return (
|
hotKeys={exportMode != "select"}
|
||||||
<div
|
fullscreen={fullscreen}
|
||||||
key={cam}
|
onTimestampUpdate={(timestamp) => {
|
||||||
className={
|
setPlayerTime(timestamp);
|
||||||
mainCameraAspect == "tall" ? "w-full" : "h-full"
|
setCurrentTime(timestamp);
|
||||||
}
|
Object.values(previewRefs.current ?? {}).forEach((prev) =>
|
||||||
style={{
|
prev.scrubToTimestamp(Math.floor(timestamp)),
|
||||||
aspectRatio: getCameraAspect(cam),
|
);
|
||||||
}}
|
}}
|
||||||
>
|
onClipEnded={onClipEnded}
|
||||||
<PreviewPlayer
|
onControllerReady={(controller) => {
|
||||||
className="size-full"
|
mainControllerRef.current = controller;
|
||||||
camera={cam}
|
}}
|
||||||
timeRange={currentTimeRange}
|
isScrubbing={scrubbing || exportMode == "timeline"}
|
||||||
cameraPreviews={allPreviews ?? []}
|
setFullResolution={setFullResolution}
|
||||||
startTime={startTime}
|
toggleFullscreen={toggleFullscreen}
|
||||||
isScrubbing={scrubbing}
|
containerRef={mainLayoutRef}
|
||||||
onControllerReady={(controller) => {
|
/>
|
||||||
previewRefs.current[cam] = controller;
|
|
||||||
controller.scrubToTimestamp(startTime);
|
|
||||||
}}
|
|
||||||
onClick={() => onSelectCamera(cam)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
<div className="w-2" />
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
{isDesktop && (
|
||||||
|
<div
|
||||||
|
ref={previewRowRef}
|
||||||
|
className={cn(
|
||||||
|
"scrollbar-container flex gap-2 overflow-auto",
|
||||||
|
mainCameraAspect == "tall"
|
||||||
|
? "h-full w-72 flex-col"
|
||||||
|
: `h-28 w-full`,
|
||||||
|
previewRowOverflows ? "" : "items-center justify-center",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="w-2" />
|
||||||
|
{allCameras.map((cam) => {
|
||||||
|
if (cam == mainCamera || cam == "birdseye") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={cam}
|
||||||
|
className={
|
||||||
|
mainCameraAspect == "tall" ? "w-full" : "h-full"
|
||||||
|
}
|
||||||
|
style={{
|
||||||
|
aspectRatio: getCameraAspect(cam),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<PreviewPlayer
|
||||||
|
className="size-full"
|
||||||
|
camera={cam}
|
||||||
|
timeRange={currentTimeRange}
|
||||||
|
cameraPreviews={allPreviews ?? []}
|
||||||
|
startTime={startTime}
|
||||||
|
isScrubbing={scrubbing}
|
||||||
|
onControllerReady={(controller) => {
|
||||||
|
previewRefs.current[cam] = controller;
|
||||||
|
controller.scrubToTimestamp(startTime);
|
||||||
|
}}
|
||||||
|
onClick={() => onSelectCamera(cam)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<div className="w-2" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Timeline
|
||||||
|
contentRef={contentRef}
|
||||||
|
mainCamera={mainCamera}
|
||||||
|
timelineType={
|
||||||
|
(exportRange == undefined ? timelineType : "timeline") ??
|
||||||
|
"timeline"
|
||||||
|
}
|
||||||
|
timeRange={timeRange}
|
||||||
|
mainCameraReviewItems={mainCameraReviewItems}
|
||||||
|
currentTime={currentTime}
|
||||||
|
exportRange={exportMode == "timeline" ? exportRange : undefined}
|
||||||
|
setCurrentTime={setCurrentTime}
|
||||||
|
manuallySetCurrentTime={manuallySetCurrentTime}
|
||||||
|
setScrubbing={setScrubbing}
|
||||||
|
setExportRange={setExportRange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="relative size-full">
|
||||||
|
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2">
|
||||||
|
No recordings or previews found for this time
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Timeline
|
)}
|
||||||
contentRef={contentRef}
|
|
||||||
mainCamera={mainCamera}
|
|
||||||
timelineType={
|
|
||||||
(exportRange == undefined ? timelineType : "timeline") ?? "timeline"
|
|
||||||
}
|
|
||||||
timeRange={timeRange}
|
|
||||||
mainCameraReviewItems={mainCameraReviewItems}
|
|
||||||
currentTime={currentTime}
|
|
||||||
exportRange={exportMode == "timeline" ? exportRange : undefined}
|
|
||||||
setCurrentTime={setCurrentTime}
|
|
||||||
manuallySetCurrentTime={manuallySetCurrentTime}
|
|
||||||
setScrubbing={setScrubbing}
|
|
||||||
setExportRange={setExportRange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user