diff --git a/app/modules/indexer/parser/mtorrent.py b/app/modules/indexer/parser/mtorrent.py index 0df4f83df..dc00292fa 100644 --- a/app/modules/indexer/parser/mtorrent.py +++ b/app/modules/indexer/parser/mtorrent.py @@ -1,14 +1,15 @@ # -*- coding: utf-8 -*- import json -from typing import Optional, Tuple +from typing import Any, Optional, Tuple, cast from urllib.parse import urljoin -from app.runtime.log import logger from app.modules.indexer.parser import SiteParserBase, SiteSchema from app.domain import site as site_rules class MTorrentSiteUserInfo(SiteParserBase): + """使用 M-Team API 解析用户信息和当前做种数据。""" + schema = SiteSchema.MTorrent request_mode = "apikey" @@ -113,7 +114,7 @@ class MTorrentSiteUserInfo(SiteParserBase): def _parse_user_torrent_seeding_info(self, html_text: str, multi_page: Optional[bool] = False) -> Optional[str]: """ - 解析用户做种信息 + 累加当前页的做种数和体积,并根据列表分页元数据继续读取。 """ if not html_text: return None @@ -144,24 +145,30 @@ class MTorrentSiteUserInfo(SiteParserBase): self.seeding_size += page_seeding_size self.seeding_info.extend(page_seeding_info) - # 查询总做种数 - seeder_count = 0 + # 用户信息解析成功后已初始化分页参数,后续页数从做种列表响应中读取。 + torrent_params = cast(dict[str, Any], self._torrent_seeding_params) + page_number = int(torrent_params["pageNumber"]) + page_size = int(torrent_params["pageSize"]) try: - result = self._get_page_content( - url=urljoin(self._base_url, "api/tracker/myPeerStatus"), - params={"uid": self.userid}, - ) - if result: - seeder_info = json.loads(result) - seeder_count = int(seeder_info.get("data", {}).get("seeder") or 0) - except Exception as e: - logger.error(f"获取做种数失败: {str(e)}") - if not seeder_count: + total_pages = int(seeding_data.get("totalPages") or 0) + except (TypeError, ValueError): + total_pages = 0 + try: + total_torrents = int(seeding_data.get("total") or 0) + except (TypeError, ValueError): + total_torrents = 0 + + if total_pages: + has_next_page = page_number < total_pages + elif total_torrents: + has_next_page = page_number * page_size < total_torrents + else: + # 旧版响应未提供总量时,满页后再请求一次确认是否结束。 + has_next_page = len(torrents) >= page_size + if not has_next_page: return None - if self.seeding >= seeder_count: - return None - # 还有下一页 - self._torrent_seeding_params["pageNumber"] += 1 + + torrent_params["pageNumber"] = page_number + 1 return "" def _parse_message_unread_links(self, html_text: str, msg_links: list) -> Optional[str]: diff --git a/tests/fixtures/architecture/mypy-baseline.json b/tests/fixtures/architecture/mypy-baseline.json index 5372f1f49..b61627e33 100644 --- a/tests/fixtures/architecture/mypy-baseline.json +++ b/tests/fixtures/architecture/mypy-baseline.json @@ -1702,7 +1702,6 @@ "app/modules/indexer/parser/mtorrent.py": { "assignment": 11, "empty-body": 1, - "index": 2, "no-untyped-def": 6, "type-arg": 1 }, diff --git a/tests/test_mtorrent_parser.py b/tests/test_mtorrent_parser.py index dfeaf0176..92dc22262 100644 --- a/tests/test_mtorrent_parser.py +++ b/tests/test_mtorrent_parser.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- import json +import pytest + from app.modules.indexer.parser.mtorrent import MTorrentSiteUserInfo @@ -17,7 +19,7 @@ def _build_parser() -> MTorrentSiteUserInfo: ) -def test_mtorrent_seeding_skips_invalid_torrent_items(monkeypatch): +def test_mtorrent_seeding_skips_invalid_torrent_items(): """ MTorrent 返回空种子对象时应跳过异常条目并只统计有效做种。 """ @@ -30,11 +32,6 @@ def test_mtorrent_seeding_skips_invalid_torrent_items(monkeypatch): "userid": parser.userid, } - monkeypatch.setattr( - parser, - "_get_page_content", - lambda **_: json.dumps({"data": {"seeder": 1}}), - ) html_text = json.dumps( { "code": "0", @@ -55,3 +52,73 @@ def test_mtorrent_seeding_skips_invalid_torrent_items(monkeypatch): assert parser.seeding == 1 assert parser.seeding_size == 1024 assert parser.seeding_info == [[3, 1024]] + + +def test_mtorrent_seeding_paginates_from_list_metadata(monkeypatch): + """应按做种列表的分页元数据读取全部种子,不依赖 tracker 私有接口。""" + parser = _build_parser() + parser.userid = "1" + parser._torrent_seeding_params = { + "pageNumber": 1, + "pageSize": 2, + "type": "SEEDING", + "userid": parser.userid, + } + monkeypatch.setattr( + parser, + "_get_page_content", + lambda **_: pytest.fail("解析做种列表时不应请求 tracker 私有接口"), + ) + + first_page = json.dumps( + { + "code": "0", + "data": { + "total": 3, + "totalPages": 2, + "data": [ + {"torrent": {"size": "1024", "source": "3"}}, + {"torrent": {"size": "2048", "source": "4"}}, + ], + }, + } + ) + second_page = json.dumps( + { + "code": "0", + "data": { + "total": 3, + "totalPages": 2, + "data": [{"torrent": {"size": "4096", "source": "5"}}], + }, + } + ) + + assert parser._parse_user_torrent_seeding_info(first_page) == "" + assert parser._torrent_seeding_params["pageNumber"] == 2 + assert parser._parse_user_torrent_seeding_info(second_page) is None + assert parser.seeding == 3 + assert parser.seeding_size == 7168 + assert parser.seeding_info == [[3, 1024], [4, 2048], [5, 4096]] + + +def test_mtorrent_seeding_uses_page_size_when_totals_are_missing(): + """接口未返回总页数时,满页后继续读取并在空页结束。""" + parser = _build_parser() + parser._torrent_seeding_params = {"pageNumber": 1, "pageSize": 2} + full_page = json.dumps( + { + "code": "0", + "data": { + "data": [ + {"torrent": {"size": "1024", "source": "3"}}, + {"torrent": {"size": "2048", "source": "4"}}, + ] + }, + } + ) + empty_page = json.dumps({"code": "0", "data": {"data": []}}) + + assert parser._parse_user_torrent_seeding_info(full_page) == "" + assert parser._torrent_seeding_params["pageNumber"] == 2 + assert parser._parse_user_torrent_seeding_info(empty_page) is None