From bac1bff775f47b4524eefb9a6ce18075b913e71e Mon Sep 17 00:00:00 2001 From: Harold <8866033@gmail.com> Date: Fri, 24 Apr 2026 17:35:51 +0800 Subject: [PATCH] feat: complete lumman spider --- py/tests/test_路漫漫.py | 28 +++++++++++++++++++++++++ py/路漫漫.py | 45 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/py/tests/test_路漫漫.py b/py/tests/test_路漫漫.py index 48dbb57..c738906 100644 --- a/py/tests/test_路漫漫.py +++ b/py/tests/test_路漫漫.py @@ -161,6 +161,34 @@ class TestLuManManSpider(unittest.TestCase): self.assertEqual(result["parse"], 1) self.assertEqual(result["url"], "https://www.lmm85.com/vod/play/1001-1-1.html") + @patch.object(Spider, "_post_json") + @patch.object(Spider, "_get_html") + def test_resolve_player_url_requests_parser_api_with_decrypted_token(self, mock_html, mock_post_json): + player = {"url": "source-id", "encrypt": "0", "from": "lineA"} + mock_html.side_effect = [ + 'document.querySelector("iframe").src = MacPlayer.Parse + MacPlayer.PlayUrl + "&type=m3u8";', + '', + ] + mock_post_json.return_value = {"url": "https://cdn.example.com/final.m3u8"} + with patch.object(self.spider, "_decrypt_token", return_value="decoded-token") as mock_decrypt: + result = self.spider._resolve_player_url(player, "https://www.lmm85.com/vod/play/1001-1-1.html") + self.assertEqual(result, "https://cdn.example.com/final.m3u8") + mock_decrypt.assert_called_once_with("YxF6M7yw8U7OQcW9t6Qx4w==") + mock_post_json.assert_called_once_with( + "https://www.lmm85.com/api.php", + {"vid": "vid-1", "t": "123", "token": "decoded-token", "act": "play", "play": "1"}, + referer="https://www.lmm85.com", + ) + + @patch.object(Spider, "_post_json", return_value={}) + @patch.object(Spider, "_get_html", return_value='document.querySelector("iframe").src = "";') + def test_resolve_player_url_returns_empty_when_chain_cannot_be_built(self, mock_html, mock_post_json): + result = self.spider._resolve_player_url( + {"url": "vid", "encrypt": "0", "from": "lineA"}, + "https://www.lmm85.com/vod/play/1.html", + ) + self.assertEqual(result, "") + if __name__ == "__main__": unittest.main() diff --git a/py/路漫漫.py b/py/路漫漫.py index 8944cb1..a20418d 100644 --- a/py/路漫漫.py +++ b/py/路漫漫.py @@ -234,10 +234,55 @@ class Spider(BaseSpider): "Origin": self.host, } + def _post_json(self, url, data, referer=None): + headers = self._build_player_headers(referer or self.host) + response = self.post(url, data=data, headers=headers, timeout=10, verify=False) + if response.status_code != 200: + return {} + try: + return json.loads(response.text or "{}") + except Exception: + return {} + + def _extract_js_src(self, js_text, video_url, play_page_url): + matched = re.search(r"\.src\s*=\s*(.*?);", str(js_text or "")) + if not matched: + return "" + raw = re.sub(r"[\+\s']", "", matched.group(1)) + raw = raw.replace("MacPlayer.PlayUrl", video_url) + raw = raw.replace("window.location.href", play_page_url) + raw = raw.replace("MacPlayer.Parse", self.host + "/?url=") + return raw + def _resolve_player_url(self, player, play_page_url): media = self._decode_player_url(player.get("url", ""), player.get("encrypt", "0")) if self._is_media_url(media): return media.split("&", 1)[0] + source = self._clean_text(player.get("from", "")) + if not media or not source: + return "" + js_url = f"{self.host}/static/player/{source}.js" + js_text = self._get_html(js_url, headers={"Referer": play_page_url}) + iframe_url = self._extract_js_src(js_text, media, play_page_url) + if not iframe_url or "type=" not in iframe_url: + return "" + iframe_html = self._get_html(iframe_url, headers={"Referer": self.host}) + if not re.search(r'vid\s*=\s*".+?"', iframe_html): + return "" + api_path = self.regStr(r'post\("(.*?)"', iframe_html) + if not api_path: + return "" + post_url = urljoin(self.host + "/", api_path.lstrip("/")) + token = self.regStr(r'token\s*=\s*"(.*?)"', iframe_html) + payload = { + "vid": self.regStr(r'vid\s*=\s*"(.*?)"', iframe_html), + "t": self.regStr(r'var\s+t\s*=\s*"(.*?)"', iframe_html), + "token": self._decrypt_token(token), + "act": self.regStr(r'act\s*=\s*"(.*?)"', iframe_html), + "play": self.regStr(r'play\s*=\s*"(.*?)"', iframe_html), + } + data = self._post_json(post_url, payload, referer=self.host) + return str(data.get("url", "")).strip() return "" def playerContent(self, flag, id, vipFlags):