mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-07 11:45:24 +03:00
Handle autoplay for mobile
This commit is contained in:
parent
08e9120db5
commit
7e13501787
@ -22,11 +22,13 @@ import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
|
|||||||
type HistoryCardProps = {
|
type HistoryCardProps = {
|
||||||
timeline: Card;
|
timeline: Card;
|
||||||
relevantPreview?: Preview;
|
relevantPreview?: Preview;
|
||||||
|
shouldAutoPlay: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function HistoryCard({
|
export default function HistoryCard({
|
||||||
relevantPreview,
|
relevantPreview,
|
||||||
timeline,
|
timeline,
|
||||||
|
shouldAutoPlay,
|
||||||
}: HistoryCardProps) {
|
}: HistoryCardProps) {
|
||||||
const { data: config } = useSWR<FrigateConfig>("config");
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
|
|
||||||
@ -41,6 +43,7 @@ export default function HistoryCard({
|
|||||||
relevantPreview={relevantPreview}
|
relevantPreview={relevantPreview}
|
||||||
startTs={Object.values(timeline.entries)[0].timestamp}
|
startTs={Object.values(timeline.entries)[0].timestamp}
|
||||||
eventId={Object.values(timeline.entries)[0].source_id}
|
eventId={Object.values(timeline.entries)[0].source_id}
|
||||||
|
shouldAutoPlay={shouldAutoPlay}
|
||||||
/>
|
/>
|
||||||
<div className="p-2">
|
<div className="p-2">
|
||||||
<div className="text-sm flex">
|
<div className="text-sm flex">
|
||||||
|
|||||||
@ -11,6 +11,7 @@ type PreviewPlayerProps = {
|
|||||||
relevantPreview?: Preview;
|
relevantPreview?: Preview;
|
||||||
startTs: number;
|
startTs: number;
|
||||||
eventId: string;
|
eventId: string;
|
||||||
|
shouldAutoPlay: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Preview = {
|
type Preview = {
|
||||||
@ -26,12 +27,13 @@ export default function PreviewThumbnailPlayer({
|
|||||||
relevantPreview,
|
relevantPreview,
|
||||||
startTs,
|
startTs,
|
||||||
eventId,
|
eventId,
|
||||||
|
shouldAutoPlay,
|
||||||
}: PreviewPlayerProps) {
|
}: PreviewPlayerProps) {
|
||||||
const { data: config } = useSWR("config");
|
const { data: config } = useSWR("config");
|
||||||
const playerRef = useRef<Player | null>(null);
|
const playerRef = useRef<Player | null>(null);
|
||||||
const apiHost = useApiHost();
|
const apiHost = useApiHost();
|
||||||
|
|
||||||
const onHover = useCallback(
|
const onPlayback = useCallback(
|
||||||
(isHovered: Boolean) => {
|
(isHovered: Boolean) => {
|
||||||
if (!relevantPreview || !playerRef.current) {
|
if (!relevantPreview || !playerRef.current) {
|
||||||
return;
|
return;
|
||||||
@ -47,6 +49,32 @@ export default function PreviewThumbnailPlayer({
|
|||||||
[relevantPreview, startTs]
|
[relevantPreview, startTs]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const observer = useRef<IntersectionObserver | null>();
|
||||||
|
const inViewRef = useCallback(
|
||||||
|
(node: HTMLElement | null) => {
|
||||||
|
if (!shouldAutoPlay || observer.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
observer.current = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
if (entries[0].isIntersecting) {
|
||||||
|
onPlayback(true);
|
||||||
|
} else {
|
||||||
|
onPlayback(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ threshold: 1.0 }
|
||||||
|
);
|
||||||
|
if (node) observer.current.observe(node);
|
||||||
|
} catch (e) {
|
||||||
|
// no op
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[observer, onPlayback]
|
||||||
|
);
|
||||||
|
|
||||||
if (!relevantPreview) {
|
if (!relevantPreview) {
|
||||||
if (isCurrentHour(startTs)) {
|
if (isCurrentHour(startTs)) {
|
||||||
return (
|
return (
|
||||||
@ -56,6 +84,7 @@ export default function PreviewThumbnailPlayer({
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
className={`${getPreviewWidth(camera, config)}`}
|
className={`${getPreviewWidth(camera, config)}`}
|
||||||
|
loading="lazy"
|
||||||
src={`${apiHost}api/preview/${camera}/${startTs}/thumbnail.jpg`}
|
src={`${apiHost}api/preview/${camera}/${startTs}/thumbnail.jpg`}
|
||||||
/>
|
/>
|
||||||
</AspectRatio>
|
</AspectRatio>
|
||||||
@ -69,6 +98,7 @@ export default function PreviewThumbnailPlayer({
|
|||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
className="w-[160px]"
|
className="w-[160px]"
|
||||||
|
loading="lazy"
|
||||||
src={`${apiHost}api/events/${eventId}/thumbnail.jpg`}
|
src={`${apiHost}api/events/${eventId}/thumbnail.jpg`}
|
||||||
/>
|
/>
|
||||||
</AspectRatio>
|
</AspectRatio>
|
||||||
@ -77,10 +107,11 @@ export default function PreviewThumbnailPlayer({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<AspectRatio
|
<AspectRatio
|
||||||
|
ref={shouldAutoPlay ? inViewRef : null}
|
||||||
ratio={16 / 9}
|
ratio={16 / 9}
|
||||||
className="bg-black flex justify-center items-center"
|
className="bg-black flex justify-center items-center"
|
||||||
onMouseEnter={() => onHover(true)}
|
onMouseEnter={() => onPlayback(true)}
|
||||||
onMouseLeave={() => onHover(false)}
|
onMouseLeave={() => onPlayback(false)}
|
||||||
>
|
>
|
||||||
<div className={`${getPreviewWidth(camera, config)}`}>
|
<div className={`${getPreviewWidth(camera, config)}`}>
|
||||||
<VideoPlayer
|
<VideoPlayer
|
||||||
|
|||||||
@ -44,7 +44,7 @@ export default function VideoPlayer({ children, options, seekOptions = {forward:
|
|||||||
videoElement.classList.add('small-player');
|
videoElement.classList.add('small-player');
|
||||||
videoElement.classList.add('video-js');
|
videoElement.classList.add('video-js');
|
||||||
videoElement.classList.add('vjs-default-skin');
|
videoElement.classList.add('vjs-default-skin');
|
||||||
videoRef.current.appendChild(videoElement);
|
videoRef.current?.appendChild(videoElement);
|
||||||
|
|
||||||
const player = playerRef.current = videojs(videoElement, { ...defaultOptions, ...options }, () => {
|
const player = playerRef.current = videojs(videoElement, { ...defaultOptions, ...options }, () => {
|
||||||
onReady && onReady(player);
|
onReady && onReady(player);
|
||||||
|
|||||||
@ -5,11 +5,10 @@ import { FrigateConfig } from "@/types/frigateConfig";
|
|||||||
import Heading from "@/components/ui/heading";
|
import Heading from "@/components/ui/heading";
|
||||||
import ActivityIndicator from "@/components/ui/activity-indicator";
|
import ActivityIndicator from "@/components/ui/activity-indicator";
|
||||||
import HistoryCard from "@/components/card/HistoryCard";
|
import HistoryCard from "@/components/card/HistoryCard";
|
||||||
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
|
|
||||||
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
|
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
||||||
const API_LIMIT = 200;
|
const API_LIMIT = 100;
|
||||||
|
|
||||||
function History() {
|
function History() {
|
||||||
const { data: config } = useSWR<FrigateConfig>("config");
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
@ -26,11 +25,15 @@ function History() {
|
|||||||
const getKey = useCallback((index: number, prevData: HourlyTimeline) => {
|
const getKey = useCallback((index: number, prevData: HourlyTimeline) => {
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
const lastDate = prevData.end;
|
const lastDate = prevData.end;
|
||||||
const pagedParams = { before: lastDate, timezone };
|
const pagedParams = { before: lastDate, timezone, limit: API_LIMIT };
|
||||||
return ["timeline/hourly", pagedParams];
|
return ["timeline/hourly", pagedParams];
|
||||||
}
|
}
|
||||||
|
|
||||||
return ["timeline/hourly", { timezone }];
|
return ["timeline/hourly", { timezone, limit: API_LIMIT }];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const shouldAutoPlay = useMemo(() => {
|
||||||
|
return window.innerWidth < 480;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -207,6 +210,7 @@ function History() {
|
|||||||
<HistoryCard
|
<HistoryCard
|
||||||
key={key}
|
key={key}
|
||||||
timeline={timeline}
|
timeline={timeline}
|
||||||
|
shouldAutoPlay={shouldAutoPlay}
|
||||||
relevantPreview={Object.values(
|
relevantPreview={Object.values(
|
||||||
allPreviews || []
|
allPreviews || []
|
||||||
).find(
|
).find(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user