Files
2026-07-11 04:45:19 +00:00

343 lines
12 KiB
Python

import re
import sys
import uuid
import json
import base64
from Crypto.Cipher import AES
from base.spider import Spider
from Crypto.Util.Padding import pad
from Crypto.Util.Padding import unpad
# 移除了urlencode导入,将使用自定义方法
sys.path.append('..')
headerx = {
'User-Agent': 'okhttp/3.10.0' # okhttp/3.14.9
}
class Spider(Spider):
global headerx
init_data = ''
search_verify = ''
def getName(self):
return "首页"
def init(self, extend):
js1=json.loads(extend)
host = js1['host']
if re.match(r'^https:\/\/.*\.(txt|json)$',host):
host = self.fetch(host, headers=headerx, timeout=10).text.rstrip('/')
self.xurl = host + js1.get('api','/api.php/getappapi')
self.key = js1['datakey']
self.iv = js1.get('dataiv',self.key)
res = self.fetch(self.xurl + '.index/initV119', headers=headerx).json()
encrypted_data = res['data']
response = self.decrypt(encrypted_data)
init_data = json.loads(response)
self.init_data = init_data
self.search_verify = init_data['config'].get('system_search_verify_status',False)
def homeContent(self, filter):
kjson = self.init_data
result = {"class": [], "filters": {}}
for i in kjson['type_list']:
if not(i['type_name'] in ['全部', 'QQ', 'juo.one'] or '企鹅群' in i['type_name']):
result['class'].append({
"type_id": i['type_id'],
"type_name": i['type_name']
})
name_mapping = {'class': '类型', 'area': '地区', 'lang': '语言', 'year': '年份', 'sort': '排序'}
filter_items = []
for filter_type in i.get('filter_type_list', []):
filter_name = filter_type.get('name')
values = filter_type.get('list', [])
if not values:
continue
value_list = [{"n": value, "v": value} for value in values]
display_name = name_mapping.get(filter_name, filter_name)
key = 'by' if filter_name == 'sort' else filter_name
filter_items.append({
"key": key,
"name": display_name,
"value": value_list
})
type_id = i.get('type_id')
if filter_items:
result["filters"][str(type_id)] = filter_items
return result
def homeVideoContent(self):
videos = []
kjson = self.init_data
for i in kjson['type_list']:
for item in i['recommend_list']:
vod_id = item['vod_id']
name = item['vod_name']
pic = item['vod_pic']
remarks = item['vod_remarks']
video = {
"vod_id": vod_id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remarks
}
videos.append(video)
return {'list': videos}
def categoryContent(self, cid, pg, filter, ext):
videos = []
payload = {
'area': ext.get('area','全部'),
'year': ext.get('year','全部'),
'type_id': cid,
'page': str(pg),
'sort': ext.get('sort','最新'),
'lang': ext.get('lang','全部'),
'class': ext.get('class','全部')
}
url = f'{self.xurl}.index/typeFilterVodList'
res = self.post(url=url, headers=headerx,data=payload).json()
encrypted_data = res['data']
kjson = self.decrypt(encrypted_data)
kjson1 = json.loads(kjson)
for i in kjson1['recommend_list']:
id = i['vod_id']
name = i['vod_name']
pic = i['vod_pic']
remarks = i['vod_remarks']
video = {
"vod_id": id,
"vod_name": name,
"vod_pic": pic,
"vod_remarks": remarks
}
videos.append(video)
return {'list': videos, 'page': pg, 'pagecount': 9999, 'limit': 90, 'total': 999999}
def detailContent(self, ids):
did = ids[0]
payload = {
'vod_id': did,
}
api_endpoints = ['vodDetail', 'vodDetail2']
for endpoint in api_endpoints:
url = f'{self.xurl}.index/{endpoint}'
response = self.post(url=url, headers=headerx, data=payload)
if response.status_code == 200:
response_data = response.json()
encrypted_data = response_data['data']
kjson1 = self.decrypt(encrypted_data)
kjson = json.loads(kjson1)
break
videos = []
play_form = ''
play_url = ''
actor = kjson['vod']['vod_actor']
director = kjson['vod'].get('vod_director', '')
area = kjson['vod']['vod_area']
name = kjson['vod']['vod_name']
year = kjson['vod']['vod_year']
content = kjson['vod']['vod_content']
subtitle = kjson['vod']['vod_remarks']
desc = kjson['vod']['vod_lang']
remark = '时间:' + subtitle + ' 语言:' + desc
for line in kjson['vod_play_list']:
keywords = ['防走丢', '群', '防失群', 'Q','juo.one',]
if any(keyword in line['player_info']['show'] for keyword in keywords):
continue
play_form += line['player_info']['show'] + '$$$'
parse = line['player_info']['parse']
player_parse_type = line['player_info']['player_parse_type']
kurls = ""
for vod in line['urls']:
kurl = vod['url']
if '.m3u8' in kurl:
kurls += str(vod['name']) + '$' + vod['url'] + '#'
else:
if 'm3u8' not in kurl:
token = 'token+' + vod['token']
kurls += str(vod['name']) + '$' + parse + ',' + vod[
'url'] + ',' + token + ',' + player_parse_type + '#'
kurls = kurls.rstrip('#')
play_url += kurls + '$$$'
play_form = play_form.rstrip('$$$')
play_url = play_url.rstrip('$$$')
videos.append({
"vod_id": did,
"vod_name": name,
"vod_actor": actor.replace('演员', ''),
"vod_director": director.replace('导演', ''),
"vod_content": content,
"vod_remarks": remark,
"vod_year": year + '年',
"vod_area": area,
"vod_play_from": play_form,
"vod_play_url": play_url
})
return {'list': videos}
def playerContent(self, flag, id, vipFlags):
url = ''
if re.search(r'https?:\/\/.*\.(m3u8|mp4|flv)', id):
url = id
elif 'url=' in id:
aid = id.split(',')
uid = aid[0]
kurl = aid[1]
kjson = uid + kurl
url2 = f"{kjson}"
response = self.fetch(url=url2)
if response.status_code == 200:
kjson1 = response.json()
url = kjson1['url']
else:
aid = id.split(',')
bid = aid[-1]
uid = aid[0]
kurl = aid[1]
token = aid[2].replace('token+', '')
id1 = self.decrypt_wb(kurl)
payload = {
'parse_api': uid,
'url': id1,
'player_parse_type': bid,
'token': token
}
url1 = f"{self.xurl}.index/vodParse"
response = self.post(url=url1, headers=headerx, data=payload)
if response.status_code == 200:
response_data = response.json()
encrypted_data = response_data['data']
kjson = self.decrypt(encrypted_data)
kjson1 = json.loads(kjson)
kjson2 = kjson1['json']
kjson3 = json.loads(kjson2)
url = kjson3['url']
return {"parse": 0, "playUrl": '', "url": url, "header": {'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 14; 23113RK12C Build/SKQ1.231004.001)'}}
def searchContent(self, key, quick, pg="1"):
videos = []
payload = {
'keywords': key,
'type_id': "0",
'page': str(pg)
}
if self.search_verify:
verifi = self.verification()
if verifi is None:
return {'list': []}
payload['code'] = verifi['code']
payload['key'] = verifi['uuid']
# 替换urlencode的实现
data_str = '&'.join([f"{k}={v}" for k, v in payload.items()])
# 设置请求头
headers = {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
**headerx # 保留原来的 headerx 内容
}
url = f'{self.xurl}.index/searchList'
res = self.post(url=url, data=data_str, headers=headers).json()
if not res.get('data'):
return {'list': [], 'msg': res.get('msg')}
encrypted_data = res['data']
kjson = self.decrypt(encrypted_data)
kjson1 = json.loads(kjson)
for i in kjson1['search_list']:
video = {
"vod_id": i['vod_id'],
"vod_name": i['vod_name'],
"vod_pic": i['vod_pic'],
"vod_remarks": f"{i.get('vod_year', '')} {i.get('vod_class', '')}".strip()
}
videos.append(video)
return {
'list': videos,
'page': pg,
'pagecount': 9999,
'limit': 90,
'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
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def decrypt(self, encrypted_data_b64):
key_text = self.key
iv_text = self.iv
key_bytes = key_text.encode('utf-8')
iv_bytes = iv_text.encode('utf-8')
encrypted_data = base64.b64decode(encrypted_data_b64)
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
decrypted_padded = cipher.decrypt(encrypted_data)
decrypted = unpad(decrypted_padded, AES.block_size)
return decrypted.decode('utf-8')
def decrypt_wb(self, sencrypted_data):
key_text = self.key
iv_text = self.iv
key_bytes = key_text.encode('utf-8')
iv_bytes = iv_text.encode('utf-8')
data_bytes = sencrypted_data.encode('utf-8')
padded_data = pad(data_bytes, AES.block_size)
cipher = AES.new(key_bytes, AES.MODE_CBC, iv_bytes)
encrypted_bytes = cipher.encrypt(padded_data)
encrypted_data_b64 = base64.b64encode(encrypted_bytes).decode('utf-8')
return encrypted_data_b64
def ocr(self,base64Img):
dat2 = self.post("https://api.nn.ci/ocr/b64/text",data=base64Img,headers=headerx).text
if dat2:
return dat2
else:
return None
def verification(self):
random_uuid = str(uuid.uuid4())
dat = self.fetch(f'{self.xurl}.verify/create?key={random_uuid}',headers=headerx).content
base64_img = base64.b64encode(dat).decode('utf-8')
if not dat:
return None
code = self.ocr(base64_img)
if not code:
return None
code = self.replace_code(code) # 修改为类方法调用
if not (len(code) == 4 and code.isdigit()):
return None
return {'uuid': random_uuid, 'code': code}
@staticmethod # 添加静态方法装饰器
def replace_code(text):
replacements = {'y': '9','口': '0','q': '0','u': '0','o': '0','>': '1','d': '0','b': '8','已': '2','D': '0','五': '5'}
if len(text) == 3:
text = text.replace('066', '1666')
text = text.replace('566', '5066')
return ''.join(replacements.get(c, c) for c in text)