meta data shown correctly

Co-authored-by: Jip Frijlink <JipFr@users.noreply.github.com>
This commit is contained in:
mrjvs 2023-10-08 18:16:30 +02:00
parent 3e210b979e
commit 0a3155d399
9 changed files with 157 additions and 15 deletions

View File

@ -0,0 +1,24 @@
import { useTranslation } from "react-i18next";
import { usePlayerStore } from "@/stores/player/store";
export function EpisodeTitle() {
const { t } = useTranslation();
const meta = usePlayerStore((s) => s.meta);
if (meta?.type !== "show") return null;
return (
<div>
<span className="text-white font-medium mr-3">
{t("seasons.seasonAndEpisode", {
season: meta?.season?.number,
episode: meta?.episode?.number,
})}
</span>
<span className="text-type-secondary font-medium">
{meta?.episode?.title}
</span>
</div>
);
}

View File

@ -0,0 +1,6 @@
import { usePlayerStore } from "@/stores/player/store";
export function Title() {
const title = usePlayerStore((s) => s.meta?.title);
return <p>{title || "Beep beep, Richie!"}</p>;
}

View File

@ -6,3 +6,5 @@ export * from "./Time";
export * from "./LoadingSpinner"; export * from "./LoadingSpinner";
export * from "./AutoPlayStart"; export * from "./AutoPlayStart";
export * from "./Volume"; export * from "./Volume";
export * from "./Title";
export * from "./EpisodeTitle";

View File

@ -16,8 +16,6 @@ export function BackLink() {
<Icon className="mr-2" icon={Icons.ARROW_LEFT} /> <Icon className="mr-2" icon={Icons.ARROW_LEFT} />
<span>{t("videoPlayer.backToHomeShort")}</span> <span>{t("videoPlayer.backToHomeShort")}</span>
</span> </span>
<span className="text mx-3 text-type-secondary">/</span>
<span>Mr Jeebaloo&apos;s Big Ocean Adventure</span>
</div> </div>
); );
} }

View File

@ -1,5 +1,6 @@
import { ReactNode, RefObject, useEffect, useRef } from "react"; import { ReactNode, RefObject, useEffect, useRef } from "react";
import { HeadUpdater } from "@/components/player/internals/HeadUpdater";
import { VideoClickTarget } from "@/components/player/internals/VideoClickTarget"; import { VideoClickTarget } from "@/components/player/internals/VideoClickTarget";
import { VideoContainer } from "@/components/player/internals/VideoContainer"; import { VideoContainer } from "@/components/player/internals/VideoContainer";
import { PlayerHoverState } from "@/stores/player/slices/interface"; import { PlayerHoverState } from "@/stores/player/slices/interface";
@ -79,6 +80,7 @@ export function Container(props: PlayerProps) {
<BaseContainer> <BaseContainer>
<VideoContainer /> <VideoContainer />
<VideoClickTarget /> <VideoClickTarget />
<HeadUpdater />
{props.children} {props.children}
</BaseContainer> </BaseContainer>
); );

View File

