Skip to content

Commit 99a9fa9

Browse files
daftenclaude
andcommitted
Stop resending TMDB base show fields on every season batch
fetch_and_cache_seasons() batches season requests to stay under TMDB's append_to_response item cap, but every batch included the full base-show fields (recommendations, external_ids, aggregate_credits, alternative_titles, watch/providers) in append_to_response, not just the first. Those fields describe the show, not a season, so they can't change between batches of the same fetch - refetching them per batch was pure waste that scales with season count (a 9-season show already pays for it twice; 20+ seasons would pay 3-4x). Only the first batch now requests the base fields and refreshes the cached tv_data from that response; later batches request only their season-specific append_to_response and reuse fetched_tv_data from the first batch. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 3ed77c8 commit 99a9fa9

2 files changed

Lines changed: 54 additions & 20 deletions

File tree

src/app/providers/tmdb.py

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -900,17 +900,30 @@ def fetch_and_cache_seasons(media_id, season_numbers, tv_data, language=None):
900900
result_data = {}
901901

902902
for i in range(0, len(season_numbers), max_seasons_per_request):
903+
is_first_batch = i == 0
903904
season_subset = season_numbers[i : i + max_seasons_per_request]
904905
append_text = ",".join(
905906
template.format(season=season)
906907
for season in season_subset
907908
for template in TV_DETAIL_SEASON_APPEND_RESPONSES
908909
)
909910

910-
params = {
911-
**base_params(language),
912-
"append_to_response": f"{TV_DETAIL_APPEND_RESPONSES},{append_text}",
913-
}
911+
# The base show fields (recommendations, external_ids,
912+
# aggregate_credits, alternative_titles, watch/providers) describe
913+
# the show, not a season, so they can't change between batches of
914+
# the same fetch. Only the first batch needs to ask TMDB for them;
915+
# later batches reuse fetched_tv_data from that first response
916+
# instead of paying for it again (#512).
917+
if is_first_batch:
918+
params = {
919+
**base_params(language),
920+
"append_to_response": f"{TV_DETAIL_APPEND_RESPONSES},{append_text}",
921+
}
922+
else:
923+
params = {
924+
**base_params(language),
925+
"append_to_response": append_text,
926+
}
914927

915928
try:
916929
response = services.api_request(
@@ -922,24 +935,26 @@ def fetch_and_cache_seasons(media_id, season_numbers, tv_data, language=None):
922935
except requests.exceptions.HTTPError as error:
923936
handle_error(error)
924937

925-
# Always refresh the root TV payload from the same TMDB response so
926-
# season fetches can pick up updated external_ids (including tvdb_id)
927-
# even when the show cache already existed before this request.
928-
refreshed_tv_data = process_tv(response, media_id=media_id)
929-
if not refreshed_tv_data.get("tvdb_id"):
930-
discovered_tvdb_id = _discover_tmdb_tvdb_id_from_search(
931-
media_id,
932-
tv_data=refreshed_tv_data,
933-
)
934-
if discovered_tvdb_id:
935-
refreshed_tv_data, _ = _apply_tvdb_id_override_to_tv_data(
938+
if is_first_batch:
939+
# Always refresh the root TV payload from the same TMDB response
940+
# so season fetches can pick up updated external_ids (including
941+
# tvdb_id) even when the show cache already existed before this
942+
# request.
943+
refreshed_tv_data = process_tv(response, media_id=media_id)
944+
if not refreshed_tv_data.get("tvdb_id"):
945+
discovered_tvdb_id = _discover_tmdb_tvdb_id_from_search(
936946
media_id,
937-
refreshed_tv_data,
947+
tv_data=refreshed_tv_data,
938948
)
939-
tv_cache_key = _tv_cache_key(media_id, language)
940-
if fetched_tv_data is None or fetched_tv_data != refreshed_tv_data:
941-
fetched_tv_data = refreshed_tv_data
942-
cache.set(tv_cache_key, fetched_tv_data)
949+
if discovered_tvdb_id:
950+
refreshed_tv_data, _ = _apply_tvdb_id_override_to_tv_data(
951+
media_id,
952+
refreshed_tv_data,
953+
)
954+
tv_cache_key = _tv_cache_key(media_id, language)
955+
if fetched_tv_data is None or fetched_tv_data != refreshed_tv_data:
956+
fetched_tv_data = refreshed_tv_data
957+
cache.set(tv_cache_key, fetched_tv_data)
943958

944959
# Process and cache each season
945960
for season_number in season_subset:

src/app/tests/providers/test_metadata.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,6 +1030,25 @@ def _mock_api_request(source, _method, url, params=None):
10301030
self.assertIn("season/8", result)
10311031
self.assertEqual(result["season/8"]["season_number"], 8)
10321032

1033+
# The base show fields (recommendations, external_ids,
1034+
# aggregate_credits, alternative_titles, watch/providers) don't
1035+
# change between batches for the same show, so they should only be
1036+
# requested on the first batch and reused for the rest (#512).
1037+
# Compare exact comma-separated tokens (not substrings), since
1038+
# "watch/providers" is also a suffix of the per-season
1039+
# "season/N/watch/providers" append token.
1040+
base_fields = set(tmdb.TV_DETAIL_APPEND_RESPONSES.split(","))
1041+
first_batch_fields = set(append_requests[0].split(","))
1042+
second_batch_fields = set(append_requests[1].split(","))
1043+
self.assertTrue(
1044+
base_fields.issubset(first_batch_fields),
1045+
"First batch should request the base show fields.",
1046+
)
1047+
self.assertFalse(
1048+
base_fields & second_batch_fields,
1049+
"Subsequent batches should not re-request the base show fields.",
1050+
)
1051+
10331052
@patch("app.providers.tvdb.search")
10341053
@patch("app.providers.tmdb.services.api_request")
10351054
@override_settings(TVDB_API_KEY="test-tvdb-key")

0 commit comments

Comments
 (0)