forked from Github/frigate
Implement recordings fullscreen and rework recordings layout size calculation (#11318)
* Implement fullscreen button * wrap items on mobile * control based on width * refresh * Implement basic fullscreen * Fix scrolling * Add observer to detect of row overflows * Use cn to simplify classnames * dynamically respond to layout sizing * Simplify listener * Simplify layout * Handle tall browser
This commit is contained in:
@@ -6,7 +6,7 @@ import {
|
||||
useState,
|
||||
} from "react";
|
||||
import Hls from "hls.js";
|
||||
import { isAndroid, isDesktop, isMobile } from "react-device-detect";
|
||||
import { isAndroid, isDesktop, isIOS, isMobile } from "react-device-detect";
|
||||
import { TransformComponent, TransformWrapper } from "react-zoom-pan-pinch";
|
||||
import VideoControls from "./VideoControls";
|
||||
import { VideoResolutionType } from "@/types/live";
|
||||
@@ -28,24 +28,28 @@ type HlsVideoPlayerProps = {
|
||||
visible: boolean;
|
||||
currentSource: string;
|
||||
hotKeys: boolean;
|
||||
fullscreen: boolean;
|
||||
onClipEnded?: () => void;
|
||||
onPlayerLoaded?: () => void;
|
||||
onTimeUpdate?: (time: number) => void;
|
||||
onPlaying?: () => void;
|
||||
setFullResolution?: React.Dispatch<React.SetStateAction<VideoResolutionType>>;
|
||||
onUploadFrame?: (playTime: number) => Promise<AxiosResponse> | undefined;
|
||||
setFullscreen?: (full: boolean) => void;
|
||||
};
|
||||
export default function HlsVideoPlayer({
|
||||
videoRef,
|
||||
visible,
|
||||
currentSource,
|
||||
hotKeys,
|
||||
fullscreen,
|
||||
onClipEnded,
|
||||
onPlayerLoaded,
|
||||
onTimeUpdate,
|
||||
onPlaying,
|
||||
setFullResolution,
|
||||
onUploadFrame,
|
||||
setFullscreen,
|
||||
}: HlsVideoPlayerProps) {
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
|
||||
@@ -153,6 +157,7 @@ export default function HlsVideoPlayer({
|
||||
seek: true,
|
||||
playbackRate: true,
|
||||
plusUpload: config?.plus?.enabled == true,
|
||||
fullscreen: !isIOS,
|
||||
}}
|
||||
setControlsOpen={setControlsOpen}
|
||||
setMuted={setMuted}
|
||||
@@ -196,6 +201,8 @@ export default function HlsVideoPlayer({
|
||||
}
|
||||
}
|
||||
}}
|
||||
fullscreen={fullscreen}
|
||||
setFullscreen={setFullscreen}
|
||||
/>
|
||||
<TransformComponent
|
||||
wrapperStyle={{
|
||||
|
||||
@@ -30,12 +30,14 @@ import {
|
||||
AlertDialogTrigger,
|
||||
} from "../ui/alert-dialog";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { FaCompress, FaExpand } from "react-icons/fa";
|
||||
|
||||
type VideoControls = {
|
||||
volume?: boolean;
|
||||
seek?: boolean;
|
||||
playbackRate?: boolean;
|
||||
plusUpload?: boolean;
|
||||
fullscreen?: boolean;
|
||||
};
|
||||
|
||||
const CONTROLS_DEFAULT: VideoControls = {
|
||||
@@ -43,6 +45,7 @@ const CONTROLS_DEFAULT: VideoControls = {
|
||||
seek: true,
|
||||
playbackRate: true,
|
||||
plusUpload: false,
|
||||
fullscreen: false,
|
||||
};
|
||||
const PLAYBACK_RATE_DEFAULT = isSafari ? [0.5, 1, 2] : [0.5, 1, 2, 4, 8, 16];
|
||||
|
||||
@@ -57,12 +60,14 @@ type VideoControlsProps = {
|
||||
playbackRates?: number[];
|
||||
playbackRate: number;
|
||||
hotKeys?: boolean;
|
||||
fullscreen?: boolean;
|
||||
setControlsOpen?: (open: boolean) => void;
|
||||
setMuted?: (muted: boolean) => void;
|
||||
onPlayPause: (play: boolean) => void;
|
||||
onSeek: (diff: number) => void;
|
||||
onSetPlaybackRate: (rate: number) => void;
|
||||
onUploadFrame?: () => void;
|
||||
setFullscreen?: (full: boolean) => void;
|
||||
};
|
||||
export default function VideoControls({
|
||||
className,
|
||||
@@ -75,12 +80,14 @@ export default function VideoControls({
|
||||
playbackRates = PLAYBACK_RATE_DEFAULT,
|
||||
playbackRate,
|
||||
hotKeys = true,
|
||||
fullscreen,
|
||||
setControlsOpen,
|
||||
setMuted,
|
||||
onPlayPause,
|
||||
onSeek,
|
||||
onSetPlaybackRate,
|
||||
onUploadFrame,
|
||||
setFullscreen,
|
||||
}: VideoControlsProps) {
|
||||
const onReplay = useCallback(
|
||||
(e: React.MouseEvent<SVGElement>) => {
|
||||
@@ -163,7 +170,7 @@ export default function VideoControls({
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"px-4 py-2 flex justify-between items-center gap-8 text-primary z-50 bg-background/60 rounded-lg",
|
||||
"w-[96%] sm:w-auto px-4 py-2 flex flex-wrap sm:flex-nowrap justify-between items-center gap-4 sm:gap-8 text-primary z-50 bg-background/60 rounded-lg",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
@@ -248,6 +255,14 @@ export default function VideoControls({
|
||||
onUploadFrame={onUploadFrame}
|
||||
/>
|
||||
)}
|
||||
{features.fullscreen && setFullscreen && (
|
||||
<div
|
||||
className="cursor-pointer"
|
||||
onClick={() => setFullscreen(!fullscreen)}
|
||||
>
|
||||
{fullscreen ? <FaCompress /> : <FaExpand />}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,10 +24,12 @@ type DynamicVideoPlayerProps = {
|
||||
startTimestamp?: number;
|
||||
isScrubbing: boolean;
|
||||
hotKeys: boolean;
|
||||
fullscreen: boolean;
|
||||
onControllerReady: (controller: DynamicVideoController) => void;
|
||||
onTimestampUpdate?: (timestamp: number) => void;
|
||||
onClipEnded?: () => void;
|
||||
setFullResolution: React.Dispatch<React.SetStateAction<VideoResolutionType>>;
|
||||
setFullscreen: (full: boolean) => void;
|
||||
};
|
||||
export default function DynamicVideoPlayer({
|
||||
className,
|
||||
@@ -37,10 +39,12 @@ export default function DynamicVideoPlayer({
|
||||
startTimestamp,
|
||||
isScrubbing,
|
||||
hotKeys,
|
||||
fullscreen,
|
||||
onControllerReady,
|
||||
onTimestampUpdate,
|
||||
onClipEnded,
|
||||
setFullResolution,
|
||||
setFullscreen,
|
||||
}: DynamicVideoPlayerProps) {
|
||||
const apiHost = useApiHost();
|
||||
const { data: config } = useSWR<FrigateConfig>("config");
|
||||
@@ -184,6 +188,7 @@ export default function DynamicVideoPlayer({
|
||||
visible={!(isScrubbing || isLoading)}
|
||||
currentSource={source}
|
||||
hotKeys={hotKeys}
|
||||
fullscreen={fullscreen}
|
||||
onTimeUpdate={onTimeUpdate}
|
||||
onPlayerLoaded={onPlayerLoaded}
|
||||
onClipEnded={onClipEnded}
|
||||
@@ -201,6 +206,7 @@ export default function DynamicVideoPlayer({
|
||||
}}
|
||||
setFullResolution={setFullResolution}
|
||||
onUploadFrame={onUploadFrameToPlus}
|
||||
setFullscreen={setFullscreen}
|
||||
/>
|
||||
<PreviewPlayer
|
||||
className={cn(
|
||||
|
||||
Reference in New Issue
Block a user