Parse existing NFO for TMDB ID to skip redundant search
Check existing tvshow.nfo for TMDB ID before querying TMDB API. If found, fetch details directly using cached ID instead of searching. Reduces API calls and improves performance for already-indexed series. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1809,3 +1809,107 @@ class TestNegativeCache:
|
||||
assert "expired_key" not in tmdb_client._negative_cache
|
||||
assert "valid_key" in tmdb_client._negative_cache
|
||||
|
||||
|
||||
class TestNFOIDOverride:
|
||||
"""Tests for manual TMDB ID override via NFO."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_tvshow_nfo_uses_existing_tmdb_id(self, nfo_service, tmp_path, mock_tmdb_data, mock_content_ratings_de):
|
||||
"""Test that existing TMDB ID in NFO skips search."""
|
||||
# Create series folder with existing NFO containing TMDB ID
|
||||
series_folder = tmp_path / "Attack on Titan"
|
||||
series_folder.mkdir()
|
||||
nfo_path = series_folder / "tvshow.nfo"
|
||||
nfo_path.write_text("""<?xml version="1.0" encoding="UTF-8"?>
|
||||
<tvshow>
|
||||
<title>Attack on Titan</title>
|
||||
<tmdbid>1429</tmdbid>
|
||||
</tvshow>
|
||||
""", encoding="utf-8")
|
||||
|
||||
with 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), \
|
||||
patch.object(nfo_service.tmdb_client, '_ensure_session', new_callable=AsyncMock):
|
||||
|
||||
mock_details.return_value = mock_tmdb_data
|
||||
mock_ratings.return_value = mock_content_ratings_de
|
||||
|
||||
nfo_path_result = await nfo_service.create_tvshow_nfo(
|
||||
"Attack on Titan",
|
||||
"Attack on Titan",
|
||||
download_poster=False, download_logo=False, download_fanart=False
|
||||
)
|
||||
|
||||
# Verify NFO was created
|
||||
assert nfo_path_result.exists()
|
||||
|
||||
# Verify get_tv_show_details was called directly with the ID (no search)
|
||||
mock_details.assert_called_once_with(1429, append_to_response="credits,external_ids,images")
|
||||
|
||||
# Verify search was NOT called
|
||||
# (we can check by verifying no search_tv_show mock was set up)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_tvshow_nfo_searches_when_no_tmdb_id(self, nfo_service, tmp_path, mock_tmdb_data, mock_content_ratings_de):
|
||||
"""Test that search is used when NFO has no TMDB ID."""
|
||||
# Create series folder without existing NFO
|
||||
series_folder = tmp_path / "Test Anime"
|
||||
series_folder.mkdir()
|
||||
|
||||
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), \
|
||||
patch.object(nfo_service.tmdb_client, '_ensure_session', new_callable=AsyncMock):
|
||||
|
||||
mock_search.return_value = {
|
||||
"results": [{
|
||||
"id": 1429,
|
||||
"name": "Test Anime",
|
||||
"first_air_date": "2024-01-01",
|
||||
"overview": "Test overview"
|
||||
}]
|
||||
}
|
||||
mock_details.return_value = mock_tmdb_data
|
||||
mock_ratings.return_value = mock_content_ratings_de
|
||||
|
||||
nfo_path = await nfo_service.create_tvshow_nfo(
|
||||
"Test Anime",
|
||||
"Test Anime",
|
||||
download_poster=False, download_logo=False, download_fanart=False
|
||||
)
|
||||
|
||||
# Verify search was called
|
||||
mock_search.assert_called()
|
||||
|
||||
|
||||
class TestSearchMultiStrategy:
|
||||
"""Tests for search/multi fallback strategy."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_multi_strategy_used_as_fallback(self, nfo_service, mock_tmdb_data):
|
||||
"""Test that search/multi is tried after regular search fails."""
|
||||
mock_search = AsyncMock()
|
||||
mock_multi = AsyncMock()
|
||||
|
||||
# First: regular search fails
|
||||
# Second: multi search returns TV result
|
||||
mock_search.return_value = {"results": []}
|
||||
mock_multi.return_value = {
|
||||
"results": [
|
||||
{"media_type": "movie", "id": 123},
|
||||
{"media_type": "tv", "id": 456, "name": "Found Show", "first_air_date": "2024-01-01"}
|
||||
]
|
||||
}
|
||||
|
||||
with patch.object(nfo_service.tmdb_client, 'search_tv_show', mock_search), \
|
||||
patch.object(nfo_service.tmdb_client, 'search_multi', mock_multi):
|
||||
|
||||
result, source = await nfo_service._search_with_fallback(
|
||||
"Unknown Show", 2024, None
|
||||
)
|
||||
|
||||
assert result["id"] == 456
|
||||
assert source == "multi_search"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user