mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-07 11:45:24 +03:00
More work on youtube view
This commit is contained in:
parent
c3ca8ebf2c
commit
82ad64143f
@ -4,21 +4,51 @@ import Logo from "../Logo";
|
|||||||
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
|
import { formatUnixTimestampToDateTime } from "@/utils/dateUtil";
|
||||||
import useSWR from "swr";
|
import useSWR from "swr";
|
||||||
import { FrigateConfig } from "@/types/frigateConfig";
|
import { FrigateConfig } from "@/types/frigateConfig";
|
||||||
|
import VideoPlayer from "../player/VideoPlayer";
|
||||||
|
import { Card } from "../ui/card";
|
||||||
|
|
||||||
type TimelineItemCardProps = {
|
type TimelineItemCardProps = {
|
||||||
timeline: Timeline;
|
timeline: Timeline;
|
||||||
relevantPreview: Preview | undefined;
|
relevantPreview: Preview | undefined;
|
||||||
|
onSelect: () => void;
|
||||||
};
|
};
|
||||||
export default function TimelineItemCard({
|
export default function TimelineItemCard({
|
||||||
timeline,
|
timeline,
|
||||||
relevantPreview,
|
relevantPreview,
|
||||||
|
onSelect,
|
||||||
}: TimelineItemCardProps) {
|
}: TimelineItemCardProps) {
|
||||||
const { data: config } = useSWR<FrigateConfig>("config");
|
const { data: config } = useSWR<FrigateConfig>("config");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative m-2 flex h-24">
|
<Card className="relative m-2 flex h-32 cursor-pointer" onClick={onSelect}>
|
||||||
<div className="w-1/2 bg-black"></div>
|
<div className="w-1/2 p-2">
|
||||||
<div className="px-1 w-1/2">
|
{relevantPreview && (
|
||||||
|
<VideoPlayer
|
||||||
|
options={{
|
||||||
|
preload: "auto",
|
||||||
|
height: "114",
|
||||||
|
width: "202",
|
||||||
|
autoplay: true,
|
||||||
|
controls: false,
|
||||||
|
fluid: false,
|
||||||
|
muted: true,
|
||||||
|
loadingSpinner: false,
|
||||||
|
sources: [
|
||||||
|
{
|
||||||
|
src: `${relevantPreview.src}`,
|
||||||
|
type: "video/mp4",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
seekOptions={{}}
|
||||||
|
onReady={(player) => {
|
||||||
|
player.pause(); // autoplay + pause is required for iOS
|
||||||
|
player.currentTime(timeline.timestamp - relevantPreview.start);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="px-2 py-1 w-1/2">
|
||||||
<div className="capitalize font-semibold text-sm">
|
<div className="capitalize font-semibold text-sm">
|
||||||
{getTimelineItemDescription(timeline)}
|
{getTimelineItemDescription(timeline)}
|
||||||
</div>
|
</div>
|
||||||
@ -31,7 +61,7 @@ export default function TimelineItemCard({
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
className="absolute bottom-0 right-0"
|
className="absolute bottom-1 right-1"
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
>
|
>
|
||||||
@ -41,6 +71,6 @@ export default function TimelineItemCard({
|
|||||||
+
|
+
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Card>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -106,14 +106,10 @@ export default function HistoryTimelineView({
|
|||||||
}, [playbackTimes]);
|
}, [playbackTimes]);
|
||||||
|
|
||||||
const onSelectItem = useCallback(
|
const onSelectItem = useCallback(
|
||||||
(data: { items: number[] }) => {
|
(timeline: Timeline | undefined) => {
|
||||||
if (data.items.length > 0) {
|
if (timeline) {
|
||||||
const selected = data.items[0];
|
setFocusedItem(timeline);
|
||||||
setFocusedItem(
|
const selected = timeline.timestamp;
|
||||||
playback.timelineItems.find(
|
|
||||||
(timeline) => timeline.timestamp == selected
|
|
||||||
)
|
|
||||||
);
|
|
||||||
playerRef.current?.pause();
|
playerRef.current?.pause();
|
||||||
|
|
||||||
let seekSeconds = 0;
|
let seekSeconds = 0;
|
||||||
@ -135,6 +131,8 @@ export default function HistoryTimelineView({
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
playerRef.current?.currentTime(seekSeconds);
|
playerRef.current?.currentTime(seekSeconds);
|
||||||
|
} else {
|
||||||
|
setFocusedItem(undefined);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[annotationOffset, recordings, playerRef]
|
[annotationOffset, recordings, playerRef]
|
||||||
@ -197,7 +195,6 @@ export default function HistoryTimelineView({
|
|||||||
hasRelevantPreview={hasRelevantPreview}
|
hasRelevantPreview={hasRelevantPreview}
|
||||||
scrubbing={scrubbing}
|
scrubbing={scrubbing}
|
||||||
focusedItem={focusedItem}
|
focusedItem={focusedItem}
|
||||||
setFocusedItem={setFocusedItem}
|
|
||||||
setSeeking={setSeeking}
|
setSeeking={setSeeking}
|
||||||
onSelectItem={onSelectItem}
|
onSelectItem={onSelectItem}
|
||||||
onScrubTime={onScrubTime}
|
onScrubTime={onScrubTime}
|
||||||
@ -218,7 +215,6 @@ export default function HistoryTimelineView({
|
|||||||
hasRelevantPreview={hasRelevantPreview}
|
hasRelevantPreview={hasRelevantPreview}
|
||||||
scrubbing={scrubbing}
|
scrubbing={scrubbing}
|
||||||
focusedItem={focusedItem}
|
focusedItem={focusedItem}
|
||||||
setFocusedItem={setFocusedItem}
|
|
||||||
setSeeking={setSeeking}
|
setSeeking={setSeeking}
|
||||||
onSelectItem={onSelectItem}
|
onSelectItem={onSelectItem}
|
||||||
onScrubTime={onScrubTime}
|
onScrubTime={onScrubTime}
|
||||||
@ -238,9 +234,8 @@ type DesktopViewProps = {
|
|||||||
hasRelevantPreview: boolean;
|
hasRelevantPreview: boolean;
|
||||||
scrubbing: boolean;
|
scrubbing: boolean;
|
||||||
focusedItem: Timeline | undefined;
|
focusedItem: Timeline | undefined;
|
||||||
setFocusedItem: (item: Timeline | undefined) => void;
|
|
||||||
setSeeking: (seeking: boolean) => void;
|
setSeeking: (seeking: boolean) => void;
|
||||||
onSelectItem: ({ items }: { items: number[] }) => void;
|
onSelectItem: (timeline: Timeline | undefined) => void;
|
||||||
onScrubTime: ({ time }: { time: Date }) => void;
|
onScrubTime: ({ time }: { time: Date }) => void;
|
||||||
onStopScrubbing: ({ time }: { time: Date }) => void;
|
onStopScrubbing: ({ time }: { time: Date }) => void;
|
||||||
};
|
};
|
||||||
@ -255,7 +250,6 @@ function DesktopView({
|
|||||||
hasRelevantPreview,
|
hasRelevantPreview,
|
||||||
scrubbing,
|
scrubbing,
|
||||||
focusedItem,
|
focusedItem,
|
||||||
setFocusedItem,
|
|
||||||
setSeeking,
|
setSeeking,
|
||||||
onSelectItem,
|
onSelectItem,
|
||||||
onScrubTime,
|
onScrubTime,
|
||||||
@ -275,7 +269,8 @@ function DesktopView({
|
|||||||
preload: "auto",
|
preload: "auto",
|
||||||
autoplay: true,
|
autoplay: true,
|
||||||
fluid: false,
|
fluid: false,
|
||||||
height: "320",
|
height: "608",
|
||||||
|
width: "1080",
|
||||||
sources: [
|
sources: [
|
||||||
{
|
{
|
||||||
src: playbackUri,
|
src: playbackUri,
|
||||||
@ -288,7 +283,7 @@ function DesktopView({
|
|||||||
playerRef.current = player;
|
playerRef.current = player;
|
||||||
player.currentTime(timelineTime - playbackTimes.start);
|
player.currentTime(timelineTime - playbackTimes.start);
|
||||||
player.on("playing", () => {
|
player.on("playing", () => {
|
||||||
setFocusedItem(undefined);
|
onSelectItem(undefined);
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
onDispose={() => {
|
onDispose={() => {
|
||||||
@ -331,13 +326,14 @@ function DesktopView({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
<div className="px-2 w-full h-80 overflow-auto">
|
<div className="px-2 w-full h-[608px] overflow-auto">
|
||||||
{playback.timelineItems.map((timeline) => {
|
{playback.timelineItems.map((timeline) => {
|
||||||
return (
|
return (
|
||||||
<TimelineItemCard
|
<TimelineItemCard
|
||||||
key={timeline.timestamp}
|
key={timeline.timestamp}
|
||||||
timeline={timeline}
|
timeline={timeline}
|
||||||
relevantPreview={playback.relevantPreview}
|
relevantPreview={playback.relevantPreview}
|
||||||
|
onSelect={() => onSelectItem(timeline)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@ -346,7 +342,7 @@ function DesktopView({
|
|||||||
<div className="m-1">
|
<div className="m-1">
|
||||||
{playback != undefined && (
|
{playback != undefined && (
|
||||||
<ActivityScrubber
|
<ActivityScrubber
|
||||||
items={timelineItemsToScrubber(playback.timelineItems)}
|
items={[]}
|
||||||
timeBars={
|
timeBars={
|
||||||
hasRelevantPreview
|
hasRelevantPreview
|
||||||
? [{ time: new Date(timelineTime * 1000), id: "playback" }]
|
? [{ time: new Date(timelineTime * 1000), id: "playback" }]
|
||||||
@ -359,7 +355,16 @@ function DesktopView({
|
|||||||
}}
|
}}
|
||||||
timechangeHandler={onScrubTime}
|
timechangeHandler={onScrubTime}
|
||||||
timechangedHandler={onStopScrubbing}
|
timechangedHandler={onStopScrubbing}
|
||||||
selectHandler={onSelectItem}
|
selectHandler={(data) => {
|
||||||
|
if (data.items.length > 0) {
|
||||||
|
const selected = data.items[0];
|
||||||
|
onSelectItem(
|
||||||
|
playback.timelineItems.find(
|
||||||
|
(timeline) => timeline.timestamp == selected
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -378,9 +383,8 @@ type MobileViewProps = {
|
|||||||
hasRelevantPreview: boolean;
|
hasRelevantPreview: boolean;
|
||||||
scrubbing: boolean;
|
scrubbing: boolean;
|
||||||
focusedItem: Timeline | undefined;
|
focusedItem: Timeline | undefined;
|
||||||
setFocusedItem: (item: Timeline | undefined) => void;
|
|
||||||
setSeeking: (seeking: boolean) => void;
|
setSeeking: (seeking: boolean) => void;
|
||||||
onSelectItem: ({ items }: { items: number[] }) => void;
|
onSelectItem: (timeline: Timeline | undefined) => void;
|
||||||
onScrubTime: ({ time }: { time: Date }) => void;
|
onScrubTime: ({ time }: { time: Date }) => void;
|
||||||
onStopScrubbing: ({ time }: { time: Date }) => void;
|
onStopScrubbing: ({ time }: { time: Date }) => void;
|
||||||
};
|
};
|
||||||
@ -395,7 +399,6 @@ function MobileView({
|
|||||||
hasRelevantPreview,
|
hasRelevantPreview,
|
||||||
scrubbing,
|
scrubbing,
|
||||||
focusedItem,
|
focusedItem,
|
||||||
setFocusedItem,
|
|
||||||
setSeeking,
|
setSeeking,
|
||||||
onSelectItem,
|
onSelectItem,
|
||||||
onScrubTime,
|
onScrubTime,
|
||||||
@ -425,7 +428,7 @@ function MobileView({
|
|||||||
playerRef.current = player;
|
playerRef.current = player;
|
||||||
player.currentTime(timelineTime - playbackTimes.start);
|
player.currentTime(timelineTime - playbackTimes.start);
|
||||||
player.on("playing", () => {
|
player.on("playing", () => {
|
||||||
setFocusedItem(undefined);
|
onSelectItem(undefined);
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
onDispose={() => {
|
onDispose={() => {
|
||||||
@ -491,7 +494,16 @@ function MobileView({
|
|||||||
}}
|
}}
|
||||||
timechangeHandler={onScrubTime}
|
timechangeHandler={onScrubTime}
|
||||||
timechangedHandler={onStopScrubbing}
|
timechangedHandler={onStopScrubbing}
|
||||||
selectHandler={onSelectItem}
|
selectHandler={(data) => {
|
||||||
|
if (data.items.length > 0) {
|
||||||
|
const selected = data.items[0];
|
||||||
|
onSelectItem(
|
||||||
|
playback.timelineItems.find(
|
||||||
|
(timeline) => timeline.timestamp == selected
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user