From 103e342a1b9c531ed19474066b08fbfc92ab49c7 Mon Sep 17 00:00:00 2001 From: Harold <8866033@gmail.com> Date: Mon, 20 Apr 2026 13:10:31 +0800 Subject: [PATCH] feat: add playerContent with multi-mode play parsing Co-Authored-By: Claude Opus 4.7 --- py/tests/test_鲸鱼APP.py | 42 ++++++++++++++++++++++++++++++ py/鲸鱼APP.py | 55 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/py/tests/test_鲸鱼APP.py b/py/tests/test_鲸鱼APP.py index 3916b74..857d03e 100644 --- a/py/tests/test_鲸鱼APP.py +++ b/py/tests/test_鲸鱼APP.py @@ -211,3 +211,45 @@ class TestJingyuSpider(unittest.TestCase): self.assertEqual(len(result["list"]), 1) self.assertEqual(result["list"][0]["vod_name"], "繁花") self.assertNotIn("pagecount", result) + + def test_player_direct_parse_type_0(self): + play_id = "线路一@@direct@@http://parse.com,http://video.m3u8,token+t1,1,0" + result = self.spider.playerContent("线路一", play_id, {}) + self.assertEqual(result["parse"], 0) + self.assertEqual(result["url"], "http://video.m3u8") + + def test_player_prefix_parse_type_2(self): + play_id = "线路二@@direct@@http://parse.com,http://video.m3u8,token+t1,1,2" + result = self.spider.playerContent("线路二", play_id, {}) + self.assertEqual(result["parse"], 1) + self.assertEqual(result["url"], "http://parse.comhttp://video.m3u8") + + def test_player_vod_parse_default(self): + play_id = "线路三@@direct@@http://parse.com,http://video.m3u8,token+t1,1,1" + inner = json.dumps({"url": "http://decrypted.m3u8"}) + encrypted_inner = self.spider._aes_encrypt(json.dumps({"json": inner})) + + class FakeRsp: + status_code = 200 + encoding = "utf-8" + def json(self): + return {"data": encrypted_inner} + + self.spider.post = lambda url, **kwargs: FakeRsp() + self.spider.host = "http://test.com" + result = self.spider.playerContent("线路三", play_id, {}) + self.assertEqual(result["parse"], 0) + self.assertEqual(result["url"], "http://decrypted.m3u8") + + def test_player_fetch_parse_player_parse_type_2(self): + play_id = "线路四@@direct@@,http://video.m3u8,token+t1,2,1" + + class FakeFetchRsp: + def json(self): + return {"url": "http://fetched.m3u8"} + + self.spider.fetch = lambda url, **kwargs: FakeFetchRsp() + self.spider.host = "http://test.com" + result = self.spider.playerContent("线路四", play_id, {}) + self.assertEqual(result["parse"], 0) + self.assertEqual(result["url"], "http://fetched.m3u8") diff --git a/py/鲸鱼APP.py b/py/鲸鱼APP.py index d22ea2f..5ed92e4 100644 --- a/py/鲸鱼APP.py +++ b/py/鲸鱼APP.py @@ -352,3 +352,58 @@ class Spider(BaseSpider): "vod_remarks": f"{i.get('vod_year', '')} {i.get('vod_class', '')}".strip(), } for i in filtered] return {"list": items, "page": int(pg), "limit": 90, "total": 999999} + + def playerContent(self, flag, id, vipFlags): + try: + parts = id.split("@@") + if len(parts) < 3: + return {"parse": 0, "jx": 0, "url": ""} + payload = parts[2] + + arr = payload.split(",") + if len(arr) < 5: + return {"parse": 0, "jx": 0, "url": ""} + + parse_api = arr[0] + kurl = arr[1] + token = arr[2].replace("token+", "") if arr[2].startswith("token+") else arr[2] + player_parse_type = arr[3] + parse_type = arr[4] + + try: + from urllib.parse import unquote + kurl = unquote(kurl) + except Exception: + pass + + header = {"User-Agent": "Dalvik/2.1.0 (Linux; Android 14)"} + + if parse_type == "0": + return {"parse": 0, "jx": 0, "url": kurl, "header": header} + + if parse_type == "2": + return {"parse": 1, "jx": 1, "url": parse_api + kurl, "header": header} + + if player_parse_type == "2": + try: + rsp = self.fetch(parse_api + kurl, headers={"User-Agent": self.ua}, timeout=10, verify=False) + fetched = rsp.json() + if fetched.get("url"): + return {"parse": 0, "jx": 0, "url": fetched["url"]} + except Exception: + pass + + encrypted_url = self._aes_encrypt(kurl) + res = self._api_post("vodParse", { + "parse_api": parse_api, + "url": encrypted_url, + "player_parse_type": player_parse_type, + "token": token, + }) + if not res or not res.get("json"): + return {"parse": 0, "jx": 0, "url": ""} + inner = json.loads(res["json"]) + return {"parse": 0, "jx": 0, "url": inner.get("url", "")} + except Exception as e: + self.log(f"播放解析失败: {e}") + return {"parse": 0, "jx": 0, "url": ""}