mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-29 16:07:40 +01:00
start of a player thingy
This commit is contained in:
parent
4bc8106cb3
commit
852e7270d2
10 changed files with 90 additions and 24 deletions
|
@ -54,6 +54,7 @@ export default function ThumbnailGeneratorInternal() {
|
|||
const descriptor = useVideoPlayerDescriptor();
|
||||
const source = useSource(descriptor);
|
||||
|
||||
// TODO fix memory leak
|
||||
const videoRef = useRef<HTMLVideoElement>(document.createElement("video"));
|
||||
const canvasRef = useRef<HTMLCanvasElement>(document.createElement("canvas"));
|
||||
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 * as Atoms from "./atoms/index";
|
||||
export
|
||||
export * from "./atoms";
|
||||
export * from "./base/Container";
|
||||
|
|
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";
|
||||
|
||||
export function VideoContainer() {
|
||||
const player = usePlayerStore();
|
||||
const videoEl = useRef<HTMLVideoElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (videoEl.current) videoEl.current.src = player.source?.url ?? "";
|
||||
}, [player.source?.url]);
|
||||
|
||||
return <video controls ref={videoEl} />;
|
||||
// should this video container show right now?
|
||||
function useShouldShow(source: SourceSliceSource | null): boolean {
|
||||
if (!source) return false;
|
||||
if (source.type !== MWStreamType.MP4) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// make video element up to par with the state
|
||||
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 interface SourceSliceSource {
|
||||
url: string;
|
||||
type: MWStreamType;
|
||||
}
|
||||
|
||||
export interface SourceSlice {
|
||||
status: PlayerStatus;
|
||||
source: {
|
||||
url: string;
|
||||
type: MWStreamType;
|
||||
} | null;
|
||||
source: SourceSliceSource | null;
|
||||
setStatus(status: PlayerStatus): void;
|
||||
setSource(url: string, type: MWStreamType): void;
|
||||
}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
import { create } from "zustand";
|
||||
|
||||
export const useVideo = create(() => ({
|
||||
|
||||
}));
|
Loading…
Reference in a new issue