mirror of
https://github.com/movie-web/movie-web.git
synced 2024-12-27 08:41:51 +01:00
start of a player thingy
This commit is contained in:
parent
4bc8106cb3
commit
852e7270d2
@ -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());
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
export * from "./atoms";
|
|
||||||
export * from "./base/Container";
|
|
@ -1,3 +1,2 @@
|
|||||||
import { ReactNode } from "react";
|
export * from "./atoms";
|
||||||
export * as Atoms from "./atoms/index";
|
export * from "./base/Container";
|
||||||
export
|
|
||||||
|
16
src/components/player/README.md
Normal file
16
src/components/player/README.md
Normal 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
|
22
src/components/player/display/displayInterface.ts
Normal file
22
src/components/player/display/displayInterface.ts
Normal 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;
|
||||||
|
}
|
@ -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} />;
|
||||||
}
|
}
|
||||||
|
5
src/stores/player/controllers/base.ts
Normal file
5
src/stores/player/controllers/base.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { Controller } from "@/stores/player/controllers/types";
|
||||||
|
|
||||||
|
function useBaseController(el: HTMLVideoElement): Controller {
|
||||||
|
return {};
|
||||||
|
}
|
6
src/stores/player/controllers/types.ts
Normal file
6
src/stores/player/controllers/types.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export interface Controller {
|
||||||
|
pause(): void;
|
||||||
|
play(): void;
|
||||||
|
setVolume(target: number): void;
|
||||||
|
registerVideoElement(): void;
|
||||||
|
}
|
@ -10,12 +10,14 @@ export const playerStatus = {
|
|||||||
|
|
||||||
export type PlayerStatus = ValuesOf<typeof playerStatus>;
|
export type PlayerStatus = ValuesOf<typeof playerStatus>;
|
||||||
|
|
||||||
|
export interface SourceSliceSource {
|
||||||
|
url: string;
|
||||||
|
type: MWStreamType;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SourceSlice {
|
export interface SourceSlice {
|
||||||
status: PlayerStatus;
|
status: PlayerStatus;
|
||||||
source: {
|
source: SourceSliceSource | null;
|
||||||
url: string;
|
|
||||||
type: MWStreamType;
|
|
||||||
} | null;
|
|
||||||
setStatus(status: PlayerStatus): void;
|
setStatus(status: PlayerStatus): void;
|
||||||
setSource(url: string, type: MWStreamType): void;
|
setSource(url: string, type: MWStreamType): void;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import { create } from "zustand";
|
|
||||||
|
|
||||||
export const useVideo = create(() => ({
|
|
||||||
|
|
||||||
}));
|
|
Loading…
Reference in New Issue
Block a user