Browse Source

the start detaching video state from react

Co-authored-by: James Hawkins <jhawki2005@gmail.com>
pull/138/head
Jelle van Snik 3 years ago
parent
commit
6ca3196b75
  1. 19
      src/video/components/VideoPlayerBase.tsx
  2. 0
      src/video/components/__old/DecoratedVideoPlayer.tsx
  3. 0
      src/video/components/__old/VideoContext.tsx
  4. 4
      src/video/components/__old/VideoPlayer.tsx
  5. 0
      src/video/components/__old/controls/AirplayControl.tsx
  6. 0
      src/video/components/__old/controls/BackdropControl.tsx
  7. 0
      src/video/components/__old/controls/ChromeCastControl.tsx
  8. 0
      src/video/components/__old/controls/FullscreenControl.tsx
  9. 0
      src/video/components/__old/controls/LoadingControl.tsx
  10. 0
      src/video/components/__old/controls/MiddlePauseControl.tsx
  11. 0
      src/video/components/__old/controls/MobileCenterControl.tsx
  12. 0
      src/video/components/__old/controls/PageTitleControl.tsx
  13. 0
      src/video/components/__old/controls/PauseControl.tsx
  14. 0
      src/video/components/__old/controls/ProgressControl.tsx
  15. 0
      src/video/components/__old/controls/ProgressListenerControl.tsx
  16. 0
      src/video/components/__old/controls/QualityDisplayControl.tsx
  17. 0
      src/video/components/__old/controls/SeriesSelectionControl.tsx
  18. 0
      src/video/components/__old/controls/ShowControl.tsx
  19. 0
      src/video/components/__old/controls/ShowTitleControl.tsx
  20. 0
      src/video/components/__old/controls/SkipTime.tsx
  21. 0
      src/video/components/__old/controls/SourceControl.tsx
  22. 0
      src/video/components/__old/controls/SourceSelectionControl.tsx
  23. 0
      src/video/components/__old/controls/TimeControl.tsx
  24. 0
      src/video/components/__old/controls/VolumeControl.tsx
  25. 0
      src/video/components/__old/hooks/controlVideo.ts
  26. 0
      src/video/components/__old/hooks/useCurrentSeriesEpisodeInfo.ts
  27. 0
      src/video/components/__old/hooks/useVideoPlayer.ts
  28. 0
      src/video/components/__old/hooks/utils.ts
  29. 0
      src/video/components/__old/hooks/volumeStore.ts
  30. 0
      src/video/components/__old/parts/VideoErrorBoundary.tsx
  31. 0
      src/video/components/__old/parts/VideoPlayerError.tsx
  32. 0
      src/video/components/__old/parts/VideoPlayerHeader.tsx
  33. 0
      src/video/components/__old/parts/VideoPlayerIconButton.tsx
  34. 0
      src/video/components/__old/parts/VideoPopout.tsx
  35. 9
      src/video/state/cache.ts
  36. 28
      src/video/state/events.ts
  37. 36
      src/video/state/hooks.tsx
  38. 44
      src/video/state/init.ts
  39. 7
      src/video/state/providers/providerTypes.ts
  40. 40
      src/video/state/providers/videoStateProvider.ts
  41. 36
      src/video/state/types.ts
  42. 2
      src/views/media/MediaView.tsx

19
src/video/components/VideoPlayerBase.tsx

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
import { VideoPlayerContextProvider } from "../state/hooks";
export interface VideoPlayerProps {
children?: React.ReactNode;
}
export function VideoPlayer(props: VideoPlayerProps) {
// TODO error boundary
// TODO move error boundary to only decorated, <VideoPlayer /> shouldn't have styling
// TODO internal controls
return (
<VideoPlayerContextProvider>
<div className="is-video-player relative h-full w-full select-none overflow-hidden bg-black [border-left:env(safe-area-inset-left)_solid_transparent] [border-right:env(safe-area-inset-right)_solid_transparent]">
<div className="absolute inset-0">{props.children}</div>
</div>
</VideoPlayerContextProvider>
);
}

0
src/components/video/DecoratedVideoPlayer.tsx → src/video/components/__old/DecoratedVideoPlayer.tsx

0
src/components/video/VideoContext.tsx → src/video/components/__old/VideoContext.tsx

4
src/components/video/VideoPlayer.tsx → src/video/components/__old/VideoPlayer.tsx

