123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- # File : 樱花动漫.py
- # Author: DaShenHan&道长-----先苦后甜,任凭晚风拂柳颜------
- # Author's Blog: https://blog.csdn.net/qq_32394351
- # Date : 2024/1/7
- import sys
- sys.path.append('..')
- try:
- # from base.spider import Spider as BaseSpider
- from base.spider import BaseSpider
- except ImportError:
- from t4.base.spider import BaseSpider
- from cachetools import cached, TTLCache # 可以缓存curd的函数,指定里面的key
- """
- 配置示例:
- 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":"https://jihulab.com/qiaoji/open/-/raw/main/yinghua"
- },
- {
- "key": "hipy_t3_樱花动漫",
- "name": "樱花动漫(hipy_t3)",
- "type": 3,
- "api": "{{host}}/txt/hipy/樱花动漫.py",
- "searchable": 1,
- "quickSearch": 0,
- "filterable": 1,
- "ext": "https://jihulab.com/qiaoji/open/-/raw/main/yinghua"
- },
- """
- def envkey(self, url: str):
- return url
- # 全局变量
- gParam = {
- "HomeDict": {},
- "TypeDict": {},
- }
- class Spider(BaseSpider): # 元类 默认的元类 type
- api_qj: str = 'https://jihulab.com/qiaoji/open/-/raw/main/yinghua'
- private_key: str = 'MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDA+5YTt3w1q/0WGw+TWyCSHTAeYiwBqAqDWot1u/1hoeANpED8gtW1AxB1mYNDQ+9eR8Ml+JC13+ME6RHjEbN4+n9V9OP90c81G0qSjBQ/DKQiMIFjbTH97RjVMtswf96tqwe4Rs/DT2ym6MP4P7QvJcxrFz5VVQXyOtUxhpMc9oktWuk0XKE8Mozu1FM879RknlM6WmJL85Wl/BnZrd+/AQbzziceELGrBfjbc1UOFAxYq2kA10H3o+Z4oOIODxUtXeh4R2oH3vHb4Ynnw6reXED5KsE3u1EO5HMQZyN16TZMTIps32bPe+vQlAT6V5nGcqXGT9fntjqIxJB0T9G3AgMBAAECggEBAKP6Yuh4BZP5g0CwV8jHKuLc6FE469mwdtZsLooo5cF68c3Fnu6xIXQAmZDDk3SpmhCLe7edASF5jwZSIL/H/68xcteQEdZP2/htKy1g16dHT4Q5oQfh9hOkznACGZuZW5ZH+HRNvyZfK5ybtkEPqERTouHwSyfo6feMpDDD/+cf3h1//7JKXKA7JPEU420YucsjQwjMuu5xdPa0TPqEc5mIbOBj753Pzn4GCScM+FRqJWr2x8e+KDPcPY8CUDLBSWxGLsB0A7+bEq/EiAQkbx09QKTwwxRLgVXjBbvyPB8BOuJpPM9BHx+vFcm5WSbkJdRI4qVFtEdsN/gDfFkwcjkCgYEA8Z8i/fTFRnzyvp9Pp8E+bSaYlvpTLUZ1KYNStaDg/BqlYGgGK1Jh90qjvRbBoiIjeBQd3IFLT4pFdd7Z9drLFdvqB22SNeVQU57kir/B6NY5G7yOjXB4qN17F4S3GubYIEcjF0W1tG/uOqqzb8FxrLJTK8WiFudbBt2ioCO4pJsCgYEAzHd8MctmD1Z1eM/xusvX1yCwGpxBuHT+ymThzLXyI6Ej0Q50jOQlf3cTyY/FgGbvAMz+oBybkEwE80gu7CPi0WPs+yCpAIB4+Th7afsrRylQI1ZWoRovaRmsyjnkIw0Mnj06VYNYPtkzm/OViRIqf4ESTTGas24bDm5DuwM9gxUCgYBwg4BR7gdnWYvYRGtdXNlrDowD0jGlZaftWt/LAE2EWAwmpooo5kYEV9eDl/M3QtptckCti++77FGIH+wzVl03op6KMvXg7xXGurkF+2GawRb62YUwS+2EBQ7q1rxFZLXD4hxvG+EPUwgGfbLtGZGLr8aXHYLrU3TJ769pDvlOfQKBgAFlAzzXtU9/eHele3GZuFQoTeswi6Y1bhN1UrDxwMALdlITtinL2JGg/0qNp3wzt4ea3lW7PDhkvFfocyF7MS3ab6Ba3aw6NBkHEJhtdSMcHgbPrPGWWyJtYWdTs8GlciOWKVKx/aUYGCkFJUz1CcMq3zQVlYeJxbd4ew/Iet/tAoGBAMRfvG1iLQAlS3AGaQeRwVxnvpciDn+7/sUCf8DEOk8Bqg4/ytJDTDrWufCtwmpsXmp6AUQig9mNKj7z26wSNbwYdzPsncK+sGRlS7eLAzzcv1a+1pghOOGDuQNzwlFOcauhkrcqjeKmu7OiKD48pvh3ZICiIWS1YL7LuMfUwHRJ'
- key: str = 'fQiG3YWTpQEYHNFTxJXCBaZrcCkkpfxH'
- iv: str = '1238389483762837'
- token: str = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJBcHBUbyIsImlhdCI6MTcwMDA3MTcwMiwiZXhwIjoxNzMxNjA3NzAyLCJuYmYiOjE3MDAwNzE3MDIsInN1YiI6IkFwcFRvIiwianRpIjoiYzRjNTAzOTQxYTM4NWI1MDMyMTAyYmY3Yzk1OGY4MzEiLCJkYXRhIjp7InVzZXJfaWQiOjI0ODc1NCwidXNlcl9jaGVjayI6ImUzYmQ3NmNhNTJhMGY4NjAwMTdjNjdkZGUwN2QzZTM3IiwidXNlcl9uYW1lIjoiaGV6aWh1aSJ9fQ.4LWs3rNL-os8_Pqa9LgKtvVG5f0aIxVyAjYIagvO1F4'
- ic: str = 'bmXes2xsCWvsSdfYav0s9D78Ly7w1o%2BOYXApKx6SUd4NWKsTsapbS52l7y%2FsTVCM2kcoLws2jryaDQlHLse5fxD2B2VXZXfaQo0eMTOv2Xq7CKoPa51uVt8WiIY2SPztc7wxGE89%2Fcw2Q3n85uUT3A%3D%3D'
- api: str = 'http://60.204.185.245:7090/appto/v1'
- api_cofig: str = api + '/config/get?p=android'
- api_home: str = api + '/home/cateData?id=1'
- api_cate: str = api + '/vod/getLists'
- api_search: str = api + '/vod/getVodSearch'
- api_detail: str = api + '/vod/getVod?__platform=android&__ic=' + ic
- api_parse: str = api + '/parsing/proxy'
- def getName(self):
- return "樱花动漫"
- @cached(cache=TTLCache(maxsize=3, ttl=3600), key=envkey)
- def get_init_api(self, url):
- try:
- print('get_init_api请求URL:', url)
- r = self.fetch(url)
- ret = self.decode_rsa(r.text[1:])
- return ret
- except Exception as e:
- print(f'get_init_api请求URL发生错误:{e}')
- return {}
- def init_extend(self, url):
- ret = self.get_init_api(url)
- if ret.get('key'):
- self.key = ret.get('key')
- if ret.get('ic'):
- self.ic = ret.get('ic')
- if ret.get('token'):
- self.token = ret.get('token')
- if ret.get('url') and ret.get('api'):
- api = ret.get('url') + ret.get('api')
- self.api = api
- self.api_cofig: str = api + '/config/get?p=android'
- self.api_home: str = api + '/home/cateData?id=1'
- self.api_cate: str = api + '/vod/getLists'
- self.api_search: str = api + '/vod/getVodSearch'
- self.api_detail: str = api + '/vod/getVod?__platform=android&__ic=' + self.ic
- self.api_parse: str = api + '/parsing/proxy'
- def init_api_ext_file(self):
- """
- 这个函数用于初始化py文件对应的json文件,用于存筛选规则。
- 执行此函数会自动生成筛选文件
- @return:
- """
- ext_file = __file__.replace('.py', '.json')
- print(f'ext_file:{ext_file}')
- ext_file_dict = self.homeContent(True)['filters']
- with open(ext_file, mode='w+', encoding='utf-8') as f:
- f.write(self.json2str(ext_file_dict))
- def init(self, extend=""):
- """
- 初始化加载extend,一般与py文件名同名的json文件作为扩展筛选
- @param extend:
- @return:
- """
- ext = self.extend
- if ext.startswith('http'):
- self.init_extend(ext)
- else:
- self.init_extend(self.api_qj)
- # 装载模块,这里只要一个就够了
- if isinstance(extend, list):
- for lib in extend:
- if '.Spider' in str(type(lib)):
- self.module = lib
- break
- def isVideoFormat(self, url):
- pass
- def manualVideoCheck(self):
- pass
- def homeContent(self, filterable=False):
- """
- 获取首页分类及筛选数据
- @param filterable: 能否筛选,跟t3/t4配置里的filterable参数一致
- @return:
- """
- filter_names = {
- 'area': '地区',
- 'class': '分类',
- 'director': '导演',
- 'lang': '语言',
- 'star': '明星',
- 'state': '状态',
- 'version': '版本',
- 'year': '年份',
- }
- r = self.fetch(self.api_cofig)
- ret = r.json()
- data = self.decode(ret['data'])
- # print(data)
- result = {}
- classes = []
- filters = {}
- type_dict = {}
- for tp in data.get('get_type') 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:
- """
- r = self.fetch(self.api_home)
- ret = r.json()
- data = self.decode(ret['data'])
- # print(data)
- d = []
- for section in data['sections']:
- items = section['items']
- 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() # 哪些刷新数据
- # ?type_id=1&area=&lang=&year=&order=time&type_name=&page=1&pageSize=21
- params = {'page': pg, 'pageSize': page_count, 'tid': tid, 'type_name': gParam['TypeDict'].get(str(tid)) or ''}
- for fl in fls:
- params[fl] = extend[fl]
- r = self.fetch(self.api_cate, data=params)
- print(r.url)
- ret = r.json()
- data = self.decode(ret['data'])
- d = data['data']
- result = {
- 'list': d,
- 'page': pg,
- 'pagecount': 9999 if len(d) >= page_count else pg,
- 'limit': 90,
- 'total': data['total'],
- }
- return result
- def detailContent(self, ids):
- """
- 返回二级详情页数据
- @param ids: 一级传过来的vod_id列表
- @return:
- """
- # id=110102
- vod_id = ids[0]
- params = {'id': vod_id}
- r = self.fetch(self.api_detail, data=params)
- print(r.url)
- 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'],
- "vod_play_from": data['vod_play_from'],
- }
- vod_play_list = data['vod_play_list']
- vod_play_urls = []
- for vod_play in vod_play_list:
- v_from = vod_play['player_info']['from']
- v_show = vod_play['player_info']['show']
- vod_play_url = '#'.join(
- [url['name'] + '$' + '&&'.join([url['url'], v_from, v_show]) for url in vod_play['urls']])
- vod_play_urls.append(vod_play_url)
- vod['vod_play_url'] = '$$$'.join(vod_play_urls)
- 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:
- """
- # ?wd=%E4%B8%89%E5%A4%A7%E9%98%9F&page=1&type=
- params = {'wd': wd, 'type': '', 'page': pg}
- r = self.fetch(self.api_search, data=params)
- print(r.url)
- ret = r.json()
- data = self.decode(ret['data'])
- # print(data)
- d = data['data']
- 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:
- """
- headers = {
- 'Content-Type': 'multipart/form-data; boundary=--dio-boundary-1205762094',
- 'token': self.token,
- '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',
- }
- if '&&' in id:
- _v = id.split('&&')
- params = {'play_url': _v[0], 'label': _v[2], 'key': _v[1]}
- else:
- params = {'play_url': id, 'label': '主线', 'key': 'mp4'}
- # print(params)
- r = self.postBinary(self.api_parse, data=params, boundary='--dio-boundary-1205762094', headers=headers)
- # print(r.request.body.decode())
- ret = r.json()
- data = self.decode(ret['data'])
- # print(data)
- url = data['url']
- parse = 0
- result = {
- 'parse': parse, # 1=嗅探,0=播放
- 'playUrl': '', # 解析链接
- 'url': url, # 直链或待嗅探地址
- # '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 decode(self, text):
- return self.str2json(self.aes_cbc_decode(text, self.key, self.iv))
- def decode_rsa(self, text):
- return self.str2json(self.rsa_private_decode(text, self.private_key))
- 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, 'https://jihulab.com/qiaoji/open/-/raw/main/yinghua')
- # spider.init_api_ext_file() # 生成筛选对应的json文件
- # print(spider.homeContent(True))
- # print(spider.homeVideoContent())
- # print(spider.categoryContent('1', 1, True, {'year': '2024'}))
- # print(spider.detailContent([110078]))
- print(spider.searchContent('斗罗大陆'))
- # print(spider.playerContent(None, 'f1d7d074f624e993e425f|11d1d091b0b28|31613145e4a7c|518737c8650978', None))
- # spider.searchContent('斗罗大陆')
|