mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
Fix frigate+ submit and recordings layouts for portrait cameras (#10486)
* Fix plus submission dialog * Different layout for portrait recordings * Fix now preview found pulsing * Fix bug with uneven milliseconds * Improve consistency of video scaling
This commit is contained in:
parent
64763293a2
commit
c14f3c3902
@ -59,6 +59,7 @@ export default function HlsVideoPlayer({
|
|||||||
|
|
||||||
const hlsRef = useRef<Hls>();
|
const hlsRef = useRef<Hls>();
|
||||||
const [useHlsCompat, setUseHlsCompat] = useState(false);
|
const [useHlsCompat, setUseHlsCompat] = useState(false);
|
||||||
|
const [loadedMetadata, setLoadedMetadata] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!videoRef.current) {
|
if (!videoRef.current) {
|
||||||
@ -153,7 +154,7 @@ export default function HlsVideoPlayer({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`relative ${className ?? ""}`}
|
className={`relative`}
|
||||||
onMouseOver={
|
onMouseOver={
|
||||||
isDesktop
|
isDesktop
|
||||||
? () => {
|
? () => {
|
||||||
@ -174,7 +175,7 @@ export default function HlsVideoPlayer({
|
|||||||
<TransformComponent>
|
<TransformComponent>
|
||||||
<video
|
<video
|
||||||
ref={videoRef}
|
ref={videoRef}
|
||||||
className="size-full rounded-2xl"
|
className={`${className ?? ""} bg-black rounded-2xl ${loadedMetadata ? "" : "invisible"}`}
|
||||||
preload="auto"
|
preload="auto"
|
||||||
autoPlay
|
autoPlay
|
||||||
controls={false}
|
controls={false}
|
||||||
@ -204,6 +205,7 @@ export default function HlsVideoPlayer({
|
|||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
onLoadedData={onPlayerLoaded}
|
onLoadedData={onPlayerLoaded}
|
||||||
|
onLoadedMetadata={() => setLoadedMetadata(true)}
|
||||||
onEnded={onClipEnded}
|
onEnded={onClipEnded}
|
||||||
onError={(e) => {
|
onError={(e) => {
|
||||||
if (
|
if (
|
||||||
|
@ -211,7 +211,7 @@ function PreviewVideoPlayer({
|
|||||||
</video>
|
</video>
|
||||||
{!loaded && <Skeleton className="absolute inset-0" />}
|
{!loaded && <Skeleton className="absolute inset-0" />}
|
||||||
{cameraPreviews && !currentPreview && (
|
{cameraPreviews && !currentPreview && (
|
||||||
<div className="absolute inset-x-0 top-1/2 -y-translate-1/2 bg-black text-white rounded-2xl align-center text-center">
|
<div className="absolute inset-0 bg-black text-white rounded-2xl flex justify-center items-center">
|
||||||
No Preview Found
|
No Preview Found
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -38,16 +38,23 @@ export default function DynamicVideoPlayer({
|
|||||||
const { data: config } = useSWR<FrigateConfig>("config");
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
|
|
||||||
// playback behavior
|
// playback behavior
|
||||||
const wideVideo = useMemo(() => {
|
|
||||||
|
const grow = useMemo(() => {
|
||||||
if (!config) {
|
if (!config) {
|
||||||
return false;
|
return "aspect-video";
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const aspectRatio =
|
||||||
config.cameras[camera].detect.width /
|
config.cameras[camera].detect.width /
|
||||||
config.cameras[camera].detect.height >
|
config.cameras[camera].detect.height;
|
||||||
1.7
|
|
||||||
);
|
if (aspectRatio > 2) {
|
||||||
|
return "";
|
||||||
|
} else if (aspectRatio < 16 / 9) {
|
||||||
|
return "aspect-tall";
|
||||||
|
} else {
|
||||||
|
return "aspect-video";
|
||||||
|
}
|
||||||
}, [camera, config]);
|
}, [camera, config]);
|
||||||
|
|
||||||
// controlling playback
|
// controlling playback
|
||||||
@ -163,7 +170,7 @@ export default function DynamicVideoPlayer({
|
|||||||
className={`w-full relative ${isScrubbing || isLoading ? "hidden" : "visible"}`}
|
className={`w-full relative ${isScrubbing || isLoading ? "hidden" : "visible"}`}
|
||||||
>
|
>
|
||||||
<HlsVideoPlayer
|
<HlsVideoPlayer
|
||||||
className={`${wideVideo ? "" : "aspect-video"}`}
|
className={`${grow}`}
|
||||||
videoRef={playerRef}
|
videoRef={playerRef}
|
||||||
currentSource={source}
|
currentSource={source}
|
||||||
onTimeUpdate={onTimeUpdate}
|
onTimeUpdate={onTimeUpdate}
|
||||||
@ -180,7 +187,7 @@ export default function DynamicVideoPlayer({
|
|||||||
</HlsVideoPlayer>
|
</HlsVideoPlayer>
|
||||||
</div>
|
</div>
|
||||||
<PreviewPlayer
|
<PreviewPlayer
|
||||||
className={`${isScrubbing || isLoading ? "visible" : "hidden"} ${className ?? ""}`}
|
className={`${isScrubbing || isLoading ? "visible" : "hidden"} ${grow}`}
|
||||||
camera={camera}
|
camera={camera}
|
||||||
timeRange={timeRange}
|
timeRange={timeRange}
|
||||||
cameraPreviews={cameraPreviews}
|
cameraPreviews={cameraPreviews}
|
||||||
|
@ -118,6 +118,7 @@ export default function Events() {
|
|||||||
endDate.setHours(0, 0, 0, 0);
|
endDate.setHours(0, 0, 0, 0);
|
||||||
} else {
|
} else {
|
||||||
endDate = new Date();
|
endDate = new Date();
|
||||||
|
endDate.setMilliseconds(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -26,6 +26,8 @@ import { FaList, FaVideo } from "react-icons/fa";
|
|||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
|
|
||||||
export default function SubmitPlus() {
|
export default function SubmitPlus() {
|
||||||
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
|
|
||||||
// filters
|
// filters
|
||||||
|
|
||||||
const [selectedCameras, setSelectedCameras] = useState<string[]>();
|
const [selectedCameras, setSelectedCameras] = useState<string[]>();
|
||||||
@ -45,6 +47,24 @@ export default function SubmitPlus() {
|
|||||||
]);
|
]);
|
||||||
const [upload, setUpload] = useState<Event>();
|
const [upload, setUpload] = useState<Event>();
|
||||||
|
|
||||||
|
const grow = useMemo(() => {
|
||||||
|
if (!config || !upload) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const camera = config.cameras[upload.camera];
|
||||||
|
|
||||||
|
if (!camera) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (camera.detect.width / camera.detect.height < 16 / 9) {
|
||||||
|
return "aspect-video object-contain";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}, [config, upload]);
|
||||||
|
|
||||||
const onSubmitToPlus = useCallback(
|
const onSubmitToPlus = useCallback(
|
||||||
async (falsePositive: boolean) => {
|
async (falsePositive: boolean) => {
|
||||||
if (!upload) {
|
if (!upload) {
|
||||||
@ -102,7 +122,7 @@ export default function SubmitPlus() {
|
|||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<img
|
<img
|
||||||
className="flex-grow-0"
|
className={`w-full ${grow} bg-black`}
|
||||||
src={`${baseUrl}api/events/${upload?.id}/snapshot.jpg`}
|
src={`${baseUrl}api/events/${upload?.id}/snapshot.jpg`}
|
||||||
alt={`${upload?.label}`}
|
alt={`${upload?.label}`}
|
||||||
/>
|
/>
|
||||||
|
@ -170,21 +170,34 @@ export function DesktopRecordingView({
|
|||||||
: null,
|
: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
const grow = useMemo(() => {
|
const mainCameraAspect = useMemo(() => {
|
||||||
if (!config) {
|
if (!config) {
|
||||||
return "aspect-video";
|
return "normal";
|
||||||
}
|
}
|
||||||
|
|
||||||
const aspectRatio =
|
const aspectRatio =
|
||||||
config.cameras[mainCamera].detect.width /
|
config.cameras[mainCamera].detect.width /
|
||||||
config.cameras[mainCamera].detect.height;
|
config.cameras[mainCamera].detect.height;
|
||||||
|
|
||||||
if (aspectRatio > 2) {
|
if (aspectRatio > 2) {
|
||||||
return "aspect-wide";
|
return "wide";
|
||||||
|
} else if (aspectRatio < 16 / 9) {
|
||||||
|
return "tall";
|
||||||
} else {
|
} else {
|
||||||
return "aspect-video";
|
return "normal";
|
||||||
}
|
}
|
||||||
}, [config, mainCamera]);
|
}, [config, mainCamera]);
|
||||||
|
|
||||||
|
const grow = useMemo(() => {
|
||||||
|
if (mainCameraAspect == "wide") {
|
||||||
|
return "w-full aspect-wide";
|
||||||
|
} else if (mainCameraAspect == "tall") {
|
||||||
|
return "h-full aspect-tall";
|
||||||
|
} else {
|
||||||
|
return "w-full aspect-video";
|
||||||
|
}
|
||||||
|
}, [mainCameraAspect]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={contentRef} className="relative size-full">
|
<div ref={contentRef} className="relative size-full">
|
||||||
<Button
|
<Button
|
||||||
@ -197,13 +210,15 @@ export function DesktopRecordingView({
|
|||||||
|
|
||||||
<div className="flex h-full justify-center overflow-hidden">
|
<div className="flex h-full justify-center overflow-hidden">
|
||||||
<div className="flex flex-1 flex-wrap">
|
<div className="flex flex-1 flex-wrap">
|
||||||
<div className="w-full flex flex-col h-full px-2 justify-center items-center">
|
<div
|
||||||
|
className={`size-full flex px-2 items-center ${mainCameraAspect == "tall" ? "flex-row justify-evenly" : "flex-col justify-center"}`}
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
key={mainCamera}
|
key={mainCamera}
|
||||||
className="w-[82%] flex justify-center items mb-5"
|
className={`flex justify-center items mb-5 ${mainCameraAspect == "tall" ? "h-[96%]" : "w-[82%]"}`}
|
||||||
>
|
>
|
||||||
<DynamicVideoPlayer
|
<DynamicVideoPlayer
|
||||||
className={`w-full ${grow}`}
|
className={grow}
|
||||||
camera={mainCamera}
|
camera={mainCamera}
|
||||||
timeRange={currentTimeRange}
|
timeRange={currentTimeRange}
|
||||||
cameraPreviews={allPreviews ?? []}
|
cameraPreviews={allPreviews ?? []}
|
||||||
@ -222,13 +237,17 @@ export function DesktopRecordingView({
|
|||||||
isScrubbing={scrubbing}
|
isScrubbing={scrubbing}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full flex justify-center gap-2 overflow-x-auto">
|
<div
|
||||||
|
className={`flex justify-center gap-2 ${mainCameraAspect == "tall" ? "h-full flex-col overflow-y-auto items-center" : "w-full overflow-x-auto"}`}
|
||||||
|
>
|
||||||
{allCameras.map((cam) => {
|
{allCameras.map((cam) => {
|
||||||
if (cam !== mainCamera) {
|
if (cam !== mainCamera) {
|
||||||
return (
|
return (
|
||||||
<div key={cam}>
|
<div key={cam}>
|
||||||
<PreviewPlayer
|
<PreviewPlayer
|
||||||
className="size-full"
|
className={
|
||||||
|
mainCameraAspect == "tall" ? "" : "size-full"
|
||||||
|
}
|
||||||
camera={cam}
|
camera={cam}
|
||||||
timeRange={currentTimeRange}
|
timeRange={currentTimeRange}
|
||||||
cameraPreviews={allPreviews ?? []}
|
cameraPreviews={allPreviews ?? []}
|
||||||
|
Loading…
Reference in New Issue
Block a user