上传文件至「xbpq」

This commit is contained in:
2026-08-14 11:28:05 +02:00
parent 2d5bac07ad
commit a335c6e010
5 changed files with 949 additions and 0 deletions
+590
View File
@@ -0,0 +1,590 @@
# coding=utf-8
import sys
import json
import re
import requests
import base64
from bs4 import BeautifulSoup
from urllib.parse import unquote, urljoin
try:
from base.spider import Spider as BaseSpider
except ImportError:
class BaseSpider():
def fetch(self, url, headers=None, timeout=10):
try:
res = requests.get(url, headers=headers, timeout=timeout, allow_redirects=True)
res.encoding = 'utf-8'
return res
except Exception as e:
print(f"fetch error: {e}")
return None
class Spider(BaseSpider):
def getName(self):
return "撸一天"
def init(self, extend=""):
self.host = "https://luyitian.com"
self.session = requests.Session()
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Connection': 'keep-alive'
})
def homeVideoContent(self):
return {"list": []}
def localProxy(self, params):
return [200, "video/MP2T", ""]
def isVideoFormat(self, url):
return False
def manualVideoCheck(self):
return False
def fetch(self, url, headers=None, timeout=5):
try:
req_headers = headers or self.session.headers
res = self.session.get(url, headers=req_headers, timeout=timeout, allow_redirects=True)
res.encoding = 'utf-8'
return res
except Exception as e:
print(f"fetch error: {e}")
return None
def _get_topic_filters(self):
url = f"{self.host}/topic/"
res = self.fetch(url, timeout=5)
if not res:
return []
soup = BeautifulSoup(res.text, 'html.parser')
topic_links = soup.select('a[href*="/topicdetail-"]')
if not topic_links:
topic_links = soup.select('a[href*="/topicdetail"]')
filters = []
seen = set()
for a in topic_links:
href = a.get('href', '')
match = re.search(r'/topicdetail-(\d+)', href)
if not match:
match = re.search(r'/topicdetail/(\d+)', href)
if not match:
continue
tid = match.group(1)
name = a.get_text(strip=True) or a.get('title', '') or f"专题{tid}"
if len(name) < 2:
continue
if tid not in seen:
seen.add(tid)
filters.append({"n": name, "v": tid})
return filters
def homeContent(self, filter):
classes = [
{"type_name": "最近更新", "type_id": "new"},
{"type_name": "热门影片", "type_id": "hot"},
{"type_name": "影片专题", "type_id": "topic"},
{"type_name": "中文字幕", "type_id": "28"},
{"type_name": "国产", "type_id": "20"},
{"type_name": "日本有码", "type_id": "21"},
{"type_name": "日本无码", "type_id": "22"},
{"type_name": "欧美", "type_id": "23"},
{"type_name": "动漫", "type_id": "24"},
{"type_name": "伦理", "type_id": "25"},
{"type_name": "韩国", "type_id": "36"},
{"type_name": "另类", "type_id": "41"}
]
filters = {
"28": [{"key": "tid", "name": "子分类", "value": [
{"n": "全部", "v": "28"},
{"n": "日本中字", "v": "51"}
]}],
"20": [{"key": "tid", "name": "子分类", "value": [
{"n": "全部", "v": "20"},
{"n": "国产精品", "v": "26"},
{"n": "国产剧情", "v": "27"},
{"n": "国产自拍", "v": "29"},
{"n": "国产主播", "v": "35"},
{"n": "国模私拍", "v": "85"},
{"n": "网红明星", "v": "91"},
{"n": "国产SM", "v": "105"},
{"n": "台湾辣妹", "v": "107"},
{"n": "香港正妹", "v": "108"}
]}],
"21": [{"key": "tid", "name": "子分类", "value": [
{"n": "全部", "v": "21"},
{"n": "人妻", "v": "31"},
{"n": "素人", "v": "44"},
{"n": "口爆颜射", "v": "46"},
{"n": "萝莉少女", "v": "47"},
{"n": "美乳巨乳", "v": "48"},
{"n": "制服诱惑", "v": "52"},
{"n": "调教", "v": "57"},
{"n": "出轨", "v": "58"},
{"n": "有码精品", "v": "101"}
]}],
"22": [{"key": "tid", "name": "子分类", "value": [
{"n": "全部", "v": "22"},
{"n": "无码精品", "v": "102"}
]}],
"23": [{"key": "tid", "name": "子分类", "value": [
{"n": "全部", "v": "23"},
{"n": "欧美精品", "v": "104"}
]}],
"24": [{"key": "tid", "name": "子分类", "value": [
{"n": "全部", "v": "24"},
{"n": "动漫精品", "v": "103"}
]}],
"25": [{"key": "tid", "name": "子分类", "value": [
{"n": "全部", "v": "25"},
{"n": "综合三级", "v": "39"}
]}],
"36": [{"key": "tid", "name": "子分类", "value": [
{"n": "全部", "v": "36"},
{"n": "韩国主播", "v": "37"}
]}],
"41": [{"key": "tid", "name": "子分类", "value": [
{"n": "全部", "v": "41"},
{"n": "Cosplay", "v": "106"}
]}]
}
topic_values = self._get_topic_filters()
if topic_values:
filters["topic"] = [{
"key": "tid",
"name": "专题",
"value": topic_values
}]
return {'class': classes, 'filters': filters}
def categoryContent(self, tid, pg, filter, extend):
pg = int(pg)
result = {"list": [], "page": pg, "pagecount": 999, "limit": 20, "total": 9999}
real_tid = extend.get('tid', tid)
soup = None
if real_tid.isdigit() and tid == "topic":
urls_to_try = [
f"{self.host}/topicdetail-{real_tid}/",
f"{self.host}/topicdetail-{real_tid}.html",
f"{self.host}/topicdetail/{real_tid}/",
f"{self.host}/topicdetail/{real_tid}.html",
f"{self.host}/topicdetail-{real_tid}-{pg}/",
f"{self.host}/topicdetail/{real_tid}-{pg}/"
]
for url in urls_to_try:
res = self.fetch(url, headers={'Referer': self.host})
if res and res.status_code == 200 and ('video-img-box' in res.text or 'vodlist' in res.text or 'vodplay' in res.text):
soup = BeautifulSoup(res.text, 'html.parser')
break
if not soup:
return result
elif real_tid in ["new", "hot", "topic"]:
if real_tid == "topic":
return result
if pg > 1:
url = f"{self.host}/label/{real_tid}/page/{pg}/"
else:
url = f"{self.host}/label/{real_tid}/"
res = self.fetch(url, headers={'Referer': self.host})
if not res:
url = f"{self.host}/label/{real_tid}/"
res = self.fetch(url, headers={'Referer': self.host})
if not res:
return result
soup = BeautifulSoup(res.text, 'html.parser')
else:
urls_to_try = [
f"{self.host}/vodtype/{real_tid}-{pg}.html",
f"{self.host}/vodtype/{real_tid}-{pg}/",
f"{self.host}/type/{real_tid}-{pg}.html",
f"{self.host}/type/{real_tid}-{pg}/",
f"{self.host}/vodtype/{real_tid}/",
f"{self.host}/vodtype/{real_tid}.html"
]
res = None
for url in urls_to_try:
res = self.fetch(url, headers={'Referer': self.host})
if res and res.status_code == 200:
if 'video-img-box' in res.text or 'vodlist' in res.text or 'item' in res.text:
break
res = None
if not res:
return result
soup = BeautifulSoup(res.text, 'html.parser')
vod_list = []
items = soup.select('.video-img-box') or soup.select('.video-film-list .video-item') or soup.select('.vodlist_item') or soup.select('.item')
for item in items:
a = item.select_one('a')
if not a:
continue
href = a.get('href', '')
vid_match = re.search(r'/vodplay/(\d+)', href) or \
re.search(r'/voddetail/(\d+)', href) or \
re.search(r'/vod/(\d+)', href) or \
re.search(r'/play/(\d+)', href)
vid = vid_match.group(1) if vid_match else href
name = ""
img = item.select_one('img')
if img and img.get('alt'):
name = img['alt']
if not name and a.get('title'):
name = a['title']
if not name:
title_elem = item.select_one('.title a') or item.select_one('.detail .title a')
if title_elem:
name = title_elem.get_text(strip=True)
if not name:
name = a.get_text(strip=True)
if not name:
name = "未知标题"
pic = ""
if img:
pic = img.get('data-src') or img.get('src', '')
if pic and not pic.startswith('http'):
pic = urljoin(self.host, pic)
remark = ""
remark_elem = item.select_one('.sub-title') or item.select_one('.remarks') or item.select_one('.video-remarks')
if remark_elem:
remark = remark_elem.get_text(strip=True)
if len(remark) > 20:
remark = remark[:20]
else:
text = item.get_text(strip=True)
parts = [p.strip() for p in text.split('\n') if p.strip()]
if parts:
remark = parts[-1][:20]
vod_list.append({
"vod_id": vid,
"vod_name": name.strip(),
"vod_pic": pic,
"vod_remarks": remark
})
result['list'] = vod_list
page_elem = soup.select_one('.pagination a:last-child') or soup.select_one('.page a:last-child')
if page_elem and page_elem.get('href'):
try:
nums = re.findall(r'(\d+)', page_elem['href'])
if nums:
result['pagecount'] = max(int(nums[-1]), 1)
except:
pass
return result
def detailContent(self, ids):
vid = ids[0]
url = f"{self.host}/vodplay/{vid}-1-1/"
res = self.fetch(url, headers={'Referer': self.host})
if not res:
return {"list": []}
soup = BeautifulSoup(res.text, 'html.parser')
raw_title = soup.title.text.split('|')[0].replace('在线播放在线观看','').replace('','').replace('','').strip()
vod = {
"vod_id": vid,
"vod_name": raw_title,
"vod_type": "视频",
"vod_content": "资源来自于网络",
"vod_play_from": "Luyitian",
"vod_play_url": f"播放${vid}-1-1"
}
return {"list": [vod]}
def searchContent(self, key, quick, pg=1):
url = f"{self.host}/vodsearch/{key}----------{pg}---/"
res = self.fetch(url, headers={'Referer': self.host})
if not res:
return {"list": []}
soup = BeautifulSoup(res.text, 'html.parser')
vod_list = []
items = soup.select('.video-img-box') or soup.select('.video-film-list .video-item')
for item in items:
a = item.select_one('a')
if not a:
continue
href = a.get('href', '')
vid_match = re.search(r'/vodplay/(\d+)', href) or re.search(r'/voddetail/(\d+)', href)
vid = vid_match.group(1) if vid_match else href
name = ""
img = item.select_one('img')
if img and img.get('alt'):
name = img['alt']
if not name and a.get('title'):
name = a['title']
if not name:
title_elem = item.select_one('.title a')
if title_elem:
name = title_elem.get_text(strip=True)
if not name:
name = a.get_text(strip=True)
if not name:
name = "搜索结果"
pic = ""
if img:
pic = img.get('data-src') or img.get('src', '')
vod_list.append({
"vod_id": vid,
"vod_name": name.strip(),
"vod_pic": pic,
"vod_remarks": ""
})
return {"list": vod_list}
def _js_decode(self, js_str):
b64_match = re.search(r'atob\s*\(\s*["\']([^"\']+)["\']\s*\)', js_str)
if b64_match:
try:
decoded = base64.b64decode(b64_match.group(1)).decode('utf-8')
return decoded
except:
pass
unescape_match = re.search(r'unescape\s*\(\s*["\']([^"\']+)["\']\s*\)', js_str)
if unescape_match:
try:
decoded = unquote(unescape_match.group(1))
return decoded
except:
pass
url_match = re.search(r'(https?://[^\s"\']+\.m3u8[^\s"\']*)', js_str, re.I)
if url_match:
return url_match.group(1)
return None
def _sniff_xhr(self, html, page_url):
patterns = [
r'fetch\s*\(\s*["\']([^"\']+\.m3u8[^"\']*)["\']',
r'XMLHttpRequest.*?\.open\s*\(\s*["\']GET["\']\s*,\s*["\']([^"\']+\.m3u8[^"\']*)["\']',
r'\.get\s*\(\s*["\']([^"\']+\.m3u8[^"\']*)["\']',
r'url\s*:\s*["\']([^"\']+\.m3u8[^"\']*)["\']',
r'src\s*=\s*["\']([^"\']+\.m3u8[^"\']*)["\']',
]
for pat in patterns:
match = re.search(pat, html, re.I)
if match:
url = match.group(1)
if not url.startswith('http'):
url = urljoin(page_url, url)
return url
scripts = re.findall(r'<script[^>]*>(.*?)</script>', html, re.I | re.S)
for script_content in scripts:
if script_content.strip():
found = self._js_decode(script_content)
if found and '.m3u8' in found:
return found
return None
def _skip_ad_time(self, m3u8_text, skip_seconds=25):
"""
解析 m3u8,累积分片时长,跳过前 skip_seconds 秒,生成新的 m3u8。
同时插入 #EXT-X-START 标签让播放器从指定时间开始。
"""
lines = m3u8_text.splitlines()
header_lines = [] # 保留到分片前的全局标签
media_sequence = 0 # 初始序列号
target_duration = None
video_segments = [] # 每个元素 (extinf_line, ts_line)
i = 0
# 先提取头部标签
while i < len(lines) and not lines[i].startswith('#EXTINF'):
line = lines[i]
if line.startswith('#EXT-X-MEDIA-SEQUENCE'):
try:
media_sequence = int(line.split(':')[1])
except:
pass
header_lines.append(line)
elif line.startswith('#EXT-X-TARGETDURATION'):
header_lines.append(line)
try:
target_duration = float(line.split(':')[1])
except:
pass
elif not line.startswith('#'):
# 意外出现非注释行,可能是分片,跳过
break
else:
header_lines.append(line)
i += 1
# 读取分片段
while i < len(lines):
if lines[i].startswith('#EXTINF'):
extinf = lines[i]
i += 1
if i < len(lines):
ts_line = lines[i]
video_segments.append((extinf, ts_line))
i += 1
else:
break
elif lines[i].startswith('#'):
# 其他标签,忽略或酌情处理
i += 1
else:
# 孤立的 ts,视为一个片段
video_segments.append(('', lines[i]))
i += 1
# 累加时长
accumulated = 0.0
start_index = 0
for idx, (extinf, _) in enumerate(video_segments):
dur = 0.0
match = re.search(r'#EXTINF:\s*([\d.]+)', extinf)
if match:
dur = float(match.group(1))
elif target_duration:
dur = target_duration
else:
dur = 3.0 # 假设 3 秒
accumulated += dur
if accumulated >= skip_seconds:
start_index = idx
break
else:
# 总时长不足 skip_seconds,不跳过
start_index = 0
# 裁剪片段,更新序列号
new_segments = video_segments[start_index:]
new_media_sequence = media_sequence + start_index
# 构建新的 m3u8 内容
new_lines = []
# 添加原始头部但移除原有的 #EXT-X-MEDIA-SEQUENCE 和可能冲突的标签
for line in header_lines:
if line.startswith('#EXT-X-MEDIA-SEQUENCE'):
continue
if line.startswith('#EXT-X-START'):
continue
new_lines.append(line)
# 添加新的序列号
new_lines.append(f'#EXT-X-MEDIA-SEQUENCE:{new_media_sequence}')
# 添加起始跳转标签
new_lines.append(f'#EXT-X-START:TIME-OFFSET={skip_seconds}')
# 添加分片
for extinf, ts in new_segments:
if extinf:
new_lines.append(extinf)
new_lines.append(ts)
return '\n'.join(new_lines)
def _get_m3u8_content(self, url, referer):
try:
headers = self.session.headers.copy()
headers['Referer'] = referer
resp = requests.get(url, headers=headers, timeout=5)
if resp.status_code == 200:
resp.encoding = 'utf-8'
return resp.text
except Exception as e:
print(f"下载 m3u8 失败: {e}")
return None
def playerContent(self, flag, id, vipFlags=None):
play_url = f"{self.host}/vodplay/{id}/"
res = self.fetch(play_url, headers={'Referer': self.host}, timeout=5)
if not res:
return {"parse": 1, "url": play_url}
html = res.text
m3u8_url = None
match = re.search(r'var\s+player_aaaa\s*=\s*(\{.*?\});', html, re.DOTALL)
if match:
try:
json_str = match.group(1).strip()
if json_str.endswith(','):
json_str = json_str[:-1]
config = json.loads(json_str)
m3u8_url = config.get('url', '')
except:
pass
if not m3u8_url:
m3u8_url = self._js_decode(html)
if not m3u8_url:
m3u8_url = self._sniff_xhr(html, play_url)
if not m3u8_url:
return {"parse": 1, "url": play_url}
m3u8_url = unquote(m3u8_url)
if m3u8_url.startswith('//'):
m3u8_url = 'https:' + m3u8_url
elif not m3u8_url.startswith('http'):
m3u8_url = urljoin(self.host, m3u8_url)
# 下载原始 m3u8
m3u8_text = self._get_m3u8_content(m3u8_url, play_url)
if not m3u8_text:
return {
"parse": 0,
"playUrl": "",
"url": m3u8_url,
"header": {
"User-Agent": self.session.headers['User-Agent'],
"Referer": play_url,
"Origin": self.host
}
}
# 跳过前25秒
filtered_m3u8 = self._skip_ad_time(m3u8_text, skip_seconds=25)
# 如果没有变化(不足25秒),直接返回原始
if filtered_m3u8 == m3u8_text:
return {
"parse": 0,
"playUrl": "",
"url": m3u8_url,
"header": {
"User-Agent": self.session.headers['User-Agent'],
"Referer": play_url,
"Origin": self.host
}
}
# 打包为 data URI
encoded_m3u8 = base64.b64encode(filtered_m3u8.encode('utf-8')).decode('ascii')
data_url = f"data:application/vnd.apple.mpegurl;base64,{encoded_m3u8}"
return {
"parse": 0,
"playUrl": "",
"url": data_url,
"header": {
"User-Agent": self.session.headers['User-Agent'],
"Referer": play_url,
"Origin": self.host
}
}
+28
View File
@@ -0,0 +1,28 @@
{
"作者": "h",
"站名": "极乐禁区",
"请求头": "User-Agent$MOBILE_UA",
"编码": "UTF-8",
"主页url":"https://iegeewiet-4mag.hscwang26y2m.xyz/jlhs",
"直接播放": "1",
"搜索模式": "1",
"数组": "<a class=\"thumbnail\"&&</div>*</li>||百大女优--<li><a hr&&</li>||番号仓库--<li><a hr&&</li>||国产传媒--<li><a hr&&</li>||91探花--<li><a hr&&</li>",
"标题": "\">&&</a></h5>||百大女优--#;\">&&</span>||番号仓库--#;\">&&</span>||国产传媒--#;\">&&</span>||91探花--#;\">&&</span>",
"副标题": "<p>&&</p>",
"图片": "data-original=\"&&\"||百大女优--<img src=\"&&\"||番号仓库--<img src=\"&&\"||国产传媒--<img src=\"&&\"||91探花--<img src=\"&&\"",
"链接": "https://iegeewiet-4mag.hscwang26y2m.xyz/vodplay/+<a href=\"/voddetail/&&/+-1-1/||百大女优--ef=\"&&/\"+-{catePg}/||番号仓库--ef=\"&&/\"+-{catePg}/||国产传媒--ef=\"&&/\"+-{catePg}/||91探花--ef=\"&&/\"+-{catePg}",
"嗅探词": ".mp4#.m3u8",
"直接播放": "1",
"搜索模式": "1",
"搜索url": "https://iegeewiet-4mag.hscwang26y2m.xyz/vodsearch/{wd}----------{pg}---/",
"搜索数组": "<a class=\"thumbnail\"&&</div>*</li>",
"搜索图片": "<img src=\"&&\"",
"搜索标题": "title=\"&&\"",
"搜索链接": "https://iegeewiet-4mag.hscwang26y2m.xyz/vodplay/+<a href=\"/voddetail/&&/+-1-1/",
"二级目录": "百大女优,番号仓库,国产传媒,91探花|folder-0-0-H",
"二级ID": "",
"特殊分类链接": "百大女优$https://iegeewiet-4mag.hscwang26y2m.xyz/label/sortjp/#番号仓库$https://iegeewiet-4mag.hscwang26y2m.xyz/label/sortnu/#国产传媒$https://iegeewiet-4mag.hscwang26y2m.xyz/label/sortcn/#91探花$https://iegeewiet-4mag.hscwang26y2m.xyz/label/sortfl/",
"分类url":"https://iegeewiet-4mag.hscwang26y2m.xyz/vodtype/{cateId}-{catePg}/",
"分类": "百大女优#番号仓库#国产传媒#91探花#视频一区$45#视频二区$46#视频三区$47",
"类型": "空||空||空||空||精品日韩$51#国产精品$49#日韩无码$50#中文字幕$52#欧美极品$53#动漫精品$54#淫欲痴女$55#SM变态$56||偷拍偷窥$57#性感人妻$61#网红主播$64#黑丝诱惑$63#三级伦理$62#童颜巨乳$58#明星换脸$517#女优明星$59||国产自拍$66#剧情解说$68#网曝黑料$71#萝莉少女$73#同性世界$70#制服诱惑$69#网曝吃瓜$67#门事件$72"
}
+34
View File
@@ -0,0 +1,34 @@
{
"作者": "h",
"站名": "超A精品+91porn+探花AV+色花堂+91大神+18动漫+猎奇之家+啪啪视频+国产片商+无码黑料+Javffm+七猫高清+1024看",
"请求头": "User-Agent$MOBILE_UA",
"编码": "UTF-8",
"超A精品": "https://gxxxx13.xyz/123/",
"91pornurl": "https://0601.9p47p.com/",
"探花AVurl": "https://tanhuavip12.xyz/853/",
"色花堂url": "https://1gkka.com/",
"91大神url": "https://xn--t4q-91dsvodcom-sb1ww420bv16drbb.xn--91shen-cy3k.com/",
"18动漫url": "https://m.18dongmanwang.com/",
"猎奇之家url": "https://xn--2pj-liqivodcom-265y81sv86ipxq897b.www-liqivod-com.com/",
"啪啪视频url": "http://kanxv3.com/6/",
"国产片商url": "https://xn---gccpsscom-e12p686ciwltn3epeo.xn--gc-if7c031i.com/",
"无码黑料url": "https://mixqq.mixxx20.xyz/123/",
"Javffm": "https://www.javffm.com/",
"七猫高清":"https://www.88jjtt.com/",
"1024看":"https://1024kan.com/",
"数组": "<div class=\"stui-vodlist__box\">&&</li>||91porn--<div id=\"videobox\">&&<div id=\"paging\">分割(后:<img src=images/dislike.png)||探花AV--<video class=\"preview hidden\"&&<div class=\"my-2 text-sm text-nord4 truncate\">||色花堂--<div class=\"img-box cover-md\">&&</h6>||91大神--<li class=\"item\">&&<div class=\"detail\">||18动漫--<div class=\"entry-card\">&&</h2>||猎奇之家--<div class=\"col-md-4&&</a></h3>||啪啪视频--<li class=\"stui-vodlist__item\">&&</a></h4>||国产片商--<li class=\"item\">&&</li>||无码黑料--<div class=\"stui-vodlist__box\">&&<div class=\"stui-vodlist__detail\">||Javffm--<article id=&&</article>||七猫高清--<div class=\"cover-div\">&&<i class=\"heart icon\"||1024看--<a class=\"xx-thumb\"&&</li>",
"标题": "title=\"&&\"||91porn--m-t-5\">&&</span>||探花AV--<img alt=\"&&\"||色花堂--<h6 class=\"title\">&&</a>||91大神--title=\"&&\"||18动漫--alt=\"&&\"||猎奇之家--title=\"&&\"||啪啪视频--title=\"&&\"||国产片商--title=\"&&\"||无码黑料--title=\"&&\"||Javffm--alt=\"&&\"||七猫高清--decodeURIComponent(\"&&\"[工具:解url]||1024看--alt=\"&&\"",
"副标题":"<span class=\"pic-text text-right\">*-&&</span>||91porn--\"duration\">&&</span>||色花堂--<div class=\"ribbon-top-left\">&&</span>||猎奇之家--<span class=\"vodtime\">&&</span>||七猫高清--<time class=\"video-date\">&&</time>+<span class=view_count>&&</span>",
"链接": "href=\"&&\"||91porn--<a href=\"&&\"||探花AV--<a href=\"&&\"||色花堂--href=\"&&\"||91大神--{{91大神url}}+<a href=\"&&\"||18动漫--<a href=\"&&\"||猎奇之家--<a href=\"&&\"||啪啪视频--<a href=\"&&\"||国产片商--{{国产片商url}}+<a href=\"&&\"||无码黑料--href=\"&&\"||Javffm--<a href=\"&&\"||七猫高清--<a href=\"&&\"||1024看--href=\"&&\"",
"图片": "data-original=\"&&\"||91porn--src=\"&&\"||探花AV--src=\"&&\"||色花堂--data-original=\"&&\"||91大神--style=\"background-image:url('&&')||18动漫--<img src=\"&&\"||猎奇之家--style=\"background-image:url('&&')||啪啪视频--data-original=\"&&\"||国产片商--style=\"background-image:url('&&')||无码黑料--data-original=\"&&\"||Javffm--<img src=\"&&\"||1024看--data-original=\"&&\"",
"嗅探词": "rsc.cdn#.mp4#.m3u8||1024看--892539.xyz",
"直接播放": "1",
"搜索模式": "1",
"搜索url": "{{超A精品}}index.php/vod/search/page/{pg}/wd/{wd}.html",
"特殊分类链接": "91porn${{91pornurl}}v.php?{cateId}&viewtype=basic&page={catePg}#探花AV${{探花AVurl}}vodtype/{cateId}-{catePg}/;;z#色花堂${{色花堂url}}typegaokk/{cateId}_{catePg}.html[{{色花堂url}}typegaokk/{cateId}.html];;z#91大神${{91大神url}}index{cateId}{catePg}.html#18动漫${{18动漫url}}{cateId}/page/{catePg}/[{{18动漫url}}{cateId}/];;z#猎奇之家${{猎奇之家url}}list/{cateId}-{catePg}.html#啪啪视频${{啪啪视频url}}index.php/vod/type/{cateId}/page/{catePg}.html#国产片商${{国产片商url}}{cateId}-{catePg}.html#无码黑料${{无码黑料url}}index.php/vod/{cateId}/page/{catePg}.html#Javffm${{Javffm}}drama/page/{catePg}/?orderby=view&tvtype{cateId}#七猫高清${{七猫高清}}htms/{cateId}/{catePg}.htm#1024看${{1024看}}c/{cateId}-{catePg}.html",
"分类url": "{{超A精品}}index.php/vod/type/{cateId}/page/{catePg}.html",
"分类": "超A精品$id/267#91porn$category=ori#探花AV$domestic-media#色花堂$1#91大神$-1-#18动漫$mhosk#猎奇之家$80#啪啪视频$id/20#国产片商$index-0#无码黑料$type/id/253#Javffm$=7#七猫高清$list26#1024看$53331175",
"类型": "明星淫梦$id/244#糖心Vlog$id/269#中文调教$id/268#中文制服$id/267#中文人妻$id/266#中文强奸$id/265#中文出轨$id/264#中文乱伦$id/263#传媒视频$id/236#精品动漫$id/246#国产视频$id/234#性感主播$id/238#伦理三级$id/239#日韩专区$id/245#家庭伦伦$id/252#VR专区$id/242#无码专区$id/241#中文字幕$id/240#萝莉少女$id/250#网曝门事件$id/249#欧美专区$id/248#AV解说$id/247#女同专区$id/259#SM专区$id/258#侵犯专区$id/256#明星换脸$id/254#强奸乱伦$id/253||91原创$category=ori#当前最热$category=hot#本月最热$category=top#10分钟以上$category=long#20分钟以上$category=longer#本月收藏$category=tf#最近加精$category=rf#高清$category=hd#本月讨论$category=md#收藏最多$category=mf||国产传媒$domestic-media#日本有码$japanese-av#中语字幕$chinese-subtitles#无码视频$ribenwuma||国产精品$1#日韩亚洲$2#自拍偷拍$15#欧美激情$3#主播视频$16#中文字幕$4#日韩无码$11#动漫成人$14#推荐视频$5#同性人妖$6#欧美大片$7#原创国产$8#制服淫穴$9#换脸AI区$10#三级综艺$12#次元动漫$13||仓本C仔$-1-#夯先生$-2-#秦先生$-3-#沙漠110$-4-#大闸蟹$-5-#JL屌哥$-6-#KK哥$-7-#轻吻也飘然$-8-#猫先生$-9-#xh98hx$-10-#jinx$-11-#呆哥$-12-#番薯哥$-13-#苍先生$-14-#Mr.s007$-15-#sweattt$-16-#吕布$-17-#啪神ben$-18-#康先生$-19-#佛爷$-20-#王老板$-21-#校长$-22-#大黄鸭$-23-#波哥$-24-#boss$-25-#小青蛙$-26-#混血哥$-27-#天堂$-28-#汤先生$-29-#夜愿哥$-30-#逍遥龙哥$-31-#椰子哥$-32-#内裤哥$-33-#洋米糕$-34-#沈先生$-35-#约约哥$-36-#德莱文$-37-#98K$-38-#王老吉$-39-#四驱兄弟$-40-#汝工作室$-41-#风月海棠$-42-||成人动漫$mhosk#动漫精品$hcmjn#卡通动漫$sitej#激情动漫$utkll#动漫卡通$lsjtl#剧情动漫$sssec#动漫肉番$dfxlt||人妖TS$80#自拍偷拍$591#女同性恋$609#女王调教$616#口交系列$617#AI换脸$620#明星偶像$621#群交淫乱$622||国产$id/20#无码$id/21#字幕$id/22#欧美$id/23#三级$id/24#动漫$id/25||全部$index-0#麻豆$index-1#蜜桃$index-2#swag$index-3#果冻$index-4#天美$index-5#精东$index-6#星空$index-7#扣扣$index-8#乐播$index-9#兔子先生$index-10#皇家华人$index-11#SA国际$index-12||强奸乱伦$type/id/253#明星换脸$type/id/254#SM专区$type/id/258#女同专区$type/id/259#AV解说$type/id/247#欧美专区$type/id/248#网曝门事件$type/id/249#中文字幕$type/id/240#无码专区$type/id/241#VR专区$type/id/242#明星淫梦$type/id/244#日韩专区$type/id/245#伦理三级$type/id/239#性感主播$type/id/238#国产视频$type/id/234#精品动漫$type/id/246#传媒视频$type/id/236#国产乱伦$type/id/237#抖阴短片$type/id/263#萝莉少女$type/id/264#制服诱惑$type/id/265#萝莉区$type/id/266#玩偶姐姐$type/id/267#学生合集$type/id/268#禁漫天堂$type/id/269#中文调教$type/id/270#中文制服$type/id/271#中文人妻$type/id/272#中文强奸$type/id/273#极品学妹$type/id/274||无码破解$|7#国产新品$=chinese-av-porn#传媒出品$=8#中文字幕$=chinese-subtitles||无码素人$list26#日本无码$list84#无码破解$list22#日韩中字$list25#欧美劲爆$list19#经典三级$list28#3D动漫$list20#综合强片$list90#国产自拍$list1#家庭乱伦$list4#少女萝莉$list3#熟女少妇$list5#酒店探花$list7#偷拍盗摄$list8#醉酒迷奸$list37#吃瓜黑料$list38#直播裸聊$list2#巨乳诱惑$list76#户外野战$list36#AI换脸$list23#群P换妻$list73#网黄博主$list57#黑人媚黑$list56||91探花$53591255#欧美精品$53421255#国产色情$53341255#日本精品$53491255#精品推荐$53321255#自拍偷拍$53461255#传媒出品$53711255#主播直播$53351255#欧美极品$53141175#三级自慰$53371175#极骚萝莉$53341175#中文字幕$53321175#动漫精品$53331175#强奸乱伦$53381175#国产自拍$53131175#日韩无码$53151175#自拍偷拍$53391185#日韩无码$53131185#日韩精品$53351185#中文字幕$53301185#大秀视频$53541185#国产精品$53161185#欧美精品$53151185#动漫精品$53321185#国产情色$53131205#中文字幕$53321205#成人动漫$53341205#韩国伦理$53391205#日本无码$53141205#国模私拍$53361205#网红主播$53331205#欧美情色$53351205#国产主播$53141165#无码专区$53161165#欧美性爱$53171165#中文字幕$53211165#亚洲情色$53131165#国产自拍$53151165#强奸乱伦$53191165#熟女人妻$53181165"
}
+31
View File
@@ -0,0 +1,31 @@
{
"作者": "h",
"站名": "超A精品+91porn+探花AV+色花堂+91大神+18动漫+猎奇之家+啪啪视频+国产片商+无码黑料+Javffm+七猫高清",
"请求头": "User-Agent$MOBILE_UA",
"编码": "UTF-8",
"超A精品": "https://gxxxx13.xyz/123/",
"91pornurl": "https://0601.9p47p.com/",
"探花AVurl": "https://tanhuavip12.xyz/853/",
"色花堂url": "https://1gkka.com/",
"91大神url": "https://xn--t4q-91dsvodcom-sb1ww420bv16drbb.xn--91shen-cy3k.com/",
"18动漫url": "https://m.18dongmanwang.com/",
"猎奇之家url": "https://xn--2pj-liqivodcom-265y81sv86ipxq897b.www-liqivod-com.com/",
"啪啪视频url": "http://kanxv3.com/6/",
"国产片商url": "https://xn---gccpsscom-e12p686ciwltn3epeo.xn--gc-if7c031i.com/",
"无码黑料url": "https://mixqq.mixxx20.xyz/123/",
"Javffm": "https://www.javffm.com/",
"七猫高清":"https://www.55mmss.com/",
"数组": "<div class=\"stui-vodlist__box\">&&</li>||91porn--<div id=\"videobox\">&&<div id=\"paging\">分割(后:<img src=images/dislike.png)||探花AV--<video class=\"preview hidden\"&&<div class=\"my-2 text-sm text-nord4 truncate\">||色花堂--<div class=\"img-box cover-md\">&&</h6>||91大神--<li class=\"item\">&&<div class=\"detail\">||18动漫--<div class=\"entry-card\">&&</h2>||猎奇之家--<div class=\"col-md-4&&</a></h3>||啪啪视频--<li class=\"stui-vodlist__item\">&&</a></h4>||国产片商--<li class=\"item\">&&</li>||无码黑料--<div class=\"stui-vodlist__box\">&&<div class=\"stui-vodlist__detail\">||Javffm--<article id=&&</article>||七猫高清--<div class=\"cover-div\">&&<i class=\"heart icon\"",
"标题": "title=\"&&\"||91porn--m-t-5\">&&</span>||探花AV--<img alt=\"&&\"||色花堂--<h6 class=\"title\">&&</a>||91大神--title=\"&&\"||18动漫--alt=\"&&\"||猎奇之家--title=\"&&\"||啪啪视频--title=\"&&\"||国产片商--title=\"&&\"||无码黑料--title=\"&&\"||Javffm--alt=\"&&\"||七猫高清--decodeURIComponent(\"&&\"[工具:解url]",
"副标题":"<span class=\"pic-text text-right\">*-&&</span>||91porn--\"duration\">&&</span>||色花堂--<div class=\"ribbon-top-left\">&&</span>||猎奇之家--<span class=\"vodtime\">&&</span>||七猫高清--<time class=\"video-date\">&&</time>+<span class=view_count>&&</span>",
"链接": "href=\"&&\"||91porn--<a href=\"&&\"||探花AV--<a href=\"&&\"||色花堂--href=\"&&\"||91大神--{{91大神url}}+<a href=\"&&\"||18动漫--<a href=\"&&\"||猎奇之家--<a href=\"&&\"||啪啪视频--<a href=\"&&\"||国产片商--{{国产片商url}}+<a href=\"&&\"||无码黑料--href=\"&&\"||Javffm--<a href=\"&&\"||七猫高清--<a href=\"&&\"",
"图片": "data-original=\"&&\"||91porn--src=\"&&\"||探花AV--src=\"&&\"||色花堂--data-original=\"&&\"||91大神--style=\"background-image:url('&&')||18动漫--<img src=\"&&\"||猎奇之家--style=\"background-image:url('&&')||啪啪视频--data-original=\"&&\"||国产片商--style=\"background-image:url('&&')||无码黑料--data-original=\"&&\"||Javffm--<img src=\"&&\"",
"嗅探词": "rsc.cdn#.mp4#.m3u8",
"直接播放": "1",
"搜索模式": "1",
"搜索url": "{{超A精品}}index.php/vod/search/page/{pg}/wd/{wd}.html",
"特殊分类链接": "91porn${{91pornurl}}v.php?{cateId}&viewtype=basic&page={catePg}#探花AV${{探花AVurl}}vodtype/{cateId}-{catePg}/;;z#色花堂${{色花堂url}}typegaokk/{cateId}_{catePg}.html[{{色花堂url}}typegaokk/{cateId}.html];;z#91大神${{91大神url}}index{cateId}{catePg}.html#18动漫${{18动漫url}}{cateId}/page/{catePg}/[{{18动漫url}}{cateId}/];;z#猎奇之家${{猎奇之家url}}list/{cateId}-{catePg}.html#啪啪视频${{啪啪视频url}}index.php/vod/type/{cateId}/page/{catePg}.html#国产片商${{国产片商url}}{cateId}-{catePg}.html#无码黑料${{无码黑料url}}index.php/vod/{cateId}/page/{catePg}.html#Javffm${{Javffm}}drama/page/{catePg}/?orderby=view&tvtype{cateId}#七猫高清${{七猫高清}}htms/{cateId}/{catePg}.htm",
"分类url": "{{超A精品}}index.php/vod/type/{cateId}/page/{catePg}.html",
"分类": "超A精品$id/267#91porn$category=ori#探花AV$domestic-media#色花堂$1#91大神$-1-#18动漫$mhosk#猎奇之家$80#啪啪视频$id/20#国产片商$index-0#无码黑料$type/id/253#Javffm$=7#七猫高清$list26",
"类型": "明星淫梦$id/244#糖心Vlog$id/269#中文调教$id/268#中文制服$id/267#中文人妻$id/266#中文强奸$id/265#中文出轨$id/264#中文乱伦$id/263#传媒视频$id/236#精品动漫$id/246#国产视频$id/234#性感主播$id/238#伦理三级$id/239#日韩专区$id/245#家庭伦伦$id/252#VR专区$id/242#无码专区$id/241#中文字幕$id/240#萝莉少女$id/250#网曝门事件$id/249#欧美专区$id/248#AV解说$id/247#女同专区$id/259#SM专区$id/258#侵犯专区$id/256#明星换脸$id/254#强奸乱伦$id/253||91原创$category=ori#当前最热$category=hot#本月最热$category=top#10分钟以上$category=long#20分钟以上$category=longer#本月收藏$category=tf#最近加精$category=rf#高清$category=hd#本月讨论$category=md#收藏最多$category=mf||国产传媒$domestic-media#日本有码$japanese-av#中语字幕$chinese-subtitles#无码视频$ribenwuma||国产精品$1#日韩亚洲$2#自拍偷拍$15#欧美激情$3#主播视频$16#中文字幕$4#日韩无码$11#动漫成人$14#推荐视频$5#同性人妖$6#欧美大片$7#原创国产$8#制服淫穴$9#换脸AI区$10#三级综艺$12#次元动漫$13||仓本C仔$-1-#夯先生$-2-#秦先生$-3-#沙漠110$-4-#大闸蟹$-5-#JL屌哥$-6-#KK哥$-7-#轻吻也飘然$-8-#猫先生$-9-#xh98hx$-10-#jinx$-11-#呆哥$-12-#番薯哥$-13-#苍先生$-14-#Mr.s007$-15-#sweattt$-16-#吕布$-17-#啪神ben$-18-#康先生$-19-#佛爷$-20-#王老板$-21-#校长$-22-#大黄鸭$-23-#波哥$-24-#boss$-25-#小青蛙$-26-#混血哥$-27-#天堂$-28-#汤先生$-29-#夜愿哥$-30-#逍遥龙哥$-31-#椰子哥$-32-#内裤哥$-33-#洋米糕$-34-#沈先生$-35-#约约哥$-36-#德莱文$-37-#98K$-38-#王老吉$-39-#四驱兄弟$-40-#汝工作室$-41-#风月海棠$-42-||成人动漫$mhosk#动漫精品$hcmjn#卡通动漫$sitej#激情动漫$utkll#动漫卡通$lsjtl#剧情动漫$sssec#动漫肉番$dfxlt||人妖TS$80#自拍偷拍$591#女同性恋$609#女王调教$616#口交系列$617#AI换脸$620#明星偶像$621#群交淫乱$622||国产$id/20#无码$id/21#字幕$id/22#欧美$id/23#三级$id/24#动漫$id/25||全部$index-0#麻豆$index-1#蜜桃$index-2#swag$index-3#果冻$index-4#天美$index-5#精东$index-6#星空$index-7#扣扣$index-8#乐播$index-9#兔子先生$index-10#皇家华人$index-11#SA国际$index-12||强奸乱伦$type/id/253#明星换脸$type/id/254#SM专区$type/id/258#女同专区$type/id/259#AV解说$type/id/247#欧美专区$type/id/248#网曝门事件$type/id/249#中文字幕$type/id/240#无码专区$type/id/241#VR专区$type/id/242#明星淫梦$type/id/244#日韩专区$type/id/245#伦理三级$type/id/239#性感主播$type/id/238#国产视频$type/id/234#精品动漫$type/id/246#传媒视频$type/id/236#国产乱伦$type/id/237#抖阴短片$type/id/263#萝莉少女$type/id/264#制服诱惑$type/id/265#萝莉区$type/id/266#玩偶姐姐$type/id/267#学生合集$type/id/268#禁漫天堂$type/id/269#中文调教$type/id/270#中文制服$type/id/271#中文人妻$type/id/272#中文强奸$type/id/273#极品学妹$type/id/274||无码破解$|7#国产新品$=chinese-av-porn#传媒出品$=8#中文字幕$=chinese-subtitles||无码素人$list26#日本无码$list84#无码破解$list22#日韩中字$list25#欧美劲爆$list19#经典三级$list28#3D动漫$list20#综合强片$list90#国产自拍$list1#家庭乱伦$list4#少女萝莉$list3#熟女少妇$list5#酒店探花$list7#偷拍盗摄$list8#醉酒迷奸$list37#吃瓜黑料$list38#直播裸聊$list2#巨乳诱惑$list76#户外野战$list36#AI换脸$list23#群P换妻$list73#网黄博主$list57#黑人媚黑$list56"
}
+266
View File
@@ -0,0 +1,266 @@
# -*- coding: utf-8 -*-
import gzip
import hashlib
import hmac
import json
import os
import time
import uuid
import requests
try:
from base.spider import Spider as BaseSpider
except Exception:
class BaseSpider:
pass
class _AESCBC:
@staticmethod
def encrypt(data, key, iv):
try:
from Crypto.Cipher import AES
return AES.new(key, AES.MODE_CBC, iv).encrypt(_AESCBC.pad(data))
except Exception:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
enc = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()).encryptor()
return enc.update(_AESCBC.pad(data)) + enc.finalize()
@staticmethod
def decrypt(data, key, iv):
try:
from Crypto.Cipher import AES
plain = AES.new(key, AES.MODE_CBC, iv).decrypt(data)
except Exception:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
dec = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()).decryptor()
plain = dec.update(data) + dec.finalize()
return _AESCBC.unpad(plain)
@staticmethod
def pad(data):
n = 16 - len(data) % 16
return data + bytes([n]) * n
@staticmethod
def unpad(data):
n = data[-1] if data else 0
return data[:-n] if 1 <= n <= 16 else data
class Spider(BaseSpider):
def __init__(self):
self.host = "https://xqjzvcvt.top"
self.api = self.host + "/api"
self.name = "黄豆短剧"
self.platform_key = "7961beb44246e3012ce228d6b5ced05a"
self.version = "2.0.0"
self.device_type = "web"
self.session_id = uuid.uuid4().hex
self.device_id = self.session_id
self.token = ""
self.headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36", "Accept": "*/*", "Origin": self.host, "Referer": self.host + "/home", "Content-Type": "application/octet-stream"}
self.session = requests.Session()
self.session.headers.update(self.headers)
self.class_cache = None
self.filter_cache = {}
def init(self, extend=""):
if extend:
try:
cfg = json.loads(extend)
self.host = (cfg.get("site") or cfg.get("base_url") or self.host).rstrip("/")
self.api = self.host + "/api"
self.token = cfg.get("token", self.token)
self.headers["Origin"] = self.host
self.headers["Referer"] = self.host + "/home"
self.session.headers.update(self.headers)
except Exception:
None
def getName(self):
return self.name
def homeContent(self, filter):
data = self._api("/drama/list", {"page": "1", "page_size": "18"})
classes = self._classes()
return {"class": classes, "filters": self._filters(classes), "list": [self._vod(x) for x in self._list(data)], "parse": 0, "jx": 0}
def categoryContent(self, tid, pg, filter, extend):
extend = extend or {}
if tid == "yuandou":
data = self._api("/drama/navBlock", {"code": "yuandou", "tab": "recommend", "page": str(pg)})
items = self._nav_items(data)
else:
req = {"page": str(pg), "page_size": "18"}
if tid and tid not in ("all", "recommend"):
tabs = self._nav_filter(tid)
idx = self._int(extend.get("sub"), 0)
sub = tabs[idx] if tabs and 0 <= idx < len(tabs) else {}
flt = sub.get("filter", {}) if isinstance(sub, dict) else {}
req["cat_id"] = flt.get("cat_id", "")
if flt.get("tag_id"):
req["tag_id"] = flt.get("tag_id", "")
req["order"] = flt.get("order", "") or extend.get("order", "")
elif extend.get("order"):
req["order"] = extend.get("order")
if extend.get("update_status"):
req["update_status"] = extend.get("update_status")
data = self._api("/drama/list", req)
items = self._list(data)
return {"page": int(pg), "pagecount": int(pg) if len(items) < 18 else int(pg) + 1, "limit": 18, "total": 99999, "list": [self._vod(x) for x in items], "parse": 0, "jx": 0}
def detailContent(self, ids):
vid = str(ids[0]).replace("rp_", "")
obj = self._api("/drama/detail", {"id": vid})
data = obj.get("data", obj) if isinstance(obj, dict) else {}
if not isinstance(data, dict):
return {"list": []}
data = self._unlock(data)
vod_id = self._sid(data.get("id") or data.get("drama_id") or vid)
name = data.get("name") or data.get("title") or data.get("t") or vod_id
eps = data.get("episodes") if isinstance(data.get("episodes"), list) else []
count = self._int(data.get("episode_count") or data.get("free_episodes"), len(eps) or 1)
play = []
if eps:
for i, ep in enumerate(eps, 1):
seq = ep.get("seq") or ep.get("episode") or ep.get("ep") or i
play.append("%s$%s|%s" % (ep.get("name") or ep.get("title") or "%s" % seq, vod_id, seq))
else:
play = ["%s集$%s|%s" % (i, vod_id, i) for i in range(1, count + 1)]
vod = {"vod_id": vod_id, "vod_name": name, "vod_pic": self._pic(data), "type_name": data.get("category") or data.get("type") or "", "vod_year": "", "vod_area": "", "vod_remarks": data.get("update_label") or "%s" % count, "vod_actor": "", "vod_director": "", "vod_content": data.get("description") or data.get("summary") or name, "vod_play_from": self.name, "vod_play_url": "#".join(play)}
return {"list": [vod], "parse": 0, "jx": 0}
def searchContent(self, key, quick, pg="1"):
data = self._api("/drama/list", {"page": str(pg), "page_size": "18", "keywords": str(key)})
items = self._list(data)
return {"page": int(pg), "pagecount": int(pg) if len(items) < 18 else int(pg) + 1, "limit": 18, "total": 99999, "list": [self._vod(x) for x in items], "parse": 0, "jx": 0}
def playerContent(self, flag, id, vipFlags):
vid, seq = self._split(id)
obj = self._api("/drama/play", {"id": vid, "seq": str(seq)}, True)
data = obj.get("data", {}) if isinstance(obj, dict) else {}
url = data.get("m3u8") or data.get("url") or self._hls(vid, seq)
return {"parse": 0, "playUrl": "", "url": url, "jx": 0, "header": {"User-Agent": self.headers["User-Agent"], "Referer": self.host + "/home", "Origin": self.host}}
def _api(self, path, data=None, silent=False):
path = "/" + path.lstrip("/")
rid = str(uuid.uuid4())
key = self._key(rid)
iv = os.urandom(16)
raw = json.dumps({"token": self.token or "", "deviceId": self.device_id, "data": data or {}}, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
body = iv + _AESCBC.encrypt(gzip.compress(raw), key, iv)
ts = int(time.time())
sign = hashlib.sha256(("Dart|%s|%s|%s|%s" % (self.session_id, rid, ts, path)).encode("utf-8")).hexdigest() + "-" + str(ts)
h = dict(self.headers)
h.update({"version": self.version, "deviceType": self.device_type, "time": str(ts), "sign": sign, "requestId": rid, "sessionId": self.session_id, "deviceBrand": "", "deviceModel": "", "systemName": "", "systemVersion": ""})
try:
r = self.session.post(self.api + path, data=body, headers=h, timeout=20, verify=False)
r.raise_for_status()
return self._decode(r.content, rid)
except Exception:
return {}
def _key(self, rid):
return hmac.new(self.platform_key.encode("utf-8"), bytes.fromhex(str(rid).replace("-", "")), hashlib.sha256).digest()
def _decode(self, blob, rid):
if not blob or len(blob) < 32 or (len(blob) - 16) % 16 != 0:
try:
return json.loads(blob.decode("utf-8"))
except Exception:
return {}
plain = _AESCBC.decrypt(blob[16:], self._key(rid), blob[:16])
if plain[:2] == b"\x1f\x8b":
plain = gzip.decompress(plain)
return json.loads(plain.decode("utf-8"))
def _classes(self):
if self.class_cache:
return self.class_cache
arr = [{"type_id": "all", "type_name": "全部短剧"}]
data = self._api("/drama/navList", {})
for item in self._list(data.get("data", data) if isinstance(data, dict) else data):
tid = str(item.get("code") or item.get("id") or item.get("cat_id") or "")
name = item.get("name") or item.get("title") or tid
if tid and name:
arr.append({"type_id": tid, "type_name": name})
self.class_cache = arr
return arr
def _filters(self, classes):
common = [{"key": "order", "name": "排序", "value": [{"n": "默认", "v": ""}, {"n": "最新", "v": "new"}, {"n": "最热", "v": "hot"}]}, {"key": "update_status", "name": "状态", "value": [{"n": "全部", "v": ""}, {"n": "连载", "v": "0"}, {"n": "完结", "v": "1"}]}]
fs = {}
for c in classes:
tid = c["type_id"]
tabs = self._nav_filter(tid) if tid not in ("all", "yuandou") else []
fs[tid] = ([{"key": "sub", "name": "子分类", "value": [{"n": t.get("name", "默认"), "v": str(i)} for i, t in enumerate(tabs)]}] if tabs else []) + common
return fs
def _nav_filter(self, code):
if code not in self.filter_cache:
data = self._api("/drama/navFilter", {"code": str(code)})
self.filter_cache[code] = self._list(data.get("data", data) if isinstance(data, dict) else data)
return self.filter_cache.get(code, [])
def _list(self, data):
if isinstance(data, list):
return data
if not isinstance(data, dict):
return []
if isinstance(data.get("list"), list):
return data["list"]
if isinstance(data.get("items"), list):
return data["items"]
if isinstance(data.get("data"), list):
return data["data"]
if isinstance(data.get("data"), dict):
return self._list(data["data"])
return []
def _nav_items(self, data):
blocks = self._list(data.get("data", data) if isinstance(data, dict) else data)
items = []
for b in blocks:
if isinstance(b, dict) and isinstance(b.get("items"), list):
items += b.get("items")
elif isinstance(b, dict) and (b.get("id") or b.get("drama_id")):
items.append(b)
return items
def _vod(self, item):
item = item or {}
vid = self._sid(item.get("id") or item.get("drama_id") or "")
remarks = item.get("update_label") or item.get("corner") or ("%s" % item.get("episode_count") if item.get("episode_count") else "")
return {"vod_id": vid, "vod_name": item.get("name") or item.get("title") or item.get("t") or vid, "vod_pic": self._pic(item), "vod_remarks": remarks}
def _pic(self, item):
return item.get("img_y") or item.get("img_x") or item.get("img") or item.get("cover") or item.get("pic") or ""
def _unlock(self, d):
eps = d.get("episodes")
if isinstance(eps, list):
for ep in eps:
if isinstance(ep, dict):
ep["is_buy"] = True
ep["type"] = "free"
ep["price"] = 0
ep["methods"] = []
d.update({"pay_type": "free", "money": 0, "episode_price": 0, "points_price": 0, "can_vip_watch": True, "is_buy_whole": True, "vip_episodes": [], "coin_episodes": [], "points_episodes": []})
return d
def _sid(self, x):
return str(x or "").replace("rp_", "")
def _split(self, x):
p = str(x).split("|", 1)
return self._sid(p[0]), p[1] if len(p) > 1 and p[1] else "1"
def _hls(self, vid, seq):
return "%s/api/drama/hls/%s/%s/play.m3u8?line=free" % (self.host, self._sid(vid), seq)
def _int(self, x, d=0):
try:
return int(x)
except Exception:
return d