mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-21 14:47:41 +01:00
episode selector
This commit is contained in:
parent
c3b409631e
commit
63be27b9ae
10 changed files with 383 additions and 37 deletions
|
@ -10,6 +10,7 @@ import { PageTitleAction } from "@/video/components/actions/PageTitleAction";
|
||||||
import { PauseAction } from "@/video/components/actions/PauseAction";
|
import { PauseAction } from "@/video/components/actions/PauseAction";
|
||||||
import { ProgressAction } from "@/video/components/actions/ProgressAction";
|
import { ProgressAction } from "@/video/components/actions/ProgressAction";
|
||||||
import { QualityDisplayAction } from "@/video/components/actions/QualityDisplayAction";
|
import { QualityDisplayAction } from "@/video/components/actions/QualityDisplayAction";
|
||||||
|
import { SeriesSelectionAction } from "@/video/components/actions/SeriesSelectionAction";
|
||||||
import { ShowTitleAction } from "@/video/components/actions/ShowTitleAction";
|
import { ShowTitleAction } from "@/video/components/actions/ShowTitleAction";
|
||||||
import { SkipTimeAction } from "@/video/components/actions/SkipTimeAction";
|
import { SkipTimeAction } from "@/video/components/actions/SkipTimeAction";
|
||||||
import { TimeAction } from "@/video/components/actions/TimeAction";
|
import { TimeAction } from "@/video/components/actions/TimeAction";
|
||||||
|
@ -118,7 +119,7 @@ export function VideoPlayer(props: Props) {
|
||||||
<div className="grid w-full grid-cols-[56px,1fr,56px] items-center">
|
<div className="grid w-full grid-cols-[56px,1fr,56px] items-center">
|
||||||
<div />
|
<div />
|
||||||
<div className="flex items-center justify-center">
|
<div className="flex items-center justify-center">
|
||||||
{/* <SeriesSelectionControl /> */}
|
<SeriesSelectionAction />
|
||||||
{/* <SourceSelectionControl media={props.media} /> */}
|
{/* <SourceSelectionControl media={props.media} /> */}
|
||||||
</div>
|
</div>
|
||||||
<FullscreenAction />
|
<FullscreenAction />
|
||||||
|
@ -128,8 +129,8 @@ export function VideoPlayer(props: Props) {
|
||||||
<LeftSideControls />
|
<LeftSideControls />
|
||||||
<div className="flex-1" />
|
<div className="flex-1" />
|
||||||
<QualityDisplayAction />
|
<QualityDisplayAction />
|
||||||
{/* <SeriesSelectionControl />
|
<SeriesSelectionAction />
|
||||||
<SourceSelectionControl media={props.media} />
|
{/* <SourceSelectionControl media={props.media} />
|
||||||
<AirplayControl />
|
<AirplayControl />
|
||||||
<ChromeCastControl /> */}
|
<ChromeCastControl /> */}
|
||||||
<FullscreenAction />
|
<FullscreenAction />
|
||||||
|
|
201
src/video/components/actions/SeriesSelectionAction.tsx
Normal file
201
src/video/components/actions/SeriesSelectionAction.tsx
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
import React, { useCallback, useMemo, useState } from "react";
|
||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
import { Icon, Icons } from "@/components/Icon";
|
||||||
|
import { useLoading } from "@/hooks/useLoading";
|
||||||
|
import { MWMediaType, MWSeasonWithEpisodeMeta } from "@/backend/metadata/types";
|
||||||
|
import { getMetaFromId } from "@/backend/metadata/getmeta";
|
||||||
|
import { decodeJWId } from "@/backend/metadata/justwatch";
|
||||||
|
import { Loading } from "@/components/layout/Loading";
|
||||||
|
import { IconPatch } from "@/components/buttons/IconPatch";
|
||||||
|
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||||
|
import { useMeta } from "@/video/state/logic/meta";
|
||||||
|
import { VideoPlayerIconButton } from "@/video/components/parts/VideoPlayerIconButton";
|
||||||
|
import { useControls } from "@/video/state/logic/controls";
|
||||||
|
import { VideoPopout } from "@/video/components/parts/VideoPopout";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function PopupSection(props: {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className={["p-4", props.className || ""].join(" ")}>
|
||||||
|
{props.children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function PopupEpisodeSelect() {
|
||||||
|
const params = useParams<{
|
||||||
|
media: string;
|
||||||
|
}>();
|
||||||
|
const descriptor = useVideoPlayerDescriptor();
|
||||||
|
const meta = useMeta(descriptor);
|
||||||
|
const controls = useControls(descriptor);
|
||||||
|
|
||||||
|
const [isPickingSeason, setIsPickingSeason] = useState<boolean>(false);
|
||||||
|
const [currentVisibleSeason, setCurrentVisibleSeason] = useState<{
|
||||||
|
seasonId: string;
|
||||||
|
season?: MWSeasonWithEpisodeMeta;
|
||||||
|
} | null>(null);
|
||||||
|
const [reqSeasonMeta, loading, error] = useLoading(
|
||||||
|
(id: string, seasonId: string) => {
|
||||||
|
return getMetaFromId(MWMediaType.SERIES, id, seasonId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const requestSeason = useCallback(
|
||||||
|
(sId: string) => {
|
||||||
|
setCurrentVisibleSeason({
|
||||||
|
seasonId: sId,
|
||||||
|
season: undefined,
|
||||||
|
});
|
||||||
|
setIsPickingSeason(false);
|
||||||
|
reqSeasonMeta(decodeJWId(params.media)?.id as string, sId).then((v) => {
|
||||||
|
if (v?.meta.type !== MWMediaType.SERIES) return;
|
||||||
|
setCurrentVisibleSeason({
|
||||||
|
seasonId: sId,
|
||||||
|
season: v?.meta.seasonData,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[reqSeasonMeta, params.media]
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentSeasonId =
|
||||||
|
currentVisibleSeason?.seasonId ?? meta?.episode?.seasonId;
|
||||||
|
|
||||||
|
const setCurrent = useCallback(
|
||||||
|
(seasonId: string, episodeId: string) => {
|
||||||
|
controls.setCurrentEpisode(seasonId, episodeId);
|
||||||
|
},
|
||||||
|
[controls]
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentSeasonInfo = useMemo(() => {
|
||||||
|
return meta?.seasons?.find((season) => season.id === currentSeasonId);
|
||||||
|
}, [meta, currentSeasonId]);
|
||||||
|
|
||||||
|
const currentSeasonEpisodes = useMemo(() => {
|
||||||
|
if (currentVisibleSeason?.season) {
|
||||||
|
return currentVisibleSeason?.season?.episodes;
|
||||||
|
}
|
||||||
|
return meta?.seasons?.find?.(
|
||||||
|
(season) => season && season.id === currentSeasonId
|
||||||
|
)?.episodes;
|
||||||
|
}, [meta, currentSeasonId, currentVisibleSeason]);
|
||||||
|
|
||||||
|
const toggleIsPickingSeason = () => {
|
||||||
|
setIsPickingSeason(!isPickingSeason);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setSeason = (id: string) => {
|
||||||
|
requestSeason(id);
|
||||||
|
setCurrentVisibleSeason({ seasonId: id });
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isPickingSeason)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PopupSection className="flex items-center space-x-3 border-b border-denim-500 font-bold text-white">
|
||||||
|
Pick a season
|
||||||
|
</PopupSection>
|
||||||
|
<PopupSection className="overflow-y-auto">
|
||||||
|
<div className="space-y-1">
|
||||||
|
{currentSeasonInfo
|
||||||
|
? meta?.seasons?.map?.((season) => (
|
||||||
|
<div
|
||||||
|
className="text-denim-800 -mx-2 flex items-center space-x-1 rounded p-2 text-white hover:bg-denim-600"
|
||||||
|
key={season.id}
|
||||||
|
onClick={() => setSeason(season.id)}
|
||||||
|
>
|
||||||
|
{season.title}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
: "No season"}
|
||||||
|
</div>
|
||||||
|
</PopupSection>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PopupSection className="flex items-center space-x-3 border-b border-denim-500 font-bold text-white">
|
||||||
|
<button
|
||||||
|
className="-m-1.5 rounded p-1.5 hover:bg-denim-600"
|
||||||
|
onClick={toggleIsPickingSeason}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<Icon icon={Icons.CHEVRON_LEFT} />
|
||||||
|
</button>
|
||||||
|
<span>{currentSeasonInfo?.title || ""}</span>
|
||||||
|
</PopupSection>
|
||||||
|
<PopupSection className="overflow-y-auto">
|
||||||
|
{loading ? (
|
||||||
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
|
<Loading />
|
||||||
|
</div>
|
||||||
|
) : error ? (
|
||||||
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
|
<div className="flex flex-col flex-wrap items-center text-slate-400">
|
||||||
|
<IconPatch
|
||||||
|
icon={Icons.EYE_SLASH}
|
||||||
|
className="text-xl text-bink-600"
|
||||||
|
/>
|
||||||
|
<p className="mt-6 w-full text-center">
|
||||||
|
Something went wrong loading the episodes for{" "}
|
||||||
|
{currentSeasonInfo?.title?.toLowerCase()}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-1">
|
||||||
|
{currentSeasonEpisodes && currentSeasonInfo
|
||||||
|
? currentSeasonEpisodes.map((e) => (
|
||||||
|
<div
|
||||||
|
className={[
|
||||||
|
"text-denim-800 -mx-2 flex items-center space-x-1 rounded p-2 text-white hover:bg-denim-600",
|
||||||
|
meta?.episode?.episodeId === e.id &&
|
||||||
|
"outline outline-2 outline-denim-700",
|
||||||
|
].join(" ")}
|
||||||
|
onClick={() => setCurrent(currentSeasonInfo.id, e.id)}
|
||||||
|
key={e.id}
|
||||||
|
>
|
||||||
|
{e.number}. {e.title}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
: "No episodes"}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</PopupSection>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SeriesSelectionAction(props: Props) {
|
||||||
|
const descriptor = useVideoPlayerDescriptor();
|
||||||
|
const meta = useMeta(descriptor);
|
||||||
|
const controls = useControls(descriptor);
|
||||||
|
|
||||||
|
if (meta?.meta.type !== MWMediaType.SERIES) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={props.className}>
|
||||||
|
<div className="relative">
|
||||||
|
<VideoPopout
|
||||||
|
id="episodes"
|
||||||
|
className="grid grid-rows-[auto,minmax(0,1fr)]"
|
||||||
|
>
|
||||||
|
<PopupEpisodeSelect />
|
||||||
|
</VideoPopout>
|
||||||
|
<VideoPlayerIconButton
|
||||||
|
icon={Icons.EPISODES}
|
||||||
|
text="Episodes"
|
||||||
|
onClick={() => controls.openPopout("episodes")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,10 +1,33 @@
|
||||||
import { MWMediaMeta } from "@/backend/metadata/types";
|
import { MWSeasonWithEpisodeMeta } from "@/backend/metadata/types";
|
||||||
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||||
import { useControls } from "@/video/state/logic/controls";
|
import { useControls } from "@/video/state/logic/controls";
|
||||||
|
import { VideoPlayerMeta } from "@/video/state/types";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
interface MetaControllerProps {
|
interface MetaControllerProps {
|
||||||
meta?: MWMediaMeta;
|
data?: VideoPlayerMeta;
|
||||||
|
seasonData?: MWSeasonWithEpisodeMeta;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMetadata(
|
||||||
|
props: MetaControllerProps
|
||||||
|
): VideoPlayerMeta | undefined {
|
||||||
|
const seasonsWithEpisodes = props.data?.seasons?.map((v) => {
|
||||||
|
if (v.id === props.seasonData?.id)
|
||||||
|
return {
|
||||||
|
...v,
|
||||||
|
episodes: props.seasonData.episodes,
|
||||||
|
};
|
||||||
|
return v;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!props.data) return undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
meta: props.data.meta,
|
||||||
|
episode: props.data.episode,
|
||||||
|
seasons: seasonsWithEpisodes,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MetaController(props: MetaControllerProps) {
|
export function MetaController(props: MetaControllerProps) {
|
||||||
|
@ -12,7 +35,7 @@ export function MetaController(props: MetaControllerProps) {
|
||||||
const controls = useControls(descriptor);
|
const controls = useControls(descriptor);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
controls.setMeta(props.meta);
|
controls.setMeta(formatMetadata(props));
|
||||||
}, [props, controls]);
|
}, [props, controls]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
39
src/video/components/controllers/SeriesController.tsx
Normal file
39
src/video/components/controllers/SeriesController.tsx
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||||
|
import { useMeta } from "@/video/state/logic/meta";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
interface SeriesControllerProps {
|
||||||
|
onSelect?: (state: { episodeId?: string; seasonId?: string }) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SeriesController(props: SeriesControllerProps) {
|
||||||
|
const descriptor = useVideoPlayerDescriptor();
|
||||||
|
const meta = useMeta(descriptor);
|
||||||
|
|
||||||
|
const lastState = useRef<{
|
||||||
|
episodeId?: string;
|
||||||
|
seasonId?: string;
|
||||||
|
} | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const currentState = {
|
||||||
|
episodeId: meta?.episode?.episodeId,
|
||||||
|
seasonId: meta?.episode?.seasonId,
|
||||||
|
};
|
||||||
|
if (lastState.current === null) {
|
||||||
|
lastState.current = currentState;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// when changes are detected, trigger event handler
|
||||||
|
if (
|
||||||
|
currentState.episodeId !== lastState.current?.episodeId ||
|
||||||
|
currentState.seasonId !== lastState.current?.seasonId
|
||||||
|
) {
|
||||||
|
lastState.current = currentState;
|
||||||
|
props.onSelect?.(currentState);
|
||||||
|
}
|
||||||
|
}, [meta, props]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
65
src/video/components/parts/VideoPopout.tsx
Normal file
65
src/video/components/parts/VideoPopout.tsx
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import { useVideoPlayerDescriptor } from "@/video/state/hooks";
|
||||||
|
import { useControls } from "@/video/state/logic/controls";
|
||||||
|
import { useInterface } from "@/video/state/logic/interface";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
children?: React.ReactNode;
|
||||||
|
id?: string;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO store popout in router history so you can press back to yeet
|
||||||
|
// TODO add transition
|
||||||
|
export function VideoPopout(props: Props) {
|
||||||
|
const descriptor = useVideoPlayerDescriptor();
|
||||||
|
const videoInterface = useInterface(descriptor);
|
||||||
|
const controls = useControls(descriptor);
|
||||||
|
|
||||||
|
const popoutRef = useRef<HTMLDivElement>(null);
|
||||||
|
const isOpen = videoInterface.popout === props.id;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isOpen) return;
|
||||||
|
const popoutEl = popoutRef.current;
|
||||||
|
function windowClick(e: MouseEvent) {
|
||||||
|
const rect = popoutEl?.getBoundingClientRect();
|
||||||
|
if (rect) {
|
||||||
|
if (
|
||||||
|
e.pageX >= rect.x &&
|
||||||
|
e.pageX <= rect.x + rect.width &&
|
||||||
|
e.pageY >= rect.y &&
|
||||||
|
e.pageY <= rect.y + rect.height
|
||||||
|
) {
|
||||||
|
// inside bounding box of popout
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
controls.closePopout();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("click", windowClick);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("click", windowClick);
|
||||||
|
};
|
||||||
|
}, [isOpen, controls]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={[
|
||||||
|
"is-popout absolute inset-x-0 h-0",
|
||||||
|
!isOpen ? "hidden" : "",
|
||||||
|
].join(" ")}
|
||||||
|
>
|
||||||
|
<div className="absolute bottom-10 right-0 h-96 w-72 rounded-lg bg-denim-400">
|
||||||
|
<div
|
||||||
|
ref={popoutRef}
|
||||||
|
className={["h-full w-full", props.className].join(" ")}
|
||||||
|
>
|
||||||
|
{isOpen ? props.children : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,15 +1,16 @@
|
||||||
import { MWMediaMeta } from "@/backend/metadata/types";
|
|
||||||
import { updateInterface } from "@/video/state/logic/interface";
|
import { updateInterface } from "@/video/state/logic/interface";
|
||||||
import { updateMeta } from "@/video/state/logic/meta";
|
import { updateMeta } from "@/video/state/logic/meta";
|
||||||
|
import { VideoPlayerMeta } from "@/video/state/types";
|
||||||
import { getPlayerState } from "../cache";
|
import { getPlayerState } from "../cache";
|
||||||
import { VideoPlayerStateController } from "../providers/providerTypes";
|
import { VideoPlayerStateController } from "../providers/providerTypes";
|
||||||
|
|
||||||
type ControlMethods = {
|
export type ControlMethods = {
|
||||||
openPopout(id: string): void;
|
openPopout(id: string): void;
|
||||||
closePopout(): void;
|
closePopout(): void;
|
||||||
setLeftControlsHover(hovering: boolean): void;
|
setLeftControlsHover(hovering: boolean): void;
|
||||||
setFocused(focused: boolean): void;
|
setFocused(focused: boolean): void;
|
||||||
setMeta(meta?: MWMediaMeta): void;
|
setMeta(data?: VideoPlayerMeta): void;
|
||||||
|
setCurrentEpisode(sId: string, eId: string): void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useControls(
|
export function useControls(
|
||||||
|
@ -65,11 +66,18 @@ export function useControls(
|
||||||
if (!meta) {
|
if (!meta) {
|
||||||
state.meta = null;
|
state.meta = null;
|
||||||
} else {
|
} else {
|
||||||
state.meta = {
|
state.meta = meta;
|
||||||
meta,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
updateMeta(descriptor, state);
|
updateMeta(descriptor, state);
|
||||||
},
|
},
|
||||||
|
setCurrentEpisode(sId, eId) {
|
||||||
|
if (state.meta) {
|
||||||
|
state.meta.episode = {
|
||||||
|
seasonId: sId,
|
||||||
|
episodeId: eId,
|
||||||
|
};
|
||||||
|
updateMeta(descriptor, state);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
// import { useEffect, useRef } from "react";
|
// import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
import { MWStreamQuality, MWStreamType } from "@/backend/helpers/streams";
|
import { MWStreamQuality, MWStreamType } from "@/backend/helpers/streams";
|
||||||
import { MWMediaType } from "@/backend/metadata/types";
|
// import { MWMediaType } from "@/backend/metadata/types";
|
||||||
import { MetaController } from "@/video/components/controllers/MetaController";
|
// import { MetaController } from "@/video/components/controllers/MetaController";
|
||||||
import { SourceController } from "@/video/components/controllers/SourceController";
|
import { SourceController } from "@/video/components/controllers/SourceController";
|
||||||
import { VideoPlayer } from "@/video/components/VideoPlayer";
|
import { VideoPlayer } from "@/video/components/VideoPlayer";
|
||||||
|
|
||||||
|
@ -32,15 +32,15 @@ export function TestView() {
|
||||||
source="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4"
|
source="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4"
|
||||||
type={MWStreamType.MP4}
|
type={MWStreamType.MP4}
|
||||||
/>
|
/>
|
||||||
<MetaController
|
{/* <MetaController
|
||||||
meta={{
|
meta={{
|
||||||
id: "test",
|
id: "test",
|
||||||
title: "Hello world",
|
title: "Hello world",
|
||||||
type: MWMediaType.MOVIE,
|
type: MWMediaType.MOVIE,
|
||||||
year: "1234",
|
year: "1234",
|
||||||
seasons: undefined,
|
seasons: undefined,
|
||||||
}}
|
}} */}
|
||||||
/>
|
{/* /> */}
|
||||||
</VideoPlayer>
|
</VideoPlayer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { MWMediaMeta } from "@/backend/metadata/types";
|
import { MWMediaMeta } from "@/backend/metadata/types";
|
||||||
import { ErrorMessage } from "@/components/layout/ErrorBoundary";
|
import { ErrorMessage } from "@/components/layout/ErrorBoundary";
|
||||||
import { Link } from "@/components/text/Link";
|
import { Link } from "@/components/text/Link";
|
||||||
import { VideoPlayerHeader } from "@/../__old/parts/VideoPlayerHeader";
|
|
||||||
import { useGoBack } from "@/hooks/useGoBack";
|
import { useGoBack } from "@/hooks/useGoBack";
|
||||||
import { conf } from "@/setup/config";
|
import { conf } from "@/setup/config";
|
||||||
|
import { VideoPlayerHeader } from "@/video/components/parts/VideoPlayerHeader";
|
||||||
import { Helmet } from "react-helmet";
|
import { Helmet } from "react-helmet";
|
||||||
|
|
||||||
export function MediaFetchErrorView() {
|
export function MediaFetchErrorView() {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { DetailedMeta, getMetaFromId } from "@/backend/metadata/getmeta";
|
||||||
import { decodeJWId } from "@/backend/metadata/justwatch";
|
import { decodeJWId } from "@/backend/metadata/justwatch";
|
||||||
import { Loading } from "@/components/layout/Loading";
|
import { Loading } from "@/components/layout/Loading";
|
||||||
import { useLoading } from "@/hooks/useLoading";
|
import { useLoading } from "@/hooks/useLoading";
|
||||||
import { MWMediaType } from "@/backend/metadata/types";
|
import { MWMediaType, MWSeasonWithEpisodeMeta } from "@/backend/metadata/types";
|
||||||
import { useGoBack } from "@/hooks/useGoBack";
|
import { useGoBack } from "@/hooks/useGoBack";
|
||||||
import { IconPatch } from "@/components/buttons/IconPatch";
|
import { IconPatch } from "@/components/buttons/IconPatch";
|
||||||
import { VideoPlayer } from "@/video/components/VideoPlayer";
|
import { VideoPlayer } from "@/video/components/VideoPlayer";
|
||||||
|
@ -16,6 +16,8 @@ import { SourceController } from "@/video/components/controllers/SourceControlle
|
||||||
import { Icons } from "@/components/Icon";
|
import { Icons } from "@/components/Icon";
|
||||||
import { VideoPlayerHeader } from "@/video/components/parts/VideoPlayerHeader";
|
import { VideoPlayerHeader } from "@/video/components/parts/VideoPlayerHeader";
|
||||||
import { ProgressListenerController } from "@/video/components/controllers/ProgressListenerController";
|
import { ProgressListenerController } from "@/video/components/controllers/ProgressListenerController";
|
||||||
|
import { VideoPlayerMeta } from "@/video/state/types";
|
||||||
|
import { SeriesController } from "@/video/components/controllers/SeriesController";
|
||||||
import { useWatchedItem } from "@/state/watched";
|
import { useWatchedItem } from "@/state/watched";
|
||||||
import { MediaFetchErrorView } from "./MediaErrorView";
|
import { MediaFetchErrorView } from "./MediaErrorView";
|
||||||
import { MediaScrapeLog } from "./MediaScrapeLog";
|
import { MediaScrapeLog } from "./MediaScrapeLog";
|
||||||
|
@ -108,13 +110,29 @@ export function MediaViewPlayer(props: MediaViewPlayerProps) {
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [props.stream]);
|
}, [props.stream]);
|
||||||
|
|
||||||
|
const metaProps: VideoPlayerMeta = {
|
||||||
|
meta: props.meta.meta,
|
||||||
|
};
|
||||||
|
let metaSeasonData: MWSeasonWithEpisodeMeta | undefined;
|
||||||
|
if (
|
||||||
|
props.selected.type === MWMediaType.SERIES &&
|
||||||
|
props.meta.meta.type === MWMediaType.SERIES
|
||||||
|
) {
|
||||||
|
metaProps.episode = {
|
||||||
|
seasonId: props.selected.season,
|
||||||
|
episodeId: props.selected.episode,
|
||||||
|
};
|
||||||
|
metaProps.seasons = props.meta.meta.seasons;
|
||||||
|
metaSeasonData = props.meta.meta.seasonData;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed top-0 left-0 h-[100dvh] w-screen">
|
<div className="fixed top-0 left-0 h-[100dvh] w-screen">
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<html data-full="true" />
|
<html data-full="true" />
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<VideoPlayer autoPlay onGoBack={goBack}>
|
<VideoPlayer autoPlay onGoBack={goBack}>
|
||||||
<MetaController meta={props.meta.meta} />
|
<MetaController data={metaProps} seasonData={metaSeasonData} />
|
||||||
<SourceController
|
<SourceController
|
||||||
source={props.stream.streamUrl}
|
source={props.stream.streamUrl}
|
||||||
type={props.stream.type}
|
type={props.stream.type}
|
||||||
|
@ -124,22 +142,13 @@ export function MediaViewPlayer(props: MediaViewPlayerProps) {
|
||||||
startAt={firstStartTime.current}
|
startAt={firstStartTime.current}
|
||||||
onProgress={updateProgress}
|
onProgress={updateProgress}
|
||||||
/>
|
/>
|
||||||
{/* {props.selected.type === MWMediaType.SERIES &&
|
<SeriesController
|
||||||
props.meta.meta.type === MWMediaType.SERIES ? (
|
|
||||||
<ShowControl
|
|
||||||
series={{
|
|
||||||
seasonId: props.selected.season,
|
|
||||||
episodeId: props.selected.episode,
|
|
||||||
}}
|
|
||||||
onSelect={(d) =>
|
onSelect={(d) =>
|
||||||
d.seasonId &&
|
d.seasonId &&
|
||||||
d.episodeId &&
|
d.episodeId &&
|
||||||
props.onChangeStream?.(d.seasonId, d.episodeId)
|
props.onChangeStream?.(d.seasonId, d.episodeId)
|
||||||
}
|
}
|
||||||
seasonData={props.meta.meta.seasonData}
|
|
||||||
seasons={props.meta.meta.seasons}
|
|
||||||
/>
|
/>
|
||||||
) : null} */}
|
|
||||||
</VideoPlayer>
|
</VideoPlayer>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,8 +6,8 @@ import { Navigation } from "@/components/layout/Navigation";
|
||||||
import { ArrowLink } from "@/components/text/ArrowLink";
|
import { ArrowLink } from "@/components/text/ArrowLink";
|
||||||
import { Title } from "@/components/text/Title";
|
import { Title } from "@/components/text/Title";
|
||||||
import { useGoBack } from "@/hooks/useGoBack";
|
import { useGoBack } from "@/hooks/useGoBack";
|
||||||
import { VideoPlayerHeader } from "@/../__old/parts/VideoPlayerHeader";
|
|
||||||
import { Helmet } from "react-helmet";
|
import { Helmet } from "react-helmet";
|
||||||
|
import { VideoPlayerHeader } from "@/video/components/parts/VideoPlayerHeader";
|
||||||
|
|
||||||
export function NotFoundWrapper(props: {
|
export function NotFoundWrapper(props: {
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
|
|
Loading…
Reference in a new issue