上传文件至「py」

This commit is contained in:
2026-08-14 11:25:10 +02:00
parent 1cd5c93451
commit 68c2058190
5 changed files with 1521 additions and 0 deletions
+249
View File
@@ -0,0 +1,249 @@
import requests
from bs4 import BeautifulSoup
import re
import sys
import json
import urllib.parse
from base.spider import Spider
from urllib.parse import quote, urljoin
sys.path.append('..')
xurl = "http://www.dgpengcheng.com"
headers = {
'User-Agent': 'Mozilla/5.0 (Linux; Android 13; M2102J2SC Build/TKQ1.221114.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/144.0.7559.31 Mobile Safari/537.36',
'Referer': xurl,
}
class Spider(Spider):
global xurl, headers
def getName(self):
return "星辰影院"
def init(self, extend):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def homeContent(self, filter):
classes = [
{"type_id": "1", "name": "电影"},
{"type_id": "2", "name": "电视剧"},
{"type_id": "3", "name": "综艺"},
{"type_id": "4", "name": "动漫"},
{"type_id": "28", "name": "纪录片"}
]
return {'class': classes}
def _parse_video_items(self, soup):
videos = []
seen_ids = set()
for li in soup.select('li'):
a_tag = li.select_one('a.stui-vodlist__thumb')
if not a_tag:
continue
href = a_tag.get('href', '')
if not href:
continue
vod_id = href if 'http' in href else xurl + href
if vod_id in seen_ids:
continue
seen_ids.add(vod_id)
img = a_tag.select_one('img')
title = a_tag.get('title', '')
if not title and img:
title = img.get('alt', '')
if not title:
title = a_tag.get_text(strip=True)
if not title:
continue
pic = a_tag.get('data-original', '')
if not pic and img:
pic = img.get('data-original') or img.get('src', '')
if pic and not pic.startswith('http'):
pic = xurl + ('' if pic.startswith('/') else '/') + pic
remark = ''
remark_span = a_tag.select_one('span.pic-text')
if remark_span:
remark = remark_span.get_text(strip=True)
videos.append({
"vod_id": vod_id,
"vod_name": title,
"vod_pic": pic,
"vod_remarks": remark
})
return videos
def homeVideoContent(self):
return {'list': self._parse_video_items(BeautifulSoup(requests.get(xurl, headers=headers).text, 'lxml'))}
def categoryContent(self, cid, pg, filter, ext):
page = int(pg) if pg else 1
url = f"{xurl}/vtype/{cid}-{page}.html"
videos = self._parse_video_items(BeautifulSoup(requests.get(url, headers=headers).text, 'lxml'))
return {
'list': videos,
'page': page,
'pagecount': 999,
'limit': 90,
'total': 9999
}
def detailContent(self, ids):
did = ids[0]
if not did.startswith('http'):
did = urljoin(xurl, did)
resp = requests.get(did, headers=headers, timeout=10)
soup = BeautifulSoup(resp.text, 'lxml')
info = {'vod_id': did}
thumb = soup.select_one('.stui-content__thumb img')
if thumb:
pic = thumb.get('data-original') or thumb.get('src', '')
if pic and pic.startswith('//'):
pic = 'https:' + pic
elif pic and not pic.startswith('http'):
pic = urljoin(xurl, pic)
info['vod_pic'] = pic
detail_div = soup.select_one('.stui-content')
if detail_div:
title_h1 = detail_div.select_one('h3.title')
if title_h1:
raw_title = title_h1.get_text(strip=True).replace('\n', '')
info['vod_name'] = raw_title
for p in detail_div.select('p.data'):
text = p.get_text(strip=True)
if '主演:' in text:
info['vod_actor'] = text.split('主演:')[-1].strip()
if '类型:' in text:
info['type_name'] = text.split('类型:')[-1].strip()
if '导演:' in text:
info['vod_director'] = text.split('导演:')[-1].strip()
if '状态:' in text:
info['vod_remarks'] = text.split('状态:')[-1].strip()
if '年代:' in text:
info['vod_year'] = text.split('年代:')[-1].strip()
if '地区:' in text:
info['vod_area'] = text.split('地区:')[-1].strip()
content_div = soup.select_one('.detail-content')
if content_div:
p = content_div.find('p')
if p:
text = p.get_text(strip=True)
else:
text = content_div.get_text(strip=True)
text = re.sub(r'^简介[:]\s*', '', text)
info['vod_content'] = text
else:
info['vod_content'] = ''
filter_lines = ['猜您喜欢', '同类型']
filter_titles = ['1080P', 'дрр滈凊']
ktabs = []
klists = []
seen_lines = set()
line_items = soup.select('.stui-pannel__head h3')
playlist_containers = soup.select('.stui-content__playlist')
for idx, tab in enumerate(line_items):
if idx >= len(playlist_containers):
break
line_name = tab.get_text(strip=True)
if not line_name or line_name in filter_lines:
continue
if line_name in seen_lines:
continue
container = playlist_containers[idx]
episode_links = container.select('li a')
if not episode_links:
continue
klist = []
for ep in episode_links:
ep_name = ep.get_text(strip=True)
ep_link = ep.get('href', '')
if ep_name in filter_titles:
continue
if ep_name and ep_link:
klist.append(f'{ep_name}${ep_link}')
if klist:
seen_lines.add(line_name)
ktabs.append(line_name)
klists.append('#'.join(klist))
info["vod_play_from"] = '$$$'.join(ktabs)
info["vod_play_url"] = '$$$'.join(klists)
return {'list': [info]}
def playerContent(self, flag, id, vipFlags):
try:
play_url = id if id.startswith(('http://', 'https://')) else xurl + id
html = requests.get(play_url, headers=headers, timeout=10).text
pattern = r'var\s+player_\w+\s*=\s*(\{[^;]+?\})\s*(?:;|</script)'
match = re.search(pattern, html, re.DOTALL)
video_url = ''
if match:
try:
json_str = match.group(1).strip()
json_str = re.sub(r',\s*}', '}', json_str)
data = json.loads(json_str)
video_url = data.get('url', '')
except Exception:
pass
if not video_url:
m3u8_match = re.search(r'["\']url["\']\s*:\s*["\'](https?://[^"\']+\.m3u8[^"\']*)["\']', html)
if m3u8_match:
video_url = m3u8_match.group(1)
if video_url:
parse = 0 if re.search(r'\.(m3u8|mp4|mkv|flv|ts)$', video_url, re.I) else 1
if parse:
video_url = play_url
else:
parse = 1
video_url = play_url
return {"parse": parse, "playUrl": "", "url": video_url, "header": headers}
except Exception as e:
print(f"player error: {e}")
return {"parse": 1, "playUrl": "", "url": xurl + id, "header": headers}
def searchContent(self, key, quick, page='1'):
page = int(page) if page else 1
encoded_key = urllib.parse.quote(key, safe='')
url = f"{xurl}/vodsearch/{encoded_key}----------{page}---.html"
soup = BeautifulSoup(requests.get(url, headers=headers).text, 'lxml')
videos = self._parse_video_items(soup)
return {
'list': videos,
'page': page,
'pagecount': 60,
'limit': 30,
'total': 999999
}
def localProxy(self, params):
if params['type'] == "m3u8":
return self.proxyM3u8(params)
elif params['type'] == "media":
return self.proxyMedia(params)
elif params['type'] == "ts":
return self.proxyTs(params)
return None
+546
View File
@@ -0,0 +1,546 @@
# coding=utf-8
# !/usr/bin/python
"""
作者 精彩一瞬间 内容均从互联网收集而来 仅供交流学习使用 严禁用于商业用途 请于24小时内删除
====================Diudiumiao====================
"""
from Crypto.Util.Padding import unpad
from Crypto.Util.Padding import pad
from urllib.parse import urlparse
from urllib.parse import unquote
from Crypto.Cipher import ARC4
from urllib.parse import quote
from base.spider import Spider
from Crypto.Cipher import AES
from datetime import datetime
from bs4 import BeautifulSoup
from base64 import b64decode
import concurrent.futures
import urllib.request
import urllib.parse
import datetime
import binascii
import requests
import hashlib
import base64
import json
import time
import hmac
import sys
import re
import os
sys.path.append('..')
xurl = "https://www.wasu.cn"
headerx = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36'
}
headerz = {
'accept': '*/*',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'cache-control': 'no-cache',
'origin': xurl,
'pragma': 'no-cache',
'priority': 'u=1, i',
'referer': xurl,
'sec-ch-ua': '"Microsoft Edge";v="143", "Chromium";v="143", "Not A(Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0',
}
class Spider(Spider):
def getName(self):
return "精彩"
def init(self, extend):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def homeVideoContent(self):
pass
def homeContent(self, filter):
result = {}
result = {"class": [{"type_id": "961", "type_name": "电影"},
{"type_id": "962", "type_name": "剧集"},
{"type_id": "963", "type_name": "少儿"},
{"type_id": "965", "type_name": "栏目"},
{"type_id": "966", "type_name": "新闻"}],
"list": [],
"filters": {"961": [{"key": "地区",
"name": "地区",
"value": [{"n": "全部", "v": ""},
{"n": "内地", "v": "内地"},
{"n": "港台", "v": "港台"},
{"n": "欧美", "v": "欧美"},
{"n": "日韩", "v": "日韩"},
{"n": "泰国", "v": "泰国"},
{"n": "其他", "v": "其他"}]},
{"key": "类型",
"name": "类型",
"value": [{"n": "全部", "v": ""},
{"n": "动作", "v": "动作"},
{"n": "科幻", "v": "科幻"},
{"n": "惊悚", "v": "惊悚"},
{"n": "冒险", "v": "冒险"},
{"n": "剧情", "v": "剧情"},
{"n": "励志", "v": "励志"},
{"n": "爱情", "v": "爱情"},
{"n": "喜剧", "v": "喜剧"},
{"n": "家庭", "v": "家庭"},
{"n": "历史", "v": "历史"},
{"n": "魔幻", "v": "魔幻"},
{"n": "恐怖", "v": "恐怖"},
{"n": "战争", "v": "战争"},
{"n": "武侠", "v": "武侠"}]},
{"key": "年代",
"name": "年代",
"value": [{"n": "全部", "v": ""},
{"n": "2025", "v": "2025"},
{"n": "2024", "v": "2024"},
{"n": "2023", "v": "2023"},
{"n": "2022", "v": "2022"},
{"n": "2021", "v": "2021"},
{"n": "2020", "v": "2020"},
{"n": "2019", "v": "2019"},
{"n": "2018", "v": "2018"},
{"n": "2017", "v": "2017"},
{"n": "2016", "v": "2016"},
{"n": "2015", "v": "2015"},
{"n": "2014", "v": "2014"},
{"n": "2013", "v": "2013"},
{"n": "2012", "v": "2012"},
{"n": "2011", "v": "2011"},
{"n": "2010", "v": "2010"}]}],
"962": [{"key": "地区",
"name": "地区",
"value": [{"n": "全部", "v": ""},
{"n": "内地", "v": "内地"},
{"n": "港台", "v": "港台"},
{"n": "日韩", "v": "日韩"},
{"n": "欧美", "v": "欧美"},
{"n": "泰国", "v": "泰国"},
{"n": "其他", "v": "其他"}]},
{"key": "类型",
"name": "类型",
"value": [{"n": "全部", "v": ""},
{"n": "都市", "v": "都市"},
{"n": "爱情", "v": "爱情"},
{"n": "战争", "v": "战争"},
{"n": "家庭", "v": "家庭"},
{"n": "悬疑", "v": "悬疑"},
{"n": "古装", "v": "古装"},
{"n": "短剧", "v": "短剧"},
{"n": "谍战", "v": "谍战"},
{"n": "喜剧", "v": "喜剧"},
{"n": "农村", "v": "农村"},
{"n": "刑侦", "v": "刑侦"},
{"n": "武侠", "v": "武侠"},
{"n": "历史", "v": "历史"}]},
{"key": "年代",
"name": "年代",
"value": [{"n": "全部", "v": ""},
{"n": "2025", "v": "2025"},
{"n": "2024", "v": "2024"},
{"n": "2023", "v": "2023"},
{"n": "2022", "v": "2022"},
{"n": "2021", "v": "2021"},
{"n": "2020", "v": "2020"},
{"n": "2019", "v": "2019"},
{"n": "2018", "v": "2018"},
{"n": "2017", "v": "2017"},
{"n": "2016", "v": "2016"},
{"n": "2015", "v": "2015"},
{"n": "2014", "v": "2014"},
{"n": "2013", "v": "2013"},
{"n": "2012", "v": "2012"},
{"n": "2011", "v": "2011"},
{"n": "2010", "v": "2010"}]}],
"963": [{"key": "地区",
"name": "地区",
"value": [{"n": "全部", "v": ""},
{"n": "内地", "v": "内地"},
{"n": "日韩", "v": "日韩"},
{"n": "欧美", "v": "欧美"},
{"n": "港台", "v": "港台"},
{"n": "其他", "v": "其他"}]},
{"key": "类型",
"name": "类型",
"value": [{"n": "全部", "v": ""},
{"n": "动作", "v": "动作"},
{"n": "冒险", "v": "冒险"},
{"n": "益智", "v": "益智"},
{"n": "亲子", "v": "亲子"},
{"n": "热血", "v": "热血"},
{"n": "剧情", "v": "剧情"},
{"n": "魔幻", "v": "魔幻"},
{"n": "励志", "v": "励志"},
{"n": "机战", "v": "机战"},
{"n": "搞笑", "v": "搞笑"},
{"n": "科幻", "v": "科幻"},
{"n": "治愈", "v": "治愈"},
{"n": "儿歌", "v": "儿歌"},
{"n": "教育", "v": "教育"},
{"n": "校园", "v": "校园"},
{"n": "童话", "v": "童话"},
{"n": "推理", "v": "推理"},
{"n": "怀旧", "v": "怀旧"},
{"n": "宠物", "v": "宠物"},
{"n": "舞蹈", "v": "舞蹈"}]},
{"key": "年代",
"name": "年代",
"value": [{"n": "全部", "v": ""},
{"n": "2025", "v": "2025"},
{"n": "2024", "v": "2024"},
{"n": "2023", "v": "2023"},
{"n": "2022", "v": "2022"},
{"n": "2021", "v": "2021"},
{"n": "2020", "v": "2020"},
{"n": "2019", "v": "2019"},
{"n": "2018", "v": "2018"},
{"n": "2017", "v": "2017"},
{"n": "2016", "v": "2016"},
{"n": "2015", "v": "2015"},
{"n": "2014", "v": "2014"},
{"n": "2013", "v": "2013"},
{"n": "2012", "v": "2012"},
{"n": "2011", "v": "2011"},
{"n": "2010", "v": "2010"}]}],
"965": [{"key": "地区",
"name": "地区",
"value": [{"n": "全部", "v": ""},
{"n": "内地", "v": "内地"},
{"n": "欧美", "v": "欧美"}]},
{"key": "类型",
"name": "类型",
"value": [{"n": "全部", "v": ""},
{"n": "文化", "v": "文化"},
{"n": "纪实", "v": "纪实"},
{"n": "访谈", "v": "访谈"},
{"n": "历史", "v": "历史"},
{"n": "美食", "v": "美食"},
{"n": "旅游", "v": "旅游"},
{"n": "时尚", "v": "时尚"},
{"n": "情感", "v": "情感"},
{"n": "生活", "v": "生活"},
{"n": "真人秀", "v": "真人秀"}]},
{"key": "年代",
"name": "年代",
"value": [{"n": "全部", "v": ""},
{"n": "2025", "v": "2025"},
{"n": "2024", "v": "2024"},
{"n": "2023", "v": "2023"},
{"n": "2022", "v": "2022"},
{"n": "2021", "v": "2021"},
{"n": "2020", "v": "2020"},
{"n": "2019", "v": "2019"},
{"n": "2018", "v": "2018"},
{"n": "2017", "v": "2017"},
{"n": "2016", "v": "2016"},
{"n": "2015", "v": "2015"},
{"n": "2014", "v": "2014"},
{"n": "2013", "v": "2013"},
{"n": "2012", "v": "2012"},
{"n": "2011", "v": "2011"},
{"n": "2010", "v": "2010"}]}],
"966": [{"key": "类型",
"name": "类型",
"value": [{"n": "全部", "v": ""},
{"n": "国内视野", "v": "国内视野"},
{"n": "国际纵览", "v": "国际纵览"},
{"n": "军事话题", "v": "军事话题"},
{"n": "社会百态", "v": "社会百态"},
{"n": "央视频", "v": "央视频"}]}]}}
return result
def generate_x_sign(self, secret_b64):
secret_key = self.decode_secret_key(secret_b64)
data = self.get_data_string()
signature = self.compute_signature(secret_key, data)
x_sign = self.encode_signature(signature)
return x_sign
def decode_secret_key(self, secret_b64):
return base64.b64decode(secret_b64)
def get_data_string(self):
return "{}"
def compute_signature(self, secret_key, data):
return hmac.new(secret_key, data.encode('utf-8'), hashlib.sha256).digest()
def encode_signature(self, signature):
return base64.b64encode(signature).decode('utf-8')
def get_current_app_key(self, xurl, headerx):
resp = self.fetch_index_page(xurl, headerx)
js_path = self.extract_js_path(resp.text)
js_url = f"{xurl}{js_path}"
js_content = self.fetch_js_content(js_url, headerx)
target_key_b64 = self.extract_target_key(js_content)
return target_key_b64
def fetch_index_page(self, xurl, headerx):
resp = requests.get(xurl, headers=headerx)
resp.raise_for_status()
return resp
def extract_js_path(self, html_content):
js_path_pattern = r'src="(/[\d\.]+/assets/js/index-[\w\.-]+\.js)"'
match = re.search(js_path_pattern, html_content)
return match.group(1)
def fetch_js_content(self, js_url, headerx):
js_resp = requests.get(js_url, headers=headerx)
js_resp.raise_for_status()
return js_resp.text
def extract_target_key(self, js_content):
key_pattern = r'const \w+="([^"]+)",\w+="([^"]+)",\w+="([^"]+)";'
key_match = re.search(key_pattern, js_content)
return key_match.group(2)
def get_headers(self, sign1, xurl):
return {
'accept': 'application/json, text/plain, */*',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'cache-control': 'no-cache',
'launchchannel': 'web_channel',
'origin': xurl,
'pragma': 'no-cache',
'priority': 'u=1, i',
'referer': xurl,
'sec-ch-ua': '"Microsoft Edge";v="143", "Chromium";v="143", "Not A(Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'cross-site',
'siteid': '1000101',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0',
'x-sign': sign1,
}
def categoryContent(self, cid, pg, filter, ext):
secret_b64 = self.get_current_app_key(xurl, headerx)
sign1 = self.generate_x_sign(secret_b64)
page = self.get_page_number(pg)
NdType, DqType, LxType = self.extract_filter_types(ext)
headers = self.get_headers(sign1, xurl)
params = self.build_category_params(cid, page, NdType, DqType, LxType)
data = self.fetch_category_data(params, headers)
videos = self.parse_category_videos(data)
return self.build_category_result(videos, pg)
def get_page_number(self, pg):
return int(pg) if pg else 1
def extract_filter_types(self, ext):
NdType = ext.get('年代', '全部')
DqType = ext.get('地区', '全部')
LxType = ext.get('类型', '全部')
return NdType, DqType, LxType
def build_category_params(self, cid, page, NdType, DqType, LxType):
return {'functionName': 'getNewsSearchedByCondition','nodeId': cid,'nodeTag': LxType,'yearTag': NdType,'countryTag': DqType,'orderType': '0','pageSize': '40','page': page,'keyword': '','siteId': '1000101',}
def fetch_category_data(self, params, headers):
detail = requests.get('https://ups.5g.wasu.tv/rmp-user-suggest/1000101/hzhs/searchServlet', params=params,headers=headers)
detail.encoding = "utf-8"
return detail.json()
def parse_category_videos(self, data):
videos = []
for vod in data['data']:
videos.append(self.parse_category_video(vod))
return videos
def parse_category_video(self, vod):
return {"vod_id": f"{vod['nodeId']}@{vod['newsId']}","vod_name": vod['title'],"vod_pic": vod['hPic'],"vod_year": vod.get('pubTime', '暂无日期'),"vod_remarks": vod.get('episodeDesc', '暂无备注')}
def build_category_result(self, videos, pg):
result = {'list': videos}
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def detailContent(self, ids):
did = ids[0]
secret_b64 = self.get_current_app_key(xurl, headerx)
sign1 = self.generate_x_sign(secret_b64)
fenge = did.split("@")
headers = self.get_headers(sign1, xurl)
params = self.build_detail_params(fenge)
data = self.fetch_detail_data(params, headers)
content = self.build_content(data)
director = self.extract_detail_field(data, 'director')
actor = self.extract_detail_field(data, 'actor')
remarks = self.extract_detail_field(data, 'episodeDesc')
year = self.extract_detail_field(data, 'pubTime')
area = self.extract_detail_field(data, 'countryTag')
bofang = self.build_play_url(data)
videos = [self.build_video_data(did, director, actor, remarks, year, area, content, bofang)]
return self.build_result(videos)
def build_detail_params(self, fenge):
return {'siteId': '1000101', 'functionName': 'getCurrentNews', 'nodeId': fenge[0], 'newsId': fenge[1],'platform': 'web',}
def fetch_detail_data(self, params, headers):
detail = requests.get('https://mcspapp.5g.wasu.tv/bvradio_app/hzhs/newsServlet', params=params, headers=headers)
detail.encoding = "utf-8"
return detail.json()
def build_content(self, data):
return '介绍剧情📢' + data.get('data', {}).get('newsAbstract', '')
def extract_detail_field(self, data, field_name):
return data.get('data', {}).get(field_name, '')
def build_play_url(self, data):
bofang = ''
for vod in data['data']['vodList']:
name = vod['title']
if len(vod['fileList']) > 1:
id = vod['fileList'][1]['playUrl']
else:
id = vod['fileList'][0]['playUrl']
bofang += name + '$' + id + '#'
return bofang[:-1]
def build_video_data(self, did, director, actor, remarks, year, area, content, bofang):
return {"vod_id": did, "vod_director": director, "vod_actor": actor, "vod_remarks": remarks, "vod_year": year, "vod_area": area, "vod_content": content, "vod_play_from": "华数专线", "vod_play_url": bofang}
def build_result(self, videos):
result = {}
result['list'] = videos
return result
def get_x_sign_for_post(self, secret_b64, playUrl):
secret_key = self.decode_secret_key(secret_b64)
payload_dict = self.build_payload_dict(playUrl)
data_string = self.serialize_payload(payload_dict)
signature = self.compute_hmac_signature(secret_key, data_string)
x_sign = self.encode_signature(signature)
return x_sign
def decode_secret_key(self, secret_b64):
return base64.b64decode(secret_b64)
def build_payload_dict(self, playUrl):
return {"playUrl": playUrl, "platform": "web"}
def serialize_payload(self, payload_dict):
return json.dumps(payload_dict, separators=(',', ':'), ensure_ascii=False)
def compute_hmac_signature(self, secret_key, data_string):
return hmac.new(secret_key, data_string.encode('utf-8'), hashlib.sha256).digest()
def encode_signature(self, signature):
return base64.b64encode(signature).decode('utf-8')
def playerContent(self, flag, id, vipFlags):
secret_b64 = self.get_current_app_key(xurl, headerx)
id = self.normalize_play_url(id)
sign2 = self.get_x_sign_for_post(secret_b64, id)
headers = self.get_headers(sign2, xurl)
json_data = self.build_request_json(id)
result = self.post_play_url_request(headers, json_data)
play_url = result['data']['playUrl']
return self.build_player_result(play_url)
def normalize_play_url(self, id):
return id.replace('.mp4', '/playlist.m3u8')
def build_request_json(self, playUrl):
return {'playUrl': playUrl, 'platform': 'web', }
def post_play_url_request(self, headers, json_data):
response = requests.post('https://mcspapp.5g.wasu.tv/thirdApiFile/file/getPlayUrl', headers=headers, json=json_data)
return response.json()
def build_player_result(self, play_url):
result = {}
result["parse"] = 0
result["playUrl"] = ''
result["url"] = play_url
result["header"] = headerz
return result
def searchContentPage(self, key, quick, pg):
page = self.get_page_number(pg)
secret_b64 = self.get_current_app_key(xurl, headerx)
sign1 = self.generate_x_sign(secret_b64)
headers = self.get_headers(sign1, xurl)
params = self.build_search_params(key, page)
data = self.fetch_search_data(params, headers)
videos = self.parse_search_videos(data)
return self.build_search_result(videos, pg)
def get_page_number(self, pg):
return int(pg) if pg else 1
def build_search_params(self, key, page):
return {'functionName': 'getNewsSearched', 'searchNewsType': '3,4,5', 'keyword': key, 'pageSize': 10, 'page': page, 'siteId': 1000101}
def fetch_search_data(self, params, headers):
detail = requests.get("https://ups.5g.wasu.tv/rmp-user-suggest/1000101/hzhs/searchServlet", params=params, headers=headers)
detail.encoding = "utf-8"
return detail.json()
def parse_search_videos(self, data):
videos = []
for vod in data['data']:
videos.append(self.parse_search_video(vod))
return videos
def parse_search_video(self, vod):
return {"vod_id": f"{vod['nodeId']}@{vod['newsId']}", "vod_name": vod['title'], "vod_pic": vod['hPic'], "vod_year": vod.get('pubTime', '暂无日期'), "vod_remarks": vod.get('episodeDesc', '暂无备注')}
def build_search_result(self, videos, pg):
result = {'list': videos}
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def searchContent(self, key, quick, pg="1"):
return self.searchContentPage(key, quick, '1')
def localProxy(self, params):
if params['type'] == "m3u8":
return self.proxyM3u8(params)
elif params['type'] == "media":
return self.proxyMedia(params)
elif params['type'] == "ts":
return self.proxyTs(params)
return None
+245
View File
@@ -0,0 +1,245 @@
# -*- coding: utf-8 -*-
import re, json, base64
import urllib.parse
from base.spider import Spider
from bs4 import BeautifulSoup
class Spider(Spider):
headers = {
"User-Agent": "Mozilla/5.0 (Linux; Android 15; Pixel 9) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Mobile Safari/537.36",
"Referer": "https://www.jkan.app/"
}
def init(self, extend=""):
self.host = "https://www.jkan.app"
self._timeout = 15
def _soup(self, html):
return BeautifulSoup(html, "html.parser") if html else None
def _fetch(self, url):
return self.fetch(url, headers=self.headers, timeout=self._timeout, verify=False)
def _fix(self, u):
if not u: return ""
if u.startswith("//"): return "https:" + u
if u.startswith("/"): return self.host + u
return u
def _parse_list(self, html):
videos, seen = [], set()
soup = self._soup(html)
if soup is None:
return videos
for a in soup.select('a[href*="/video/"]'):
href = a.get("href", "")
if not href or href in seen:
continue
if a.get("class") and "text_muted" in a.get("class"):
continue
seen.add(href)
title = a.get("title", "") or a.get_text(strip=True)[:100]
pic = a.get("data-original", "") or a.get("data-background-image", "")
if not pic:
bg = re.search(r"background(?:-image)?\s*:\s*url\([\"']?([^\"')]+)[\"']?\)", a.get("style", "") or "")
if bg:
pic = bg.group(1)
remark = ""
rt = a.select_one("span.xszxj, span.pic_text")
if rt:
remark = rt.get_text(strip=True)
videos.append({"vod_id": href, "vod_name": title, "vod_pic": self._fix(pic), "vod_remarks": remark})
if len(videos) >= 50:
break
return videos
def _get_filters(self, tid):
return [
{"key": "class", "name": "类型", "value": [
{"n": "全部", "v": ""}, {"n": "喜剧", "v": "喜剧"}, {"n": "爱情", "v": "爱情"},
{"n": "恐怖", "v": "恐怖"}, {"n": "动作", "v": "动作"}, {"n": "科幻", "v": "科幻"},
{"n": "剧情", "v": "剧情"}, {"n": "战争", "v": "战争"}, {"n": "悬疑", "v": "悬疑"},
{"n": "惊悚", "v": "惊悚"}, {"n": "冒险", "v": "冒险"}, {"n": "犯罪", "v": "犯罪"},
{"n": "动画", "v": "动画"}, {"n": "奇幻", "v": "奇幻"}, {"n": "武侠", "v": "武侠"},
{"n": "古装", "v": "古装"}, {"n": "历史", "v": "历史"}, {"n": "记录", "v": "记录"},
]},
{"key": "area", "name": "地区", "value": [
{"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "美国", "v": "美国"},
{"n": "韩国", "v": "韩国"}, {"n": "日本", "v": "日本"}, {"n": "香港", "v": "香港"},
{"n": "台湾", "v": "台湾"}, {"n": "泰国", "v": "泰国"}, {"n": "英国", "v": "英国"},
{"n": "法国", "v": "法国"}, {"n": "德国", "v": "德国"}, {"n": "印度", "v": "印度"},
]},
{"key": "year", "name": "年份", "value":
[{"n": "全部", "v": ""}] + [{"n": str(y), "v": str(y)} for y in range(2026, 1999, -1)]},
{"key": "sort", "name": "排序", "value": [
{"n": "按时间", "v": "time"}, {"n": "按人气", "v": "hits"}, {"n": "按评分", "v": "score"},
]},
]
def homeContent(self, filter):
return {
"class": [
{"type_id": "1", "type_name": "电影"},
{"type_id": "2", "type_name": "电视剧"},
{"type_id": "3", "type_name": "动漫"},
{"type_id": "4", "type_name": "综艺"},
],
"filters": {tid: self._get_filters(tid) for tid in ["1", "2", "3", "4"]}
}
def homeVideoContent(self):
try:
rsp = self._fetch(self.host)
return {"list": self._parse_list(rsp.text)}
except:
return {"list": []}
def _build_vodshow_url(self, tid, pg, extend):
segs = [""] * 12
segs[0] = tid
if extend.get("area"): segs[1] = extend["area"]
if extend.get("sort"): segs[2] = extend["sort"]
if extend.get("class"): segs[3] = extend["class"]
if extend.get("year"): segs[11] = extend["year"]
url = f"{self.host}/show/{'-'.join(segs)}.html"
if pg != "1":
url = f"{self.host}/show/{'-'.join(segs)}-{pg}.html"
return url
def categoryContent(self, tid, pg, filter, extend):
try:
if filter and any(v for v in extend.values()):
url = self._build_vodshow_url(tid, pg, extend)
else:
url = f"{self.host}/show/{tid}-----------{pg}.html"
rsp = self._fetch(url)
return {"list": self._parse_list(rsp.text), "page": str(pg), "pagecount": 999}
except:
return {"list": [], "page": str(pg), "pagecount": 0}
def _get_detail(self, vid):
if vid.startswith("/"):
url = self._fix(vid)
elif vid.startswith("http"):
url = vid
else:
url = f"{self.host}/video/{vid}.html"
try:
rsp = self._fetch(url)
except:
return {"vod_id": vid}
html = rsp.text
if re.search(r"(模板文件不存在|系统安全验证)", html, re.I):
return {"vod_id": "0", "vod_name": ""}
vod = {"vod_id": vid}
soup = self._soup(html)
if soup is None:
return vod
vod["vod_name"] = self._og(soup, "og:title")
vod["vod_pic"] = self._og(soup, "og:image")
vod["vod_content"] = self._og(soup, "og:description")
vod["vod_year"] = self._og(soup, "og:video:release_date")
vod["vod_area"] = self._og(soup, "og:video:area")
vod["vod_director"] = self._og(soup, "og:video:director")
vod["vod_actor"] = self._og(soup, "og:video:actor")
vod["type_name"] = self._og(soup, "og:video:class")
score = self._og(soup, "og:video:score")
if score: vod["vod_score"] = score
# extract play sources and episodes
src_tabs = soup.select_one(".play_source_tab")
src_names = [a.get("alt", "").strip() or a.get_text(strip=True) for a in src_tabs.select("a")] if src_tabs else []
for i, ul in enumerate(soup.select("ul.content_playlist, ul.playlist")):
source = src_names[i] if i < len(src_names) else "默认"
episodes = []
seen_urls = set()
for a in ul.select("a[href]"):
ep_url = a.get("href", "")
if ep_url in seen_urls:
continue
seen_urls.add(ep_url)
ep_name = a.get_text(strip=True) or "播放"
episodes.append(f"{ep_name}${self._fix(ep_url)}")
if episodes:
vod.setdefault("vod_play_from", []).append(source)
vod.setdefault("vod_play_url", []).append("#".join(episodes))
if "vod_play_from" in vod:
vod["vod_play_from"] = "$$$".join(vod.pop("vod_play_from"))
vod["vod_play_url"] = "$$$".join(vod.pop("vod_play_url"))
else:
vod["vod_play_from"] = "默认线路"
vod["vod_play_url"] = ""
return vod
def _og(self, soup, prop):
m = soup.select_one(f'meta[property="{prop}"]')
if m: return m.get("content", "")
m = soup.select_one(f'meta[name="{prop}"]')
if m: return m.get("content", "")
return ""
def detailContent(self, ids):
return {"list": [self._get_detail(ids[0])]}
def playerContent(self, flag, id, vipFlags):
play_url = self._fix(id)
if self.isVideoFormat(play_url):
return {"parse": 0, "url": play_url, "header": self.headers}
try:
rsp = self._fetch(play_url)
return self._parse_player(rsp.text, play_url)
except:
return {"parse": 0, "url": play_url, "header": self.headers}
def _parse_player(self, html, play_url):
match = re.search(r'player_aaaa\s*=\s*(\{[\s\S]*?\});?\s*</script>', html, re.I)
if not match:
return {"parse": 1, "url": play_url, "header": self.headers}
try:
data = json.loads(match.group(1))
url_enc = data.get("url", "")
enc_type = str(data.get("encrypt", "0"))
if enc_type == "1":
url_enc = urllib.parse.unquote(url_enc)
elif enc_type == "2":
url_enc = urllib.parse.unquote(base64.b64decode(url_enc).decode())
real_url = self._unescape(url_enc)
return {"parse": 0, "url": real_url or play_url, "header": self.headers}
except:
return {"parse": 0, "url": play_url, "header": self.headers}
def _unescape(self, s):
result, i, n = [], 0, len(s)
while i < n:
if s[i] == '%' and i + 5 < n and s[i + 1] == 'u':
result.append(chr(int(s[i + 2:i + 6], 16)))
i += 6
elif s[i] == '%' and i + 2 < n:
result.append(chr(int(s[i + 1:i + 3], 16)))
i += 3
else:
result.append(s[i])
i += 1
return ''.join(result)
def searchContent(self, key, quick, pg="1"):
# 搜索翻页格式: ---------{N}--- (page1: 13 dashes, pageN: 10 dashes + N + 3 dashes)
try:
kw = urllib.parse.quote(key)
if pg == "1":
url = f"{self.host}/search/{kw}-------------.html"
else:
url = f"{self.host}/search/{kw}----------{pg}---.html"
rsp = self._fetch(url)
videos = self._parse_list(rsp.text)
return {"list": videos, "page": str(pg), "pagecount": 99}
except:
return {"list": [], "pagecount": 0}
def isVideoFormat(self, url):
return bool(re.search(r'\.(mp4|m3u8|flv|avi|mkv|ts|webm|mpg|mpeg)\b', url, re.I))
def destroy(self):
pass
+256
View File
@@ -0,0 +1,256 @@
from base.spider import Spider
import requests
import re
import json
import urllib.parse
host = "https://www.hongguoguo.tv"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36',
'Referer': host
}
timeout = 10
# TVBox 兼容的 header 字符串格式
def _header_str():
return f"User-Agent={headers['User-Agent']}&Referer={headers['Referer']}"
class Spider(Spider):
def getName(self):
return "红果果短剧"
def init(self, extend):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def homeContent(self, filter):
# 福利短剧(fuliduanju)需要会员权限,已移除
classes = [
{"type_id": "jingxuanduanju", "type_name": "精选短剧"}
]
return {"class": classes}
def homeVideoContent(self):
try:
r = requests.get(host, headers=headers, timeout=timeout)
return {'list': self._parse_list(r.text)}
except Exception as e:
print(f"[homeVideoContent] error: {e}")
return {'list': []}
def categoryContent(self, cid, pg, filter, ext):
page = int(pg) if pg else 1
if page == 1:
url = f"{host}/vod/show/id/{cid}.html"
else:
url = f"{host}/vod/show/id/{cid}/page/{page}.html"
try:
r = requests.get(url, headers=headers, timeout=timeout)
videos = self._parse_list(r.text)
return {
'list': videos,
'page': pg,
'pagecount': 9999,
'limit': 24,
'total': 999999
}
except Exception as e:
print(f"[categoryContent] error: {e}")
return {'list': []}
def detailContent(self, ids):
did = str(ids[0])
try:
play_page = f"{host}/vod/play/id/{did}/sid/1/nid/1.html"
r = requests.get(play_page, headers=headers, timeout=timeout)
text = r.text
# 权限检查
if '没有权限' in text or '升级会员' in text:
print(f"[detailContent] ID={did} 需要会员权限")
return {'list': []}
# 提取 player_aaaa(支持多种闭合标签)
player = None
for pattern in [
r'var player_aaaa=(\{.*?\})</script>',
r'var player_aaaa=(\{.*?\});',
r'player_aaaa=(\{.*?\})</script>',
]:
m = re.search(pattern, text, re.DOTALL)
if m:
try:
player = json.loads(m.group(1))
break
except json.JSONDecodeError:
continue
if not player:
print(f"[detailContent] ID={did} 未找到 player_aaaa")
return {'list': []}
vod_data = player.get('vod_data', {})
play_url = player.get('url', '').replace('\\/', '/')
play_from = player.get('from', 'm3u8')
if not play_url:
print(f"[detailContent] ID={did} 无播放地址")
return {'list': []}
# 提取封面
pic = ''
pic_m = re.search(r'"thumbnailUrl":"([^"]+)"', text)
if pic_m:
pic = pic_m.group(1).replace('\\/', '/')
# 提取简介
content = ''
desc_m = re.search(r'<p[^>]*class="[^"]*desc[^"]*"[^>]*>(.*?)</p>', text, re.DOTALL)
if desc_m:
content = re.sub(r'<[^>]+>', '', desc_m.group(1)).strip()
# 提取集数按钮
ep_pattern = r'href="/vod/play/id/' + did + r'/sid/(\d+)/nid/(\d+)\.html"[^>]*>([^<]*)</a>'
episodes = re.findall(ep_pattern, text)
play_from_list = []
play_url_list = []
if not episodes:
# 单集短剧
play_from_list.append(play_from)
vod_name = vod_data.get('vod_name', '第1集')
play_url_list.append(f"{vod_name}${play_url}")
else:
# 按线路分组
lines = {}
for sid, nid, name in episodes:
sid = int(sid)
if sid not in lines:
lines[sid] = []
lines[sid].append((int(nid), name.strip()))
for sid in sorted(lines.keys()):
line_eps = sorted(lines[sid], key=lambda x: x[0])
urls = []
line_name = None
for idx, (nid, name) in enumerate(line_eps):
if sid == 1 and nid == 1:
# 当前页面(第一集)
line_name = play_from
urls.append(f"{name}${play_url}")
else:
# 请求其他集页面
ep_url = f"{host}/vod/play/id/{did}/sid/{sid}/nid/{nid}.html"
try:
rr = requests.get(ep_url, headers=headers, timeout=8)
if '没有权限' in rr.text:
continue
for pattern in [
r'var player_aaaa=(\{.*?\})</script>',
r'var player_aaaa=(\{.*?\});',
r'player_aaaa=(\{.*?\})</script>',
]:
mm = re.search(pattern, rr.text, re.DOTALL)
if mm:
try:
ep_player = json.loads(mm.group(1))
ep_url_real = ep_player.get('url', '').replace('\\/', '/')
if ep_url_real:
urls.append(f"{name}${ep_url_real}")
if line_name is None:
line_name = ep_player.get('from', f'线路{sid}')
break
except json.JSONDecodeError:
continue
except Exception as e:
print(f"[detailContent] 获取集 {sid}/{nid} 失败: {e}")
continue
if urls and line_name:
play_from_list.append(line_name)
play_url_list.append("#".join(urls))
# 如果多线路处理失败,回退到单集
if not play_from_list:
play_from_list.append(play_from)
vod_name = vod_data.get('vod_name', '第1集')
play_url_list.append(f"{vod_name}${play_url}")
VOD = {
"vod_id": did,
"vod_name": vod_data.get('vod_name', ''),
"vod_pic": pic,
"vod_actor": vod_data.get('vod_actor', ''),
"vod_director": vod_data.get('vod_director', ''),
"type_name": vod_data.get('vod_class', ''),
"vod_remarks": "短剧",
"vod_content": content,
"vod_play_from": "$$$".join(play_from_list),
"vod_play_url": "$$$".join(play_url_list)
}
return {'list': [VOD]}
except Exception as e:
print(f"[detailContent] 获取详情失败: {e}")
return {'list': []}
def playerContent(self, flag, id, vipFlags):
print(f"[playerContent] flag={flag}, url={id[:80]}...")
return {
"parse": 0,
"playUrl": '',
"url": id,
"header": _header_str()
}
def searchContent(self, key, quick, pg=1):
page = int(pg) if pg else 1
keyword = urllib.parse.quote(key)
if page == 1:
url = f"{host}/vod/search.html?wd={keyword}"
else:
url = f"{host}/vod/search.html?wd={keyword}&page={page}"
try:
r = requests.get(url, headers=headers, timeout=timeout)
videos = self._parse_list(r.text)
return {
'list': videos,
'page': page,
'pagecount': 9999,
'limit': 24,
'total': 999999
}
except Exception as e:
print(f"[searchContent] error: {e}")
return {'list': []}
def _parse_list(self, text):
videos = []
pattern = (
r'<a[^>]*href="/vod/play/id/(\d+)/sid/1/nid/1\.html"[^>]*>'
r'.*?<div class="Image">.*?'
r'<div class="lazy" data-original="([^"]+)"[^>]*>.*?</div>'
r'.*?<span class="f">([^<]*)</span>'
r'.*?<span class="b">([^<]*)</span>'
r'.*?</div>.*?'
r'<h2 class="tim-title">([^<]*)</h2>'
r'.*?</a>'
)
matches = re.findall(pattern, text, re.DOTALL)
for m in matches:
vid, pic, score, status, name = m
videos.append({
"vod_id": vid,
"vod_name": name.strip(),
"vod_pic": pic.strip(),
"vod_remarks": f"{score.strip()} {status.strip()}",
"vod_content": ""
})
return videos
+225
View File
@@ -0,0 +1,225 @@
# -*- coding: utf-8 -*-
# by @嗷呜
import json
import sys
import threading
import uuid
import requests
sys.path.append('..')
from base.spider import Spider
import time
from Crypto.Hash import MD5, SHA1
class Spider(Spider):
'''
配置示例:
{
"key": "xxxx",
"name": "xxxx",
"type": 3,
"api": ".所在路径/金牌.py",
"searchable": 1,
"quickSearch": 1,
"filterable": 1,
"changeable": 1,
"ext": {
"site": "https://www.jiabaide.cn,域名2,域名3"
}
},
'''
def init(self, extend=""):
if extend:
hosts=json.loads(extend)['site']
self.host = self.host_late(hosts)
pass
def getName(self):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def destroy(self):
pass
def homeContent(self, filter):
cdata = self.fetch(f"{self.host}/api/mw-movie/anonymous/get/filer/type", headers=self.getheaders()).json()
fdata = self.fetch(f"{self.host}/api/mw-movie/anonymous/v1/get/filer/list", headers=self.getheaders()).json()
result = {}
classes = []
filters={}
for k in cdata['data']:
classes.append({
'type_name': k['typeName'],
'type_id': str(k['typeId']),
})
sort_values = [{"n": "最近更新", "v": "2"},{"n": "人气高低", "v": "3"}, {"n": "评分高低", "v": "4"}]
for tid, d in fdata['data'].items():
current_sort_values = sort_values.copy()
if tid == '1':
del current_sort_values[0]
filters[tid] = [
{"key": "type", "name": "类型",
"value": [{"n": i["itemText"], "v": i["itemValue"]} for i in d["typeList"]]},
*([] if not d["plotList"] else [{"key": "v_class", "name": "剧情",
"value": [{"n": i["itemText"], "v": i["itemText"]}
for i in d["plotList"]]}]),
{"key": "area", "name": "地区",
"value": [{"n": i["itemText"], "v": i["itemText"]} for i in d["districtList"]]},
{"key": "year", "name": "年份",
"value": [{"n": i["itemText"], "v": i["itemText"]} for i in d["yearList"]]},
{"key": "lang", "name": "语言",
"value": [{"n": i["itemText"], "v": i["itemText"]} for i in d["languageList"]]},
{"key": "sort", "name": "排序", "value": current_sort_values}
]
result['class'] = classes
result['filters'] = filters
return result
def homeVideoContent(self):
data1 = self.fetch(f"{self.host}/api/mw-movie/anonymous/v1/home/all/list", headers=self.getheaders()).json()
data2=self.fetch(f"{self.host}/api/mw-movie/anonymous/home/hotSearch",headers=self.getheaders()).json()
data=[]
for i in data1['data'].values():
data.extend(i['list'])
data.extend(data2['data'])
vods=self.getvod(data)
return {'list':vods}
def categoryContent(self, tid, pg, filter, extend):
params = {
"area": extend.get('area', ''),
"filterStatus": "1",
"lang": extend.get('lang', ''),
"pageNum": pg,
"pageSize": "30",
"sort": extend.get('sort', '1'),
"sortBy": "1",
"type": extend.get('type', ''),
"type1": tid,
"v_class": extend.get('v_class', ''),
"year": extend.get('year', '')
}
data = self.fetch(f"{self.host}/api/mw-movie/anonymous/video/list?{self.js(params)}", headers=self.getheaders(params)).json()
result = {}
result['list'] = self.getvod(data['data']['list'])
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def detailContent(self, ids):
data=self.fetch(f"{self.host}/api/mw-movie/anonymous/video/detail?id={ids[0]}",headers=self.getheaders({'id':ids[0]})).json()
vod=self.getvod([data['data']])[0]
vod['vod_play_from']='金牌'
vod['vod_play_url'] = '#'.join(
f"{i['name'] if len(vod['episodelist']) > 1 else vod['vod_name']}${ids[0]}@@{i['nid']}" for i in
vod['episodelist'])
vod.pop('episodelist', None)
return {'list':[vod]}
def searchContent(self, key, quick, pg="1"):
params = {
"keyword": key,
"pageNum": pg,
"pageSize": "8",
"sourceCode": "1"
}
data=self.fetch(f"{self.host}/api/mw-movie/anonymous/video/searchByWord?{self.js(params)}",headers=self.getheaders(params)).json()
vods=self.getvod(data['data']['result']['list'])
return {'list':vods,'page':pg}
def playerContent(self, flag, id, vipFlags):
self.header = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.61 Chrome/126.0.6478.61 Not/A)Brand/8 Safari/537.36',
'sec-ch-ua-platform': '"Windows"',
'DNT': '1',
'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"',
'sec-ch-ua-mobile': '?0',
'Origin': self.host,
'Referer': f'{self.host}/'
}
ids=id.split('@@')
pdata = self.fetch(f"{self.host}/api/mw-movie/anonymous/v2/video/episode/url?clientType=1&id={ids[0]}&nid={ids[1]}",headers=self.getheaders({'clientType':'1','id': ids[0], 'nid': ids[1]})).json()
vlist=[]
for i in pdata['data']['list']:vlist.extend([i['resolutionName'],i['url']])
return {'parse':0,'url':vlist,'header':self.header}
def localProxy(self, param):
pass
def host_late(self, url_list):
if isinstance(url_list, str):
urls = [u.strip() for u in url_list.split(',')]
else:
urls = url_list
if len(urls) <= 1:
return urls[0] if urls else ''
results = {}
threads = []
def test_host(url):
try:
start_time = time.time()
response = requests.head(url, timeout=1.0, allow_redirects=False)
delay = (time.time() - start_time) * 1000
results[url] = delay
except Exception as e:
results[url] = float('inf')
for url in urls:
t = threading.Thread(target=test_host, args=(url,))
threads.append(t)
t.start()
for t in threads:
t.join()
return min(results.items(), key=lambda x: x[1])[0]
def md5(self, sign_key):
md5_hash = MD5.new()
md5_hash.update(sign_key.encode('utf-8'))
md5_result = md5_hash.hexdigest()
return md5_result
def js(self, param):
return '&'.join(f"{k}={v}" for k, v in param.items())
def getheaders(self, param=None):
if param is None:param = {}
t=str(int(time.time()*1000))
param['key']='cb808529bae6b6be45ecfab29a4889bc'
param['t']=t
sha1_hash = SHA1.new()
sha1_hash.update(self.md5(self.js(param)).encode('utf-8'))
sign = sha1_hash.hexdigest()
deviceid = str(uuid.uuid4())
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.61 Chrome/126.0.6478.61 Not/A)Brand/8 Safari/537.36',
'Accept': 'application/json, text/plain, */*',
'sign': sign,
't': t,
'deviceid':deviceid
}
return headers
def convert_field_name(self, field):
field = field.lower()
if field.startswith('vod') and len(field) > 3:
field = field.replace('vod', 'vod_')
if field.startswith('type') and len(field) > 4:
field = field.replace('type', 'type_')
return field
def getvod(self, array):
return [{self.convert_field_name(k): v for k, v in item.items()} for item in array]