diff --git a/src/video/components/VideoPlayerBase.tsx b/src/video/components/VideoPlayerBase.tsx
new file mode 100644
index 00000000..b151250b
--- /dev/null
+++ b/src/video/components/VideoPlayerBase.tsx
@@ -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, shouldn't have styling
+ // TODO internal controls
+
+ return (
+
+
+
+ );
+}
diff --git a/src/components/video/DecoratedVideoPlayer.tsx b/src/video/components/__old/DecoratedVideoPlayer.tsx
similarity index 100%
rename from src/components/video/DecoratedVideoPlayer.tsx
rename to src/video/components/__old/DecoratedVideoPlayer.tsx
diff --git a/src/components/video/VideoContext.tsx b/src/video/components/__old/VideoContext.tsx
similarity index 100%
rename from src/components/video/VideoContext.tsx
rename to src/video/components/__old/VideoContext.tsx
diff --git a/src/components/video/VideoPlayer.tsx b/src/video/components/__old/VideoPlayer.tsx
similarity index 96%
rename from src/components/video/VideoPlayer.tsx
rename to src/video/components/__old/VideoPlayer.tsx
index 3e064aa1..b16416d5 100644
--- a/src/components/video/VideoPlayer.tsx
+++ b/src/video/components/__old/VideoPlayer.tsx
@@ -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;
diff --git a/src/components/video/controls/AirplayControl.tsx b/src/video/components/__old/controls/AirplayControl.tsx
similarity index 100%
rename from src/components/video/controls/AirplayControl.tsx
rename to src/video/components/__old/controls/AirplayControl.tsx
diff --git a/src/components/video/controls/BackdropControl.tsx b/src/video/components/__old/controls/BackdropControl.tsx
similarity index 100%
rename from src/components/video/controls/BackdropControl.tsx
rename to src/video/components/__old/controls/BackdropControl.tsx
diff --git a/src/components/video/controls/ChromeCastControl.tsx b/src/video/components/__old/controls/ChromeCastControl.tsx
similarity index 100%
rename from src/components/video/controls/ChromeCastControl.tsx
rename to src/video/components/__old/controls/ChromeCastControl.tsx
diff --git a/src/components/video/controls/FullscreenControl.tsx b/src/video/components/__old/controls/FullscreenControl.tsx
similarity index 100%
rename from src/components/video/controls/FullscreenControl.tsx
rename to src/video/components/__old/controls/FullscreenControl.tsx
diff --git a/src/components/video/controls/LoadingControl.tsx b/src/video/components/__old/controls/LoadingControl.tsx
similarity index 100%
rename from src/components/video/controls/LoadingControl.tsx
rename to src/video/components/__old/controls/LoadingControl.tsx
diff --git a/src/components/video/controls/MiddlePauseControl.tsx b/src/video/components/__old/controls/MiddlePauseControl.tsx
similarity index 100%
rename from src/components/video/controls/MiddlePauseControl.tsx
rename to src/video/components/__old/controls/MiddlePauseControl.tsx
diff --git a/src/components/video/controls/MobileCenterControl.tsx b/src/video/components/__old/controls/MobileCenterControl.tsx
similarity index 100%
rename from src/components/video/controls/MobileCenterControl.tsx
rename to src/video/components/__old/controls/MobileCenterControl.tsx
diff --git a/src/components/video/controls/PageTitleControl.tsx b/src/video/components/__old/controls/PageTitleControl.tsx
similarity index 100%
rename from src/components/video/controls/PageTitleControl.tsx
rename to src/video/components/__old/controls/PageTitleControl.tsx
diff --git a/src/components/video/controls/PauseControl.tsx b/src/video/components/__old/controls/PauseControl.tsx
similarity index 100%
rename from src/components/video/controls/PauseControl.tsx
rename to src/video/components/__old/controls/PauseControl.tsx
diff --git a/src/components/video/controls/ProgressControl.tsx b/src/video/components/__old/controls/ProgressControl.tsx
similarity index 100%
rename from src/components/video/controls/ProgressControl.tsx
rename to src/video/components/__old/controls/ProgressControl.tsx
diff --git a/src/components/video/controls/ProgressListenerControl.tsx b/src/video/components/__old/controls/ProgressListenerControl.tsx
similarity index 100%
rename from src/components/video/controls/ProgressListenerControl.tsx
rename to src/video/components/__old/controls/ProgressListenerControl.tsx
diff --git a/src/components/video/controls/QualityDisplayControl.tsx b/src/video/components/__old/controls/QualityDisplayControl.tsx
similarity index 100%
rename from src/components/video/controls/QualityDisplayControl.tsx
rename to src/video/components/__old/controls/QualityDisplayControl.tsx
diff --git a/src/components/video/controls/SeriesSelectionControl.tsx b/src/video/components/__old/controls/SeriesSelectionControl.tsx
similarity index 100%
rename from src/components/video/controls/SeriesSelectionControl.tsx
rename to src/video/components/__old/controls/SeriesSelectionControl.tsx
diff --git a/src/components/video/controls/ShowControl.tsx b/src/video/components/__old/controls/ShowControl.tsx
similarity index 100%
rename from src/components/video/controls/ShowControl.tsx
rename to src/video/components/__old/controls/ShowControl.tsx
diff --git a/src/components/video/controls/ShowTitleControl.tsx b/src/video/components/__old/controls/ShowTitleControl.tsx
similarity index 100%
rename from src/components/video/controls/ShowTitleControl.tsx
rename to src/video/components/__old/controls/ShowTitleControl.tsx
diff --git a/src/components/video/controls/SkipTime.tsx b/src/video/components/__old/controls/SkipTime.tsx
similarity index 100%
rename from src/components/video/controls/SkipTime.tsx
rename to src/video/components/__old/controls/SkipTime.tsx
diff --git a/src/components/video/controls/SourceControl.tsx b/src/video/components/__old/controls/SourceControl.tsx
similarity index 100%
rename from src/components/video/controls/SourceControl.tsx
rename to src/video/components/__old/controls/SourceControl.tsx
diff --git a/src/components/video/controls/SourceSelectionControl.tsx b/src/video/components/__old/controls/SourceSelectionControl.tsx
similarity index 100%
rename from src/components/video/controls/SourceSelectionControl.tsx
rename to src/video/components/__old/controls/SourceSelectionControl.tsx
diff --git a/src/components/video/controls/TimeControl.tsx b/src/video/components/__old/controls/TimeControl.tsx
similarity index 100%
rename from src/components/video/controls/TimeControl.tsx
rename to src/video/components/__old/controls/TimeControl.tsx
diff --git a/src/components/video/controls/VolumeControl.tsx b/src/video/components/__old/controls/VolumeControl.tsx
similarity index 100%
rename from src/components/video/controls/VolumeControl.tsx
rename to src/video/components/__old/controls/VolumeControl.tsx
diff --git a/src/components/video/hooks/controlVideo.ts b/src/video/components/__old/hooks/controlVideo.ts
similarity index 100%
rename from src/components/video/hooks/controlVideo.ts
rename to src/video/components/__old/hooks/controlVideo.ts
diff --git a/src/components/video/hooks/useCurrentSeriesEpisodeInfo.ts b/src/video/components/__old/hooks/useCurrentSeriesEpisodeInfo.ts
similarity index 100%
rename from src/components/video/hooks/useCurrentSeriesEpisodeInfo.ts
rename to src/video/components/__old/hooks/useCurrentSeriesEpisodeInfo.ts
diff --git a/src/components/video/hooks/useVideoPlayer.ts b/src/video/components/__old/hooks/useVideoPlayer.ts
similarity index 100%
rename from src/components/video/hooks/useVideoPlayer.ts
rename to src/video/components/__old/hooks/useVideoPlayer.ts
diff --git a/src/components/video/hooks/utils.ts b/src/video/components/__old/hooks/utils.ts
similarity index 100%
rename from src/components/video/hooks/utils.ts
rename to src/video/components/__old/hooks/utils.ts
diff --git a/src/components/video/hooks/volumeStore.ts b/src/video/components/__old/hooks/volumeStore.ts
similarity index 100%
rename from src/components/video/hooks/volumeStore.ts
rename to src/video/components/__old/hooks/volumeStore.ts
diff --git a/src/components/video/parts/VideoErrorBoundary.tsx b/src/video/components/__old/parts/VideoErrorBoundary.tsx
similarity index 100%
rename from src/components/video/parts/VideoErrorBoundary.tsx
rename to src/video/components/__old/parts/VideoErrorBoundary.tsx
diff --git a/src/components/video/parts/VideoPlayerError.tsx b/src/video/components/__old/parts/VideoPlayerError.tsx
similarity index 100%
rename from src/components/video/parts/VideoPlayerError.tsx
rename to src/video/components/__old/parts/VideoPlayerError.tsx
diff --git a/src/components/video/parts/VideoPlayerHeader.tsx b/src/video/components/__old/parts/VideoPlayerHeader.tsx
similarity index 100%
rename from src/components/video/parts/VideoPlayerHeader.tsx
rename to src/video/components/__old/parts/VideoPlayerHeader.tsx
diff --git a/src/components/video/parts/VideoPlayerIconButton.tsx b/src/video/components/__old/parts/VideoPlayerIconButton.tsx
similarity index 100%
rename from src/components/video/parts/VideoPlayerIconButton.tsx
rename to src/video/components/__old/parts/VideoPlayerIconButton.tsx
diff --git a/src/components/video/parts/VideoPopout.tsx b/src/video/components/__old/parts/VideoPopout.tsx
similarity index 100%
rename from src/components/video/parts/VideoPopout.tsx
rename to src/video/components/__old/parts/VideoPopout.tsx
diff --git a/src/video/state/cache.ts b/src/video/state/cache.ts
new file mode 100644
index 00000000..4c5becb1
--- /dev/null
+++ b/src/video/state/cache.ts
@@ -0,0 +1,9 @@
+import { VideoPlayerState } from "./types";
+
+export const _players: Map = 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;
+}
diff --git a/src/video/state/events.ts b/src/video/state/events.ts
new file mode 100644
index 00000000..08794991
--- /dev/null
+++ b/src/video/state/events.ts
@@ -0,0 +1,28 @@
+export type VideoPlayerEvent = "progress";
+
+function createEventString(id: string, event: VideoPlayerEvent): string {
+ return `_vid:::${id}:::${event}`;
+}
+
+export function sendEvent(id: string, event: VideoPlayerEvent, data: T) {
+ const evObj = new CustomEvent(createEventString(id, event), {
+ detail: data,
+ });
+ document.dispatchEvent(evObj);
+}
+
+export function listenEvent(
+ id: string,
+ event: VideoPlayerEvent,
+ cb: (data: T) => void
+) {
+ document.addEventListener(createEventString(id, event), cb);
+}
+
+export function unlistenEvent(
+ id: string,
+ event: VideoPlayerEvent,
+ cb: (data: T) => void
+) {
+ document.removeEventListener(createEventString(id, event), cb);
+}
diff --git a/src/video/state/hooks.tsx b/src/video/state/hooks.tsx
new file mode 100644
index 00000000..c320d81b
--- /dev/null
+++ b/src/video/state/hooks.tsx
@@ -0,0 +1,36 @@
+import {
+ createContext,
+ ReactNode,
+ useContext,
+ useEffect,
+ useState,
+} from "react";
+import { registerVideoPlayer, unregisterVideoPlayer } from "./init";
+
+const VideoPlayerContext = createContext("");
+
+export function VideoPlayerContextProvider(props: { children: ReactNode }) {
+ const [id, setId] = useState(null);
+
+ useEffect(() => {
+ const vidId = registerVideoPlayer();
+ setId(vidId);
+
+ return () => {
+ unregisterVideoPlayer(vidId);
+ };
+ }, [setId]);
+
+ if (!id) return null;
+
+ return (
+
+ {props.children}
+
+ );
+}
+
+export function useVideoPlayerDescriptor(): string {
+ const id = useContext(VideoPlayerContext);
+ return id;
+}
diff --git a/src/video/state/init.ts b/src/video/state/init.ts
new file mode 100644
index 00000000..afe672f9
--- /dev/null
+++ b/src/video/state/init.ts
@@ -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);
+}
diff --git a/src/video/state/providers/providerTypes.ts b/src/video/state/providers/providerTypes.ts
new file mode 100644
index 00000000..8f5d4795
--- /dev/null
+++ b/src/video/state/providers/providerTypes.ts
@@ -0,0 +1,7 @@
+export type VideoPlayerStateProvider = {
+ pause: () => void;
+ play: () => void;
+ providerStart: () => {
+ destroy: () => void;
+ };
+};
diff --git a/src/video/state/providers/videoStateProvider.ts b/src/video/state/providers/videoStateProvider.ts
new file mode 100644
index 00000000..7104ad65
--- /dev/null
+++ b/src/video/state/providers/videoStateProvider.ts
@@ -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);
+ },
+ };
+ },
+ };
+}
diff --git a/src/video/state/types.ts b/src/video/state/types.ts
new file mode 100644
index 00000000..f7ec3893
--- /dev/null
+++ b/src/video/state/types.ts
@@ -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;
+};
diff --git a/src/views/media/MediaView.tsx b/src/views/media/MediaView.tsx
index d9f36da9..dda6242e 100644
--- a/src/views/media/MediaView.tsx
+++ b/src/views/media/MediaView.tsx
@@ -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";