mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-21 14:47:41 +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 "./LoadingSpinner";
|
||||||
export * from "./AutoPlayStart";
|
export * from "./AutoPlayStart";
|
||||||
export * from "./Volume";
|
export * from "./Volume";
|
||||||
|
export * from "./Title";
|
||||||
|
export * from "./EpisodeTitle";
|
||||||
|
|
|
@ -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's Big Ocean Adventure</span>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
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 { 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 />
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
Loading…
Reference in a new issue