feat: add feikuai player parsing

This commit is contained in:
Harold
2026-04-26 08:52:47 +08:00
parent 6bc012401a
commit 0aa6292d3f
2 changed files with 68 additions and 1 deletions
+27
View File
@@ -116,3 +116,30 @@ class TestFeikuaiSpider(unittest.TestCase):
vod["vod_play_url"], vod["vod_play_url"],
"第1集$/vodplay/1-1-1.html#第2集$/vodplay/1-1-2.html$$$第1集$/vodplay/1-2-1.html$$$夸克资源$https://pan.quark.cn/s/demo1$$$百度合集$https://pan.baidu.com/s/demo2", "第1集$/vodplay/1-1-1.html#第2集$/vodplay/1-1-2.html$$$第1集$/vodplay/1-2-1.html$$$夸克资源$https://pan.quark.cn/s/demo1$$$百度合集$https://pan.baidu.com/s/demo2",
) )
def test_base64decode_decodes_fixture(self):
self.assertEqual(
self.spider._base64decode("aHR0cHM6Ly9jZG4uZXhhbXBsZS5jb20vdjIubTN1OA=="),
"https://cdn.example.com/v2.m3u8",
)
@patch.object(Spider, "_request_html")
def test_player_content_supports_encrypt_1_and_encrypt_2(self, mock_request_html):
mock_request_html.side_effect = [
'<script>player_aaaa={"url":"https%3A//cdn.example.com/v1.m3u8","encrypt":"1"}</script>',
'<script>player_aaaa={"url":"aHR0cHM6Ly9jZG4uZXhhbXBsZS5jb20vdjIubTN1OA==","encrypt":"2"}</script>',
]
direct = self.spider.playerContent("feikuai", "/vodplay/1-1-1.html", {})
encoded = self.spider.playerContent("feikuai", "/vodplay/1-1-2.html", {})
self.assertEqual(direct["parse"], 0)
self.assertEqual(direct["url"], "https://cdn.example.com/v1.m3u8")
self.assertEqual(encoded["parse"], 0)
self.assertEqual(encoded["url"], "https://cdn.example.com/v2.m3u8")
@patch.object(Spider, "_request_html")
def test_player_content_falls_back_when_script_missing(self, mock_request_html):
mock_request_html.return_value = "<html><body>empty</body></html>"
result = self.spider.playerContent("feikuai", "/vodplay/1-1-3.html", {})
self.assertEqual(result["parse"], 1)
self.assertEqual(result["jx"], 1)
self.assertEqual(result["url"], "https://feikuai.tv/vodplay/1-1-3.html")
+41 -1
View File
@@ -1,7 +1,9 @@
# coding=utf-8 # coding=utf-8
import base64
import json
import re import re
import sys import sys
from urllib.parse import quote, urljoin from urllib.parse import quote, unquote, urljoin
from base.spider import Spider as BaseSpider from base.spider import Spider as BaseSpider
@@ -134,6 +136,21 @@ class Spider(BaseSpider):
def _join_group_urls(self, groups): def _join_group_urls(self, groups):
return "$$$".join("#".join(group) for group in groups if group) return "$$$".join("#".join(group) for group in groups if group)
def _base64decode(self, value):
try:
return base64.b64decode(str(value or "")).decode("utf-8")
except Exception:
return ""
def _extract_player_data(self, html):
matched = re.search(r"player_aaaa\s*=\s*(\{[\s\S]*?\})", str(html or ""))
if not matched:
return {}
try:
return json.loads(matched.group(1))
except Exception:
return {}
def categoryContent(self, tid, pg, filter, extend): def categoryContent(self, tid, pg, filter, extend):
page = max(1, int(pg)) page = max(1, int(pg))
url = self.host + f"/vodshow/{tid}--------{page}---.html" url = self.host + f"/vodshow/{tid}--------{page}---.html"
@@ -213,3 +230,26 @@ class Spider(BaseSpider):
"vod_play_url": self._join_group_urls(play_urls), "vod_play_url": self._join_group_urls(play_urls),
} }
return {"list": [vod]} return {"list": [vod]}
def playerContent(self, flag, id, vipFlags):
raw_id = str(id or "")
if raw_id.startswith(("http://", "https://")) and any(
raw_id.lower().split("?")[0].endswith(ext) for ext in [".m3u8", ".mp4", ".flv"]
):
return {"parse": 0, "jx": 0, "url": raw_id}
target = self.host + raw_id
data = self._extract_player_data(self._request_html(target))
raw_url = str(data.get("url") or "")
encrypt = str(data.get("encrypt") or "")
media_url = ""
if encrypt == "1":
media_url = unquote(raw_url)
elif encrypt == "2":
media_url = unquote(self._base64decode(raw_url))
elif raw_url:
media_url = raw_url
if media_url.startswith("http"):
return {"parse": 0, "jx": 0, "url": media_url}
return {"parse": 1, "jx": 1, "url": target}