LibVIO解析网盘
This commit is contained in:
+43
-3
@@ -12,7 +12,7 @@ sys.path.append("..")
|
|||||||
class Spider(BaseSpider):
|
class Spider(BaseSpider):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.name = "LibVIO"
|
self.name = "LibVIO"
|
||||||
self.host = "https://libvio.site"
|
self.host = "https://www.libvio.la"
|
||||||
self.headers = {
|
self.headers = {
|
||||||
"User-Agent": (
|
"User-Agent": (
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
|
||||||
@@ -181,6 +181,13 @@ class Spider(BaseSpider):
|
|||||||
seen.add(play_id)
|
seen.add(play_id)
|
||||||
episodes.append(f"{name}${play_id}")
|
episodes.append(f"{name}${play_id}")
|
||||||
|
|
||||||
|
netdisk_groups = self._extract_netdisk_sources(html)
|
||||||
|
play_from = ["LibVIO"] if episodes else []
|
||||||
|
play_url = ["#".join(episodes)] if episodes else []
|
||||||
|
for group in netdisk_groups:
|
||||||
|
play_from.append(group["from"])
|
||||||
|
play_url.append(group["urls"])
|
||||||
|
|
||||||
vod = {
|
vod = {
|
||||||
"vod_id": vod_id,
|
"vod_id": vod_id,
|
||||||
"path": self._build_detail_request_url(vod_id),
|
"path": self._build_detail_request_url(vod_id),
|
||||||
@@ -189,8 +196,8 @@ class Spider(BaseSpider):
|
|||||||
"vod_tag": "",
|
"vod_tag": "",
|
||||||
"vod_time": "",
|
"vod_time": "",
|
||||||
"vod_remarks": "",
|
"vod_remarks": "",
|
||||||
"vod_play_from": "LibVIO",
|
"vod_play_from": "$$$".join(play_from),
|
||||||
"vod_play_url": "#".join(episodes),
|
"vod_play_url": "$$$".join(play_url),
|
||||||
"type_name": self._extract_detail_field(detail_root, "类型"),
|
"type_name": self._extract_detail_field(detail_root, "类型"),
|
||||||
"vod_content": self._extract_detail_field(detail_root, "简介"),
|
"vod_content": self._extract_detail_field(detail_root, "简介"),
|
||||||
"vod_year": self._extract_detail_field(detail_root, "年份"),
|
"vod_year": self._extract_detail_field(detail_root, "年份"),
|
||||||
@@ -201,6 +208,33 @@ class Spider(BaseSpider):
|
|||||||
}
|
}
|
||||||
return {"list": [vod]}
|
return {"list": [vod]}
|
||||||
|
|
||||||
|
def _extract_netdisk_sources(self, html):
|
||||||
|
root = self.html(html)
|
||||||
|
if root is None:
|
||||||
|
return []
|
||||||
|
|
||||||
|
groups = []
|
||||||
|
for panel in root.xpath("//*[contains(@class,'netdisk-panel')]"):
|
||||||
|
heading = self._clean_text("".join(panel.xpath(".//h3[1]//text()")))
|
||||||
|
matched = re.search(r"\(([^()]+)\)", heading)
|
||||||
|
line_name = self._clean_text(matched.group(1) if matched else heading.replace("视频下载", "")) or "网盘资源"
|
||||||
|
|
||||||
|
entries = []
|
||||||
|
seen_links = set()
|
||||||
|
for anchor in panel.xpath(".//a[contains(@class,'netdisk-item')]"):
|
||||||
|
href = ((anchor.xpath("./@href") or [""])[0]).strip()
|
||||||
|
url_text = self._clean_text("".join(anchor.xpath(".//*[contains(@class,'netdisk-url')][1]//text()")))
|
||||||
|
link = url_text or href
|
||||||
|
if not link or link in seen_links:
|
||||||
|
continue
|
||||||
|
seen_links.add(link)
|
||||||
|
title = self._clean_text("".join(anchor.xpath(".//*[contains(@class,'netdisk-name')][1]//text()"))) or line_name
|
||||||
|
entries.append(f"{title}${link}")
|
||||||
|
|
||||||
|
if entries:
|
||||||
|
groups.append({"from": line_name, "urls": "#".join(entries)})
|
||||||
|
return groups
|
||||||
|
|
||||||
def homeVideoContent(self):
|
def homeVideoContent(self):
|
||||||
html = self._request_html("/", expect_xpath="//*[contains(@class,'stui-vodlist__box')]")
|
html = self._request_html("/", expect_xpath="//*[contains(@class,'stui-vodlist__box')]")
|
||||||
return {"list": self._parse_list_cards(html)}
|
return {"list": self._parse_list_cards(html)}
|
||||||
@@ -256,6 +290,12 @@ class Spider(BaseSpider):
|
|||||||
return self._request_html(f"/static/player/{source}.js", referer=self.host + "/")
|
return self._request_html(f"/static/player/{source}.js", referer=self.host + "/")
|
||||||
|
|
||||||
def playerContent(self, flag, id, vipFlags):
|
def playerContent(self, flag, id, vipFlags):
|
||||||
|
raw_id = str(id or "").strip()
|
||||||
|
if raw_id.startswith(("https://", "http://")) and any(
|
||||||
|
token in raw_id for token in ("drive.uc.cn", "pan.quark.cn", "pan.baidu.com", "alipan.com", "aliyundrive.com")
|
||||||
|
):
|
||||||
|
return {"parse": 0, "playUrl": "", "url": raw_id}
|
||||||
|
|
||||||
play_page_url = self._build_play_request_url(id)
|
play_page_url = self._build_play_request_url(id)
|
||||||
detail_html = self._request_html(play_page_url, referer=self.host + "/")
|
detail_html = self._request_html(play_page_url, referer=self.host + "/")
|
||||||
config = self._parse_player_config(detail_html)
|
config = self._parse_player_config(detail_html)
|
||||||
|
|||||||
+60
-8
@@ -33,7 +33,7 @@ class TestLibVioSpider(unittest.TestCase):
|
|||||||
{
|
{
|
||||||
"vod_id": "456",
|
"vod_id": "456",
|
||||||
"vod_name": "示例影片",
|
"vod_name": "示例影片",
|
||||||
"vod_pic": "https://libvio.site/cover.jpg",
|
"vod_pic": "https://www.libvio.la/cover.jpg",
|
||||||
"vod_remarks": "更新至10集",
|
"vod_remarks": "更新至10集",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -75,7 +75,7 @@ class TestLibVioSpider(unittest.TestCase):
|
|||||||
self.assertEqual(result["list"][0]["vod_id"], "333")
|
self.assertEqual(result["list"][0]["vod_id"], "333")
|
||||||
self.assertEqual(result["list"][0]["vod_name"], "搜索影片")
|
self.assertEqual(result["list"][0]["vod_name"], "搜索影片")
|
||||||
|
|
||||||
def test_parse_detail_page_extracts_fields_and_filters_pan_sources(self):
|
def test_parse_detail_page_extracts_fields_and_preserves_pan_sources(self):
|
||||||
html = """
|
html = """
|
||||||
<div class="stui-content__thumb">
|
<div class="stui-content__thumb">
|
||||||
<img data-original="/poster.jpg" />
|
<img data-original="/poster.jpg" />
|
||||||
@@ -98,11 +98,24 @@ class TestLibVioSpider(unittest.TestCase):
|
|||||||
<ul class="stui-content__playlist clearfix">
|
<ul class="stui-content__playlist clearfix">
|
||||||
<li><a href="/play/pan-1.html">网盘</a></li>
|
<li><a href="/play/pan-1.html">网盘</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<div class="playlist-panel netdisk-panel">
|
||||||
|
<div class="panel-head netdisk-head">
|
||||||
|
<div class="netdisk-head-inner">
|
||||||
|
<h3>视频下载(UC)</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="netdisk-list">
|
||||||
|
<a class="netdisk-item" href="https://drive.uc.cn/s/e1532998c2bf4?public=1" target="_blank">
|
||||||
|
<span class="netdisk-name">合集</span>
|
||||||
|
<span class="netdisk-url">https://drive.uc.cn/s/e1532998c2bf4?public=1</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
"""
|
"""
|
||||||
result = self.spider._parse_detail_page(html, "999")
|
result = self.spider._parse_detail_page(html, "999")
|
||||||
vod = result["list"][0]
|
vod = result["list"][0]
|
||||||
self.assertEqual(vod["vod_id"], "999")
|
self.assertEqual(vod["vod_id"], "999")
|
||||||
self.assertEqual(vod["path"], "https://libvio.site/detail/999.html")
|
self.assertEqual(vod["path"], "https://www.libvio.la/detail/999.html")
|
||||||
self.assertEqual(vod["vod_name"], "示例剧")
|
self.assertEqual(vod["vod_name"], "示例剧")
|
||||||
self.assertEqual(vod["type_name"], "剧情")
|
self.assertEqual(vod["type_name"], "剧情")
|
||||||
self.assertEqual(vod["vod_area"], "大陆")
|
self.assertEqual(vod["vod_area"], "大陆")
|
||||||
@@ -110,8 +123,36 @@ class TestLibVioSpider(unittest.TestCase):
|
|||||||
self.assertEqual(vod["vod_director"], "张三")
|
self.assertEqual(vod["vod_director"], "张三")
|
||||||
self.assertEqual(vod["vod_actor"], "李四,王五")
|
self.assertEqual(vod["vod_actor"], "李四,王五")
|
||||||
self.assertEqual(vod["vod_content"], "一段剧情简介")
|
self.assertEqual(vod["vod_content"], "一段剧情简介")
|
||||||
self.assertEqual(vod["vod_play_from"], "LibVIO")
|
self.assertEqual(vod["vod_play_from"], "LibVIO$$$UC")
|
||||||
self.assertEqual(vod["vod_play_url"], "第1集$999-1-1#第2集$999-1-2")
|
self.assertEqual(
|
||||||
|
vod["vod_play_url"],
|
||||||
|
"第1集$999-1-1#第2集$999-1-2$$$合集$https://drive.uc.cn/s/e1532998c2bf4?public=1",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_extract_netdisk_sources_deduplicates_same_link(self):
|
||||||
|
html = """
|
||||||
|
<div class="playlist-panel netdisk-panel">
|
||||||
|
<div class="panel-head netdisk-head">
|
||||||
|
<div class="netdisk-head-inner">
|
||||||
|
<h3>视频下载(UC)</h3>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="netdisk-list">
|
||||||
|
<a class="netdisk-item" href="https://drive.uc.cn/s/e1532998c2bf4?public=1" target="_blank">
|
||||||
|
<span class="netdisk-name">合集</span>
|
||||||
|
<span class="netdisk-url">https://drive.uc.cn/s/e1532998c2bf4?public=1</span>
|
||||||
|
</a>
|
||||||
|
<a class="netdisk-item" href="https://drive.uc.cn/s/e1532998c2bf4?public=1" target="_blank">
|
||||||
|
<span class="netdisk-name">一键复制</span>
|
||||||
|
<span class="netdisk-url">https://drive.uc.cn/s/e1532998c2bf4?public=1</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
self.assertEqual(
|
||||||
|
self.spider._extract_netdisk_sources(html),
|
||||||
|
[{"from": "UC", "urls": "合集$https://drive.uc.cn/s/e1532998c2bf4?public=1"}],
|
||||||
|
)
|
||||||
|
|
||||||
@patch.object(Spider, "_request_html")
|
@patch.object(Spider, "_request_html")
|
||||||
def test_detail_content_builds_detail_request_url_from_vod_id(self, mock_request_html):
|
def test_detail_content_builds_detail_request_url_from_vod_id(self, mock_request_html):
|
||||||
@@ -122,7 +163,7 @@ class TestLibVioSpider(unittest.TestCase):
|
|||||||
</ul>
|
</ul>
|
||||||
"""
|
"""
|
||||||
result = self.spider.detailContent(["123"])
|
result = self.spider.detailContent(["123"])
|
||||||
self.assertEqual(mock_request_html.call_args.args[0], "https://libvio.site/detail/123.html")
|
self.assertEqual(mock_request_html.call_args.args[0], "https://www.libvio.la/detail/123.html")
|
||||||
self.assertEqual(result["list"][0]["vod_id"], "123")
|
self.assertEqual(result["list"][0]["vod_id"], "123")
|
||||||
|
|
||||||
def test_extract_player_config_reads_json_assignment(self):
|
def test_extract_player_config_reads_json_assignment(self):
|
||||||
@@ -132,7 +173,7 @@ class TestLibVioSpider(unittest.TestCase):
|
|||||||
|
|
||||||
def test_extract_play_api_base_reads_player_js(self):
|
def test_extract_play_api_base_reads_player_js(self):
|
||||||
body = 'var player={}; src="/player/api.php?url=";'
|
body = 'var player={}; src="/player/api.php?url=";'
|
||||||
self.assertEqual(self.spider._extract_play_api_base(body), "https://libvio.site/player/api.php?url=")
|
self.assertEqual(self.spider._extract_play_api_base(body), "https://www.libvio.la/player/api.php?url=")
|
||||||
|
|
||||||
@patch.object(Spider, "_request_html")
|
@patch.object(Spider, "_request_html")
|
||||||
def test_player_content_resolves_direct_api_url(self, mock_request_html):
|
def test_player_content_resolves_direct_api_url(self, mock_request_html):
|
||||||
@@ -144,13 +185,24 @@ class TestLibVioSpider(unittest.TestCase):
|
|||||||
result = self.spider.playerContent("LibVIO", "999-1-1", {})
|
result = self.spider.playerContent("LibVIO", "999-1-1", {})
|
||||||
self.assertEqual(result["parse"], 0)
|
self.assertEqual(result["parse"], 0)
|
||||||
self.assertEqual(result["url"], "https://video.example/final.m3u8")
|
self.assertEqual(result["url"], "https://video.example/final.m3u8")
|
||||||
self.assertEqual(result["header"]["Referer"], "https://libvio.site/")
|
self.assertEqual(result["header"]["Referer"], "https://www.libvio.la/")
|
||||||
|
|
||||||
@patch.object(Spider, "_request_html")
|
@patch.object(Spider, "_request_html")
|
||||||
def test_player_content_returns_empty_for_pan_source(self, mock_request_html):
|
def test_player_content_returns_empty_for_pan_source(self, mock_request_html):
|
||||||
mock_request_html.return_value = '<script>var player_x={"url":"abc","from":"kuake"};</script>'
|
mock_request_html.return_value = '<script>var player_x={"url":"abc","from":"kuake"};</script>'
|
||||||
self.assertEqual(self.spider.playerContent("LibVIO", "999-1-1", {}), {"parse": 0, "playUrl": "", "url": ""})
|
self.assertEqual(self.spider.playerContent("LibVIO", "999-1-1", {}), {"parse": 0, "playUrl": "", "url": ""})
|
||||||
|
|
||||||
|
def test_player_content_returns_direct_netdisk_link(self):
|
||||||
|
result = self.spider.playerContent("UC", "https://drive.uc.cn/s/e1532998c2bf4?public=1", {})
|
||||||
|
self.assertEqual(
|
||||||
|
result,
|
||||||
|
{
|
||||||
|
"parse": 0,
|
||||||
|
"playUrl": "",
|
||||||
|
"url": "https://drive.uc.cn/s/e1532998c2bf4?public=1",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user