mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-29 16:07:40 +01:00
meta data shown correctly
Co-authored-by: Jip Frijlink <JipFr@users.noreply.github.com>
This commit is contained in:
parent
3e210b979e
commit
0a3155d399
9 changed files with 157 additions and 15 deletions
24
src/components/player/atoms/EpisodeTitle.tsx
Normal file
24
src/components/player/atoms/EpisodeTitle.tsx
Normal 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>
|
||||
);
|
||||
}
|
6
src/components/player/atoms/Title.tsx
Normal file
6
src/components/player/atoms/Title.tsx
Normal 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>;
|
||||
}
|
|
@ -6,3 +6,5 @@ export * from "./Time";
|
|||
export * from "./LoadingSpinner";
|
||||
export * from "./AutoPlayStart";
|
||||
export * from "./Volume";
|
||||
export * from "./Title";
|
||||
export * from "./EpisodeTitle";
|
||||
|
|
|
@ -16,8 +16,6 @@ export function BackLink() {
|
|||
<Icon className="mr-2" icon={Icons.ARROW_LEFT} />
|
||||
<span>{t("videoPlayer.backToHomeShort")}</span>
|
||||
</span>
|
||||
<span className="text mx-3 text-type-secondary">/</span>
|
||||
<span>Mr Jeebaloo's Big Ocean Adventure</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { ReactNode, RefObject, useEffect, useRef } from "react";
|
||||
|
||||
import { HeadUpdater } from "@/components/player/internals/HeadUpdater";
|
||||
import { VideoClickTarget } from "@/components/player/internals/VideoClickTarget";
|
||||
import { VideoContainer } from "@/components/player/internals/VideoContainer";
|
||||
import { PlayerHoverState } from "@/stores/player/slices/interface";
|
||||
|
@ -79,6 +80,7 @@ export function Container(props: PlayerProps) {
|
|||
<BaseContainer>
|
||||
<VideoContainer />
|
||||
<VideoClickTarget />
|
||||
<HeadUpdater />
|
||||
{props.children}
|
||||
</BaseContainer>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { MWStreamType } from "@/backend/helpers/streams";
|
||||
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";
|
||||
|
||||
export interface Source {
|
||||
|
@ -10,12 +10,16 @@ export interface Source {
|
|||
|
||||
export function usePlayer() {
|
||||
const setStatus = usePlayerStore((s) => s.setStatus);
|
||||
const setMeta = usePlayerStore((s) => s.setMeta);
|
||||
const status = usePlayerStore((s) => s.status);
|
||||
const display = usePlayerStore((s) => s.display);
|
||||
const { init } = useInitializePlayer();
|
||||
|
||||
return {
|
||||
status,
|
||||
setMeta(meta: PlayerMeta) {
|
||||
setMeta(meta);
|
||||
},
|
||||
playMedia(source: Source) {
|
||||
display?.load(source);
|
||||
setStatus(playerStatus.PLAYING);
|
||||
|
|
31
src/components/player/internals/HeadUpdater.tsx
Normal file
31
src/components/player/internals/HeadUpdater.tsx
Normal 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>
|
||||
);
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
import { useEffect, useMemo } from "react";
|
||||
|
||||
import { MWStreamType } from "@/backend/helpers/streams";
|
||||
import { BrandPill } from "@/components/layout/BrandPill";
|
||||
import { Player } from "@/components/player";
|
||||
|
@ -5,22 +7,45 @@ import { AutoPlayStart } from "@/components/player/atoms";
|
|||
import { usePlayer } from "@/components/player/hooks/usePlayer";
|
||||
import { useShouldShowControls } from "@/components/player/hooks/useShouldShowControls";
|
||||
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() {
|
||||
const { status, setScrapeStatus, playMedia } = usePlayer();
|
||||
const { status, setScrapeStatus, playMedia, setMeta } = usePlayer();
|
||||
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 (
|
||||
<Player.Container onLoad={setScrapeStatus}>
|
||||
{status === playerStatus.SCRAPING ? (
|
||||
<ScrapingPart
|
||||
media={{
|
||||
type: "movie",
|
||||
title: "Everything Everywhere All At Once",
|
||||
tmdbId: "545611",
|
||||
releaseYear: 2022,
|
||||
}}
|
||||
media={scrapeMedia}
|
||||
onGetStream={(out) => {
|
||||
if (out?.stream.type !== "file") return;
|
||||
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="flex space-x-3 items-center">
|
||||
<Player.BackLink />
|
||||
<span className="text mx-3 text-type-secondary">/</span>
|
||||
<Player.Title />
|
||||
<Player.BookmarkButton />
|
||||
</div>
|
||||
<div className="text-center hidden xl:flex justify-center items-center">
|
||||
<span className="text-white font-medium mr-3">S1 E5</span>
|
||||
<span className="text-type-secondary font-medium">
|
||||
Mr. Jeebaloo discovers Atlantis
|
||||
</span>
|
||||
<Player.EpisodeTitle />
|
||||
</div>
|
||||
<div className="flex items-center justify-end">
|
||||
<BrandPill />
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { ScrapeMedia } from "@movie-web/providers";
|
||||
|
||||
import { MWStreamType } from "@/backend/helpers/streams";
|
||||
import { MakeSlice } from "@/stores/player/slices/types";
|
||||
import { ValuesOf } from "@/utils/typeguard";
|
||||
|
@ -15,21 +17,70 @@ export interface SourceSliceSource {
|
|||
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 {
|
||||
status: PlayerStatus;
|
||||
source: SourceSliceSource | null;
|
||||
meta: PlayerMeta | null;
|
||||
setStatus(status: PlayerStatus): 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) => ({
|
||||
source: null,
|
||||
status: playerStatus.IDLE,
|
||||
meta: null,
|
||||
setStatus(status: PlayerStatus) {
|
||||
set((s) => {
|
||||
s.status = status;
|
||||
});
|
||||
},
|
||||
setMeta(meta) {
|
||||
set((s) => {
|
||||
s.meta = meta;
|
||||
});
|
||||
},
|
||||
setSource(url: string, type: MWStreamType) {
|
||||
set((s) => {
|
||||
s.source = {
|
||||
|
|
Loading…
Reference in a new issue