From f8e325de0e5d1fad0d89d055a890b77f7891bf0a Mon Sep 17 00:00:00 2001 From: alantang <107459091+alantang1977@users.noreply.github.com> Date: Fri, 14 Feb 2025 15:02:16 +0800 Subject: [PATCH] Add files via upload --- py/py_hitv.py | 148 +++++++++++++++++ py/py_lav.py | 213 ++++++++++++++++++++++++ py/py_mp.py | 94 +++++++++++ py/py_xpg.py | 172 ++++++++++++++++++++ py/py_光速.py | 195 ++++++++++++++++++++++ py/py_剧多短剧.py | 269 +++++++++++++++++++++++++++++++ py/py_小红薯.py | 176 ++++++++++++++++++++ py/py_推特.py | 217 +++++++++++++++++++++++++ py/py_浴火社.py | 350 ++++++++++++++++++++++++++++++++++++++++ py/py_爱.py | 256 +++++++++++++++++++++++++++++ py/py_胖虎.py | 216 +++++++++++++++++++++++++ py/py_腾.py | 366 ++++++++++++++++++++++++++++++++++++++++++ py/py_芒.py | 207 ++++++++++++++++++++++++ py/py_视觉.py | 241 +++++++++++++++++++++++++++ py/py_金牌.py | 225 ++++++++++++++++++++++++++ 15 files changed, 3345 insertions(+) create mode 100644 py/py_hitv.py create mode 100644 py/py_lav.py create mode 100644 py/py_mp.py create mode 100644 py/py_xpg.py create mode 100644 py/py_光速.py create mode 100644 py/py_剧多短剧.py create mode 100644 py/py_小红薯.py create mode 100644 py/py_推特.py create mode 100644 py/py_浴火社.py create mode 100644 py/py_爱.py create mode 100644 py/py_胖虎.py create mode 100644 py/py_腾.py create mode 100644 py/py_芒.py create mode 100644 py/py_视觉.py create mode 100644 py/py_金牌.py diff --git a/py/py_hitv.py b/py/py_hitv.py new file mode 100644 index 00000000..215ee9ec --- /dev/null +++ b/py/py_hitv.py @@ -0,0 +1,148 @@ +# coding=utf-8 +# !/usr/bin/python +# 嗷呜 +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/py_lav.py b/py/py_lav.py new file mode 100644 index 00000000..2386caf6 --- /dev/null +++ b/py/py_lav.py @@ -0,0 +1,213 @@ +# coding=utf-8 +# !/usr/bin/python +# 嗷呜 +import sys +from base64 import b64encode, b64decode +from Crypto.Hash import MD5, SHA256 +sys.path.append("..") +from base.spider import Spider +from Crypto.Cipher import AES +import json +import time + + +class Spider(Spider): + + def getName(self): + return "lav" + + def init(self, extend=""): + self.id = self.ms(str(int(time.time() * 1000)))[:16] + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def action(self, action): + pass + + def destroy(self): + pass + + host = "http://sir_new.tiansexyl.tv" + t = str(int(time.time() * 1000)) + headers = {'User-Agent': 'okhttp-okgo/jeasonlzy', 'Connection': 'Keep-Alive', + 'Content-Type': 'application/x-www-form-urlencoded'} + + def homeContent(self, filter): + cateManual = {"演员": "actor", "分类": "avsearch", } + classes = [] + for k in cateManual: + classes.append({'type_name': k, 'type_id': cateManual[k]}) + j = {'code': 'homePage', 'mod': 'down', 'channel': 'self', 'via': 'agent', 'bundleId': 'com.tvlutv', + 'app_type': 'rn', 'os_version': '12.0.5', 'version': '3.2.3', 'oauth_type': 'android_rn', + 'oauth_id': self.id} + + body = self.aes(j) + data = self.post(f'{self.host}/api.php?t={str(int(time.time() * 1000))}', data=body, headers=self.headers).json()['data'] + data1 = self.aes(data, False)['data'] + self.r = data1['r'] + for i, d in enumerate(data1['avTag']): + # if i == 4: + # break + classes.append({'type_name': d['name'], 'type_id': d['tag']}) + resutl = {} + resutl["class"] = classes + return resutl + + def homeVideoContent(self): + pass + + def categoryContent(self, tid, pg, filter, extend): + id = tid.split("@@") + result = {} + result["page"] = pg + result["pagecount"] = 9999 + result["limit"] = 90 + result["total"] = 999999 + if id[0] == 'avsearch': + if pg == '1': + j = {'code': 'avsearch', 'mod': 'search', 'channel': 'self', 'via': 'agent', 'bundleId': 'com.tvlutv', + 'app_type': 'rn', 'os_version': '12.0.5', 'version': '3.2.3', 'oauth_type': 'android_rn', + 'oauth_id': self.id} + if len(id) > 1: + j = {'code': 'find', 'mod': 'tag', 'channel': 'self', 'via': 'agent', 'bundleId': 'com.tvlutv', + 'app_type': 'rn', 'os_version': '12.0.5', 'version': '3.2.3', 'oauth_type': 'android_rn', + 'oauth_id': self.id, 'type': 'av', 'dis': 'new', 'page': str(pg), 'tag': id[1]} + elif id[0] == 'actor': + j = {'mod': 'actor', 'channel': 'self', 'via': 'agent', 'bundleId': 'com.tvlutv', 'app_type': 'rn', + 'os_version': '12.0.5', 'version': '3.2.3', 'oauth_type': 'android_rn', 'oauth_id': self.id, + 'page': str(pg), 'filter': ''} + if len(id) > 1: + j = {'code': 'eq', 'mod': 'actor', 'channel': 'self', 'via': 'agent', 'bundleId': 'com.tvlutv', + 'app_type': 'rn', 'os_version': '12.0.5', 'version': '3.2.3', 'oauth_type': 'android_rn', + 'oauth_id': self.id, 'page': str(pg), 'id': id[1], 'actor': id[2]} + else: + j = {'code': 'search', 'mod': 'av', 'channel': 'self', 'via': 'agent', 'bundleId': 'com.tvlutv', + 'app_type': 'rn', 'os_version': '12.0.5', 'version': '3.2.3', 'oauth_type': 'android_rn', + 'oauth_id': self.id, 'page': str(pg), 'tag': id[0]} + + body = self.aes(j) + data = self.post(f'{self.host}/api.php?t={str(int(time.time() * 1000))}', data=body, headers=self.headers).json()['data'] + data1 = self.aes(data, False)['data'] + videos = [] + if tid == 'avsearch' and len(id) == 1: + for item in data1: + videos.append({"vod_id": id[0] + "@@" + str(item.get('tags')), 'vod_name': item.get('name'), + 'vod_pic': self.imgs(item.get('ico')), 'vod_tag': 'folder', + 'style': {"type": "rect", "ratio": 1.33}}) + elif tid == 'actor' and len(id) == 1: + for item in data1: + videos.append({"vod_id": id[0] + "@@" + str(item.get('id')) + "@@" + item.get('name'), + 'vod_name': item.get('name'), 'vod_pic': self.imgs(item.get('cover')), + 'vod_tag': 'folder', 'style': {"type": "oval"}}) + else: + for item in data1: + if item.get('_id'): + videos.append({"vod_id": str(item.get('id')), 'vod_name': item.get('title'), + 'vod_pic': self.imgs(item.get('cover_thumb') or item.get('cover_full')), + 'vod_remarks': item.get('good'), 'style': {"type": "rect", "ratio": 1.33}}) + result["list"] = videos + return result + + def detailContent(self, ids): + id = ids[0] + j = {'code': 'detail', 'mod': 'av', 'channel': 'self', 'via': 'agent', 'bundleId': 'com.tvlutv', + 'app_type': 'rn', 'os_version': '12.0.5', 'version': '3.2.3', 'oauth_type': 'android_rn', + 'oauth_id': self.id, 'id': id} + body = self.aes(j) + data = self.post(f'{self.host}/api.php?t={str(int(time.time() * 1000))}', data=body, headers=self.headers).json()['data'] + data1 = self.aes(data, False)['line'] + vod = {} + play = [] + for itt in data1: + a = itt['line'].get('s720') + if a: + b = a.split('.') + b[0] = 'https://m3u8' + a = '.'.join(b) + play.append(itt['info']['tips'] + "$" + a) + break + vod["vod_play_from"] = 'LAV' + vod["vod_play_url"] = "#".join(play) + result = {"list": [vod]} + return result + + def searchContent(self, key, quick, pg="1"): + pass + + def playerContent(self, flag, id, vipFlags): + url = self.getProxyUrl() + "&url=" + b64encode(id.encode('utf-8')).decode('utf-8') + "&type=m3u8" + self.hh = {'User-Agent': 'dd', 'Connection': 'Keep-Alive', 'Referer': self.r} + result = {} + result["parse"] = 0 + result["url"] = url + result["header"] = self.hh + return result + + def localProxy(self, param): + url = param["url"] + if param.get('type') == "m3u8": + return self.vod(b64decode(url).decode('utf-8')) + else: + return self.img(url) + + def vod(self, url): + data = self.fetch(url, headers=self.hh).text + key = bytes.fromhex("13d47399bda541b85e55830528d4e66f1791585b2d2216f23215c4c63ebace31") + iv = bytes.fromhex(data[:32]) + data = data[32:] + cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128) + data_bytes = bytes.fromhex(data) + decrypted = cipher.decrypt(data_bytes) + encoded = decrypted.decode("utf-8").replace("\x08", "") + return [200, "application/vnd.apple.mpegur", encoded] + + def imgs(self, url): + return self.getProxyUrl() + '&url=' + url + + def img(self, url): + type = url.split('.')[-1] + data = self.fetch(url).text + key = bytes.fromhex("ba78f184208d775e1553550f2037f4af22cdcf1d263a65b4d5c74536f084a4b2") + iv = bytes.fromhex(data[:32]) + data = data[32:] + cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128) + data_bytes = bytes.fromhex(data) + decrypted = cipher.decrypt(data_bytes) + return [200, f"image/{type}", decrypted] + + def ms(self, data, m=False): + h = MD5.new() + if m: + h = SHA256.new() + h.update(data.encode('utf-8')) + return h.hexdigest() + + def aes(self, data, operation=True): + key = bytes.fromhex("620f15cfdb5c79c34b3940537b21eda072e22f5d7151456dec3932d7a2b22c53") + t = str(int(time.time())) + ivt = self.ms(t) + if operation: + data = json.dumps(data, separators=(',', ':')) + iv = bytes.fromhex(ivt) + else: + iv = bytes.fromhex(data[:32]) + data = data[32:] + cipher = AES.new(key, AES.MODE_CFB, iv, segment_size=128) + if operation: + data_bytes = data.encode('utf-8') + encrypted = cipher.encrypt(data_bytes) + ep = f'{ivt}{encrypted.hex()}' + edata = f"data={ep}×tamp={t}0d27dfacef1338483561a46b246bf36d" + sign = self.ms(self.ms(edata, True)) + edata = f"timestamp={t}&data={ep}&sign={sign}" + return edata + else: + data_bytes = bytes.fromhex(data) + decrypted = cipher.decrypt(data_bytes) + return json.loads(decrypted.decode('utf-8')) + diff --git a/py/py_mp.py b/py/py_mp.py new file mode 100644 index 00000000..11ab9505 --- /dev/null +++ b/py/py_mp.py @@ -0,0 +1,94 @@ +# coding=utf-8 +# !/usr/bin/python +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/py_xpg.py b/py/py_xpg.py new file mode 100644 index 00000000..137d6c0e --- /dev/null +++ b/py/py_xpg.py @@ -0,0 +1,172 @@ +# coding=utf-8 +# !/usr/bin/python +import sys + +sys.path.append('') +from base.spider import Spider +from urllib.parse import quote + +class Spider(Spider): + def getName(self): + return "xpg" + + def init(self, extend=""): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + def homeContent(self, filter): + data = self.fetch( + "{0}/api.php/v2.vod/androidtypes".format(self.host), + headers=self.header, + ).json() + dy = { + "classes": "类型", + "areas": "地区", + "years": "年份", + "sortby": "排序", + } + filters = {} + classes = [] + for item in data['data']: + has_non_empty_field = False + item['soryby'] = ['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 + + host = "http://item.xpgtv.com" + header = { + 'User-Agent': 'okhttp/3.12.11', + 'token': 'ElEDlwCVgXcFHFhddiq2JKteHofExRBUrfNlmHrWetU3VVkxnzJAodl52N9EUFS+Dig2A/fBa/V9RuoOZRBjYvI+GW8kx3+xMlRecaZuECdb/3AdGkYpkjW3wCnpMQxf8vVeCz5zQLDr8l8bUChJiLLJLGsI+yiNskiJTZz9HiGBZhZuWh1mV1QgYah5CLTbSz8=', + 'token2': 'a0kEsBKRgTkBZ29NZ3WcNKN/C4T00RN/hNkmmGa5JMBeEENnqydLoetm/t8=', + 'user_id': 'XPGBOX', + 'version': 'XPGBOX com.phoenix.tv1.5.3', + 'timestamp': '1732286435', + 'hash': 'd9ab', + } + + def homeVideoContent(self): + rsp = self.fetch("{0}/api.php/v2.main/androidhome".format(self.host), headers=self.header) + root = rsp.json()['data']['list'] + videos = [] + for vodd in root: + for vod in vodd['list']: + videos.append({ + "vod_id": vod['id'], + "vod_name": vod['name'], + "vod_pic": vod['pic'], + "vod_remarks": vod['score'] + }) + result = { + 'list': videos + } + return result + + def categoryContent(self, tid, pg, filter, extend): + parms = [] + parms.append(f"page={pg}") + parms.append(f"type={tid}") + if extend.get('areas'): + parms.append(f"area={quote(extend['areaes'])}") + if extend.get('years'): + parms.append(f"year={quote(extend['yeares'])}") + if extend.get('sortby'): + parms.append(f"sortby={extend['sortby']}") + if extend.get('classes'): + parms.append(f"class={quote(extend['classes'])}") + parms = "&".join(parms) + result = {} + url = '{0}/api.php/v2.vod/androidfilter10086?{1}'.format(self.host, parms) + rsp = self.fetch(url, headers=self.header) + root = rsp.json()['data'] + videos = [] + for vod in root: + videos.append({ + "vod_id": vod['id'], + "vod_name": vod['name'], + "vod_pic": vod['pic'], + "vod_remarks": vod['score'] + }) + result['list'] = videos + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + id = ids[0] + url = '{0}/api.php/v3.vod/androiddetail2?vod_id={1}'.format(self.host, id) + rsp = self.fetch(url, headers=self.header) + root = rsp.json()['data'] + node = root['urls'] + d = [it['key'] + "$" + f"http://c.xpgtv.net/m3u8/{it['url']}.m3u8" for it in node] + vod = { + "vod_name": root['name'], + 'vod_play_from': '小苹果', + 'vod_play_url': '#'.join(d), + } + print(vod) + result = { + 'list': [ + vod + ] + } + return result + + def searchContent(self, key, quick, pg='1'): + url = '{0}/api.php/v2.vod/androidsearch10086?page={1}&wd={2}'.format(self.host, pg, key) + rsp = self.fetch(url, headers=self.header) + root = rsp.json()['data'] + videos = [] + for vod in root: + videos.append({ + "vod_id": vod['id'], + "vod_name": vod['name'], + "vod_pic": vod['pic'], + "vod_remarks": vod['score'] + }) + result = { + 'list': videos + } + return result + + def playerContent(self, flag, id, vipFlags): + result = {} + result["parse"] = 0 + result["url"] = id + result["header"] = self.header + return result + + def localProxy(self, param): + pass + + diff --git a/py/py_光速.py b/py/py_光速.py new file mode 100644 index 00000000..ebece1f0 --- /dev/null +++ b/py/py_光速.py @@ -0,0 +1,195 @@ +# coding=utf-8 +# !/usr/bin/python +# by嗷呜 +import re +import sys +from urllib.parse import quote + +from Crypto.Hash import MD5 + +sys.path.append("..") +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad +from base64 import b64encode, b64decode +import json +import time +from base.spider import Spider + + +class Spider(Spider): + + def getName(self): + return "光速" + + 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"] + 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 + 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"]) + parse = itt["player_info"]["parse"] + ua = '' + if itt["player_info"].get("user_agent", ''): + ua = b64encode(itt["player_info"]["user_agent"].encode('utf-8')).decode('utf-8') + for it in itt["urls"]: + url = it["url"] + if not re.search(r'\.m3u8|\.mp4', url): + url = parse + '@@' + url + url = b64encode(url.encode('utf-8')).decode('utf-8') + a.append(f"{it['name']}${url}|||{ua}|||{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}" + data = self.getdata("/api.php/getappapi.index/searchList", body) + result = {"list": data["search_list"], "page": pg} + return result + + phend = { + 'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 11; M2012K10C Build/RP1A.200720.011)'} + + def playerContent(self, flag, id, vipFlags): + ids = id.split("|||") + if ids[1]: self.phend['User-Agent'] = b64decode(ids[1]).decode('utf-8') + url = b64decode(ids[0]).decode('utf-8') + if not re.search(r'\.m3u8|\.mp4', url): + a = url.split("@@") + body = f"parse_api={a[0]}&url={quote(self.aes('encrypt', a[1]))}&token={ids[-1]}" + jd = self.getdata("/api.php/getappapi.index/vodParse", body)['json'] + url = json.loads(jd)['url'] + # if '.mp4' not in url: + # l=self.fetch(url, headers=self.phend,allow_redirects=False) + # if l.status_code == 200 and l.headers.get('Location',''): + # url=l.headers['Location'] + if '.jpg' in url or '.png' in url or '.jpeg' 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"] = self.phend + return result + + def localProxy(self, param): + url = b64decode(param["url"]).decode('utf-8') + durl = url[:url.rfind('/')] + data = self.fetch(url, headers=self.phend).content.decode("utf-8") + inde = None + pd = True + lines = data.strip().split('\n') + for index, string in enumerate(lines): + # if '#EXT-X-DISCONTINUITY' in string and pd: + # pd = False + # inde = index + if '#EXT' not in string and 'http' not in string: + lines[index] = durl + ('' if string.startswith('/') else '/') + string + if inde: + del lines[inde:inde + 4] + data = '\n'.join(lines) + return [200, "application/vnd.apple.mpegur", data] + + def gethost(self): + host = self.fetch('https://jingyu-1312635929.cos.ap-nanjing.myqcloud.com/1.json').text.strip() + return host + + 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())) + md5_hash = MD5.new() + md5_hash.update(t.encode('utf-8')) + signature_md5 = md5_hash.hexdigest() + header = {"User-Agent": "okhttp/3.14.9", "app-version-code": "300", "app-ui-mode": "light", + "app-user-device-id": signature_md5, "app-api-verify-time": t, + "app-api-verify-sign": self.aes("encrypt", t), "Content-Type": "application/x-www-form-urlencoded"} + return header + + def getdata(self, path, data=None): + # data = self.post(self.host + path, headers=self.header(), data=data).text + data = self.post(self.host + path, headers=self.header(), data=data, verify=False).json()["data"] + data1 = self.aes("decrypt", data) + return json.loads(data1) diff --git a/py/py_剧多短剧.py b/py/py_剧多短剧.py new file mode 100644 index 00000000..18c7c215 --- /dev/null +++ b/py/py_剧多短剧.py @@ -0,0 +1,269 @@ +# coding=utf-8 +# !/usr/bin/python +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.did=self.random_str(16) + self.ntid=self.random_str(24) + self.sign= {'token':None, 'uid':None} + _ = 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 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.sign['token']=data['data']['token'] + self.sign['uid'] = data['data']['userId'] + return 666 + + def getdata(self,path,params=None): + t = int(time.time()) + n=self.md5(f'{self.uuid()}{t*1000}') + 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(t), + 's': s, + # 'Content-Type': 'application/json; charset=utf-8', + } + if self.sign['token']: + headers['ta-token'] = self.sign['token'] + headers['userId'] = self.sign['uid'] + if params: + params={'ct':ct.replace('=','\u003d')} + 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): + data=self.getdata(f'/supports/configs?tenantId={self.ntid}') + oic=['','',''] + for i in data['data']: + if i['name']=='image_cdn': + oic[0]=i['records'][0]['value'] + if i['name']=='image_cdn_path': + oic[1]=i['records'][0]['value'] + if i['name']=='cdn-domain': + oic[1]=i['records'][0]['value'].split('#')[0] + return (oic[0],oic[1],oic[2]) + + 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): + type=param.get('path').split('.')[-1] + 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 = b64decode("iM41VipvCFtToAFFRExEXw==") + iv = 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, f'image/{type}', decrypt(data.content)] diff --git a/py/py_小红薯.py b/py/py_小红薯.py new file mode 100644 index 00000000..125af7c0 --- /dev/null +++ b/py/py_小红薯.py @@ -0,0 +1,176 @@ +# coding=utf-8 +# !/usr/bin/python +# by嗷呜 +import json +import random +import sys +import time +from base64 import b64decode +from Crypto.Cipher import AES +from Crypto.Hash import MD5 +from Crypto.Util.Padding import unpad +sys.path.append('..') +from base.spider import Spider + + +class Spider(Spider): + + def getName(self): + return "小红书" + + def init(self, extend=""): + self.did = self.random_str(32) + self.token,self.phost = self.gettoken() + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + def random_str(self,length=16): + hex_chars = '0123456789abcdef' + return ''.join(random.choice(hex_chars) for _ in range(length)) + + def md5(self, text: str) -> str: + h = MD5.new() + h.update(text.encode('utf-8')) + return h.hexdigest() + + def homeContent(self, filter): + data = self.fetch(f'{self.host}/api/video/queryClassifyList?mark=4', headers=self.headers()).json()['encData'] + data1 = self.aes(data) + result = {} + classes = [] + for k in data1['data']: + classes.append({'type_name': k['classifyTitle'], 'type_id': k['classifyId']}) + result['class'] = classes + return result + + def homeVideoContent(self): + pass + + def categoryContent(self, tid, pg, filter, extend): + path=f'/api/short/video/getShortVideos?classifyId={tid}&videoMark=4&page={pg}&pageSize=20' + result = {} + videos = [] + data=self.fetch(f'{self.host}{path}', headers=self.headers()).json()['encData'] + vdata=self.aes(data) + for k in vdata['data']: + videos.append({"vod_id": k['videoId'], 'vod_name': k.get('title'), 'vod_pic': self.getProxyUrl() + '&url=' + k['coverImg'], + 'vod_remarks': self.dtim(k.get('playTime'))}) + result["list"] = videos + result["page"] = pg + result["pagecount"] = 9999 + result["limit"] = 90 + result["total"] = 999999 + return result + + def detailContent(self, ids): + path = f'/api/video/getVideoById?videoId={ids[0]}' + data = self.fetch(f'{self.host}{path}', headers=self.headers()).json()['encData'] + v = self.aes(data) + d=f'{v["title"]}$auth_key={v["authKey"]}&path={v["videoUrl"]}' + vod = {'vod_name': v["title"], 'type_name': ''.join(v.get('tagTitles',[])),'vod_play_from': v.get('nickName') or "小红书官方", 'vod_play_url': d} + result = {"list": [vod]} + return result + + def searchContent(self, key, quick, pg='1'): + pass + + def playerContent(self, flag, id, vipFlags): + h=self.headers() + h['Authorization'] = h.pop('aut') + del h['deviceid'] + result = {"parse": 0, "url": f"{self.host}/api/m3u8/decode/authPath?{id}", "header": h} + return result + + def localProxy(self, param): + return self.action(param) + + def aes(self, word): + key = b64decode("SmhiR2NpT2lKSVV6STFOaQ==") + iv = key + cipher = AES.new(key, AES.MODE_CBC, iv) + decrypted = unpad(cipher.decrypt(b64decode(word)), AES.block_size) + return json.loads(decrypted.decode('utf-8')) + + def dtim(self, seconds): + try: + seconds = int(seconds) + hours = seconds // 3600 + remaining_seconds = seconds % 3600 + minutes = remaining_seconds // 60 + remaining_seconds = remaining_seconds % 60 + + formatted_minutes = str(minutes).zfill(2) + formatted_seconds = str(remaining_seconds).zfill(2) + + if hours > 0: + formatted_hours = str(hours).zfill(2) + return f"{formatted_hours}:{formatted_minutes}:{formatted_seconds}" + else: + return f"{formatted_minutes}:{formatted_seconds}" + except: + return '' + + def getsign(self): + t=str(int(time.time() * 1000)) + return self.md5(t[3:8]) + + def gettoken(self): + url = f'{self.host}/api/user/traveler' + headers = { + 'User-Agent': 'Mozilla/5.0 (Linux; Android 11; M2012K10C Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36;SuiRui/xhs/ver=1.2.6', + 'deviceid': self.did, 't': str(int(time.time() * 1000)), 's': self.getsign(), } + data = {'deviceId': self.did, 'tt': 'U', 'code': '', 'chCode': 'dafe13'} + data1 = self.post(url, json=data, headers=headers).json() + data2 = data1['data'] + return data2['token'], data2['imgDomain'] + + host = 'https://jhfkdnov21vfd.fhoumpjjih.work' + + def headers(self): + henda = { + 'User-Agent': 'Mozilla/5.0 (Linux; Android 11; M2012K10C Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36;SuiRui/xhs/ver=1.2.6', + 'deviceid': self.did, 't': str(int(time.time() * 1000)), 's': self.getsign(), 'aut': self.token} + return henda + + def action(self, param): + headers = { + 'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 11; M2012K10C Build/RP1A.200720.011)'} + data = self.fetch(f'{self.phost}{param["url"]}', headers=headers) + type=data.headers.get('Content-Type').split(';')[0] + base64_data = self.img(data.content, 100, '2020-zq3-888') + return [200, type, base64_data] + + def img(self, data: bytes, length: int, key: str): + GIF = b'\x47\x49\x46' + JPG = b'\xFF\xD8\xFF' + PNG = b'\x89\x50\x4E\x47\x0D\x0A\x1A\x0A' + + def is_dont_need_decode_for_gif(data): + return len(data) > 2 and data[:3] == GIF + + def is_dont_need_decode_for_jpg(data): + return len(data) > 7 and data[:3] == JPG + + def is_dont_need_decode_for_png(data): + return len(data) > 7 and data[1:8] == PNG[1:8] + + if is_dont_need_decode_for_png(data): + return data + elif is_dont_need_decode_for_gif(data): + return data + elif is_dont_need_decode_for_jpg(data): + return data + else: + key_bytes = key.encode('utf-8') + result = bytearray(data) + for i in range(length): + result[i] ^= key_bytes[i % len(key_bytes)] + return bytes(result) diff --git a/py/py_推特.py b/py/py_推特.py new file mode 100644 index 00000000..e943b378 --- /dev/null +++ b/py/py_推特.py @@ -0,0 +1,217 @@ +# coding=utf-8 +# !/usr/bin/python +# by嗷呜 +import json +import sys +import time +from base64 import b64decode +from urllib.parse import quote +from Crypto.Cipher import AES +from Crypto.Hash import MD5 +from Crypto.Util.Padding import unpad +sys.path.append('..') +from base.spider import Spider + + +class Spider(Spider): + + def getName(self): + return "tuit" + + def init(self, extend=""): + self.did = MD5.new((self.t).encode()).hexdigest() + self.token = self.gettoken() + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def action(self, action): + pass + + def destroy(self): + pass + + def aes(self, word): + key = b64decode("SmhiR2NpT2lKSVV6STFOaQ==") + iv = key + cipher = AES.new(key, AES.MODE_CBC, iv) + decrypted = unpad(cipher.decrypt(b64decode(word)), AES.block_size) + return json.loads(decrypted.decode('utf-8')) + + def dtim(self, seconds): + seconds = int(seconds) + hours = seconds // 3600 + remaining_seconds = seconds % 3600 + minutes = remaining_seconds // 60 + remaining_seconds = remaining_seconds % 60 + + formatted_minutes = str(minutes).zfill(2) + formatted_seconds = str(remaining_seconds).zfill(2) + + if hours > 0: + formatted_hours = str(hours).zfill(2) + return f"{formatted_hours}:{formatted_minutes}:{formatted_seconds}" + else: + return f"{formatted_minutes}:{formatted_seconds}" + + def gettoken(self): + url = 'https://d1frehx187fm2c.cloudfront.net/api/user/traveler' + headers = { + 'User-Agent': 'Mozilla/5.0 (Linux; Android 11; M2012K10C Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36;SuiRui/twitter/ver=1.3.4', + 'deviceid': self.did, 't': self.t, 's': self.sign, } + data = {'deviceId': self.did, 'tt': 'U', 'code': '', 'chCode': ''} + data1 = self.post(url, json=data, headers=headers).json() + token = data1['data']['token'] + return token + + t = str(int(time.time() * 1000)) + sign = MD5.new((t[3:8]).encode()).hexdigest() + host = 'https://api.wcyfhknomg.work' + + def headers(self): + henda = { + 'User-Agent': 'Mozilla/5.0 (Linux; Android 11; M2012K10C Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36;SuiRui/twitter/ver=1.3.4', + 'deviceid': self.did, 't': self.t, 's': self.sign, 'aut': self.token} + return henda + + def homeContent(self, filter): + data = self.fetch(f'{self.host}/api/video/classifyList', headers=self.headers()).json()['encData'] + data1 = self.aes(data) + result = {'filters': {"1": [{"key": "fl", "name": "分类", + "value": [{"n": "最近更新", "v": "1"}, {"n": "最多播放", "v": "2"}, + {"n": "好评榜", "v": "3"}]}], "2": [{"key": "fl", "name": "分类", + "value": [ + {"n": "最近更新", "v": "1"}, + {"n": "最多播放", "v": "2"}, + {"n": "好评榜", "v": "3"}]}], + "3": [{"key": "fl", "name": "分类", + "value": [{"n": "最近更新", "v": "1"}, {"n": "最多播放", "v": "2"}, + {"n": "好评榜", "v": "3"}]}], "4": [{"key": "fl", "name": "分类", + "value": [ + {"n": "最近更新", "v": "1"}, + {"n": "最多播放", "v": "2"}, + {"n": "好评榜", "v": "3"}]}], + "5": [{"key": "fl", "name": "分类", + "value": [{"n": "最近更新", "v": "1"}, {"n": "最多播放", "v": "2"}, + {"n": "好评榜", "v": "3"}]}], "6": [{"key": "fl", "name": "分类", + "value": [ + {"n": "最近更新", "v": "1"}, + {"n": "最多播放", "v": "2"}, + {"n": "好评榜", "v": "3"}]}], + "7": [{"key": "fl", "name": "分类", + "value": [{"n": "最近更新", "v": "1"}, {"n": "最多播放", "v": "2"}, + {"n": "好评榜", "v": "3"}]}], "jx": [{"key": "type", "name": "精选", + "value": [{"n": "日榜", "v": "1"}, + {"n": "周榜", "v": "2"}, + {"n": "月榜", "v": "3"}, + {"n": "总榜", + "v": "4"}]}]}} + classes = [{'type_name': "精选", 'type_id': "jx"}] + for k in data1['data']: + classes.append({'type_name': k['classifyTitle'], 'type_id': k['classifyId']}) + result['class'] = classes + return result + + def homeVideoContent(self): + pass + + def categoryContent(self, tid, pg, filter, extend): + path = f'/api/video/queryVideoByClassifyId?pageSize=20&page={pg}&classifyId={tid}&sortType={extend.get("fl", "1")}' + if 'click' in tid: + path = f'/api/video/queryPersonVideoByType?pageSize=20&page={pg}&userId={tid.replace("click", "")}' + if tid == 'jx': + path = f'/api/video/getRankVideos?pageSize=20&page={pg}&type={extend.get("type", "1")}' + data = self.fetch(f'{self.host}{path}', headers=self.headers()).json()['encData'] + data1 = self.aes(data)['data'] + result = {} + videos = [] + for k in data1: + img = 'https://dg2ordyr4k5v3.cloudfront.net/' + k.get('coverImg')[0] + id = f'{k.get("videoId")}?{k.get("userId")}?{k.get("nickName")}' + if 'click' in tid: + id = id + 'click' + videos.append({"vod_id": id, 'vod_name': k.get('title'), 'vod_pic': self.getProxyUrl() + '&url=' + img, + 'vod_remarks': self.dtim(k.get('playTime')),'style': {"type": "rect", "ratio": 1.33}}) + result["list"] = videos + result["page"] = pg + result["pagecount"] = 9999 + result["limit"] = 90 + result["total"] = 999999 + return result + + def detailContent(self, ids): + vid = ids[0].replace('click', '').split('?') + path = f'/api/video/can/watch?videoId={vid[0]}' + data = self.fetch(f'{self.host}{path}', headers=self.headers()).json()['encData'] + data1 = self.aes(data)['playPath'] + clj = '[a=cr:' + json.dumps({'id': vid[1] + 'click', 'name': vid[2]}) + '/]' + vid[2] + '[/a]' + if 'click' in ids[0]: + clj = vid[2] + vod = {'vod_director': clj, 'vod_play_from': "推特", 'vod_play_url': vid[2] + "$" + data1} + result = {"list": [vod]} + return result + + def searchContent(self, key, quick, pg='1'): + path = f'/api/search/keyWord?pageSize=20&page={pg}&searchWord={quote(key)}&searchType=1' + data = self.fetch(f'{self.host}{path}', headers=self.headers()).json()['encData'] + data1 = self.aes(data)['videoList'] + result = {} + videos = [] + for k in data1: + img = 'https://dg2ordyr4k5v3.cloudfront.net/' + k.get('coverImg')[0] + id = f'{k.get("videoId")}?{k.get("userId")}?{k.get("nickName")}' + videos.append({"vod_id": id, 'vod_name': k.get('title'), 'vod_pic': self.getProxyUrl() + '&url=' + img, + 'vod_remarks': self.dtim(k.get('playTime')), 'style': {"type": "rect", "ratio": 1.33}}) + result["list"] = videos + result["page"] = pg + result["pagecount"] = 9999 + result["limit"] = 90 + result["total"] = 999999 + return result + + def playerContent(self, flag, id, vipFlags): + result = {"parse": 0, "url": id, "header": {'User-Agent': 'Mozilla/5.0 (Linux; Android 11; M2012K10C Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36;SuiRui/twitter/ver=1.3.4'}} + return result + + def localProxy(self, param): + return self.imgs(param) + + def imgs(self, param): + headers = { + 'User-Agent': 'Mozilla/5.0 (Linux; Android 11; M2012K10C Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36;SuiRui/twitter/ver=1.3.4'} + url = param['url'] + type = url.split('.')[-1].split('_')[0] + data = self.fetch(url,headers=headers).content + bdata = self.img(data, 100, '2020-zq3-888') + return [200, f'image/{type}', bdata] + + def img(self, data: bytes, length: int, key: str): + GIF = b'\x47\x49\x46' + JPG = b'\xFF\xD8\xFF' + PNG = b'\x89\x50\x4E\x47\x0D\x0A\x1A\x0A' + + def is_dont_need_decode_for_gif(data): + return len(data) > 2 and data[:3] == GIF + + def is_dont_need_decode_for_jpg(data): + return len(data) > 7 and data[:3] == JPG + + def is_dont_need_decode_for_png(data): + return len(data) > 7 and data[1:8] == PNG[1:8] + + if is_dont_need_decode_for_png(data): + return data + elif is_dont_need_decode_for_gif(data): + return data + elif is_dont_need_decode_for_jpg(data): + return data + else: + key_bytes = key.encode('utf-8') + result = bytearray(data) + for i in range(length): + result[i] ^= key_bytes[i % len(key_bytes)] + return bytes(result) diff --git a/py/py_浴火社.py b/py/py_浴火社.py new file mode 100644 index 00000000..f47f3b5d --- /dev/null +++ b/py/py_浴火社.py @@ -0,0 +1,350 @@ +# coding=utf-8 +# !/usr/bin/python +# by嗷呜 +import json +import re +import sys +import threading +import time +from base64 import b64decode, b64encode +import requests +from Crypto.Cipher import AES +from Crypto.Hash import MD5 +from Crypto.Util.Padding import unpad +sys.path.append('..') +from base.spider import Spider + + +class Spider(Spider): + + def init(self, extend=""): + self.did = self.getdid() + self.token=self.gettoken() + domain=self.domain() + self.phost=self.host_late(domain['domain_preview']) + self.bhost=domain['domain_original'] + self.names=domain['name_original'] + pass + + def getName(self): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + host = 'https://lulu-api-92mizw.jcdwn.com' + + headers = { + 'User-Agent': 'okhttp/4.11.0', + 'referer': 'https://app.nova-traffic-1688.com', + } + + def homeContent(self, filter): + BASE_CATEGORIES = [ + {'type_name': '片商', 'type_id': 'makers'}, + {'type_name': '演员', 'type_id': 'actor'} + ] + + SORT_OPTIONS = { + 'key': 'sortby', + 'name': 'sortby', + 'value': [ + {'n': '最新', 'v': 'on_shelf_at'}, + {'n': '最热', 'v': 'hot'} + ] + } + + tags = self.getdata('/api/v1/video/tag?current=1&pageSize=100&level=1') + producers = self.getdata('/api/v1/video/producer?current=1&pageSize=100&status=1') + regions = self.getdata('/api/v1/video/region?current=1&pageSize=100') + result = {'class': [], 'filters': {}} + result['class'].extend(BASE_CATEGORIES) + for category in BASE_CATEGORIES: + result['filters'][category['type_id']] = [SORT_OPTIONS] + if tags.get('data'): + main_tag = tags['data'][0] + result['class'].append({ + 'type_name': '发现', + 'type_id': f'{main_tag["id"]}_tag' + }) + tag_values = [ + {'n': tag['name'], 'v': f"{tag['id']}_tag"} + for tag in tags['data'][1:] + if tag.get('id') + ] + result['filters'][f'{main_tag["id"]}_tag'] = [ + {'key': 'tagtype', 'name': 'tagtype', 'value': tag_values}, + SORT_OPTIONS + ] + + region_filter = { + 'key': 'region_ids', + 'name': 'region_ids', + 'value': [ + {'n': region['name'], 'v': region['id']} + for region in regions['data'][1:] + if region.get('id') + ] + } + self.aid=regions['data'][0]['id'] + result['filters']['actor'].append({ + 'key': 'region_id', + 'name': 'region_id', + 'value': region_filter['value'][:2] + }) + complex_sort = { + 'key': 'sortby', + 'name': 'sortby', + 'value': [ + {'n': '综合', 'v': 'complex'}, + *SORT_OPTIONS['value'] + ] + } + producer_filters = [region_filter, complex_sort] + for producer in producers['data']: + result['class'].append({ + 'type_name': producer['name'], + 'type_id': f'{producer["id"]}_sx' + }) + result['filters'][f'{producer["id"]}_sx'] = producer_filters + return result + + def homeVideoContent(self): + data=self.getdata('/api/v1/video?current=1&pageSize=60®ion_ids=&sortby=complex') + return {'list':self.getlist(data)} + + def categoryContent(self, tid, pg, filter, extend): + if 'act' in tid: + data=self.getact(tid, pg, filter, extend) + elif 'tag' in tid: + data=self.gettag(tid, pg, filter, extend) + elif 'sx' in tid: + data=self.getsx(tid, pg, filter, extend) + elif 'make' in tid: + data=self.getmake(tid, pg, filter, extend) + result = {} + result['list'] = data + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + v=self.getdata(f'/api/v1/video?current=1&pageSize=1&id={ids[0]}&detail=1') + v=v['data'][0] + vod = { + 'vod_name': v.get('title'), + 'type_name': '/'.join(v.get('tag_names',[])), + 'vod_play_from': '浴火社', + 'vod_play_url': '' + } + p=[] + for i,j in enumerate(self.bhost): + p.append(f'{self.names[i]}${j}{v.get("highres_url") or v.get("preview_url")}@@@{v["id"]}') + vod['vod_play_url'] = '#'.join(p) + return {'list':[vod]} + + def searchContent(self, key, quick, pg="1"): + data=self.getdata(f'/api/v1/video?current={pg}&pageSize=30&title={key}') + return {'list':self.getlist(data),'page':pg} + + def playerContent(self, flag, id, vipFlags): + url=f'{self.getProxyUrl()}&url={self.e64(id)}&type=m3u8' + return {'parse': 0, 'url': url, 'header': self.headers} + + def localProxy(self, param): + if param.get('type')=='image': + data=self.fetch(param.get('url'), headers=self.headers).text + content=b64decode(data.encode('utf-8')) + return [200, 'image/png', content] + if param.get('type')=='m3u8': + ids=self.d64(param.get('url')).split('@@@') + data=self.fetch(ids[0], headers=self.headers).text + lines = data.strip().split('\n') + for index, string in enumerate(lines): + if 'URI=' in string: + replacement = f'URI="{self.getProxyUrl()}&id={ids[1]}&type=mkey"' + lines[index]=re.sub(r'URI="[^"]+"', replacement, string) + continue + if '#EXT' not in string and 'http' not in string: + last_slash_index = ids[0].rfind('/') + lpath = ids[0][:last_slash_index + 1] + lines[index] = f'{lpath}{string}' + data = '\n'.join(lines) + return [200, 'audio/x-mpegurl', data] + if param.get('type')=='mkey': + id=param.get('id') + headers = { + 'User-Agent': 'Mozilla/5.0 (Linux; Android 11; M2012K10C Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36', + 'authdog': self.token + } + response = self.fetch(f'{self.host}/api/v1/video/key/{id}', headers=headers) + type=response.headers.get('Content-Type') + return [200, type, response.content] + + 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 getdid(self): + did = self.md5(str(int(time.time() * 1000))) + try: + if self.getCache('did'): + return self.getCache('did') + else: + self.setCache('did', did) + return did + except Exception as e: + self.setCache('did', did) + return did + + 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 domain(self): + headers = { + 'User-Agent': 'Mozilla/5.0 (Linux; Android 11; M2012K10C Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36', + } + response = self.fetch(f'{self.host}/api/v1/system/domain', headers=headers) + return self.aes(response.content) + + def aes(self, word): + key = b64decode("amtvaWc5ZnJ2Ym5taml1eQ==") + iv = b64decode("AAEFAwQFCQcICQoLDA0ODw==") + cipher = AES.new(key, AES.MODE_CBC, iv) + decrypted = unpad(cipher.decrypt(word), AES.block_size) + return json.loads(decrypted.decode('utf-8')) + + def md5(self, text): + h = MD5.new() + h.update(text.encode('utf-8')) + return h.hexdigest() + + def gettoken(self): + headers = { + 'User-Agent': 'Mozilla/5.0 (Linux; Android 11; M2012K10C Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36', + 'cookei': self.md5(f'{self.did}+android'), + 'siteid': '11', + 'siteauthority': 'lls888.tv' + } + + json_data = { + 'app_id': 'jukjoe.zqgpi.hfzvde.sdot', + 'phone_device': 'Redmi M2012K10C', + 'device_id': self.did, + 'device_type': 'android', + 'invite_code': 'oi1o', + 'is_first': 1, + 'os_version': '11', + 'version': '8.59', + } + response = self.post(f'{self.host}/api/v1/member/device', headers=headers, json=json_data) + tdata = self.aes(response.content) + return f'{tdata["token_type"]} {tdata["access_token"]}' + + def getdata(self, path): + headers = { + 'User-Agent': 'Mozilla/5.0 (Linux; Android 11; M2012K10C Build/RP1A.200720.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/87.0.4280.141 Mobile Safari/537.36', + 'authdog': self.token + } + response = self.fetch(f'{self.host}{path}', headers=headers) + return self.aes(response.content) + + def getimg(self, path): + if not path.startswith('/'): + path = f'/{path}' + return f'{self.getProxyUrl()}&url={self.phost}{path}&type=image' + + def getlist(self,data): + videos = [] + for i in data['data']: + videos.append({ + 'vod_id': i['id'], + 'vod_name': i['title'], + 'vod_pic': self.getimg(i.get('coverphoto_h' or i.get('coverphoto_v'))), + 'style': {"type": "rect", "ratio": 1.33}}) + return videos + + def geticon(self, data, st='',style=None): + if style is None:style = {"type": "oval"} + videos = [] + for i in data['data']: + videos.append({ + 'vod_id': f'{i["id"]}{st}', + 'vod_name': i['name'], + 'vod_pic': self.getimg(i.get('icon_path')), + 'vod_tag': 'folder', + 'style': style}) + return videos + + def getact(self, tid, pg, filter, extend): + if tid == 'actor' and pg=='1': + data = self.getdata(f'/api/v1/video/actor?current=1&pageSize=999®ion_id={extend.get("region_id",self.aid)}&discover_page={pg}') + return self.geticon(data, '_act') + elif '_act' in tid: + data = self.getdata(f'/api/v1/video?current={pg}&pageSize=50&actor_ids={tid.split("_")[0]}&sortby={extend.get("sortby","on_shelf_at")}') + return self.getlist(data) + + def gettag(self, tid, pg, filter, extend): + if '_tag' in tid: + tid=extend.get('tagtype',tid) + data=self.getdata(f'/api/v1/video/tag?current={pg}&pageSize=100&level=2&parent_id={tid.split("_")[0]}') + return self.geticon(data, '_stag',{"type": "rect", "ratio": 1.33}) + elif '_stag' in tid: + data = self.getdata(f'/api/v1/video?current={pg}&pageSize=50&tag_ids={tid.split("_")[0]}&sortby={extend.get("sortby","on_shelf_at")}') + return self.getlist(data) + + def getsx(self, tid, pg, filter, extend): + data=self.getdata(f'/api/v1/video?current={pg}&pageSize=20&producer_ids={tid.split("_")[0]}®ion_ids={extend.get("region_ids","")}&sortby={extend.get("sortby","complex")}') + return self.getlist(data) + + def getmake(self, tid, pg, filter, extend): + if pg=='1': + data=self.getdata('/api/v1/video/producer?current=1&pageSize=100&status=1') + return self.geticon(data, '_sx',{"type": "rect", "ratio": 1.33}) + diff --git a/py/py_爱.py b/py/py_爱.py new file mode 100644 index 00000000..599c8057 --- /dev/null +++ b/py/py_爱.py @@ -0,0 +1,256 @@ +# coding=utf-8 +# !/usr/bin/python +# by嗷呜 +import random +import sys +from base64 import b64encode, b64decode +from concurrent.futures import ThreadPoolExecutor, as_completed +from pprint import pprint +from urllib.parse import urlencode + +sys.path.append('..') +from base.spider import Spider + + +class Spider(Spider): + + def init(self, extend=""): + self.did = self.random_str(32) + pass + + def getName(self): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + rhost = 'https://www.iqiyi.com' + + hhost='https://mesh.if.iqiyi.com' + + dhost='https://miniapp.iqiyi.com' + + headers = { + 'Origin': rhost, + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36', + 'Referer': f'{rhost}/', + } + + def homeContent(self, filter): + result = {} + cateManual = { + "全部": "1009", + "电影": "1", + "剧集": "2", + "综艺": "6", + "动漫": "4", + "儿童": "15", + "微剧": "35", + "纪录片": "3" + } + classes = [] + filters = {} + for k in cateManual: + classes.append({ + 'type_name': k, + 'type_id': cateManual[k] + }) + 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 + return result + + def homeVideoContent(self): + data=self.fetch(f'{self.hhost}/portal/lw/v5/channel/recommend?v=13.014.21150', headers=self.headers).json() + vlist = [] + for i in data['items']: + for j in i['video'][0]['data']: + id = j.get('firstId') + if id: + pu=j.get('prevue',{}).get('page_url') or j.get('page_url').split('?')[0] + id = f'{id}@{self.e64(pu)}' + vlist.append({ + 'vod_id': id, + 'vod_name': j.get('display_name'), + 'vod_pic': j.get('prevue',{}).get('image_url'), + 'vod_year': j.get('sns_score'), + 'vod_remarks': j.get('dq_updatestatus') or j.get('rank_prefix') + }) + return {'list':vlist} + + def categoryContent(self, tid, pg, filter, extend): + if pg == "1": + self.sid = '' + new_data = {'mode':'24'} + for key, value in extend.items(): + if value: + key_value_pairs = self.d64(value).split(',') + for pair in key_value_pairs: + k, v = pair.split('=') + if k in new_data: + new_data[k] += "," + v + else: + new_data[k] = v + path=f'/portal/lw/videolib/data?uid=&passport_id=&ret_num=60&version=13.014.21150&device_id={self.did}&channel_id={tid}&page_id={pg}&session={self.sid}&os=&conduit_id=&vip=0&auth&recent_selected_tag=&ad=%5B%7B%22lm%22:%225%22,%22ai%22:%225%22,%22fp%22:%226%22,%22sei%22:%22Sa867aa9d326e2bd8654d8c2a8636055e%22,%22position%22:%22library%22%7D%5D&adExt=%7B%22r%22:%221.2.1-ares6-pure%22%7D&dfp=a12f96215b2f7842a98c082799ca0c3d9236be00946701b106829754d8ece3aaf8&filter={urlencode(new_data)}' + data=self.fetch(f'{self.hhost}{path}', headers=self.headers).json() + self.sid = data['session'] + videos = [] + for i in data['data']: + id = i.get('firstId') or i.get('tv_id') + if not id: + id=i.get('play_url').split(';')[0].split('=')[-1] + if id and not i.get('h'): + id=f'{id}@{self.e64(i.get("page_url"))}' + videos.append({ + 'vod_id': id, + 'vod_name': i.get('display_name'), + 'vod_pic': i.get('album_image_url_hover'), + 'vod_year': i.get('sns_score'), + 'vod_remarks': i.get('dq_updatestatus') or i.get('pay_mark') + }) + result = {} + result['list'] = videos + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + ids = ids[0].split('@') + ids[-1] = self.d64(ids[-1]) + data = self.fetch(f'{self.dhost}/h5/mina/baidu/play/body/v1/{ids[0]}/', headers=self.headers).json() + # t=str(int(time.time()*1000)) + # e = 'ad_ext={"r":"1.2.1-ares6-pure"}&ad_param=[{"azt":"730","azd":"1000000000942","lm":1,"position":"related"},{"azt":"731","azd":"1000000000943","lm":1,"position":"recommend"},{"azt":"734","azd":"1000000000910","lm":1,"position":"middlecard"},{"azt":"738","azd":"1000000000911","lm":1,"position":"bottomcard"},{"azt":"612","azd":"1000000000695","lm":2,"position":"recommend_full"},{"azt":"","azd":"1000000000912","lm":1,"position":"rcd_image"}]&app_mode=standard&app_version=13.014.21150&auth_cookie=&conduit_id=&device_id='+self.did+'&entity_id='+ids[0]+'&entity_type=1&filter=&ext=&os=&src=pca_tvg×tamp=' + t + '&user_id=0&vip_status=0&vip_type=-1&secret_key=howcuteitis' + # sign=self.md5(e).upper() + # path=f'/tvg/v2/lw/base_info?entity_id={ids[0]}&device_id={self.did}&auth_cookie&user_id=0&vip_type=-1&vip_status=0&conduit_id&app_version=13.014.21150&ext&app_mode=standard×tamp={t}&src=pca_tvg&os&ad_param=%5B%7B%22azt%22:%22730%22,%22azd%22:%221000000000942%22,%22lm%22:1,%22position%22:%22related%22%7D,%7B%22azt%22:%22731%22,%22azd%22:%221000000000943%22,%22lm%22:1,%22position%22:%22recommend%22%7D,%7B%22azt%22:%22734%22,%22azd%22:%221000000000910%22,%22lm%22:1,%22position%22:%22middlecard%22%7D,%7B%22azt%22:%22738%22,%22azd%22:%221000000000911%22,%22lm%22:1,%22position%22:%22bottomcard%22%7D,%7B%22azt%22:%22612%22,%22azd%22:%221000000000695%22,%22lm%22:2,%22position%22:%22recommend_full%22%7D,%7B%22azt%22:%22%22,%22azd%22:%221000000000912%22,%22lm%22:1,%22position%22:%22rcd_image%22%7D%5D&ad_ext=%7B%22r%22:%221.2.1-ares6-pure%22%7D&sign={sign}' + # vdata=self.fetch(f'{self.hhost}{path}', headers=self.headers).json() + # print(vdata) + v=data['data']['playInfo'] + vod = { + 'vod_name': v.get('albumName'), + 'type_name': v.get('tags'), + 'vod_year': v.get('albumYear'), + 'vod_remarks': v.get('updateStrategy'), + 'vod_actor': v.get('mainActors'), + 'vod_director': v.get('directors'), + 'vod_content': v.get('albumDesc'), + 'vod_play_from': '爱奇艺', + 'vod_play_url': '' + } + if data.get('data') and data['data'].get('videoList') and data['data']['videoList'].get('videos'): + purl=[f'{i["shortTitle"]}${i["pageUrl"]}' for i in data['data']['videoList']['videos']] + pg=data['data']['videoList'].get('totalPages') + if pg and pg > 1: + id = v['albumId'] + pages = list(range(2, pg + 1)) + page_results = {} + with ThreadPoolExecutor(max_workers=10) as executor: + future_to_page = { + executor.submit(self.fetch_page_data, page, id): page + for page in pages + } + for future in as_completed(future_to_page): + page = future_to_page[future] + try: + result = future.result() + page_results[page] = result + except Exception as e: + print(f"Error fetching page {page}: {e}") + for page in sorted(page_results.keys()): + purl.extend(page_results[page]) + vod['vod_play_url'] = '#'.join(purl) + else: + vdata=self.fetch(f'{self.dhost}/h5/mina/baidu/play/head/v1/{ids[0]}/', headers=self.headers).json() + v=vdata['data']['playInfo'] + vod = { + 'vod_name': v.get('shortTitle'), + 'type_name': v.get('channelName'), + 'vod_year': v.get('year'), + 'vod_remarks': v.get('focus'), + 'vod_actor': v.get('mainActors'), + 'vod_director': v.get('directors'), + 'vod_content': v.get('desc'), + 'vod_play_from': '爱奇艺', + 'vod_play_url': f'{v.get("shortTitle")}${ids[-1]}' + } + return {'list':[vod]} + + def searchContent(self, key, quick, pg="1"): + data=self.fetch(f'{self.hhost}/portal/lw/search/homePageV3?key={key}¤t_page={pg}&mode=1&source=input&suggest=&version=13.014.21150&pageNum={pg}&pageSize=25&pu=&u={self.did}&scale=150&token=&userVip=0&conduit=&vipType=-1&os=&osShortName=win10&dataType=&appMode=', headers=self.headers).json() + videos = [] + vdata=data['data']['templates'] + for i in data['data']['templates']: + if i.get('intentAlbumInfos'): + vdata=[{'albumInfo': c} for c in i['intentAlbumInfos']]+vdata + + for i in vdata: + if i.get('albumInfo') and (i['albumInfo'].get('playQipuId','') or i['albumInfo'].get('qipuId')) and i['albumInfo'].get('pageUrl'): + b=i['albumInfo'] + id=f"{(b.get('playQipuId','') or b.get('qipuId'))}@{self.e64(b.get('pageUrl'))}" + videos.append({ + 'vod_id': id, + 'vod_name': b.get('title'), + 'vod_pic': b.get('img'), + 'vod_year': (b.get('year',{}) or {}).get('value'), + 'vod_remarks': b.get('subscriptContent') or b.get('channel') or b.get('vipTips') + }) + return {'list':videos,'page':pg} + + def playerContent(self, flag, id, vipFlags): + return {'parse': 1, 'url': id, 'header': ''} + + def localProxy(self, param): + pass + + def fetch_page_data(self, page, id): + try: + url = f'{self.dhost}/h5/mina/avlist/{page}/{id}/' + data = self.fetch(url, headers=self.headers).json() + return [f'{i["shortTitle"]}${i["pageUrl"]}' for i in data['data']['videoList']['videos']] + except: + return [] + + def getf(self,body): + data=self.fetch(f'{self.hhost}/portal/lw/videolib/tag?channel_id={body["type_id"]}&tagAdd=&selected_tag_name=&version=13.014.21150&device={self.did}&uid=', headers=self.headers).json() + ft = [] + # for i in data[:-1]: + for i in data: + try: + value_array = [{"n": value['text'], "v": self.e64(value['tag_param'])} for value in i['tags'] if + value.get('tag_param')] + ft.append({"key": i['group'], "name": i['group'], "value": value_array}) + except: + print(i) + return (body['type_id'], ft) + + 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: str): + 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 random_str(self,length=16): + hex_chars = '0123456789abcdef' + return ''.join(random.choice(hex_chars) for _ in range(length)) diff --git a/py/py_胖虎.py b/py/py_胖虎.py new file mode 100644 index 00000000..cab0978b --- /dev/null +++ b/py/py_胖虎.py @@ -0,0 +1,216 @@ +# coding=utf-8 +# !/usr/bin/python +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/py_腾.py b/py/py_腾.py new file mode 100644 index 00000000..45da31f6 --- /dev/null +++ b/py/py_腾.py @@ -0,0 +1,366 @@ +# coding=utf-8 +# !/usr/bin/python +# by嗷呜 +import json +import sys +import uuid +import copy +sys.path.append('..') +from base.spider import Spider +from pyquery import PyQuery as pq +from concurrent.futures import ThreadPoolExecutor, as_completed + + +class Spider(Spider): + + def init(self, extend=""): + self.dbody = { + "page_params": { + "channel_id": "", + "filter_params": "sort=75", + "page_type": "channel_operation", + "page_id": "channel_list_second_page" + } + } + self.body = self.dbody + pass + + def getName(self): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + host = 'https://v.qq.com' + + apihost = 'https://pbaccess.video.qq.com' + + headers = { + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.5410.0 Safari/537.36', + 'origin': host, + 'referer': f'{host}/' + } + + def homeContent(self, filter): + cdata = { + "电视剧": "100113", + "电影": "100173", + "综艺": "100109", + "纪录片": "100105", + "动漫": "100119", + "少儿": "100150", + "短剧": "110755" + } + result = {} + classes = [] + filters = {} + for k in cdata: + classes.append({ + 'type_name': k, + 'type_id': cdata[k] + }) + with ThreadPoolExecutor(max_workers=len(classes)) as executor: + futures = [executor.submit(self.get_filter_data, item['type_id']) for item in classes] + for future in futures: + cid, data = future.result() + if not data.get('data', {}).get('module_list_datas'): + continue + filter_dict = {} + try: + items = data['data']['module_list_datas'][-1]['module_datas'][-1]['item_data_lists']['item_datas'] + for item in items: + if not item.get('item_params', {}).get('index_item_key'): + continue + params = item['item_params'] + filter_key = params['index_item_key'] + if filter_key not in filter_dict: + filter_dict[filter_key] = { + 'key': filter_key, + 'name': params['index_name'], + 'value': [] + } + filter_dict[filter_key]['value'].append({ + 'n': params['option_name'], + 'v': params['option_value'] + }) + except (IndexError, KeyError): + continue + filters[cid] = list(filter_dict.values()) + result['class'] = classes + result['filters'] = filters + return result + + def homeVideoContent(self): + vlist = [] + data = self.gethtml(self.host) + its = data('script') + s = None + for it in its.items(): + if 'window.__INITIAL_STATE__' in it.text(): + s = it.text() + break + if s: + index = s.find('=') + if index != -1: + sd = json.loads(s[index + 1:]) + if sd.get('storeModulesData', {}).get('channelsModulesMap', {}).get('choice', {}).get('cardListData'): + for its in sd['storeModulesData']['channelsModulesMap']['choice']['cardListData']: + if its and its.get('children_list', {}).get('list', {}).get('cards'): + for it in its['children_list']['list']['cards']: + if it and it.get('params'): + p = it['params'] + tag = json.loads(p.get('uni_imgtag', '{}') or p.get('imgtag', '{}') or '{}') + id = it.get('id') or p.get('cid') + name = p.get('mz_title') or p.get('title') + if name and 'http' not in id: + vlist.append({ + 'vod_id': id, + 'vod_name': name, + 'vod_pic': p.get('image_url'), + 'vod_year': tag.get('tag_2', {}).get('text'), + 'vod_remarks': tag.get('tag_4', {}).get('text') + }) + return {'list': vlist} + + def categoryContent(self, tid, pg, filter, extend): + result = {} + params = { + "sort": extend.get('sort', '75'), + "attr": extend.get('attr', '-1'), + "itype": extend.get('itype', '-1'), + "ipay": extend.get('ipay', '-1'), + "iarea": extend.get('iarea', '-1'), + "iyear": extend.get('iyear', '-1'), + "theater": extend.get('theater', '-1'), + "award": extend.get('award', '-1'), + "recommend": extend.get('recommend', '-1') + } + if pg == '1': + self.body = self.dbody.copy() + self.body['page_params']['channel_id'] = tid + self.body['page_params']['filter_params'] = self.josn_to_params(params) + data = self.post( + f'{self.apihost}/trpc.universal_backend_service.page_server_rpc.PageServer/GetPageData?video_appid=1000005&vplatform=2&vversion_name=8.9.10&new_mark_label_enabled=1', + json=self.body, headers=self.headers).json() + ndata = data['data'] + if ndata['has_next_page']: + result['pagecount'] = 9999 + self.body['page_context'] = ndata['next_page_context'] + else: + result['pagecount'] = int(pg) + vlist = [] + for its in ndata['module_list_datas'][-1]['module_datas'][-1]['item_data_lists']['item_datas']: + id = its.get('item_params', {}).get('cid') + if id: + p = its['item_params'] + tag = json.loads(p.get('uni_imgtag', '{}') or p.get('imgtag', '{}') or '{}') + name = p.get('mz_title') or p.get('title') + pic = p.get('new_pic_hz') or p.get('new_pic_vt') + vlist.append({ + 'vod_id': id, + 'vod_name': name, + 'vod_pic': pic, + 'vod_year': tag.get('tag_2', {}).get('text'), + 'vod_remarks': tag.get('tag_4', {}).get('text') + }) + result['list'] = vlist + result['page'] = pg + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + vbody = { + "page_params": { + "req_from": "web", + "cid": ids[0], + "vid": "", + "lid": "", + "page_type": "detail_operation", + "page_id": "detail_page_introduction" + }, + "has_cache": 1 + } + + body = { + "page_params": { + "req_from": "web_vsite", + "page_id": "vsite_episode_list", + "page_type": "detail_operation", + "id_type": "1", + "page_size": "", + "cid": ids[0], + "vid": "", + "lid": "", + "page_num": "", + "page_context": "", + "detail_page_type": "1" + }, + "has_cache": 1 + } + + with ThreadPoolExecutor(max_workers=2) as executor: + future_detail = executor.submit(self.get_vdata, vbody) + future_episodes = executor.submit(self.get_vdata, body) + vdata = future_detail.result() + data = future_episodes.result() + + pdata = self.process_tabs(data, body, ids) + if not pdata: + return self.handle_exception(None, "No pdata available") + + try: + star_list = vdata['data']['module_list_datas'][0]['module_datas'][0]['item_data_lists']['item_datas'][ + 0].get('sub_items', {}).get('star_list', {}).get('item_datas', []) + actors = [star['item_params']['name'] for star in star_list] + names = ['腾讯视频', '预告片'] + plist, ylist = self.process_pdata(pdata, ids) + if not plist: + del names[0] + if not ylist: + del names[1] + vod = self.build_vod(vdata, actors, plist, ylist, names) + return {'list': [vod]} + except Exception as e: + return self.handle_exception(e, "Error processing detail") + + def searchContent(self, key, quick, pg="1"): + body = {"version": "24072901", "clientType": 1, "filterValue": "", "uuid": str(uuid.uuid4()), "retry": 0, + "query": key, "pagenum": int(pg) - 1, "pagesize": 30, "queryFrom": 0, "searchDatakey": "", + "transInfo": "", "isneedQc": True, "preQid": "", "adClientInfo": "", + "extraInfo": {"isNewMarkLabel": "1", "multi_terminal_pc": "1"}} + data = self.post(f'{self.apihost}/trpc.videosearch.mobile_search.MultiTerminalSearch/MbSearch?vplatform=2', + json=body, headers=self.headers).json() + vlist = [] + for k in data['data']['areaBoxList'][-1]['itemList']: + if k.get('doc', {}).get('id'): + img_tag = k.get('videoInfo', {}).get('imgTag') + if img_tag is not None and isinstance(img_tag, str): + try: + tag = json.loads(img_tag) + except json.JSONDecodeError as e: + tag = {} + else: + tag = {} + pic = k.get('videoInfo', {}).get('imgUrl') + vlist.append({ + 'vod_id': k['doc']['id'], + 'vod_name': k['videoInfo']['title'], + 'vod_pic': pic, + 'vod_year': tag.get('tag_2', {}).get('text', ''), + 'vod_remarks': tag.get('tag_4', {}).get('text', '') + }) + return {'list': vlist, 'page': pg} + + def playerContent(self, flag, id, vipFlags): + ids = id.split('@') + url = f"{self.host}/x/cover/{ids[0]}/{ids[1]}.html" + return {'parse': 1, 'url': url, 'header': ''} + + def localProxy(self, param): + pass + + def gethtml(self, url): + rsp = self.fetch(url, headers=self.headers) + rsp = self.cleanText(rsp.text) + return pq(rsp) + + def get_filter_data(self, cid): + hbody = self.dbody.copy() + hbody['page_params']['channel_id'] = cid + data = self.post( + f'{self.apihost}/trpc.universal_backend_service.page_server_rpc.PageServer/GetPageData?video_appid=1000005&vplatform=2&vversion_name=8.9.10&new_mark_label_enabled=1', + json=hbody, headers=self.headers).json() + return cid, data + + def get_vdata(self, body): + try: + vdata = self.post( + f'{self.apihost}/trpc.universal_backend_service.page_server_rpc.PageServer/GetPageData?video_appid=3000010&vplatform=2&vversion_name=8.2.96', + json=body, headers=self.headers + ).json() + # print(body) + return vdata + except Exception as e: + print(f"Error in get_vdata: {str(e)}") + return {'data': {'module_list_datas': []}} + + def process_pdata(self, pdata, ids): + plist = [] + ylist = [] + for k in pdata: + if k.get('item_id'): + pid = f"{k['item_params']['union_title']}${ids[0]}@{k['item_id']}" + if '预告' in k['item_params']['union_title']: + ylist.append(pid) + else: + plist.append(pid) + return plist, ylist + + def build_vod(self, vdata, actors, plist, ylist, names): + d = vdata['data']['module_list_datas'][0]['module_datas'][0]['item_data_lists']['item_datas'][0]['item_params'] + urls = [] + if plist: + urls.append('#'.join(plist)) + if ylist: + urls.append('#'.join(ylist)) + vod = { + 'type_name': d.get('sub_genre', ''), + 'vod_name': d.get('title', ''), + 'vod_year': d.get('year', ''), + 'vod_area': d.get('area_name', ''), + 'vod_remarks': d.get('holly_online_time', '') or d.get('hotval', ''), + 'vod_actor': ','.join(actors), + 'vod_content': d.get('cover_description', ''), + 'vod_play_from': '$$$'.join(names), + 'vod_play_url': '$$$'.join(urls) + } + return vod + + def handle_exception(self, e, message): + print(f"{message}: {str(e)}") + return {'list': [{'vod_play_from': '哎呀翻车啦', 'vod_play_url': '翻车啦#555'}]} + + def process_tabs(self, data, body, ids): + try: + pdata = data['data']['module_list_datas'][-1]['module_datas'][-1]['item_data_lists']['item_datas'] + tabs = data['data']['module_list_datas'][-1]['module_datas'][-1]['module_params'].get('tabs') + if tabs and len(json.loads(tabs)): + tabs = json.loads(tabs) + remaining_tabs = tabs[1:] + task_queue = [] + for tab in remaining_tabs: + nbody = copy.deepcopy(body) + nbody['page_params']['page_context'] = tab['page_context'] + task_queue.append(nbody) + with ThreadPoolExecutor(max_workers=10) as executor: + future_map = {executor.submit(self.get_vdata, task): idx for idx, task in enumerate(task_queue)} + results = [None] * len(task_queue) + for future in as_completed(future_map.keys()): + idx = future_map[future] + results[idx] = future.result() + for result in results: + if result: + page_data = result['data']['module_list_datas'][-1]['module_datas'][-1]['item_data_lists'][ + 'item_datas'] + pdata.extend(page_data) + return pdata + except Exception as e: + print(f"Error processing episodes: {str(e)}") + return [] + + def josn_to_params(self, params, skip_empty=False): + query = [] + for k, v in params.items(): + if skip_empty and not v: + continue + query.append(f"{k}={v}") + return "&".join(query) + + diff --git a/py/py_芒.py b/py/py_芒.py new file mode 100644 index 00000000..b804e9fd --- /dev/null +++ b/py/py_芒.py @@ -0,0 +1,207 @@ +# coding=utf-8 +# !/usr/bin/python +# by嗷呜 +import sys +import time +from concurrent.futures import ThreadPoolExecutor, as_completed +sys.path.append('..') +from base.spider import Spider + + +class Spider(Spider): + + def init(self, extend=""): + pass + + def getName(self): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + rhost='https://www.mgtv.com' + + host='https://pianku.api.mgtv.com' + + vhost='https://pcweb.api.mgtv.com' + + mhost='https://dc.bz.mgtv.com' + + shost='https://mobileso.bz.mgtv.com' + + headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.61 Chrome/126.0.6478.61 Not/A)Brand/8 Safari/537.36', + 'origin': rhost, + 'referer': f'{rhost}/' + } + + def homeContent(self, filter): + result = {} + cateManual = { + "电影": "3", + "电视剧": "2", + "综艺": "1", + "动画": "50", + "少儿": "10", + "纪录片": "51", + "教育": "115" + } + classes = [] + filters = {} + for k in cateManual: + classes.append({ + 'type_name': k, + 'type_id': cateManual[k] + }) + 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 + return result + + def homeVideoContent(self): + data=self.fetch(f'{self.mhost}/dynamic/v1/channel/index/0/0/0/1000000/0/0/17/1354?type=17&version=5.0&t={str(int(time.time()*1000))}&_support=10000000', headers=self.headers).json() + videoList = [] + for i in data['data']: + if i.get('DSLList') and len(i['DSLList']): + for j in i['DSLList']: + if j.get('data') and j['data'].get('items') and len(j['data']['items']): + for k in j['data']['items']: + videoList.append({ + 'vod_id': k["videoId"], + 'vod_name': k['videoName'], + 'vod_pic': k['img'], + 'vod_year': k.get('cornerTitle'), + 'vod_remarks': k.get('time') or k.get('desc'), + }) + return {'list':videoList} + + def categoryContent(self, tid, pg, filter, extend): + body={ + 'allowedRC': '1', + 'platform': 'pcweb', + 'channelId': tid, + 'pn': pg, + 'pc': '80', + 'hudong': '1', + '_support': '10000000' + } + body.update(extend) + data=self.fetch(f'{self.host}/rider/list/pcweb/v3', params=body, headers=self.headers).json() + videoList = [] + for i in data['data']['hitDocs']: + videoList.append({ + 'vod_id': i["playPartId"], + 'vod_name': i['title'], + 'vod_pic': i['img'], + 'vod_year': (i.get('rightCorner',{}) or {}).get('text') or i.get('year'), + 'vod_remarks': i['updateInfo'] + }) + result = {} + result['list'] = videoList + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + vbody={'allowedRC': '1', 'vid': ids[0], 'type': 'b', '_support': '10000000'} + vdata=self.fetch(f'{self.vhost}/video/info', params=vbody, headers=self.headers).json() + d=vdata['data']['info']['detail'] + vod = { + 'vod_name': vdata['data']['info']['title'], + 'type_name': d.get('kind'), + 'vod_year': d.get('releaseTime'), + 'vod_area': d.get('area'), + 'vod_lang': d.get('language'), + 'vod_remarks': d.get('updateInfo'), + 'vod_actor': d.get('leader'), + 'vod_director': d.get('director'), + 'vod_content': d.get('story'), + 'vod_play_from': '芒果TV', + 'vod_play_url': '' + } + data,pdata=self.fetch_page_data('1', ids[0],True) + pagecount=data['data'].get('total_page') or 1 + if int(pagecount)>1: + pages = list(range(2, pagecount+1)) + page_results = {} + with ThreadPoolExecutor(max_workers=10) as executor: + future_to_page = { + executor.submit(self.fetch_page_data, page, ids[0]): page + for page in pages + } + for future in as_completed(future_to_page): + page = future_to_page[future] + try: + result = future.result() + page_results[page] = result + except Exception as e: + print(f"Error fetching page {page}: {e}") + for page in sorted(page_results.keys()): + pdata.extend(page_results[page]) + vod['vod_play_url'] = '#'.join(pdata) + return {'list':[vod]} + + def searchContent(self, key, quick, pg="1"): + data=self.fetch(f'{self.shost}/applet/search/v1?channelCode=mobile-wxap&q={key}&pn={pg}&pc=10&_support=10000000', headers=self.headers).json() + videoList = [] + for i in data['data']['contents']: + if i.get('data') and len(i['data']): + k = i['data'][0] + if k.get('vid') and k.get('img'): + try: + videoList.append({ + 'vod_id': k['vid'], + 'vod_name': k['title'], + 'vod_pic': k['img'], + 'vod_year': (i.get('rightTopCorner',{}) or {}).get('text') or i.get('year'), + 'vod_remarks': '/'.join(i.get('desc',[])), + }) + except: + print(k) + return {'list':videoList,'page':pg} + + def playerContent(self, flag, id, vipFlags): + id=f'{self.rhost}{id}' + return {'parse': 1, 'url': id, 'header': ''} + + def localProxy(self, param): + pass + + def getf(self, body): + params = { + 'allowedRC': '1', + 'channelId': body['type_id'], + 'platform': 'pcweb', + '_support': '10000000', + } + data = self.fetch(f'{self.host}/rider/config/channel/v1', params=params, headers=self.headers).json() + ft = [] + for i in data['data']['listItems']: + try: + value_array = [{"n": value['tagName'], "v": value['tagId']} for value in i['items'] if + value.get('tagName')] + ft.append({"key": i['eName'], "name": i['typeName'], "value": value_array}) + except: + print(i) + return body['type_id'], ft + + def fetch_page_data(self, page, id, b=False): + body = {'version': '5.5.35', 'video_id': id, 'page': page, 'size': '30', + 'platform': '4', 'src': 'mgtv', 'allowedRC': '1', '_support': '10000000'} + data = self.fetch(f'{self.vhost}/episode/list', params=body, headers=self.headers).json() + ldata = [f'{i["t3"]}${i["url"]}' for i in data['data']['list']] + if b: + return data, ldata + else: + return ldata diff --git a/py/py_视觉.py b/py/py_视觉.py new file mode 100644 index 00000000..9f16d418 --- /dev/null +++ b/py/py_视觉.py @@ -0,0 +1,241 @@ +# coding=utf-8 +# !/usr/bin/python +# 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/py_金牌.py b/py/py_金牌.py new file mode 100644 index 00000000..6cb0a950 --- /dev/null +++ b/py/py_金牌.py @@ -0,0 +1,225 @@ +# coding=utf-8 +# !/usr/bin/python +import json +import sys +import threading +import uuid +from pprint import pprint +import requests +sys.path.append('..') +from base.spider import Spider +import time +from Crypto.Hash import MD5, SHA1 + +class Spider(Spider): + + def init(self, extend=""): + ''' + { + "key": "", + "name": "", + "type": 3, + "api": "", + "searchable": 1, + "quickSearch": 1, + "filterable": 1, + "ext": { + "site": "https://www.tjrongze.com,https://www.jiabaide.cn,https://cqzuoer.com" + } + }, + fm写法 + ''' + if extend: + hosts=json.loads(extend)['site'] + # hosts = "https://www.tjrongze.com,https://www.jiabaide.cn,https://cqzuoer.com" + self.host = self.host_late(hosts) + pass + + def getName(self): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def destroy(self): + pass + + def homeContent(self, filter): + cdata = self.fetch(f"{self.host}/api/mw-movie/anonymous/get/filer/type", headers=self.getheaders()).json() + fdata = self.fetch(f"{self.host}/api/mw-movie/anonymous/v1/get/filer/list", headers=self.getheaders()).json() + result = {} + classes = [] + filters={} + for k in cdata['data']: + classes.append({ + 'type_name': k['typeName'], + 'type_id': str(k['typeId']), + }) + sort_values = [{"n": "最近更新", "v": "2"},{"n": "人气高低", "v": "3"}, {"n": "评分高低", "v": "4"}] + for tid, d in fdata['data'].items(): + current_sort_values = sort_values.copy() + if tid == '1': + del current_sort_values[0] + filters[tid] = [ + {"key": "type", "name": "类型", + "value": [{"n": i["itemText"], "v": i["itemValue"]} for i in d["typeList"]]}, + + *([] if not d["plotList"] else [{"key": "v_class", "name": "剧情", + "value": [{"n": i["itemText"], "v": i["itemText"]} + for i in d["plotList"]]}]), + + {"key": "area", "name": "地区", + "value": [{"n": i["itemText"], "v": i["itemText"]} for i in d["districtList"]]}, + + {"key": "year", "name": "年份", + "value": [{"n": i["itemText"], "v": i["itemText"]} for i in d["yearList"]]}, + + {"key": "lang", "name": "语言", + "value": [{"n": i["itemText"], "v": i["itemText"]} for i in d["languageList"]]}, + + {"key": "sort", "name": "排序", "value": current_sort_values} + ] + result['class'] = classes + result['filters'] = filters + return result + + def homeVideoContent(self): + data1 = self.fetch(f"{self.host}/api/mw-movie/anonymous/v1/home/all/list", headers=self.getheaders()).json() + data2=self.fetch(f"{self.host}/api/mw-movie/anonymous/home/hotSearch",headers=self.getheaders()).json() + data=[] + for i in data1['data'].values(): + data.extend(i['list']) + data.extend(data2['data']) + vods=self.getvod(data) + return {'list':vods} + + def categoryContent(self, tid, pg, filter, extend): + + params = { + "area": extend.get('area', ''), + "filterStatus": "1", + "lang": extend.get('lang', ''), + "pageNum": pg, + "pageSize": "30", + "sort": extend.get('sort', '1'), + "sortBy": "1", + "type": extend.get('type', ''), + "type1": tid, + "v_class": extend.get('v_class', ''), + "year": extend.get('year', '') + } + data = self.fetch(f"{self.host}/api/mw-movie/anonymous/video/list?{self.js(params)}", headers=self.getheaders(params)).json() + result = {} + result['list'] = self.getvod(data['data']['list']) + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + data=self.fetch(f"{self.host}/api/mw-movie/anonymous/video/detail?id={ids[0]}",headers=self.getheaders({'id':ids[0]})).json() + vod=self.getvod([data['data']])[0] + vod['vod_play_from']='嗷呜有金牌' + vod['vod_play_url'] = '#'.join( + f"{i['name'] if len(vod['episodelist']) > 1 else vod['vod_name']}${ids[0]}@@{i['nid']}" for i in + vod['episodelist']) + vod.pop('episodelist', None) + return {'list':[vod]} + + def searchContent(self, key, quick, pg="1"): + params = { + "keyword": key, + "pageNum": pg, + "pageSize": "8", + "sourceCode": "1" + } + data=self.fetch(f"{self.host}/api/mw-movie/anonymous/video/searchByWord?{self.js(params)}",headers=self.getheaders(params)).json() + vods=self.getvod(data['data']['result']['list']) + return {'list':vods,'page':pg} + + def playerContent(self, flag, id, vipFlags): + self.header = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.61 Chrome/126.0.6478.61 Not/A)Brand/8 Safari/537.36', + 'sec-ch-ua-platform': '"Windows"', + 'DNT': '1', + 'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Google Chrome";v="126"', + 'sec-ch-ua-mobile': '?0', + 'Origin': self.host, + 'Referer': f'{self.host}/' + } + ids=id.split('@@') + pdata=self.fetch(f"{self.host}/api/mw-movie/anonymous/v1/video/episode/url?id={ids[0]}&nid={ids[1]}",headers=self.getheaders({'id':ids[0],'nid':ids[1]})).json() + return {'parse':0,'url':pdata['data']['playUrl'],'header':self.header} + + def localProxy(self, param): + pass + + def host_late(self, url_list): + if isinstance(url_list, str): + urls = [u.strip() for u in url_list.split(',')] + else: + urls = url_list + if len(urls) <= 1: + return urls[0] if urls else '' + + results = {} + threads = [] + + def test_host(url): + try: + start_time = time.time() + response = requests.head(url, timeout=1.0, allow_redirects=False) + delay = (time.time() - start_time) * 1000 + results[url] = delay + except Exception as e: + results[url] = float('inf') + for url in urls: + t = threading.Thread(target=test_host, args=(url,)) + threads.append(t) + t.start() + for t in threads: + t.join() + return min(results.items(), key=lambda x: x[1])[0] + + def md5(self, sign_key): + md5_hash = MD5.new() + md5_hash.update(sign_key.encode('utf-8')) + md5_result = md5_hash.hexdigest() + return md5_result + + def js(self, param): + return '&'.join(f"{k}={v}" for k, v in param.items()) + + def getheaders(self, param=None): + if param is None:param = {} + t=str(int(time.time()*1000)) + param['key']='cb808529bae6b6be45ecfab29a4889bc' + param['t']=t + sha1_hash = SHA1.new() + sha1_hash.update(self.md5(self.js(param)).encode('utf-8')) + sign = sha1_hash.hexdigest() + deviceid = str(uuid.uuid4()) + headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.61 Chrome/126.0.6478.61 Not/A)Brand/8 Safari/537.36', + 'Accept': 'application/json, text/plain, */*', + 'sign': sign, + 't': t, + 'deviceid':deviceid + } + return headers + + def convert_field_name(self, field): + field = field.lower() + if field.startswith('vod') and len(field) > 3: + field = field.replace('vod', 'vod_') + if field.startswith('type') and len(field) > 4: + field = field.replace('type', 'type_') + return field + + def getvod(self, array): + return [{self.convert_field_name(k): v for k, v in item.items()} for item in array] +