Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically skip to next preview / clip when watching full recordings #10055

Merged
merged 3 commits into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 42 additions & 16 deletions web/src/components/player/DynamicVideoPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,6 @@ export default function DynamicVideoPlayer({
return <ActivityIndicator />;
}

//console.log(`${config.detect.width / config.detect.height < 1.7 ? "16:9" : undefined}`)

return (
<div className={className}>
<div
Expand Down Expand Up @@ -228,7 +226,7 @@ export default function DynamicVideoPlayer({
player.on("timeupdate", () => {
controller.updateProgress(player.currentTime() || 0);
});
player.on("ended", () => controller.fireClipEndEvent());
player.on("ended", () => controller.fireClipChangeEvent("forward"));

if (onControllerReady) {
onControllerReady(controller);
Expand Down Expand Up @@ -264,6 +262,7 @@ export default function DynamicVideoPlayer({
previewRef.current = player;
player.pause();
player.on("seeked", () => controller.finishedSeeking());
player.on("loadeddata", () => controller.previewReady());
}}
onDispose={() => {
previewRef.current = undefined;
Expand All @@ -285,14 +284,16 @@ export class DynamicVideoController {
// playback
private recordings: Recording[] = [];
private onPlaybackTimestamp: ((time: number) => void) | undefined = undefined;
private onClipEnded: (() => void) | undefined = undefined;
private onClipChange: ((dir: "forward" | "backward") => void) | undefined =
undefined;
private annotationOffset: number;
private timeToStart: number | undefined = undefined;

// preview
private preview: Preview | undefined = undefined;
private timeToSeek: number | undefined = undefined;
private seeking = false;
private readyToScrub = true;

constructor(
playerRef: MutableRefObject<Player | undefined>,
Expand Down Expand Up @@ -395,30 +396,50 @@ export class DynamicVideoController {
this.onPlaybackTimestamp = listener;
}

onClipEndedEvent(listener: () => void) {
this.onClipEnded = listener;
onClipChangedEvent(listener: (dir: "forward" | "backward") => void) {
this.onClipChange = listener;
}

fireClipEndEvent() {
if (this.onClipEnded) {
this.onClipEnded();
fireClipChangeEvent(dir: "forward" | "backward") {
if (this.onClipChange) {
this.onClipChange(dir);
}
}

scrubToTimestamp(time: number) {
if (!this.preview) {
return;
}

if (!this.readyToScrub) {
return;
}

if (time > this.preview.end) {
if (this.playerMode == "scrubbing") {
this.playerMode = "playback";
this.setScrubbing(false);
this.timeToSeek = undefined;
this.seeking = false;
this.readyToScrub = false;
this.fireClipChangeEvent("forward");
}
return;
}

if (this.playerMode != "scrubbing") {
this.playerMode = "scrubbing";
this.playerRef.current?.pause();
this.setScrubbing(true);
}

if (this.preview) {
if (this.seeking) {
this.timeToSeek = time;
} else {
this.previewRef.current?.currentTime(time - this.preview.start);
this.seeking = true;
}
if (this.seeking) {
this.timeToSeek = time;
} else {
this.previewRef.current?.currentTime(
Math.max(0, time - this.preview.start)
);
this.seeking = true;
}
}

Expand All @@ -438,4 +459,9 @@ export class DynamicVideoController {
this.seeking = false;
}
}

previewReady() {
this.previewRef.current?.pause();
this.readyToScrub = true;
}
}
23 changes: 15 additions & 8 deletions web/src/views/events/DesktopRecordingView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@ export default function DesktopRecordingView({
relevantPreviews,
}: DesktopRecordingViewProps) {
const navigate = useNavigate();
const controllerRef = useRef<DynamicVideoController | undefined>(undefined);
const contentRef = useRef<HTMLDivElement | null>(null);

// controller state

const [playerReady, setPlayerReady] = useState(false);
const controllerRef = useRef<DynamicVideoController | undefined>(undefined);

// timeline time

const timeRange = useMemo(
Expand All @@ -44,12 +48,14 @@ export default function DesktopRecordingView({
return;
}

if (selectedRangeIdx < timeRange.ranges.length - 1) {
controllerRef.current.onClipEndedEvent(() => {
controllerRef.current.onClipChangedEvent((dir) => {
if (dir == "forward" && selectedRangeIdx < timeRange.ranges.length - 1) {
setSelectedRangeIdx(selectedRangeIdx + 1);
});
}
}, [controllerRef, selectedRangeIdx]);
} else if (selectedRangeIdx > 0) {
setSelectedRangeIdx(selectedRangeIdx - 1);
}
});
}, [playerReady, selectedRangeIdx]);

// scrubbing and timeline state

Expand All @@ -62,13 +68,13 @@ export default function DesktopRecordingView({
if (scrubbing) {
controllerRef.current?.scrubToTimestamp(currentTime);
}
}, [controllerRef, currentTime, scrubbing]);
}, [currentTime, scrubbing]);

useEffect(() => {
if (!scrubbing) {
controllerRef.current?.seekToTimestamp(currentTime, true);
}
}, [controllerRef, scrubbing]);
}, [scrubbing]);

return (
<div ref={contentRef} className="relative w-full h-full">
Expand All @@ -87,6 +93,7 @@ export default function DesktopRecordingView({
cameraPreviews={relevantPreviews || []}
onControllerReady={(controller) => {
controllerRef.current = controller;
setPlayerReady(true);
controllerRef.current.onPlayerTimeUpdate((timestamp: number) => {
setCurrentTime(timestamp);
});
Expand Down
Loading