From 1205eebc2ea71ae15bee48140749a3083ae5cf91 Mon Sep 17 00:00:00 2001 From: Pas <74743263+Pasithea0@users.noreply.github.com> Date: Sat, 3 May 2025 13:59:42 -0600 Subject: [PATCH] scape tmdb data with languages --- src/backend/metadata/tmdb.ts | 23 ++++++++++------- src/components/overlays/DetailsModal.tsx | 14 ++++++++++- src/pages/discover/discoverContent.tsx | 23 ++++++++++------- src/pages/discover/hooks/useTMDBData.tsx | 14 ++++++++--- src/utils/language.ts | 32 ++++++++++++++++++++++++ 5 files changed, 83 insertions(+), 23 deletions(-) diff --git a/src/backend/metadata/tmdb.ts b/src/backend/metadata/tmdb.ts index 3ce0ec78..2f3287c0 100644 --- a/src/backend/metadata/tmdb.ts +++ b/src/backend/metadata/tmdb.ts @@ -1,7 +1,9 @@ import slugify from "slugify"; import { conf } from "@/setup/config"; +import { useLanguageStore } from "@/stores/language"; import { usePreferencesStore } from "@/stores/preferences"; +import { getTmdbLanguageCode } from "@/utils/language"; import { MediaItem } from "@/utils/mediaTypes"; import { getProxyUrls } from "@/utils/proxyUrls"; @@ -176,12 +178,20 @@ export async function get(url: string, params?: object): Promise { const proxyUrls = getProxyUrls(); const proxy = getNextProxy(proxyUrls); const shouldProxyTmdb = usePreferencesStore.getState().proxyTmdb; + const userLanguage = useLanguageStore.getState().language; + const formattedLanguage = getTmdbLanguageCode(userLanguage); + if (!apiKey) throw new Error("TMDB API key not set"); // directly writing parameters, otherwise it will start the first parameter in the proxied request as "&" instead of "?" because it doesnt understand its proxied const fullUrl = new URL(tmdbBaseUrl1 + url); - if (params) { - Object.entries(params).forEach(([key, value]) => { + const allParams = { + ...params, + language: formattedLanguage, + }; + + if (allParams) { + Object.entries(allParams).forEach(([key, value]) => { fullUrl.searchParams.append(key, String(value)); }); } @@ -205,18 +215,14 @@ export async function get(url: string, params?: object): Promise { return await mwFetch(encodeURI(url), { headers: tmdbHeaders, baseURL: tmdbBaseUrl1, - params: { - ...params, - }, + params: allParams, signal: abortOnTimeout(5000), }); } catch (err) { return mwFetch(encodeURI(url), { headers: tmdbHeaders, baseURL: tmdbBaseUrl2, - params: { - ...params, - }, + params: allParams, signal: abortOnTimeout(30000), }); } @@ -228,7 +234,6 @@ export async function multiSearch( const data = await get("search/multi", { query, include_adult: false, - language: "en-US", page: 1, }); // filter out results that aren't movies or shows diff --git a/src/components/overlays/DetailsModal.tsx b/src/components/overlays/DetailsModal.tsx index c2584329..a1876f93 100644 --- a/src/components/overlays/DetailsModal.tsx +++ b/src/components/overlays/DetailsModal.tsx @@ -13,9 +13,11 @@ import { Dropdown } from "@/components/form/Dropdown"; import { Icon, Icons } from "@/components/Icon"; import { hasAired } from "@/components/player/utils/aired"; import { useBookmarkStore } from "@/stores/bookmarks"; +import { useLanguageStore } from "@/stores/language"; import { useProgressStore } from "@/stores/progress"; import { shouldShowProgress } from "@/stores/progress/utils"; import { scrapeIMDb } from "@/utils/imdbScraper"; +import { getTmdbLanguageCode } from "@/utils/language"; import { useModal } from "./Modal"; import { OverlayPortal } from "./OverlayDisplay"; @@ -199,7 +201,17 @@ function DetailsContent({ setIsLoadingImdb(true); try { - const imdbMetadata = await scrapeIMDb(data.imdbId); + // Get the user's selected language and format it properly + const userLanguage = useLanguageStore.getState().language; + const formattedLanguage = getTmdbLanguageCode(userLanguage); + + // Pass the language to the scrapeIMDb function + const imdbMetadata = await scrapeIMDb( + data.imdbId, + undefined, + undefined, + formattedLanguage, + ); setImdbData(imdbMetadata); } catch (error) { console.error("Failed to fetch IMDb data:", error); diff --git a/src/pages/discover/discoverContent.tsx b/src/pages/discover/discoverContent.tsx index 3614a2a8..e761a6ad 100644 --- a/src/pages/discover/discoverContent.tsx +++ b/src/pages/discover/discoverContent.tsx @@ -14,6 +14,8 @@ import { } from "@/pages/discover/common"; import { conf } from "@/setup/config"; import { MediaItem } from "@/utils/mediaTypes"; +import { useLanguageStore } from "@/stores/language"; +import { getTmdbLanguageCode } from "@/utils/language"; import { CategoryButtons } from "./components/CategoryButtons"; import { LazyMediaCarousel } from "./components/LazyMediaCarousel"; @@ -132,6 +134,9 @@ export function DiscoverContent() { // ); const { t } = useTranslation(); + const userLanguage = useLanguageStore.getState().language; + const formattedLanguage = getTmdbLanguageCode(userLanguage); + // Only load data for the active tab const isMoviesTab = selectedCategory === "movies"; const isTVShowsTab = selectedCategory === "tvshows"; @@ -145,7 +150,7 @@ export function DiscoverContent() { try { const data = await get("/genre/tv/list", { api_key: conf().TMDB_READ_API_KEY, - language: "en-US", + language: formattedLanguage, }); // Fetch only the first 10 TV show genres setTVGenres(data.genres.slice(0, 10)); @@ -155,7 +160,7 @@ export function DiscoverContent() { }; fetchTVGenres(); - }, [isTVShowsTab]); + }, [isTVShowsTab, formattedLanguage]); // Fetch Movie genres useEffect(() => { @@ -165,7 +170,7 @@ export function DiscoverContent() { try { const data = await get("/genre/movie/list", { api_key: conf().TMDB_READ_API_KEY, - language: "en-US", + language: formattedLanguage, }); // Fetch only the first 12 genres @@ -176,7 +181,7 @@ export function DiscoverContent() { }; fetchGenres(); - }, [isMoviesTab]); + }, [isMoviesTab, formattedLanguage]); // Fetch Editor Picks Movies useEffect(() => { @@ -187,7 +192,7 @@ export function DiscoverContent() { const moviePromises = EDITOR_PICKS_MOVIES.map((item) => get(`/movie/${item.id}`, { api_key: conf().TMDB_READ_API_KEY, - language: "en-US", + language: formattedLanguage, append_to_response: "videos,images", }), ); @@ -202,7 +207,7 @@ export function DiscoverContent() { }; fetchEditorPicksMovies(); - }, [isEditorPicksTab]); + }, [isEditorPicksTab, formattedLanguage]); // Fetch Editor Picks TV Shows useEffect(() => { @@ -213,7 +218,7 @@ export function DiscoverContent() { const tvShowPromises = EDITOR_PICKS_TV_SHOWS.map((item) => get(`/tv/${item.id}`, { api_key: conf().TMDB_READ_API_KEY, - language: "en-US", + language: formattedLanguage, append_to_response: "videos,images", }), ); @@ -228,7 +233,7 @@ export function DiscoverContent() { }; fetchEditorPicksTVShows(); - }, [isEditorPicksTab]); + }, [isEditorPicksTab, formattedLanguage]); useEffect(() => { let countdownInterval: NodeJS.Timeout; @@ -290,7 +295,7 @@ export function DiscoverContent() { api_key: conf().TMDB_READ_API_KEY, with_watch_providers: id, watch_region: "US", - language: "en-US", + language: formattedLanguage, }); setData(data.results); } catch (error) { diff --git a/src/pages/discover/hooks/useTMDBData.tsx b/src/pages/discover/hooks/useTMDBData.tsx index c82e84b9..60f89782 100644 --- a/src/pages/discover/hooks/useTMDBData.tsx +++ b/src/pages/discover/hooks/useTMDBData.tsx @@ -1,6 +1,8 @@ import { useCallback, useEffect, useState } from "react"; import { get } from "@/backend/metadata/tmdb"; +import { useLanguageStore } from "@/stores/language"; +import { getTmdbLanguageCode } from "@/utils/language"; import { Category, Genre, Movie, TVShow } from "@/pages/discover/common"; import { conf } from "@/setup/config"; @@ -19,6 +21,8 @@ export function useTMDBData( [categoryName: string]: Movie[] | TVShow[]; }>({}); const [isLoading, setIsLoading] = useState(false); + const userLanguage = useLanguageStore.getState().language; + const formattedLanguage = getTmdbLanguageCode(userLanguage); // Unified fetch function const fetchMedia = useCallback( @@ -29,7 +33,7 @@ export function useTMDBData( for (let page = 1; page <= 2; page += 1) { const data = await get(endpoint, { api_key: conf().TMDB_READ_API_KEY, - language: "en-US", + language: formattedLanguage, page: page.toString(), ...(isGenre ? { with_genres: key } : {}), }); @@ -51,7 +55,7 @@ export function useTMDBData( return []; } }, - [mediaType], + [mediaType, formattedLanguage], ); // Fetch media for each genre @@ -104,6 +108,8 @@ export function useLazyTMDBData( ) { const [media, setMedia] = useState([]); const [isLoading, setIsLoading] = useState(false); + const userLanguage = useLanguageStore.getState().language; + const formattedLanguage = getTmdbLanguageCode(userLanguage); const fetchMedia = useCallback( async (endpoint: string, key: string, isGenre: boolean) => { @@ -113,7 +119,7 @@ export function useLazyTMDBData( // Only fetch one page for better performance const data = await get(endpoint, { api_key: conf().TMDB_READ_API_KEY, - language: "en-US", + language: formattedLanguage, page: "1", ...(isGenre ? { with_genres: key } : {}), }); @@ -130,7 +136,7 @@ export function useLazyTMDBData( return []; } }, - [mediaType], + [mediaType, formattedLanguage], ); useEffect(() => { diff --git a/src/utils/language.ts b/src/utils/language.ts index 9864b675..a611d7de 100644 --- a/src/utils/language.ts +++ b/src/utils/language.ts @@ -224,3 +224,35 @@ export function getLocaleInfo(locale: string): LocaleInfo | null { nativeName: output.nativeName[0] ?? undefined, }; } + +/** + * Converts a language code to a TMDB-compatible format (ISO 639-1 with region) + * @param language The language code to convert + * @returns A TMDB-compatible language code (e.g., "en-US", "el-GR") + */ +export function getTmdbLanguageCode(language: string): string { + // Handle empty or undefined + if (!language) return "en-US"; + + // If it already has a region code (e.g., "en-US"), use it directly + if (language.includes("-")) return language; + + // Handle special/custom languages by defaulting to English + if (language.length > 2 || Object.keys(extraLanguages).includes(language)) + return "en-US"; + + // For standard language codes, find the appropriate region from the existing defaultLanguageCodes array + const defaultCode = defaultLanguageCodes.find((code) => + code.startsWith(`${language}-`), + ); + + if (defaultCode) return defaultCode; + + // If we can't find a good match, create a standard format like "fr-FR" from "fr" + if (language.length === 2) { + return `${language}-${language.toUpperCase()}`; + } + + // Last resort fallback + return "en-US"; +}