@ -1,6 +1,6 @@
import { MWStreamType } from "@/backend/helpers/streams"; import { MWStreamType } from "@/backend/helpers/streams";
import { useInitializePlayer } from "@/components/player/hooks/useInitializePlayer"; import { useInitializePlayer } from "@/components/player/hooks/useInitializePlayer";
import { playerStatus } from "@/stores/player/slices/source"; import { PlayerMeta, playerStatus } from "@/stores/player/slices/source";
import { usePlayerStore } from "@/stores/player/store"; import { usePlayerStore } from "@/stores/player/store";
export interface Source { export interface Source {
@ -10,12 +10,16 @@ export interface Source {
export function usePlayer() { export function usePlayer() {
const setStatus = usePlayerStore((s) => s.setStatus); const setStatus = usePlayerStore((s) => s.setStatus);
const setMeta = usePlayerStore((s) => s.setMeta);
const status = usePlayerStore((s) => s.status); const status = usePlayerStore((s) => s.status);
const display = usePlayerStore((s) => s.display); const display = usePlayerStore((s) => s.display);
const { init } = useInitializePlayer(); const { init } = useInitializePlayer();
return { return {
status, status,
setMeta(meta: PlayerMeta) {
setMeta(meta);
},
playMedia(source: Source) { playMedia(source: Source) {
display?.load(source); display?.load(source);
setStatus(playerStatus.PLAYING); setStatus(playerStatus.PLAYING);

View File

@ -0,0 +1,31 @@
import { Helmet } from "react-helmet";
import { useTranslation } from "react-i18next";
import { usePlayerStore } from "@/stores/player/store";
export function HeadUpdater() {
const { t } = useTranslation();
const meta = usePlayerStore((s) => s.meta);
if (!meta) return null;
if (meta.type !== "show") {
return (
<Helmet>
<title>{meta.title}</title>
</Helmet>
);
}
const humanizedEpisodeId = t("videoPlayer.seasonAndEpisode", {
season: meta.season?.number,
episode: meta.episode?.number,
});
return (
<Helmet>
<title>
{meta.title} - {humanizedEpisodeId}
</title>
</Helmet>
);
}

View File

@ -1,3 +1,5 @@
import { useEffect, useMemo } from "react";
import { MWStreamType } from "@/backend/helpers/streams"; import { MWStreamType } from "@/backend/helpers/streams";
import { BrandPill } from "@/components/layout/BrandPill"; import { BrandPill } from "@/components/layout/BrandPill";
import { Player } from "@/components/player"; import { Player } from "@/components/player";
@ -5,22 +7,45 @@ import { AutoPlayStart } from "@/components/player/atoms";
import { usePlayer } from "@/components/player/hooks/usePlayer"; import { usePlayer } from "@/components/player/hooks/usePlayer";
import { useShouldShowControls } from "@/components/player/hooks/useShouldShowControls"; import { useShouldShowControls } from "@/components/player/hooks/useShouldShowControls";
import { ScrapingPart } from "@/pages/parts/player/ScrapingPart"; import { ScrapingPart } from "@/pages/parts/player/ScrapingPart";
import { playerStatus } from "@/stores/player/slices/source"; import {
PlayerMeta,
metaToScrapeMedia,
playerStatus,
} from "@/stores/player/slices/source";
export function PlayerView() { export function PlayerView() {
const { status, setScrapeStatus, playMedia } = usePlayer(); const { status, setScrapeStatus, playMedia, setMeta } = usePlayer();
const desktopControlsVisible = useShouldShowControls(); const desktopControlsVisible = useShouldShowControls();
const meta = useMemo<PlayerMeta>(
() => ({
type: "show",
title: "House",
tmdbId: "1408",
releaseYear: 2004,
episode: {
number: 1,
title: "Pilot",
tmdbId: "63738",
},
season: {
number: 1,
tmdbId: "3674",
title: "Season 1",
},
}),
[]
);
useEffect(() => {
setMeta(meta);
}, [setMeta, meta]);
const scrapeMedia = useMemo(() => metaToScrapeMedia(meta), [meta]);
return ( return (
<Player.Container onLoad={setScrapeStatus}> <Player.Container onLoad={setScrapeStatus}>
{status === playerStatus.SCRAPING ? ( {status === playerStatus.SCRAPING ? (
<ScrapingPart <ScrapingPart
media={{ media={scrapeMedia}
type: "movie",
title: "Everything Everywhere All At Once",
tmdbId: "545611",
releaseYear: 2022,
}}
onGetStream={(out) => { onGetStream={(out) => {
if (out?.stream.type !== "file") return; if (out?.stream.type !== "file") return;
const qualities = Object.keys( const qualities = Object.keys(
@ -47,13 +72,12 @@ export function PlayerView() {
<div className="grid grid-cols-[1fr,auto] xl:grid-cols-3 items-center"> <div className="grid grid-cols-[1fr,auto] xl:grid-cols-3 items-center">
<div className="flex space-x-3 items-center"> <div className="flex space-x-3 items-center">
<Player.BackLink /> <Player.BackLink />
<span className="text mx-3 text-type-secondary">/</span>
<Player.Title />
<Player.BookmarkButton /> <Player.BookmarkButton />
</div> </div>
<div className="text-center hidden xl:flex justify-center items-center"> <div className="text-center hidden xl:flex justify-center items-center">
<span className="text-white font-medium mr-3">S1 E5</span> <Player.EpisodeTitle />
<span className="text-type-secondary font-medium">
Mr. Jeebaloo discovers Atlantis
</span>
</div> </div>
<div className="flex items-center justify-end"> <div className="flex items-center justify-end">
<BrandPill /> <BrandPill />

View File

@ -1,3 +1,5 @@
import { ScrapeMedia } from "@movie-web/providers";
import { MWStreamType } from "@/backend/helpers/streams"; import { MWStreamType } from "@/backend/helpers/streams";
import { MakeSlice } from "@/stores/player/slices/types"; import { MakeSlice } from "@/stores/player/slices/types";
import { ValuesOf } from "@/utils/typeguard"; import { ValuesOf } from "@/utils/typeguard";
@ -15,21 +17,70 @@ export interface SourceSliceSource {
type: MWStreamType; type: MWStreamType;
} }
export interface PlayerMeta {
type: "movie" | "show";
title: string;
tmdbId: string;
imdbId?: string;
releaseYear: number;
episode?: {
number: number;
tmdbId: string;
title: string;
};
season?: {
number: number;
tmdbId: string;
title: string;
};
}
export interface SourceSlice { export interface SourceSlice {
status: PlayerStatus; status: PlayerStatus;
source: SourceSliceSource | null; source: SourceSliceSource | null;
meta: PlayerMeta | null;
setStatus(status: PlayerStatus): void; setStatus(status: PlayerStatus): void;
setSource(url: string, type: MWStreamType): void; setSource(url: string, type: MWStreamType): void;
setMeta(meta: PlayerMeta): void;
}
export function metaToScrapeMedia(meta: PlayerMeta): ScrapeMedia {
if (meta.type === "show") {
if (!meta.episode || !meta.season) throw new Error("missing show data");
return {
title: meta.title,
releaseYear: meta.releaseYear,
tmdbId: meta.tmdbId,
type: "show",
imdbId: meta.imdbId,
episode: meta.episode,
season: meta.season,
};
}
return {
title: meta.title,
releaseYear: meta.releaseYear,
tmdbId: meta.tmdbId,
type: "movie",
imdbId: meta.imdbId,
};
} }
export const createSourceSlice: MakeSlice<SourceSlice> = (set) => ({ export const createSourceSlice: MakeSlice<SourceSlice> = (set) => ({
source: null, source: null,
status: playerStatus.IDLE, status: playerStatus.IDLE,
meta: null,
setStatus(status: PlayerStatus) { setStatus(status: PlayerStatus) {
set((s) => { set((s) => {
s.status = status; s.status = status;
}); });
}, },
setMeta(meta) {
set((s) => {
s.meta = meta;
});
},
setSource(url: string, type: MWStreamType) { setSource(url: string, type: MWStreamType) {
set((s) => { set((s) => {
s.source = { s.source = {