From 2b4ba9c927cb60ba0f8f31344377ac115d09f156 Mon Sep 17 00:00:00 2001 From: thsrite Date: Thu, 10 Sep 2026 17:59:08 +0800 Subject: [PATCH] =?UTF-8?q?fix(mediaserver):=20=E7=BF=BB=E9=A1=B5=E6=8C=89?= =?UTF-8?q?=E6=80=BB=E6=95=B0=E6=94=B6=E6=95=9B=EF=BC=8C=E5=89=A7=E9=9B=86?= =?UTF-8?q?=E5=AE=9A=E4=BD=8D=E5=91=BD=E4=B8=AD=E5=8D=B3=E8=BF=94=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 按代码审查意见再修两处,都与上一轮引入的关键字翻页有关: - 末页恰好装满时仍会多请求一页。响应里已带可靠总数,改为累计条数达到总数即 结束,只有没有总数时才按页是否满额判断;那次多余请求一旦失败,会把完整结果 误报成服务不可达 - 按标题定位剧集先把所有页读完再检查匹配,第一页已命中的目标会因后续页请求 失败而返回不可达,比改动前更差;改为逐行检查、命中即返回,只有在命中前中断 才报告不可达 补 2 条回归,均已确认在修正前失败。 --- app/modules/mediavault/mediavault.py | 31 ++++++++++++++++++---------- tests/test_mediavault_module.py | 31 ++++++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 13 deletions(-) diff --git a/app/modules/mediavault/mediavault.py b/app/modules/mediavault/mediavault.py index 9888593d4..199df08ca 100644 --- a/app/modules/mediavault/mediavault.py +++ b/app/modules/mediavault/mediavault.py @@ -244,10 +244,18 @@ class MediaVault: def rows() -> Generator[Dict[str, Any], Any, None]: result: Dict[str, Any] = first page = 1 + seen = 0 while True: batch = result.get("items") or [] + seen += len(batch) yield from batch - if len(batch) < self.PAGE_LIMIT: + total = result.get("total") + # 有可靠总数时以它为准:末页恰好满额也不再多发一次请求, + # 那次多余请求一旦失败会把完整结果误报成服务不可达 + if isinstance(total, int): + if seen >= total: + return + elif len(batch) < self.PAGE_LIMIT: return page += 1 following = self.__query_items( @@ -355,18 +363,19 @@ class MediaVault: if rows is None: return None try: - search_rows = list(rows) + # 逐行检查、命中即返回:预取全部页会让第一页已命中的目标 + # 因后续页请求失败被误报成服务不可达 + for row in rows: + item = self.__format_item_info(row) + if not item or item.title != title: + continue + if year and str(item.year) != str(year): + continue + if not MediaServerIdentityHelper.is_compatible(item, media_source, media_id): + continue + return str(item.item_id) except _SearchInterrupted: return None - for row in search_rows: - item = self.__format_item_info(row) - if not item or item.title != title: - continue - if year and str(item.year) != str(year): - continue - if not MediaServerIdentityHelper.is_compatible(item, media_source, media_id): - continue - return str(item.item_id) return "" def get_season_episode_ids(self, item_id: str, season: int) -> Dict[int, str]: diff --git a/tests/test_mediavault_module.py b/tests/test_mediavault_module.py index 1112e214a..232a63c3f 100644 --- a/tests/test_mediavault_module.py +++ b/tests/test_mediavault_module.py @@ -549,7 +549,8 @@ def _failing_second_page(total_rows: list): def test_get_movies_reports_unreachable_when_a_later_page_fails(): """翻页中途断连必须返回 None,不能把残缺结果当成「不在库中」。""" - rows = [_item_row(i, title=f"沙丘{i}") for i in range(MediaVault.PAGE_LIMIT)] + # total 必须超过一页,否则第一页就判定取完,构造不出「中途失败」 + rows = [_item_row(i, title=f"沙丘{i}") for i in range(MediaVault.PAGE_LIMIT + 1)] client = _client({"/items": _failing_second_page(rows)}) assert client.get_movies(title="沙丘") is None @@ -557,7 +558,33 @@ def test_get_movies_reports_unreachable_when_a_later_page_fails(): def test_find_series_reports_unreachable_when_a_later_page_fails(): """同上:剧集定位不能把断连误判成整部剧未入库。""" - rows = [_item_row(i, kind="Series", title=f"剧{i}") for i in range(MediaVault.PAGE_LIMIT)] + rows = [_item_row(i, kind="Series", title=f"剧{i}") for i in range(MediaVault.PAGE_LIMIT + 1)] client = _client({"/items": _failing_second_page(rows)}) assert client.get_tv_episodes(title="剧A") == (None, None) + + +def test_search_stops_at_reported_total_without_an_extra_request(): + """末页恰好满额时用 total 判定取完,不再多发一次可能失败的请求。""" + rows = [_item_row(i, title="沙丘", year=2021) for i in range(MediaVault.PAGE_LIMIT)] + client = _client({"/items": _paged_items(rows)}) + + matched = client.get_movies(title="沙丘") + + assert len(matched) == MediaVault.PAGE_LIMIT + assert [call["params"]["page"] for call in client._api.calls] == [1] + + +def test_find_series_returns_first_page_hit_even_if_a_later_page_fails(): + """第一页已命中就该直接返回,不因后续页失败被误报成服务不可达。""" + rows = [_item_row(0, kind="Series", title="剧A", year=2020)] + rows += [_item_row(i, kind="Series", title=f"其它{i}") for i in range(1, MediaVault.PAGE_LIMIT + 1)] + routes = { + "/items": _failing_second_page(rows), + "/items/id-0/episodes": Result(True, {"seasons": {"1": [1]}}), + } + + item_id, seasons = _client(routes).get_tv_episodes(title="剧A", year="2020") + + assert item_id == "id-0" + assert seasons == {1: [1]}