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;
}
export interface TraktNetworkResponse {
type: string;
platforms: string[];
count: number;
}
// Pagination utility
export function paginateResults(
results: TraktLatestResponse,
@ -106,6 +112,10 @@ export const getPopularMovies = () => fetchFromTrakt("/popularmovies");
export const getDiscoverContent = () =>
fetchFromTrakt<TraktDiscoverResponse>("/discover");
// Network content
export const getNetworkContent = (tmdbId: string) =>
fetchFromTrakt<TraktNetworkResponse>(`/network/${tmdbId}`);
// Type conversion utilities
export function convertToMediaType(type: TraktContentType): MWMediaType {
return type === "movie" ? MWMediaType.MOVIE : MWMediaType.SERIES;
@ -130,3 +140,14 @@ export const GENRE_TO_TRAKT_MAP = {
"28": "action", // Action
"18": "drama", // Drama
} 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 { useCopyToClipboard } from "react-use";
import { getNetworkContent } from "@/backend/metadata/traktApi";
import { TMDBContentTypes } from "@/backend/metadata/types/tmdb";
import { Icon, Icons } from "@/components/Icon";
import { useLanguageStore } from "@/stores/language";
@ -22,6 +23,9 @@ import { DetailsContentProps } from "./types";
export function DetailsContent({ data, minimal = false }: DetailsContentProps) {
const [imdbData, setImdbData] = useState<any>(null);
const [rtData, setRtData] = useState<any>(null);
const [providerData, setProviderData] = useState<string | undefined>(
undefined,
);
const [, setIsLoadingImdb] = useState(false);
const [showTrailer, setShowTrailer] = useState(false);
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(() => {
const fetchExternalData = async () => {
if (!data.imdbId) return;
@ -273,7 +301,12 @@ export function DetailsContent({ data, minimal = false }: DetailsContentProps) {
{/* Right Column - Details Info (1/3) */}
<div className="md:col-span-1">
<DetailsInfo data={data} imdbData={imdbData} rtData={rtData} />
<DetailsInfo
data={data}
imdbData={imdbData}
rtData={rtData}
provider={providerData}
/>
</div>
</div>

View file

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

View file

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

View file

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