start of a player thingy

This commit is contained in:
mrjvs 2023-08-15 19:55:48 +02:00
parent 4bc8106cb3
commit 852e7270d2
10 changed files with 90 additions and 24 deletions

View File

@ -54,6 +54,7 @@ export default function ThumbnailGeneratorInternal() {
const descriptor = useVideoPlayerDescriptor(); const descriptor = useVideoPlayerDescriptor();
const source = useSource(descriptor); const source = useSource(descriptor);
// TODO fix memory leak
const videoRef = useRef<HTMLVideoElement>(document.createElement("video")); const videoRef = useRef<HTMLVideoElement>(document.createElement("video"));
const canvasRef = useRef<HTMLCanvasElement>(document.createElement("canvas")); const canvasRef = useRef<HTMLCanvasElement>(document.createElement("canvas"));
const hlsRef = useRef<Hls>(new Hls()); const hlsRef = useRef<Hls>(new Hls());

View File

@ -1,2 +0,0 @@
export * from "./atoms";
export * from "./base/Container";

View File

@ -1,3 +1,2 @@
import { ReactNode } from "react"; export * from "./atoms";
export * as Atoms from "./atoms/index"; export * from "./base/Container";
export

View File

@ -0,0 +1,16 @@
# Video player component
Video player is quite a complex component, so here is a rundown of all the parts
# Composable parts
These parts can be used to build any shape of a video player.
- `/atoms`- any ui element that controls the player. (Seekbar, Pause button, quality selection, etc)
- `/base` - base components that are used to build a player. Like the main container
# internal parts
These parts are internally used, they aren't exported. Do not use them outside of player internals.
- `/display` - display interface, abstraction on how to actually play the content (e.g Video element, HLS player, Standard video element, etc)
- `/internals` - Internal components that are always rendered on every player.
- `/utils` - miscellaneous logic
- `/hooks` - hooks only used for video player
- `~/src/stores/player` - state for the video player. Should only be used by internal parts

View File

@ -0,0 +1,22 @@
import { Source } from "@/components/player/hooks/usePlayer";
type EventMap = Record<string, any>;
type EventKey<T extends EventMap> = string & keyof T;
type EventReceiver<T> = (params: T) => void;
export interface Emitter<T extends EventMap> {
on<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void;
off<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void;
emit<K extends EventKey<T>>(eventName: K, params: T[K]): void;
}
interface Listener<T extends EventMap> {
on<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void;
}
export interface DisplayInterface<Events extends EventMap>
extends Listener<Events> {
play(): void;
pause(): void;
load(source: Source): void;
}

View File

@ -1,14 +1,36 @@
import { useEffect, useRef } from "react"; import { RefObject, useEffect, useRef } from "react";
import { MWStreamType } from "@/backend/helpers/streams";
import { SourceSliceSource } from "@/stores/player/slices/source";
import { AllSlices } from "@/stores/player/slices/types";
import { usePlayerStore } from "@/stores/player/store"; import { usePlayerStore } from "@/stores/player/store";
export function VideoContainer() { // should this video container show right now?
const player = usePlayerStore(); function useShouldShow(source: SourceSliceSource | null): boolean {
const videoEl = useRef<HTMLVideoElement>(null); if (!source) return false;
if (source.type !== MWStreamType.MP4) return false;
useEffect(() => { return true;
if (videoEl.current) videoEl.current.src = player.source?.url ?? ""; }
}, [player.source?.url]);
// make video element up to par with the state
return <video controls ref={videoEl} />; function useRestoreVideo(
videoRef: RefObject<HTMLVideoElement>,
player: AllSlices
) {
useEffect(() => {
const el = videoRef.current;
const src = player.source?.url ?? "";
if (!el) return;
if (el.src !== src) el.src = src;
}, [player.source?.url, videoRef]);
}
export function VideoContainer() {
const videoEl = useRef<HTMLVideoElement>(null);
const player = usePlayerStore();
useRestoreVideo(videoEl, player);
const show = useShouldShow(player.source);
if (!show) return null;
return <video autoPlay ref={videoEl} />;
} }

View File

@ -0,0 +1,5 @@
import { Controller } from "@/stores/player/controllers/types";
function useBaseController(el: HTMLVideoElement): Controller {
return {};
}

View File

@ -0,0 +1,6 @@
export interface Controller {
pause(): void;
play(): void;
setVolume(target: number): void;
registerVideoElement(): void;
}

View File

@ -10,12 +10,14 @@ export const playerStatus = {
export type PlayerStatus = ValuesOf<typeof playerStatus>; export type PlayerStatus = ValuesOf<typeof playerStatus>;
export interface SourceSlice { export interface SourceSliceSource {
status: PlayerStatus;
source: {
url: string; url: string;
type: MWStreamType; type: MWStreamType;
} | null; }
export interface SourceSlice {
status: PlayerStatus;
source: SourceSliceSource | null;
setStatus(status: PlayerStatus): void; setStatus(status: PlayerStatus): void;
setSource(url: string, type: MWStreamType): void; setSource(url: string, type: MWStreamType): void;
} }

View File

@ -1,5 +0,0 @@
import { create } from "zustand";
export const useVideo = create(() => ({
}));