From ac736fb2a3f6a211a9b4a3734c1bc9d33aaf2b30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=B7=E9=9B=A8=20=E4=B9=94?= <22626198@qq.com> Date: Sat, 3 May 2025 13:00:40 +0000 Subject: [PATCH] =?UTF-8?q?Update=20PY/app/APPV2.py,=20PY/app/CliCli?= =?UTF-8?q?=E5=8A=A8=E6=BC=ABAPP.py,=20PY/app/hitvAPP.py,=20PY/app/MiFunAP?= =?UTF-8?q?P.py,=20PY/app/=E7=88=B1=E7=93=9CTVAPP.py,=20PY/app/=E8=BE=B9?= =?UTF-8?q?=E7=BC=98=E5=BD=B1=E8=A7=86APP.py,=20PY/app/=E5=85=89=E9=80=9FA?= =?UTF-8?q?PP.py,=20PY/app/=E7=81=AB=E8=BD=A6=E5=A4=AA=E9=A1=BAAPP.py,=20P?= =?UTF-8?q?Y/app/=E5=9B=BD=E5=A4=96=E5=89=A7APP.py,=20PY/app/=E5=89=A7?= =?UTF-8?q?=E5=A4=9A=E7=9F=AD=E5=89=A7APP.py,=20PY/app/=E6=81=8B=E9=B1=BC?= =?UTF-8?q?=E5=BD=B1=E8=A7=86APP.py,=20PY/app/=E9=9B=B6=E5=BA=A6=E5=BD=B1?= =?UTF-8?q?=E8=A7=86APP.py,=20PY/app/=E7=BE=8E=E5=B8=95APP.py,=20PY/app/?= =?UTF-8?q?=E8=83=96=E8=99=8EAPP.py,=20PY/app/=E9=AD=94=E6=96=B9=E5=BD=B1?= =?UTF-8?q?=E8=A7=86APP.py,=20PY/app/=E7=9A=AE=E7=9A=AE=E8=99=BEAPP.py,=20?= =?UTF-8?q?PY/app/=E5=A5=87=E8=BF=B9APP.py,=20PY/app/=E8=8B=A5=E6=83=9C?= =?UTF-8?q?=E8=BF=BD=E5=89=A7APP.py,=20PY/app/=E4=B8=89=E5=8F=B7=E5=8A=A8?= =?UTF-8?q?=E6=BC=ABAPP.py,=20PY/app/=E8=A7=86=E8=A7=89APP.py,=20PY/app/?= =?UTF-8?q?=E5=B0=8F=E8=8B=B9=E6=9E=9CAPP.py,=20PY/app/=E5=93=87=E5=93=87A?= =?UTF-8?q?PP.py,=20PY/app/=E6=82=A0=E6=82=A0APP.py,=20PY/app/=E4=BA=91?= =?UTF-8?q?=E7=AB=AFAPP.py,=20PY/app/=E4=BA=91=E9=80=9F=E5=BD=B1=E8=A7=86A?= =?UTF-8?q?PP.py=20files?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PY/app/APPV2.py | 97 +++++++++++ PY/app/CliCli动漫APP.py | 254 ++++++++++++++++++++++++++++ PY/app/MiFunAPP.py | 249 ++++++++++++++++++++++++++++ PY/app/hitvAPP.py | 146 ++++++++++++++++ PY/app/三号动漫APP.py | 175 ++++++++++++++++++++ PY/app/云端APP.py | 245 +++++++++++++++++++++++++++ PY/app/云速影视APP.py | 219 ++++++++++++++++++++++++ PY/app/光速APP.py | 222 +++++++++++++++++++++++++ PY/app/剧多短剧APP.py | 313 +++++++++++++++++++++++++++++++++++ PY/app/哇哇APP.py | 263 +++++++++++++++++++++++++++++ PY/app/国外剧APP.py | 216 ++++++++++++++++++++++++ PY/app/奇迹APP.py | 216 ++++++++++++++++++++++++ PY/app/小苹果APP.py | 140 ++++++++++++++++ PY/app/恋鱼影视APP.py | 212 ++++++++++++++++++++++++ PY/app/悠悠APP.py | 220 ++++++++++++++++++++++++ PY/app/火车太顺APP.py | 301 +++++++++++++++++++++++++++++++++ PY/app/爱瓜TVAPP.py | 166 +++++++++++++++++++ PY/app/皮皮虾APP.py | 211 +++++++++++++++++++++++ PY/app/美帕APP.py | 93 +++++++++++ PY/app/胖虎APP.py | 215 ++++++++++++++++++++++++ PY/app/若惜追剧APP.py | 255 ++++++++++++++++++++++++++++ PY/app/视觉APP.py | 239 +++++++++++++++++++++++++++ PY/app/边缘影视APP.py | 340 ++++++++++++++++++++++++++++++++++++++ PY/app/零度影视APP.py | 224 +++++++++++++++++++++++++ PY/app/魔方影视APP.py | 209 +++++++++++++++++++++++ 25 files changed, 5440 insertions(+) create mode 100644 PY/app/APPV2.py create mode 100644 PY/app/CliCli动漫APP.py create mode 100644 PY/app/MiFunAPP.py create mode 100644 PY/app/hitvAPP.py create mode 100644 PY/app/三号动漫APP.py create mode 100644 PY/app/云端APP.py create mode 100644 PY/app/云速影视APP.py create mode 100644 PY/app/光速APP.py create mode 100644 PY/app/剧多短剧APP.py create mode 100644 PY/app/哇哇APP.py create mode 100644 PY/app/国外剧APP.py create mode 100644 PY/app/奇迹APP.py create mode 100644 PY/app/小苹果APP.py create mode 100644 PY/app/恋鱼影视APP.py create mode 100644 PY/app/悠悠APP.py create mode 100644 PY/app/火车太顺APP.py create mode 100644 PY/app/爱瓜TVAPP.py create mode 100644 PY/app/皮皮虾APP.py create mode 100644 PY/app/美帕APP.py create mode 100644 PY/app/胖虎APP.py create mode 100644 PY/app/若惜追剧APP.py create mode 100644 PY/app/视觉APP.py create mode 100644 PY/app/边缘影视APP.py create mode 100644 PY/app/零度影视APP.py create mode 100644 PY/app/魔方影视APP.py diff --git a/PY/app/APPV2.py b/PY/app/APPV2.py new file mode 100644 index 0000000..c3fe418 --- /dev/null +++ b/PY/app/APPV2.py @@ -0,0 +1,97 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +import sys +sys.path.append('..') +from base.spider import Spider + +class Spider(Spider): + + def init(self, extend=""): + ''' + example: + { + "key": "py_appV2", + "name": "xxx", + "type": 3, + "searchable": 1, + "quickSearch": 1, + "filterable": 1, + "api": "./py/APPV2.py", + "ext": "http://cmsyt.lyyytv.cn" + } + + ''' + self.host=extend + pass + + def getName(self): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + headers = { + 'User-Agent': 'okhttp/4.12.0', + } + + def homeContent(self, filter): + data = self.fetch(f"{self.host}//api.php/app/nav?token=",headers=self.headers).json() + keys = ["class", "area", "lang", "year", "letter", "by", "sort"] + filters = {} + classes = [] + for item in data['list']: + has_non_empty_field = False + jsontype_extend = item["type_extend"] + classes.append({"type_name": item["type_name"], "type_id": item["type_id"]}) + for key in keys: + if key in jsontype_extend and jsontype_extend[key].strip() != "": + has_non_empty_field = True + break + if has_non_empty_field: + filters[str(item["type_id"])] = [] + for dkey in jsontype_extend: + if dkey in keys and jsontype_extend[dkey].strip() != "": + values = jsontype_extend[dkey].split(",") + value_array = [{"n": value.strip(), "v": value.strip()} for value in values if + value.strip() != ""] + filters[str(item["type_id"])].append({"key": dkey, "name": dkey, "value": value_array}) + result = {} + result["class"] = classes + result["filters"] = filters + return result + + def homeVideoContent(self): + data=self.fetch(f"{self.host}/api.php/app/index_video?token=",headers=self.headers).json() + videos=[] + for item in data['list']:videos.extend(item['vlist']) + return {'list':videos} + + def categoryContent(self, tid, pg, filter, extend): + params = {'tid':tid,'class':extend.get('class',''),'area':extend.get('area',''),'lang':extend.get('lang',''),'year':extend.get('year',''),'limit':'18','pg':pg} + data=self.fetch(f"{self.host}/api.php/app/video",params=params,headers=self.headers).json() + return data + + def detailContent(self, ids): + data=self.fetch(f"{self.host}/api.php/app/video_detail?id={ids[0]}",headers=self.headers).json() + return {'list':[data['data']]} + + def searchContent(self, key, quick, pg="1"): + data=self.fetch(f"{self.host}/api.php/app/search?text={key}&pg={pg}",headers=self.headers).json() + videos=data['list'] + for item in data['list']: + item.pop('type', None) + return {'list':videos,'page':pg} + + def playerContent(self, flag, id, vipFlags): + return {'jx':1,'playUrl':'','parse': 1, 'url': id, 'header': self.headers} + + def localProxy(self, param): + pass + + diff --git a/PY/app/CliCli动漫APP.py b/PY/app/CliCli动漫APP.py new file mode 100644 index 0000000..e373780 --- /dev/null +++ b/PY/app/CliCli动漫APP.py @@ -0,0 +1,254 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +import base64 +import json +import sys +import time +from base64 import b64decode, b64encode +from Crypto.Cipher import AES, PKCS1_v1_5 +from Crypto.Hash import MD5 +from Crypto.PublicKey import RSA +from Crypto.Util.Padding import unpad, pad +sys.path.append('..') +from base.spider import Spider + + +class Spider(Spider): + + def init(self, extend=""): + self.did=self.getdid() + pass + + def getName(self): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + host='http://60.204.242.79:8091' + + def homeContent(self, filter): + res = self.fetch(f'{self.host}/app/channel?top-level=true', headers=self.getheaders()).text + data = self.getdata(res) + result = {} + classes = [] + filters = {} + sortsn = ['最新','最热','高分'] + for k in data['data']: + classes.append({ + 'type_name': k['name'], + 'type_id': k['id'] + }) + filters[k['id']] = [] + k['sorts']=['addtime','hits','gold'] + for key,value in k.items(): + if type(value) == list: + filters[k['id']].append({ + 'name': key, + 'key': key, + 'value': [{'v': x,'n': x if key !='sorts' else sortsn[i]} for i,x in enumerate(value) if x] + }) + result['class'] = classes + result['filters'] = filters + return result + + def homeVideoContent(self): + res=self.fetch(f'{self.host}/app/banners/0',headers=self.getheaders()).text + data=self.getdata(res) + videos=[] + for i in data['data']: + videos.append({ + 'vod_id': i['vid'], + 'vod_name': i['vname'], + 'vod_pic': i['img'], + 'vod_remarks': i['continu'] + }) + return {'list':videos} + + def categoryContent(self, tid, pg, filter, extend): + params={'channel':tid,'type':extend.get('types',''),'area':extend.get('areas',''),'year':extend.get('years',''),'sort':extend.get('sorts','addtime'),'limit':'30','page':pg} + data=self.fetch(f'{self.host}/app/video/list',params=params,headers=self.getheaders()).text + data=self.getdata(data) + videos=[] + for i in data['data']['items']: + videos.append({ + 'vod_id': i.get('id'), + 'vod_name': i.get('name'), + 'vod_pic': i.get('pic'), + 'vod_year': i.get('year'), + 'vod_remarks': i.get('continu') + }) + result = {} + result['list'] = videos + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + data=self.fetch(f'{self.host}/app/video/detail?id={ids[0]}',headers=self.getheaders()).text + data=self.getdata(data) + v=data['data'] + vod = { + 'type_name': v.get('type'), + 'vod_year': v.get('year'), + 'vod_area': v.get('area'), + 'vod_remarks': v.get('continu'), + 'vod_actor': v.get('actor'), + 'vod_director': v.get('director'), + 'vod_content': v.get('content'), + 'vod_play_from': '', + 'vod_play_url': '' + } + parts,names = [],[] + for i in v['parts']: + names.append(i['play_zh']) + p=[] + for j,x in enumerate(i['part']): + params={'id':ids[0],'play':i['play'],'part':x} + p.append(f'{x}${self.e64(json.dumps(params))}') + parts.append('#'.join(p)) + vod['vod_play_from'] = '$$$'.join(names) + vod['vod_play_url'] = '$$$'.join(parts) + return {'list':[vod]} + + def searchContent(self, key, quick, pg="1"): + params={'key':key,'limit':'25','page':pg} + data=self.fetch(f'{self.host}/app/video/search',params=params,headers=self.getheaders()).text + data=self.getdata(data) + videos = [] + for i in data['data']['items']: + videos.append({ + 'vod_id': i.get('id'), + 'vod_name': i.get('name'), + 'vod_pic': i.get('pic'), + 'vod_year': i.get('year'), + 'vod_remarks': i.get('continu') + }) + return {'list':videos,'page':pg} + + def playerContent(self, flag, id, vipFlags): + params= json.loads(self.d64(id)) + data=self.fetch(f'{self.host}/app/video/play',params=params,headers=self.getheaders()).text + data=self.getdata(data) + urls=[] + for i in data['data']: + if i.get('url'):urls.extend([i['resolution'],i['url']]) + return {'parse': 0, 'url': urls, 'header': {'User-Agent': 'Dart/3.6 (dart:io)'}} + + def liveContent(self, url): + pass + + def localProxy(self, param): + pass + + def getheaders(self): + t=str(int(time.time() * 1000)) + stinf=f"3.0.0.2-{t}-Android-1.0.4.5-{self.did}" + authentication=self.aes_encrypt(self.e64(stinf)) + headers = { + 'User-Agent': 'Dart/3.6 (dart:io)', + 'x-version': '2020-09-17', + 'appid': '4150439554430614', + 'ts': t, + 'authentication': authentication, + 'content-type': 'application/json; charset=utf-8', + } + return headers + + def aes_encrypt(self, text): + key = b'ziISjqkXPsGUMRNGyWigxDGtJbfTdcGv' + iv = b'WonrnVkxeIxDcFbv' + cipher = AES.new(key, AES.MODE_CBC, iv) + ct_bytes = cipher.encrypt(pad(text.encode("utf-8"), AES.block_size)) + ct = b64encode(ct_bytes).decode("utf-8") + return ct + + def aes_decrypt(self, key,text): + iv=key[::-1].encode("utf-8") + key=key.encode("utf-8") + cipher = AES.new(key, AES.MODE_CBC, iv) + pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size) + return json.loads(pt.decode("utf-8")) + + def rsa_decrypt(self, encrypted_data): + try: + private_key_string = '''-----BEGIN RSA PRIVATE KEY----- + MIIEpAIBAAKCAQEA5xpfniKIMYdjTytUBu5rsLbMtcCRW9B9DB78QEdf4wW5jO8r + Mw7j+/mYk3ghi0xrxpjtHm1R2KgNT1b0akJCExTH7gBVcjVywpmXdNXbcuCGfVCK + S6vYfMypmj5lNBgalCHe5AVc0ghhP3FG5j8Q5B7q00+tk4nT9nFsTmTeNcAKSH9h + aM6a0fbiJ3eXbxEr2o8raAjck10act35t/MIUOkcrQjHx5E9Yvqgs3qbq4yDakaG + 4qfMAV4DAkkmdZ8N3fdEQ+rFJ67Spd4zzowj81+YO9wMUP2hNgfXmLOGLS5Lyi+x + vrwwWZXAIRUkhdQEAYQlhGs8wV9P4bJnTzplewIDAQABAoIBAEnRzNUwZpybiIdT + acXFBrUtzvoHhubzE955T04g/mn//CMeiogGq6BjO+9vIhfi01Jequ9bMBeqpoW/ + WtdOTtjVfH9zr9eJZxzt/skdPrnVKmCBB4vgWoiSv2I7qAwZ3vOOVioz5FBayOWB + A4qsfnK/xXa2LtW/4usHk/b+lVRJZhHl3eKio2CnVBrgRb2DTx1GAwpvaRXp0oHm + LXDEtngxN4/rh2irPKgaG/lgrCBISKUHtwtgytcpltsHMASMXIKAjZjNgCA98fA3 + te96U58wGHzQBQ5XtwTf0PiFEfJ7yOhgNRgCtiwsjGOhJFJFiiXYKzTef1GnVxPa + wuPc0TECgYEA+KCts3ArkWLqWbi4bVDpekP71geEnQIklSAk3RRZ0eiC1pmmkuTh + +q/4jOfoQHGuYCc8GvJqxQ8Y+aspPptbsAeRMSVovjQUvpRMqD0SWT8o3W2xGfqd + 0W4p14CIF7oXjMqQVeY468AYzxUdNsaulrp9Wnpa5njzE5D5WGDu0IcCgYEA7fSq + kvz1oXjlljlskBwJ8gDB8j53PhuqV6Ori71G/qIGpYuOVjHSfPD/04a9T3M9olpk + vlLOLn7GS7xa4pjugmp0EDdxBIJJtTHbbi4NL4ZoYg+vHkiemkjGLis4x5qRKjg6 + jNUEhnpksm68IUMSyO2toasfR0nVUmkb+ylKhG0CgYEAqNDZAJSyUHZcb21YdIlS + 7rzIe2wBZGZ3FnaL8T0HO9rnM/WCQA1/Tys61doFPfSylQEu85EUZBc7OxM33xW3 + 7M9Gi5s+Ap/0Ue76GeXV1plnEuqPLPeZPwHREU1pmsq1gNhtppW6ooB9l+ZbPr0r + AJdB1DRuEj2ftvJiC9tNbHMCgYEAvHaliply6hrYq6x7gX/TmKpk8bnrs3Mx7Qui + WKDm09H8Na1cZIQ9U9uEo0H6OizpyeaSF/N5fXXHFEDwMrwxW3V4y0c96fZO7oW4 + Z4FtzBBGKDSH3BJkG4o7/GEbLWwMQUYbiWNFnETf8DqoIif/fshQVtUzhsDBhe3d + zYUckdkCgYAJlTYhJz0qXcO8a5KsQ20/hEGRtOcq+mfPOdGYBOv6LB2ThuDKunbY + WsmAvqSo1qoJONnhQVMSpzKWEjCYV6hcifV9aeFofD4kNmG1gWC18QIYfrihLyOU + E4GDW7QN8HO2YiQpopGP/muKsIlCmxKP6DasgCCO36xs87Wi8gu1DA== + -----END RSA PRIVATE KEY-----''' + private_key = RSA.import_key(private_key_string) + cipher = PKCS1_v1_5.new(private_key) + encrypted_bytes = base64.b64decode(encrypted_data) + decrypted_bytes = cipher.decrypt(encrypted_bytes, None) + return decrypted_bytes.decode('utf-8') + except: + return "" + + def getdata(self, data): + ds=data.split('.') + key=self.rsa_decrypt(ds[0]) + result=self.aes_decrypt(key,ds[1]) + return result + + def getdid(self): + did=self.getCache('did') + if not did: + t = str(int(time.time())) + did = self.md5(t) + self.setCache('did', did) + return did + + def e64(self, text): + try: + text_bytes = text.encode('utf-8') + encoded_bytes = b64encode(text_bytes) + return encoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64编码错误: {str(e)}") + return "" + + def d64(self, encoded_text): + try: + encoded_bytes = encoded_text.encode('utf-8') + decoded_bytes = b64decode(encoded_bytes) + return decoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64解码错误: {str(e)}") + return "" + + def md5(self, text): + h = MD5.new() + h.update(text.encode('utf-8')) + return h.hexdigest() + diff --git a/PY/app/MiFunAPP.py b/PY/app/MiFunAPP.py new file mode 100644 index 0000000..be827d8 --- /dev/null +++ b/PY/app/MiFunAPP.py @@ -0,0 +1,249 @@ +import re +import sys +import threading +import requests +from Crypto.Hash import MD5 +sys.path.append("..") +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad +from urllib.parse import quote, urlparse +from base64 import b64encode, b64decode +import json +import time +from base.spider import Spider + + +class Spider(Spider): + + def init(self, extend=""): + self.host = self.gethost() + self.did=self.getdid() + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def action(self, action): + pass + + def destroy(self): + pass + + def homeContent(self, filter): + data = self.getdata("/api.php/getappapi.index/initV119") + dy = {"class": "类型", "area": "地区", "lang": "语言", "year": "年份", "letter": "字母", "by": "排序", + "sort": "排序"} + filters = {} + classes = [] + json_data = data["type_list"] + homedata = data["banner_list"][8:] + for item in json_data: + if item["type_name"] == "全部": + continue + has_non_empty_field = False + jsontype_extend = json.loads(item["type_extend"]) + homedata.extend(item["recommend_list"]) + jsontype_extend["sort"] = "最新,最热,最赞" + classes.append({"type_name": item["type_name"], "type_id": item["type_id"]}) + for key in dy: + if key in jsontype_extend and jsontype_extend[key].strip() != "": + has_non_empty_field = True + break + if has_non_empty_field: + filters[str(item["type_id"])] = [] + for dkey in jsontype_extend: + if dkey in dy and jsontype_extend[dkey].strip() != "": + values = jsontype_extend[dkey].split(",") + value_array = [{"n": value.strip(), "v": value.strip()} for value in values if + value.strip() != ""] + filters[str(item["type_id"])].append({"key": dkey, "name": dy[dkey], "value": value_array}) + result = {} + result["class"] = classes + result["filters"] = filters + result["list"] = homedata[1:] + return result + + def homeVideoContent(self): + pass + + def categoryContent(self, tid, pg, filter, extend): + body = {"area": extend.get('area', '全部'), "year": extend.get('year', '全部'), "type_id": tid, "page": pg, + "sort": extend.get('sort', '最新'), "lang": extend.get('lang', '全部'), + "class": extend.get('class', '全部')} + result = {} + data = self.getdata("/api.php/getappapi.index/typeFilterVodList", body) + result["list"] = data["recommend_list"] + result["page"] = pg + result["pagecount"] = 9999 + result["limit"] = 90 + result["total"] = 999999 + return result + + def detailContent(self, ids): + body = f"vod_id={ids[0]}" + data = self.getdata("/api.php/getappapi.index/vodDetail", body) + vod = data["vod"] + play = [] + names = [] + for itt in data["vod_play_list"]: + a = [] + names.append(itt["player_info"]["show"]) + for it in itt['urls']: + it['user_agent'] = itt["player_info"].get("user_agent") + it["parse"] = itt["player_info"].get("parse") + a.append(f"{it['name']}${self.e64(json.dumps(it))}") + play.append("#".join(a)) + vod["vod_play_from"] = "$$$".join(names) + vod["vod_play_url"] = "$$$".join(play) + result = {"list": [vod]} + return result + + def searchContent(self, key, quick, pg="1"): + body = f"keywords={key}&type_id=0&page={pg}" + data = self.getdata("/api.php/getappapi.index/searchList", body) + result = {"list": data["search_list"], "page": pg} + return result + + def playerContent(self, flag, id, vipFlags): + ids = json.loads(self.d64(id)) + h = {"User-Agent": (ids['user_agent'] or "okhttp/3.14.9")} + try: + if re.search(r'url=', ids['parse_api_url']): + data = self.fetch(ids['parse_api_url'], headers=h, timeout=10).json() + url = data.get('url') or data['data'].get('url') + else: + body = f"parse_api={ids.get('parse') or ids['parse_api_url'].replace(ids['url'], '')}&url={quote(self.aes(ids['url'], True))}&token={ids.get('token')}" + b = self.getdata("/api.php/getappapi.index/vodParse", body)['json'] + url = json.loads(b)['url'] + if 'error' in url: raise ValueError(f"解析失败: {url}") + p = 0 + except Exception as e: + print('错误信息:', e) + url, p = ids['url'], 1 + + if re.search(r'\.jpg|\.png|\.jpeg', url): + url = self.Mproxy(url) + result = {} + result["parse"] = p + result["url"] = url + result["header"] = h + return result + + def localProxy(self, param): + return self.Mlocal(param) + + def gethost(self): + headers = { + 'User-Agent': 'okhttp/3.14.9' + } + response = self.fetch('https://miget-1313189639.cos.ap-guangzhou.myqcloud.com/mifun.txt',headers=headers).text + return self.host_late(response.split('\n')) + + 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: + url = url.strip() + 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 getdid(self): + did=self.getCache('did') + if not did: + t = str(int(time.time())) + did = self.md5(t) + self.setCache('did', did) + return did + + def aes(self, text, b=None): + key = b"GETMIFUNGEIMIFUN" + cipher = AES.new(key, AES.MODE_CBC, key) + if b: + ct_bytes = cipher.encrypt(pad(text.encode("utf-8"), AES.block_size)) + ct = b64encode(ct_bytes).decode("utf-8") + return ct + else: + pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size) + return pt.decode("utf-8") + + def header(self): + t = str(int(time.time())) + header = {"Referer": self.host, + "User-Agent": "okhttp/3.14.9", "app-version-code": "516", "app-ui-mode": "light", + "app-api-verify-time": t, "app-user-device-id": self.did, + "app-api-verify-sign": self.aes(t, True), + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"} + return header + + def getdata(self, path, data=None): + vdata = self.post(f"{self.host}{path}", headers=self.header(), data=data, timeout=10).json()['data'] + data1 = self.aes(vdata) + return json.loads(data1) + + def Mproxy(self, url): + return f"{self.getProxyUrl()}&url={self.e64(url)}&type=m3u8" + + def Mlocal(self, param, header=None): + url = self.d64(param["url"]) + ydata = self.fetch(url, headers=header, allow_redirects=False) + data = ydata.content.decode('utf-8') + if ydata.headers.get('Location'): + url = ydata.headers['Location'] + data = self.fetch(url, headers=header).content.decode('utf-8') + parsed_url = urlparse(url) + durl = parsed_url.scheme + "://" + parsed_url.netloc + lines = data.strip().split('\n') + for index, string in enumerate(lines): + if '#EXT' not in string and 'http' not in string: + last_slash_index = string.rfind('/') + lpath = string[:last_slash_index + 1] + lines[index] = durl + ('' if lpath.startswith('/') else '/') + lpath + data = '\n'.join(lines) + return [200, "application/vnd.apple.mpegur", data] + + def e64(self, text): + try: + text_bytes = text.encode('utf-8') + encoded_bytes = b64encode(text_bytes) + return encoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64编码错误: {str(e)}") + return "" + + def d64(self, encoded_text): + try: + encoded_bytes = encoded_text.encode('utf-8') + decoded_bytes = b64decode(encoded_bytes) + return decoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64解码错误: {str(e)}") + return "" + + def md5(self, text): + h = MD5.new() + h.update(text.encode('utf-8')) + return h.hexdigest() + diff --git a/PY/app/hitvAPP.py b/PY/app/hitvAPP.py new file mode 100644 index 0000000..ae501ac --- /dev/null +++ b/PY/app/hitvAPP.py @@ -0,0 +1,146 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +import sys +sys.path.append('..') +from base.spider import Spider +import requests + + +class Spider(Spider): + + def init(self, extend=""): + pass + + def getName(self): + return "hitv" + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + def homeContent(self, filter): + result = {} + cateManual = { + # "直播": "live", + '排行榜': 'rank', + "电影": "1", + "剧集": "2", + "综艺": "3", + "动画": "4", + "短片": "5" + } + classes = [] + for k in cateManual: + classes.append({ + 'type_name': k, + 'type_id': cateManual[k] + }) + result['class'] = classes + return result + + host = "https://wys.upfuhn.com" + headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " + "Chrome/80.0.3987.149 Safari/537.36" + } + + def list(self, list): + videos = [] + for it in list: + videos.append({ + "vod_id": it['video_site_id'], + "vod_name": it['video_name'], + "vod_pic": it['video_horizontal_url'] or it['video_vertical_url'], + "vod_remarks": it['newest_series_num'], + "vod_year": it['years'], + }) + return videos + + def homeVideoContent(self): + url = f'{self.host}/v1/ys_video_sites/hot?t=1' + data = requests.get(url, headers=self.headers).json() + videos = self.list(data['data']['data']) + result = {'list': videos} + return result + + def categoryContent(self, tid, pg, filter, extend): + path = f'/v1/ys_video_sites?t={tid}&s_t=0&a&y&o=0&ps=21&pn={pg}' + rank = False + if tid == 'rank': + if pg == 1: + path = f'/v1/ys_video_sites/ranking' + rank = True + else: + path = '' + # elif tid == 'live' and pg == 1: + # path = f'/v1/ys_live_tvs' + videos = [] + result = {} + try: + data = requests.get(self.host + path, headers=self.headers).json() + if rank: + for video in data['data']: + videos.extend(data['data'][video]) + else: + videos = data['data']['data'] + result = {} + result['list'] = self.list(videos) + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + except: + result['list'] = [] + return result + + def detailContent(self, ids): + tid = ids[0] + url = f'{self.host}/v1/ys_video_series/by_vid/{tid}' + data = requests.get(url, headers=self.headers).json() + data1 = data['data']['ys_video_site'] + urls = [] + for it in data['data']['data']: + urls.append(it['series_num'] + '$' + it['video_url']) + vod = { + 'vod_name': data1['video_name'], + 'type_name': data1['tag'], + 'vod_year': data1['years'], + 'vod_area': data1['area'], + 'vod_director': data1['main_actor'], + 'vod_content': data1['video_desc'], + 'vod_play_from': '嗷呜在线', + 'vod_play_url': '#'.join(urls), + } + result = { + 'list': [ + vod + ] + } + return result + + def searchContent(self, key, quick, pg=1): + url = f'{self.host}/v1/ys_video_sites/search?s={key}&o=0&ps=200&pn={pg}' + data = requests.get(url, headers=self.headers).json() + videos = data['data']['video_sites'] + if data['data']['first_video_series'] is not None: + videos = [data['data']['first_video_series']] + videos + result = {} + result['list'] = self.list(videos) + result['page'] = pg + return result + + def playerContent(self, flag, id, vipFlags): + result = { + 'url': id, + 'parse': 0, + 'header': self.headers + } + return result + + def localProxy(self, param): + pass diff --git a/PY/app/三号动漫APP.py b/PY/app/三号动漫APP.py new file mode 100644 index 0000000..ded29cb --- /dev/null +++ b/PY/app/三号动漫APP.py @@ -0,0 +1,175 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +import json +import sys +from base64 import b64decode, b64encode +from Crypto.Cipher import AES +from Crypto.Util.Padding import unpad, pad +from concurrent.futures import ThreadPoolExecutor +sys.path.append('..') +from base.spider import Spider + + +class Spider(Spider): + + def init(self, extend=""): + self.host = self.gethost() + self.hkey,self.playerinfos=self.getinfo() + pass + + def getName(self): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + headers = { + 'User-Agent': 'Dalvik/1.4.0 (Linux; U; Android 11; Redmi Build/M2012K10C)', + 'version': '1.4.0' + } + + keys=['rectangleadsadxa','aassddwwxxllsx1x'] + + def homeContent(self, filter): + cdata=self.getdata('/api.php/v1.home/types') + result = {} + classes = [] + filters = {} + for i in cdata['data']['types'][1:]: + classes.append({ + 'type_id': i['type_id'], + 'type_name': i['type_name'] + }) + with ThreadPoolExecutor(max_workers=len(classes)) as executor: + futures = [executor.submit(self.getf, i['type_id']) + for i in classes] + for future in futures: + try: + type_id, filter_data = future.result() + if len(filter_data):filters[type_id] = filter_data + except Exception as e: + print(f'处理筛选数据失败: {e}') + result['class'] = classes + result['filters'] = filters + return result + + def homeVideoContent(self): + data=self.getdata('/api.php/v1.home/data?type_id=26') + return {'list':data['data']['banners']} + + def categoryContent(self, tid, pg, filter, extend): + json_data = { + 'area': extend.get('area', '全部地区'), + 'lang': extend.get('lang', '全部语言'), + 'rank': extend.get('rank', '最新'), + 'type': extend.get('type', '全部类型'), + 'type_id': int(tid), + 'year': extend.get('year', '全部年代'), + } + data=self.getdata(f'/api.php/v1.classify/content?page={pg}',method=False,json_data=json_data) + result = {} + result['list'] = data['data']['video_list'] + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + data=self.getdata(f'/api.php/v1.player/details?vod_id={ids[0]}') + vod = data['data']['detail'] + plist,names = [],[] + for i in vod['play_url_list']: + names.append(i['show']) + plist.append('#'.join([f"{j['name']}${i['from']}@@{j['url']}" for j in i['urls']])) + vod.pop('play_url_list', None) + vod.update({'vod_play_from': '$$$'.join(names), 'vod_play_url': '$$$'.join(plist)}) + return {'list':[vod]} + + def searchContent(self, key, quick, pg="1"): + data=self.getdata(f'/api.php/v1.search/data?wd={key}&type_id=0&page={pg}') + return {'list': data['data']['search_data'], 'page': pg} + + def playerContent(self, flag, id, vipFlags): + ids=id.split('@@') + try: + body={'parse':self.getparse(ids[0]),'url':ids[-1],'matching':''} + data=self.getdata(f'/shark/api.php?action=parsevod',method=False,data=body) + url=data.get('url') or data['data'].get('url') + if not url: + raise ValueError("解析失败") + p=0 + except: + p,url = 1,ids[-1] + return {'parse': p, 'url': url, 'header': {'User-Agent':'aliplayer(appv=1.4.0&av=6.16.0&av2=6.16.0_40316683&os=android&ov=11&dm=M2012K10C)'}} + + def localProxy(self, param): + pass + + def getparse(self,id): + for i in self.playerinfos: + if i['playername']==id: + j= i['playerjiekou'] + return self.aes(j,self.hkey) + return '' + + def gethost(self): + headers = { + 'User-Agent': 'okhttp/4.11.0', + 'Connection': 'Keep-Alive' + } + response = self.fetch('https://shopv1.oss-accelerate.aliyuncs.com/api.txt', headers=headers).text + host=json.loads(self.aes(response.strip(),self.keys[0]))[0] + return host + + def getinfo(self): + data=self.post(f'{self.host}/shark/api.php?action=configs',headers=self.headers,data={'username':'','token':''}).text + datas=json.loads(self.aes(data)) + hkey = datas['config']['hulue'].split('&')[0] + playerinfos = datas['playerinfos'] + return hkey,playerinfos + + def getdata(self,parh,method=True,data=None,json_data=None): + url = f'{self.host}{parh}' + if method: + response = self.fetch(url, headers=self.headers).text + else: + response = self.post(url, headers=self.headers, data=data, json=json_data).text + return json.loads(self.aes(response)) + + def getf(self, type_id): + try: + fdata = self.getdata(f'/api.php/v1.classify/types?type_id={type_id}') + filter_list = [] + for key, value in fdata['data'].items(): + if len(value): + filter_list.append({ + 'key': key.split('_')[0], + 'name': key.split('_')[0], + 'value': [{'n': j['type_name'], 'v': j['type_name']} for j in value if j.get('type_name')] + }) + return type_id, filter_list + except Exception as e: + print(f'获取type_id={type_id}的筛选数据失败: {e}') + return type_id, [] + + def aes(self, word,key=None, b=True): + if not key:key=self.keys[1] + cipher = AES.new(key.encode(), AES.MODE_ECB) + word = word.encode('utf-8-sig').decode('ascii', errors='ignore') + if b: + word = b64decode(word) + decrypted = cipher.decrypt(word) + word = unpad(decrypted, AES.block_size).decode() + else: + padded = pad(word.encode(), AES.block_size) + encrypted = cipher.encrypt(padded) + word = b64encode(encrypted).decode() + return word + diff --git a/PY/app/云端APP.py b/PY/app/云端APP.py new file mode 100644 index 0000000..596a860 --- /dev/null +++ b/PY/app/云端APP.py @@ -0,0 +1,245 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +import re +import sys +import threading +import requests +from Crypto.Hash import MD5 +sys.path.append("..") +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad +from urllib.parse import quote, urlparse +from base64 import b64encode, b64decode +import json +import time +from base.spider import Spider + + +class Spider(Spider): + + def init(self, extend=""): + self.host = self.gethost() + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def action(self, action): + pass + + def destroy(self): + pass + + def homeContent(self, filter): + data = self.getdata("/api.php/getappapi.index/initV119") + dy = {"class": "类型", "area": "地区", "lang": "语言", "year": "年份", "letter": "字母", "by": "排序", + "sort": "排序"} + filters = {} + classes = [] + json_data = data["type_list"] + homedata = data["banner_list"][8:] + for item in json_data: + if item["type_name"] == "全部": + continue + has_non_empty_field = False + jsontype_extend = json.loads(item["type_extend"]) + homedata.extend(item["recommend_list"]) + jsontype_extend["sort"] = "最新,最热,最赞" + classes.append({"type_name": item["type_name"], "type_id": item["type_id"]}) + for key in dy: + if key in jsontype_extend and jsontype_extend[key].strip() != "": + has_non_empty_field = True + break + if has_non_empty_field: + filters[str(item["type_id"])] = [] + for dkey in jsontype_extend: + if dkey in dy and jsontype_extend[dkey].strip() != "": + values = jsontype_extend[dkey].split(",") + value_array = [{"n": value.strip(), "v": value.strip()} for value in values if + value.strip() != ""] + filters[str(item["type_id"])].append({"key": dkey, "name": dy[dkey], "value": value_array}) + result = {} + result["class"] = classes + result["filters"] = filters + result["list"] = homedata[1:] + return result + + def homeVideoContent(self): + pass + + def categoryContent(self, tid, pg, filter, extend): + body = {"area": extend.get('area', '全部'), "year": extend.get('year', '全部'), "type_id": tid, "page": pg, + "sort": extend.get('sort', '最新'), "lang": extend.get('lang', '全部'), + "class": extend.get('class', '全部')} + result = {} + data = self.getdata("/api.php/getappapi.index/typeFilterVodList", body) + result["list"] = data["recommend_list"] + result["page"] = pg + result["pagecount"] = 9999 + result["limit"] = 90 + result["total"] = 999999 + return result + + def detailContent(self, ids): + body = f"vod_id={ids[0]}" + data = self.getdata("/api.php/getappapi.index/vodDetail", body) + vod = data["vod"] + play = [] + names = [] + for itt in data["vod_play_list"]: + a = [] + names.append(itt["player_info"]["show"]) + for it in itt['urls']: + it['user_agent'] = itt["player_info"].get("user_agent") + it["parse"] = itt["player_info"].get("parse") + a.append(f"{it['name']}${self.e64(json.dumps(it))}") + play.append("#".join(a)) + vod["vod_play_from"] = "$$$".join(names) + vod["vod_play_url"] = "$$$".join(play) + result = {"list": [vod]} + return result + + def searchContent(self, key, quick, pg="1"): + body = f"keywords={key}&type_id=0&page={pg}" + data = self.getdata("/api.php/getappapi.index/searchList", body) + result = {"list": data["search_list"], "page": pg} + return result + + def playerContent(self, flag, id, vipFlags): + ids = json.loads(self.d64(id)) + h = {"User-Agent": (ids['user_agent'] or "okhttp/3.14.9")} + try: + if re.search(r'url=', ids['parse_api_url']): + data = self.fetch(ids['parse_api_url'], headers=h, timeout=10).json() + url = data.get('url') or data['data'].get('url') + else: + body = f"parse_api={ids.get('parse') or ids['parse_api_url'].replace(ids['url'], '')}&url={quote(self.aes(ids['url'], True))}&token={ids.get('token')}" + b = self.getdata("/api.php/getappapi.index/vodParse", body)['json'] + url = json.loads(b)['url'] + if 'error' in url: raise ValueError(f"解析失败: {url}") + p = 0 + except Exception as e: + print('错误信息:', e) + url, p = ids['url'], 1 + + if re.search(r'\.jpg|\.png|\.jpeg', url): + url = self.Mproxy(url) + result = {} + result["parse"] = p + result["url"] = url + result["header"] = h + return result + + def localProxy(self, param): + return self.Mlocal(param) + + def gethost(self): + headers = { + 'User-Agent': 'okhttp/3.14.9' + } + response = self.fetch('https://ydysdynamicdomainname.68.gy:10678/c9m2js298x82h6/l9m8bx23j2o2p9q/dynamicdomainname.txt', + headers=headers).text + return self.host_late(response.split('\n')) + + 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 aes(self, text, b=None): + key = b"k9o3p2c8b7m3z0o8" + cipher = AES.new(key, AES.MODE_CBC, key) + if b: + ct_bytes = cipher.encrypt(pad(text.encode("utf-8"), AES.block_size)) + ct = b64encode(ct_bytes).decode("utf-8") + return ct + else: + pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size) + return pt.decode("utf-8") + + def header(self): + t = str(int(time.time())) + header = {"Referer": self.host, + "User-Agent": "okhttp/3.14.9", "app-version-code": "140", "app-ui-mode": "light", + "app-api-verify-time": t, "app-user-device-id": self.md5(t), + "app-api-verify-sign": self.aes(t, True), + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"} + return header + + def getdata(self, path, data=None): + vdata = self.post(f"{self.host}{path}", headers=self.header(), data=data, timeout=10).json()['data'] + data1 = self.aes(vdata) + return json.loads(data1) + + def Mproxy(self, url): + return f"{self.getProxyUrl()}&url={self.e64(url)}&type=m3u8" + + def Mlocal(self, param, header=None): + url = self.d64(param["url"]) + ydata = self.fetch(url, headers=header, allow_redirects=False) + data = ydata.content.decode('utf-8') + if ydata.headers.get('Location'): + url = ydata.headers['Location'] + data = self.fetch(url, headers=header).content.decode('utf-8') + parsed_url = urlparse(url) + durl = parsed_url.scheme + "://" + parsed_url.netloc + lines = data.strip().split('\n') + for index, string in enumerate(lines): + if '#EXT' not in string and 'http' not in string: + last_slash_index = string.rfind('/') + lpath = string[:last_slash_index + 1] + lines[index] = durl + ('' if lpath.startswith('/') else '/') + lpath + data = '\n'.join(lines) + return [200, "application/vnd.apple.mpegur", data] + + def e64(self, text): + try: + text_bytes = text.encode('utf-8') + encoded_bytes = b64encode(text_bytes) + return encoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64编码错误: {str(e)}") + return "" + + def d64(self, encoded_text): + try: + encoded_bytes = encoded_text.encode('utf-8') + decoded_bytes = b64decode(encoded_bytes) + return decoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64解码错误: {str(e)}") + return "" + + def md5(self, text): + h = MD5.new() + h.update(text.encode('utf-8')) + return h.hexdigest() diff --git a/PY/app/云速影视APP.py b/PY/app/云速影视APP.py new file mode 100644 index 0000000..a87909f --- /dev/null +++ b/PY/app/云速影视APP.py @@ -0,0 +1,219 @@ +import re +import sys +from Crypto.Hash import MD5 +sys.path.append("..") +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad +from urllib.parse import quote, urlparse +from base64 import b64encode, b64decode +import json +import time +from base.spider import Spider + + +class Spider(Spider): + + def init(self, extend=""): + self.host = self.gethost() + self.did=self.getdid() + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def action(self, action): + pass + + def destroy(self): + pass + + def homeContent(self, filter): + data = self.getdata("/api.php/getappapi.index/initV119") + dy = {"class": "类型", "area": "地区", "lang": "语言", "year": "年份", "letter": "字母", "by": "排序", + "sort": "排序"} + filters = {} + classes = [] + json_data = data["type_list"] + homedata = data["banner_list"][8:] + for item in json_data: + if item["type_name"] == "全部": + continue + has_non_empty_field = False + jsontype_extend = json.loads(item["type_extend"]) + homedata.extend(item["recommend_list"]) + jsontype_extend["sort"] = "最新,最热,最赞" + classes.append({"type_name": item["type_name"], "type_id": item["type_id"]}) + for key in dy: + if key in jsontype_extend and jsontype_extend[key].strip() != "": + has_non_empty_field = True + break + if has_non_empty_field: + filters[str(item["type_id"])] = [] + for dkey in jsontype_extend: + if dkey in dy and jsontype_extend[dkey].strip() != "": + values = jsontype_extend[dkey].split(",") + value_array = [{"n": value.strip(), "v": value.strip()} for value in values if + value.strip() != ""] + filters[str(item["type_id"])].append({"key": dkey, "name": dy[dkey], "value": value_array}) + result = {} + result["class"] = classes + result["filters"] = filters + result["list"] = homedata[1:] + return result + + def homeVideoContent(self): + pass + + def categoryContent(self, tid, pg, filter, extend): + body = {"area": extend.get('area', '全部'), "year": extend.get('year', '全部'), "type_id": tid, "page": pg, + "sort": extend.get('sort', '最新'), "lang": extend.get('lang', '全部'), + "class": extend.get('class', '全部')} + result = {} + data = self.getdata("/api.php/getappapi.index/typeFilterVodList", body) + result["list"] = data["recommend_list"] + result["page"] = pg + result["pagecount"] = 9999 + result["limit"] = 90 + result["total"] = 999999 + return result + + def detailContent(self, ids): + body = f"vod_id={ids[0]}" + data = self.getdata("/api.php/getappapi.index/vodDetail", body) + vod = data["vod"] + play = [] + names = [] + for itt in data["vod_play_list"]: + a = [] + names.append(itt["player_info"]["show"]) + for it in itt['urls']: + it['user_agent'] = itt["player_info"].get("user_agent") + it["parse"] = itt["player_info"].get("parse") + a.append(f"{it['name']}${self.e64(json.dumps(it))}") + play.append("#".join(a)) + vod["vod_play_from"] = "$$$".join(names) + vod["vod_play_url"] = "$$$".join(play) + result = {"list": [vod]} + return result + + def searchContent(self, key, quick, pg="1"): + body = f"keywords={key}&type_id=0&page={pg}" + data = self.getdata("/api.php/getappapi.index/searchList", body) + result = {"list": data["search_list"], "page": pg} + return result + + def playerContent(self, flag, id, vipFlags): + ids = json.loads(self.d64(id)) + h = {"User-Agent": (ids['user_agent'] or "okhttp/3.14.9")} + try: + if re.search(r'url=', ids['parse_api_url']): + data = self.fetch(ids['parse_api_url'], headers=h, timeout=10).json() + url = data.get('url') or data['data'].get('url') + else: + body = f"parse_api={ids.get('parse') or ids['parse_api_url'].replace(ids['url'], '')}&url={quote(self.aes(ids['url'], True))}&token={ids.get('token')}" + b = self.getdata("/api.php/getappapi.index/vodParse", body)['json'] + url = json.loads(b)['url'] + if 'error' in url: raise ValueError(f"解析失败: {url}") + p = 0 + except Exception as e: + print('错误信息:', e) + url, p = ids['url'], 1 + + if re.search(r'\.jpg|\.png|\.jpeg', url): + url = self.Mproxy(url) + result = {} + result["parse"] = p + result["url"] = url + result["header"] = h + return result + + def localProxy(self, param): + return self.Mlocal(param) + + def gethost(self): + headers = { + 'User-Agent': 'okhttp/3.14.9' + } + response = self.fetch('https://jingyu-1312635929.cos.ap-nanjing.myqcloud.com/1.json',headers=headers).text + return response.strip() + + def getdid(self): + did=self.getCache('did') + if not did: + t = str(int(time.time())) + did = self.md5(t) + self.setCache('did', did) + return did + + def aes(self, text, b=None): + key = b"4d83b87c4c5ea111" + cipher = AES.new(key, AES.MODE_CBC, key) + if b: + ct_bytes = cipher.encrypt(pad(text.encode("utf-8"), AES.block_size)) + ct = b64encode(ct_bytes).decode("utf-8") + return ct + else: + pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size) + return pt.decode("utf-8") + + def header(self): + t = str(int(time.time())) + header = {"Referer": self.host, + "User-Agent": "okhttp/3.14.9", "app-version-code": "300", "app-ui-mode": "light", + "app-api-verify-time": t, "app-user-device-id": self.did, + "app-api-verify-sign": self.aes(t, True), + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"} + return header + + def getdata(self, path, data=None): + vdata = self.post(f"{self.host}{path}", headers=self.header(), data=data, timeout=10).json()['data'] + data1 = self.aes(vdata) + return json.loads(data1) + + def Mproxy(self, url): + return f"{self.getProxyUrl()}&url={self.e64(url)}&type=m3u8" + + def Mlocal(self, param, header=None): + url = self.d64(param["url"]) + ydata = self.fetch(url, headers=header, allow_redirects=False) + data = ydata.content.decode('utf-8') + if ydata.headers.get('Location'): + url = ydata.headers['Location'] + data = self.fetch(url, headers=header).content.decode('utf-8') + parsed_url = urlparse(url) + durl = parsed_url.scheme + "://" + parsed_url.netloc + lines = data.strip().split('\n') + for index, string in enumerate(lines): + if '#EXT' not in string and 'http' not in string: + last_slash_index = string.rfind('/') + lpath = string[:last_slash_index + 1] + lines[index] = durl + ('' if lpath.startswith('/') else '/') + lpath + data = '\n'.join(lines) + return [200, "application/vnd.apple.mpegur", data] + + def e64(self, text): + try: + text_bytes = text.encode('utf-8') + encoded_bytes = b64encode(text_bytes) + return encoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64编码错误: {str(e)}") + return "" + + def d64(self, encoded_text): + try: + encoded_bytes = encoded_text.encode('utf-8') + decoded_bytes = b64decode(encoded_bytes) + return decoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64解码错误: {str(e)}") + return "" + + def md5(self, text): + h = MD5.new() + h.update(text.encode('utf-8')) + return h.hexdigest() + diff --git a/PY/app/光速APP.py b/PY/app/光速APP.py new file mode 100644 index 0000000..33b1204 --- /dev/null +++ b/PY/app/光速APP.py @@ -0,0 +1,222 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +import re +import sys +from Crypto.Hash import MD5 +sys.path.append('..') +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad +from urllib.parse import quote, urlparse +from base64 import b64encode, b64decode +import json +import time +from base.spider import Spider + +class Spider(Spider): + + def init(self, extend=""): + self.host = self.gethost() + pass + + def getName(self): + pass + + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def action(self, action): + pass + + def destroy(self): + pass + + def homeContent(self, filter): + data = self.getdata("/api.php/getappapi.index/initV119") + dy = {"class": "类型", "area": "地区", "lang": "语言", "year": "年份", "letter": "字母", "by": "排序", + "sort": "排序"} + filters = {} + classes = [] + json_data = data["type_list"] + homedata = data["banner_list"][8:] + for item in json_data: + if item["type_name"] == "全部": + continue + has_non_empty_field = False + jsontype_extend = json.loads(item["type_extend"]) + homedata.extend(item["recommend_list"]) + jsontype_extend["sort"] = "最新,最热,最赞" + classes.append({"type_name": item["type_name"], "type_id": item["type_id"]}) + for key in dy: + if key in jsontype_extend and jsontype_extend[key].strip() != "": + has_non_empty_field = True + break + if has_non_empty_field: + filters[str(item["type_id"])] = [] + for dkey in jsontype_extend: + if dkey in dy and jsontype_extend[dkey].strip() != "": + values = jsontype_extend[dkey].split(",") + value_array = [{"n": value.strip(), "v": value.strip()} for value in values if + value.strip() != ""] + filters[str(item["type_id"])].append({"key": dkey, "name": dy[dkey], "value": value_array}) + result = {} + result["class"] = classes + result["filters"] = filters + result["list"] = homedata[1:] + return result + + def homeVideoContent(self): + pass + + def categoryContent(self, tid, pg, filter, extend): + body = {"area": extend.get('area', '全部'), "year": extend.get('year', '全部'), "type_id": tid, "page": pg, + "sort": extend.get('sort', '最新'), "lang": extend.get('lang', '全部'), + "class": extend.get('class', '全部')} + result = {} + data = self.getdata("/api.php/getappapi.index/typeFilterVodList", body) + result["list"] = data["recommend_list"] + result["page"] = pg + result["pagecount"] = 9999 + result["limit"] = 90 + result["total"] = 999999 + return result + + def detailContent(self, ids): + body = f"vod_id={ids[0]}" + data = self.getdata("/api.php/getappapi.index/vodDetail", body) + vod = data["vod"] + play = [] + names = [] + for itt in data["vod_play_list"]: + a = [] + names.append(itt["player_info"]["show"]) + for it in itt['urls']: + it['user_agent']=itt["player_info"].get("user_agent") + it["parse"]=itt["player_info"].get("parse") + a.append(f"{it['name']}${self.e64(json.dumps(it))}") + play.append("#".join(a)) + vod["vod_play_from"] = "$$$".join(names) + vod["vod_play_url"] = "$$$".join(play) + result = {"list": [vod]} + return result + + def searchContent(self, key, quick, pg="1"): + body = f"keywords={key}&type_id=0&page={pg}" + data = self.getdata("/api.php/getappapi.index/searchList", body) + result = {"list": data["search_list"], "page": pg} + return result + + def playerContent(self, flag, id, vipFlags): + ids = json.loads(self.d64(id)) + h = {"User-Agent": (ids['user_agent'] or "okhttp/3.14.9")} + try: + if re.search(r'url=', ids['parse_api_url']): + data = self.fetch(ids['parse_api_url'], headers=h, timeout=10).json() + url = data.get('url') or data['data'].get('url') + else: + body = f"parse_api={ids.get('parse') or ids['parse_api_url'].replace(ids['url'], '')}&url={quote(self.aes(ids['url'], True))}&token={ids.get('token')}" + b = self.getdata("/api.php/getappapi.index/vodParse", body)['json'] + url = json.loads(b)['url'] + if 'error' in url: raise ValueError(f"解析失败: {url}") + p = 0 + except Exception as e: + print('错误信息:', e) + url, p = ids['url'], 1 + + if re.search(r'\.jpg|\.png|\.jpeg', url): + url = self.Mproxy(url) + result = {} + result["parse"] = p + result["url"] = url + result["header"] = h + return result + + def localProxy(self, param): + return self.Mlocal(param) + + def gethost(self): + headers = { + 'User-Agent': 'okhttp/3.14.9' + } + host = self.fetch('https://jingyu-1312635929.cos.ap-nanjing.myqcloud.com/1.json', + headers=headers).text.strip() + return host + + phend = { + 'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 11; M2012K10C Build/RP1A.200720.011)', + 'allowCrossProtocolRedirects': 'true' + } + + def aes(self, operation, text): + key = "4d83b87c4c5ea111".encode("utf-8") + iv = key + if operation == "encrypt": + cipher = AES.new(key, AES.MODE_CBC, iv) + ct_bytes = cipher.encrypt(pad(text.encode("utf-8"), AES.block_size)) + ct = b64encode(ct_bytes).decode("utf-8") + return ct + elif operation == "decrypt": + cipher = AES.new(key, AES.MODE_CBC, iv) + pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size) + return pt.decode("utf-8") + + def header(self): + t = str(int(time.time())) + header = {"Referer":self.host, + "User-Agent": "okhttp/3.14.9", "app-version-code": "300", "app-ui-mode": "light", + "app-api-verify-time": t, "app-user-device-id": self.md5(t), + "app-api-verify-sign": self.aes("encrypt", t), + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"} + return header + + def getdata(self, path, data=None): + vdata = self.post(f"{self.host}{path}", headers=self.header(), data=data, timeout=10).json()['data'] + data1 = self.aes("decrypt", vdata) + return json.loads(data1) + + def Mproxy(self, url): + return self.getProxyUrl() + "&url=" + b64encode(url.encode('utf-8')).decode('utf-8') + "&type=m3u8" + + def Mlocal(self, param,header=None): + url = self.d64(param["url"]) + ydata = self.fetch(url, headers=header, allow_redirects=False) + data = ydata.content.decode('utf-8') + if ydata.headers.get('Location'): + url = ydata.headers['Location'] + data = self.fetch(url, headers=header).content.decode('utf-8') + parsed_url = urlparse(url) + durl = parsed_url.scheme + "://" + parsed_url.netloc + lines = data.strip().split('\n') + for index, string in enumerate(lines): + if '#EXT' not in string and 'http' not in string: + last_slash_index = string.rfind('/') + lpath = string[:last_slash_index + 1] + lines[index] = durl + ('' if lpath.startswith('/') else '/') + lpath + data = '\n'.join(lines) + return [200, "application/vnd.apple.mpegur", data] + + def e64(self, text): + try: + text_bytes = text.encode('utf-8') + encoded_bytes = b64encode(text_bytes) + return encoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64编码错误: {str(e)}") + return "" + + def d64(self,encoded_text): + try: + encoded_bytes = encoded_text.encode('utf-8') + decoded_bytes = b64decode(encoded_bytes) + return decoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64解码错误: {str(e)}") + return "" + + def md5(self, text): + h = MD5.new() + h.update(text.encode('utf-8')) + return h.hexdigest() diff --git a/PY/app/剧多短剧APP.py b/PY/app/剧多短剧APP.py new file mode 100644 index 0000000..a25f44f --- /dev/null +++ b/PY/app/剧多短剧APP.py @@ -0,0 +1,313 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +import base64 +import binascii +import json +import random +import sys +import time +import uuid +from base64 import b64decode, b64encode +from Crypto.Cipher import AES +from Crypto.Hash import MD5 +from Crypto.Util.Padding import unpad, pad +sys.path.append('..') +from base.spider import Spider + + +class Spider(Spider): + + def init(self, extend=""): + self.ut = False + # self.did, self.ntid =self.getdid() + self.did, self.ntid = 'e59eb2465f61b9ca','65a0de19b3a2ec93fa479ad6' + self.token, self.uid = self.gettoken() + self.phost, self.phz,self.mphost=self.getpic() + # self.phost, self.phz,self.mphost = ('https://dbtp.tgydy.com','.log','https://dplay.nbzsmc.com') + pass + + def getName(self): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + host='http://192.151.245.34:8089' + + def md5(self, text): + h = MD5.new() + h.update(text.encode('utf-8')) + return h.hexdigest() + + def uuid(self): + return str(uuid.uuid4()) + + def getdid(self): + did = self.random_str(16) + ntid = self.random_str(24) + return did, ntid + # try: + # if self.getCache('did'): + # return self.getCache('did'), self.getCache('ntid') + # else: + # self.setCache('did', did) + # self.setCache('ntid', ntid) + # return did, ntid + # except Exception as e: + # self.setCache('did', did) + # self.setCache('ntid', ntid) + # return did, ntid + + def aes(self, text, bool=True): + key = b64decode('c0k4N1RfKTY1U1cjJERFRA==') + iv = b64decode('VzIjQWRDVkdZSGFzSEdEVA==') + if bool: + cipher = AES.new(key, AES.MODE_CBC, iv) + ct_bytes = cipher.encrypt(pad(text.encode("utf-8"), AES.block_size)) + ct = b64encode(ct_bytes).decode("utf-8") + return ct + else: + cipher = AES.new(key, AES.MODE_CBC, iv) + pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size) + ptt=json.loads(pt.decode("utf-8")) + return ptt + + def random_str(self,length=24): + hex_chars = '0123456789abcdef' + return ''.join(random.choice(hex_chars) for _ in range(length)) + + def gettoken(self): + params={"deviceId":self.did,"deviceModel":"8848钛晶手机","devicePlatform":"1","tenantId":self.ntid} + data=self.getdata('/supports/anonyLogin',params) + self.ut=True + return data['data']['token'], data['data']['userId'] + + def getdata(self,path,params=None): + t = int(time.time()*1000) + n=self.md5(f'{self.uuid()}{t}') + if params: + ct=self.aes(json.dumps(params)) + else: + ct=f'{t}{n}' + s=self.md5(f'{ct}8j@78m.367HGDF') + headers = { + 'User-Agent': 'okhttp-okgo/jeasonlzy', + 'Connection': 'Keep-Alive', + 'Accept-Language': 'zh-CN,zh;q=0.8', + 'tenantId': self.ntid, + 'n': n, + 't': str(int(t/1000)), + 's': s, + } + if self.ut: + headers['ta-token'] = self.token + headers['userId'] = self.uid + if params: + params={'ct':ct} + response = self.post(f'{self.host}{path}', headers=headers, json=params).text + else: + response = self.fetch(f'{self.host}{path}', headers=headers).text + data=self.aes(response[1:-1],False) + return data + + def getpic(self): + try: + at = int(time.time() * 1000) + t=str(int(at/ 1000)) + n = self.md5(f'{self.uuid()}{at}') + headers = { + 'Host': '192.151.245.34:8089', + 'User-Agent': 'okhttp-okgo/jeasonlzy', + 'Connection': 'Keep-Alive', + 'Accept-Language': 'zh-CN,zh;q=0.8', + 'tenantId': self.ntid, + 'userId': self.uid, + 'ta-token': self.token, + 'n': n, + 't': t, + 's': self.md5(f'{t}{n}8j@78m.367HGDF') + } + params = { + 'tenantId': self.ntid, + } + response = self.fetch(f'{self.host}/supports/configs', params=params, headers=headers).text + data=self.aes(response[1:-1],False) + config = { + 'image_cdn': '', + 'image_cdn_path': '', + 'cdn-domain': '' + } + for item in data.get('data', []): + name = item.get('name') + records = item.get('records', []) + + if name in config and records: + value = records[0].get('value', '') + if name == 'cdn-domain': + value = value.split('#')[0] + config[name] = value + + return config['image_cdn'], config['image_cdn_path'], config['cdn-domain'] + + except Exception as e: + print(f"Error in getpic: {e}") + return 'https://dbtp.tgydy.com', '.log', 'https://dplay.nbzsmc.com' + + def getlist(self,data): + vod=[] + for i in data: + vod.append({ + 'vod_id': f'{i.get("movieId")}@{i.get("entryNum")}', + 'vod_name': i.get('title'), + 'vod_pic': f'{self.getProxyUrl()}&path={i.get("thumbnail")}', + 'vod_year': i.get('score'), + 'vod_remarks': f'{i.get("entryNum")}集' + }) + return vod + + def homeContent(self, filter): + data=self.getdata('/movies/classifies') + result = {} + cateManual = { + "榜单": "ranking/getTodayHotRank", + "专辑": "getTMovieFolderPage", + "剧场": "getClassMoviePage2", + "演员": "follow/getRecommendActorPage", + } + classes = [] + for k in cateManual: + classes.append({ + 'type_name': k, + 'type_id': cateManual[k] + }) + filters = {} + if data.get('data'): + filters["getClassMoviePage2"] = [ + { + "key": "type", + "name": "分类", + "value": [ + {"n": item["name"], "v": item["classifyId"]} + for item in data["data"] + ] + } + ] + filters["ranking/getTodayHotRank"] = [ + { + "key": "type", + "name": "榜单", + "value": [ + {"n": "播放榜", "v": "getWeekHotPlayRank"}, + {"n": "高赞榜", "v": "getWeekStarRank"}, + {"n": "追剧榜", "v": "getSubTMoviePage"}, + {"n": "高分榜", "v": "ranking/getScoreRank"} + ] + } + ] + filters["follow/getRecommendActorPage"] = [ + { + "key": "type", + "name": "性别", + "value": [ + {"n": "男", "v": "0"}, + {"n": "女", "v": "1"} + ] + } + ] + result['class'] = classes + result['filters'] = filters + return result + + def homeVideoContent(self): + params = {"pageNo":"1","pageSize":"30","platform":"1","deviceId":self.did,"tenantId":self.ntid} + data=self.getdata('/news/getRecommendTMoviePage',params) + vod=self.getlist(data['data']['records']) + return {'list':vod} + + def categoryContent(self, tid, pg, filter, extend): + params={} + path = f'/news/{tid}' + if tid=='getClassMoviePage2': + parama={"pageNo":pg,"pageSize":"30","orderFlag":"0","haveActor":"-1","classifyId":extend.get('type','-1'),"tagId":""} + elif 'rank' in tid: + path=f'/news/{extend.get("type") or tid}' + parama={"pageNo":pg,"pageSize":"30"} + elif 'follow' in tid: + parama={"pageNo":pg,"pageSize":"20"} + if extend.get('type'): + path=f'/news/getActorPage' + parama={"pageNo":pg,"pageSize":"50","sex":extend.get('type')} + elif tid=='getTMovieFolderPage': + parama={"pageNo":pg,"pageSize":"20"} + elif '@' in tid: + path='/news/getActorTMoviePage' + parama={"id":tid.split('@')[0],"pageNo":pg,"pageSize":"30"} + params['platform'] = '1' + params['deviceId'] = self.did + params['tenantId'] = self.ntid + data=self.getdata(path,parama) + vods=[] + if 'follow' in tid: + for i in data['data']['records']: + vods.append({ + 'vod_id': f'{i.get("id")}@', + 'vod_name': i.get('name'), + 'vod_pic': f"{self.getProxyUrl()}&path={i.get('avatar')}", + 'vod_tag': 'folder', + 'vod_remarks': f'作品{i.get("movieNum")}', + 'style': {"type": "oval"} + }) + else: + vdata=data['data']['records'] + if tid=='getTMovieFolderPage': + vdata=[j for i in data['data']['records'] for j in i['movieList']] + vods=self.getlist(vdata) + result = {} + result['list'] = vods + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + ids=ids[0].split('@') + params = {"pageNo": "1", "pageSize": ids[1], "movieId": ids[0], "platform": "1", "deviceId": self.did, "tenantId": self.ntid} + data = self.getdata('/news/getEntryPage', params) + print(data) + plist=[f'第{i.get("entryNum")}集${i.get("mp4PlayAddress") or i.get("playAddress")}' for i in data['data']['records']] + vod = { + 'vod_play_from': '嗷呜爱看短剧', + 'vod_play_url': '#'.join(plist), + } + return {'list':[vod]} + + def searchContent(self, key, quick, pg="1"): + params = {"pageNo": pg, "pageSize": "20", "keyWord": key, "orderFlag": "0", "platform": "1", "deviceId": self.did, "tenantId": self.ntid} + data = self.getdata('/news/searchTMoviePage', params) + vod = self.getlist(data['data']['records']) + return {'list':vod,'page':pg} + + def playerContent(self, flag, id, vipFlags): + return {'parse': 0, 'url': f'{self.mphost}{id}', 'header': {'User-Agent':'Dalvik/2.1.0 (Linux; U; Android 11; M2012K10C Build/RP1A.200720.011)'}} + + def localProxy(self, param): + data=self.fetch(f'{self.phost}{param.get("path")}{self.phz}',headers={'User-Agent':'Dalvik/2.1.0 (Linux; U; Android 11; M2012K10C Build/RP1A.200720.011)'}) + def decrypt(encrypted_text): + try: + key = base64.urlsafe_b64decode("iM41VipvCFtToAFFRExEXw==") + iv = base64.urlsafe_b64decode("0AXRTXzmMSrlRSemWb4sVQ==") + cipher = AES.new(key, AES.MODE_CBC, iv) + decrypted_padded = cipher.decrypt(encrypted_text) + decrypted_data = unpad(decrypted_padded, AES.block_size) + return decrypted_data + except (binascii.Error, ValueError): + return None + return [200, data.headers.get('Content-Type'), decrypt(data.content)] + diff --git a/PY/app/哇哇APP.py b/PY/app/哇哇APP.py new file mode 100644 index 0000000..a49ed1f --- /dev/null +++ b/PY/app/哇哇APP.py @@ -0,0 +1,263 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +import json +import sys +import time +import uuid +from base64 import b64decode, b64encode +from concurrent.futures import ThreadPoolExecutor, as_completed + +from Crypto.Cipher import AES +from Crypto.Hash import SHA256, MD5 +from Crypto.PublicKey import RSA +from Crypto.Signature import pkcs1_15 +from Crypto.Util.Padding import unpad + +sys.path.append('..') +from base.spider import Spider + + +class Spider(Spider): + + def init(self, extend=""): + self.host, self.appKey, self.rsakey = self.userinfo() + pass + + def getName(self): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + def homeContent(self, filter): + data = self.fetch(f"{self.host}/api.php/zjv6.vod/types", headers=self.getheader()).json() + dy = {"class": "类型", "area": "地区", "lang": "语言", "year": "年份", "letter": "字母", "by": "排序", } + filters = {} + classes = [] + json_data = data['data']['list'] + for item in json_data: + has_non_empty_field = False + jsontype_extend = item["type_extend"] + jsontype_extend['by'] = '按更新,按播放,按评分,按收藏' + classes.append({"type_name": item["type_name"], "type_id": item["type_id"]}) + for key in dy: + if key in jsontype_extend and jsontype_extend[key].strip() != "": + has_non_empty_field = True + break + if has_non_empty_field: + filters[str(item["type_id"])] = [] + for dkey in jsontype_extend: + if dkey in dy and jsontype_extend[dkey].strip() != "": + values = jsontype_extend[dkey].split(",") + sl = {'按更新': 'time', '按播放': 'hits', '按评分': 'score', '按收藏': 'store_num'} + value_array = [ + {"n": value.strip(), "v": sl[value.strip()] if dkey == "by" else value.strip()} + for value in values + if value.strip() != "" + ] + filters[str(item["type_id"])].append( + {"key": dkey, "name": dy[dkey], "value": value_array} + ) + result = {"class": classes, "filters": filters} + return result + + def homeVideoContent(self): + data = self.fetch(f"{self.host}/api.php/zjv6.vod/vodPhbAll", headers=self.getheader()).json() + return {'list': data['data']['list'][0]['vod_list']} + + def categoryContent(self, tid, pg, filter, extend): + params = { + "type": tid, + "class": extend.get('class', ''), + "lang": extend.get('lang', ''), + "area": extend.get('area', ''), + "year": extend.get('year', ''), + "by": extend.get('by', ''), + "page": pg, + "limit": "12" + } + data = self.fetch(f"{self.host}/api.php/zjv6.vod", headers=self.getheader(), params=params).json() + result = {} + result['list'] = 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.php/zjv6.vod/detail?vod_id={ids[0]}&rel_limit=10", + headers=self.getheader()).json() + vod = data['data'] + v, np = {'vod_play_from': [], 'vod_play_url': []}, {} + for i in vod['vod_play_list']: + n = i['player_info']['show'] + np[n] = [] + for j in i['urls']: + j['parse'] = i['player_info']['parse2'] + nm = j.pop('name') + np[n].append(f"{nm}${self.e64(json.dumps(j))}") + for key, value in np.items(): + v['vod_play_from'].append(key) + v['vod_play_url'].append('#'.join(value)) + v['vod_play_from'] = '$$$'.join(v['vod_play_from']) + v['vod_play_url'] = '$$$'.join(v['vod_play_url']) + vod.update(v) + vod.pop('vod_play_list', None) + vod.pop('type', None) + return {'list': [vod]} + + def searchContent(self, key, quick, pg="1"): + data = self.fetch(f"{self.host}/api.php/zjv6.vod?page={pg}&limit=20&wd={key}", headers=self.getheader()).json() + return {'list': data['data']['list'], 'page': pg} + + def playerContent(self, flag, id, vipFlags): + ids = json.loads(self.d64(id)) + target_url = ids['url'] + try: + parse_str = ids.get('parse', '') + if parse_str: + parse_urls = parse_str.split(',') + result_url = self.try_all_parses(parse_urls, target_url) + if result_url: + return { + 'parse': 0, + 'url': result_url, + 'header': {'User-Agent': 'dart:io'} + } + return { + 'parse': 1, + 'url': target_url, + 'header': {'User-Agent': 'dart:io'} + } + + except Exception as e: + print(e) + return { + 'parse': 1, + 'url': target_url, + 'header': {'User-Agent': 'dart:io'} + } + + def liveContent(self, url): + pass + + def localProxy(self, param): + pass + + def userinfo(self): + t = str(int(time.time() * 1000)) + uid = self.generate_uid() + sign = self.md5(f"appKey=3bbf7348cf314874883a18d6b6fcf67a&uid={uid}&time={t}") + headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36', + 'Connection': 'Keep-Alive', + 'appKey': '3bbf7348cf314874883a18d6b6fcf67a', + 'uid': uid, + 'time': t, + 'sign': sign, + } + + params = { + 'access_token': '74d5879931b9774be10dee3d8c51008e', + } + + response = self.fetch('https://gitee.com/api/v5/repos/aycapp/openapi/contents/wawaconf.txt', params=params, + headers=headers).json() + data = json.loads(self.decrypt(response['content'])) + return data['baseUrl'], data['appKey'], data['appSecret'] + + def e64(self, text): + try: + text_bytes = text.encode('utf-8') + encoded_bytes = b64encode(text_bytes) + return encoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64编码错误: {str(e)}") + return "" + + def d64(self, encoded_text): + try: + encoded_bytes = encoded_text.encode('utf-8') + decoded_bytes = b64decode(encoded_bytes) + return decoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64解码错误: {str(e)}") + return "" + + def md5(self, text): + h = MD5.new() + h.update(text.encode('utf-8')) + return h.hexdigest() + + def generate_uid(self): + return uuid.uuid4().hex + + def getheader(self): + t = str(int(time.time() * 1000)) + uid = self.generate_uid() + sign = self.sign_message(f"appKey={self.appKey}&time={t}&uid={uid}") + headers = { + 'User-Agent': 'okhttp/4.9.3', + 'Connection': 'Keep-Alive', + 'uid': uid, + 'time': t, + 'appKey': self.appKey, + 'sign': sign, + } + return headers + + def decrypt(self, encrypted_data): + key = b64decode('Crm4FXWkk5JItpYirFDpqg==') + cipher = AES.new(key, AES.MODE_ECB) + encrypted = bytes.fromhex(self.d64(encrypted_data)) + decrypted = cipher.decrypt(encrypted) + unpadded = unpad(decrypted, AES.block_size) + return unpadded.decode('utf-8') + + def sign_message(self, message): + private_key_str = f"-----BEGIN PRIVATE KEY-----\n{self.rsakey}\n-----END PRIVATE KEY-----" + private_key = RSA.import_key(private_key_str) + message_hash = SHA256.new(message.encode('utf-8')) + signature = pkcs1_15.new(private_key).sign(message_hash) + signature_b64 = b64encode(signature).decode('utf-8') + return signature_b64 + + def fetch_url(self, parse_url, target_url): + try: + response = self.fetch(f"{parse_url.replace('..', '.')}{target_url}", + headers={"user-agent": "okhttp/4.1.0/luob.app"}, timeout=5) + if response.status_code == 200: + try: + data = response.json() + result_url = data.get('url') or data.get('data', {}).get('url') + if result_url: + return result_url + except: + pass + return None + except: + return None + + def try_all_parses(self, parse_urls, target_url): + with ThreadPoolExecutor(max_workers=(len(parse_urls))) as executor: + future_to_url = { + executor.submit(self.fetch_url, parse_url.strip(), target_url): parse_url + for parse_url in parse_urls if parse_url.strip() + } + + for future in as_completed(future_to_url): + try: + result = future.result() + if result: + return result + except: + continue + return None + diff --git a/PY/app/国外剧APP.py b/PY/app/国外剧APP.py new file mode 100644 index 0000000..eae5fc7 --- /dev/null +++ b/PY/app/国外剧APP.py @@ -0,0 +1,216 @@ +import re +import sys +from Crypto.Hash import MD5 +sys.path.append("..") +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad +from urllib.parse import quote, urlparse +from base64 import b64encode, b64decode +import json +import time +from base.spider import Spider + + +class Spider(Spider): + + def init(self, extend=""): + self.host = 'https://guowaiju.com' + self.did=self.getdid() + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def action(self, action): + pass + + def destroy(self): + pass + + def homeContent(self, filter): + data = self.getdata("/api.php/getappapi.index/initV119") + dy = {"class": "类型", "area": "地区", "lang": "语言", "year": "年份", "letter": "字母", "by": "排序", + "sort": "排序"} + filters = {} + classes = [] + json_data = data["type_list"] + homedata = data["banner_list"][8:] + for item in json_data: + if item["type_name"] == "全部": + continue + has_non_empty_field = False + jsontype_extend = json.loads(item["type_extend"]) + homedata.extend(item["recommend_list"]) + jsontype_extend["sort"] = "最新,最热,最赞" + classes.append({"type_name": item["type_name"], "type_id": item["type_id"]}) + for key in dy: + if key in jsontype_extend and jsontype_extend[key].strip() != "": + has_non_empty_field = True + break + if has_non_empty_field: + filters[str(item["type_id"])] = [] + for dkey in jsontype_extend: + if dkey in dy and jsontype_extend[dkey].strip() != "": + values = jsontype_extend[dkey].split(",") + value_array = [{"n": value.strip(), "v": value.strip()} for value in values if + value.strip() != ""] + filters[str(item["type_id"])].append({"key": dkey, "name": dy[dkey], "value": value_array}) + result = {} + result["class"] = classes + result["filters"] = filters + result["list"] = homedata[1:] + return result + + def homeVideoContent(self): + pass + + def categoryContent(self, tid, pg, filter, extend): + body = {"area": extend.get('area', '全部'), "year": extend.get('year', '全部'), "type_id": tid, "page": pg, + "sort": extend.get('sort', '最新'), "lang": extend.get('lang', '全部'), + "class": extend.get('class', '全部')} + result = {} + data = self.getdata("/api.php/getappapi.index/typeFilterVodList", body) + result["list"] = data["recommend_list"] + result["page"] = pg + result["pagecount"] = 9999 + result["limit"] = 90 + result["total"] = 999999 + return result + + def detailContent(self, ids): + body = f"vod_id={ids[0]}" + data = self.getdata("/api.php/getappapi.index/vodDetail", body) + vod = data["vod"] + play = [] + names = [] + for itt in reversed(data["vod_play_list"]): + a = [] + names.append(itt["player_info"]["show"]) + for it in itt['urls']: + it['user_agent'] = itt["player_info"].get("user_agent") + it["parse"] = itt["player_info"].get("parse") + a.append(f"{it['name']}${self.e64(json.dumps(it))}") + play.append("#".join(a)) + vod["vod_play_from"] = "$$$".join(names) + vod["vod_play_url"] = "$$$".join(play) + result = {"list": [vod]} + return result + + def searchContent(self, key, quick, pg="1"): + body = f"keywords={key}&type_id=0&page={pg}" + data = self.getdata("/api.php/getappapi.index/searchList", body) + result = {"list": data["search_list"], "page": pg} + return result + + def playerContent(self, flag, id, vipFlags): + ids = json.loads(self.d64(id)) + h = {"User-Agent": (ids['user_agent'] or "okhttp/3.14.9")} + try: + if re.search(r'url=', ids['parse_api_url']): + data = self.fetch(ids['parse_api_url'], headers=h, timeout=10).json() + url = data.get('url') or data['data'].get('url') + else: + body = f"parse_api={ids.get('parse') or ids['parse_api_url'].replace(ids['url'], '')}&url={quote(self.aes(ids['url'], True))}&token={ids.get('token')}" + b = self.getdata("/api.php/getappapi.index/vodParse", body)['json'] + url = json.loads(b)['url'] + if 'error' in url: raise ValueError(f"解析失败: {url}") + p = 0 + except Exception as e: + print('错误信息:', e) + url, p = ids['url'], 1 + + if re.search(r'\.jpg|\.png|\.jpeg', url): + url = self.Mproxy(url) + result = {} + result["parse"] = p + result["url"] = url + result["header"] = h + return result + + def localProxy(self, param): + headers = {"User-Agent": "okhttp/3.14.9"} + url = self.d64(param['url']) + ydata = self.fetch(url, headers=headers, allow_redirects=False) + data = ydata.content.decode('utf-8') + if ydata.headers.get('Location'): + url = ydata.headers['Location'] + data = self.fetch(url, headers=headers).content.decode('utf-8') + lines = data.strip().split('\n') + last_r = url[:url.rfind('/')] + parsed_url = urlparse(url) + durl = parsed_url.scheme + "://" + parsed_url.netloc + for index, string in enumerate(lines): + if '#EXT' not in string: + if 'http' not in string: + domain = last_r if string.count('/') < 2 else durl + string = domain + ('' if string.startswith('/') else '/') + string + if string.split('.')[-1].split('?')[0] == 'm3u8': + string = self.Mproxy(string) + lines[index] = string + data = '\n'.join(lines) + return [200, "application/vnd.apple.mpegur", data] + + def getdid(self): + did=self.getCache('did') + if not did: + t = str(int(time.time())) + did = self.md5(t) + self.setCache('did', did) + return did + + def aes(self, text, b=None): + key = b"7xv16h7qgkrs9b1p" + cipher = AES.new(key, AES.MODE_CBC, key) + if b: + ct_bytes = cipher.encrypt(pad(text.encode("utf-8"), AES.block_size)) + ct = b64encode(ct_bytes).decode("utf-8") + return ct + else: + pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size) + return pt.decode("utf-8") + + def header(self): + t = str(int(time.time())) + header = { + "User-Agent": "okhttp/3.14.9", "app-version-code": "110", "app-ui-mode": "light", + "app-api-verify-time": t, "app-user-device-id": self.did, + "app-api-verify-sign": self.aes(t, True), + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" + } + return header + + def getdata(self, path, data=None): + vdata = self.post(f"{self.host}{path}", headers=self.header(), data=data, timeout=10).json()['data'] + data1 = self.aes(vdata) + return json.loads(data1) + + def Mproxy(self, url): + return f"{self.getProxyUrl()}&url={self.e64(url)}&type=m3u8" + + def e64(self, text): + try: + text_bytes = text.encode('utf-8') + encoded_bytes = b64encode(text_bytes) + return encoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64编码错误: {str(e)}") + return "" + + def d64(self, encoded_text): + try: + encoded_bytes = encoded_text.encode('utf-8') + decoded_bytes = b64decode(encoded_bytes) + return decoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64解码错误: {str(e)}") + return "" + + def md5(self, text): + h = MD5.new() + h.update(text.encode('utf-8')) + return h.hexdigest() + + diff --git a/PY/app/奇迹APP.py b/PY/app/奇迹APP.py new file mode 100644 index 0000000..b3ff4c7 --- /dev/null +++ b/PY/app/奇迹APP.py @@ -0,0 +1,216 @@ +import re +import sys +from Crypto.Hash import MD5 +sys.path.append("..") +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad +from urllib.parse import quote, urlparse +from base64 import b64encode, b64decode +import json +import time +from base.spider import Spider + + +class Spider(Spider): + + def init(self, extend=""): + self.host='https://www.qj1080.top' + self.did=self.getdid() + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def action(self, action): + pass + + def destroy(self): + pass + + def homeContent(self, filter): + data = self.getdata("/api.php/getappapi.index/initV119") + dy = {"class": "类型", "area": "地区", "lang": "语言", "year": "年份", "letter": "字母", "by": "排序", + "sort": "排序"} + filters = {} + classes = [] + json_data = data["type_list"] + homedata = data["banner_list"][8:] + for item in json_data: + if item["type_name"] == "全部": + continue + has_non_empty_field = False + jsontype_extend = json.loads(item["type_extend"]) + homedata.extend(item["recommend_list"]) + jsontype_extend["sort"] = "最新,最热,最赞" + classes.append({"type_name": item["type_name"], "type_id": item["type_id"]}) + for key in dy: + if key in jsontype_extend and jsontype_extend[key].strip() != "": + has_non_empty_field = True + break + if has_non_empty_field: + filters[str(item["type_id"])] = [] + for dkey in jsontype_extend: + if dkey in dy and jsontype_extend[dkey].strip() != "": + values = jsontype_extend[dkey].split(",") + value_array = [{"n": value.strip(), "v": value.strip()} for value in values if + value.strip() != ""] + filters[str(item["type_id"])].append({"key": dkey, "name": dy[dkey], "value": value_array}) + result = {} + result["class"] = classes + result["filters"] = filters + result["list"] = homedata[1:] + return result + + def homeVideoContent(self): + pass + + def categoryContent(self, tid, pg, filter, extend): + body = {"area": extend.get('area', '全部'), "year": extend.get('year', '全部'), "type_id": tid, "page": pg, + "sort": extend.get('sort', '最新'), "lang": extend.get('lang', '全部'), + "class": extend.get('class', '全部')} + result = {} + data = self.getdata("/api.php/getappapi.index/typeFilterVodList", body) + result["list"] = data["recommend_list"] + result["page"] = pg + result["pagecount"] = 9999 + result["limit"] = 90 + result["total"] = 999999 + return result + + def detailContent(self, ids): + body = f"vod_id={ids[0]}" + data = self.getdata("/api.php/getappapi.index/vodDetail", body) + vod = data["vod"] + play = [] + names = [] + for itt in data["vod_play_list"]: + a = [] + names.append(itt["player_info"]["show"]) + for it in itt['urls']: + it['user_agent'] = itt["player_info"].get("user_agent") + it["parse"] = itt["player_info"].get("parse") + a.append(f"{it['name']}${self.e64(json.dumps(it))}") + play.append("#".join(a)) + vod["vod_play_from"] = "$$$".join(names) + vod["vod_play_url"] = "$$$".join(play) + result = {"list": [vod]} + return result + + def searchContent(self, key, quick, pg="1"): + body = f"keywords={key}&type_id=0&page={pg}" + data = self.getdata("/api.php/getappapi.index/searchList", body) + result = {"list": data["search_list"], "page": pg} + return result + + def playerContent(self, flag, id, vipFlags): + ids = json.loads(self.d64(id)) + h = {"User-Agent": (ids['user_agent'] or "okhttp/3.14.9")} + try: + if re.search(r'url=', ids['parse_api_url']): + data = self.fetch(ids['parse_api_url'], headers=h, timeout=10).json() + url = data.get('url') or data['data'].get('url') + else: + body = f"parse_api={ids.get('parse') or ids['parse_api_url'].replace(ids['url'], '')}&url={quote(self.aes(ids['url'], True))}&token={ids.get('token')}" + b = self.getdata("/api.php/getappapi.index/vodParse", body)['json'] + url = json.loads(b)['url'] + if 'error' in url: raise ValueError(f"解析失败: {url}") + p = 0 + except Exception as e: + print('错误信息:', e) + url, p = ids['url'], 1 + + if re.search(r'\.jpg|\.png|\.jpeg', url): + url = self.Mproxy(url) + result = {} + result["parse"] = p + result["url"] = url + result["header"] = h + return result + + def localProxy(self, param): + headers = {"User-Agent": "okhttp/3.14.9"} + url = self.d64(param['url']) + ydata = self.fetch(url, headers=headers, allow_redirects=False) + data = ydata.content.decode('utf-8') + if ydata.headers.get('Location'): + url = ydata.headers['Location'] + data = self.fetch(url, headers=headers).content.decode('utf-8') + lines = data.strip().split('\n') + last_r = url[:url.rfind('/')] + parsed_url = urlparse(url) + durl = parsed_url.scheme + "://" + parsed_url.netloc + for index, string in enumerate(lines): + if '#EXT' not in string: + if 'http' not in string: + domain = last_r if string.count('/') < 2 else durl + string = domain + ('' if string.startswith('/') else '/') + string + if string.split('.')[-1].split('?')[0] == 'm3u8': + string = self.Mproxy(string) + lines[index] = string + data = '\n'.join(lines) + return [200, "application/vnd.apple.mpegur", data] + + def getdid(self): + did=self.getCache('did') + if not did: + t = str(int(time.time())) + did = self.md5(t) + self.setCache('did', did) + return did + + def aes(self, text, b=None): + key = b"8t2L9x5Qz4A7p3y6" + cipher = AES.new(key, AES.MODE_CBC, key) + if b: + ct_bytes = cipher.encrypt(pad(text.encode("utf-8"), AES.block_size)) + ct = b64encode(ct_bytes).decode("utf-8") + return ct + else: + pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size) + return pt.decode("utf-8") + + def header(self): + t = str(int(time.time())) + header = { + "User-Agent": "okhttp/3.14.9", "app-version-code": "666", "app-ui-mode": "light", + "app-api-verify-time": t, "app-user-device-id": self.did, + "app-api-verify-sign": self.aes(t, True), + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" + } + return header + + def getdata(self, path, data=None): + vdata = self.post(f"{self.host}{path}", headers=self.header(), data=data, timeout=10).json()['data'] + data1 = self.aes(vdata) + return json.loads(data1) + + def Mproxy(self, url): + return f"{self.getProxyUrl()}&url={self.e64(url)}&type=m3u8" + + def e64(self, text): + try: + text_bytes = text.encode('utf-8') + encoded_bytes = b64encode(text_bytes) + return encoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64编码错误: {str(e)}") + return "" + + def d64(self, encoded_text): + try: + encoded_bytes = encoded_text.encode('utf-8') + decoded_bytes = b64decode(encoded_bytes) + return decoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64解码错误: {str(e)}") + return "" + + def md5(self, text): + h = MD5.new() + h.update(text.encode('utf-8')) + return h.hexdigest() + + diff --git a/PY/app/小苹果APP.py b/PY/app/小苹果APP.py new file mode 100644 index 0000000..d673fe3 --- /dev/null +++ b/PY/app/小苹果APP.py @@ -0,0 +1,140 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +import sys +sys.path.append('..') +from base.spider import Spider + +class Spider(Spider): + + def init(self, extend=""): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + host='http://item.xpgcom.com' + + headers = { + "User-Agent": "okhttp/3.12.11" + } + + def homeContent(self, filter): + data = self.fetch(f"{self.host}/api.php/v2.vod/androidtypes",headers=self.headers,).json() + dy = { + "classes": "类型", + "areas": "地区", + "years": "年份", + "sortby": "排序", + } + filters = {} + classes = [] + for item in data['data']: + has_non_empty_field = False + item['sortby'] = ['updatetime', 'hits', 'score'] + demos = ['时间', '人气', '评分'] + classes.append({"type_name": item["type_name"], "type_id": str(item["type_id"])}) + for key in dy: + if key in item and len(item[key]) > 1: + has_non_empty_field = True + break + if has_non_empty_field: + filters[str(item["type_id"])] = [] + for dkey in item: + if dkey in dy and len(item[dkey]) > 1: + values = item[dkey] + value_array = [ + {"n": demos[idx] if dkey == "sortby" else value.strip(), "v": value.strip()} + for idx, value in enumerate(values) + if value.strip() != "" + ] + filters[str(item["type_id"])].append( + {"key": dkey, "name": dy[dkey], "value": value_array} + ) + result = {} + result["class"] = classes + result["filters"] = filters + return result + + def homeVideoContent(self): + rsp = self.fetch(f"{self.host}/api.php/v2.main/androidhome", headers=self.headers).json() + videos = [] + for i in rsp['data']['list']:videos.extend(self.getlist(i['list'])) + return {'list':videos} + + def categoryContent(self, tid, pg, filter, extend): + params = { + "page": pg, + "type": tid, + "area":extend.get('areaes',''), + "year":extend.get('yeares',''), + "sortby":extend.get('sortby',''), + "class":extend.get('classes','') + } + params={i:v for i,v in params.items() if v} + rsp = self.fetch(f'{self.host}/api.php/v2.vod/androidfilter10086', headers=self.headers, params=params).json() + result = {} + result['list'] = self.getlist(rsp['data']) + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + rsp = self.fetch(f'{self.host}/api.php/v3.vod/androiddetail2?vod_id={ids[0]}', headers=self.headers).json() + v = rsp['data'] + vod = { + 'vod_year':v.get('year'), + 'vod_area':v.get('area'), + 'vod_lang':v.get('lang'), + 'type_name':v.get('className'), + 'vod_actor':v.get('actor'), + 'vod_director':v.get('director'), + 'vod_content':v.get('content'), + 'vod_play_from': '小苹果', + 'vod_play_url': '#'.join([f"{i['key']}${i['url']}" for i in v['urls']]) + } + return {'list':[vod]} + + def searchContent(self, key, quick, pg='1'): + rsp = self.fetch(f'{self.host}/api.php/v2.vod/androidsearch10086?page={pg}&wd={key}', headers=self.headers).json() + return {'list':self.getlist(rsp['data']),'page':pg} + + def playerContent(self, flag, id, vipFlags): + header = { + 'user_id': 'XPGBOX', + 'token2': 'SnAXiSW8vScXE0Z9aDOnK5xffbO75w1+uPom3WjnYfVEA1oWtUdi2Ihy1N8=', + 'version': 'XPGBOX com.phoenix.tv1.5.7', + 'hash': 'd78a', + 'screenx': '2345', + 'user-agent': 'Lavf/58.12.100', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36', + 'token': 'ElEDlwCVgXcFHFhddiq2JKteHofExRBUrfNlmHrWetU3VVkxnzJAodl52N9EUFS+Dig2A/fBa/V9RuoOZRBjYvI+GW8kx3+xMlRecaZuECdb/3AdGkYpkjW3wCnpMQxf8vVeCz5zQLDr8l8bUChJiLLJLGsI+yiNskiJTZz9HiGBZhZuWh1mV1QgYah5CLTbSz8=', + 'timestamp': '1743060300', + 'screeny': '1065', + } + if 'http' not in id:id=f"http://c.xpgtv.net/m3u8/{id}.m3u8" + return {"parse": 0, "url": id, "header": header} + + def localProxy(self, param): + pass + + def getlist(self,data): + videos = [] + for vod in data: + r=f"更新至{vod.get('updateInfo')}" if vod.get('updateInfo') else '' + videos.append({ + "vod_id": vod['id'], + "vod_name": vod['name'], + "vod_pic": vod['pic'], + "vod_remarks": r or vod['score'] + }) + return videos + + diff --git a/PY/app/恋鱼影视APP.py b/PY/app/恋鱼影视APP.py new file mode 100644 index 0000000..bfefb89 --- /dev/null +++ b/PY/app/恋鱼影视APP.py @@ -0,0 +1,212 @@ +import re +import sys +from Crypto.Hash import MD5 +sys.path.append("..") +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad +from urllib.parse import quote, urlparse +from base64 import b64encode, b64decode +import json +import time +from base.spider import Spider + + +class Spider(Spider): + + def init(self, extend=""): + self.host = 'http://47.122.22.78' + self.did=self.getdid() + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def action(self, action): + pass + + def destroy(self): + pass + + def homeContent(self, filter): + data = self.getdata("/api.php/getappapi.index/initV119") + dy = {"class": "类型", "area": "地区", "lang": "语言", "year": "年份", "letter": "字母", "by": "排序", + "sort": "排序"} + filters = {} + classes = [] + json_data = data["type_list"] + homedata = data["banner_list"][8:] + for item in json_data: + if item["type_name"] == "全部": + continue + has_non_empty_field = False + jsontype_extend = json.loads(item["type_extend"]) + homedata.extend(item["recommend_list"]) + jsontype_extend["sort"] = "最新,最热,最赞" + classes.append({"type_name": item["type_name"], "type_id": item["type_id"]}) + for key in dy: + if key in jsontype_extend and jsontype_extend[key].strip() != "": + has_non_empty_field = True + break + if has_non_empty_field: + filters[str(item["type_id"])] = [] + for dkey in jsontype_extend: + if dkey in dy and jsontype_extend[dkey].strip() != "": + values = jsontype_extend[dkey].split(",") + value_array = [{"n": value.strip(), "v": value.strip()} for value in values if + value.strip() != ""] + filters[str(item["type_id"])].append({"key": dkey, "name": dy[dkey], "value": value_array}) + result = {} + result["class"] = classes + result["filters"] = filters + result["list"] = homedata[1:] + return result + + def homeVideoContent(self): + pass + + def categoryContent(self, tid, pg, filter, extend): + body = {"area": extend.get('area', '全部'), "year": extend.get('year', '全部'), "type_id": tid, "page": pg, + "sort": extend.get('sort', '最新'), "lang": extend.get('lang', '全部'), + "class": extend.get('class', '全部')} + result = {} + data = self.getdata("/api.php/getappapi.index/typeFilterVodList", body) + result["list"] = data["recommend_list"] + result["page"] = pg + result["pagecount"] = 9999 + result["limit"] = 90 + result["total"] = 999999 + return result + + def detailContent(self, ids): + body = f"vod_id={ids[0]}" + data = self.getdata("/api.php/getappapi.index/vodDetail", body) + vod = data["vod"] + play = [] + names = [] + for itt in data["vod_play_list"]: + a = [] + names.append(itt["player_info"]["show"]) + for it in itt['urls']: + it['user_agent'] = itt["player_info"].get("user_agent") + it["parse"] = itt["player_info"].get("parse") + a.append(f"{it['name']}${self.e64(json.dumps(it))}") + play.append("#".join(a)) + vod["vod_play_from"] = "$$$".join(names) + vod["vod_play_url"] = "$$$".join(play) + result = {"list": [vod]} + return result + + def searchContent(self, key, quick, pg="1"): + body = f"keywords={key}&type_id=0&page={pg}" + data = self.getdata("/api.php/getappapi.index/searchList", body) + result = {"list": data["search_list"], "page": pg} + return result + + def playerContent(self, flag, id, vipFlags): + ids = json.loads(self.d64(id)) + h = {"User-Agent": (ids['user_agent'] or "okhttp/3.14.9")} + try: + if re.search(r'url=', ids['parse_api_url']): + data = self.fetch(ids['parse_api_url'], headers=h, timeout=10).json() + url = data.get('url') or data['data'].get('url') + else: + body = f"parse_api={ids.get('parse') or ids['parse_api_url'].replace(ids['url'], '')}&url={quote(self.aes(ids['url'], True))}&token={ids.get('token')}" + b = self.getdata("/api.php/getappapi.index/vodParse", body)['json'] + url = json.loads(b)['url'] + if 'error' in url: raise ValueError(f"解析失败: {url}") + p = 0 + except Exception as e: + print('错误信息:', e) + url, p = ids['url'], 1 + + if re.search(r'\.jpg|\.png|\.jpeg', url): + url = self.Mproxy(url) + result = {} + result["parse"] = p + result["url"] = url + result["header"] = h + return result + + def localProxy(self, param): + return self.Mlocal(param) + + def aes(self, text, b=None): + key = b"1234567890123456" + cipher = AES.new(key, AES.MODE_CBC, key) + if b: + ct_bytes = cipher.encrypt(pad(text.encode("utf-8"), AES.block_size)) + ct = b64encode(ct_bytes).decode("utf-8") + return ct + else: + pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size) + return pt.decode("utf-8") + + def header(self): + t = str(int(time.time())) + header = {"Referer": self.host, + "User-Agent": "okhttp/3.14.9", "app-version-code": "101", "app-ui-mode": "light", + "app-api-verify-time": t, "app-user-device-id": self.did, + "app-api-verify-sign": self.aes(t, True), + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"} + return header + + def getdid(self): + did=self.getCache('did') + if not did: + t = str(int(time.time())) + did = self.md5(t) + self.setCache('did', did) + return did + + def getdata(self, path, data=None): + vdata = self.post(f"{self.host}{path}", headers=self.header(), data=data, timeout=10).json()['data'] + data1 = self.aes(vdata) + return json.loads(data1) + + def Mproxy(self, url): + return f"{self.getProxyUrl()}&url={self.e64(url)}&type=m3u8" + + def Mlocal(self, param, header=None): + url = self.d64(param["url"]) + ydata = self.fetch(url, headers=header, allow_redirects=False) + data = ydata.content.decode('utf-8') + if ydata.headers.get('Location'): + url = ydata.headers['Location'] + data = self.fetch(url, headers=header).content.decode('utf-8') + parsed_url = urlparse(url) + durl = parsed_url.scheme + "://" + parsed_url.netloc + lines = data.strip().split('\n') + for index, string in enumerate(lines): + if '#EXT' not in string and 'http' not in string: + last_slash_index = string.rfind('/') + lpath = string[:last_slash_index + 1] + lines[index] = durl + ('' if lpath.startswith('/') else '/') + lpath + data = '\n'.join(lines) + return [200, "application/vnd.apple.mpegur", data] + + def e64(self, text): + try: + text_bytes = text.encode('utf-8') + encoded_bytes = b64encode(text_bytes) + return encoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64编码错误: {str(e)}") + return "" + + def d64(self, encoded_text): + try: + encoded_bytes = encoded_text.encode('utf-8') + decoded_bytes = b64decode(encoded_bytes) + return decoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64解码错误: {str(e)}") + return "" + + def md5(self, text): + h = MD5.new() + h.update(text.encode('utf-8')) + return h.hexdigest() + diff --git a/PY/app/悠悠APP.py b/PY/app/悠悠APP.py new file mode 100644 index 0000000..f4940fa --- /dev/null +++ b/PY/app/悠悠APP.py @@ -0,0 +1,220 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +import re +import sys +from Crypto.Hash import MD5 +sys.path.append("..") +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad +from urllib.parse import quote, urlparse +from base64 import b64encode, b64decode +import json +import time +from base.spider import Spider + +class Spider(Spider): + + def init(self, extend=""): + self.host = self.gethost() + pass + + def getName(self): + pass + + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def action(self, action): + pass + + def destroy(self): + pass + + def homeContent(self, filter): + data = self.getdata("/api.php/getappapi.index/initV119") + dy = {"class": "类型", "area": "地区", "lang": "语言", "year": "年份", "letter": "字母", "by": "排序", + "sort": "排序"} + filters = {} + classes = [] + json_data = data["type_list"] + homedata = data["banner_list"][8:] + for item in json_data: + if item["type_name"] == "全部": + continue + has_non_empty_field = False + jsontype_extend = json.loads(item["type_extend"]) + homedata.extend(item["recommend_list"]) + jsontype_extend["sort"] = "最新,最热,最赞" + classes.append({"type_name": item["type_name"], "type_id": item["type_id"]}) + for key in dy: + if key in jsontype_extend and jsontype_extend[key].strip() != "": + has_non_empty_field = True + break + if has_non_empty_field: + filters[str(item["type_id"])] = [] + for dkey in jsontype_extend: + if dkey in dy and jsontype_extend[dkey].strip() != "": + values = jsontype_extend[dkey].split(",") + value_array = [{"n": value.strip(), "v": value.strip()} for value in values if + value.strip() != ""] + filters[str(item["type_id"])].append({"key": dkey, "name": dy[dkey], "value": value_array}) + result = {} + result["class"] = classes + result["filters"] = filters + result["list"] = homedata[1:] + return result + + def homeVideoContent(self): + pass + + def categoryContent(self, tid, pg, filter, extend): + body = {"area": extend.get('area', '全部'), "year": extend.get('year', '全部'), "type_id": tid, "page": pg, + "sort": extend.get('sort', '最新'), "lang": extend.get('lang', '全部'), + "class": extend.get('class', '全部')} + result = {} + data = self.getdata("/api.php/getappapi.index/typeFilterVodList", body) + result["list"] = data["recommend_list"] + result["page"] = pg + result["pagecount"] = 9999 + result["limit"] = 90 + result["total"] = 999999 + return result + + def detailContent(self, ids): + body = f"vod_id={ids[0]}" + data = self.getdata("/api.php/getappapi.index/vodDetail", body) + vod = data["vod"] + play = [] + names = [] + for itt in data["vod_play_list"]: + a = [] + names.append(itt["player_info"]["show"]) + for it in itt['urls']: + it['user_agent']=itt["player_info"].get("user_agent") + it["parse"]=itt["player_info"].get("parse") + a.append(f"{it['name']}${self.e64(json.dumps(it))}") + play.append("#".join(a)) + vod["vod_play_from"] = "$$$".join(names) + vod["vod_play_url"] = "$$$".join(play) + result = {"list": [vod]} + return result + + def searchContent(self, key, quick, pg="1"): + body = f"keywords={key}&type_id=0&page={pg}" + data = self.getdata("/api.php/getappapi.index/searchList", body) + result = {"list": data["search_list"], "page": pg} + return result + + def playerContent(self, flag, id, vipFlags): + ids = json.loads(self.d64(id)) + h = {"User-Agent": (ids['user_agent'] or "okhttp/3.14.9")} + try: + if re.search(r'url=', ids['parse_api_url']): + data = self.fetch(ids['parse_api_url'], headers=h, timeout=10).json() + url = data.get('url') or data['data'].get('url') + else: + body = f"parse_api={ids.get('parse') or ids['parse_api_url'].replace(ids['url'], '')}&url={quote(self.aes(ids['url'], True))}&token={ids.get('token')}" + b = self.getdata("/api.php/getappapi.index/vodParse", body)['json'] + url = json.loads(b)['url'] + if 'error' in url: raise ValueError(f"解析失败: {url}") + p = 0 + except Exception as e: + print('错误信息:', e) + url, p = ids['url'], 1 + + if re.search(r'\.jpg|\.png|\.jpeg', url): + url = self.Mproxy(url) + result = {} + result["parse"] = p + result["url"] = url + result["header"] = h + return result + + def localProxy(self, param): + return self.Mlocal(param) + + def gethost(self): + headers = { + 'User-Agent': 'okhttp/3.14.9' + } + host = self.fetch('http://host.yyys.news/250123.txt', headers=headers).text + return host.strip() + + phend = { + 'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 11; M2012K10C Build/RP1A.200720.011)', + 'allowCrossProtocolRedirects': 'true' + } + + def aes(self, text,b=None): + key = b"RuN9LRvwTRgpQnpK" + cipher = AES.new(key, AES.MODE_CBC, key) + if b: + ct_bytes = cipher.encrypt(pad(text.encode("utf-8"), AES.block_size)) + ct = b64encode(ct_bytes).decode("utf-8") + return ct + else : + pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size) + return pt.decode("utf-8") + + def header(self): + t = str(int(time.time())) + header = {"Referer":self.host, + "User-Agent": "okhttp/3.14.9", "app-version-code": "547", "app-ui-mode": "light", + "app-api-verify-time": t, "app-user-device-id": self.md5(t), + "app-api-verify-sign": self.aes(t,True), + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"} + return header + + def getdata(self, path, data=None): + vdata = self.post(f"{self.host}{path}", headers=self.header(), data=data, timeout=10).json()['data'] + data1 = self.aes(vdata) + return json.loads(data1) + + def Mproxy(self, url): + return f"{self.getProxyUrl()}&url={self.e64(url)}&type=m3u8" + + def Mlocal(self, param,header=None): + url = self.d64(param["url"]) + ydata = self.fetch(url, headers=header, allow_redirects=False) + data = ydata.content.decode('utf-8') + if ydata.headers.get('Location'): + url = ydata.headers['Location'] + data = self.fetch(url, headers=header).content.decode('utf-8') + parsed_url = urlparse(url) + durl = parsed_url.scheme + "://" + parsed_url.netloc + lines = data.strip().split('\n') + for index, string in enumerate(lines): + if '#EXT' not in string and 'http' not in string: + last_slash_index = string.rfind('/') + lpath = string[:last_slash_index + 1] + lines[index] = durl + ('' if lpath.startswith('/') else '/') + lpath + data = '\n'.join(lines) + return [200, "application/vnd.apple.mpegur", data] + + def e64(self, text): + try: + text_bytes = text.encode('utf-8') + encoded_bytes = b64encode(text_bytes) + return encoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64编码错误: {str(e)}") + return "" + + def d64(self,encoded_text): + try: + encoded_bytes = encoded_text.encode('utf-8') + decoded_bytes = b64decode(encoded_bytes) + return decoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64解码错误: {str(e)}") + return "" + + def md5(self, text): + h = MD5.new() + h.update(text.encode('utf-8')) + return h.hexdigest() + diff --git a/PY/app/火车太顺APP.py b/PY/app/火车太顺APP.py new file mode 100644 index 0000000..6a80179 --- /dev/null +++ b/PY/app/火车太顺APP.py @@ -0,0 +1,301 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +import sys +from urllib.parse import urlparse +sys.path.append("..") +import re +import hashlib +import hmac +import random +import string +from Crypto.Util.Padding import unpad +from concurrent.futures import ThreadPoolExecutor +from Crypto.PublicKey import RSA +from Crypto.Cipher import PKCS1_v1_5, AES +from base64 import b64encode, b64decode +import json +import time +from base.spider import Spider + + +class Spider(Spider): + + def init(self, extend=""): + self.device = self.device_id() + self.host = self.gethost() + pass + + def getName(self): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def action(self, action): + pass + + def destroy(self): + pass + + def homeContent(self, filter): + result = {} + filters = {} + classes = [] + bba = self.url() + data = self.fetch(f"{self.host}/api/v1/app/config?pack={bba[0]}&signature={bba[1]}", headers=self.header()).text + data1 = self.aes(data) + dy = {"class": "类型", "area": "地区", "lang": "语言", "year": "年份", "letter": "字母", "by": "排序", + "sort": "排序"} + data1['data']['movie_screen']['sort'].pop(0) + for item in data1['data']['movie_screen']['sort']: + item['n'] = item.pop('name') + item['v'] = item.pop('value') + for item in data1['data']['movie_screen']['filter']: + has_non_empty_field = False + classes.append({"type_name": item["name"], "type_id": str(item["id"])}) + for key in dy: + if key in item and item[key]: + has_non_empty_field = True + break + if has_non_empty_field: + filters[str(item["id"])] = [] + filters[str(item["id"])].append( + {"key": 'sort', "name": '排序', "value": data1['data']['movie_screen']['sort']}) + for dkey in item: + if dkey in dy and item[dkey]: + item[dkey].pop(0) + value_array = [ + {"n": value.strip(), "v": value.strip()} + for value in item[dkey] + if value.strip() != "" + ] + filters[str(item["id"])].append( + {"key": dkey, "name": dy[dkey], "value": value_array} + ) + result["class"] = classes + result["filters"] = filters + return result + + def homeVideoContent(self): + bba = self.url() + url = f'{self.host}/api/v1/movie/index_recommend?pack={bba[0]}&signature={bba[1]}' + data = self.fetch(url, headers=self.header()).json() + videos = [] + for item in data['data']: + if len(item['list']) > 0: + for it in item['list']: + try: + videos.append(self.voides(it)) + except Exception as e: + continue + result = {"list": videos} + return result + + def categoryContent(self, tid, pg, filter, extend): + body = {"type_id": tid, "sort": extend.get("sort", "by_default"), "class": extend.get("class", "类型"), + "area": extend.get("area", "地区"), "year": extend.get("year", "年份"), "page": str(pg), + "pageSize": "21"} + result = {} + list = [] + bba = self.url(body) + url = f"{self.host}/api/v1/movie/screen/list?pack={bba[0]}&signature={bba[1]}" + data = self.fetch(url, headers=self.header()).json()['data']['list'] + for item in data: + list.append(self.voides(item)) + result["list"] = list + result["page"] = pg + result["pagecount"] = 9999 + result["limit"] = 90 + result["total"] = 999999 + return result + + def detailContent(self, ids): + body = {"id": ids[0]} + bba = self.url(body) + url = f'{self.host}/api/v1/movie/detail?pack={bba[0]}&signature={bba[1]}' + data = self.fetch(url, headers=self.header()).json()['data'] + video = {'vod_name': data.get('name'), 'type_name': data.get('type_name'), 'vod_year': data.get('year'), + 'vod_area': data.get('area'), 'vod_remarks': data.get('dynami'), 'vod_content': data.get('content')} + play = [] + names = [] + tasks = [] + for itt in data["play_from"]: + name = itt["name"] + a = [] + if len(itt["list"]) > 0: + names.append(name) + play.append(self.playeach(itt['list'])) + else: + tasks.append({"movie_id": ids[0], "from_code": itt["code"]}) + names.append(name) + if tasks: + with ThreadPoolExecutor(max_workers=len(tasks)) as executor: + results = executor.map(self.playlist, tasks) + for result in results: + if result: + play.append(result) + else: + play.append("") + video["vod_play_from"] = "$$$".join(names) + video["vod_play_url"] = "$$$".join(play) + result = {"list": [video]} + return result + + def searchContent(self, key, quick, pg=1): + body = {"keyword": key, "sort": "", "type_id": "0", "page": str(pg), "pageSize": "10", + "res_type": "by_movie_name"} + bba = self.url(body) + url = f"{self.host}/api/v1/movie/search?pack={bba[0]}&signature={bba[1]}" + data = self.fetch(url, headers=self.header()).json()['data'].get('list') + videos = [] + for it in data: + try: + videos.append(self.voides(it)) + except Exception as e: + continue + result = {"list": videos, "page": pg} + return result + + def playerContent(self, flag, id, vipFlags): + url = id + if not re.search(r"\.m3u8|\.mp4", url): + try: + data = json.loads(b64decode(id.encode('utf-8')).decode('utf-8')) + bba = self.url(data) + data2 = self.fetch(f"{self.host}/api/v1/movie_addr/parse_url?pack={bba[0]}&signature={bba[1]}", + headers=self.header()).json()['data'] + url = data2.get('play_url') or data2.get('download_url') + except Exception as e: + pass + if re.search(r'\.jpg|\.png|\.jpeg', url): + url = self.Mproxy(url) + result = {} + result["parse"] = 0 + result["url"] = url + result["header"] = {'user-agent': 'okhttp/4.9.2'} + return result + + def localProxy(self, param): + return self.Mlocal(param) + + def Mproxy(self, url): + return self.getProxyUrl() + "&url=" + b64encode(url.encode('utf-8')).decode('utf-8') + "&type=m3u8" + + def Mlocal(self, param,header=None): + url = self.d64(param["url"]) + ydata = self.fetch(url, headers=header, allow_redirects=False) + data = ydata.content.decode('utf-8') + if ydata.headers.get('Location'): + url = ydata.headers['Location'] + data = self.fetch(url, headers=header).content.decode('utf-8') + parsed_url = urlparse(url) + durl = parsed_url.scheme + "://" + parsed_url.netloc + lines = data.strip().split('\n') + for index, string in enumerate(lines): + if '#EXT' not in string and 'http' not in string: + last_slash_index = string.rfind('/') + lpath = string[:last_slash_index + 1] + lines[index] = durl + ('' if lpath.startswith('/') else '/') + lpath + data = '\n'.join(lines) + return [200, "application/vnd.apple.mpegur", data] + + def device_id(self): + characters = string.ascii_lowercase + string.digits + random_string = ''.join(random.choices(characters, k=32)) + return random_string + + def gethost(self): + try: + url = 'https://dns.alidns.com/dns-query' + headers = { + 'User-Agent': 'okhttp/4.9.2', + 'Accept': 'application/dns-message' + } + params = { + 'dns': 'AAABAAABAAAAAAAACWJmbTExYXM5ZgdmdXFpeXVuAmNuAAAcAAE' + } + response = self.fetch(url, headers=headers, params=params) + host=self.parse_dns_name(response.content, 12) + return f"https://{host}" + except: + return "https://bfm11as9f.fuqiyun.cn" + + def parse_dns_name(self, data, offset): + parts = [] + while True: + length = data[offset] + if length == 0: + break + offset += 1 + parts.append(data[offset:offset + length].decode('utf-8')) + offset += length + return '.'.join(parts) + + def header(self): + headers = { + 'User-Agent': 'Android', + 'Accept': 'application/prs.55App.v2+json', + 'timestamp': str(int(time.time())), + 'x-client-setting': '{"pure-mode":0}', + 'x-client-uuid': '{"device_id":' + self.device + '}, "type":1,"brand":"Redmi", "model":"M2012K10C", "system_version":30, "sdk_version":"3.1.0.7"}', + 'x-client-version': '3096 ' + } + return headers + + def url(self, id=None): + if not id: + id = {} + id["timestamp"] = str(int(time.time())) + public_key = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA02F/kPg5A2NX4qZ5JSns+bjhVMCC6JbTiTKpbgNgiXU+Kkorg6Dj76gS68gB8llhbUKCXjIdygnHPrxVHWfzmzisq9P9awmXBkCk74Skglx2LKHa/mNz9ivg6YzQ5pQFUEWS0DfomGBXVtqvBlOXMCRxp69oWaMsnfjnBV+0J7vHbXzUIkqBLdXSNfM9Ag5qdRDrJC3CqB65EJ3ARWVzZTTcXSdMW9i3qzEZPawPNPe5yPYbMZIoXLcrqvEZnRK1oak67/ihf7iwPJqdc+68ZYEmmdqwunOvRdjq89fQMVelmqcRD9RYe08v+xDxG9Co9z7hcXGTsUquMxkh29uNawIDAQAB' + encrypted_text = json.dumps(id) + public_key = RSA.import_key(b64decode(public_key)) + cipher = PKCS1_v1_5.new(public_key) + encrypted_message = cipher.encrypt(encrypted_text.encode('utf-8')) + encrypted_message_base64 = b64encode(encrypted_message).decode('utf-8') + result = encrypted_message_base64.replace('+', '-').replace('/', '_').replace('=', '') + key = '635a580fcb5dc6e60caa39c31a7bde48' + sign = hmac.new(key.encode(), result.encode(), hashlib.md5).hexdigest() + return result, sign + + def playlist(self, body): + try: + bba = self.url(body) + url = f'{self.host}/api/v1/movie_addr/list?pack={bba[0]}&signature={bba[1]}' + data = self.fetch(url, headers=self.header()).json()['data'] + return self.playeach(data) + except Exception: + return [] + + def playeach(self, data): + play_urls = [] + for it in data: + if re.search(r"mp4|m3u8", it["play_url"]): + play_urls.append(f"{it['episode_name']}${it['play_url']}") + else: + vd={"from_code": it['from_code'], "play_url": it['play_url'], "episode_id": it['episode_id'], "type": "play"} + play_urls.append( + f"{it['episode_name']}${b64encode(json.dumps(vd).encode('utf-8')).decode('utf-8')}" + ) + return '#'.join(play_urls) + + def voides(self, item): + if item['name'] or item['title']: + voide = { + "vod_id": item.get('id') or item.get('click'), + 'vod_name': item.get('name') or item.get('title'), + 'vod_pic': item.get('cover') or item.get('image'), + 'vod_year': item.get('year') or item.get('label'), + 'vod_remarks': item.get('dynamic') or item.get('sub_title') + } + return voide + + def aes(self, text): + text = text.replace('-', '+').replace('_', '/') + '==' + key = b"e6d5de5fcc51f53d" + iv = b"2f13eef7dfc6c613" + cipher = AES.new(key, AES.MODE_CBC, iv) + pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size).decode("utf-8") + return json.loads(pt) diff --git a/PY/app/爱瓜TVAPP.py b/PY/app/爱瓜TVAPP.py new file mode 100644 index 0000000..e3b1e51 --- /dev/null +++ b/PY/app/爱瓜TVAPP.py @@ -0,0 +1,166 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +# 温馨提示:搜索只能搜拼音联想 +# 播放需要挂代理 +import sys +import time +import uuid +from Crypto.Hash import MD5 +sys.path.append('..') +from base.spider import Spider + + +class Spider(Spider): + + def init(self, extend=""): + self.uid = self.getuid() + self.token, self.code = self.getuserinfo() + pass + + def getName(self): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + host = 'https://tvapi211.magicetech.com' + + headers = {'User-Agent': 'okhttp/3.11.0'} + + def homeContent(self, filter): + body = {'token': self.token, 'authcode': self.code} + data = self.post(f'{self.host}/hr_1_1_0/apptvapi/web/index.php/video/filter-header', json=self.getbody(body), + headers=self.headers).json() + result = {} + classes = [] + filters = {} + for k in data['data']: + classes.append({ + 'type_name': k['channel_name'], + 'type_id': str(k['channel_id']), + }) + filters[str(k['channel_id'])] = [] + for i in k['search_box']: + if len(i['list']): + filters[str(k['channel_id'])].append({ + 'key': i['field'], + 'name': i['label'], + 'value': [{'n': j['display'], 'v': str(j['value'])} for j in i['list'] if j['value']] + }) + result['class'] = classes + result['filters'] = filters + return result + + def homeVideoContent(self): + body = {'token': self.token, 'authcode': self.code} + data = self.post(f'{self.host}/hr_1_1_0/apptvapi/web/index.php/video/index-tv', json=self.getbody(body), + headers=self.headers).json() + return {'list': self.getlist(data['data'][0]['banner'])} + + def categoryContent(self, tid, pg, filter, extend): + body = {'token': self.token, 'authcode': self.code, 'channel_id': tid, 'area': extend.get('area', '0'), + 'year': extend.get('year', '0'), 'sort': extend.get('sort', '0'), 'tag': extend.get('tag', 'hot'), + 'status': extend.get('status', '0'), 'page_num': pg, 'page_size': '24'} + data = self.post(f'{self.host}/hr_1_1_0/apptvapi/web/index.php/video/filter-video', json=self.getbody(body), + headers=self.headers).json() + result = {} + result['list'] = self.getlist(data['data']['list']) + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + ids = ids[0].split('@') + body = {'token': self.token, 'authcode': self.code, 'channel_id': ids[0], 'video_id': ids[1]} + data = self.post(f'{self.host}/hr_1_1_0/apptvapi/web/index.php/video/detail', json=self.getbody(body), + headers=self.headers).json() + vdata = {} + for k in data['data']['chapters']: + i = k['sourcelist'] + for j in i: + if j['source_name'] not in vdata: vdata[j['source_name']] = [] + vdata[j['source_name']].append(f"{k['title']}${j['source_url']}") + plist, names = [], [] + for key, value in vdata.items(): + names.append(key) + plist.append('#'.join(value)) + vod = { + 'vod_play_from': '$$$'.join(names), + 'vod_play_url': '$$$'.join(plist), + } + return {'list': [vod]} + + def searchContent(self, key, quick, pg="1"): + body = {'token': self.token, 'authcode': self.code, 'keyword': key, 'page_num': pg} + data = self.post(f'{self.host}/hr_1_1_0/apptvapi/web/index.php/search/letter-result', json=self.getbody(body), + headers=self.headers).json() + return {'list': self.getlist(data['data']['list'])} + + def playerContent(self, flag, id, vipFlags): + # https://rysp.tv + # https://aigua.tv + result = { + "parse": 0, + "url": id, + "header": { + "User-Agent": "Dalvik/2.1.0 (Linux; U; Android 11; M2012K10C Build/RP1A.200720.011)", + "Origin": "https://aigua.tv", + "Referer": "https://aigua.tv/" + } + } + return result + + def localProxy(self, param): + pass + + def getuserinfo(self): + data = self.post(f'{self.host}/hr_1_1_0/apptvapi/web/index.php/user/auth-login', json=self.getbody(), + headers=self.headers).json() + v = data['data'] + return v['user_token'], v['authcode'] + + def getuid(self): + uid = self.getCache('uid') + if not uid: + uid = str(uuid.uuid4()) + self.setCache('uid', uid) + return uid + + def getbody(self, json_data=None): + if json_data is None: json_data = {} + params = {"product": "4", "ver": "1.1.0", "debug": "1", "appId": "1", "osType": "3", "marketChannel": "tv", + "sysVer": "11", "time": str(int(time.time())), "packageName": "com.gzsptv.gztvvideo", + "udid": self.uid, } + json_data.update(params) + sorted_json = dict(sorted(json_data.items(), key=lambda item: item[0])) + text = '&'.join(f"{k}={v}" for k, v in sorted_json.items() if v != '') + md5_hash = self.md5(f"jI7POOBbmiUZ0lmi{text}D9ShYdN51ksWptpkTu11yenAJu7Zu3cR").upper() + json_data.update({'sign': md5_hash}) + return json_data + + def md5(self, text): + h = MD5.new() + h.update(text.encode('utf-8')) + return h.hexdigest() + + def getlist(self, data): + videos = [] + for i in data: + if type(i.get('video')) == dict: i = i['video'] + videos.append({ + 'vod_id': f"{i.get('channel_id')}@{i.get('video_id')}", + 'vod_name': i.get('video_name'), + 'vod_pic': i.get('cover'), + 'vod_year': i.get('score'), + 'vod_remarks': i.get('flag'), + }) + return videos + diff --git a/PY/app/皮皮虾APP.py b/PY/app/皮皮虾APP.py new file mode 100644 index 0000000..6d8c14d --- /dev/null +++ b/PY/app/皮皮虾APP.py @@ -0,0 +1,211 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +import re +import sys +from Crypto.Hash import MD5 +sys.path.append("..") +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad +from urllib.parse import quote, urlparse +from base64 import b64encode, b64decode +import json +import time +from base.spider import Spider + +class Spider(Spider): + + def init(self, extend=""): + self.host = "http://ppx.bjx365.top" + pass + + def getName(self): + pass + + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def action(self, action): + pass + + def destroy(self): + pass + + def homeContent(self, filter): + data = self.getdata("/api.php/getappapi.index/initV119") + dy = {"class": "类型", "area": "地区", "lang": "语言", "year": "年份", "letter": "字母", "by": "排序", + "sort": "排序"} + filters = {} + classes = [] + json_data = data["type_list"] + homedata = data["banner_list"][8:] + for item in json_data: + if item["type_name"] == "全部": + continue + has_non_empty_field = False + jsontype_extend = json.loads(item["type_extend"]) + homedata.extend(item["recommend_list"]) + jsontype_extend["sort"] = "最新,最热,最赞" + classes.append({"type_name": item["type_name"], "type_id": item["type_id"]}) + for key in dy: + if key in jsontype_extend and jsontype_extend[key].strip() != "": + has_non_empty_field = True + break + if has_non_empty_field: + filters[str(item["type_id"])] = [] + for dkey in jsontype_extend: + if dkey in dy and jsontype_extend[dkey].strip() != "": + values = jsontype_extend[dkey].split(",") + value_array = [{"n": value.strip(), "v": value.strip()} for value in values if + value.strip() != ""] + filters[str(item["type_id"])].append({"key": dkey, "name": dy[dkey], "value": value_array}) + result = {} + result["class"] = classes + result["filters"] = filters + result["list"] = homedata[1:] + return result + + def homeVideoContent(self): + pass + + def categoryContent(self, tid, pg, filter, extend): + body = {"area": extend.get('area', '全部'), "year": extend.get('year', '全部'), "type_id": tid, "page": pg, + "sort": extend.get('sort', '最新'), "lang": extend.get('lang', '全部'), + "class": extend.get('class', '全部')} + result = {} + data = self.getdata("/api.php/getappapi.index/typeFilterVodList", body) + result["list"] = data["recommend_list"] + result["page"] = pg + result["pagecount"] = 9999 + result["limit"] = 90 + result["total"] = 999999 + return result + + def detailContent(self, ids): + body = f"vod_id={ids[0]}" + data = self.getdata("/api.php/getappapi.index/vodDetail", body) + vod = data["vod"] + play = [] + names = [] + for itt in data["vod_play_list"]: + a = [] + names.append(itt["player_info"]["show"]) + for it in itt['urls']: + it['user_agent']=itt["player_info"].get("user_agent") + it["parse"]=itt["player_info"].get("parse") + a.append(f"{it['name']}${self.e64(json.dumps(it))}") + play.append("#".join(a)) + vod["vod_play_from"] = "$$$".join(names) + vod["vod_play_url"] = "$$$".join(play) + result = {"list": [vod]} + return result + + def searchContent(self, key, quick, pg="1"): + body = f"keywords={key}&type_id=0&page={pg}" + data = self.getdata("/api.php/getappapi.index/searchList", body) + result = {"list": data["search_list"], "page": pg} + return result + + def playerContent(self, flag, id, vipFlags): + ids = json.loads(self.d64(id)) + h={"User-Agent": (ids['user_agent'] or "okhttp/3.14.9")} + url = ids['url'] + p=1 + try: + if re.search(r'\?url=', ids['parse_api_url']): + data=self.fetch(ids['parse_api_url'], headers=h, timeout=10).json() + url=data.get('url') or data['data'].get('url') + elif not re.search(r'\.m3u8|\.mp4', ids.get('url')): + body = f"parse_api={ids.get('parse') or ids['parse_api_url'].replace(ids['url'], '')}&url={quote(self.aes('encrypt', ids['url']))}&token={ids.get('token')}" + b = self.getdata("/api.php/getappapi.index/vodParse", body)['json'] + url = json.loads(b)['url'] + p=0 + except Exception as e: + print('错误信息:',e) + pass + if re.search(r'\.jpg|\.png|\.jpeg', url): + url = self.Mproxy(url) + result = {} + result["parse"] = p + result["url"] = url + result["header"] = h + return result + + def localProxy(self, param): + return self.Mlocal(param) + + def aes(self, operation, text): + key = "pipixia217522324".encode("utf-8") + iv = key + if operation == "encrypt": + cipher = AES.new(key, AES.MODE_CBC, iv) + ct_bytes = cipher.encrypt(pad(text.encode("utf-8"), AES.block_size)) + ct = b64encode(ct_bytes).decode("utf-8") + return ct + elif operation == "decrypt": + cipher = AES.new(key, AES.MODE_CBC, iv) + pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size) + return pt.decode("utf-8") + + def header(self): + t = str(int(time.time())) + header = {"Referer":self.host, + "User-Agent": "okhttp/3.14.9", "app-version-code": "300", "app-ui-mode": "light", + "app-api-verify-time": t, "app-user-device-id": self.md5(t), + "app-api-verify-sign": self.aes("encrypt", t), + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"} + return header + + def getdata(self, path, data=None): + vdata = self.post(f"{self.host}{path}", headers=self.header(), data=data, timeout=10).json()['data'] + data1 = self.aes("decrypt", vdata) + return json.loads(data1) + + def Mproxy(self, url): + return self.getProxyUrl() + "&url=" + b64encode(url.encode('utf-8')).decode('utf-8') + "&type=m3u8" + + def Mlocal(self, param,header=None): + url = self.d64(param["url"]) + ydata = self.fetch(url, headers=header, allow_redirects=False) + data = ydata.content.decode('utf-8') + if ydata.headers.get('Location'): + url = ydata.headers['Location'] + data = self.fetch(url, headers=header).content.decode('utf-8') + parsed_url = urlparse(url) + durl = parsed_url.scheme + "://" + parsed_url.netloc + lines = data.strip().split('\n') + for index, string in enumerate(lines): + if '#EXT' not in string and 'http' not in string: + last_slash_index = string.rfind('/') + lpath = string[:last_slash_index + 1] + lines[index] = durl + ('' if lpath.startswith('/') else '/') + lpath + data = '\n'.join(lines) + return [200, "application/vnd.apple.mpegur", data] + + def e64(self, text): + try: + text_bytes = text.encode('utf-8') + encoded_bytes = b64encode(text_bytes) + return encoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64编码错误: {str(e)}") + return "" + + def d64(self,encoded_text): + try: + encoded_bytes = encoded_text.encode('utf-8') + decoded_bytes = b64decode(encoded_bytes) + return decoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64解码错误: {str(e)}") + return "" + + def md5(self, text): + h = MD5.new() + h.update(text.encode('utf-8')) + return h.hexdigest() + + diff --git a/PY/app/美帕APP.py b/PY/app/美帕APP.py new file mode 100644 index 0000000..bcb3a51 --- /dev/null +++ b/PY/app/美帕APP.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +import sys +sys.path.append('..') +from base.spider import Spider + + +class Spider(Spider): + def getName(self): + return "mp" + + def init(self, extend=""): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + host = 'https://g.c494.com' + + header = { + 'User-Agent': 'Dart/2.10 (dart:io)', + 'platform_version': 'RP1A.200720.011', + 'version': '2.2.3', + 'copyright': 'xiaogui', + 'platform': 'android', + 'client_name': '576O5p+P5b2x6KeG', + } + + def homeContent(self, filter): + data = self.fetch(f'{self.host}/api.php/app/nav?token=', headers=self.header).json() + dy = {"class": "类型", "area": "地区", "lang": "语言", "year": "年份", "letter": "字母", "by": "排序", + "sort": "排序"} + filters = {} + classes = [] + json_data = data["list"] + for item in json_data: + has_non_empty_field = False + jsontype_extend = item["type_extend"] + classes.append({"type_name": item["type_name"], "type_id": str(item["type_id"])}) + for key in dy: + if key in jsontype_extend and jsontype_extend[key].strip() != "": + has_non_empty_field = True + break + if has_non_empty_field: + filters[str(item["type_id"])] = [] + for dkey in jsontype_extend: + if dkey in dy and jsontype_extend[dkey].strip() != "": + values = jsontype_extend[dkey].split(",") + value_array = [{"n": value.strip(), "v": value.strip()} for value in values if + value.strip() != ""] + filters[str(item["type_id"])].append({"key": dkey, "name": dy[dkey], "value": value_array}) + result = {} + result["class"] = classes + result["filters"] = filters + return result + + def homeVideoContent(self): + rsp = self.fetch(f"{self.host}/api.php/app/index_video?token=", headers=self.header) + root = rsp.json()['list'] + videos = [item for vodd in root for item in vodd['vlist']] + return {'list': videos} + + def categoryContent(self, tid, pg, filter, extend): + parms = {"pg": pg, "tid": tid, "class": extend.get("class", ""), "area": extend.get("area", ""), + "lang": extend.get("lang", ""), "year": extend.get("year", ""), "token": ""} + data = self.fetch(f'{self.host}/api.php/app/video', params=parms, headers=self.header).json() + return data + + def detailContent(self, ids): + parms = {"id": ids[0], "token": ""} + data = self.fetch(f'{self.host}/api.php/app/video_detail', params=parms, headers=self.header).json() + vod = data['data'] + vod.pop('pause_advert_list', None) + vod.pop('init_advert_list', None) + vod.pop('vod_url_with_player', None) + return {"list": [vod]} + + def searchContent(self, key, quick, pg='1'): + parms = {'pg': pg, 'text': key, 'token': ''} + data = self.fetch(f'{self.host}/api.php/app/search', params=parms, headers=self.header).json() + return data + + def playerContent(self, flag, id, vipFlags): + return {"parse": 0, "url": id, "header": {'User-Agent': 'User-Agent: Lavf/58.12.100'}} + + def localProxy(self, param): + pass diff --git a/PY/app/胖虎APP.py b/PY/app/胖虎APP.py new file mode 100644 index 0000000..a76035f --- /dev/null +++ b/PY/app/胖虎APP.py @@ -0,0 +1,215 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +import re +import sys +sys.path.append('..') +from base.spider import Spider +from Cryptodome.Cipher import AES +from Cryptodome.Util.Padding import pad, unpad +from base64 import b64encode, b64decode +import json +import time + + +class Spider(Spider): + def getName(self): + return "py_胖虎" + + def init(self, extend=""): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + def aes(self, operation, text): + key = "ihIwTbt2YAe9TGea".encode('utf-8') + iv = key + + if operation == 'encrypt': + cipher = AES.new(key, AES.MODE_CBC, iv) + ct_bytes = cipher.encrypt(pad(text.encode('utf-8'), AES.block_size)) + ct = b64encode(ct_bytes).decode('utf-8') + return ct + elif operation == 'decrypt': + cipher = AES.new(key, AES.MODE_CBC, iv) + pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size) + return pt.decode('utf-8') + + host = "http://sm.physkan.top:3389" + t = str(int(time.time())) + + def homeContent(self, filter): + self.header = { + 'User-Agent': 'okhttp/3.14.9', + 'app-version-code': '402', + 'app-ui-mode': 'light', + 'app-user-device-id': '25f869d32598d3d3089a929453dff0bb7', + 'app-api-verify-time': self.t, + 'app-api-verify-sign': self.aes('encrypt', self.t), + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' + } + data = self.fetch("{0}/api.php/getappapi.index/initV119".format(self.host), headers=self.header).content.decode( + 'utf-8') + data1 = json.loads(data)['data'] + print(data1) + data2 = self.aes('decrypt', data1) + dy = { + "class": "类型", + "area": "地区", + "lang": "语言", + "year": "年份", + "letter": "字母", + "by": "排序", + "sort": "排序" + } + + filter = {} + classes = [] + json_data = json.loads(data2)['type_list'] + self.homedata = json.loads(data2)['banner_list'] + + for item in json_data: + if item['type_name'] == '全部': + continue + + has_non_empty_field = False + jsontype_extend = json.loads(item['type_extend']) + jsontype_extend["sort"] = "最新,最热,最赞" + + classes.append({ + "type_name": item['type_name'], + "type_id": item['type_id'] + }) + + for key in dy: + if key in jsontype_extend and jsontype_extend[key].strip() != "": + has_non_empty_field = True + break + + if has_non_empty_field: + filter[str(item['type_id'])] = [] + + for dkey in jsontype_extend: + if dkey in dy and jsontype_extend[dkey].strip() != "": + values = jsontype_extend[dkey].split(',') + value_array = [ + {"n": value.strip(), "v": value.strip()} + for value in values if value.strip() != '' + ] + + filter[str(item['type_id'])].append({ + "key": dkey, + "name": dy[dkey], + "value": value_array + }) + result = {} + result['class'] = classes + result['filter'] = filter + return result + + def homeVideoContent(self): + result = { + 'list': self.homedata + } + return result + + def categoryContent(self, tid, pg, filter, extend): + body = f"area={extend.get('area', '全部')}&year={extend.get('year', '全部')}&type_id={tid}&page={pg}&sort={extend.get('sort', '最新')}&lang={extend.get('lang', '全部')}&class={extend.get('class', '全部')}" + result = {} + url = '{0}/api.php/getappapi.index/typeFilterVodList'.format(self.host) + data = self.post(url, headers=self.header, data=body).content.decode('utf-8') + data1 = json.loads(data)['data'] + data2 = self.aes('decrypt', data1) + result['list'] = json.loads(data2)['recommend_list'] + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + body = f"vod_id={ids[0]}" + print(body) + url = '{0}/api.php/getappapi.index/vodDetail'.format(self.host) + data = self.post(url, headers=self.header, data=body).content.decode('utf-8') + data1 = json.loads(data)['data'] + data2 = json.loads(self.aes('decrypt', data1)) + print(data2) + vod = data2['vod'] + print(vod) + play = [] + names = [] + for itt in data2['vod_play_list']: + a = [] + names.append(itt['player_info']['show']) + parse = itt['player_info']['parse'] + for it in itt['urls']: + if re.search(r'mp4|m3u8', it['url']): + a.append(f"{it['name']}${it['url']}") + elif re.search(r'www.yemu.xyz', it['parse_api_url']): + a.append(f"{it['name']}${it['parse_api_url']}") + else: + a.append( + f"{it['name']}${'parse_api=' + parse + '&url=' + self.aes('encrypt', it['url']) + '&token=' + it['token']}") + play.append('#'.join(a)) + vod['vod_play_from'] = '$$$'.join(names) + vod['vod_play_url'] = '$$$'.join(play) + result = { + 'list': [ + vod + ] + } + return result + + def searchContent(self, key, quick, pg='1'): + body = f"keywords={key}&type_id=0&page={pg}" + url = '{0}/api.php/getappapi.index/searchList'.format(self.host) + data = self.post(url, headers=self.header, data=body).content.decode('utf-8') + data1 = json.loads(data)['data'] + data2 = self.aes('decrypt', data1) + result = { + 'list': json.loads(data2)['search_list'] + } + return result + + def playerContent(self, flag, id, vipFlags): + def edu(str): + def replacer(match): + from urllib.parse import quote_plus + return match.group(1) + quote_plus(match.group(2)) + match.group(3) + + return re.sub(r'(url=)(.*?)(&token)', replacer, str) + + url = id + parse = 0 + if 'm3u8' not in url and 'mp4' not in url: + try: + body = edu(url) + print(body) + data = self.post('{0}/api.php/getappapi.index/vodParse'.format(self.host), headers=self.header, + data=body).content.decode('utf-8') + data1 = json.loads(data)['data'] + data2 = json.loads(self.aes('decrypt', data1))['json'] + url = json.loads(data2)['url'] + except: + url = id + parse = 1 + if not id.startswith('https://www.yemu.xyz'): + url = 'https://www.yemu.xyz/?url={0}'.format(id) + result = {} + print(url) + headers = self.header.copy() + del headers['Content-type'] + result["parse"] = parse + result["url"] = url + result["header"] = headers + return result + + def localProxy(self, param): + pass diff --git a/PY/app/若惜追剧APP.py b/PY/app/若惜追剧APP.py new file mode 100644 index 0000000..1b33d5d --- /dev/null +++ b/PY/app/若惜追剧APP.py @@ -0,0 +1,255 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +import re +import sys +from Crypto.Hash import MD5 +sys.path.append("..") +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad +from urllib.parse import quote, urlparse +from base64 import b64encode, b64decode +from concurrent.futures import ThreadPoolExecutor +import json +import time +from base.spider import Spider + +class Spider(Spider): + + ''' + sites照常配置, + lives配置: + { + "name": "xxxx", + "type": 3, + "api": "路径/若惜追剧APP.py", + "ext": "" + } + ''' + + def init(self, extend=""): + self.host = self.gethost() + pass + + def getName(self): + pass + + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def action(self, action): + pass + + def destroy(self): + pass + + def homeContent(self, filter): + data = self.getdata("/api.php/getappapi.index/initV119") + dy = {"class": "类型", "area": "地区", "lang": "语言", "year": "年份", "letter": "字母", "by": "排序", + "sort": "排序"} + filters = {} + classes = [] + json_data = data["type_list"] + homedata = data["banner_list"][8:] + for item in json_data: + if item["type_name"] == "全部": + continue + has_non_empty_field = False + jsontype_extend = json.loads(item["type_extend"]) + homedata.extend(item["recommend_list"]) + jsontype_extend["sort"] = "最新,最热,最赞" + classes.append({"type_name": item["type_name"], "type_id": item["type_id"]}) + for key in dy: + if key in jsontype_extend and jsontype_extend[key].strip() != "": + has_non_empty_field = True + break + if has_non_empty_field: + filters[str(item["type_id"])] = [] + for dkey in jsontype_extend: + if dkey in dy and jsontype_extend[dkey].strip() != "": + values = jsontype_extend[dkey].split(",") + value_array = [{"n": value.strip(), "v": value.strip()} for value in values if + value.strip() != ""] + filters[str(item["type_id"])].append({"key": dkey, "name": dy[dkey], "value": value_array}) + result = {} + result["class"] = classes + result["filters"] = filters + result["list"] = homedata[1:] + return result + + def homeVideoContent(self): + pass + + def categoryContent(self, tid, pg, filter, extend): + body = {"area": extend.get('area', '全部'), "year": extend.get('year', '全部'), "type_id": tid, "page": pg, + "sort": extend.get('sort', '最新'), "lang": extend.get('lang', '全部'), + "class": extend.get('class', '全部')} + result = {} + data = self.getdata("/api.php/getappapi.index/typeFilterVodList", body) + result["list"] = data["recommend_list"] + result["page"] = pg + result["pagecount"] = 9999 + result["limit"] = 90 + result["total"] = 999999 + return result + + def detailContent(self, ids): + body = f"vod_id={ids[0]}" + data = self.getdata("/api.php/getappapi.index/vodDetail", body) + vod = data["vod"] + play = [] + names = [] + for itt in data["vod_play_list"]: + a = [] + names.append(itt["player_info"]["show"]) + for it in itt['urls']: + it['user_agent']=itt["player_info"].get("user_agent") + it["parse"]=itt["player_info"].get("parse") + a.append(f"{it['name']}${self.e64(json.dumps(it))}") + play.append("#".join(a)) + vod["vod_play_from"] = "$$$".join(names) + vod["vod_play_url"] = "$$$".join(play) + result = {"list": [vod]} + return result + + def searchContent(self, key, quick, pg="1"): + body = f"keywords={key}&type_id=0&page={pg}" + data = self.getdata("/api.php/getappapi.index/searchList", body) + result = {"list": data["search_list"], "page": pg} + return result + + def playerContent(self, flag, id, vipFlags): + ids = json.loads(self.d64(id)) + h={"User-Agent": (ids['user_agent'] or "okhttp/3.14.9")} + try: + if re.search(r'url=', ids['parse_api_url']): + data=self.fetch(ids['parse_api_url'], headers=h, timeout=10).json() + url=data.get('url') or data['data'].get('url') + else: + body = f"parse_api={ids.get('parse') or ids['parse_api_url'].replace(ids['url'], '')}&url={quote(self.aes(ids['url'],True))}&token={ids.get('token')}" + b = self.getdata("/api.php/getappapi.index/vodParse", body)['json'] + url = json.loads(b)['url'] + if 'error' in url:raise ValueError(f"解析失败: {url}") + p=0 + except Exception as e: + print('错误信息:',e) + url, p = ids['url'], 1 + + if re.search(r'\.jpg|\.png|\.jpeg', url): + url = self.Mproxy(url) + result = {} + result["parse"] = p + result["url"] = url + result["header"] = h + return result + + def liveContent(self, url): + id=self.homeContent(True)['class'][-1]['type_id'] + vlist=self.categoryContent(id,1,False,{})['list'] + results = [] + with ThreadPoolExecutor(max_workers=len(vlist)) as executor: + futures = [executor.submit(self.livedetailContent, item['vod_name'], item['vod_id']) for item in vlist] + for future in futures: + try: + detail = future.result() + if detail: + results.append(detail) + except Exception as e: + print(f"处理详情数据失败: {str(e)}") + return '\n'.join(results) + + def livedetailContent(self, name,id): + try: + print(f"获取直播源:{name}") + body = f"vod_id={id}" + data = self.getdata("/api.php/getappapi.index/vodDetail", body) + play = [f"{name},#genre#"] + for itt in data["vod_play_list"]: + for it in itt['urls']: + play.append(f"{it['name']}, {it['url']}") + except Exception as e: + print(f"获取直播源失败:{str(e)}") + play=[] + return '\n'.join(play) + + def localProxy(self, param): + return self.Mlocal(param) + + def gethost(self): + headers = { + 'User-Agent': 'okhttp/3.14.9' + } + host = self.fetch('https://rxysyyds.oss-cn-chengdu.aliyuncs.com/getapp.txt', headers=headers).text + return host.strip() + + def aes(self, text,b=None): + key = b"ebad3f1a58b13933" + cipher = AES.new(key, AES.MODE_CBC, key) + if b: + ct_bytes = cipher.encrypt(pad(text.encode("utf-8"), AES.block_size)) + ct = b64encode(ct_bytes).decode("utf-8") + return ct + else : + pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size) + return pt.decode("utf-8") + + def header(self): + t = str(int(time.time())) + header = {"Referer":self.host, + "User-Agent": "okhttp/3.14.9", "app-version-code": "140", "app-ui-mode": "light", + "app-api-verify-time": t, "app-user-device-id": self.md5(t), + "app-api-verify-sign": self.aes(t,True), + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"} + return header + + def getdata(self, path, data=None): + vdata = self.post(f"{self.host}{path}", headers=self.header(), data=data, timeout=10).json()['data'] + data1 = self.aes(vdata) + return json.loads(data1) + + def Mproxy(self, url): + return f"{self.getProxyUrl()}&url={self.e64(url)}&type=m3u8" + + def Mlocal(self, param,header=None): + url = self.d64(param["url"]) + ydata = self.fetch(url, headers=header, allow_redirects=False) + data = ydata.content.decode('utf-8') + if ydata.headers.get('Location'): + url = ydata.headers['Location'] + data = self.fetch(url, headers=header).content.decode('utf-8') + parsed_url = urlparse(url) + durl = parsed_url.scheme + "://" + parsed_url.netloc + lines = data.strip().split('\n') + for index, string in enumerate(lines): + if '#EXT' not in string and 'http' not in string: + last_slash_index = string.rfind('/') + lpath = string[:last_slash_index + 1] + lines[index] = durl + ('' if lpath.startswith('/') else '/') + lpath + data = '\n'.join(lines) + return [200, "application/vnd.apple.mpegur", data] + + def e64(self, text): + try: + text_bytes = text.encode('utf-8') + encoded_bytes = b64encode(text_bytes) + return encoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64编码错误: {str(e)}") + return "" + + def d64(self,encoded_text): + try: + encoded_bytes = encoded_text.encode('utf-8') + decoded_bytes = b64decode(encoded_bytes) + return decoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64解码错误: {str(e)}") + return "" + + def md5(self, text): + h = MD5.new() + h.update(text.encode('utf-8')) + return h.hexdigest() diff --git a/PY/app/视觉APP.py b/PY/app/视觉APP.py new file mode 100644 index 0000000..8d6df57 --- /dev/null +++ b/PY/app/视觉APP.py @@ -0,0 +1,239 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +import sys +sys.path.append("..") +import re +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad +from base64 import b64encode, b64decode +import json +from base.spider import Spider +from urllib.parse import quote + + +class Spider(Spider): + + def getName(self): + return "视觉" + + def init(self, extend=""): + self.host = self.host() + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def action(self, action): + pass + + def destroy(self): + pass + + def homeContent(self, filter): + data = self.fetch( + f"{self.host}/api/v3/drama/getCategory?orderBy=type_id", + headers=self.headers, + ).json() + dy = { + "class": "类型", + "area": "地区", + "lang": "语言", + "year": "年份", + "letter": "字母", + "by": "排序", + "sort": "排序", + } + filters = {} + classes = [] + for item in data["data"]: + has_non_empty_field = False + jsontype_extend = json.loads(item["converUrl"]) + classes.append({"type_name": item["name"], "type_id": str(item["id"])}) + for key in dy: + if key in jsontype_extend and jsontype_extend[key].strip() != "": + has_non_empty_field = True + break + if has_non_empty_field: + filters[str(item["id"])] = [] + for dkey in jsontype_extend: + if dkey in dy and jsontype_extend[dkey].strip() != "": + values = jsontype_extend[dkey].split(",") + value_array = [ + {"n": value.strip(), "v": value.strip()} + for value in values + if value.strip() != "" + ] + filters[str(item["id"])].append( + {"key": dkey, "name": dy[dkey], "value": value_array} + ) + result = {} + result["class"] = classes + result["filters"] = filters + return result + + def homeVideoContent(self): + data = self.fetch(f"{self.host}/api/ex/v3/security/tag/list", headers=self.headers).json()["data"] + data1 = self.aes(self.aes(data, self.key[0]), self.key[1], 'decrypt', True) + list = [] + for item in data1[0]['carousels']: + id = item['link'].split("id=")[1] + list.append({ + "vod_id": id, + 'vod_name': item.get("title"), + 'vod_pic': item.get("cover"), + 'vod_remarks': item.get("sort"), + }) + result = {"list": list} + return result + + def categoryContent(self, tid, pg, filter, extend): + params = [] + if extend.get('area'): + params.append(f"vodArea={extend['area']}") + if extend.get('classs'): + params.append(f"vodClass={extend['class']}") + params.append("pagesize=20") + params.append(f"typeId1={tid}") + params.append(f"page={pg}") + if extend.get('year'): + params.append(f"vodYear={extend['year']}") + body = '&'.join(params) + path = self.aes(self.aes(body, self.key[1], 'encrypt'), self.key[0], 'encrypt', True) + data = self.fetch(f"{self.host}/api/ex/v3/security/drama/list?query={path}", headers=self.headers).json()[ + "data"] + data = self.aes(self.aes(data, self.key[0]), self.key[1], 'decrypt', True)['list'] + list = [] + for item in data: + list.append({ + 'vod_id': item.get("id"), + 'vod_pic': item["coverImage"].get("path"), + 'vod_name': item.get("name"), + 'vod_year': item.get("year"), + 'vod_remarks': item.get("remark") + }) + result = {} + result["list"] = list + result["page"] = pg + result["pagecount"] = 9999 + result["limit"] = 90 + result["total"] = 999999 + return result + + def detailContent(self, ids): + url = f"{self.host}/api/v3/drama/getDetail?id={ids[0]}" + data = self.post(url, headers=self.headers).json()["data"] + vod = { + 'vod_name': data.get("name"), + 'vod_area': data.get("area"), + 'type_name': data.get("clazz"), + 'vod_actor': data.get("actor"), + 'vod_director': data.get("director"), + 'vod_content': data.get("brief").strip(), + } + play = [] + names = [] + plays = {} + for itt in data["videos"]: + if itt["sourceCn"] not in names: + plays[itt["source"]] = [] + names.append(itt["sourceCn"]) + url = f"vodPlayFrom={itt['source']}&playUrl={itt['path']}" + if re.search(r"\.(mp4|m3u8|flv)$", itt["path"]): + url = itt["path"] + plays[itt["source"]].append(f"{itt['titleOld']}${url}") + for it in plays: + play.append("#".join(plays[it])) + vod["vod_play_from"] = "$$$".join(names) + vod["vod_play_url"] = "$$$".join(play) + result = {"list": [vod]} + return result + + def searchContent(self, key, quick, pg=1): + body = f"pagesize=20&page={pg}&searchKeys={key}" + path = self.aes(self.aes(body, self.key[1], 'encrypt'), self.key[0], 'encrypt', True) + data = self.fetch(f"{self.host}/api/ex/v3/security/drama/list?query={path}", headers=self.headers).json()[ + "data"] + data = self.aes(self.aes(data, self.key[0]), self.key[1], 'decrypt', True)['list'] + list = [] + for item in data: + list.append({ + 'vod_id': item.get("id"), + 'vod_pic': item["coverImage"].get("path"), + 'vod_name': item.get("name"), + 'vod_year': item.get("year"), + 'vod_remarks': item.get("remark") + }) + result = {"list": list, "page": pg} + return result + + def playerContent(self, flag, id, vipFlags): + url = id + if "vodPlayFrom" in url: + try: + path = self.aes(self.aes(id, self.key[1], 'encrypt'), self.key[0], 'encrypt', True) + data = \ + self.fetch(f"{self.host}/api/ex/v3/security/videoUsableUrl?query={path}", headers=self.headers).json()[ + "data"] + url = self.aes(self.aes(data, self.key[0]), self.key[1], 'decrypt', True)['playUrl'] + # try: + # url1 = self.fetch(url, headers=self.headers, timeout=5, allow_redirects=False).headers['Location'] + # if "http" in url1 and url1: + # url = url1 + # except: + # pass + except Exception as e: + pass + if '.jpg' in url or '.jpeg' in url or '.png' in url: + url = self.getProxyUrl() + "&url=" + b64encode(url.encode('utf-8')).decode('utf-8') + "&type=m3u8" + result = {} + result["parse"] = 0 + result["url"] = url + result["header"] = {'User-Agent': 'okhttp/3.12.1'} + return result + + def localProxy(self, param): + url = b64decode(param["url"]).decode('utf-8') + durl = url[:url.rfind('/')] + data = self.fetch(url, headers=self.headers).content.decode("utf-8") + lines = data.strip().split('\n') + for index, string in enumerate(lines): + if '#EXT' not in string and 'http' not in string: + lines[index] = durl + ('' if string.startswith('/') else '/') + string + data = '\n'.join(lines) + return [200, "application/vnd.apple.mpegur", data] + + def host(self): + try: + url = self.fetch('https://www.shijue.pro/token.txt', headers=self.headers).json()['domain'] + return url + except: + return "http://118.25.18.217:6632" + + headers = { + 'User-Agent': 'okhttp/3.12.1', + 'Content-Type': 'application/json;' + } + key = ['TFLYWVJ5EG5YB1PLZLVVMGVLBGRIDCSW', 'nj6E5K4yYYT5W4ScJ3J3rJ2zrzcJkpTk'] + + def aes(self, word, key, mode='decrypt', bool=False): + key = key.encode('utf-8') + if mode == 'decrypt': + word = b64decode(word) + cipher = AES.new(key, AES.MODE_ECB) + decrypted = cipher.decrypt(word) + word = unpad(decrypted, AES.block_size).decode('utf-8') + if bool: + word = json.loads(word) + elif mode == 'encrypt': + cipher = AES.new(key, AES.MODE_ECB) + padded = pad(word.encode('utf-8'), AES.block_size) + encrypted = cipher.encrypt(padded) + word = b64encode(encrypted).decode('utf-8') + if bool: + word = quote(word) + return word + + diff --git a/PY/app/边缘影视APP.py b/PY/app/边缘影视APP.py new file mode 100644 index 0000000..0ebb1dd --- /dev/null +++ b/PY/app/边缘影视APP.py @@ -0,0 +1,340 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +import binascii +import json +import os +import re +import sys +import time +import uuid +from urllib.parse import urlparse +from concurrent.futures import ThreadPoolExecutor +sys.path.append('..') +from base.spider import Spider +from base64 import b64encode, b64decode +from Crypto.PublicKey import RSA +from Crypto.Cipher import AES, PKCS1_v1_5 +from Crypto.Util.Padding import unpad, pad +from Crypto.Hash import MD5 + + +class Spider(Spider): + + def init(self, extend=""): + self.host = self.gethost() + pass + + def getName(self): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + headers = { + 'AppID': '534', + 'app_id': '534', + 'version': '1.0.3', + 'package': 'com.hjmore.wallpaper', + 'user_id': '3507f394e83d2424', + 'user-id': '3507f394e83d2424', + 'app_name': 'lanlan', + 'app-name': 'lanlan', + 'Content-Type': 'application/json; charset=utf-8;', + 'User-Agent': 'okhttp/4.9.0' + } + + def homeContent(self, filter): + hdata=self.getdata('/api.php/provide/index',self.getbody({'tid':'0'})) + vlist=hdata['data'].get('tj',[]) + result = {} + classes = [] + filters = {} + for i in hdata['data']['sub_data']: + id=str(i['type_id']) + classes.append({'type_id': id, 'type_name': i['type_name']}) + if len(i['data']): + vlist.extend(i['data']) + with ThreadPoolExecutor(max_workers=len(classes)) as executor: + results = executor.map(self.getf, classes) + for id, ft in results: + if len(ft):filters[id] = ft + result['class'] = classes + result['filters'] = filters + result['list'] = vlist + return result + + def homeVideoContent(self): + pass + + def categoryContent(self, tid, pg, filter, extend): + body={ + "tid": tid, + "type": extend.get('type'), + "lang": extend.get('lang'), + "area": extend.get('area'), + "year": extend.get('year'), + "pg": pg + } + body = {k: v for k, v in body.items() if v is not None and v != ""} + data=self.getdata('/api.php/provide/nav',self.getbody(body)) + result = {} + result['list'] = data['data']['data'] + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + pass + + def detailContent(self, ids): + data=self.getdata('/api.php/provide/vod',self.getbody({'ids':ids[0]})) + vod=data['data'] + plist=[] + names=[] + for i in vod['vod_play_url']: + ulist=[] + names.append(i['name'].split(' ')[0]) + jdata={'parse':''} + if i.get('parse') and isinstance(i['parse'], list) and len(i['parse']): + jdata['parse']=self.e64(json.dumps(i['parse'])) + for j in i['data']: + jdata['url']=j['url'] + ulist.append(f'{j["name"]}${self.e64(json.dumps(jdata))}') + plist.append('#'.join(ulist)) + vod['vod_play_from']='$$$'.join(names) + vod['vod_play_url']='$$$'.join(plist) + vod.pop('cover_list', None) + return {'list':[vod]} + + def searchContent(self, key, quick, pg="1"): + body={"wd":key,"tid":"0","pg":pg} + data=self.getdata('/api.php/provide/search',self.getbody(body)) + vlist=[] + for i in data['data']: + i.pop('vod_play_from', None) + vlist.append(i) + return {'list':vlist,'page':pg} + + def playerContent(self, flag, id, vipFlags): + data=json.loads(self.d64(id)) + parse=data.get('parse') + url,p,head = data.get('url'),1,'' + if parse: + parse=json.loads(self.d64(parse)) + if not re.search(r'\.m3u8|.mp4|\.flv', url) and parse: + for p in parse: + try: + data=self.fetch(f'{p}{url}',self.headers).json() + url=data.get('data',{}).get('url') or data.get('url') + head=data.get('data',{}).get('header') or data.get('header') + p=0 + break + except: + p,url=1,data.get('url') + head = {'User-Agent': 'okhttp/4.9.0'} + return {'parse': p, 'url': url, 'header': head} + + def localProxy(self, param): + pass + + def getf(self, map): + ft,id =[], map['type_id'] + try: + fdata = self.getdata('/api.php/provide/nav', self.getbody({'tid': id, 'pg': '1'})) + dy = ['area', 'year', 'lang', 'type'] + fd = fdata['data']['type_extend'] + has_non_empty_field = False + for key in dy: + if key in fd and fd[key].strip() != "": + has_non_empty_field = True + break + if has_non_empty_field: + for dkey in fd: + if dkey in dy and fd[dkey].strip() != "": + values = fd[dkey].split(",") + value_array = [{"n": value.strip(), "v": value.strip()} for value in values if + value.strip() != ""] + ft.append({"key": dkey, "name": dkey, "value": value_array}) + return (id, ft) + except: + return (id, ft) + + def getskey(self): + random_bytes = os.urandom(16) + return binascii.hexlify(random_bytes).decode() + + def getohost(self): + url='https://bianyuan001.oss-cn-beijing.aliyuncs.com/huidu1.0.0.json' + response = self.fetch(url, headers=self.headers).json() + return response['servers'][0] + + def gethost(self): + body={ + "gr_rp_size": "1080*2272", + "gr_app_list": "%E5%B1%8F%E5%B9%95%E5%BD%95%E5%88%B6%EF%BC%88com.miui.screenrecorder%29%0A%E5%A4%B8%E5%85%8B%EF%BC%88com.quark.browser%29%0A%E8%BE%B9%E7%BC%98%E8%A7%86%E9%A2%91%EF%BC%88com.hjmore.wallpaper%29%0A%E5%93%94%E5%93%A9%E5%93%94%E5%93%A9%EF%BC%88tv.danmaku.bili%29%0A%E7%81%AB%E6%98%9F%E6%90%9C%E9%A2%98%EF%BC%88com.fenbi.android.souti%29%0A%E6%94%AF%E4%BB%98%E5%AE%9D%EF%BC%88com.eg.android.AlipayGphone%29%0AWPS%20Office%EF%BC%88cn.wps.moffice_eng%29", + "gr_lal": "0.0%2C0.0", + "gr_system_type": "android", + "gr_device_imei": "3507f394e83d2424", + "gr_app_version": "1.0.3", + "gr_device_model": "Xiaomi%20M2012K10C%20%28Android%20%E7%89%88%E6%9C%AC%3A%2011%2C%20SDK%E7%89%88%E6%9C%AC%3A%2030%29", + "gr_city": "%E8%B4%B5%E5%B7%9E%2C%E6%9C%AA%E7%9F%A5%2C%E6%9C%AA%E7%9F%A5", + "requestId": self.uuid(), + "timeStamp": str(int(time.time() * 1000)), + "version": "1.0.3", + "package": "com.hjmore.wallpaper", + "userLoginToken": "", + "app_id": "534", + "appName": 2131951658, + "device_id": "3507f394e83d2424", + "device-id": "3507f394e83d2424", + "oaid": "", + "imei": "", + "referer_shop": "边缘影视", + "referer-shop": "边缘影视", + "access_fine_location": 0, + "access-fine-location": 0 + } + ohost = self.getohost() + data=self.getdata(f'/api.php/settings/grayscale_list',body,ohost) + parsed_url = urlparse(data['data']['grayscale']['server_url'][0]) + domain = parsed_url.scheme + "://" + parsed_url.netloc + return domain + + def drsa(self, encrypted_data): + private_key_pem = """-----BEGIN RSA PRIVATE KEY----- + MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDA5NWiAwRjH50/ + IJY1N0zLopa4jpuWE7kWMn1Qunu6SjBgTvNRmRUoPDHn54haLfbfXIa2X+/sIaMB + /O3HhrpVsz55E5W2vpZ5fBYWh+M65bQERKTW+l72H7GR9x0yj3QPByzzfsj/QkyP + 81prpwR9i8yMe7yG9TFKqUQCPE+/GrhNU1Qf6nFmV+vMnlP9DantkwAt4fPOMZn3 + j4da65/1YQV+F5bYzaLenNVKbHf8U8fVYLZWIy4yk2Vpe4R2Z+JX/eHWsChE9hOu + iFm02eTW5NJLZlWUxYrSE23VXi8oXSEdON3UEOrwSdAUh4SXxLZ9U7KpNVdTwWyR + AS4GyzJ/AgMBAAECggEBAKzmcXefLLeNBu4mz30z7Go7es5DRcLoOudiqmFKRs1c + 4q/xFLj3drdx/WnZZ6ctvDPKRBYFOJF4NRz7Ekfew/c9i6oLnA8KFuceCs53T37j + ltCclwT7t1L2ZbxovIsteuJdlDVOV+w2CVqez1Xfh27heKAT6ZEvBtfdkVBPr0uj + oVwa2+XlJmYZw5dHeB7ySVeAQ+69zDuADB8OWxPWsv6Del+Fhf0kTHAw4WgqcYsd + JUunCjgLdJUlDgXzH/M/Nj8NYVEuq6QpmhaktJ4fwn/F7u3lQllVCFKj5lr0Xb92 + y7lvQlGqMKX1oxf+P5c5/vie1kDx1Rj4S++flIcVlUECgYEA4BuxCZ1c8oOF98bs + KTAONnnZniQ1BRt7rA+O9+++lDjxJhxkuthwjB9YzrnZtxHJtvIIie9Jv8MVfzHa + p2woDtiEh3YYwmIlgNUFvTcGe++tTiEiLDcGc/xNhpvfbLaw9QB7/HQ+LT1QCMxJ + ufdBrR98l0khIGjYqxDW3W5pV70CgYEA3Ff/9+GM2XI/EUSTYrpnwp5R5OsXz1DL + 3CFFgp1EPCNk/c3YNWnrUtTkfmKAlRqWIHfphvH/jS6jpGrfRxDggPwGMtBc134b + brIM5i4KNj/EcE+w5g03HaKBf1ZihHDQ53c6wTn6IFOHJNSPRLqMNqRymfbclNyO + lBMHQmB8yOsCgYBCdZPTwRnuRTi2WQRx1nFwkEQL1Lrwb80GInsIZc2DkTtaTPNG + QadmtmkUrSK2Wo0SNsZ3eUHKn2TBmpw4KCfc9zKeJVSEWKy8fu+7xBSlLlebotHK + gOrl/H1VHOZuC+OAVItwO1yw98zDPynh/0Q3ve2pw6MSRGV0nYLKmdKdlQKBgQCJ + Ty1rw1qKhu9WS22tMIxIc3CFPxtvTeI8I1+1rVtAPq5Im2YIoyDKVXCucaO/RvoW + 8aLNPTELQe0oIJFTL+k3d9ZFBCNXBncB3GK9biNe+w3nD0IlmkamaQZZ2/M4pTUJ + iPtMPlzomCS3ht5g7f9CbegcmgGLooYXMGRtsMMSUQKBgQCoj+3UciH2i+HyUla5 + 1FxivjH3MqSTE4Q7OdzrELb6DoLYzjgWAbpG8HIuodD4uG5xz1oR5H7vkblf1itB + hwOwDEiabyX76e/I3Q0ovwBV+9PMjM4UVU0kHoiu3Z2s90ckwNh58w3QH5fn9E0b + fqMnB6uWze+xrXWijaOzVZhIZg== + -----END RSA PRIVATE KEY-----""" + private_key = RSA.import_key(private_key_pem) + cipher = PKCS1_v1_5.new(private_key) + decrypted_data = cipher.decrypt(b64decode(encrypted_data), None) + return decrypted_data.decode('utf-8') + + def ersa(self, data): + public_key = """-----BEGIN PUBLIC KEY----- + MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+0QMb3WDXjNBRovRhTLH + g3d+CliZAva2tepWNNN0Pj6DgE3ZTnPR34iL/cjo9Jbd3dqAJs/YkKnFurGkDxz5 + TthIqvmz244wiFcHt+FGWoJsj5ZVvrH3pPwH85ggmI1DjxSJEUhB12Z9X6FGli8D + drR9xeLe5y8vFekux8xCQ7pwH1mNQu4Wy32WVM8aLjmRjNzEWOvEMAWCRuwymEdS + zlWoH53qk1dqd6DAmOJhWU2hH6Yt2ZY9LTaDGiHrS+g0DuwajAQzhbM8eonGYMph + nP4q0UTHWEfaGR3HoILmeM32M+qF/UCGfgfR6tCMiXPoHwnD2zoxbZ2p+QlYuTZL + vQIDAQAB + -----END PUBLIC KEY-----""" + key = RSA.importKey(public_key) + cipher = PKCS1_v1_5.new(key) + encrypted = cipher.encrypt(data.encode()) + return b64encode(encrypted).decode() + + def eaes(self, data, key): + key = key.encode('utf-8') + cipher = AES.new(key, AES.MODE_ECB) + padded = pad(data.encode('utf-8'), AES.block_size) + encrypted = cipher.encrypt(padded) + word = b64encode(encrypted).decode('utf-8') + return word + + def daes(self, encrypted_data, key): + key = key.encode('utf-8') + cipher = AES.new(key, AES.MODE_ECB) + encrypted = b64decode(encrypted_data) + decrypted = cipher.decrypt(encrypted) + unpadded = unpad(decrypted, AES.block_size) + return unpadded.decode('utf-8') + + def getbody(self,params=None): + body = { + "requestId": self.uuid(), + "timeStamp": str(int(time.time()*1000)), + "version": "1.0.3", + "package": "com.hjmore.wallpaper", + "userLoginToken": "", + "app_id": "534", + "appName": 2131951658, + "device_id": "3507f394e83d2424", + "device-id": "3507f394e83d2424", + "oaid": "", + "imei": "", + "referer_shop": "边缘影视", + "referer-shop": "边缘影视", + "access_fine_location": 0, + "access-fine-location": 0 + } + if params: + body.update(params) + return body + + def getdata(self, path, body,host=None): + jdata=json.dumps(body) + msign = self.md5(jdata) + skey = self.getskey() + jsign={'key': skey,'sign': msign} + Sign=self.ersa(json.dumps(jsign)) + header=self.headers.copy() + header['Sign']=Sign + dbody=self.eaes(jdata, skey) + response = self.post(f'{host or self.host}{path}', headers=header, data=dbody) + rdata=response.text + if response.headers.get('Sign'): + dkey=self.drsa(response.headers['Sign']) + rdata=self.daes(rdata, dkey) + return json.loads(rdata) + + def e64(self, text): + try: + text_bytes = text.encode('utf-8') + encoded_bytes = b64encode(text_bytes) + return encoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64编码错误: {str(e)}") + return "" + + def d64(self,encoded_text): + try: + encoded_bytes = encoded_text.encode('utf-8') + decoded_bytes = b64decode(encoded_bytes) + return decoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64解码错误: {str(e)}") + return "" + + def md5(self,text): + h = MD5.new() + h.update(text.encode('utf-8')) + return h.hexdigest() + + def uuid(self): + return str(uuid.uuid4()) + + + + diff --git a/PY/app/零度影视APP.py b/PY/app/零度影视APP.py new file mode 100644 index 0000000..02c71f6 --- /dev/null +++ b/PY/app/零度影视APP.py @@ -0,0 +1,224 @@ +# -*- coding: utf-8 -*- +# by @嗷呜 +import json +import random +import sys +from base64 import b64encode, b64decode +from concurrent.futures import ThreadPoolExecutor +sys.path.append('..') +from base.spider import Spider + +class Spider(Spider): + + def init(self, extend=""): + did=self.getdid() + self.headers.update({'deviceId': did}) + token=self.gettk() + self.headers.update({'token': token}) + pass + + def getName(self): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + host='http://zero.mitotv.com' + + headers = { + 'User-Agent': 'okhttp/4.12.0', + 'client': 'app', + 'deviceType': 'Android' + } + + def homeContent(self, filter): + data=self.post(f"{self.host}/api/v1/app/screen/screenType", headers=self.headers).json() + result = {} + cate = { + "类型": "classify", + "地区": "region", + "年份": "year" + } + sort={ + 'key':'sreecnTypeEnum', + 'name': '排序', + 'value':[{'n':'人气','v':'POPULARITY'},{'n':'评分','v':'COLLECT'},{'n':'热搜','v':'HOT'}] + } + classes = [] + filters = {} + for k in data['data']: + classes.append({ + 'type_name': k['name'], + 'type_id': k['id'] + }) + filters[k['id']] = [ + { + 'name': v['name'], + 'key': cate[v['name']], + 'value': [ + {'n': i['name'], 'v': i['name']} + for i in v['children'] + ] + } + for v in k['children'] + ] + filters[k['id']].append(sort) + result['class'] = classes + result['filters'] = filters + return result + + def homeVideoContent(self): + jdata={"condition":64,"pageNum":1,"pageSize":40} + data=self.post(f"{self.host}/api/v1/app/recommend/recommendSubList", headers=self.headers, json=jdata).json() + return {'list':self.getlist(data['data']['records'])} + + def categoryContent(self, tid, pg, filter, extend): + jdata = { + 'condition': { + 'sreecnTypeEnum': 'NEWEST', + 'typeId': tid, + }, + 'pageNum': int(pg), + 'pageSize': 40, + } + jdata['condition'].update(extend) + data = self.post(f"{self.host}/api/v1/app/screen/screenMovie", headers=self.headers, json=jdata).json() + result = {} + result['list'] = self.getlist(data['data']['records']) + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + ids = ids[0].split('@@') + jdata = {"id": int(ids[0]), "typeId": ids[-1]} + v = self.post(f"{self.host}/api/v1/app/play/movieDesc", headers=self.headers, json=jdata).json() + v = v['data'] + vod = { + 'type_name': v.get('classify'), + 'vod_year': v.get('year'), + 'vod_area': v.get('area'), + 'vod_actor': v.get('star'), + 'vod_director': v.get('director'), + 'vod_content': v.get('introduce'), + 'vod_play_from': '', + 'vod_play_url': '' + } + c = self.post(f"{self.host}/api/v1/app/play/movieDetails", headers=self.headers, json=jdata).json() + l = c['data']['moviePlayerList'] + n = {str(i['id']): i['moviePlayerName'] for i in l} + m = jdata.copy() + m.update({'playerId': str(l[0]['id'])}) + pd = self.getv(m, c['data']['episodeList']) + if len(l)-1: + with ThreadPoolExecutor(max_workers=len(l)-1) as executor: + future_to_player = {executor.submit(self.getd, jdata, player): player for player in l[1:]} + for future in future_to_player: + try: + o,p = future.result() + pd.update(self.getv(o,p)) + except Exception as e: + print(f"请求失败: {e}") + w, e = [],[] + for i, x in pd.items(): + if x: + w.append(n[i]) + e.append(x) + vod['vod_play_from'] = '$$$'.join(w) + vod['vod_play_url'] = '$$$'.join(e) + return {'list': [vod]} + + def searchContent(self, key, quick, pg="1"): + jdata={ + "condition": { + "value": key + }, + "pageNum": int(pg), + "pageSize": 40 + } + data=self.post(f"{self.host}/api/v1/app/search/searchMovie", headers=self.headers, json=jdata).json() + return {'list':self.getlist(data['data']['records']),'page':pg} + + def playerContent(self, flag, id, vipFlags): + jdata=json.loads(self.d64(id)) + data = self.post(f"{self.host}/api/v1/app/play/movieDetails", headers=self.headers, json=jdata).json() + try: + params={'playerUrl':data['data']['url'],'playerId':jdata['playerId']} + pd=self.fetch(f"{self.host}/api/v1/app/play/analysisMovieUrl", headers=self.headers, params=params).json() + url,p=pd['data'],0 + except Exception as e: + print(f"请求失败: {e}") + url,p=data['data']['url'],0 + return {'parse': p, 'url': url, 'header': {'User-Agent': 'okhttp/4.12.0'}} + + def localProxy(self, param): + pass + + def liveContent(self, url): + pass + + def gettk(self): + data=self.fetch(f"{self.host}/api/v1/app/user/visitorInfo", headers=self.headers).json() + return data['data']['token'] + + def getdid(self): + did=self.getCache('ldid') + if not did: + hex_chars = '0123456789abcdef' + did =''.join(random.choice(hex_chars) for _ in range(16)) + self.setCache('ldid',did) + return did + + def getd(self,jdata,player): + x = jdata.copy() + x.update({'playerId': str(player['id'])}) + response = self.post(f"{self.host}/api/v1/app/play/movieDetails", headers=self.headers, json=x).json() + return x, response['data']['episodeList'] + + def getv(self,d,c): + f={d['playerId']:''} + g=[] + for i in c: + j=d.copy() + j.update({'episodeId':str(i['id'])}) + g.append(f"{i['episode']}${self.e64(json.dumps(j))}") + f[d['playerId']]='#'.join(g) + return f + + def getlist(self,data): + videos = [] + for i in data: + videos.append({ + 'vod_id': f"{i['id']}@@{i['typeId']}", + 'vod_name': i.get('name'), + 'vod_pic': i.get('cover'), + 'vod_year': i.get('year'), + 'vod_remarks': i.get('totalEpisode') + }) + return videos + + def e64(self, text): + try: + text_bytes = text.encode('utf-8') + encoded_bytes = b64encode(text_bytes) + return encoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64编码错误: {str(e)}") + return "" + + def d64(self,encoded_text): + try: + encoded_bytes = encoded_text.encode('utf-8') + decoded_bytes = b64decode(encoded_bytes) + return decoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64解码错误: {str(e)}") + return "" diff --git a/PY/app/魔方影视APP.py b/PY/app/魔方影视APP.py new file mode 100644 index 0000000..f8203bf --- /dev/null +++ b/PY/app/魔方影视APP.py @@ -0,0 +1,209 @@ +import re +import sys +from Crypto.Hash import MD5 +sys.path.append("..") +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad +from urllib.parse import quote, urlparse +from base64 import b64encode, b64decode +import json +import time +from base.spider import Spider + + +class Spider(Spider): + + def init(self, extend=""): + self.host = self.gethost() + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def action(self, action): + pass + + def destroy(self): + pass + + def homeContent(self, filter): + data = self.getdata("/api.php/getappapi.index/initV119") + dy = {"class": "类型", "area": "地区", "lang": "语言", "year": "年份", "letter": "字母", "by": "排序", + "sort": "排序"} + filters = {} + classes = [] + json_data = data["type_list"] + homedata = data["banner_list"][8:] + for item in json_data: + if item["type_name"] == "全部": + continue + has_non_empty_field = False + jsontype_extend = json.loads(item["type_extend"]) + homedata.extend(item["recommend_list"]) + jsontype_extend["sort"] = "最新,最热,最赞" + classes.append({"type_name": item["type_name"], "type_id": item["type_id"]}) + for key in dy: + if key in jsontype_extend and jsontype_extend[key].strip() != "": + has_non_empty_field = True + break + if has_non_empty_field: + filters[str(item["type_id"])] = [] + for dkey in jsontype_extend: + if dkey in dy and jsontype_extend[dkey].strip() != "": + values = jsontype_extend[dkey].split(",") + value_array = [{"n": value.strip(), "v": value.strip()} for value in values if + value.strip() != ""] + filters[str(item["type_id"])].append({"key": dkey, "name": dy[dkey], "value": value_array}) + result = {} + result["class"] = classes + result["filters"] = filters + result["list"] = homedata[1:] + return result + + def homeVideoContent(self): + pass + + def categoryContent(self, tid, pg, filter, extend): + body = {"area": extend.get('area', '全部'), "year": extend.get('year', '全部'), "type_id": tid, "page": pg, + "sort": extend.get('sort', '最新'), "lang": extend.get('lang', '全部'), + "class": extend.get('class', '全部')} + result = {} + data = self.getdata("/api.php/getappapi.index/typeFilterVodList", body) + result["list"] = data["recommend_list"] + result["page"] = pg + result["pagecount"] = 9999 + result["limit"] = 90 + result["total"] = 999999 + return result + + def detailContent(self, ids): + body = f"vod_id={ids[0]}" + data = self.getdata("/api.php/getappapi.index/vodDetail", body) + vod = data["vod"] + play = [] + names = [] + for itt in data["vod_play_list"]: + a = [] + names.append(itt["player_info"]["show"]) + for it in itt['urls']: + it['user_agent'] = itt["player_info"].get("user_agent") + it["parse"] = itt["player_info"].get("parse") + a.append(f"{it['name']}${self.e64(json.dumps(it))}") + play.append("#".join(a)) + vod["vod_play_from"] = "$$$".join(names) + vod["vod_play_url"] = "$$$".join(play) + result = {"list": [vod]} + return result + + def searchContent(self, key, quick, pg="1"): + body = f"keywords={key}&type_id=0&page={pg}" + data = self.getdata("/api.php/getappapi.index/searchList", body) + result = {"list": data["search_list"], "page": pg} + return result + + def playerContent(self, flag, id, vipFlags): + ids = json.loads(self.d64(id)) + h = {"User-Agent": (ids['user_agent'] or "okhttp/3.14.9")} + try: + if re.search(r'url=', ids['parse_api_url']): + data = self.fetch(ids['parse_api_url'], headers=h, timeout=10).json() + url = data.get('url') or data['data'].get('url') + else: + body = f"parse_api={ids.get('parse') or ids['parse_api_url'].replace(ids['url'], '')}&url={quote(self.aes(ids['url'], True))}&token={ids.get('token')}" + b = self.getdata("/api.php/getappapi.index/vodParse", body)['json'] + url = json.loads(b)['url'] + if 'error' in url: raise ValueError(f"解析失败: {url}") + p = 0 + except Exception as e: + print('错误信息:', e) + url, p = ids['url'], 1 + + if re.search(r'\.jpg|\.png|\.jpeg', url): + url = self.Mproxy(url) + result = {} + result["parse"] = p + result["url"] = url + result["header"] = h + return result + + def localProxy(self, param): + return self.Mlocal(param) + + def gethost(self): + headers = { + 'User-Agent': 'okhttp/3.14.9' + } + response = self.fetch('https://snysw.xyz/mfys.txt',headers=headers).text + return response.strip() + + def aes(self, text, b=None): + key = b"1234567887654321" + cipher = AES.new(key, AES.MODE_CBC, key) + if b: + ct_bytes = cipher.encrypt(pad(text.encode("utf-8"), AES.block_size)) + ct = b64encode(ct_bytes).decode("utf-8") + return ct + else: + pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size) + return pt.decode("utf-8") + + def header(self): + t = str(int(time.time())) + header = {"Referer": self.host, + "User-Agent": "okhttp/3.14.9", "app-version-code": "140", "app-ui-mode": "light", + "app-api-verify-time": t, "app-user-device-id": self.md5(t), + "app-api-verify-sign": self.aes(t, True), + "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"} + return header + + def getdata(self, path, data=None): + vdata = self.post(f"{self.host}{path}", headers=self.header(), data=data, timeout=10).json()['data'] + data1 = self.aes(vdata) + return json.loads(data1) + + def Mproxy(self, url): + return f"{self.getProxyUrl()}&url={self.e64(url)}&type=m3u8" + + def Mlocal(self, param, header=None): + url = self.d64(param["url"]) + ydata = self.fetch(url, headers=header, allow_redirects=False) + data = ydata.content.decode('utf-8') + if ydata.headers.get('Location'): + url = ydata.headers['Location'] + data = self.fetch(url, headers=header).content.decode('utf-8') + parsed_url = urlparse(url) + durl = parsed_url.scheme + "://" + parsed_url.netloc + lines = data.strip().split('\n') + for index, string in enumerate(lines): + if '#EXT' not in string and 'http' not in string: + last_slash_index = string.rfind('/') + lpath = string[:last_slash_index + 1] + lines[index] = durl + ('' if lpath.startswith('/') else '/') + lpath + data = '\n'.join(lines) + return [200, "application/vnd.apple.mpegur", data] + + def e64(self, text): + try: + text_bytes = text.encode('utf-8') + encoded_bytes = b64encode(text_bytes) + return encoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64编码错误: {str(e)}") + return "" + + def d64(self, encoded_text): + try: + encoded_bytes = encoded_text.encode('utf-8') + decoded_bytes = b64decode(encoded_bytes) + return decoded_bytes.decode('utf-8') + except Exception as e: + print(f"Base64解码错误: {str(e)}") + return "" + + def md5(self, text): + h = MD5.new() + h.update(text.encode('utf-8')) + return h.hexdigest()