add network images

This commit is contained in:
Pas 2025-06-07 17:51:02 -06:00
parent eaf2399539
commit 9b6fbf8aa8
12 changed files with 95 additions and 7 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
public/platforms/disney.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

BIN
public/platforms/hulu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
public/platforms/max.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
public/platforms/prime.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -35,6 +35,12 @@ export interface TraktDiscoverResponse {
count: number; count: number;
} }
export interface TraktNetworkResponse {
type: string;
platforms: string[];
count: number;
}
// Pagination utility // Pagination utility
export function paginateResults( export function paginateResults(
results: TraktLatestResponse, results: TraktLatestResponse,
@ -106,6 +112,10 @@ export const getPopularMovies = () => fetchFromTrakt("/popularmovies");
export const getDiscoverContent = () => export const getDiscoverContent = () =>
fetchFromTrakt<TraktDiscoverResponse>("/discover"); fetchFromTrakt<TraktDiscoverResponse>("/discover");
// Network content
export const getNetworkContent = (tmdbId: string) =>
fetchFromTrakt<TraktNetworkResponse>(`/network/${tmdbId}`);
// Type conversion utilities // Type conversion utilities
export function convertToMediaType(type: TraktContentType): MWMediaType { export function convertToMediaType(type: TraktContentType): MWMediaType {
return type === "movie" ? MWMediaType.MOVIE : MWMediaType.SERIES; return type === "movie" ? MWMediaType.MOVIE : MWMediaType.SERIES;
@ -130,3 +140,14 @@ export const GENRE_TO_TRAKT_MAP = {
"28": "action", // Action "28": "action", // Action
"18": "drama", // Drama "18": "drama", // Drama
} as const; } as const;
// Map provider names to their image filenames
export const PROVIDER_TO_IMAGE_MAP: Record<string, string> = {
Max: "max",
"Prime Video": "prime",
Netflix: "netflix",
"Disney+": "disney",
Hulu: "hulu",
"Apple TV+": "appletv",
"Paramount+": "paramount",
};

View file

@ -2,6 +2,7 @@ import { t } from "i18next";
import { useEffect, useMemo, useRef, useState } from "react"; import { useEffect, useMemo, useRef, useState } from "react";
import { useCopyToClipboard } from "react-use"; import { useCopyToClipboard } from "react-use";
import { getNetworkContent } from "@/backend/metadata/traktApi";
import { TMDBContentTypes } from "@/backend/metadata/types/tmdb"; import { TMDBContentTypes } from "@/backend/metadata/types/tmdb";
import { Icon, Icons } from "@/components/Icon"; import { Icon, Icons } from "@/components/Icon";
import { useLanguageStore } from "@/stores/language"; import { useLanguageStore } from "@/stores/language";
@ -22,6 +23,9 @@ import { DetailsContentProps } from "./types";
export function DetailsContent({ data, minimal = false }: DetailsContentProps) { export function DetailsContent({ data, minimal = false }: DetailsContentProps) {
const [imdbData, setImdbData] = useState<any>(null); const [imdbData, setImdbData] = useState<any>(null);
const [rtData, setRtData] = useState<any>(null); const [rtData, setRtData] = useState<any>(null);
const [providerData, setProviderData] = useState<string | undefined>(
undefined,
);
const [, setIsLoadingImdb] = useState(false); const [, setIsLoadingImdb] = useState(false);
const [showTrailer, setShowTrailer] = useState(false); const [showTrailer, setShowTrailer] = useState(false);
const [selectedSeason, setSelectedSeason] = useState<number>(1); const [selectedSeason, setSelectedSeason] = useState<number>(1);
@ -62,6 +66,30 @@ export function DetailsContent({ data, minimal = false }: DetailsContentProps) {
} }
}, []); }, []);
useEffect(() => {
const fetchNetworkData = async () => {
if (!data.id) return;
try {
const networkData = await getNetworkContent(data.id.toString());
if (
networkData &&
networkData.platforms &&
networkData.platforms.length > 0
) {
setProviderData(networkData.platforms[0]);
} else {
setProviderData(undefined);
}
} catch (error) {
console.error("Failed to fetch network data:", error);
setProviderData(undefined);
}
};
fetchNetworkData();
}, [data.id]);
useEffect(() => { useEffect(() => {
const fetchExternalData = async () => { const fetchExternalData = async () => {
if (!data.imdbId) return; if (!data.imdbId) return;
@ -273,7 +301,12 @@ export function DetailsContent({ data, minimal = false }: DetailsContentProps) {
{/* Right Column - Details Info (1/3) */} {/* Right Column - Details Info (1/3) */}
<div className="md:col-span-1"> <div className="md:col-span-1">
<DetailsInfo data={data} imdbData={imdbData} rtData={rtData} /> <DetailsInfo
data={data}
imdbData={imdbData}
rtData={rtData}
provider={providerData}
/>
</div> </div>
</div> </div>

View file

@ -5,7 +5,12 @@ import { Trans } from "react-i18next";
import { DetailsRatings } from "./DetailsRatings"; import { DetailsRatings } from "./DetailsRatings";
import { DetailsInfoProps } from "./types"; import { DetailsInfoProps } from "./types";
export function DetailsInfo({ data, imdbData, rtData }: DetailsInfoProps) { export function DetailsInfo({
data,
imdbData,
rtData,
provider,
}: DetailsInfoProps) {
const [isShiftPressed, setIsShiftPressed] = useState(false); const [isShiftPressed, setIsShiftPressed] = useState(false);
const [showCopied, setShowCopied] = useState(false); const [showCopied, setShowCopied] = useState(false);
@ -122,6 +127,7 @@ export function DetailsInfo({ data, imdbData, rtData }: DetailsInfoProps) {
imdbId={data.imdbId} imdbId={data.imdbId}
voteAverage={data.voteAverage} voteAverage={data.voteAverage}
voteCount={data.voteCount} voteCount={data.voteCount}
provider={provider}
/> />
</div> </div>
</div> </div>

View file

@ -1,5 +1,6 @@
import { t } from "i18next"; import { t } from "i18next";
import { PROVIDER_TO_IMAGE_MAP } from "@/backend/metadata/traktApi";
import { Icon, Icons } from "@/components/Icon"; import { Icon, Icons } from "@/components/Icon";
import { getRTIcon } from "@/utils/rottenTomatoesScraper"; import { getRTIcon } from "@/utils/rottenTomatoesScraper";
@ -10,19 +11,44 @@ export function DetailsRatings({
mediaId, mediaId,
mediaType, mediaType,
imdbId, imdbId,
provider,
}: DetailsRatingsProps) { }: DetailsRatingsProps) {
const getProviderImage = (providerName: string) => {
const imageKey =
PROVIDER_TO_IMAGE_MAP[providerName] ||
providerName.toLowerCase().replace(/\s+/g, "");
return `/platforms/${imageKey}.png`;
};
return ( return (
<div className="space-y-1"> <div className="space-y-1">
{/* External Links */} {/* External Links */}
<div className="flex gap-3 mt-2"> <div className="flex gap-3 mt-2">
{provider && (
<div
className="w-8 h-8 flex items-center justify-center transition-transform hover:scale-110 animate-[scaleIn_0.6s_ease-out_forwards]"
style={{
animationDelay: "0ms",
transform: "scale(0)",
opacity: 0,
}}
title={provider}
>
<img
src={getProviderImage(provider)}
alt={provider}
className="w-8 h-8 rounded-md"
/>
</div>
)}
{mediaId && ( {mediaId && (
<a <a
href={`https://www.themoviedb.org/${mediaType === "show" ? "tv" : "movie"}/${mediaId}`} href={`https://www.themoviedb.org/${mediaType === "show" ? "tv" : "movie"}/${mediaId}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="w-8 h-8 rounded-full bg-[#0d253f] flex items-center justify-center transition-transform hover:scale-110 animate-[scaleIn_0.6s_ease-out_forwards]" className="w-8 h-8 rounded-md bg-[#0d253f] flex items-center justify-center transition-transform hover:scale-110 animate-[scaleIn_0.6s_ease-out_forwards]"
style={{ style={{
animationDelay: "0ms", animationDelay: "60ms",
transform: "scale(0)", transform: "scale(0)",
opacity: 0, opacity: 0,
}} }}
@ -36,9 +62,9 @@ export function DetailsRatings({
href={`https://www.imdb.com/title/${imdbId}`} href={`https://www.imdb.com/title/${imdbId}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="w-8 h-8 rounded-full bg-yellow-500 flex items-center justify-center transition-transform hover:scale-110 animate-[scaleIn_0.6s_ease-out_forwards]" className="w-8 h-8 rounded-md bg-yellow-500 flex items-center justify-center transition-transform hover:scale-110 animate-[scaleIn_0.6s_ease-out_forwards]"
style={{ style={{
animationDelay: "60ms", animationDelay: "120ms",
transform: "scale(0)", transform: "scale(0)",
opacity: 0, opacity: 0,
}} }}
@ -53,7 +79,7 @@ export function DetailsRatings({
<div <div
className="flex items-center gap-1 animate-[scaleIn_0.6s_ease-out_forwards]" className="flex items-center gap-1 animate-[scaleIn_0.6s_ease-out_forwards]"
style={{ style={{
animationDelay: "120ms", animationDelay: "180ms",
transform: "scale(0)", transform: "scale(0)",
opacity: 0, opacity: 0,
}} }}

View file

@ -120,6 +120,7 @@ export interface DetailsInfoProps {
data: DetailsContent; data: DetailsContent;
imdbData?: any; imdbData?: any;
rtData?: any; rtData?: any;
provider?: string;
} }
export interface DetailsRatingsProps { export interface DetailsRatingsProps {
@ -130,4 +131,5 @@ export interface DetailsRatingsProps {
mediaId?: number; mediaId?: number;
mediaType?: "movie" | "show"; mediaType?: "movie" | "show";
imdbId?: string; imdbId?: string;
provider?: string;
} }