@ -1,12 +1,12 @@ @@ -1,12 +1,12 @@
import { useGoBack } from "@/hooks/useGoBack";
import { useVolumeControl } from "@/hooks/useVolumeToggle";
import { forwardRef, useContext, useEffect, useRef } from "react";
import { VideoErrorBoundary } from "./parts/VideoErrorBoundary";
import { VideoErrorBoundary } from "../../components/video/parts/VideoErrorBoundary";
import {
useVideoPlayerState,
VideoPlayerContext,
VideoPlayerContextProvider,
} from "./VideoContext";
} from "../../video/components./../components/video/VideoContext";
export interface VideoPlayerProps {
autoPlay?: boolean;

0
src/components/video/controls/AirplayControl.tsx → src/video/components/__old/controls/AirplayControl.tsx

0
src/components/video/controls/BackdropControl.tsx → src/video/components/__old/controls/BackdropControl.tsx

0
src/components/video/controls/ChromeCastControl.tsx → src/video/components/__old/controls/ChromeCastControl.tsx

0
src/components/video/controls/FullscreenControl.tsx → src/video/components/__old/controls/FullscreenControl.tsx

0
src/components/video/controls/LoadingControl.tsx → src/video/components/__old/controls/LoadingControl.tsx

0
src/components/video/controls/MiddlePauseControl.tsx → src/video/components/__old/controls/MiddlePauseControl.tsx

0
src/components/video/controls/MobileCenterControl.tsx → src/video/components/__old/controls/MobileCenterControl.tsx

0
src/components/video/controls/PageTitleControl.tsx → src/video/components/__old/controls/PageTitleControl.tsx

0
src/components/video/controls/PauseControl.tsx → src/video/components/__old/controls/PauseControl.tsx

0
src/components/video/controls/ProgressControl.tsx → src/video/components/__old/controls/ProgressControl.tsx

0
src/components/video/controls/ProgressListenerControl.tsx → src/video/components/__old/controls/ProgressListenerControl.tsx

0
src/components/video/controls/QualityDisplayControl.tsx → src/video/components/__old/controls/QualityDisplayControl.tsx

0
src/components/video/controls/SeriesSelectionControl.tsx → src/video/components/__old/controls/SeriesSelectionControl.tsx

0
src/components/video/controls/ShowControl.tsx → src/video/components/__old/controls/ShowControl.tsx

0
src/components/video/controls/ShowTitleControl.tsx → src/video/components/__old/controls/ShowTitleControl.tsx

0
src/components/video/controls/SkipTime.tsx → src/video/components/__old/controls/SkipTime.tsx

0
src/components/video/controls/SourceControl.tsx → src/video/components/__old/controls/SourceControl.tsx

0
src/components/video/controls/SourceSelectionControl.tsx → src/video/components/__old/controls/SourceSelectionControl.tsx

0
src/components/video/controls/TimeControl.tsx → src/video/components/__old/controls/TimeControl.tsx

0
src/components/video/controls/VolumeControl.tsx → src/video/components/__old/controls/VolumeControl.tsx

0
src/components/video/hooks/controlVideo.ts → src/video/components/__old/hooks/controlVideo.ts

0
src/components/video/hooks/useCurrentSeriesEpisodeInfo.ts → src/video/components/__old/hooks/useCurrentSeriesEpisodeInfo.ts

0
src/components/video/hooks/useVideoPlayer.ts → src/video/components/__old/hooks/useVideoPlayer.ts

0
src/components/video/hooks/utils.ts → src/video/components/__old/hooks/utils.ts

0
src/components/video/hooks/volumeStore.ts → src/video/components/__old/hooks/volumeStore.ts

0
src/components/video/parts/VideoErrorBoundary.tsx → src/video/components/__old/parts/VideoErrorBoundary.tsx

0
src/components/video/parts/VideoPlayerError.tsx → src/video/components/__old/parts/VideoPlayerError.tsx

0
src/components/video/parts/VideoPlayerHeader.tsx → src/video/components/__old/parts/VideoPlayerHeader.tsx

0
src/components/video/parts/VideoPlayerIconButton.tsx → src/video/components/__old/parts/VideoPlayerIconButton.tsx

0
src/components/video/parts/VideoPopout.tsx → src/video/components/__old/parts/VideoPopout.tsx

9
src/video/state/cache.ts

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
import { VideoPlayerState } from "./types";
export const _players: Map<string, VideoPlayerState> = new Map();
export function getPlayerState(descriptor: string): VideoPlayerState {
const state = _players.get(descriptor);
if (!state) throw new Error("invalid descriptor or has been unregistered");
return state;
}

28
src/video/state/events.ts

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
export type VideoPlayerEvent = "progress";
function createEventString(id: string, event: VideoPlayerEvent): string {
return `_vid:::${id}:::${event}`;
}
export function sendEvent<T>(id: string, event: VideoPlayerEvent, data: T) {
const evObj = new CustomEvent(createEventString(id, event), {
detail: data,
});
document.dispatchEvent(evObj);
}
export function listenEvent<T>(
id: string,
event: VideoPlayerEvent,
cb: (data: T) => void
) {
document.addEventListener<any>(createEventString(id, event), cb);
}
export function unlistenEvent<T>(
id: string,
event: VideoPlayerEvent,
cb: (data: T) => void
) {
document.removeEventListener<any>(createEventString(id, event), cb);
}

36
src/video/state/hooks.tsx

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
import {
createContext,
ReactNode,
useContext,
useEffect,
useState,
} from "react";
import { registerVideoPlayer, unregisterVideoPlayer } from "./init";
const VideoPlayerContext = createContext<string>("");
export function VideoPlayerContextProvider(props: { children: ReactNode }) {
const [id, setId] = useState<string | null>(null);
useEffect(() => {
const vidId = registerVideoPlayer();
setId(vidId);
return () => {
unregisterVideoPlayer(vidId);
};
}, [setId]);
if (!id) return null;
return (
<VideoPlayerContext.Provider value={id}>
{props.children}
</VideoPlayerContext.Provider>
);
}
export function useVideoPlayerDescriptor(): string {
const id = useContext(VideoPlayerContext);
return id;
}

44
src/video/state/init.ts

@ -0,0 +1,44 @@ @@ -0,0 +1,44 @@
import { nanoid } from "nanoid";
import { _players } from "./cache";
import { VideoPlayerState } from "./types";
function initPlayer(): VideoPlayerState {
return {
isPlaying: false,
isPaused: true,
isFullscreen: false,
isFocused: false,
isLoading: false,
isSeeking: false,
isFirstLoading: true,
time: 0,
duration: 0,
volume: 0,
buffered: 0,
pausedWhenSeeking: false,
hasInitialized: false,
leftControlHovering: false,
hasPlayedOnce: false,
error: null,
popout: null,
seasonData: {
isSeries: false,
},
canAirplay: false,
};
}
export function registerVideoPlayer(): string {
const id = nanoid();
if (_players.has(id)) {
throw new Error("duplicate id");
}
_players.set(id, initPlayer());
return id;
}
export function unregisterVideoPlayer(id: string) {
if (_players.has(id)) _players.delete(id);
}

7
src/video/state/providers/providerTypes.ts

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
export type VideoPlayerStateProvider = {
pause: () => void;
play: () => void;
providerStart: () => {
destroy: () => void;
};
};

40
src/video/state/providers/videoStateProvider.ts

@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
import { getPlayerState } from "../cache";
import { VideoPlayerStateProvider } from "./providerTypes";
export function createVideoStateProvider(
descriptor: string,
player: HTMLVideoElement
): VideoPlayerStateProvider {
const state = getPlayerState(descriptor);
return {
play() {
player.play();
},
pause() {
player.pause();
},
providerStart() {
// TODO reactivity through events
const pause = () => {
state.isPaused = true;
state.isPlaying = false;
};
const playing = () => {
state.isPaused = false;
state.isPlaying = true;
state.isLoading = false;
state.hasPlayedOnce = true;
};
player.addEventListener("pause", pause);
player.addEventListener("playing", playing);
return {
destroy: () => {
player.removeEventListener("pause", pause);
player.removeEventListener("playing", playing);
},
};
},
};
}

36
src/video/state/types.ts

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
export type VideoPlayerState = {
isPlaying: boolean;
isPaused: boolean;
isSeeking: boolean;
isLoading: boolean;
isFirstLoading: boolean;
isFullscreen: boolean;
time: number;
duration: number;
volume: number;
buffered: number;
pausedWhenSeeking: boolean;
hasInitialized: boolean;
leftControlHovering: boolean;
hasPlayedOnce: boolean;
popout: string | null;
isFocused: boolean;
seasonData: {
isSeries: boolean;
current?: {
episodeId: string;
seasonId: string;
};
seasons?: {
id: string;
number: number;
title: string;
episodes?: { id: string; number: number; title: string }[];
}[];
};
error: null | {
name: string;
description: string;
};
canAirplay: boolean;
};

2
src/views/media/MediaView.tsx

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
import { useHistory, useParams } from "react-router-dom";
import { Helmet } from "react-helmet";
import { useEffect, useRef, useState } from "react";
import { DecoratedVideoPlayer } from "@/components/video/DecoratedVideoPlayer";
import { DecoratedVideoPlayer } from "@/video/components/__old/DecoratedVideoPlayer";
import { MWStream } from "@/backend/helpers/streams";
import { SelectedMediaData, useScrape } from "@/hooks/useScrape";
import { VideoPlayerHeader } from "@/components/video/parts/VideoPlayerHeader";

Loading…
Cancel
Save