diff --git a/config.example.toml b/config.example.toml index ed5f1b6..d762a1e 100644 --- a/config.example.toml +++ b/config.example.toml @@ -86,6 +86,13 @@ enabled = false api_key = "" user = "" +[metadata] +# Primary languages for metadata fetching (ISO 639-1 codes) +# When a TV show or movie's original language matches one of these languages, +# MediaManager will display the original title and fetch metadata in that language. +# Examples: ["en", "no", "da"] +primary_languages = ["en"] + [torrents] # qBittorrent settings [torrents.qbittorrent] diff --git a/media_manager/metadataProvider/config.py b/media_manager/metadataProvider/config.py index 1997612..0dd31b9 100644 --- a/media_manager/metadataProvider/config.py +++ b/media_manager/metadataProvider/config.py @@ -12,3 +12,6 @@ class TvdbConfig(BaseSettings): class MetadataProviderConfig(BaseSettings): tvdb: TvdbConfig = TvdbConfig() tmdb: TmdbConfig = TmdbConfig() + # ISO 639-1 language codes (e.g., ["en", "no", "sv"]) + # When media's original language matches one of these, original title and metadata will be used + primary_languages: list[str] = ["en"] diff --git a/media_manager/metadataProvider/tmdb.py b/media_manager/metadataProvider/tmdb.py index 45f27ea..b2cb672 100644 --- a/media_manager/metadataProvider/tmdb.py +++ b/media_manager/metadataProvider/tmdb.py @@ -22,12 +22,28 @@ class TmdbMetadataProvider(AbstractMetadataProvider): name = "tmdb" def __init__(self): - config = AllEncompassingConfig().metadata.tmdb - self.url = config.tmdb_relay_url + config = AllEncompassingConfig() + self.url = config.metadata.tmdb.tmdb_relay_url + self.primary_languages = config.metadata.primary_languages - def __get_show_metadata(self, id: int) -> dict: + def __get_language_param(self, original_language: str | None) -> str: + """ + Determine the language parameter to use for TMDB API calls. + Returns the original language if it's in primary_languages, otherwise returns English. + + :param original_language: The original language code (ISO 639-1) of the media + :return: Language parameter (ISO 639-1 format, e.g., 'en', 'no') + """ + if original_language and original_language in self.primary_languages: + return original_language + return "en" + + def __get_show_metadata(self, id: int, language: str = "en") -> dict: try: - response = requests.get(url=f"{self.url}/tv/shows/{id}") + response = requests.get( + url=f"{self.url}/tv/shows/{id}", + params={"language": language} + ) response.raise_for_status() return response.json() except requests.RequestException as e: @@ -39,10 +55,11 @@ class TmdbMetadataProvider(AbstractMetadataProvider): ) raise - def __get_season_metadata(self, show_id: int, season_number: int) -> dict: + def __get_season_metadata(self, show_id: int, season_number: int, language: str = "en") -> dict: try: response = requests.get( - url=f"{self.url}/tv/shows/{show_id}/{season_number}" + url=f"{self.url}/tv/shows/{show_id}/{season_number}", + params={"language": language} ) response.raise_for_status() return response.json() @@ -87,9 +104,12 @@ class TmdbMetadataProvider(AbstractMetadataProvider): ) raise - def __get_movie_metadata(self, id: int) -> dict: + def __get_movie_metadata(self, id: int, language: str = "en") -> dict: try: - response = requests.get(url=f"{self.url}/movies/{id}") + response = requests.get( + url=f"{self.url}/movies/{id}", + params={"language": language} + ) response.raise_for_status() return response.json() except requests.RequestException as e: @@ -132,7 +152,17 @@ class TmdbMetadataProvider(AbstractMetadataProvider): raise def download_show_poster_image(self, show: Show) -> bool: + # First fetch to get original_language show_metadata = self.__get_show_metadata(show.external_id) + original_language = show_metadata.get("original_language") + + # Determine which language to use + language = self.__get_language_param(original_language) + + # Fetch metadata in the appropriate language to get localized poster + if language != "en": + show_metadata = self.__get_show_metadata(show.external_id, language=language) + # downloading the poster # all pictures from TMDB should already be jpeg, so no need to convert if show_metadata["poster_path"] is not None: @@ -159,12 +189,23 @@ class TmdbMetadataProvider(AbstractMetadataProvider): :return: returns a ShowMetadata object :rtype: ShowMetadata """ + show_metadata = self.__get_show_metadata(id) + original_language = show_metadata.get("original_language") + + # Determine which language to use for metadata + language = self.__get_language_param(original_language) + + # Fetch show metadata in the appropriate language + show_metadata = self.__get_show_metadata(id, language=language) + season_list = [] # inserting all the metadata into the objects for season in show_metadata["seasons"]: season_metadata = self.__get_season_metadata( - show_id=show_metadata["id"], season_number=season["season_number"] + show_id=show_metadata["id"], + season_number=season["season_number"], + language=language ) episode_list = [] @@ -231,11 +272,21 @@ class TmdbMetadataProvider(AbstractMetadataProvider): ) else: poster_url = None + + # Determine which name to use based on primary_languages + original_language = result.get("original_language") + original_name = result.get("original_name") + display_name = result["name"] + + # Use original name if language is in primary_languages + if original_language and original_language in self.primary_languages: + display_name = original_name + formatted_results.append( MetaDataProviderSearchResult( poster_path=poster_url, overview=result["overview"], - name=result["name"], + name=display_name, external_id=result["id"], year=media_manager.metadataProvider.utils.get_year_from_date( result["first_air_date"] @@ -243,6 +294,7 @@ class TmdbMetadataProvider(AbstractMetadataProvider): metadata_provider=self.name, added=False, vote_average=result["vote_average"], + original_language=original_language, ) ) except Exception as e: @@ -252,12 +304,21 @@ class TmdbMetadataProvider(AbstractMetadataProvider): def get_movie_metadata(self, id: int = None) -> Movie: """ - :param id: the external id of the show + :param id: the external id of the movie :type id: int - :return: returns a ShowMetadata object - :rtype: ShowMetadata + :return: returns a Movie object + :rtype: Movie """ + movie_metadata = self.__get_movie_metadata(id=id) + original_language = movie_metadata.get("original_language") + + # Determine which language to use for metadata + language = self.__get_language_param(original_language) + + # Fetch movie metadata in the appropriate language + movie_metadata = self.__get_movie_metadata(id=id, language=language) + year = media_manager.metadataProvider.utils.get_year_from_date( movie_metadata["release_date"] ) @@ -300,11 +361,21 @@ class TmdbMetadataProvider(AbstractMetadataProvider): ) else: poster_url = None + + # Determine which name to use based on primary_languages + original_language = result.get("original_language") + original_title = result.get("original_title") + display_name = result["title"] + + # Use original title if language is in primary_languages + if original_language and original_language in self.primary_languages and original_title: + display_name = original_title + formatted_results.append( MetaDataProviderSearchResult( poster_path=poster_url, overview=result["overview"], - name=result["title"], + name=display_name, external_id=result["id"], year=media_manager.metadataProvider.utils.get_year_from_date( result["release_date"] @@ -312,6 +383,7 @@ class TmdbMetadataProvider(AbstractMetadataProvider): metadata_provider=self.name, added=False, vote_average=result["vote_average"], + original_language=original_language, ) ) except Exception as e: @@ -319,7 +391,17 @@ class TmdbMetadataProvider(AbstractMetadataProvider): return formatted_results def download_movie_poster_image(self, movie: Movie) -> bool: + # First fetch to get original_language movie_metadata = self.__get_movie_metadata(id=movie.external_id) + original_language = movie_metadata.get("original_language") + + # Determine which language to use + language = self.__get_language_param(original_language) + + # Fetch metadata in the appropriate language to get localized poster + if language != "en": + movie_metadata = self.__get_movie_metadata(id=movie.external_id, language=language) + # downloading the poster # all pictures from TMDB should already be jpeg, so no need to convert if movie_metadata["poster_path"] is not None: @@ -329,11 +411,11 @@ class TmdbMetadataProvider(AbstractMetadataProvider): if media_manager.metadataProvider.utils.download_poster_image( storage_path=self.storage_path, poster_url=poster_url, id=movie.id ): - log.info("Successfully downloaded poster image for show " + movie.name) + log.info("Successfully downloaded poster image for movie " + movie.name) else: - log.warning(f"download for image of show {movie.name} failed") + log.warning(f"download for image of movie {movie.name} failed") return False else: - log.warning(f"image for show {movie.name} could not be downloaded") + log.warning(f"image for movie {movie.name} could not be downloaded") return False return True diff --git a/metadata_relay/app/tmdb.py b/metadata_relay/app/tmdb.py index d2b3afc..053cb91 100644 --- a/metadata_relay/app/tmdb.py +++ b/metadata_relay/app/tmdb.py @@ -24,12 +24,12 @@ else: return Search().tv(page=page, query=query, include_adult=True) @router.get("/tv/shows/{show_id}") - async def get_tmdb_show(show_id: int): - return TV(show_id).info() + async def get_tmdb_show(show_id: int, language: str = "en"): + return TV(show_id).info(language=language) @router.get("/tv/shows/{show_id}/{season_number}") - async def get_tmdb_season(season_number: int, show_id: int): - return TV_Seasons(season_number=season_number, tv_id=show_id).info() + async def get_tmdb_season(season_number: int, show_id: int, language: str = "en"): + return TV_Seasons(season_number=season_number, tv_id=show_id).info(language=language) @router.get("/movies/trending") async def get_tmdb_trending_movies(): @@ -40,5 +40,5 @@ else: return Search().movie(page=page, query=query, include_adult=True) @router.get("/movies/{movie_id}") - async def get_tmdb_movie(movie_id: int): - return Movies(movie_id).info() + async def get_tmdb_movie(movie_id: int, language: str = "en"): + return Movies(movie_id).info(language=language)