mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-22 08:57:20 -06:00
Tweaks and fixes (#11541)
* Update config version to be stored inside of the config * Don't remove items from list when navigating back * Use video api instead of webps for live current hour filmstrip * Check that the config file is writable * Show camera name when camera is offline * Show camera name when offline * Cleanup
This commit is contained in:
parent
63d81bef45
commit
c2eac10925
@ -1355,6 +1355,7 @@ class FrigateConfig(FrigateBaseModel):
|
|||||||
default_factory=TimestampStyleConfig,
|
default_factory=TimestampStyleConfig,
|
||||||
title="Global timestamp style configuration.",
|
title="Global timestamp style configuration.",
|
||||||
)
|
)
|
||||||
|
version: Optional[float] = Field(default=None, title="Current config version.")
|
||||||
|
|
||||||
def runtime_config(self, plus_api: PlusApi = None) -> FrigateConfig:
|
def runtime_config(self, plus_api: PlusApi = None) -> FrigateConfig:
|
||||||
"""Merge camera config with globals."""
|
"""Merge camera config with globals."""
|
||||||
|
@ -17,16 +17,17 @@ CURRENT_CONFIG_VERSION = 0.14
|
|||||||
def migrate_frigate_config(config_file: str):
|
def migrate_frigate_config(config_file: str):
|
||||||
"""handle migrating the frigate config."""
|
"""handle migrating the frigate config."""
|
||||||
logger.info("Checking if frigate config needs migration...")
|
logger.info("Checking if frigate config needs migration...")
|
||||||
version_file = os.path.join(CONFIG_DIR, ".version")
|
|
||||||
|
|
||||||
if not os.path.isfile(version_file):
|
if not os.access(config_file, mode=os.W_OK):
|
||||||
previous_version = 0.13
|
logger.error("Config file is read-only, unable to migrate config file.")
|
||||||
else:
|
return
|
||||||
with open(version_file) as f:
|
|
||||||
try:
|
yaml = YAML()
|
||||||
previous_version = float(f.readline())
|
yaml.indent(mapping=2, sequence=4, offset=2)
|
||||||
except Exception:
|
with open(config_file, "r") as f:
|
||||||
previous_version = 0.13
|
config: dict[str, dict[str, any]] = yaml.load(f)
|
||||||
|
|
||||||
|
previous_version = config.get("version", 0.13)
|
||||||
|
|
||||||
if previous_version == CURRENT_CONFIG_VERSION:
|
if previous_version == CURRENT_CONFIG_VERSION:
|
||||||
logger.info("frigate config does not need migration...")
|
logger.info("frigate config does not need migration...")
|
||||||
@ -35,11 +36,6 @@ def migrate_frigate_config(config_file: str):
|
|||||||
logger.info("copying config as backup...")
|
logger.info("copying config as backup...")
|
||||||
shutil.copy(config_file, os.path.join(CONFIG_DIR, "backup_config.yaml"))
|
shutil.copy(config_file, os.path.join(CONFIG_DIR, "backup_config.yaml"))
|
||||||
|
|
||||||
yaml = YAML()
|
|
||||||
yaml.indent(mapping=2, sequence=4, offset=2)
|
|
||||||
with open(config_file, "r") as f:
|
|
||||||
config: dict[str, dict[str, any]] = yaml.load(f)
|
|
||||||
|
|
||||||
if previous_version < 0.14:
|
if previous_version < 0.14:
|
||||||
logger.info(f"Migrating frigate config from {previous_version} to 0.14...")
|
logger.info(f"Migrating frigate config from {previous_version} to 0.14...")
|
||||||
new_config = migrate_014(config)
|
new_config = migrate_014(config)
|
||||||
@ -57,9 +53,6 @@ def migrate_frigate_config(config_file: str):
|
|||||||
os.path.join(EXPORT_DIR, file), os.path.join(EXPORT_DIR, new_name)
|
os.path.join(EXPORT_DIR, file), os.path.join(EXPORT_DIR, new_name)
|
||||||
)
|
)
|
||||||
|
|
||||||
with open(version_file, "w") as f:
|
|
||||||
f.write(str(CURRENT_CONFIG_VERSION))
|
|
||||||
|
|
||||||
logger.info("Finished frigate config migration...")
|
logger.info("Finished frigate config migration...")
|
||||||
|
|
||||||
|
|
||||||
@ -141,6 +134,7 @@ def migrate_014(config: dict[str, dict[str, any]]) -> dict[str, dict[str, any]]:
|
|||||||
|
|
||||||
new_config["cameras"][name] = camera_config
|
new_config["cameras"][name] = camera_config
|
||||||
|
|
||||||
|
new_config["version"] = 0.14
|
||||||
return new_config
|
return new_config
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,33 +1,20 @@
|
|||||||
import { useFrigateStats } from "@/api/ws";
|
|
||||||
import {
|
import {
|
||||||
StatusBarMessagesContext,
|
StatusBarMessagesContext,
|
||||||
StatusMessage,
|
StatusMessage,
|
||||||
} from "@/context/statusbar-provider";
|
} from "@/context/statusbar-provider";
|
||||||
import useStats from "@/hooks/use-stats";
|
import useStats, { useAutoFrigateStats } from "@/hooks/use-stats";
|
||||||
import { FrigateStats } from "@/types/stats";
|
|
||||||
import { useContext, useEffect, useMemo } from "react";
|
import { useContext, useEffect, useMemo } from "react";
|
||||||
import { FaCheck } from "react-icons/fa";
|
import { FaCheck } from "react-icons/fa";
|
||||||
import { IoIosWarning } from "react-icons/io";
|
import { IoIosWarning } from "react-icons/io";
|
||||||
import { MdCircle } from "react-icons/md";
|
import { MdCircle } from "react-icons/md";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import useSWR from "swr";
|
|
||||||
|
|
||||||
export default function Statusbar() {
|
export default function Statusbar() {
|
||||||
const { data: initialStats } = useSWR<FrigateStats>("stats", {
|
|
||||||
revalidateOnFocus: false,
|
|
||||||
});
|
|
||||||
const { payload: latestStats } = useFrigateStats();
|
|
||||||
const { messages, addMessage, clearMessages } = useContext(
|
const { messages, addMessage, clearMessages } = useContext(
|
||||||
StatusBarMessagesContext,
|
StatusBarMessagesContext,
|
||||||
)!;
|
)!;
|
||||||
|
|
||||||
const stats = useMemo(() => {
|
const stats = useAutoFrigateStats();
|
||||||
if (latestStats) {
|
|
||||||
return latestStats;
|
|
||||||
}
|
|
||||||
|
|
||||||
return initialStats;
|
|
||||||
}, [initialStats, latestStats]);
|
|
||||||
|
|
||||||
const cpuPercent = useMemo(() => {
|
const cpuPercent = useMemo(() => {
|
||||||
const systemCpu = stats?.cpu_usages["frigate.full_system"]?.cpu;
|
const systemCpu = stats?.cpu_usages["frigate.full_system"]?.cpu;
|
||||||
|
@ -7,12 +7,10 @@ import { REVIEW_PADDING, ReviewSegment } from "@/types/review";
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { RecordingStartingPoint } from "@/types/record";
|
import { RecordingStartingPoint } from "@/types/record";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import {
|
import { VideoPreview } from "../player/PreviewThumbnailPlayer";
|
||||||
InProgressPreview,
|
|
||||||
VideoPreview,
|
|
||||||
} from "../player/PreviewThumbnailPlayer";
|
|
||||||
import { isCurrentHour } from "@/utils/dateUtil";
|
import { isCurrentHour } from "@/utils/dateUtil";
|
||||||
import { useCameraPreviews } from "@/hooks/use-camera-previews";
|
import { useCameraPreviews } from "@/hooks/use-camera-previews";
|
||||||
|
import { baseUrl } from "@/api/baseUrl";
|
||||||
|
|
||||||
type AnimatedEventCardProps = {
|
type AnimatedEventCardProps = {
|
||||||
event: ReviewSegment;
|
event: ReviewSegment;
|
||||||
@ -105,18 +103,11 @@ export function AnimatedEventCard({ event }: AnimatedEventCardProps) {
|
|||||||
windowVisible={windowVisible}
|
windowVisible={windowVisible}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<InProgressPreview
|
<video
|
||||||
review={event}
|
src={`${baseUrl}api/review/${event.id}/preview?format=ts`}
|
||||||
timeRange={{
|
muted
|
||||||
after: event.start_time,
|
autoPlay
|
||||||
before: event.end_time ?? event.start_time + 20,
|
|
||||||
}}
|
|
||||||
loop
|
loop
|
||||||
showProgress={false}
|
|
||||||
setReviewed={() => {}}
|
|
||||||
setIgnoreClick={() => {}}
|
|
||||||
isPlayingBack={() => {}}
|
|
||||||
windowVisible={windowVisible}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,7 +46,7 @@ export default function LivePlayer({
|
|||||||
}: LivePlayerProps) {
|
}: LivePlayerProps) {
|
||||||
// camera activity
|
// camera activity
|
||||||
|
|
||||||
const { activeMotion, activeTracking, objects } =
|
const { activeMotion, activeTracking, objects, offline } =
|
||||||
useCameraActivity(cameraConfig);
|
useCameraActivity(cameraConfig);
|
||||||
|
|
||||||
const cameraActive = useMemo(
|
const cameraActive = useMemo(
|
||||||
@ -224,9 +224,16 @@ export default function LivePlayer({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="absolute right-2 top-2 size-4">
|
<div className="absolute right-2 top-2">
|
||||||
{activeMotion && (
|
{!offline && activeMotion && (
|
||||||
<MdCircle className="size-2 animate-pulse text-danger shadow-danger drop-shadow-md" />
|
<MdCircle className="mr-2 size-2 animate-pulse text-danger shadow-danger drop-shadow-md" />
|
||||||
|
)}
|
||||||
|
{offline && (
|
||||||
|
<Chip
|
||||||
|
className={`z-0 flex items-start justify-between space-x-1 bg-gray-500 bg-gradient-to-br from-gray-400 to-gray-500 text-xs capitalize`}
|
||||||
|
>
|
||||||
|
{cameraConfig.name.replaceAll("_", " ")}
|
||||||
|
</Chip>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -10,11 +10,13 @@ import { useTimelineUtils } from "./use-timeline-utils";
|
|||||||
import { ObjectType } from "@/types/ws";
|
import { ObjectType } from "@/types/ws";
|
||||||
import useDeepMemo from "./use-deep-memo";
|
import useDeepMemo from "./use-deep-memo";
|
||||||
import { isEqual } from "lodash";
|
import { isEqual } from "lodash";
|
||||||
|
import { useAutoFrigateStats } from "./use-stats";
|
||||||
|
|
||||||
type useCameraActivityReturn = {
|
type useCameraActivityReturn = {
|
||||||
activeTracking: boolean;
|
activeTracking: boolean;
|
||||||
activeMotion: boolean;
|
activeMotion: boolean;
|
||||||
objects: ObjectType[];
|
objects: ObjectType[];
|
||||||
|
offline: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useCameraActivity(
|
export function useCameraActivity(
|
||||||
@ -116,12 +118,31 @@ export function useCameraActivity(
|
|||||||
handleSetObjects(newObjects);
|
handleSetObjects(newObjects);
|
||||||
}, [camera, updatedEvent, objects, handleSetObjects]);
|
}, [camera, updatedEvent, objects, handleSetObjects]);
|
||||||
|
|
||||||
|
// determine if camera is offline
|
||||||
|
|
||||||
|
const stats = useAutoFrigateStats();
|
||||||
|
|
||||||
|
const offline = useMemo(() => {
|
||||||
|
if (!stats) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cameras = stats["cameras"];
|
||||||
|
|
||||||
|
if (!cameras) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cameras[camera.name].camera_fps == 0;
|
||||||
|
}, [camera, stats]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
activeTracking: hasActiveObjects,
|
activeTracking: hasActiveObjects,
|
||||||
activeMotion: detectingMotion
|
activeMotion: detectingMotion
|
||||||
? detectingMotion === "ON"
|
? detectingMotion === "ON"
|
||||||
: initialCameraState?.motion === true,
|
: initialCameraState?.motion === true,
|
||||||
objects,
|
objects,
|
||||||
|
offline,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ import { useMemo } from "react";
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import useDeepMemo from "./use-deep-memo";
|
import useDeepMemo from "./use-deep-memo";
|
||||||
import { capitalizeFirstLetter } from "@/utils/stringUtil";
|
import { capitalizeFirstLetter } from "@/utils/stringUtil";
|
||||||
|
import { useFrigateStats } from "@/api/ws";
|
||||||
|
|
||||||
export default function useStats(stats: FrigateStats | undefined) {
|
export default function useStats(stats: FrigateStats | undefined) {
|
||||||
const { data: config } = useSWR<FrigateConfig>("config");
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
@ -91,3 +92,20 @@ export default function useStats(stats: FrigateStats | undefined) {
|
|||||||
|
|
||||||
return { potentialProblems };
|
return { potentialProblems };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useAutoFrigateStats() {
|
||||||
|
const { data: initialStats } = useSWR<FrigateStats>("stats", {
|
||||||
|
revalidateOnFocus: false,
|
||||||
|
});
|
||||||
|
const { payload: latestStats } = useFrigateStats();
|
||||||
|
|
||||||
|
const stats = useMemo(() => {
|
||||||
|
if (latestStats) {
|
||||||
|
return latestStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
return initialStats;
|
||||||
|
}, [initialStats, latestStats]);
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
@ -10,6 +10,7 @@ import {
|
|||||||
ReviewSegment,
|
ReviewSegment,
|
||||||
ReviewSeverity,
|
ReviewSeverity,
|
||||||
ReviewSummary,
|
ReviewSummary,
|
||||||
|
SegmentedReviewData,
|
||||||
} from "@/types/review";
|
} from "@/types/review";
|
||||||
import { getTimestampOffset } from "@/utils/dateUtil";
|
import { getTimestampOffset } from "@/utils/dateUtil";
|
||||||
import EventView from "@/views/events/EventView";
|
import EventView from "@/views/events/EventView";
|
||||||
@ -138,6 +139,66 @@ export default function Events() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const reviewItems = useMemo<SegmentedReviewData>(() => {
|
||||||
|
if (!reviews) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const all: ReviewSegment[] = [];
|
||||||
|
const alerts: ReviewSegment[] = [];
|
||||||
|
const detections: ReviewSegment[] = [];
|
||||||
|
const motion: ReviewSegment[] = [];
|
||||||
|
|
||||||
|
reviews?.forEach((segment) => {
|
||||||
|
all.push(segment);
|
||||||
|
|
||||||
|
switch (segment.severity) {
|
||||||
|
case "alert":
|
||||||
|
alerts.push(segment);
|
||||||
|
break;
|
||||||
|
case "detection":
|
||||||
|
detections.push(segment);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
motion.push(segment);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
all: all,
|
||||||
|
alert: alerts,
|
||||||
|
detection: detections,
|
||||||
|
significant_motion: motion,
|
||||||
|
};
|
||||||
|
}, [reviews]);
|
||||||
|
|
||||||
|
const currentItems = useMemo(() => {
|
||||||
|
if (!reviewItems || !severity) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let current;
|
||||||
|
|
||||||
|
if (reviewFilter?.showAll) {
|
||||||
|
current = reviewItems.all;
|
||||||
|
} else {
|
||||||
|
current = reviewItems[severity];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current || current.length == 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reviewFilter?.showReviewed != 1) {
|
||||||
|
return current.filter((seg) => !seg.has_been_reviewed);
|
||||||
|
} else {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
// only refresh when severity or filter changes
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [severity, reviewFilter, reviewItems?.all.length]);
|
||||||
|
|
||||||
// review summary
|
// review summary
|
||||||
|
|
||||||
const { data: reviewSummary, mutate: updateSummary } = useSWR<ReviewSummary>(
|
const { data: reviewSummary, mutate: updateSummary } = useSWR<ReviewSummary>(
|
||||||
@ -353,7 +414,8 @@ export default function Events() {
|
|||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<EventView
|
<EventView
|
||||||
reviews={reviews}
|
reviewItems={reviewItems}
|
||||||
|
currentReviewItems={currentItems}
|
||||||
reviewSummary={reviewSummary}
|
reviewSummary={reviewSummary}
|
||||||
relevantPreviews={allPreviews}
|
relevantPreviews={allPreviews}
|
||||||
timeRange={selectedTimeRange}
|
timeRange={selectedTimeRange}
|
||||||
|
@ -20,6 +20,15 @@ export type ReviewData = {
|
|||||||
zones: string[];
|
zones: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SegmentedReviewData =
|
||||||
|
| {
|
||||||
|
all: ReviewSegment[];
|
||||||
|
alert: ReviewSegment[];
|
||||||
|
detection: ReviewSegment[];
|
||||||
|
significant_motion: ReviewSegment[];
|
||||||
|
}
|
||||||
|
| undefined;
|
||||||
|
|
||||||
export type ReviewFilter = {
|
export type ReviewFilter = {
|
||||||
cameras?: string[];
|
cameras?: string[];
|
||||||
labels?: string[];
|
labels?: string[];
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
ReviewSegment,
|
ReviewSegment,
|
||||||
ReviewSeverity,
|
ReviewSeverity,
|
||||||
ReviewSummary,
|
ReviewSummary,
|
||||||
|
SegmentedReviewData,
|
||||||
} from "@/types/review";
|
} from "@/types/review";
|
||||||
import { getChunkedTimeRange } from "@/utils/timelineUtil";
|
import { getChunkedTimeRange } from "@/utils/timelineUtil";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
@ -49,7 +50,8 @@ import { Toaster } from "@/components/ui/sonner";
|
|||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
type EventViewProps = {
|
type EventViewProps = {
|
||||||
reviews?: ReviewSegment[];
|
reviewItems?: SegmentedReviewData;
|
||||||
|
currentReviewItems: ReviewSegment[] | null;
|
||||||
reviewSummary?: ReviewSummary;
|
reviewSummary?: ReviewSummary;
|
||||||
relevantPreviews?: Preview[];
|
relevantPreviews?: Preview[];
|
||||||
timeRange: TimeRange;
|
timeRange: TimeRange;
|
||||||
@ -64,7 +66,8 @@ type EventViewProps = {
|
|||||||
updateFilter: (filter: ReviewFilter) => void;
|
updateFilter: (filter: ReviewFilter) => void;
|
||||||
};
|
};
|
||||||
export default function EventView({
|
export default function EventView({
|
||||||
reviews,
|
reviewItems,
|
||||||
|
currentReviewItems,
|
||||||
reviewSummary,
|
reviewSummary,
|
||||||
relevantPreviews,
|
relevantPreviews,
|
||||||
timeRange,
|
timeRange,
|
||||||
@ -116,42 +119,6 @@ export default function EventView({
|
|||||||
}
|
}
|
||||||
}, [filter, reviewSummary]);
|
}, [filter, reviewSummary]);
|
||||||
|
|
||||||
// review paging
|
|
||||||
|
|
||||||
const reviewItems = useMemo(() => {
|
|
||||||
if (!reviews) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const all: ReviewSegment[] = [];
|
|
||||||
const alerts: ReviewSegment[] = [];
|
|
||||||
const detections: ReviewSegment[] = [];
|
|
||||||
const motion: ReviewSegment[] = [];
|
|
||||||
|
|
||||||
reviews?.forEach((segment) => {
|
|
||||||
all.push(segment);
|
|
||||||
|
|
||||||
switch (segment.severity) {
|
|
||||||
case "alert":
|
|
||||||
alerts.push(segment);
|
|
||||||
break;
|
|
||||||
case "detection":
|
|
||||||
detections.push(segment);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
motion.push(segment);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
all: all,
|
|
||||||
alert: alerts,
|
|
||||||
detection: detections,
|
|
||||||
significant_motion: motion,
|
|
||||||
};
|
|
||||||
}, [reviews]);
|
|
||||||
|
|
||||||
// review interaction
|
// review interaction
|
||||||
|
|
||||||
const [selectedReviews, setSelectedReviews] = useState<string[]>([]);
|
const [selectedReviews, setSelectedReviews] = useState<string[]>([]);
|
||||||
@ -182,6 +149,7 @@ export default function EventView({
|
|||||||
severity: review.severity,
|
severity: review.severity,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
review.has_been_reviewed = true;
|
||||||
markItemAsReviewed(review);
|
markItemAsReviewed(review);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -332,6 +300,7 @@ export default function EventView({
|
|||||||
<DetectionReview
|
<DetectionReview
|
||||||
contentRef={contentRef}
|
contentRef={contentRef}
|
||||||
reviewItems={reviewItems}
|
reviewItems={reviewItems}
|
||||||
|
currentItems={currentReviewItems}
|
||||||
relevantPreviews={relevantPreviews}
|
relevantPreviews={relevantPreviews}
|
||||||
selectedReviews={selectedReviews}
|
selectedReviews={selectedReviews}
|
||||||
itemsToReview={reviewCounts[severityToggle]}
|
itemsToReview={reviewCounts[severityToggle]}
|
||||||
@ -372,6 +341,7 @@ type DetectionReviewProps = {
|
|||||||
detection: ReviewSegment[];
|
detection: ReviewSegment[];
|
||||||
significant_motion: ReviewSegment[];
|
significant_motion: ReviewSegment[];
|
||||||
};
|
};
|
||||||
|
currentItems: ReviewSegment[] | null;
|
||||||
itemsToReview?: number;
|
itemsToReview?: number;
|
||||||
relevantPreviews?: Preview[];
|
relevantPreviews?: Preview[];
|
||||||
selectedReviews: string[];
|
selectedReviews: string[];
|
||||||
@ -388,6 +358,7 @@ type DetectionReviewProps = {
|
|||||||
function DetectionReview({
|
function DetectionReview({
|
||||||
contentRef,
|
contentRef,
|
||||||
reviewItems,
|
reviewItems,
|
||||||
|
currentItems,
|
||||||
itemsToReview,
|
itemsToReview,
|
||||||
relevantPreviews,
|
relevantPreviews,
|
||||||
selectedReviews,
|
selectedReviews,
|
||||||
@ -405,33 +376,6 @@ function DetectionReview({
|
|||||||
|
|
||||||
const segmentDuration = 60;
|
const segmentDuration = 60;
|
||||||
|
|
||||||
// review data
|
|
||||||
const currentItems = useMemo(() => {
|
|
||||||
if (!reviewItems) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let current;
|
|
||||||
|
|
||||||
if (filter?.showAll) {
|
|
||||||
current = reviewItems.all;
|
|
||||||
} else {
|
|
||||||
current = reviewItems[severity];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!current || current.length == 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filter?.showReviewed != 1) {
|
|
||||||
return current.filter((seg) => !seg.has_been_reviewed);
|
|
||||||
} else {
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
// only refresh when severity or filter changes
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [severity, filter, reviewItems?.all.length]);
|
|
||||||
|
|
||||||
// preview
|
// preview
|
||||||
|
|
||||||
const [previewTime, setPreviewTime] = useState<number>();
|
const [previewTime, setPreviewTime] = useState<number>();
|
||||||
|
Loading…
Reference in New Issue
Block a user