From 08b5e9d34c023f8f4dcdac5da70cb3e717802c91 Mon Sep 17 00:00:00 2001 From: ranzhigg <126594517+ranzhigg@users.noreply.github.com> Date: Fri, 4 Sep 2026 07:51:22 +0800 Subject: [PATCH] fix(plex): skip episodes with missing season or episode numbers --- app/modules/plex/plex.py | 3 ++ tests/test_plex_episode_numbers.py | 48 ++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 tests/test_plex_episode_numbers.py diff --git a/app/modules/plex/plex.py b/app/modules/plex/plex.py index 382071cbe..6d08ee21d 100644 --- a/app/modules/plex/plex.py +++ b/app/modules/plex/plex.py @@ -353,6 +353,9 @@ class Plex: for episode in episodes: if season is not None and episode.seasonNumber != int(season): continue + # Plex 空条目可能缺少季集号,不能传入后续季集模型校验。 + if episode.seasonNumber is None or episode.index is None: + continue if episode.seasonNumber not in season_episodes: season_episodes[episode.seasonNumber] = [] season_episodes[episode.seasonNumber].append(episode.index) diff --git a/tests/test_plex_episode_numbers.py b/tests/test_plex_episode_numbers.py new file mode 100644 index 000000000..ec2a05da2 --- /dev/null +++ b/tests/test_plex_episode_numbers.py @@ -0,0 +1,48 @@ +from types import SimpleNamespace +from typing import Optional +from unittest.mock import Mock + +import pytest + +from app.modules.plex.plex import Plex +from app.schemas.types import MediaSource + + +@pytest.mark.parametrize( + ("numbers", "season", "expected"), + [ + ([(None, 1), (1, None), (None, None), (1, 2)], None, {1: [2]}), + ([(None, 1), (1, None), (1, 2), (2, 1)], 1, {1: [2]}), + ([(None, 1), (1, None), (None, None)], None, {}), + ([(0, 1), (1, 0), (1, 2)], None, {0: [1], 1: [0, 2]}), + ([(0, 1), (1, 1)], 0, {0: [1]}), + ], + ids=["missing-numbers", "season-filter", "all-invalid", "preserve-zero", "specials"], +) +def test_plex_tv_episodes_skips_missing_numbers( + numbers: list[tuple[Optional[int], Optional[int]]], + season: Optional[int], + expected: dict[int, list[int]], +) -> None: + """缺失季集号不能进入同步结果,合法的零值和季过滤应保持不变。""" + plex = Plex.__new__(Plex) + plex._plex = Mock() + show = Mock() + show.key = "/library/metadata/123" + show.guids = [{"id": "tmdb://12345"}] + show.episodes.return_value = [ + SimpleNamespace(seasonNumber=season_number, index=episode_number) + for season_number, episode_number in numbers + ] + plex._plex.fetchItem.return_value = show + + item_id, episodes = plex.get_tv_episodes( + item_id="123", + media_source=MediaSource.TMDB, + media_id="12345", + season=season, + ) + + assert item_id == show.key + assert episodes == expected + plex._plex.library.search.assert_not_called()