Fix NFO plot fallback by using en-US search overview when German result is empty

This commit is contained in:
2026-04-19 18:53:11 +02:00
parent 6ad14c03b5
commit 2274403899
2 changed files with 111 additions and 3 deletions

View File

@@ -174,6 +174,32 @@ class NFOService:
# Enrich with fallback languages for empty overview/tagline # Enrich with fallback languages for empty overview/tagline
# Pass search result overview as last resort fallback # Pass search result overview as last resort fallback
search_overview = tv_show.get("overview") or None search_overview = tv_show.get("overview") or None
if not search_overview:
try:
logger.debug(
"No overview in German search result, trying en-US search fallback for: %s",
search_name,
)
en_search_results = await self.tmdb_client.search_tv_show(
search_name,
language="en-US",
)
if en_search_results.get("results"):
en_match = self._find_best_match(
en_search_results["results"], search_name, year
)
search_overview = en_match.get("overview") or None
if search_overview:
logger.info(
"Using en-US search overview fallback for %s",
search_name,
)
except (TMDBAPIError, Exception) as exc:
logger.warning(
"Failed en-US search fallback for overview: %s",
exc,
)
details = await self._enrich_details_with_fallback( details = await self._enrich_details_with_fallback(
details, search_overview=search_overview details, search_overview=search_overview
) )

View File

@@ -424,7 +424,14 @@ class TestCreateTVShowNFO:
patch.object(nfo_service.tmdb_client, 'get_tv_show_content_ratings', new_callable=AsyncMock) as mock_ratings, \ patch.object(nfo_service.tmdb_client, 'get_tv_show_content_ratings', new_callable=AsyncMock) as mock_ratings, \
patch.object(nfo_service, '_download_media_files', new_callable=AsyncMock): patch.object(nfo_service, '_download_media_files', new_callable=AsyncMock):
mock_search.return_value = {"results": [{"id": 1429, "name": "Attack on Titan", "first_air_date": "2013-04-07"}]} mock_search.return_value = {
"results": [{
"id": 1429,
"name": "Attack on Titan",
"first_air_date": "2013-04-07",
"overview": "Several hundred years ago, humans were nearly...",
}]
}
mock_details.return_value = mock_tmdb_data mock_details.return_value = mock_tmdb_data
mock_ratings.return_value = mock_content_ratings_de mock_ratings.return_value = mock_content_ratings_de
@@ -463,7 +470,14 @@ class TestCreateTVShowNFO:
patch.object(nfo_service.tmdb_client, 'get_tv_show_content_ratings', new_callable=AsyncMock) as mock_ratings, \ patch.object(nfo_service.tmdb_client, 'get_tv_show_content_ratings', new_callable=AsyncMock) as mock_ratings, \
patch.object(nfo_service, '_download_media_files', new_callable=AsyncMock): patch.object(nfo_service, '_download_media_files', new_callable=AsyncMock):
mock_search.return_value = {"results": [{"id": 1429, "name": "Attack on Titan", "first_air_date": "2013-04-07"}]} mock_search.return_value = {
"results": [{
"id": 1429,
"name": "Attack on Titan",
"first_air_date": "2013-04-07",
"overview": "Several hundred years ago, humans were nearly...",
}]
}
mock_details.return_value = mock_tmdb_data mock_details.return_value = mock_tmdb_data
mock_ratings.return_value = mock_content_ratings_no_de mock_ratings.return_value = mock_content_ratings_no_de
@@ -749,7 +763,14 @@ class TestNFOServiceEdgeCases:
"poster_path": None, "backdrop_path": None "poster_path": None, "backdrop_path": None
} }
mock_search.return_value = {"results": [{"id": 1, "name": "Series", "first_air_date": "2020-01-01"}]} mock_search.return_value = {
"results": [{
"id": 1,
"name": "Series",
"first_air_date": "2020-01-01",
"overview": "Test overview.",
}]
}
mock_details.return_value = tmdb_data mock_details.return_value = tmdb_data
mock_ratings.return_value = {"results": []} mock_ratings.return_value = {"results": []}
@@ -1486,6 +1507,67 @@ class TestEnrichFallbackLanguages:
content = nfo_path.read_text(encoding="utf-8") content = nfo_path.read_text(encoding="utf-8")
assert "<plot>Search result overview text.</plot>" in content assert "<plot>Search result overview text.</plot>" in content
@pytest.mark.asyncio
async def test_en_us_search_fallback_when_german_search_overview_empty(
self, nfo_service, tmp_path
):
"""When the German search overview is empty, fallback to en-US search overview."""
series_folder = tmp_path / "Rare Anime"
series_folder.mkdir()
empty_data = {
"id": 77777, "name": "Rare Anime",
"original_name": "新しいアニメ", "first_air_date": "2025-01-01",
"overview": "",
"vote_average": 0, "vote_count": 0,
"status": "Continuing", "episode_run_time": [],
"genres": [], "networks": [], "production_countries": [],
"poster_path": None, "backdrop_path": None,
"external_ids": {}, "credits": {"cast": []},
"images": {"logos": []},
}
async def search_side_effect(query, language="de-DE", page=1):
if language == "en-US":
return {
"results": [{
"id": 77777,
"name": "Rare Anime",
"first_air_date": "2025-01-01",
"overview": "English search overview text.",
}],
}
return {
"results": [{
"id": 77777,
"name": "Rare Anime",
"first_air_date": "2025-01-01",
"overview": "",
}],
}
async def details_side_effect(tv_id, **kwargs):
return empty_data
with patch.object(nfo_service.tmdb_client, 'search_tv_show', new_callable=AsyncMock) as mock_search, \
patch.object(nfo_service.tmdb_client, 'get_tv_show_details', new_callable=AsyncMock) as mock_details, \
patch.object(nfo_service.tmdb_client, 'get_tv_show_content_ratings', new_callable=AsyncMock) as mock_ratings, \
patch.object(nfo_service, '_download_media_files', new_callable=AsyncMock):
mock_search.side_effect = search_side_effect
mock_details.side_effect = details_side_effect
mock_ratings.return_value = {"results": []}
nfo_path = await nfo_service.create_tvshow_nfo(
"Rare Anime", "Rare Anime",
download_poster=False, download_logo=False, download_fanart=False,
)
content = nfo_path.read_text(encoding="utf-8")
assert "<plot>English search overview text.</plot>" in content
assert mock_search.call_count == 2
assert mock_search.call_args_list[1].kwargs['language'] == 'en-US'
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_no_japanese_fallback_when_english_succeeds( async def test_no_japanese_fallback_when_english_succeeds(
self, nfo_service, tmp_path, self, nfo_service, tmp_path,