Fix current hour

This commit is contained in:
Nicolas Mowen 2024-03-28 16:39:52 -06:00
parent 223b6c19de
commit 9b72f2dedd
4 changed files with 56 additions and 71 deletions

View File

@ -12,8 +12,7 @@ import { Preview } from "@/types/preview";
import { PreviewPlayback } from "@/types/playback"; import { PreviewPlayback } from "@/types/playback";
import { isCurrentHour } from "@/utils/dateUtil"; import { isCurrentHour } from "@/utils/dateUtil";
import { baseUrl } from "@/api/baseUrl"; import { baseUrl } from "@/api/baseUrl";
import { isAndroid, isChrome, isMobile, isSafari } from "react-device-detect"; import { isAndroid, isChrome, isMobile } from "react-device-detect";
import { Skeleton } from "../ui/skeleton";
import { TimeRange } from "@/types/timeline"; import { TimeRange } from "@/types/timeline";
type PreviewPlayerProps = { type PreviewPlayerProps = {
@ -34,7 +33,6 @@ export default function PreviewPlayer({
cameraPreviews, cameraPreviews,
startTime, startTime,
isScrubbing, isScrubbing,
forceAspect,
onControllerReady, onControllerReady,
onClick, onClick,
}: PreviewPlayerProps) { }: PreviewPlayerProps) {
@ -62,7 +60,6 @@ export default function PreviewPlayer({
cameraPreviews={cameraPreviews} cameraPreviews={cameraPreviews}
startTime={startTime} startTime={startTime}
isScrubbing={isScrubbing} isScrubbing={isScrubbing}
forceAspect={forceAspect}
currentHourFrame={currentHourFrame} currentHourFrame={currentHourFrame}
onControllerReady={onControllerReady} onControllerReady={onControllerReady}
onClick={onClick} onClick={onClick}
@ -92,7 +89,6 @@ type PreviewVideoPlayerProps = {
cameraPreviews: Preview[]; cameraPreviews: Preview[];
startTime?: number; startTime?: number;
isScrubbing: boolean; isScrubbing: boolean;
forceAspect?: number;
currentHourFrame?: string; currentHourFrame?: string;
onControllerReady: (controller: PreviewVideoController) => void; onControllerReady: (controller: PreviewVideoController) => void;
onClick?: () => void; onClick?: () => void;
@ -105,7 +101,6 @@ function PreviewVideoPlayer({
cameraPreviews, cameraPreviews,
startTime, startTime,
isScrubbing, isScrubbing,
forceAspect,
currentHourFrame, currentHourFrame,
onControllerReady, onControllerReady,
onClick, onClick,
@ -148,8 +143,6 @@ function PreviewVideoPlayer({
// initial state // initial state
const [loaded, setLoaded] = useState(false);
const [hasCanvas, setHasCanvas] = useState(false);
const initialPreview = useMemo(() => { const initialPreview = useMemo(() => {
return cameraPreviews.find( return cameraPreviews.find(
(preview) => (preview) =>
@ -191,7 +184,6 @@ function PreviewVideoPlayer({
if (preview != currentPreview) { if (preview != currentPreview) {
setCurrentPreview(preview); setCurrentPreview(preview);
setLoaded(false);
} }
controller.newPlayback({ controller.newPlayback({
@ -215,21 +207,20 @@ function PreviewVideoPlayer({
return; return;
} }
if (canvasRef.current) { if (!canvasRef.current && videoSize[0] > 0) {
const context = canvasRef.current.getContext("2d"); const canvas = document.createElement("canvas");
canvas.width = videoSize[0];
canvas.height = videoSize[1];
canvasRef.current = canvas;
}
const context = canvasRef.current?.getContext("2d");
if (context) { if (context) {
context.drawImage(previewRef.current, 0, 0, videoSize[0], videoSize[1]); context.drawImage(previewRef.current, 0, 0, videoSize[0], videoSize[1]);
setCurrentHourFrame(canvasRef.current?.toDataURL("image/webp"));
} }
setHasCanvas(true);
}
if (isSafari) {
setTimeout(() => previewRef.current?.load(), 100);
} else {
previewRef.current.load();
}
// we only want this to change when current preview changes // we only want this to change when current preview changes
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentPreview, previewRef]); }, [currentPreview, previewRef]);
@ -237,26 +228,16 @@ function PreviewVideoPlayer({
return ( return (
<div <div
className={`relative rounded-2xl bg-black overflow-hidden ${onClick ? "cursor-pointer" : ""} ${className ?? ""}`} className={`relative rounded-2xl bg-black overflow-hidden ${onClick ? "cursor-pointer" : ""} ${className ?? ""}`}
style={{
aspectRatio: forceAspect,
}}
onClick={onClick} onClick={onClick}
> >
{currentHourFrame && (
<img <img
className="absolute size-full object-contain" className={`absolute size-full object-contain ${currentHourFrame ? "visible" : "invisible"}`}
src={currentHourFrame} src={currentHourFrame}
/> onLoad={() => previewRef.current?.load()}
)}
<canvas
ref={canvasRef}
width={videoSize[0]}
height={videoSize[1]}
className={`h-full absolute left-1/2 -translate-x-1/2 ${!loaded && hasCanvas ? "" : "hidden"}`}
/> />
<video <video
ref={previewRef} ref={previewRef}
className="size-full" className="absolute size-full"
preload="auto" preload="auto"
autoPlay autoPlay
playsInline playsInline
@ -265,7 +246,6 @@ function PreviewVideoPlayer({
onSeeked={onPreviewSeeked} onSeeked={onPreviewSeeked}
onLoadedData={() => { onLoadedData={() => {
setCurrentHourFrame(undefined); setCurrentHourFrame(undefined);
setLoaded(true);
if (controller) { if (controller) {
controller.previewReady(); controller.previewReady();
@ -289,9 +269,6 @@ function PreviewVideoPlayer({
<source src={currentPreview.src} type={currentPreview.type} /> <source src={currentPreview.src} type={currentPreview.type} />
)} )}
</video> </video>
{!loaded && !hasCanvas && !currentHourFrame && (
<Skeleton className="absolute inset-0" />
)}
{cameraPreviews && !currentPreview && ( {cameraPreviews && !currentPreview && (
<div className="absolute inset-0 text-white rounded-2xl flex justify-center items-center"> <div className="absolute inset-0 text-white rounded-2xl flex justify-center items-center">
No Preview Found No Preview Found
@ -479,7 +456,7 @@ function PreviewFramesPlayer({
return ( return (
<div <div
className={`relative w-full ${className ?? ""} ${onClick ? "cursor-pointer" : ""}`} className={`relative ${className ?? ""} ${onClick ? "cursor-pointer" : ""}`}
onClick={onClick} onClick={onClick}
> >
<img <img

View File

@ -9,7 +9,6 @@ import PreviewPlayer, { PreviewController } from "../PreviewPlayer";
import { DynamicVideoController } from "./DynamicVideoController"; import { DynamicVideoController } from "./DynamicVideoController";
import HlsVideoPlayer from "../HlsVideoPlayer"; import HlsVideoPlayer from "../HlsVideoPlayer";
import { TimeRange, Timeline } from "@/types/timeline"; import { TimeRange, Timeline } from "@/types/timeline";
import { isDesktop } from "react-device-detect";
/** /**
* Dynamically switches between video playback and scrubbing preview player. * Dynamically switches between video playback and scrubbing preview player.
@ -151,7 +150,7 @@ export default function DynamicVideoPlayer({
return ( return (
<> <>
<HlsVideoPlayer <HlsVideoPlayer
className={isDesktop ? `w-full ${className}` : "max-h-[50dvh]"} className={className ?? ""}
videoRef={playerRef} videoRef={playerRef}
visible={!(isScrubbing || isLoading)} visible={!(isScrubbing || isLoading)}
currentSource={source} currentSource={source}
@ -175,7 +174,7 @@ export default function DynamicVideoPlayer({
)} )}
</HlsVideoPlayer> </HlsVideoPlayer>
<PreviewPlayer <PreviewPlayer
className={`${isScrubbing || isLoading ? "visible" : "hidden"} ${isDesktop ? `w-full ${className}` : "max-h-[50dvh]"}`} className={`${isScrubbing || isLoading ? "visible" : "hidden"} ${className}`}
camera={camera} camera={camera}
timeRange={timeRange} timeRange={timeRange}
cameraPreviews={cameraPreviews} cameraPreviews={cameraPreviews}

View File

@ -127,8 +127,8 @@ export function getTimelineItemDescription(timelineItem: Timeline) {
* @returns timeRange chunked into individual hours * @returns timeRange chunked into individual hours
*/ */
export function getChunkedTimeDay(timeRange: TimeRange): TimeRange[] { export function getChunkedTimeDay(timeRange: TimeRange): TimeRange[] {
const endOfThisHour = new Date(); const endOfThisHour = new Date(timeRange.before * 1000);
endOfThisHour.setHours(endOfThisHour.getHours() + 1, 0, 0, 0); endOfThisHour.setSeconds(0, 0);
const data: TimeRange[] = []; const data: TimeRange[] = [];
const startDay = new Date(timeRange.after * 1000); const startDay = new Date(timeRange.after * 1000);
startDay.setMinutes(0, 0, 0); startDay.setMinutes(0, 0, 0);
@ -136,7 +136,7 @@ export function getChunkedTimeDay(timeRange: TimeRange): TimeRange[] {
let end = 0; let end = 0;
for (let i = 0; i < 24; i++) { for (let i = 0; i < 24; i++) {
startDay.setHours(startDay.getHours() + 1); startDay.setHours(startDay.getHours() + 1, 0, 0, 0);
if (startDay > endOfThisHour) { if (startDay > endOfThisHour) {
break; break;
@ -150,6 +150,11 @@ export function getChunkedTimeDay(timeRange: TimeRange): TimeRange[] {
start = startDay.getTime() / 1000; start = startDay.getTime() / 1000;
} }
data.push({
after: start,
before: Math.floor(timeRange.before),
});
return data; return data;
} }

View File

@ -226,14 +226,14 @@ export function RecordingView({
}, [getCameraAspect, mainCamera]); }, [getCameraAspect, mainCamera]);
const grow = useMemo(() => { const grow = useMemo(() => {
if (isMobile) {
return "";
}
if (mainCameraAspect == "wide") { if (mainCameraAspect == "wide") {
return "w-full aspect-wide"; return "w-full aspect-wide";
} else if (isDesktop && mainCameraAspect == "tall") { } else if (mainCameraAspect == "tall") {
if (isDesktop) {
return "h-full aspect-tall flex flex-col justify-center"; return "h-full aspect-tall flex flex-col justify-center";
} else {
return "size-full";
}
} else { } else {
return "w-full aspect-video"; return "w-full aspect-video";
} }
@ -352,10 +352,11 @@ export function RecordingView({
: `w-full ${mainCameraAspect == "wide" ? "aspect-wide" : "aspect-video"}` : `w-full ${mainCameraAspect == "wide" ? "aspect-wide" : "aspect-video"}`
} }
style={{ style={{
aspectRatio: aspectRatio: isDesktop
mainCameraAspect == "tall" ? mainCameraAspect == "tall"
? getCameraAspect(mainCamera) ? getCameraAspect(mainCamera)
: undefined, : undefined
: Math.max(1, getCameraAspect(mainCamera) ?? 0),
}} }}
> >
<DynamicVideoPlayer <DynamicVideoPlayer
@ -381,35 +382,38 @@ export function RecordingView({
</div> </div>
{isDesktop && ( {isDesktop && (
<div <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) => { {allCameras.map((cam) => {
if (cam !== mainCamera) { if (cam == mainCamera) {
const preview = ( return;
<PreviewPlayer }
return (
<div
key={cam} key={cam}
className={`${mainCameraAspect == "wide" ? "flex-grow" : ""}`} className={
mainCameraAspect == "tall" ? undefined : "h-full"
}
style={{
aspectRatio: getCameraAspect(cam),
}}
>
<PreviewPlayer
className="size-full"
camera={cam} camera={cam}
timeRange={currentTimeRange} timeRange={currentTimeRange}
cameraPreviews={allPreviews ?? []} cameraPreviews={allPreviews ?? []}
startTime={startTime} startTime={startTime}
isScrubbing={scrubbing} isScrubbing={scrubbing}
forceAspect={getCameraAspect(cam)}
onControllerReady={(controller) => { onControllerReady={(controller) => {
previewRefs.current[cam] = controller; previewRefs.current[cam] = controller;
controller.scrubToTimestamp(startTime); controller.scrubToTimestamp(startTime);
}} }}
onClick={() => onSelectCamera(cam)} onClick={() => onSelectCamera(cam)}
/> />
</div>
); );
if (mainCameraAspect == "tall") {
return <div key={`${cam}-t`}>{preview}</div>;
}
return preview;
}
return null;
})} })}
</div> </div>
)} )}