mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-20 14:37:43 +01:00
loading + time control
Co-authored-by: James Hawkins <jhawki2005@gmail.com>
This commit is contained in:
parent
44149203cb
commit
b43b8b19e4
4 changed files with 71 additions and 8 deletions
9
src/components/video/controls/LoadingControl.tsx
Normal file
9
src/components/video/controls/LoadingControl.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { useVideoPlayerState } from "../VideoContext";
|
||||
|
||||
export function LoadingControl() {
|
||||
const { videoState } = useVideoPlayerState();
|
||||
|
||||
if (!videoState.isLoading) return null;
|
||||
|
||||
return <p>Loading...</p>;
|
||||
}
|
36
src/components/video/controls/TimeControl.tsx
Normal file
36
src/components/video/controls/TimeControl.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
import { useVideoPlayerState } from "../VideoContext";
|
||||
|
||||
function durationExceedsHour(secs: number): boolean {
|
||||
return secs > 60 * 60;
|
||||
}
|
||||
|
||||
function formatSeconds(secs: number, showHours = false): string {
|
||||
let time = secs;
|
||||
const seconds = time % 60;
|
||||
|
||||
time /= 60;
|
||||
const minutes = time % 60;
|
||||
|
||||
time /= 60;
|
||||
const hours = minutes % 60;
|
||||
|
||||
const minuteString = `${Math.round(minutes)
|
||||
.toString()
|
||||
.padStart(2)}:${Math.round(seconds).toString().padStart(2, "0")}`;
|
||||
|
||||
if (!showHours) return minuteString;
|
||||
return `${Math.round(hours).toString()}:${minuteString}`;
|
||||
}
|
||||
|
||||
export function TimeControl() {
|
||||
const { videoState } = useVideoPlayerState();
|
||||
const hasHours = durationExceedsHour(videoState.duration);
|
||||
const time = formatSeconds(videoState.time, hasHours);
|
||||
const duration = formatSeconds(videoState.duration, hasHours);
|
||||
|
||||
return (
|
||||
<p>
|
||||
{time} / {duration}
|
||||
</p>
|
||||
);
|
||||
}
|
|
@ -10,6 +10,7 @@ export type PlayerState = {
|
|||
isPlaying: boolean;
|
||||
isPaused: boolean;
|
||||
isSeeking: boolean;
|
||||
isLoading: boolean;
|
||||
isFullscreen: boolean;
|
||||
time: number;
|
||||
duration: number;
|
||||
|
@ -21,6 +22,7 @@ export const initialPlayerState: PlayerState = {
|
|||
isPlaying: false,
|
||||
isPaused: true,
|
||||
isFullscreen: false,
|
||||
isLoading: false,
|
||||
isSeeking: false,
|
||||
time: 0,
|
||||
duration: 0,
|
||||
|
@ -43,16 +45,26 @@ function readState(player: HTMLVideoElement, update: SetPlayer) {
|
|||
state.duration = player.duration;
|
||||
state.volume = player.volume;
|
||||
state.buffered = handleBuffered(player.currentTime, player.buffered);
|
||||
state.isLoading = false;
|
||||
|
||||
update(state);
|
||||
}
|
||||
|
||||
function registerListeners(player: HTMLVideoElement, update: SetPlayer) {
|
||||
const pause = () => {
|
||||
update((s) => ({ ...s, isPaused: true, isPlaying: false }));
|
||||
update((s) => ({
|
||||
...s,
|
||||
isPaused: true,
|
||||
isPlaying: false,
|
||||
}));
|
||||
};
|
||||
const play = () => {
|
||||
update((s) => ({ ...s, isPaused: false, isPlaying: true }));
|
||||
const playing = () => {
|
||||
update((s) => ({
|
||||
...s,
|
||||
isPaused: false,
|
||||
isPlaying: true,
|
||||
isLoading: false,
|
||||
}));
|
||||
};
|
||||
const seeking = () => {
|
||||
update((s) => ({ ...s, isSeeking: true }));
|
||||
|
@ -60,6 +72,9 @@ function registerListeners(player: HTMLVideoElement, update: SetPlayer) {
|
|||
const seeked = () => {
|
||||
update((s) => ({ ...s, isSeeking: false }));
|
||||
};
|
||||
const waiting = () => {
|
||||
update((s) => ({ ...s, isLoading: true }));
|
||||
};
|
||||
const fullscreenchange = () => {
|
||||
update((s) => ({ ...s, isFullscreen: !!document.fullscreenElement }));
|
||||
};
|
||||
|
@ -90,7 +105,7 @@ function registerListeners(player: HTMLVideoElement, update: SetPlayer) {
|
|||
};
|
||||
|
||||
player.addEventListener("pause", pause);
|
||||
player.addEventListener("play", play);
|
||||
player.addEventListener("playing", playing);
|
||||
player.addEventListener("seeking", seeking);
|
||||
player.addEventListener("seeked", seeked);
|
||||
document.addEventListener("fullscreenchange", fullscreenchange);
|
||||
|
@ -98,10 +113,11 @@ function registerListeners(player: HTMLVideoElement, update: SetPlayer) {
|
|||
player.addEventListener("loadedmetadata", loadedmetadata);
|
||||
player.addEventListener("volumechange", volumechange);
|
||||
player.addEventListener("progress", progress);
|
||||
player.addEventListener("waiting", waiting);
|
||||
|
||||
return () => {
|
||||
player.removeEventListener("pause", pause);
|
||||
player.removeEventListener("play", play);
|
||||
player.removeEventListener("playing", playing);
|
||||
player.removeEventListener("seeking", seeking);
|
||||
player.removeEventListener("seeked", seeked);
|
||||
document.removeEventListener("fullscreenchange", fullscreenchange);
|
||||
|
@ -109,6 +125,7 @@ function registerListeners(player: HTMLVideoElement, update: SetPlayer) {
|
|||
player.removeEventListener("loadedmetadata", loadedmetadata);
|
||||
player.removeEventListener("volumechange", volumechange);
|
||||
player.removeEventListener("progress", progress);
|
||||
player.removeEventListener("waiting", waiting);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { FullscreenControl } from "@/components/video/controls/FullscreenControl";
|
||||
import { LoadingControl } from "@/components/video/controls/LoadingControl";
|
||||
import { PauseControl } from "@/components/video/controls/PauseControl";
|
||||
import { ProgressControl } from "@/components/video/controls/ProgressControl";
|
||||
import { SourceControl } from "@/components/video/controls/SourceControl";
|
||||
import { TimeControl } from "@/components/video/controls/TimeControl";
|
||||
import { VolumeControl } from "@/components/video/controls/VolumeControl";
|
||||
import { VideoPlayer } from "@/components/video/VideoPlayer";
|
||||
import { useCallback, useState } from "react";
|
||||
|
@ -9,16 +11,13 @@ import { useCallback, useState } from "react";
|
|||
// test videos: https://gist.github.com/jsturgis/3b19447b304616f18657
|
||||
|
||||
// TODO video todos:
|
||||
// - captions
|
||||
// - make pretty
|
||||
// - better seeking
|
||||
// - improve seekables
|
||||
// - buffering
|
||||
// - error handling
|
||||
// - middle pause button + click to pause
|
||||
// - improve pausing while seeking/buffering
|
||||
// - captions
|
||||
// - show formatted time
|
||||
// - IOS support: (no volume, fullscreen video element instead of wrapper)
|
||||
// - IpadOS support: (fullscreen video wrapper should work, see (lookmovie.io) )
|
||||
// - HLS support: feature detection otherwise use HLS.js
|
||||
|
@ -39,6 +38,8 @@ export function TestView() {
|
|||
<FullscreenControl />
|
||||
<ProgressControl />
|
||||
<VolumeControl />
|
||||
<LoadingControl />
|
||||
<TimeControl />
|
||||
<SourceControl source="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4" />
|
||||
</VideoPlayer>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue