Files
2026-06-29 03:41:14 +00:00

319 lines
12 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
# by @嗷呜
import base64
import sys
from pprint import pprint
import hmac
import hashlib
import secrets
import time
import uuid
import json
import random
import string
from urllib.parse import quote
import requests
from Crypto.Cipher import AES
from Crypto.Hash import MD5
from Crypto.Util.Padding import pad
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
sys.path.append('..')
from base.spider import Spider
class Spider(Spider):
def init(self, extend='{}'):
self.session = requests.session()
pass
def getName(self):
pass
def isVideoFormat(self, url):
pass
def manualVideoCheck(self):
pass
def destroy(self):
pass
host='https://film.symx.club'
RSA_N = "c1e3934d1614465b33053e7f48ee4ec87b14b95ef88947713d25eecbff7e74c7977d02dc1d9451f79dd5d1c10c29acb6a9b4d6fb7d0a0279b6719e1772565f09af627715919221aef91899cae08c0d686d748b20a3603be2318ca6bc2b59706592a9219d0bf05c9f65023a21d2330807252ae0066d59ceefa5f2748ea80bab81"
RSA_E = 65537
STATIC_BASE = "https://static.geetest.com/"
VERIFY_HOST = 'https://gcaptcha4.geetest.com'
ClientId=MD5.new(str(int(time.time())).encode()).hexdigest();Token=''
def rsa_encrypt(self,random_key):
pub_key = RSA.construct((int(self.RSA_N, 16), self.RSA_E))
cipher = PKCS1_v1_5.new(pub_key)
return cipher.encrypt(random_key.encode('utf-8')).hex()
def aes_encrypt(self,plaintext, key):
iv = b"0000000000000000"
cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv)
padded_data = pad(plaintext.encode('utf-8'), AES.block_size)
return cipher.encrypt(padded_data).hex()
def get_w(self, payload_dict):
random_key = "".join(random.choices(string.ascii_letters + string.digits, k=16))
json_str = json.dumps(payload_dict, separators=(',', ':'))
return self.aes_encrypt(json_str, random_key) + self.rsa_encrypt(random_key)
def get_dynamic_payload(self,lot_number, set_left, passtime, captcha_id, pow_detail):
key_name = lot_number[26:30] + lot_number[12:16]
sub_key = lot_number[16:24]
val = lot_number[6:10]
pow_msg = f"1|0|md5|{pow_detail['datetime']}|{captcha_id}|{lot_number}||{secrets.token_hex(8) }"
pow_sign = hashlib.md5(pow_msg.encode()).hexdigest()
payload = {
"setLeft": set_left,
"passtime": passtime,
"userresponse": set_left / 1.0059466666666665 +2,
"device_id": "",
"lot_number": lot_number,
"pow_msg": pow_msg,
"pow_sign": pow_sign,
"geetest": "captcha",
"lang": "zh",
"ep": "123",
"biht": "1426265548",
"yDWL": "hZGx",
key_name: {sub_key: val},
"em": {"ph": 0, "cp": 0, "ek": "11", "wd": 1, "nt": 0, "si": 0, "sc": 0}
}
return payload
def generate_checksum_timestamp(self):
r = str(int(time.time() * 1000))
prefix = r[:-1]
digit_sum = sum(int(d) for d in prefix)
check_digit = digit_sum % 10
return prefix + str(check_digit)
def get_site_headers(self,path, end=0):
secret_key = "lslx_sk"
timestamp = self.generate_checksum_timestamp()
raw_data = f"{timestamp}symx_{secret_key}{path}"
arranged = raw_data.replace("1", "i").replace("0", "o").replace("5", "s")
if end:
secret_key = ''
arranged = ''
signature = hmac.new(
secret_key.encode('utf-8'),
arranged.encode('utf-8'),
digestmod=hashlib.sha256
).hexdigest()
header = {
'User-Agent': 'SYMX_ANDROID',
'user-agent': 'Mozilla/5.0 (Linux; Android 13; M2012K10C Build/TP1A.220624.014; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Mobile Safari/537.36 uni-app Html5Plus/1.0 (Immersed/30.545454)',
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json;charset=UTF-8',
'X-Platform': 'android',
'X-Timestamp': timestamp,
'X-Sign-X': signature,
'X-Client-Id':self.ClientId,
'Referer': 'https://film.symx.club/',
}
if self.Token: header['X-Verify-Token'] = self.Token
if end:
del header['X-Sign-X']
header['X-Report-Id'] = signature
return header
def run_verify(self,i=0):
if i>3: return
try:
config = self.session.get(f'{self.host}/api/auth/verify/config',
headers=self.get_site_headers('/auth/verify/config')).json()
captcha_id = config['data']['captchaId']
params = {
'callback': f'geetest_{int(time.time()* 1000)}',
'captcha_id': captcha_id,
'challenge': str(uuid.uuid4()),
'client_type': 'web',
'lang': 'zho',
}
load_res = self.fetch(f'{self.VERIFY_HOST}/load', params=params).text
data = json.loads(load_res[len(params['callback']) + 1:-1])['data']
t=str(int(time.time() * 1000))
heade={
'timestamp':t,
"sign":MD5.new(f'44344434tffrfeeffgdggdg{t}'.encode()).hexdigest()
}
body={
'type':'solve',
'bg':base64.b64encode(self.fetch(f"{self.STATIC_BASE}{data['bg']}").content).decode(),
'hb':base64.b64encode(self.fetch(f"{self.STATIC_BASE}{data['slice']}").content).decode(),
}
resp=self.post("http://mytv6688.xyz/aowuapp",json=body,headers=heade).json()
print("验证结果1", resp)
pass_time = random.randint(1200, 2200)
inner_payload = self.get_dynamic_payload(data['lot_number'], resp["result"], pass_time, captcha_id,
data['pow_detail'])
verify_params = {
"callback": f"geetest_{int(time.time() * 1000)}",
"captcha_id": captcha_id,
"client_type": "web",
"lot_number": data['lot_number'],
"payload": data['payload'],
"process_token": data['process_token'],
"payload_protocol": data['payload_protocol'],
"pt": data['pt'],
"w": self.get_w(inner_payload)
}
verify_res_raw = self.fetch(f'{self.VERIFY_HOST}/verify', params=verify_params).text
print("验证结果2", verify_res_raw)
verify_data = json.loads(verify_res_raw[len(verify_params['callback']) + 1:-1])
sc = verify_data['data']['seccode']
json_body = {
"captchaId": sc['captcha_id'],
"captchaOutput": sc['captcha_output'],
"genTime": int(sc['gen_time']),
"lotNumber": sc['lot_number'],
"passToken": sc['pass_token']
}
final_res = self.session.post(f'{self.host}/api/auth/verify', headers=self.get_site_headers("/auth/verify"),
json=json_body)
print("验证结果3", final_res.text)
self.Token = final_res.json()["data"]["token"]
except Exception as e:
print(e)
return self.run_verify(i+1)
def Req(self,path,params,i=0):
self.session.headers.update(self.get_site_headers(path.split("/api")[-1], i))
resp=self.session.get(f"{self.host}{path}",params=params)
if '完成验证' in resp.text:
self.run_verify()
self.session.headers.update(self.get_site_headers(path.split("/api")[-1], i))
resp = self.session.get(f"{self.host}{path}",params=params)
print(resp.status_code)
# print(resp.text)
return resp.json()
def homeContent(self, filter):
data=self.Req("/api/category/top",{},1)
result = {}
classes = []
for k in data['data']:
classes.append({
'type_name': k['name'],
'type_id': k['id']
})
# fil = []
# resp=self.Req("/api/film/category/filter",{'categoryId':k['id']},1)
# for i,v in resp['data'].items():
# if not isinstance(v,list) or len(v)==0 or i=='sortOptions':continue
# fil.append({
# 'key': i,
# 'name': i,
# 'value': [{'n':x,'v':x} for x in v]
# })
# fil.append(self.ddd)
# filters[k['id']] = fil
result['class'] = classes
result['filters'] = self.fetch("http://mytv6688.xyz/pyplugin/木兮筛选.json").json()
return result
def homeVideoContent(self):
data=self.Req("/api/poster/list",{},1)
vlist = []
for k in data['data']:
vlist.append({
'vod_id': k.get('filmId'),
'vod_name': k.get('filmName'),
'vod_pic': k.get('poster'),
})
return {'list':vlist}
def getList(self,data):
vlist = []
for k in data:
vlist.append({
'vod_id': k.get('id'),
'vod_name': k.get('name'),
'vod_pic': k.get('cover'),
'vod_remarks': k.get('updateStatus'),
})
return vlist
def categoryContent(self, tid, pg, filter, extend):
params={
"area": extend.get('areaOptions', ''),
"childCategoryId": "",
"categoryId": tid,
"language": extend.get('languageOptions', ''),
"pageNum": pg,
"pageSize": "10",
"sort": extend.get('sortOptions', ''),
"year": extend.get('yearOptions', '')
}
resp=self.Req("/api/film/category/list", params)
result = {}
result['list'] =self.getList(resp['data']['list'])
result['page'] = pg
result['pagecount'] = 9999
result['limit'] = 90
result['total'] = 999999
return result
def detailContent(self, ids):
resp=self.Req("/api/film/detail/play/app",{'id': ids[0]})
v=resp['data']
n,p=[],[]
for i in v.get('playLineList'):
n.append(i['playerName'])
m=[f"{j['name']}${j['id']}" for j in i.get('lines')]
p.append('#'.join(m))
vod = {
'type_name': v.get('categoryName'),
'vod_year': v.get('year'),
'vod_area': v.get('area'),
'vod_remarks': v.get('updateStatus'),
'vod_actor': v.get('actor'),
'vod_director': '云霄仙子(困困版)',
'vod_content': v.get('blurb'),
'vod_play_from': '$$$'.join(n),
'vod_play_url': '$$$'.join(p)
}
return {'list':[vod]}
def searchContent(self, key, quick, pg="1"):
params={
"pageNum": pg,
"pageSize": "10",
"keyword": key
}
resp=self.Req('/api/film/search',params=params)
return {'list':self.getList( resp['data']['list']),'page':pg}
def playerContent(self, flag, id, vipFlags):
resp=self.Req("/api/line/play/parse", {"lineId": id})
return {'parse': 0, 'url': resp['data'], 'header': ''}
def localProxy(self, param):
pass
def liveContent(self, url):
pass
if __name__ == "__main__":
sp = Spider()
formatJo = sp.init()
formatJo = sp.homeContent(False) # 主页,等于真表示启用筛选
# formatJo = sp.homeVideoContent() # 主页视频
# formatJo = sp.searchContent("斗罗",False,'1') # 搜索{"area":"大陆","by":"hits","class":"国产","lg":"国语"}
# formatJo = sp.categoryContent('2', '1', False, {}) # 分类
# formatJo = sp.detailContent(['126634']) # 详情
# formatJo = sp.playerContent("","https://www.yingmeng.net/vodplay/140148-2-1.html",{}) # 播放
# formatJo = sp.localProxy({"":"https://www.yingmeng.net/vodplay/140148-2-1.html"}) # 播放
pprint(formatJo)