mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-10 13:15:25 +03:00
Fix current hour
This commit is contained in:
parent
223b6c19de
commit
9b72f2dedd
@ -12,8 +12,7 @@ import { Preview } from "@/types/preview";
|
||||
import { PreviewPlayback } from "@/types/playback";
|
||||
import { isCurrentHour } from "@/utils/dateUtil";
|
||||
import { baseUrl } from "@/api/baseUrl";
|
||||
import { isAndroid, isChrome, isMobile, isSafari } from "react-device-detect";
|
||||
import { Skeleton } from "../ui/skeleton";
|
||||
import { isAndroid, isChrome, isMobile } from "react-device-detect";
|
||||
import { TimeRange } from "@/types/timeline";
|
||||
|
||||
type PreviewPlayerProps = {
|
||||
@ -34,7 +33,6 @@ export default function PreviewPlayer({
|
||||
cameraPreviews,
|
||||
startTime,
|
||||
isScrubbing,
|
||||
forceAspect,
|
||||
onControllerReady,
|
||||
onClick,
|
||||
}: PreviewPlayerProps) {
|
||||
@ -62,7 +60,6 @@ export default function PreviewPlayer({
|
||||
cameraPreviews={cameraPreviews}
|
||||
startTime={startTime}
|
||||
isScrubbing={isScrubbing}
|
||||
forceAspect={forceAspect}
|
||||
currentHourFrame={currentHourFrame}
|
||||
onControllerReady={onControllerReady}
|
||||
onClick={onClick}
|
||||
@ -92,7 +89,6 @@ type PreviewVideoPlayerProps = {
|
||||
cameraPreviews: Preview[];
|
||||
startTime?: number;
|
||||
isScrubbing: boolean;
|
||||
forceAspect?: number;
|
||||
currentHourFrame?: string;
|
||||
onControllerReady: (controller: PreviewVideoController) => void;
|
||||
onClick?: () => void;
|
||||
@ -105,7 +101,6 @@ function PreviewVideoPlayer({
|
||||
cameraPreviews,
|
||||
startTime,
|
||||
isScrubbing,
|
||||
forceAspect,
|
||||
currentHourFrame,
|
||||
onControllerReady,
|
||||
onClick,
|
||||
@ -148,8 +143,6 @@ function PreviewVideoPlayer({
|
||||
|
||||
// initial state
|
||||
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
const [hasCanvas, setHasCanvas] = useState(false);
|
||||
const initialPreview = useMemo(() => {
|
||||
return cameraPreviews.find(
|
||||
(preview) =>
|
||||
@ -191,7 +184,6 @@ function PreviewVideoPlayer({
|
||||
|
||||
if (preview != currentPreview) {
|
||||
setCurrentPreview(preview);
|
||||
setLoaded(false);
|
||||
}
|
||||
|
||||
controller.newPlayback({
|
||||
@ -215,21 +207,20 @@ function PreviewVideoPlayer({
|
||||
return;
|
||||
}
|
||||
|
||||
if (canvasRef.current) {
|
||||
const context = canvasRef.current.getContext("2d");
|
||||
|
||||
if (context) {
|
||||
context.drawImage(previewRef.current, 0, 0, videoSize[0], videoSize[1]);
|
||||
}
|
||||
|
||||
setHasCanvas(true);
|
||||
if (!canvasRef.current && videoSize[0] > 0) {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = videoSize[0];
|
||||
canvas.height = videoSize[1];
|
||||
canvasRef.current = canvas;
|
||||
}
|
||||
|
||||
if (isSafari) {
|
||||
setTimeout(() => previewRef.current?.load(), 100);
|
||||
} else {
|
||||
previewRef.current.load();
|
||||
const context = canvasRef.current?.getContext("2d");
|
||||
|
||||
if (context) {
|
||||
context.drawImage(previewRef.current, 0, 0, videoSize[0], videoSize[1]);
|
||||
setCurrentHourFrame(canvasRef.current?.toDataURL("image/webp"));
|
||||
}
|
||||
|
||||
// we only want this to change when current preview changes
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentPreview, previewRef]);
|
||||
@ -237,26 +228,16 @@ function PreviewVideoPlayer({
|
||||
return (
|
||||
<div
|
||||
className={`relative rounded-2xl bg-black overflow-hidden ${onClick ? "cursor-pointer" : ""} ${className ?? ""}`}
|
||||
style={{
|
||||
aspectRatio: forceAspect,
|
||||
}}
|
||||
onClick={onClick}
|
||||
>
|
||||
{currentHourFrame && (
|
||||
<img
|
||||
className="absolute size-full object-contain"
|
||||
src={currentHourFrame}
|
||||
/>
|
||||
)}
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
width={videoSize[0]}
|
||||
height={videoSize[1]}
|
||||
className={`h-full absolute left-1/2 -translate-x-1/2 ${!loaded && hasCanvas ? "" : "hidden"}`}
|
||||
<img
|
||||
className={`absolute size-full object-contain ${currentHourFrame ? "visible" : "invisible"}`}
|
||||
src={currentHourFrame}
|
||||
onLoad={() => previewRef.current?.load()}
|
||||
/>
|
||||
<video
|
||||
ref={previewRef}
|
||||
className="size-full"
|
||||
className="absolute size-full"
|
||||
preload="auto"
|
||||
autoPlay
|
||||
playsInline
|
||||
@ -265,7 +246,6 @@ function PreviewVideoPlayer({
|
||||
onSeeked={onPreviewSeeked}
|
||||
onLoadedData={() => {
|
||||
setCurrentHourFrame(undefined);
|
||||
setLoaded(true);
|
||||
|
||||
if (controller) {
|
||||
controller.previewReady();
|
||||
@ -289,9 +269,6 @@ function PreviewVideoPlayer({
|
||||
<source src={currentPreview.src} type={currentPreview.type} />
|
||||
)}
|
||||
</video>
|
||||
{!loaded && !hasCanvas && !currentHourFrame && (
|
||||
<Skeleton className="absolute inset-0" />
|
||||
)}
|
||||
{cameraPreviews && !currentPreview && (
|
||||
<div className="absolute inset-0 text-white rounded-2xl flex justify-center items-center">
|
||||
No Preview Found
|
||||
@ -479,7 +456,7 @@ function PreviewFramesPlayer({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`relative w-full ${className ?? ""} ${onClick ? "cursor-pointer" : ""}`}
|
||||
className={`relative ${className ?? ""} ${onClick ? "cursor-pointer" : ""}`}
|
||||
onClick={onClick}
|
||||
>
|
||||
<img
|
||||
|
||||
@ -9,7 +9,6 @@ import PreviewPlayer, { PreviewController } from "../PreviewPlayer";
|
||||
import { DynamicVideoController } from "./DynamicVideoController";
|
||||
import HlsVideoPlayer from "../HlsVideoPlayer";
|
||||
import { TimeRange, Timeline } from "@/types/timeline";
|
||||
import { isDesktop } from "react-device-detect";
|
||||
|
||||
/**
|
||||
* Dynamically switches between video playback and scrubbing preview player.
|
||||
@ -151,7 +150,7 @@ export default function DynamicVideoPlayer({
|
||||
return (
|
||||
<>
|
||||
<HlsVideoPlayer
|
||||
className={isDesktop ? `w-full ${className}` : "max-h-[50dvh]"}
|
||||
className={className ?? ""}
|
||||
videoRef={playerRef}
|
||||
visible={!(isScrubbing || isLoading)}
|
||||
currentSource={source}
|
||||
@ -175,7 +174,7 @@ export default function DynamicVideoPlayer({
|
||||
)}
|
||||
</HlsVideoPlayer>
|
||||
<PreviewPlayer
|
||||
className={`${isScrubbing || isLoading ? "visible" : "hidden"} ${isDesktop ? `w-full ${className}` : "max-h-[50dvh]"}`}
|
||||
className={`${isScrubbing || isLoading ? "visible" : "hidden"} ${className}`}
|
||||
camera={camera}
|
||||
timeRange={timeRange}
|
||||
cameraPreviews={cameraPreviews}
|
||||
|
||||
@ -127,8 +127,8 @@ export function getTimelineItemDescription(timelineItem: Timeline) {
|
||||
* @returns timeRange chunked into individual hours
|
||||
*/
|
||||
export function getChunkedTimeDay(timeRange: TimeRange): TimeRange[] {
|
||||
const endOfThisHour = new Date();
|
||||
endOfThisHour.setHours(endOfThisHour.getHours() + 1, 0, 0, 0);
|
||||
const endOfThisHour = new Date(timeRange.before * 1000);
|
||||
endOfThisHour.setSeconds(0, 0);
|
||||
const data: TimeRange[] = [];
|
||||
const startDay = new Date(timeRange.after * 1000);
|
||||
startDay.setMinutes(0, 0, 0);
|
||||
@ -136,7 +136,7 @@ export function getChunkedTimeDay(timeRange: TimeRange): TimeRange[] {
|
||||
let end = 0;
|
||||
|
||||
for (let i = 0; i < 24; i++) {
|
||||
startDay.setHours(startDay.getHours() + 1);
|
||||
startDay.setHours(startDay.getHours() + 1, 0, 0, 0);
|
||||
|
||||
if (startDay > endOfThisHour) {
|
||||
break;
|
||||
@ -150,6 +150,11 @@ export function getChunkedTimeDay(timeRange: TimeRange): TimeRange[] {
|
||||
start = startDay.getTime() / 1000;
|
||||
}
|
||||
|
||||
data.push({
|
||||
after: start,
|
||||
before: Math.floor(timeRange.before),
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
@ -226,14 +226,14 @@ export function RecordingView({
|
||||
}, [getCameraAspect, mainCamera]);
|
||||
|
||||
const grow = useMemo(() => {
|
||||
if (isMobile) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (mainCameraAspect == "wide") {
|
||||
return "w-full aspect-wide";
|
||||
} else if (isDesktop && mainCameraAspect == "tall") {
|
||||
return "h-full aspect-tall flex flex-col justify-center";
|
||||
} else if (mainCameraAspect == "tall") {
|
||||
if (isDesktop) {
|
||||
return "h-full aspect-tall flex flex-col justify-center";
|
||||
} else {
|
||||
return "size-full";
|
||||
}
|
||||
} else {
|
||||
return "w-full aspect-video";
|
||||
}
|
||||
@ -352,10 +352,11 @@ export function RecordingView({
|
||||
: `w-full ${mainCameraAspect == "wide" ? "aspect-wide" : "aspect-video"}`
|
||||
}
|
||||
style={{
|
||||
aspectRatio:
|
||||
mainCameraAspect == "tall"
|
||||
aspectRatio: isDesktop
|
||||
? mainCameraAspect == "tall"
|
||||
? getCameraAspect(mainCamera)
|
||||
: undefined,
|
||||
: undefined
|
||||
: Math.max(1, getCameraAspect(mainCamera) ?? 0),
|
||||
}}
|
||||
>
|
||||
<DynamicVideoPlayer
|
||||
@ -381,35 +382,38 @@ export function RecordingView({
|
||||
</div>
|
||||
{isDesktop && (
|
||||
<div
|
||||
className={`flex gap-2 ${mainCameraAspect == "tall" ? "h-full w-[16%] flex-col overflow-y-auto" : "w-full justify-center overflow-x-auto"}`}
|
||||
className={`flex gap-2 ${mainCameraAspect == "tall" ? "h-full w-[12%] flex-col justify-center overflow-y-auto" : "w-full h-[14%] justify-center items-center overflow-x-auto"} `}
|
||||
>
|
||||
{allCameras.map((cam) => {
|
||||
if (cam !== mainCamera) {
|
||||
const preview = (
|
||||
if (cam == mainCamera) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
key={cam}
|
||||
className={
|
||||
mainCameraAspect == "tall" ? undefined : "h-full"
|
||||
}
|
||||
style={{
|
||||
aspectRatio: getCameraAspect(cam),
|
||||
}}
|
||||
>
|
||||
<PreviewPlayer
|
||||
key={cam}
|
||||
className={`${mainCameraAspect == "wide" ? "flex-grow" : ""}`}
|
||||
className="size-full"
|
||||
camera={cam}
|
||||
timeRange={currentTimeRange}
|
||||
cameraPreviews={allPreviews ?? []}
|
||||
startTime={startTime}
|
||||
isScrubbing={scrubbing}
|
||||
forceAspect={getCameraAspect(cam)}
|
||||
onControllerReady={(controller) => {
|
||||
previewRefs.current[cam] = controller;
|
||||
controller.scrubToTimestamp(startTime);
|
||||
}}
|
||||
onClick={() => onSelectCamera(cam)}
|
||||
/>
|
||||
);
|
||||
|
||||
if (mainCameraAspect == "tall") {
|
||||
return <div key={`${cam}-t`}>{preview}</div>;
|
||||
}
|
||||
|
||||
return preview;
|
||||
}
|
||||
return null;
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user