From cd02ba1e88b0fd97950bd5c9a9a75b8bc2ebc86d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=99=9A=E9=A3=8E=E6=8B=82=E6=9F=B3=E9=A2=9C?= <434857005@qq.com> Date: Thu, 25 Jan 2024 19:55:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=96=B5=E6=AC=A1=E5=85=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/version.txt | 2 +- txt/hipy/base_spider.py | 14 +- txt/hipy/喵次元.py | 398 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 412 insertions(+), 2 deletions(-) create mode 100644 txt/hipy/喵次元.py diff --git a/js/version.txt b/js/version.txt index b8b685a..2abd5b9 100644 --- a/js/version.txt +++ b/js/version.txt @@ -1 +1 @@ -3.9.49beta27 \ No newline at end of file +3.9.49beta28 \ No newline at end of file diff --git a/txt/hipy/base_spider.py b/txt/hipy/base_spider.py index 147e8e4..7a33b95 100644 --- a/txt/hipy/base_spider.py +++ b/txt/hipy/base_spider.py @@ -294,7 +294,8 @@ class Spider(BaseSpider): # 元类 默认的元类 type @param vipFlags: vip标识 @return: """ - url = 'http://bizcommon.alicdn.com/l2nDqpMmn6DGHnWzZQA/Cg9qI5imMInpPvK5Mnm%40%40hd.m3u8' + # url = 'http://bizcommon.alicdn.com/l2nDqpMmn6DGHnWzZQA/Cg9qI5imMInpPvK5Mnm%40%40hd.m3u8' + url = 'https://s1.bfzycdn.com/video/renmindemingyi/%E7%AC%AC07%E9%9B%86/index.m3u8' parse = 0 headers = { 'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1' @@ -307,6 +308,10 @@ class Spider(BaseSpider): # 元类 默认的元类 type } return result + @staticmethod + def adRemove(): + return 'reg:/video/adjump.*?ts' + config = { "player": {}, "filter": {} @@ -411,3 +416,10 @@ if __name__ == '__main__': a = spider.superStr2dict(code) print(type(a), a) # spider.searchContent('斗罗大陆') + print(spider.playerContent(None, 1, None)) + with open('ad.m3u8', encoding='utf-8') as f: + adt = f.read() + url = adt.split('\n')[0] + adt = '\n'.join(adt.split('\n')[1:]) + ad_remove = 'reg:/video/adjump(.*?)ts' + print(spider.fixAdM3u8(adt, url, ad_remove)) diff --git a/txt/hipy/喵次元.py b/txt/hipy/喵次元.py new file mode 100644 index 0000000..55aa3f1 --- /dev/null +++ b/txt/hipy/喵次元.py @@ -0,0 +1,398 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# File : 喵次元.py +# Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------ +# Author's Blog: https://blog.csdn.net/qq_32394351 +# Date : 2024/1/17 + +import sys +import time + +sys.path.append('..') +try: + from base.spider import BaseSpider +except ImportError: + from t4.base.spider import BaseSpider + +""" +配置示例: +t4的配置里ext节点会自动变成api对应query参数extend,但t4的ext字符串不支持路径格式,比如./开头或者.json结尾 +api里会自动含有ext参数是base64编码后的选中的筛选条件 + { + "key":"hipy_t4_喵次元", + "name":"喵次元(hipy_t4)", + "type":4, + "api":"http://192.168.31.49:5707/api/v1/vod/喵次元", + "searchable":1, + "quickSearch":0, + "filterable":1, + "ext":"" +}, +{ + "key": "hipy_t3_喵次元", + "name": "喵次元(hipy_t3)", + "type": 3, + "api": "{{host}}/txt/hipy/喵次元.py", + "searchable": 1, + "quickSearch": 0, + "filterable": 1, + "ext": "" +}, +""" + +# 全局变量 +gParam = { + "HomeDict": {}, + "TypeDict": {}, +} + + +class Spider(BaseSpider): # 元类 默认的元类 type + key: str = 'sLunqcoH85Nm/jDmFKns7A== ' + key_str: str = 'sLunqcoH85Nm/jDmFKns7A==' + iv: str = 'fedcba9876543210' + token: str = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJBcHBUbyIsImlhdCI6MTcwMDA3MTcwMiwiZXhwIjoxNzMxNjA3NzAyLCJuYmYiOjE3MDAwNzE3MDIsInN1YiI6IkFwcFRvIiwianRpIjoiYzRjNTAzOTQxYTM4NWI1MDMyMTAyYmY3Yzk1OGY4MzEiLCJkYXRhIjp7InVzZXJfaWQiOjI0ODc1NCwidXNlcl9jaGVjayI6ImUzYmQ3NmNhNTJhMGY4NjAwMTdjNjdkZGUwN2QzZTM3IiwidXNlcl9uYW1lIjoiaGV6aWh1aSJ9fQ.4LWs3rNL-os8_Pqa9LgKtvVG5f0aIxVyAjYIagvO1F4' + ic: str = 'bmXes2xsCWvsSdfYav0s9D78Ly7w1o%2BOYXApKx6SUd4NWKsTsapbS52l7y%2FsTVCM2kcoLws2jryaDQlHLse5fxD2B2VXZXfaQo0eMTOv2Xq7CKoPa51uVt8WiIY2SPztc7wxGE89%2Fcw2Q3n85uUT3A%3D%3D' + api: str = 'https://cym.zhui.la/api.php' + api_cofig: str = api + '/type/get_list' + api_home: str = api + '/video/index' + api_cate: str = api + '/video/get_list' + api_search: str = api + '/video/get_list' + api_detail: str = api + '/video/get_detail' + api_tabs: str = api + '/video/get_player' + api_parse: str = api + '/video/get_definition' + params: dict = {"versionName": "5.6.9", "uuid": "9cc01079c64e2495", "version": "4835d0a2", "versionCode": "35"} + + def getName(self): + return "喵次元" + + def init(self, extend=""): + """ + 初始化加载extend,一般与py文件名同名的json文件作为扩展筛选 + @param extend: + @return: + """ + ext = self.extend + self.log(f'ext:{ext}') + key = self.key_str + # 转hex + key_hex_str = self.bytesToHexString(key.encode('utf-8')) + # 右侧补16个0 + key_hex_str += '0' * 16 + key_hex = key_hex_str + # key_hex = '734C756E71636F4838354E6D2F6A446D464B6E7337413D3D0000000000000000' + # 转回来 + key = self.hexStringTobytes(key_hex).decode('utf-8') + self.key = key + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def homeContent(self, filterable=False): + """ + 获取首页分类及筛选数据 + @param filterable: 能否筛选,跟t3/t4配置里的filterable参数一致 + @return: + """ + filter_names = { + 'class': '分类', + 'area': '地区', + 'lang': '语言', + 'year': '年份', + 'star': '明星', + 'director': '导演', + 'state': '状态', + 'version': '版本', + } + ret = self.fetch(self.api_cofig).json() + data = self.decode(ret['data']) + result = {} + classes = [] + filters = {} + type_dict = {} + for tp in data.get('list') or []: + classes.append({ + 'type_name': tp['type_name'], + 'type_id': tp['type_id'] + }) + type_dict[str(tp['type_id'])] = tp['type_name'] + tp_filters = [] + for key, value in tp['type_extend'].items(): + if value: + tp_filters.append({ + 'key': key, + 'name': filter_names.get(key) or key, + 'value': [{'n': '全部', 'v': ''}] + [{'n': i, 'v': i} for i in value.split(',') if i] + }) + filters[tp['type_id']] = tp_filters + + result['class'] = classes + if filterable: + result['filters'] = filters + global gParam + gParam['HomeDict'].update(result) + gParam['TypeDict'].update(type_dict) + return result + + def homeVideoContent(self): + """ + 首页推荐列表 + @return: + """ + ret = self.fetch(self.api_home).json() + data = self.decode(ret['data']) + # print(data) + d = [] + for cate_data in data: + items = cate_data['video'] + for item in items: + d.append({ + 'vod_name': item['vod_name'], + 'vod_id': item['vod_id'], + 'vod_pic': item['vod_pic'], + 'vod_remarks': item['vod_remarks'], + }) + result = { + 'list': d + } + return result + + def categoryContent(self, tid, pg, filterable, extend): + """ + 返回一级列表页数据 + @param tid: 分类id + @param pg: 当前页数 + @param filterable: 能否筛选 + @param extend: 当前筛选数据 + @return: + """ + page_count = 21 # 默认赋值一页列表21条数据|这个值一定要写正确看他默认一页多少条 + fls = extend.keys() # 哪些刷新数据 + new_params = self.params.copy() + new_params.update({'type_id': str(tid), 'limit': str(page_count), 'page': str(pg), + 'orderby': '', 'ctime': str(int(time.time())) + }) + for fl in fls: + new_params[f'vod_{fl}'] = extend[fl] + + params = self.get_sign_params(new_params) + # print(params) + r = self.postJson(self.api_cate, json=params) + ret = r.json() + data = self.decode(ret['data']) + # print(data) + d = data['list'] + result = { + 'list': d, + 'page': pg, + 'pagecount': 9999 if len(d) >= page_count else pg, + 'limit': 90, + 'total': data['count'], + } + return result + + def detailContent(self, ids): + """ + 返回二级详情页数据 + @param ids: 一级传过来的vod_id列表 + @return: + """ + # id=110102 + vod_id = ids[0] + new_params = self.params.copy() + new_params.update({'vod_id': str(vod_id), 'ctime': str(int(time.time()))}) + params = self.get_sign_params(new_params) + # print(params) + r = self.postJson(self.api_detail, json=params) + ret = r.json() + data = self.decode(ret['data']) + # print(data) + vod = {"vod_id": vod_id, + "vod_name": data['vod_name'], + "vod_pic": data['vod_pic'], + "type_name": data['vod_en'], + "vod_year": data['vod_year'], + "vod_area": data['vod_area'], + "vod_remarks": data['vod_remarks'], + "vod_actor": data['vod_actor'], + "vod_director": data['vod_director'], + "vod_content": data['vod_blurb'], + } + episodes = data['player'] + play_map = {} + play_from = [] + play_list = [] + for ep in episodes: + player = ep["code"] + source = ep["name"] + new_params = self.params.copy() + new_params.update({ + 'vod_id': str(vod_id), 'ctime': str(int(time.time())), + 'limit': str(5000), 'page': str(1), + 'player': player, + }) + params = self.get_sign_params(new_params) + r = self.postJson(self.api_tabs, json=params) + ret = r.json() + data = self.decode(ret['data']) + # print(data) + for playurl in data['list']: + if source not in play_map: + play_map[source] = [] + play_map[source].append( + playurl["drama"] + "$" + '&'.join( + [str(playurl["ju_id"]), str(playurl["plyer"]), str(playurl["video_id"])])) + + for key, value in play_map.items(): + play_from.append(key) + play_list.append('#'.join(value)) + + vod['vod_play_from'] = '$$$'.join(play_from) + vod['vod_play_url'] = '$$$'.join(play_list) + result = { + 'list': [vod] + } + # print(vod) + return result + + def searchContent(self, wd, quick=False, pg=1): + """ + 返回搜索列表 + @param wd: 搜索关键词 + @param quick: 是否来自快速搜索。t3/t4配置里启用了快速搜索,在快速搜索在执行才会是True + @param pg: 页数 + @return: + """ + page_count = 21 # 默认赋值一页列表21条数据|这个值一定要写正确看他默认一页多少条 + new_params = self.params.copy() + new_params.update({ + 'orderby': 'up', 'ctime': str(int(time.time())), + 'limit': str(page_count), 'page': str(pg), 'vod_name': str(wd) + }) + params = self.get_sign_params(new_params) + # print(params) + r = self.postJson(self.api_cate, json=params) + ret = r.json() + data = self.decode(ret['data']) + # print(data) + d = data['list'] + result = { + 'list': d + } + return result + + def playerContent(self, flag, id, vipFlags): + """ + 解析播放,返回json。壳子视情况播放直链或进行嗅探 + @param flag: vod_play_from 播放来源线路 + @param id: vod_play_url 播放的链接 + @param vipFlags: vip标识 + @return: + """ + _v = id.split('&') + ju_id = _v[0] + plyer = _v[1] + video_id = _v[2] + new_params = self.params.copy() + new_params.update({ + 'player_id': str(plyer), 'ctime': str(int(time.time())), + 'ju_id': str(ju_id), 'vod_id': str(video_id) + }) + params = self.get_sign_params(new_params) + # print(params) + r = self.postJson(self.api_parse, json=params) + ret = r.json() + data = self.decode(ret['data']) + # print(data) + # 列表里第1条的分辨率最高 + url = data[0]['url'] + # print(url) + + """ + + # 原始key + key = 'sLunqcoH85Nm/jDmFKns7A==' + # 转hex + key_hex_str = self.bytesToHexString(key.encode('utf-8')).replace(' ', '') + # 右侧补16个0 + key_hex_str += '0'*16 + key_hex = key_hex_str + # key_hex = '734C756E71636F4838354E6D2F6A446D464B6E7337413D3D0000000000000000' + # 转回来 + key = self.hexStringTobytes(key_hex).decode('utf-8') + # print(key) + iv = 'fedcba9876543210' + + """ + + # key = self.key + # iv = self.iv + # input = self.aes_cbc_decode(url,key,iv) + + input = self.decode_aes(url) + parse = 0 + result = { + 'parse': parse, # 1=嗅探,0=播放 + 'playUrl': '', # 解析链接 + 'url': input, # 直链或待嗅探地址 + # 'header': headers, # 播放UA + } + return result + + config = { + "player": {}, + "filter": {} + } + header = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36", + "Host": "www.baidu.com", + "Referer": "https://www.baidu.com/" + } + + def localProxy(self, params): + return [200, "video/MP2T", ""] + + # -----------------------------------------------自定义函数----------------------------------------------- + def get_sign_params(self, params: dict): + keys = list(params.keys()) + keys.sort() + str_list = [] + for key in keys: + if params.get(key): + str_list.append(params[key]) + str_list.append('alskeuscli') + sign = self.md5(''.join(str_list)) + params['sign'] = sign + return params + + def decode(self, text): + return text + # return self.str2json(self.aes_cbc_decode(text, self.key, self.iv)) + + def decode_aes(self, text): + key = self.key + iv = self.iv + input = self.aes_cbc_decode(text, key, iv) + return input + + +if __name__ == '__main__': + # 在线aes测试 https://config.net.cn/tools/AES.html + # 分类页:http://60.204.185.245:7090/appto/v1/home/cateData?id=1 + # 推荐页:http://60.204.185.245:7090/appto/v1/config/get?p=android + from t4.core.loader import t4_spider_init + + spider = Spider() + t4_spider_init(spider) + # spider.init_api_ext_file() # 生成筛选对应的json文件 + + # print(spider.homeContent(True)) + # print(spider.homeVideoContent()) + # print(spider.categoryContent('23', 1, True, {'year': '2024'})) + # print(spider.detailContent([7533])) + # print(spider.searchContent('斗罗大陆')) + print(spider.playerContent('线路J', '1&duoduan&7533', None)) + print(spider.playerContent('线路Z', '1&ziru&7533', None))