mirror of
https://github.com/p-stream/p-stream.git
synced 2026-03-11 17:55:33 +00:00
Add split overlay scraping part with title and info
This commit is contained in:
parent
24132618fd
commit
b3c2d58f8d
1 changed files with 184 additions and 52 deletions
|
|
@ -9,12 +9,15 @@ import {
|
||||||
scrapePartsToProviderMetric,
|
scrapePartsToProviderMetric,
|
||||||
useReportProviders,
|
useReportProviders,
|
||||||
} from "@/backend/helpers/report";
|
} from "@/backend/helpers/report";
|
||||||
|
import { getMediaDetails, getMediaLogo } from "@/backend/metadata/tmdb";
|
||||||
|
import { TMDBContentTypes } from "@/backend/metadata/types/tmdb";
|
||||||
import { Button } from "@/components/buttons/Button";
|
import { Button } from "@/components/buttons/Button";
|
||||||
import { Loading } from "@/components/layout/Loading";
|
import { Loading } from "@/components/layout/Loading";
|
||||||
import {
|
import {
|
||||||
ScrapeCard,
|
ScrapeCard,
|
||||||
ScrapeItem,
|
ScrapeItem,
|
||||||
} from "@/components/player/internals/ScrapeCard";
|
} from "@/components/player/internals/ScrapeCard";
|
||||||
|
import { useIsMobile } from "@/hooks/useIsMobile";
|
||||||
import {
|
import {
|
||||||
ScrapingItems,
|
ScrapingItems,
|
||||||
ScrapingSegment,
|
ScrapingSegment,
|
||||||
|
|
@ -23,6 +26,12 @@ import {
|
||||||
} from "@/hooks/useProviderScrape";
|
} from "@/hooks/useProviderScrape";
|
||||||
import { playerStatus } from "@/stores/player/slices/source";
|
import { playerStatus } from "@/stores/player/slices/source";
|
||||||
import { usePlayerStore } from "@/stores/player/store";
|
import { usePlayerStore } from "@/stores/player/store";
|
||||||
|
import { usePreferencesStore } from "@/stores/preferences";
|
||||||
|
|
||||||
|
interface ScrapingMediaDetails {
|
||||||
|
voteAverage: number | null;
|
||||||
|
genres: string[];
|
||||||
|
}
|
||||||
|
|
||||||
export interface ScrapingProps {
|
export interface ScrapingProps {
|
||||||
media: ScrapeMedia;
|
media: ScrapeMedia;
|
||||||
|
|
@ -43,6 +52,66 @@ export function ScrapingPart(props: ScrapingProps) {
|
||||||
const setStatus = usePlayerStore((s) => s.setStatus);
|
const setStatus = usePlayerStore((s) => s.setStatus);
|
||||||
const addFailedSource = usePlayerStore((s) => s.addFailedSource);
|
const addFailedSource = usePlayerStore((s) => s.addFailedSource);
|
||||||
const sourceId = usePlayerStore((s) => s.sourceId);
|
const sourceId = usePlayerStore((s) => s.sourceId);
|
||||||
|
const meta = usePlayerStore((s) => s.meta);
|
||||||
|
const enablePauseOverlay = usePreferencesStore((s) => s.enablePauseOverlay);
|
||||||
|
const enableImageLogos = usePreferencesStore((s) => s.enableImageLogos);
|
||||||
|
const { isMobile } = useIsMobile();
|
||||||
|
|
||||||
|
const showMediaColumn = enablePauseOverlay && !isMobile && !!meta;
|
||||||
|
const [logoUrl, setLogoUrl] = useState<string | null>(null);
|
||||||
|
const [details, setDetails] = useState<ScrapingMediaDetails>({
|
||||||
|
voteAverage: null,
|
||||||
|
genres: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!showMediaColumn || !meta?.tmdbId) return;
|
||||||
|
let mounted = true;
|
||||||
|
const fetchLogo = async () => {
|
||||||
|
if (!enableImageLogos) {
|
||||||
|
setLogoUrl(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const type =
|
||||||
|
meta.type === "movie" ? TMDBContentTypes.MOVIE : TMDBContentTypes.TV;
|
||||||
|
const url = await getMediaLogo(meta.tmdbId, type);
|
||||||
|
if (mounted) setLogoUrl(url || null);
|
||||||
|
} catch {
|
||||||
|
if (mounted) setLogoUrl(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchLogo();
|
||||||
|
return () => {
|
||||||
|
mounted = false;
|
||||||
|
};
|
||||||
|
}, [showMediaColumn, meta?.tmdbId, meta?.type, enableImageLogos]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!showMediaColumn || !meta?.tmdbId) return;
|
||||||
|
let mounted = true;
|
||||||
|
const fetchDetails = async () => {
|
||||||
|
try {
|
||||||
|
const type =
|
||||||
|
meta.type === "movie" ? TMDBContentTypes.MOVIE : TMDBContentTypes.TV;
|
||||||
|
const data = await getMediaDetails(meta.tmdbId, type, false);
|
||||||
|
if (mounted && data) {
|
||||||
|
const voteAverage =
|
||||||
|
typeof data.vote_average === "number" ? data.vote_average : null;
|
||||||
|
const genres = (data.genres ?? []).map(
|
||||||
|
(g: { name: string }) => g.name,
|
||||||
|
);
|
||||||
|
setDetails({ voteAverage, genres });
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
if (mounted) setDetails({ voteAverage: null, genres: [] });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchDetails();
|
||||||
|
return () => {
|
||||||
|
mounted = false;
|
||||||
|
};
|
||||||
|
}, [showMediaColumn, meta?.tmdbId, meta?.type]);
|
||||||
|
|
||||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||||
const listRef = useRef<HTMLDivElement | null>(null);
|
const listRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
@ -125,11 +194,73 @@ export function ScrapingPart(props: ScrapingProps) {
|
||||||
if (currentProviderIndex === -1)
|
if (currentProviderIndex === -1)
|
||||||
currentProviderIndex = sourceOrder.length - 1;
|
currentProviderIndex = sourceOrder.length - 1;
|
||||||
|
|
||||||
|
const overview =
|
||||||
|
meta && (meta.type === "show" ? meta.episode?.overview : meta.overview);
|
||||||
|
const hasMediaDetails =
|
||||||
|
details.voteAverage !== null || details.genres.length > 0;
|
||||||
|
const hasMediaContent =
|
||||||
|
showMediaColumn &&
|
||||||
|
meta &&
|
||||||
|
(overview || logoUrl || meta.title || hasMediaDetails);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="h-full w-full relative dir-neutral:origin-top-left flex"
|
className={classNames(
|
||||||
|
"h-full w-full relative dir-neutral:origin-top-left flex",
|
||||||
|
showMediaColumn && "gap-8 lg:gap-12",
|
||||||
|
)}
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
>
|
>
|
||||||
|
{showMediaColumn && hasMediaContent && (
|
||||||
|
<div className="flex-shrink-0 w-80 max-w-[min(20rem,40%)] flex items-center py-6">
|
||||||
|
<div className="max-w-sm">
|
||||||
|
{logoUrl ? (
|
||||||
|
<img
|
||||||
|
src={logoUrl}
|
||||||
|
alt={meta.title}
|
||||||
|
className="mb-6 max-h-32 object-contain drop-shadow-lg"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<h1 className="mb-4 text-4xl font-bold text-white drop-shadow-lg">
|
||||||
|
{meta.title}
|
||||||
|
</h1>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{meta.type === "show" && meta.episode && (
|
||||||
|
<h2 className="mb-2 text-2xl font-semibold text-white/90 drop-shadow-md">
|
||||||
|
{meta.episode.title}
|
||||||
|
</h2>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{hasMediaDetails && (
|
||||||
|
<div className="mb-3 flex flex-wrap items-center gap-x-2 gap-y-1 text-sm text-white/80 drop-shadow-md">
|
||||||
|
{details.voteAverage !== null && (
|
||||||
|
<span>
|
||||||
|
{details.voteAverage.toFixed(1)}
|
||||||
|
<span className="text-white/60 ml-0.5">/10</span>
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{details.genres.length > 0 && (
|
||||||
|
<>
|
||||||
|
{details.voteAverage !== null && (
|
||||||
|
<span className="text-white/60">•</span>
|
||||||
|
)}
|
||||||
|
<span>{details.genres.slice(0, 4).join(", ")}</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{overview && (
|
||||||
|
<p className="text-lg text-white/80 drop-shadow-md line-clamp-6">
|
||||||
|
{overview}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="flex-1 min-w-0 relative flex">
|
||||||
{!sourceOrder || sourceOrder.length === 0 ? (
|
{!sourceOrder || sourceOrder.length === 0 ? (
|
||||||
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-center flex flex-col justify-center z-0">
|
<div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 text-center flex flex-col justify-center z-0">
|
||||||
<Loading className="mb-8" />
|
<Loading className="mb-8" />
|
||||||
|
|
@ -186,6 +317,7 @@ export function ScrapingPart(props: ScrapingProps) {
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue