From c0773b5084b9aff81d90eff14f024e1e71ae1de7 Mon Sep 17 00:00:00 2001 From: xmbjm <140031499+xmbjm@users.noreply.github.com> Date: Wed, 8 Jan 2025 10:09:04 +0800 Subject: [PATCH] Add files via upload --- py/cntv央视.py | 1056 ++++++++++++++++++++++++++++++++++++ py/py_bilibili.py | 1310 +++++++++++++++++++++++++++++++++++++++++++++ py/py_hitv.py | 151 ++++++ py/py_次元.py | 205 +++++++ py/py_电影猎手.py | 279 ++++++++++ py/py_黑料.py | 270 ++++++++++ py/五五短剧.py | 373 +++++++++++++ py/光速.py | 193 +++++++ py/奈飞影视.py | 377 +++++++++++++ py/新视觉.py | 238 ++++++++ py/映播.py | 411 ++++++++++++++ py/策驰影院.py | 377 +++++++++++++ py/素白白.py | 356 ++++++++++++ py/荐片.py | 368 +++++++++++++ py/酷云.py | 199 +++++++ py/首映网.py | 373 +++++++++++++ py/黑料.py | 270 ++++++++++ 17 files changed, 6806 insertions(+) create mode 100644 py/cntv央视.py create mode 100644 py/py_bilibili.py create mode 100644 py/py_hitv.py create mode 100644 py/py_次元.py create mode 100644 py/py_电影猎手.py create mode 100644 py/py_黑料.py create mode 100644 py/五五短剧.py create mode 100644 py/光速.py create mode 100644 py/奈飞影视.py create mode 100644 py/新视觉.py create mode 100644 py/映播.py create mode 100644 py/策驰影院.py create mode 100644 py/素白白.py create mode 100644 py/荐片.py create mode 100644 py/酷云.py create mode 100644 py/首映网.py create mode 100644 py/黑料.py diff --git a/py/cntv央视.py b/py/cntv央视.py new file mode 100644 index 0000000..ad1f886 --- /dev/null +++ b/py/cntv央视.py @@ -0,0 +1,1056 @@ +# coding=utf-8 +# !/usr/bin/python +import os.path +import random +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 +import json +import time +import base64 +import datetime +import re +from urllib import request, parse +from pathlib import Path +import urllib +import urllib.request + +""" +配置示例: +t4的配置里ext节点会自动变成api对应query参数extend,但t4的ext字符串不支持路径格式,比如./开头或者.json结尾 +api里会自动含有ext参数是base64编码后的选中的筛选条件 + +错误示例,ext含有json: +{ + "key":"hipy_cntv央视", + "name":"cntv央视(hipy_t4)", + "type":4, + "api":"http://192.168.31.49:5707/api/v1/vod/cntv央视?api_ext={{host}}/txt/hipy/cntv央视.json", + "searchable":1, + "quickSearch":1, + "filterable":0, + "ext":"cntv央视.json" + } + 正确示例。同时存在ext和api_ext会优先取ext作为extend加载init + { + "key":"hipy_t4_cntv央视", + "name":"cntv央视(hipy_t4)", + "type":4, + "api":"http://192.168.31.49:5707/api/v1/vod/cntv央视?api_ext={{host}}/txt/hipy/cntv央视.json", + "searchable":1, + "quickSearch":0, + "filterable":1, + "ext":"{{host}}/files/hipy/cntv央视.json" + }, + { + "key": "hipy_t3_cntv央视", + "name": "cntv央视(hipy_t3)", + "type": 3, + "api": "{{host}}/txt/hipy/cntv央视.py", + "searchable": 1, + "quickSearch": 0, + "filterable": 1, + "ext": "{{host}}/files/hipy/cntv央视.json" +}, +""" + + +class Spider(BaseSpider): # 元类 默认的元类 type + module = None + + def getDependence(self): + return ['base_spider'] + + def getName(self): + return "中央电视台" # 可搜索 + + def init_api_ext_file(self): + ext_file = __file__.replace('.py', '.json') + print(f'ext_file:{ext_file}') + # 特别节目网页: https://tv.cctv.com/yxg/index.shtml?spm=C28340.PlFTqGe6Zk8M.E2PQtIunpEaz.65 + # 特别节目分类筛选获取页面: https://tv.cctv.com/yxg/tbjm/index.shtml + # 纪录片网页: https://tv.cctv.com/yxg/index.shtml?spm=C28340.PlFTqGe6Zk8M.E2PQtIunpEaz.65 + # 纪录片分类筛选获取页面:https://tv.cctv.com/yxg/jlp/index.shtml + # ==================== 获取特别节目的筛选条件 ====================== + r = self.fetch('https://tv.cctv.com/yxg/tbjm/index.shtml') + html = r.text + html = self.html(html) + + filter_tbjm = [] + lis = html.xpath('//*[@id="pindao"]/li') + li_value = [] + for li in lis: + li_value.append({ + 'n': ''.join(li.xpath('./span//text()')), + 'v': ''.join(li.xpath('@datacd')), + }) + # print(li_value) + filter_tbjm.append({ + "key": "datapd-channel", + "name": "频道", + "value": li_value + }) + + lis = html.xpath('//*[@id="fenlei"]/li') + li_value = [] + for li in lis: + li_value.append({ + 'n': ''.join(li.xpath('./span//text()')), + 'v': ''.join(li.xpath('@datalx')), + }) + # print(li_value) + filter_tbjm.append({ + "key": "datafl-sc", + "name": "类型", + "value": li_value + }) + + lis = html.xpath('//*[@id="zimu"]/li') + li_value = [] + for li in lis: + li_value.append({ + 'n': ''.join(li.xpath('./span//text()')), + 'v': ''.join(li.xpath('@datazm')), + }) + # print(li_value) + filter_tbjm.append({ + "key": "dataszm-letter", + "name": "首字母", + "value": li_value + }) + + print(filter_tbjm) + + # ==================== 纪录片筛选获取 ====================== + r = self.fetch('https://tv.cctv.com/yxg/jlp/index.shtml') + html = r.text + html = self.html(html) + + filter_jlp = [] + lis = html.xpath('//*[@id="pindao"]/li') + li_value = [] + for li in lis: + li_value.append({ + 'n': ''.join(li.xpath('./span//text()')), + 'v': ''.join(li.xpath('@datacd')), + }) + # print(li_value) + filter_jlp.append({ + "key": "datapd-channel", + "name": "频道", + "value": li_value + }) + + lis = html.xpath('//*[@id="fenlei"]/li') + li_value = [] + for li in lis: + li_value.append({ + 'n': ''.join(li.xpath('./span//text()')), + 'v': ''.join(li.xpath('@datalx')), + }) + # print(li_value) + filter_jlp.append({ + "key": "datafl-sc", + "name": "类型", + "value": li_value + }) + + lis = html.xpath('//*[@id="nianfen"]/li') + li_value = [] + for li in lis: + li_value.append({ + 'n': ''.join(li.xpath('./span//text()')), + 'v': ''.join(li.xpath('@datanf')), + }) + # print(li_value) + filter_jlp.append({ + "key": "datanf-year", + "name": "年份", + "value": li_value + }) + + lis = html.xpath('//*[@id="zimu"]/li') + li_value = [] + for li in lis: + li_value.append({ + 'n': ''.join(li.xpath('./span//text()')), + 'v': ''.join(li.xpath('@datazm')), + }) + # print(li_value) + filter_jlp.append({ + "key": "dataszm-letter", + "name": "首字母", + "value": li_value + }) + + print(filter_jlp) + + ext_file_dict = { + "特别节目": filter_tbjm, + "纪录片": filter_jlp, + } + + # print(json.dumps(ext_file_dict,ensure_ascii=False,indent=4)) + with open(ext_file, mode='w+', encoding='utf-8') as f: + # f.write(json.dumps(ext_file_dict,ensure_ascii=False,indent=4)) + f.write(json.dumps(ext_file_dict, ensure_ascii=False)) + + def init(self, extend=""): + def init_file(ext_file): + ext_file = Path(ext_file).as_posix() + # print(f'ext_file:{ext_file}') + if os.path.exists(ext_file): + # print('存在扩展文件') + with open(ext_file, mode='r', encoding='utf-8') as f: + try: + ext_dict = json.loads(f.read()) + # print(ext_dict) + self.config['filter'].update(ext_dict) + except Exception as e: + print(f'更新扩展筛选条件发生错误:{e}') + + print("============依赖列表:{0}============".format(extend)) + ext = self.extend + print("============ext:{0}============".format(ext)) + if isinstance(ext, str) and ext: + if ext.startswith('./'): + ext_file = os.path.join(os.path.dirname(__file__), ext) + init_file(ext_file) + elif ext.startswith('http'): + try: + r = self.fetch(ext) + self.config['filter'].update(r.json()) + except Exception as e: + print(f'更新扩展筛选条件发生错误:{e}') + elif not ext.startswith('./') and not ext.startswith('http'): + ext_file = os.path.join(os.path.dirname(__file__), './' + ext + '.json') + init_file(ext_file) + + # ==================== 栏目大全加载年月筛选 ====================== + lanmu_list = self.config['filter']['栏目大全'] + lanmu_keys_list = [lanmu['key'] for lanmu in lanmu_list] + if 'year' not in lanmu_keys_list: + currentYear = datetime.date.today().year + yearList = [{"n": "全部", "v": ""}] + for year in range(currentYear, currentYear - 10, -1): + yearList.append({"n": year, "v": year}) + yearDict = {"key": "year", "name": "年份", "value": yearList} + lanmu_list.append(yearDict) + if 'month' not in lanmu_keys_list: + monthList = [{"n": "全部", "v": ""}] + for month in range(1, 13): + text = str(month).rjust(2, '0') + monthList.append({"n": text, "v": text}) + monthDict = {"key": "month", "name": "月份", "value": monthList} + lanmu_list.append(monthDict) + + # 装载模块,这里只要一个就够了 + 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, filter): + result = {} + cateManual = { + "4K专区": "4K专区", + "栏目大全": "栏目大全", + "特别节目": "特别节目", + "纪录片": "纪录片", + "电视剧": "电视剧", + "动画片": "动画片", + "频道直播": "频道直播", + + } + classes = [] + for k in cateManual: + classes.append({ + 'type_name': k, + 'type_id': cateManual[k] + }) + result['class'] = classes + if (filter): + result['filters'] = self.config['filter'] + return result + + def homeVideoContent(self): + result = { + 'list': [] + } + if self.module: + result = self.module.homeVideoContent() + return result + + def categoryContent(self, tid, pg, filter, extend): + result = {} + month = "" # 月 + year = "" # 年 + area = '' # 地区 + channel = '' # 频道 + datafl = '' # 类型 + letter = '' # 字母 + year_prefix = '' # 栏目大全的年月筛选过滤 + pagecount = 24 + if tid == '动画片': + id = urllib.parse.quote(tid) + if 'datadq-area' in extend.keys(): + area = urllib.parse.quote(extend['datadq-area']) + if 'dataszm-letter' in extend.keys(): + letter = extend['dataszm-letter'] + if 'datafl-sc' in extend.keys(): + datafl = urllib.parse.quote(extend['datafl-sc']) + url = 'https://api.cntv.cn/list/getVideoAlbumList?channelid=CHAL1460955899450127&area={0}&sc={4}&fc={1}&letter={2}&p={3}&n=24&serviceId=tvcctv&topv=1&t=json'.format( + area, id, letter, pg, datafl) + elif tid == '纪录片': + id = urllib.parse.quote(tid) + if 'datapd-channel' in extend.keys(): + channel = urllib.parse.quote(extend['datapd-channel']) + if 'datafl-sc' in extend.keys(): + datafl = urllib.parse.quote(extend['datafl-sc']) + if 'datanf-year' in extend.keys(): + year = extend['datanf-year'] + if 'dataszm-letter' in extend.keys(): + letter = extend['dataszm-letter'] + url = 'https://api.cntv.cn/list/getVideoAlbumList?channelid=CHAL1460955924871139&fc={0}&channel={1}&sc={2}&year={3}&letter={4}&p={5}&n=24&serviceId=tvcctv&topv=1&t=json'.format( + id, channel, datafl, year, letter, pg) + elif tid == '电视剧': + id = urllib.parse.quote(tid) + if 'datafl-sc' in extend.keys(): + datafl = urllib.parse.quote(extend['datafl-sc']) + if 'datanf-year' in extend.keys(): + year = extend['datanf-year'] + if 'dataszm-letter' in extend.keys(): + letter = extend['dataszm-letter'] + url = 'https://api.cntv.cn/list/getVideoAlbumList?channelid=CHAL1460955853485115&area={0}&sc={1}&fc={2}&year={3}&letter={4}&p={5}&n=24&serviceId=tvcctv&topv=1&t=json'.format( + area, datafl, id, year, letter, pg) + elif tid == '特别节目': + id = urllib.parse.quote(tid) + if 'datapd-channel' in extend.keys(): + channel = urllib.parse.quote(extend['datapd-channel']) + if 'datafl-sc' in extend.keys(): + datafl = urllib.parse.quote(extend['datafl-sc']) + if 'dataszm-letter' in extend.keys(): + letter = extend['dataszm-letter'] + url = 'https://api.cntv.cn/list/getVideoAlbumList?channelid=CHAL1460955953877151&channel={0}&sc={1}&fc={2}&bigday=&letter={3}&p={4}&n=24&serviceId=tvcctv&topv=1&t=json'.format( + channel, datafl, id, letter, pg) + elif tid == '栏目大全': + cid = '' # 频道 + if 'cid' in extend.keys(): + cid = extend['cid'] + fc = '' # 分类 + if 'fc' in extend.keys(): + fc = extend['fc'] + fl = '' # 字母 + if 'fl' in extend.keys(): + fl = extend['fl'] + year = extend.get('year') or '' + month = extend.get('month') or '' + if year: + year_prefix = year + month + url = 'https://api.cntv.cn/lanmu/columnSearch?&fl={0}&fc={1}&cid={2}&p={3}&n=20&serviceId=tvcctv&t=json&cb=ko'.format( + fl, fc, cid, pg) + pagecount = 20 + elif tid == '4K专区': + cid = 'CHAL1558416868484111' + url = 'https://api.cntv.cn/NewVideo/getLastVideoList4K?serviceId=cctv4k&cid={0}&p={1}&n={2}&t=json&cb=ko'.format( + cid, pg, pagecount + ) + elif tid == '频道直播': + url = 'https://tv.cctv.com/epg/index.shtml' + else: + url = 'https://tv.cctv.com/epg/index.shtml' + + videos = [] + htmlText = self.fetch(url).text + if tid == '栏目大全': + index = htmlText.rfind(');') + if index > -1: + htmlText = htmlText[3:index] + videos = self.get_list1(html=htmlText, tid=tid, year_prefix=year_prefix) + elif tid == '4K专区': + index = htmlText.rfind(');') + if index > -1: + htmlText = htmlText[3:index] + videos = self.get_list_4k(html=htmlText, tid=tid) + elif tid == '频道直播': + html = self.html(htmlText) + lis = html.xpath('//*[@id="jiemudan01"]//div[contains(@class,"channel_con")]//ul/li') + for li in lis: + vid = ''.join(li.xpath('./img/@title')) + pic = ''.join(li.xpath('./img/@src')) + pic = self.urljoin('https://tv.cctv.com/epg/index.shtml', pic) + videos.append({ + 'vod_id': '||'.join([tid, vid, f'https://tv.cctv.com/live/{vid}/', pic]), + 'vod_name': vid, + 'vod_pic': pic, + 'vod_mark': '', + }) + + else: + videos = self.get_list(html=htmlText, tid=tid) + # print(videos) + + result['list'] = videos + result['page'] = pg + result['pagecount'] = 9999 if len(videos) >= pagecount else pg + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, array): + result = {} + year_prefix = '' + did = array[0] + if '$$$' in did: + year_prefix = did.split('$$$')[0] + did = did.split('$$$')[1] + aid = did.split('||') + tid = aid[0] + title = aid[1] + lastVideo = aid[2] + logo = aid[3] + if tid == '频道直播': + vod = { + "vod_id": did, + "vod_name": title.replace(' ', ''), + "vod_pic": logo, + "vod_content": f'频道{title}正在直播中', + "vod_play_from": '道长在线直播', + "vod_play_url": f'在线观看${title}||{lastVideo}', + } + result = {'list': [vod]} + return result + + id = aid[4] + + vod_year = aid[5] + actors = aid[6] if len(aid) > 6 else '' + brief = aid[7] if len(aid) > 7 else '' # get请求最长255,这个描述会有可能直接被干没了。 + fromId = 'CCTV' + if tid == "栏目大全": + lastUrl = 'https://api.cntv.cn/video/videoinfoByGuid?guid={0}&serviceId=tvcctv'.format(id) + # htmlTxt = self.webReadFile(urlStr=lastUrl, header=self.header) + htmlTxt = self.fetch(lastUrl).text + topicId = json.loads(htmlTxt)['ctid'] + url = 'https://api.cntv.cn/NewVideo/getVideoListByColumn' + # params = { + # 'p': '1', + # 'n': '100', + # 't': 'json', + # 'mode': '0', + # 'sort': 'desc', + # 'serviceId': 'tvcctv', + # 'd': year_prefix, + # 'id': topicId + # } + # htmlTxt = self.fetch(url,data=params).text + + Url = "{0}?id={1}&d=&p=1&n=100&sort=desc&mode=0&serviceId=tvcctv&t=json&d={2}".format( + url, topicId, year_prefix) + + + elif tid == "4K专区": + Url = 'https://api.cntv.cn/NewVideo/getVideoListByAlbumIdNew?id={0}&serviceId=cctv4k&p=1&n=100&mode=0&pub=1'.format( + id) + print(Url) + else: + Url = 'https://api.cntv.cn/NewVideo/getVideoListByAlbumIdNew?id={0}&serviceId=tvcctv&p=1&n=100&mode=0&pub=1'.format( + id) + jRoot = '' + videoList = [] + try: + if tid == "搜索": + fromId = '中央台' + videoList = [title + "$" + lastVideo] + else: + # htmlTxt = self.webReadFile(urlStr=Url, header=self.header) + htmlTxt = self.fetch(Url).text + jRoot = json.loads(htmlTxt) + data = jRoot['data'] + jsonList = data['list'] + videoList = self.get_EpisodesList(jsonList=jsonList) + if len(videoList) < 1: + # htmlTxt = self.webReadFile(urlStr=lastVideo, header=self.header) + htmlTxt = self.fetch(lastVideo).text + if tid == "电视剧" or tid == "纪录片" or tid == "4K专区": + patternTxt = r"'title':\s*'(?P.+?)',\n{0,1}\s*'brief':\s*'(.+?)',\n{0,1}\s*'img':\s*'(.+?)',\n{0,1}\s*'url':\s*'(?P<url>.+?)'" + elif tid == "特别节目": + patternTxt = r'class="tp1"><a\s*href="(?P<url>https://.+?)"\s*target="_blank"\s*title="(?P<title>.+?)"></a></div>' + elif tid == "动画片": + patternTxt = r"'title':\s*'(?P<title>.+?)',\n{0,1}\s*'img':\s*'(.+?)',\n{0,1}\s*'brief':\s*'(.+?)',\n{0,1}\s*'url':\s*'(?P<url>.+?)'" + elif tid == "栏目大全": + patternTxt = r'href="(?P<url>.+?)" target="_blank" alt="(?P<title>.+?)" title=".+?">' + videoList = self.get_EpisodesList_re(htmlTxt=htmlTxt, patternTxt=patternTxt) + fromId = '央视' + except: + pass + if len(videoList) == 0: + return {} + vod = { + "vod_id": did, + "vod_name": title.replace(' ', ''), + "vod_pic": logo, + "type_name": tid, + "vod_year": vod_year, + "vod_area": "", + "vod_remarks": '', + "vod_actor": actors, + "vod_director": '', + "vod_content": brief + } + vod['vod_play_from'] = fromId + vod['vod_play_url'] = "#".join(videoList) + result = { + 'list': [ + vod + ] + } + return result + + def get_lineList(self, Txt, mark, after): + circuit = [] + origin = Txt.find(mark) + while origin > 8: + end = Txt.find(after, origin) + circuit.append(Txt[origin:end]) + origin = Txt.find(mark, end) + return circuit + + def get_RegexGetTextLine(self, Text, RegexText, Index): + returnTxt = [] + pattern = re.compile(RegexText, re.M | re.S) + ListRe = pattern.findall(Text) + if len(ListRe) < 1: + return returnTxt + for value in ListRe: + returnTxt.append(value) + return returnTxt + + def searchContent(self, key, quick, pg=1): + key = urllib.parse.quote(key) + Url = 'https://search.cctv.com/ifsearch.php?page=1&qtext={0}&sort=relevance&pageSize=20&type=video&vtime=-1&datepid=1&channel=&pageflag=0&qtext_str={0}'.format( + key) + # htmlTxt = self.webReadFile(urlStr=Url, header=self.header) + htmlTxt = self.fetch(Url).text + videos = self.get_list_search(html=htmlTxt, tid='搜索') + result = { + 'list': videos + } + return result + + def playerContent(self, flag, id, vipFlags): + result = {} + url = '' + 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' + } + if flag == 'CCTV': + url = self.get_m3u8(urlTxt=id) + elif flag == '道长在线直播': + # _url = id + title = id.split('||')[0] # 获取标题 + _url = f'https://vdn.live.cntv.cn/api2/liveHtml5.do?channel=pc://cctv_p2p_hd{title}&channel_id={title}' + htmlTxt = self.fetch(_url).text + # print(htmlTxt) + vdata = self.regStr(htmlTxt, "var .*?=.*?'(.*?)';") + vdata = self.str2json(vdata) + print(vdata) + url = vdata['hls_url']['hls1'] + print(url) + url = self.fixm3u8_url(url) + else: + try: + # htmlTxt = self.webReadFile(urlStr=id, header=self.header) + htmlTxt = self.fetch(id).text + guid = self.get_RegexGetText(Text=htmlTxt, RegexText=r'var\sguid\s*=\s*"(.+?)";', Index=1) + url = self.get_m3u8(urlTxt=guid) + except: + url = id + parse = 1 + if url.find('https:') < 0: + url = id + parse = 1 + result["parse"] = parse # 1=嗅探,0=播放 + result["playUrl"] = '' + result["url"] = url + result["header"] = headers + return result + + # 分类抓取地址: + # 栏目大全:https://tv.cctv.com/lm/index.shtml?spm=C28340.Pu9TN9YUsfNZ.E2PQtIunpEaz.24 + # 电视剧:https://tv.cctv.com/yxg/index.shtml?spm=C28340.PlFTqGe6Zk8M.E2PQtIunpEaz.65#datacid=dsj&datafl=&datadq=&fc=%E7%94%B5%E8%A7%86%E5%89%A7&datanf=&dataszm= + # 动画片:https://tv.cctv.com/yxg/index.shtml?spm=C28340.PlFTqGe6Zk8M.E2PQtIunpEaz.65#datacid=dhp&datafl=&datadq=&fc=%E5%8A%A8%E7%94%BB%E7%89%87&dataszm= + # 记录片:https://tv.cctv.com/yxg/index.shtml?spm=C28340.PlFTqGe6Zk8M.E2PQtIunpEaz.65#datacid=jlp&datapd=&datafl=&fc=%E7%BA%AA%E5%BD%95%E7%89%87&datanf=&dataszm= + # 特别节目:https://tv.cctv.com/yxg/index.shtml?spm=C28340.PlFTqGe6Zk8M.E2PQtIunpEaz.65#datacid=tbjm&datapd=&datafl=&fc=%E7%89%B9%E5%88%AB%E8%8A%82%E7%9B%AE&datajr=&dataszm= + config = { + "player": {}, + "filter": { + "电视剧": [ + {"key": "datafl-sc", "name": "类型", + "value": [{"n": "全部", "v": ""}, {"n": "谍战", "v": "谍战"}, {"n": "悬疑", "v": "悬疑"}, + {"n": "刑侦", "v": "刑侦"}, {"n": "历史", "v": "历史"}, {"n": "古装", "v": "古装"}, + {"n": "武侠", "v": "武侠"}, {"n": "军旅", "v": "军旅"}, {"n": "战争", "v": "战争"}, + {"n": "喜剧", "v": "喜剧"}, {"n": "青春", "v": "青春"}, {"n": "言情", "v": "言情"}, + {"n": "偶像", "v": "偶像"}, {"n": "家庭", "v": "家庭"}, {"n": "年代", "v": "年代"}, + {"n": "革命", "v": "革命"}, {"n": "农村", "v": "农村"}, {"n": "都市", "v": "都市"}, + {"n": "其他", "v": "其他"}]}, + {"key": "datadq-area", "name": "地区", + "value": [{"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "中国香港", "v": "香港"}, + {"n": "美国", "v": "美国"}, {"n": "欧洲", "v": "欧洲"}, {"n": "泰国", "v": "泰国"}]}, + {"key": "datanf-year", "name": "年份", + "value": [{"n": "全部", "v": ""}, {"n": "2024", "v": "2024"}, {"n": "2023", "v": "2023"}, + {"n": "2022", "v": "2022"}, + {"n": "2021", "v": "2021"}, {"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"}, + {"n": "2018", "v": "2018"}, {"n": "2017", "v": "2017"}, {"n": "2016", "v": "2016"}, + {"n": "2015", "v": "2015"}, {"n": "2014", "v": "2014"}, {"n": "2013", "v": "2013"}, + {"n": "2012", "v": "2012"}, {"n": "2011", "v": "2011"}, {"n": "2010", "v": "2010"}, + {"n": "2009", "v": "2009"}, {"n": "2008", "v": "2008"}, {"n": "2007", "v": "2007"}, + {"n": "2006", "v": "2006"}, {"n": "2005", "v": "2005"}, {"n": "2004", "v": "2004"}, + {"n": "2003", "v": "2003"}, {"n": "2002", "v": "2002"}, {"n": "2001", "v": "2001"}, + {"n": "2000", "v": "2000"}, {"n": "1999", "v": "1999"}, {"n": "1998", "v": "1998"}, + {"n": "1997", "v": "1997"}]}, + {"key": "dataszm-letter", "name": "字母", + "value": [{"n": "全部", "v": ""}, {"n": "A", "v": "A"}, {"n": "C", "v": "C"}, {"n": "E", "v": "E"}, + {"n": "F", "v": "F"}, {"n": "G", "v": "G"}, {"n": "H", "v": "H"}, {"n": "I", "v": "I"}, + {"n": "J", "v": "J"}, {"n": "K", "v": "K"}, {"n": "L", "v": "L"}, {"n": "M", "v": "M"}, + {"n": "N", "v": "N"}, {"n": "O", "v": "O"}, {"n": "P", "v": "P"}, {"n": "Q", "v": "Q"}, + {"n": "R", "v": "R"}, {"n": "S", "v": "S"}, {"n": "T", "v": "T"}, {"n": "U", "v": "U"}, + {"n": "V", "v": "V"}, {"n": "W", "v": "W"}, {"n": "X", "v": "X"}, {"n": "Y", "v": "Y"}, + {"n": "Z", "v": "Z"}, {"n": "0-9", "v": "0-9"}]} + ], + "动画片": [ + {"key": "datafl-sc", "name": "类型", + "value": [{"n": "全部", "v": ""}, {"n": "亲子", "v": "亲子"}, {"n": "搞笑", "v": "搞笑"}, + {"n": "冒险", "v": "冒险"}, {"n": "动作", "v": "动作"}, {"n": "宠物", "v": "宠物"}, + {"n": "体育", "v": "体育"}, {"n": "益智", "v": "益智"}, {"n": "历史", "v": "历史"}, + {"n": "教育", "v": "教育"}, {"n": "校园", "v": "校园"}, {"n": "言情", "v": "言情"}, + {"n": "武侠", "v": "武侠"}, {"n": "经典", "v": "经典"}, {"n": "未来", "v": "未来"}, + {"n": "古代", "v": "古代"}, {"n": "神话", "v": "神话"}, {"n": "真人", "v": "真人"}, + {"n": "励志", "v": "励志"}, {"n": "热血", "v": "热血"}, {"n": "奇幻", "v": "奇幻"}, + {"n": "童话", "v": "童话"}, {"n": "剧情", "v": "剧情"}, {"n": "夺宝", "v": "夺宝"}, + {"n": "其他", "v": "其他"}]}, + {"key": "datadq-area", "name": "地区", + "value": [{"n": "全部", "v": ""}, {"n": "中国大陆", "v": "中国大陆"}, {"n": "美国", "v": "美国"}, + {"n": "欧洲", "v": "欧洲"}]}, + {"key": "dataszm-letter", "name": "字母", + "value": [{"n": "全部", "v": ""}, {"n": "A", "v": "A"}, {"n": "C", "v": "C"}, {"n": "E", "v": "E"}, + {"n": "F", "v": "F"}, {"n": "G", "v": "G"}, {"n": "H", "v": "H"}, {"n": "I", "v": "I"}, + {"n": "J", "v": "J"}, {"n": "K", "v": "K"}, {"n": "L", "v": "L"}, {"n": "M", "v": "M"}, + {"n": "N", "v": "N"}, {"n": "O", "v": "O"}, {"n": "P", "v": "P"}, {"n": "Q", "v": "Q"}, + {"n": "R", "v": "R"}, {"n": "S", "v": "S"}, {"n": "T", "v": "T"}, {"n": "U", "v": "U"}, + {"n": "V", "v": "V"}, {"n": "W", "v": "W"}, {"n": "X", "v": "X"}, {"n": "Y", "v": "Y"}, + {"n": "Z", "v": "Z"}, {"n": "0-9", "v": "0-9"}]} + ], + "纪录片": [ + {"key": "datafl-sc", "name": "类型", + "value": [{"n": "全部", "v": ""}, {"n": "人文历史", "v": "人文历史"}, {"n": "人物", "v": "人物"}, + {"n": "军事", "v": "军事"}, {"n": "探索", "v": "探索"}, {"n": "社会", "v": "社会"}, + {"n": "时政", "v": "时政"}, {"n": "经济", "v": "经济"}, {"n": "科技", "v": "科技"}]}, + {"key": "datanf-year", "name": "年份", + "value": [{"n": "全部", "v": ""}, {"n": "2024", "v": "2024"}, {"n": "2023", "v": "2023"}, + {"n": "2022", "v": "2022"}, + {"n": "2021", "v": "2021"}, {"n": "2020", "v": "2020"}, {"n": "2019", "v": "2019"}, + {"n": "2018", "v": "2018"}, {"n": "2017", "v": "2017"}, {"n": "2016", "v": "2016"}, + {"n": "2015", "v": "2015"}, {"n": "2014", "v": "2014"}, {"n": "2013", "v": "2013"}, + {"n": "2012", "v": "2012"}, {"n": "2011", "v": "2011"}, {"n": "2010", "v": "2010"}, + {"n": "2009", "v": "2009"}, {"n": "2008", "v": "2008"}]}, + {"key": "dataszm-letter", "name": "字母", + "value": [{"n": "全部", "v": ""}, {"n": "A", "v": "A"}, {"n": "C", "v": "C"}, {"n": "E", "v": "E"}, + {"n": "F", "v": "F"}, {"n": "G", "v": "G"}, {"n": "H", "v": "H"}, {"n": "I", "v": "I"}, + {"n": "J", "v": "J"}, {"n": "K", "v": "K"}, {"n": "L", "v": "L"}, {"n": "M", "v": "M"}, + {"n": "N", "v": "N"}, {"n": "O", "v": "O"}, {"n": "P", "v": "P"}, {"n": "Q", "v": "Q"}, + {"n": "R", "v": "R"}, {"n": "S", "v": "S"}, {"n": "T", "v": "T"}, {"n": "U", "v": "U"}, + {"n": "V", "v": "V"}, {"n": "W", "v": "W"}, {"n": "X", "v": "X"}, {"n": "Y", "v": "Y"}, + {"n": "Z", "v": "Z"}, {"n": "0-9", "v": "0-9"}]} + ], + "特别节目": [ + {"key": "datafl-sc", "name": "类型", + "value": [{"n": "全部", "v": ""}, {"n": "全部", "v": "全部"}, {"n": "新闻", "v": "新闻"}, + {"n": "经济", "v": "经济"}, {"n": "综艺", "v": "综艺"}, {"n": "体育", "v": "体育"}, + {"n": "军事", "v": "军事"}, {"n": "影视", "v": "影视"}, {"n": "科教", "v": "科教"}, + {"n": "戏曲", "v": "戏曲"}, {"n": "青少", "v": "青少"}, {"n": "音乐", "v": "音乐"}, + {"n": "社会", "v": "社会"}, {"n": "公益", "v": "公益"}, {"n": "其他", "v": "其他"}]}, + {"key": "dataszm-letter", "name": "字母", + "value": [{"n": "全部", "v": ""}, {"n": "A", "v": "A"}, {"n": "C", "v": "C"}, {"n": "E", "v": "E"}, + {"n": "F", "v": "F"}, {"n": "G", "v": "G"}, {"n": "H", "v": "H"}, {"n": "I", "v": "I"}, + {"n": "J", "v": "J"}, {"n": "K", "v": "K"}, {"n": "L", "v": "L"}, {"n": "M", "v": "M"}, + {"n": "N", "v": "N"}, {"n": "O", "v": "O"}, {"n": "P", "v": "P"}, {"n": "Q", "v": "Q"}, + {"n": "R", "v": "R"}, {"n": "S", "v": "S"}, {"n": "T", "v": "T"}, {"n": "U", "v": "U"}, + {"n": "V", "v": "V"}, {"n": "W", "v": "W"}, {"n": "X", "v": "X"}, {"n": "Y", "v": "Y"}, + {"n": "Z", "v": "Z"}, {"n": "0-9", "v": "0-9"}]} + ], + "栏目大全": [{"key": "cid", "name": "频道", + "value": [{"n": "全部", "v": ""}, {"n": "CCTV-1综合", "v": "EPGC1386744804340101"}, + {"n": "CCTV-2财经", "v": "EPGC1386744804340102"}, + {"n": "CCTV-3综艺", "v": "EPGC1386744804340103"}, + {"n": "CCTV-4中文国际", "v": "EPGC1386744804340104"}, + {"n": "CCTV-5体育", "v": "EPGC1386744804340107"}, + {"n": "CCTV-6电影", "v": "EPGC1386744804340108"}, + {"n": "CCTV-7国防军事", "v": "EPGC1386744804340109"}, + {"n": "CCTV-8电视剧", "v": "EPGC1386744804340110"}, + {"n": "CCTV-9纪录", "v": "EPGC1386744804340112"}, + {"n": "CCTV-10科教", "v": "EPGC1386744804340113"}, + {"n": "CCTV-11戏曲", "v": "EPGC1386744804340114"}, + {"n": "CCTV-12社会与法", "v": "EPGC1386744804340115"}, + {"n": "CCTV-13新闻", "v": "EPGC1386744804340116"}, + {"n": "CCTV-14少儿", "v": "EPGC1386744804340117"}, + {"n": "CCTV-15音乐", "v": "EPGC1386744804340118"}, + {"n": "CCTV-16奥林匹克", "v": "EPGC1634630207058998"}, + {"n": "CCTV-17农业农村", "v": "EPGC1563932742616872"}, + {"n": "CCTV-5+体育赛事", "v": "EPGC1468294755566101"}]}, + {"key": "fc", "name": "分类", + "value": [{"n": "全部", "v": ""}, {"n": "新闻", "v": "新闻"}, {"n": "体育", "v": "体育"}, + {"n": "综艺", "v": "综艺"}, {"n": "健康", "v": "健康"}, {"n": "生活", "v": "生活"}, + {"n": "科教", "v": "科教"}, {"n": "经济", "v": "经济"}, {"n": "农业", "v": "农业"}, + {"n": "法治", "v": "法治"}, {"n": "军事", "v": "军事"}, {"n": "少儿", "v": "少儿"}, + {"n": "动画", "v": "动画"}, {"n": "纪实", "v": "纪实"}, {"n": "戏曲", "v": "戏曲"}, + {"n": "音乐", "v": "音乐"}, {"n": "影视", "v": "影视"}]}, + {"key": "fl", "name": "字母", + "value": [{"n": "全部", "v": ""}, {"n": "A", "v": "A"}, {"n": "B", "v": "B"}, + {"n": "C", "v": "C"}, {"n": "D", "v": "D"}, {"n": "E", "v": "E"}, + {"n": "F", "v": "F"}, {"n": "G", "v": "G"}, {"n": "H", "v": "H"}, + {"n": "I", "v": "I"}, {"n": "J", "v": "J"}, {"n": "K", "v": "K"}, + {"n": "L", "v": "L"}, {"n": "M", "v": "M"}, {"n": "N", "v": "N"}, + {"n": "O", "v": "O"}, {"n": "P", "v": "P"}, {"n": "Q", "v": "Q"}, + {"n": "R", "v": "R"}, {"n": "S", "v": "S"}, {"n": "T", "v": "T"}, + {"n": "U", "v": "U"}, {"n": "V", "v": "V"}, {"n": "W", "v": "W"}, + {"n": "X", "v": "X"}, {"n": "Y", "v": "Y"}, {"n": "Z", "v": "Z"}]}, + ] + } + } + 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": "tv.cctv.com", + "Referer": "https://tv.cctv.com/" + } + + def localProxy(self, params): + return [200, "video/MP2T", ""] + + # -----------------------------------------------自定义函数----------------------------------------------- + # 访问网页 + def webReadFile(self, urlStr, header): + html = '' + req = urllib.request.Request(url=urlStr) # ,headers=header + with urllib.request.urlopen(req) as response: + html = response.read().decode('utf-8') + return html + + # 判断网络地址是否存在 + def TestWebPage(self, urlStr, header): + html = '' + req = urllib.request.Request(url=urlStr, method='HEAD') # ,headers=header + with urllib.request.urlopen(req) as response: + html = response.getcode() + return html + + # 正则取文本 + def get_RegexGetText(self, Text, RegexText, Index): + returnTxt = "" + Regex = re.search(RegexText, Text, re.M | re.S) + if Regex is None: + returnTxt = "" + else: + returnTxt = Regex.group(Index) + return returnTxt + + # 取集数 + def get_EpisodesList(self, jsonList): + videos = [] + for vod in jsonList: + url = vod['guid'] + title = vod['title'] + if len(url) == 0: + continue + videos.append(title + "$" + url) + return videos + + # 取集数 + def get_EpisodesList_re(self, htmlTxt, patternTxt): + ListRe = re.finditer(patternTxt, htmlTxt, re.M | re.S) + videos = [] + for vod in ListRe: + url = vod.group('url') + title = vod.group('title') + if len(url) == 0: + continue + videos.append(title + "$" + url) + return videos + + # 取剧集区 + def get_lineList(self, Txt, mark, after): + circuit = [] + origin = Txt.find(mark) + while origin > 8: + end = Txt.find(after, origin) + circuit.append(Txt[origin:end]) + origin = Txt.find(mark, end) + return circuit + + # 正则取文本,返回数组 + def get_RegexGetTextLine(self, Text, RegexText, Index): + returnTxt = [] + pattern = re.compile(RegexText, re.M | re.S) + ListRe = pattern.findall(Text) + if len(ListRe) < 1: + return returnTxt + for value in ListRe: + returnTxt.append(value) + return returnTxt + + # 删除html标签 + def removeHtml(self, txt): + soup = re.compile(r'<[^>]+>', re.S) + txt = soup.sub('', txt) + return txt.replace(" ", " ") + + def hookM3u8(self, url): + """ + https://www.52pojie.cn/thread-1932358-1-1.html + JavaScript:$.ajaxSettings.async = false; var s = ""; let a = $.get(vodh5player.playerList[0].ads.contentSrc); for (var m = 0; m < a.responseText.match(/asp.*?m3u8/g).length; m++) { s = s + "https://hls.cntv.myalicdn.com//asp" + a.responseText.match(/asp.*?m3u8/g)[m].slice(7) + "\n\n" }; var blob = new Blob([s], { type: "text/plain" }); var url = URL.createObjectURL(blob); window.open(url); + @param url: + @return: + """ + url = url or '' + hook1 = lambda x: x.replace('asp/', 'asp//', 1) + hook2 = lambda x: x.replace('hls/', 'hls//', 1) + hook3 = lambda x: x.replace('https://newcntv.qcloudcdn.com', 'https://hls.cntv.myalicdn.com/', 1) + hooks = [hook1, hook2, hook3] + hook = random.choice(hooks) + return hook(url) + + # 取m3u8 + def get_m3u8(self, urlTxt): + """ + https://blog.csdn.net/panwang666/article/details/135347859 + + JavaScript:jQuery.getJSON("https://vdn.apps.cntv.cn/api/getHttpVideoInfo.do?pid="+guid,function(result){document.writeln(result.hls_url.link(result.hls_url));}); + + https://newcntv.qcloudcdn.com/asp/hls/main/0303000a/3/default/3628bb15af644f588dc91ec68425b9ac/main.m3u8?maxbr=2048 + @param urlTxt: + @return: + """ + url = "https://vdn.apps.cntv.cn/api/getHttpVideoInfo.do?pid={0}".format(urlTxt) + # htmlTxt = self.webReadFile(urlStr=url, header=self.header) + htmlTxt = self.fetch(url).text + jo = json.loads(htmlTxt) + link = jo['hls_url'].strip() + # print('hls_url:',link) + # 获取域名前缀 + urlPrefix = self.get_RegexGetText(Text=link, RegexText='(http[s]?://[a-zA-z0-9.]+)/', Index=1) + # 域名前缀指定替换,然后可以获取到更高质量的视频列表 + # /asp/h5e/hls/2000/0303000a/3/default/3628bb15af644f588dc91ec68425b9ac/2000.m3u8 + new_link = link.replace(f'{urlPrefix}/asp/hls/', 'https://dh5.cntv.qcloudcdn.com/asp/h5e/hls/').split('?')[0] + # print('new_link:',new_link) + html = self.webReadFile(urlStr=new_link, header=self.header) + content = html.strip() + arr = content.split('\n') + subUrl = arr[-1].split('/') + # hdUrl = urlPrefix + arr[-1] + + # subUrl[3] = '2000' + # subUrl[-1] = '2000.m3u8' + # hdUrl = urlPrefix + '/'.join(subUrl) + maxVideo = subUrl[-1].replace('.m3u8', '') + hdUrl = link.replace('main', maxVideo) + hdUrl = hdUrl.replace(urlPrefix, 'https://newcntv.qcloudcdn.com') + hdRsp = self.TestWebPage(urlStr=hdUrl, header=self.header) + if hdRsp == 200: + url = hdUrl.split('?')[0] + url = self.hookM3u8(url) + self.log(f'视频链接: {url}') + else: + url = '' + return url + + def fixm3u8_url(self, url): + # 获取域名前缀 + urlPrefix = self.get_RegexGetText(Text=url, RegexText='(http[s]?://[a-zA-z0-9.]+)/', Index=1) + # 域名前缀指定替换,然后可以获取到更高质量的视频列表 + new_link = url.split('?')[0] + # print(new_link) + html = self.webReadFile(urlStr=new_link, header=self.header) + content = html.strip() + # print(content) + arr = content.split('\n') + subUrl = arr[3] if 'EXT-X-VERSION' in content else arr[2] + hdUrl = self.urljoin(new_link, subUrl).split('?')[0] + # hdUrl = hdUrl.replace(urlPrefix, 'https://newcntv.qcloudcdn.com') + hdRsp = self.TestWebPage(urlStr=hdUrl, header=self.header) + if hdRsp == 200: + url = hdUrl + self.log(f'视频链接: {url}') + else: + url = '' + return url + + # 搜索 + def get_list_search(self, html, tid): + jRoot = json.loads(html) + jsonList = jRoot['list'] + videos = [] + for vod in jsonList: + url = vod['urllink'] + title = self.removeHtml(txt=vod['title']) + img = vod['imglink'] + id = vod['id'] + brief = vod['channel'] + year = vod['uploadtime'] + if len(url) == 0: + continue + guids = [tid, title, url, img, id, year, '', brief] + guid = "||".join(guids) + videos.append({ + "vod_id": guid, + "vod_name": title, + "vod_pic": img, + "vod_remarks": year + }) + return videos + + def get_list1(self, html, tid, year_prefix=None): + jRoot = json.loads(html) + videos = [] + data = jRoot['response'] + if data is None: + return [] + jsonList = data['docs'] + for vod in jsonList: + id = vod['lastVIDE']['videoSharedCode'] + desc = vod['lastVIDE']['videoTitle'] + title = vod['column_name'] + url = vod['column_website'] + img = vod['column_logo'] + year = vod['column_playdate'] + brief = vod['column_brief'] + actors = '' + if len(url) == 0: + continue + guids = [tid, title, url, img, id, year, actors, brief] + guid = "||".join(guids) + # print(vod_id) + videos.append({ + "vod_id": year_prefix + '$$$' + guid if year_prefix else guid, + "vod_name": title, + "vod_pic": img, + "vod_remarks": desc.split('》')[1].strip() if '》' in desc else desc.strip() + }) + # print(videos) + return videos + + # 分类取结果 + def get_list(self, html, tid): + jRoot = json.loads(html) + videos = [] + data = jRoot['data'] + if data is None: + return [] + jsonList = data['list'] + for vod in jsonList: + url = vod['url'] + title = vod['title'] + img = vod['image'] + id = vod['id'] + try: + brief = vod['brief'] + except: + brief = '' + try: + year = vod['year'] + except: + year = '' + try: + actors = vod['actors'] + except: + actors = '' + if len(url) == 0: + continue + guids = [tid, title, url, img, id, year, actors, brief] + guid = "||".join(guids) + # print(vod_id) + videos.append({ + "vod_id": guid, + "vod_name": title, + "vod_pic": img, + "vod_remarks": '' + }) + return videos + + # 4k分类取结果 + def get_list_4k(self, html, tid): + jRoot = json.loads(html) + videos = [] + data = jRoot['data'] + if data is None: + return [] + jsonList = data['list'] + for vod in jsonList: + vod_remarks = vod['title'] + id = vod['id'] + vod = vod['last_video'] + img = vod['image'] + url = vod['url'] + title = vod['title'] + brief = vod.get('brief') or '' + year = vod.get('year') or '' + actors = vod.get('actors') or '' + if len(url) == 0: + continue + guids = [tid, title, url, img, id, year, actors, brief] + guid = "||".join(guids) + # print(vod_id) + videos.append({ + "vod_id": guid, + "vod_name": title, + "vod_pic": img, + "vod_remarks": vod_remarks + }) + return videos + + +if __name__ == '__main__': + from t4.core.loader import t4_spider_init + + spider = Spider() + t4_spider_init(spider) + # print(spider.homeContent(True)) + # print(spider.homeVideoContent()) + # spider.init_api_ext_file() + # url = 'https://api.cntv.cn/lanmu/columnSearch?&fl=&fc=%E6%96%B0%E9%97%BB&cid=&p=1&n=20&serviceId=tvcctv&t=jsonp&cb=Callback' + # url = 'https://api.cntv.cn/lanmu/columnSearch?&fl=&fc=&cid=&p=1&n=20&serviceId=tvcctv&t=json&cb=ko' + # r = spider.fetch(url) + # print(r.text) + # home_content = spider.homeContent(None) + # print(home_content) + cate_content = spider.categoryContent('栏目大全', 1, {'cid': 'n'}, {}) + # cate_content = spider.categoryContent('频道直播', 1, None, None) + print(cate_content) + vid = cate_content['list'][0]['vod_id'] + print(vid) + detail_content = spider.detailContent([vid]) + print(detail_content) + # # + vod_play_from = detail_content['list'][0]['vod_play_from'] + vod_play_url = detail_content['list'][0]['vod_play_url'] + print(vod_play_from, vod_play_url) + _url = vod_play_url.split('#')[0].split('$')[1] + print(_url) + print('vod_play_from:', vod_play_from, ' vod_play_url:', _url) + play = spider.playerContent(vod_play_from, _url, None) + print(play) + + # play = spider.playerContent('道长在线直播', 'cctv1||https://tv.cctv.com/live/cctv1/', None) + # print(play) diff --git a/py/py_bilibili.py b/py/py_bilibili.py new file mode 100644 index 0000000..3b498d8 --- /dev/null +++ b/py/py_bilibili.py @@ -0,0 +1,1310 @@ +Cv='Referer' +Cu='application/dash+xml' +Ct='interaction' +Cs='subtitle' +Cr='mimeType' +Cq='192000' +Cp='media_ft' +Co='media_bangumi' +Cn='stream' +Cm='playurl_info' +Cl='vod_actor' +Ck='\u3000👥 ' +Cj='setting' +Ci='message' +Ch='totalrank' +Cg='room_id' +Cf='favorite' +Ce='attention' +Cd='videos' +Cc=' 个人主页' +Cb='vod_pc' +Ca='series' +CZ='oldest' +CY='description' +CX='user_cover' +CW='roomid' +CV='text_small' +CU='watched_show' +CT='live_status' +CS='查看直播细化标签' +CR='https:' +CQ='fav_list' +CP='https://api.bilibili.com/x/web-interface/nav' +CO='bangumi_pay_parse' +CN='bangumi_vip_parse' +CM='raw_cookie_vip' +CL='raw_cookie_line' +CJ='contentType' +CI='header' +CH='mpd' +CG='codecid' +CF='playurl' +CE='season_title' +CD='edgeid' +CC='vod_list' +CB='AllPt' +CA='vod_year' +C9='title_type' +C8='bangumi' +C7='bili_user' +C6='seasons' +C5='sort' +C4='season_status' +C3='all' +C2='special' +C1='悄悄关注' +C0='最近关注' +B_='like_num' +Bz='crname' +By='part' +Bx='play' +Bw='%H:%M:%S' +Bv='module_author' +Bu='isVIP' +Bt='isLogin' +Bs='https://' +Br='utf-8' +Bq='排行榜' +Bp='showLiveFilterTag' +Bo='vodDefaultCodec' +Bn='vodDefaultQn' +Bm='favMode' +Bl='maxHomeVideoContent' +Bf='deadline' +Be='durl' +Bd='csrf' +Bc='codec' +Bb='AllPu' +Ba='🆙 ' +BZ='vod_director' +BY='fromep' +BX='@thisepisode@' +BW='预告' +BV='000' +BU='最近访问' +BT='最常访问' +BS='coin' +BR='danmaku' +BQ='稍后再看' +BP='vod_count' +BO='episodes' +BN='view' +BM='stat' +BL=' ▶' +BK='archive' +BJ='UP主' +BI='modules' +BH='type_name' +BG=None +BF='历史' +BE='关注' +BD='rankingLis' +BC='tuijianLis' +BB='cateManual' +BA='heartbeatInterval' +B9=float +B7='s_title' +B6='graph_version' +B5='ssid' +B4='pages' +B3='ugc_season' +B2='parse' +B1='特别关注' +B0='正在直播' +A_='[/a]' +Az='"}/]' +Ay='pubdate' +Ax='new_ep' +Aw='index_show' +Av='content' +Au='pic' +At='keyword' +As='登录' +Ar='userid' +Aq='搜索' +Ap='vodDefaultAudio' +Ao='@@@' +An='\n' +Am='like' +Al='desc' +Ak='[a=cr:{"id": "' +Aj='fans' +Ai='up' +Ah='owner' +Ag=' 🆙' +Af='mlid' +Ae='收藏' +Ad='直播' +Ac='影视' +Ab=True +Aa=type +AZ='audio' +AY='qn' +AX='format' +AW='vod_content' +AV='redirect_url' +AU='\u3000' +AT='ep' +AS='following' +AR='/' +AQ='4' +AP='User-Agent' +AO='cateLive' +AN='动态' +AM='推荐' +AL='3' +AK=round +AJ='﹩' +AI='this_array' +AH='\\"' +AG='live' +AF='vip' +AE='cookies_dic' +AD='_tmp' +AC='vod_play_url' +AB='vod_play_from' +AA='"' +A9='ss' +A8='season_id' +A7=' ' +A6='uname' +A5='face' +A4='cateManualLiveExtra' +A3='热门' +A2='﹟' +A1='tid' +A0='2' +z=list +w='$' +v='cid' +u='result' +t='id' +s='mid' +r='cateManualLive' +q='order' +p='video' +o='epid' +n='duration' +m='av' +l='filter' +k='type' +j='users' +i='$$$' +h='cover' +g='limit' +f='pagecount' +e='master' +d=dict +c='total' +b='url' +a='page' +Z='page_size' +Y='vod_remarks' +W='key' +V='1' +U=len +T='aid' +S='vod_pic' +R='vod_id' +Q='fake' +P='title' +O='vod_name' +N='_' +M='code' +L='0' +K='value' +J=map +I='#' +H='name' +G='list' +F=int +E='data' +D='n' +C='v' +B='' +A=str +import sys,os,json as Bg,threading as x,hashlib,time as X,random as B8 +from base.spider import Spider +from requests import session as Bh,utils as Bi,head +from requests.adapters import HTTPAdapter as Cw,Retry +from concurrent.futures import ThreadPoolExecutor as Cx,as_completed as Bj +from functools import reduce +from urllib.parse import quote as CK,urlencode as Bk +sys.path.append('..') +y,Cy=os.path.split(os.path.abspath(__file__)) +if y.startswith('/data/'):y=os.path.abspath(os.path.join(y,'..'));y=os.path.abspath(os.path.join(y,'..'));y=f"{y}/files" +sys.path.append(y) +class Spider(Spider): + defaultConfig={'currentVersion':'20240815_1',CL:B,CM:B,Bl:AL,Bm:L,Z:12,BA:'15',Bn:'80',Bo:'7',Ap:'30280',CN:Ab,CO:Ab,Bp:L,BB:[AM,Ac,Ad,AN,Ae,BE,BF,Aq],BC:[A3,Bq,'每周必看','入站必刷','番剧时间表','国创时间表'],BD:['动画','音乐','舞蹈','游戏','鬼畜','知识','科技','运动','生活','美食','动物','汽车','时尚','娱乐',Ac,'原创','新人']};focus_on_up_list=[];focus_on_search_key=[] + def getName(A):return'哔哩哔哩' + def load_config(A): + try: + with open(f"{y}/config.json",encoding=Br)as C:A.userConfig=Bg.load(C) + B=A.userConfig.get(j,{}) + if B.get(e)and B[e].get(AE):A.session_master.cookies=Bi.cookiejar_from_dict(B[e][AE]);A.userid=B[e][Ar] + if B.get(Q)and B[Q].get(AE):A.session_fake.cookies=Bi.cookiejar_from_dict(B[Q][AE]) + except:A.userConfig={} + A.userConfig={**A.defaultConfig,**A.userConfig} + dump_config_lock=x.Lock() + def dump_config(A): + F=[j,AO,r,A4];C={} + for(B,D)in A.userConfig.items(): + E=A.defaultConfig.get(B) + if E!=BG and D!=E or B in F:C[B]=D + A.dump_config_lock.acquire() + with open(f"{y}/config.json",'w',encoding=Br)as G:H=Bg.dumps(C,indent=1,ensure_ascii=False);G.write(H) + A.dump_config_lock.release() + pool=Cx(max_workers=8);task_pool=[] + def homeContent(A,filter): + A.pool.submit(A.add_live_filter);A.pool.submit(A.add_search_key);A.pool.submit(A.add_focus_on_up_filter);A.pool.submit(A.get_tuijian_filter);A.pool.submit(A.add_fav_filter);F=[AN,Ae,BE,BF];B=A.userConfig[BB] + if not A.userid and not As in B:B+=[As] + D=[] + for C in B: + if C in F and not A.userid:continue + D.append({BH:C,'type_id':C}) + E={'class':D};A.add_focus_on_up_filter_event.wait();A.add_live_filter_event.wait();A.add_fav_filter_event.wait();A.add_search_key_event.wait() + if filter:E['filters']=A.config[l] + A.pool.submit(A.dump_config);return E + userid=csrf=B;session_master=Bh();session_vip=Bh();session_fake=Bh();con=x.Condition();getCookie_event=x.Event();retries=Retry(total=5,backoff_factor=.1);adapter=Cw(max_retries=retries);session_master.mount(Bs,adapter);session_vip.mount(Bs,adapter);session_fake.mount(Bs,adapter) + def getCookie_dosth(B,co): + A=co.strip().split('=',1) + if not'%'in A[1]:A[1]=CK(A[1]) + return A + def getCookie(A,_type=e): + D=_type;G=CL + if D==AF:G=CM + G=A.userConfig.get(G);K=A.userConfig.get(j,{});C=K.get(D,{}) + if not G and not C: + if D==e:A.getCookie_event.set() + with A.con:A.con.notifyAll() + return + I=C.get(AE,{}) + if G:I=d(J(A.getCookie_dosth,G.split(';'))) + L=Bi.cookiejar_from_dict(I);N=CP;O=A.fetch(N,headers=A.header,cookies=L);H=Bg.loads(O.text);C[Bt]=0 + if H[M]==0: + C[Bt]=1;C[Ar]=H[E][s];C[A5]=H[E][A5];C[A6]=H[E][A6];C[AE]=I;C[Bu]=F(H[E]['vipStatus']) + if D==e:A.session_master.cookies=L;A.userid=C[Ar];A.csrf=I['bili_jct'] + if C[Bu]:A.session_vip.cookies=L + else:A.userid=B + K[D]=C + with A.con: + if U(C)>1:A.userConfig.update({j:K}) + if D==e:A.getCookie_event.set() + getFakeCookie_event=x.Event() + def getFakeCookie(A,fromSearch=BG): + if A.session_fake.cookies:A.getFakeCookie_event.set() + C={};C[AP]=A.header[AP];B=A.fetch('https://space.bilibili.com/2/video',headers=C);A.session_fake.cookies=B.cookies;A.getFakeCookie_event.set() + with A.con:D=A.userConfig.get(j,{});D[Q]={AE:d(B.cookies)};A.userConfig.update({j:D}) + if not fromSearch: + A.getCookie_event.wait() + if not A.session_master.cookies:A.session_master.cookies=B.cookies + add_fav_filter_event=x.Event() + def add_fav_filter(B): + N=B.userConfig.get(j,{}) + if N.get(e)and N[e].get(Ar):F=B.userConfig[j][e][Ar] + else:B.getCookie_event.wait();F=B.userid + I=[] + if F: + Q='https://api.bilibili.com/x/v3/fav/folder/created/list-all?up_mid=%s&jsonp=jsonp'%A(F);L=B._get_sth(Q).json() + if L[M]==0 and L.get(E):R=L[E].get(G);I=z(J(lambda x:{D:B.cleanCharacters(x[P].strip()),C:x[t]},R)) + S=[{D:'追番',C:V},{D:'追剧',C:A0}];O=B.config[l].get(Ae) + if O:O.insert(0,{W:Af,H:'分区',K:S+I}) + B.add_fav_filter_event.set();B.userConfig[CQ]=I + add_focus_on_up_filter_event=x.Event() + def add_focus_on_up_filter(B): + F=B.focus_on_up_list + if not B.session_master.cookies:B.getCookie_event.wait() + L=z(J(lambda x:x[C],F)) + if B.session_master.cookies: + N='https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all?timezone_offset=-480&type=video&page=1';G=B._get_sth(N).json() + if G[M]==0 and G.get(E): + O=G[E].get('items',[]) + for I in J(lambda x:{D:x[BI][Bv][H],C:A(x[BI][Bv][s])},O): + if not I in F and not I[C]in L:F.append(I) + P=[{D:'登录与设置',C:As}];F+=P;B.config[l][AN]=Q=[B.config[l].get(AN,[])[-1]];Q.insert(0,{W:s,H:BJ,K:F});B.add_focus_on_up_filter_event.set() + def get_live_parent_area_list(O,parent_area):B=parent_area;E=B[H];id=A(B[t]);F=B[G];I=z(J(lambda area:{D:area[H],C:A(area['parent_id'])+N+A(area[t])},F));L={W:A1,H:E,K:I};M={t:id+'_0',K:L};return E,M + def get_live_list(A): + C='https://api.live.bilibili.com/xlive/web-interface/v1/index/getWebAreaList?source_id=2';B=A._get_sth(C,Q).json() + if B[M]==0:D=B[E][E];A.userConfig[AO]=d(J(A.get_live_parent_area_list,D)) + return A.userConfig[AO] + def set_default_cateManualLive(A): + B=[{D:AM,C:AM}] + for E in A.userConfig[AO]:F={D:E,C:A.userConfig[AO][E][t]};B.append(F) + A.defaultConfig[r]=B;return B + add_live_filter_event=x.Event() + def add_live_filter(A): + C=A.userConfig.get(AO,{});G=A.pool.submit(A.get_live_list) + if not C:C=G.result() + I=A.pool.submit(A.set_default_cateManualLive);A.config[l][Ad]=D=[];B=A.userConfig.get(r,[]) + if not B:B=I.result() + if B:J={W:A1,H:'分区',K:B};D.append(J) + if F(A.userConfig[Bp]): + for E in C.values(): + if U(E[K][K])>1:D.append(E[K]) + A.add_live_filter_event.set() + add_search_key_event=x.Event() + def add_search_key(A): + B=A.focus_on_search_key;L='https://api.bilibili.com/x/web-interface/search/square?limit=10&platform=web';F=A._get_sth(L,Q).json();P={} + if F[M]==0:N=F[E]['trending'].get(G,[]);B+=z(J(lambda x:x[At],N)) + I={W:At,H:'搜索词',K:[]};I[K]=z(J(lambda i:{D:i,C:i},B));A.config[l][Aq]=O=A.config[l][Aq][-3:];O.insert(0,I);A.add_search_key_event.set() + def get_tuijian_filter(E): + I={'番剧时间表':'10001','国创时间表':'10004',Bq:L,'动画':V,'音乐':AL,'舞蹈':'129','游戏':AQ,'鬼畜':'119','知识':'36','科技':'188','运动':'234','生活':'160','美食':'211','动物':'217','汽车':'223','时尚':'155','娱乐':'5',Ac:'181','原创':'origin','新人':'rookie'};J=[{D:BC,C:'分区'},{D:BD,C:Bq}];E.config[l][AM]=M=[] + for F in J: + G={W:A1,H:F[C],K:[]};N=E.userConfig.get(F[D],[]) + for A in N: + B=I.get(A) + if not B:B=A + O={D:A,C:B};G[K].append(O) + M.append(G) + def __init__(A):A.load_config();A.pool.submit(A.getCookie);A.pool.submit(A.getFakeCookie);A.pool.submit(A.getCookie,AF);B=AK(X.time());C=X.gmtime(B).tm_hour;A.pool.submit(A.get_wbiKey,C) + def init(A,extend=B):print('============{0}============'.format(extend)) + def isVideoFormat(A,url):0 + def manualVideoCheck(A):0 + def destroy(A):0 + def format_img(B,img): + A=img;A+='@672w_378h_1c.webp' + if not A.startswith('http'):A=CR+A + return A + def pagination(A,array,pg):B=A.userConfig[Z]*F(pg);C=B-A.userConfig[Z];return array[C:B] + def zh(D,num): + C=num + if F(C)>=100000000:B=AK(B9(C)/B9(100000000),1);B=A(B)+'亿' + elif F(C)>=10000:B=AK(B9(C)/B9(10000),1);B=A(B)+'万' + else:B=A(C) + return B + def second_to_time(D,a): + a=F(a) + if a<3600:C=X.strftime('%M:%S',X.gmtime(a)) + else:C=X.strftime(Bw,X.gmtime(a)) + if A(C).startswith(L):C=A(C).replace(L,B,1) + return C + def str2sec(E,x): + x=A(x) + try:D,B,C=x.strip().split(':');return F(D)*3600+F(B)*60+F(C) + except:B,C=x.strip().split(':');return F(B)*60+F(C) + def find_bangumi_id(C,url): + B=A(url).split(AR)[-1] + if not B:B=A(url).split(AR)[-2] + B=B.split('?')[0];return B + def get_Login_qrcode(H,pg): + N='https://www.bilibili.com/favicon.ico';K='setting_login_';A={} + if F(pg)!=1:return A + D=[{R:'setting_tab&filter',O:'标签与筛选',S:N},{R:'setting_liveExtra',O:CS,S:N}];I='https://passport.bilibili.com/x/passport-login/web/qrcode/generate';J=H._get_sth(I,Q).json() + if J[M]==0: + id=J[E]['qrcode_key'];I=J[E][b];P={e:'主账号',AF:'副账号'};T={0:'未登录',1:'已登录'};U={0:B,1:'👑'};V=H.userConfig.get(j,{}) + for(W,X)in P.items(): + C=V.get(W) + if C:D.append({R:K+id,O:C[A6],S:H.format_img(C[A5]),Y:U[C[Bu]]+X+A7+T[C[Bt]]}) + L={E:I,'quietzone':'208','codepage':'UTF8','quietunit':'px','errorcorrection':'M','size':'small'};D.append({R:K+id,S:'http://jm92swf.s1002.xrea.com/?'+Bk(L)});D.append({R:K+id,S:'https://bili.ming1992.xyz/API/QRCode?'+Bk(L)}) + A[G]=D;A[a]=1;A[f]=1;A[g]=1;A[c]=1;return A + time_diff1={V:[0,300],A0:[300,900],AL:[900,1800],AQ:[1800,3600],'5':[3600,0x4ee2d6d415b85acef80ffffffff]};time_diff=L;dynamic_offset=B + def get_dynamic(C,pg,mid,order): + if mid==L: + D={} + if F(pg)==1:C.dynamic_offset=B + Q='https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all?timezone_offset=-480&type=video&offset=%s&page=%s'%(C.dynamic_offset,pg);J=C._get_sth(Q).json() + if J[M]==0: + C.dynamic_offset=J[E].get('offset');K=[];U=J[E]['items'] + for N in U:V=N[BI][Bv][H];I=N[BI]['module_dynamic']['major'][BK];W=A(I[T]).strip();X=C.cleanCharacters(I[P].strip());Z=I[h].strip();b=A(C.second_to_time(C.str2sec(I['duration_text']))).strip()+Ag+A(V).strip();K.append({R:m+W,O:X,S:C.format_img(Z),Y:b}) + D[G]=K;D[a]=pg;D[f]=9999;D[g]=99;D[c]=999999 + return D + else:return C.get_up_videos(mid=mid,pg=pg,order=order) + def get_found_vod(G,vod): + C=vod;D=C.get(T,B) + if not D:D=C.get(t,B) + E=C.get('goto',B) + if not E or E and E==m:D=m+A(D).strip() + elif E=='ad':return[] + N=C[P].strip();Q=C[Au].strip();U=C.get('is_followed') + if E==AG: + L=C['room_info'];I=B;V=L.get(CT,B) + if V:I='直播中 ' + else:return[] + I+='👁'+L[CU][CV]+Ag+C[Ah][H].strip() + else: + K=C.get('rcmd_reason',B) + if K and Aa(K)==d and K.get(Av): + F=' 🔥'+K[Av].strip() + if'人气飙升'in F:F=' 🔥人气飙升' + elif U:F=' 已关注' + else:F=Ag+C[Ah][H].strip() + I=A(G.second_to_time(C[n])).strip()+BL+G.zh(C[BM][BN])+F + M=[{R:D,O:N,S:G.format_img(Q),Y:I}] + for W in J(G.get_found_vod,C.get('others',[])):M.extend(W) + return M + _popSeriesInit=0 + def get_found(A,tid,rid,pg): + H=tid;C=pg;D={};K=1 + if H==AM:O=A.encrypt_wbi(fresh_type=4,feed_version='V8',brush=1,fresh_idx=C,fresh_idx_1h=C,ps=A.userConfig[Z])[0];B='https://api.bilibili.com/x/web-interface/wbi/index/top/feed/rcmd?'+O;K=99 + elif H==A3:B='https://api.bilibili.com/x/web-interface/popular?pn={0}&ps={1}'.format(C,A.userConfig[Z]);K=99 + elif H=='入站必刷':B='https://api.bilibili.com/x/web-interface/popular/precious' + elif H=='每周必看': + if F(C)==1:B='https://api.bilibili.com/x/web-interface/popular/series/list';I=A._get_sth(B,Q).json();A._popSeriesInit=F(I[E][G][0]['number']) + P=A._popSeriesInit-F(C)+1;K=A._popSeriesInit;B=f"https://api.bilibili.com/x/web-interface/popular/series/one?number={P}" + else:B='https://api.bilibili.com/x/web-interface/ranking/v2?rid={0}&type={1}'.format(rid,H) + I=A._get_sth(B).json() + if I[M]==0: + N=[];L=I[E].get('item') + if not L:L=I[E][G] + for R in J(A.get_found_vod,L):N.extend(R) + D[G]=N;D[a]=C;D[f]=K;D[g]=99;D[c]=999999 + return D + def get_bangumi(D,tid,pg,order,season_status): + X='first_ep';W='first_ep_info';T=order;I=tid;H={} + if T=='追番剧':J='https://api.bilibili.com/x/space/bangumi/follow/list?type={0}&vmid={1}&pn={2}&ps={3}'.format(I,D.userid,pg,D.userConfig[Z]);K=D._get_sth(J).json() + else: + J='https://api.bilibili.com/pgc/season/index/result?type=1&season_type={0}&page={1}&order={2}&season_status={3}&pagesize={4}'.format(I,pg,T,season_status,D.userConfig[Z]) + if T==A3: + if I==V:J='https://api.bilibili.com/pgc/web/rank/list?season_type={0}&day=3'.format(I) + else:J='https://api.bilibili.com/pgc/season/rank/web/list?season_type={0}&day=3'.format(I) + K=D._get_sth(J,Q).json() + if K[M]==0: + if E in K:L=K[E][G] + else:L=K[u][G] + if U(L)>D.userConfig[Z]:L=D.pagination(L,pg) + b=[] + for C in L: + e=A(C[A8]).strip();i=C[P];N=C.get('ss_horizontal_cover') + if not N: + if C.get(W)and h in C[W]:N=C[W][h] + elif C.get(X)and h in C[X]:N=C[X][h] + else:N=C[h].strip() + F=C.get(Aw,B) + if not F and C.get(Ax)and C[Ax].get(Aw):F=C[Ax][Aw] + F=F.replace('更新至','🆕');d=C.get(BM) + if d:F='▶'+D.zh(d.get(BN))+' '+F + b.append({R:A9+e,O:i,S:D.format_img(N),Y:F}) + H[G]=b;H[a]=pg;H[f]=9999;H[g]=90;H[c]=999999 + return H + def get_timeline(E,tid,pg): + b='pub_index';Z='ep_cover';D={};d='https://api.bilibili.com/pgc/web/timeline/v2?season_type={0}&day_before=2&day_after=4'.format(tid);F=E._get_sth(d,Q).json() + if F[M]==0: + T=[];H=F[u]['latest'] + for C in H:I=A(C[A8]).strip();J=C[P].strip();K=C[Z].strip();N='🆕'+C[b]+' ❤ '+C['follows'].replace('系列',B).replace('追番',B);T.append({R:A9+I,O:J,S:E.format_img(K),Y:N}) + V=[];W=F[u]['timeline'] + for e in range(U(W)): + H=W[e][BO] + for C in H: + if A(C['published'])==L:I=A(C[A8]).strip();J=A(C[P]).strip();K=A(C[Z]).strip();h=A(X.strftime('%m-%d %H:%M',X.localtime(C['pub_ts'])));N=h+' '+C[b];V.append({R:A9+I,O:J,S:E.format_img(K),Y:N}) + D[G]=V+T;D[a]=1;D[f]=1;D[g]=90;D[c]=999999 + return D + def get_live(F,pg,parent_area_id,area_id): + N='recommend_room_list';I=parent_area_id;C={} + if I==AM:H='https://api.live.bilibili.com/xlive/web-interface/v1/webMain/getList?platform=web&page=%s'%pg;J=F._get_sth(H).json() + else: + H='https://api.live.bilibili.com/xlive/web-interface/v1/second/getList?platform=web&parent_area_id=%s&area_id=%s&sort_type=online&page=%s'%(I,area_id,pg) + if I==A3:H='https://api.live.bilibili.com/room/v1/room/get_user_recommend?page=%s&page_size=%s'%(pg,F.userConfig[Z]) + J=F._get_sth(H,Q).json() + if J[M]==0: + L=[];B=J[E] + if N in B:B=B[N] + elif G in B:B=B[G] + for D in B: + T=A(D[CW]).strip();U=F.cleanCharacters(D[P]);K=D.get(CX) + if not K:K=D.get(h) + V='👁'+D[CU][CV].strip()+Ag+D[A6].strip();L.append({R:T,O:U,S:F.format_img(K),Y:V}) + C[G]=L;C[a]=pg;C[f]=9999;C[g]=99;C[c]=999999 + return C + def get_up_series(I,mid,pg): + C={};N='https://api.bilibili.com/x/polymer/web-space/seasons_series_list?mid=%s&page_num=%s&page_size=%s'%(mid,pg,I.userConfig[Z]);D=I._get_sth(N,Q).json() + if D[M]==0: + K=[];D=D[E]['items_lists'];P=D['seasons_list']+D['series_list'] + for L in P: + F=L.get('meta');J=A(F.get(A8,B)).strip() + if J:J=m+A(L['recent_aids'][0]) + else:J='list_'+A(mid)+'_series_'+A(F.get('series_id',B)).strip() + T=I.cleanCharacters(F[H]);U=F.get(h);V=F.get(CY,B).strip();K.append({R:J,O:T,S:I.format_img(U),Y:V}) + C[G]=K;C[a]=pg;C[f]=9999;C[g]=99;C[c]=999999 + return C + get_up_videos_result=d() + def get_up_videos(C,mid,pg,order): + L=order;K=pg;D=mid;I={} + if not D in C.up_info or F(K)==1:C.get_up_info_event.clear();C.pool.submit(C.get_up_info,D) + V=W=B + if L==CZ:W=L;L=Ay + elif L=='quicksearch': + V='投稿: ';J=C.get_up_videos_result.get(D,[]) + if J:I[G]=J;return I + elif L==Ca:return C.get_up_series(mid=D,pg=K) + X=K + if W:C.get_up_info_event.wait();X=C.up_info[D][Cb]-F(K)+1 + h=C.encrypt_wbi(mid=D,pn=X,ps=C.userConfig[Z],order=L)[0];i=f"https://api.bilibili.com/x/space/wbi/arc/search?{h}";b=C._get_sth(i,Q).json();J=[] + if b[M]==0: + j=b[E][G]['vlist'] + for N in j: + k=A(N[T]).strip();l=C.cleanCharacters(N[P].strip());n=N[Au].strip();d=C.second_to_time(C.str2sec(A(N['length']).strip()))+BL+C.zh(N[Bx]) + if not V:d+=' 💬'+C.zh(N['video_review']) + J.append({R:m+k,O:V+l,S:C.format_img(n),Y:d}) + if W:J.reverse() + if F(K)==1: + C.get_up_info_event.wait();U=C.up_info[D];e=U[H]+Cc + if V:e='UP: '+U[H] + o={R:Ai+A(D),O:e,S:C.format_img(U[A5]),Y:U[AS]+' 👥'+U[Aj]+' 🎬'+A(U[BP])};J.insert(0,o) + if V:C.get_up_videos_result[D]=J + I[G]=J;I[a]=K;I[f]=99;I[g]=99;I[c]=999999 + return I + history_view_at=0 + def get_history(D,type,pg): + W='progress';H={} + if F(pg)==1:D.history_view_at=0 + X='https://api.bilibili.com/x/web-interface/history/cursor?ps={0}&view_at={1}&type={2}'.format(D.userConfig[Z],D.history_view_at,type) + if type==BQ:X='https://api.bilibili.com/x/v2/history/toview' + U=D._get_sth(X).json() + if U[M]==0: + b=[];V=U[E].get(G,[]) + if type==BQ:V=D.pagination(V,pg) + else:D.history_view_at=U[E]['cursor']['view_at'] + for C in V: + I=C.get('history',B) + if I:J=I['business'];K=A(I['oid']).strip();d=C[h].strip();Q=A(I[By]).strip() + else:J=BK;K=A(C[T]).strip();d=C[Au].strip();Q=A(C[a][By]).strip() + if J=='article':continue + elif J=='pgc':K=AT+A(I[o]);e=C[c];Q=C.get('show_title') + elif J==BK:K=m+K;e=C[Cd] + i=D.cleanCharacters(C[P]) + if J==AG:j=C.get('badge',B);N=j+Ag+C['author_name'].strip() + else: + if A(C[W])=='-1':N='已看完' + elif A(C[W])==L:N='刚开始看' + else:k=A(D.second_to_time(C[W])).strip();N='看到 '+k + if not e in[0,1]and Q:N+=' ('+A(Q)+')' + b.append({R:K,O:i,S:D.format_img(d),Y:N}) + H[G]=b;H[a]=pg;H[f]=9999;H[g]=90;H[c]=999999 + return H + def get_fav_detail(C,pg,mlid,order): + I='cnt_info';D={};J='https://api.bilibili.com/x/v3/fav/resource/list?media_id=%s&order=%s&pn=%s&ps=10&platform=web&type=0'%(mlid,order,pg);F=C._get_sth(J).json() + if F[M]==0: + H=[];K=F[E].get('medias',[]) + for B in K: + if B.get(k)in[2]and B.get(P)!='已失效视频':L=A(B[t]).strip();N=C.cleanCharacters(B[P]);Q=B[h].strip();T=A(C.second_to_time(B[n])).strip()+BL+C.zh(B[I][Bx])+'\u3000💬'+C.zh(B[I][BR]);H.append({R:m+L+'_mlid'+A(mlid),O:N,S:C.format_img(Q),Y:T}) + D[G]=H;D[a]=pg;D[f]=9999;D[g]=99;D[c]=999999 + return D + def get_up_videoNum(B,mid): + C={};I=f"http://api.bilibili.com/x/space/navnum?mid={mid}";D=B._get_sth(I,Q).json() + if D[M]==0: + C[BP]=A(D[E][p]).strip();G=divmod(F(C[BP]),B.userConfig[Z]);H=G[0] + if G[1]!=0:H+=1 + C[Cb]=H + B.up_info[mid].update(C);B.get_up_info_event.set() + get_up_info_event=x.Event();up_info={} + def get_up_info(B,mid,data={}): + J='Official';D=data;C=mid;B.up_info[C]=A=B.up_info.get(C,{});B.pool.submit(B.get_up_videoNum,C) + if not D: + K=f"https://api.bilibili.com/x/web-interface/card?mid={C}";G=B._get_sth(K).json() + if G[M]==0:D=G[E] + else:return A + F=D['card'];A[AS]='未关注' + if D[AS]:A[AS]='已关注' + A[H]=I=B.cleanCharacters(F[H]);A[Bz]=Ak+C+'_pubdate_getupvideos","name": "'+I.replace(AA,AH)+Az+I+A_;A[A5]=F[A5];A[Aj]=B.zh(F[Aj]);A[B_]=B.zh(D[B_]);A[Al]=F[J][Al]+AU+F[J][P];return A + def get_vod_relation(F,query): + G=f"https://api.bilibili.com/x/web-interface/archive/relation?{query}";A=F._get_sth(G).json();B=[] + if A[M]==0: + A=A[E] + if A[Ce]:B.append('已关注') + else:B.append('未关注') + C=[] + if A[Cf]:C.append('⭐') + if A[Am]:C.append('👍') + D=A.get(BS) + if D:C.append('💰'*D) + if U(C)==3:B.append('👍💰⭐') + else:B.extend(C) + if A['dislike']:B.append('👎') + if A['season_fav']:B.append('已订阅合集') + return B + def get_follow(I,pg,sort): + J=pg;D=sort;K={} + if D==BT:L='https://api.bilibili.com/x/relation/followings?vmid={0}&pn={1}&ps=10&order=desc&order_type=attention'.format(I.userid,J) + elif D==C0:L='https://api.bilibili.com/x/relation/followings?vmid={0}&pn={1}&ps=10&order=desc&order_type='.format(I.userid,J) + elif D==B0:L='https://api.live.bilibili.com/xlive/web-ucenter/v1/xfetter/GetWebList?page={0}&page_size=10'.format(J) + elif D==BU:L='https://api.bilibili.com/x/v2/history?pn={0}&ps=15'.format(J) + elif D==B1:L='https://api.bilibili.com/x/relation/tag?mid={0}&tagid=-10&pn={1}&ps=10'.format(I.userid,J) + elif D==C1:L='https://api.bilibili.com/x/relation/whispers?pn={0}&ps=10'.format(J) + else:L='https://api.bilibili.com/x/relation/followers?vmid={0}&pn={1}&ps=10&order=desc&order_type=attention'.format(I.userid,J) + Q=I._get_sth(L).json() + if Q[M]!=0:return K + if D==B1 or D==BU:T=Q[E] + elif D==B0:T=Q[E]['rooms'] + else:T=Q[E][G] + if F(J)==1:I.recently_up_list=[] + X=[] + for C in T: + U=B + if D==BU: + N=Ai+A(C[Ah][s]) + if N in I.recently_up_list:continue + I.recently_up_list.append(N);V=A(C[Ah][H]).strip();W=A(C[Ah][A5]).strip() + elif D==B0:N=A(C[Cg]);V=I.cleanCharacters(C[P]);W=C['cover_from_user'].strip();U=C[A6].strip() + else:N=Ai+A(C[s]);V=A(C[A6]).strip();W=A(C[A5]).strip() + if C2 in C and C[C2]==1:U=B1 + X.append({R:N,O:V,S:I.format_img(W),Y:U}) + K[G]=X;K[a]=J;K[f]=9999;K[g]=99;K[c]=999999;return K + def homeVideoContent(A):B=A.get_found(rid=L,tid=C3,pg=1)[G][:F(A.userConfig[Bl])];C={G:B};return C + def categoryContent(I,tid,pg,filter,extend): + u='_clicklink';O=pg;H=extend;D=tid;I.pool.submit(I.stop_heartbeat) + if D==AM: + if A1 in H:D=H[A1] + if D.isdigit(): + D=F(D) + if D>10000:D-=10000;return I.get_timeline(tid=D,pg=O) + b=D;D=C3;return I.get_found(tid=D,rid=b,pg=O) + b=L;return I.get_found(tid=D,rid=b,pg=O) + elif D==Ac: + D=V;P=A3;d='-1' + if A1 in H:D=H[A1] + if q in H:P=H[q] + if C4 in H: + if P==A3:P=A0 + d=H[C4] + return I.get_bangumi(D,O,P,d) + elif D==AN: + R=L;P=Ay + if s in H:R=H[s] + if q in H:P=H[q] + if R==L and not I.userid or R==As:return I.get_Login_qrcode(O) + return I.get_dynamic(pg=O,mid=R,order=P) + elif D==Ad: + D=A3;h=L + if A1 in H:D=H[A1] + if N in D:i=D.split(N);D=i[0];h=i[1] + return I.get_live(pg=O,parent_area_id=D,area_id=h) + elif D==As:return I.get_Login_qrcode(O) + elif D==BE: + j=BT + if C5 in H:j=H[C5] + return I.get_follow(O,j) + elif D==Ae: + S=A(I.userConfig[Bm]) + if Af in H:S=H[Af] + m=I.config[l].get(Ae) + if S in[V,A0]:return I.get_bangumi(tid=S,pg=O,order='追番剧',season_status=B) + elif S==L and m: + for Q in m: + if Q[W]==Af: + if U(Q[K])>1:S=Q[K][2][C] + break + P='mtime' + if q in H:P=H[q] + return I.get_fav_detail(pg=O,mlid=S,order=P) + elif D==BF: + type=C3 + if k in H:type=H[k] + if type==BJ:return I.get_follow(pg=O,sort=BU) + return I.get_history(type=type,pg=O) + elif D.endswith('_getbangumiseasons'): + if F(O)==1:return{G:I.detailContent_args[D.split(N)[0]][C6]} + elif D.endswith('_getupvideos'):R,P,v=D.split(N);return I.get_up_videos(pg=O,mid=R,order=P) + elif D.endswith('_related'): + w,v=D.split(N);x=f"https://api.bilibili.com/x/web-interface/archive/related?aid={w}";o=I._get_sth(x,e).json();T={} + if o.get(M)==0: + r=[] + for y in J(I.get_found_vod,o[E]):r.extend(y) + T[G]=r;T[a]=1;T[f]=1;T[g]=99;T[c]=40 + return T + elif D.endswith(u): + X=D.replace(u,B);Y=L + if n in H:Y=H[n] + return I.get_search_content(key=X,pg=O,duration_diff=Y,order=B,type=p,ps=I.userConfig[Z]) + else: + Y=L + if n in H:Y=H[n] + type=p + if k in H:type=H[k] + P=Ch + if q in H:P=H[q] + X=A(I.search_key);t=I.config[l].get(Aq) + if not X and t: + for Q in t: + if Q[W]==At: + if U(Q[K])>0:X=Q[K][0][C] + break + if At in H:X=H[At] + return I.get_search_content(key=X,pg=O,duration_diff=Y,order=P,type=type,ps=I.userConfig[Z]) + def get_search_content(D,key,pg,duration_diff,order,type,ps): + I=pg;L=BG + if not A(I).isdigit():L=I;I=1 + X=D.encrypt_wbi(keyword=key,page=I,duration=duration_diff,order=order,search_type=type,page_size=ps)[0];Z=f"https://api.bilibili.com/x/web-interface/wbi/search/type?{X}";V=D._get_sth(Z,Q).json();F={} + if V.get(M)==0 and u in V[E]: + W=[];J=V[E].get(u) + if J and type==AG:J=J.get('live_room') + if not J:return F + for C in J: + if type!=C[k]:continue + H=B + if type==C7:N=Ai+A(C[s]).strip();U=C['upic'].strip();K='👥'+D.zh(C[Aj])+' 🎬'+D.zh(C[Cd]);H=C[A6] + elif type==AG:N=A(C[CW]).strip();U=C[h].strip();K='👁'+D.zh(C['online'])+Ag+C[A6] + elif'media'in type:N=A9+A(C[A8]).strip();U=C[h].strip();K=A(C[Aw]).strip().replace('更新至','🆕') + else: + N=m+A(C[T]).strip();U=C[Au].strip();K=A(D.second_to_time(D.str2sec(C[n]))).strip()+BL+D.zh(C[Bx]) + if L==BG:K+=' 💬'+D.zh(C[BR]) + if not H:H=D.cleanCharacters(C[P]) + if L:H=L+H + W.append({R:N,O:H,S:D.format_img(U),Y:K}) + F[G]=W;F[a]=I;F[f]=9999;F[g]=99;F[c]=999999 + return F + def cleanSpace(C,s):return A(s).replace(An,B).replace('\t',B).replace('\r',B).replace(A7,B) + def cleanCharacters(C,s):return A(s).replace('<em class="keyword">',B).replace('</em>',B).replace('"',AA).replace('&','&') + def get_normal_episodes(L,episode): + D=episode;M=D.get(AI);C=L.detailContent_args + if M:C=C[M] + N=D.get(T,B) + if not N:N=C[T] + S=D.get(v,B);H=D.get(P,B) + if not H:H=D.get(By,B) + J=D.get(n,B) + if not J: + X=D.get(a,B) + if X:J=X[n] + E=G=Y=U=B;O=D.get('ep_id',B) + if AV in D and C8 in D[AV]:O=L.find_bangumi_id(D[AV]) + if O: + if J and A(J).endswith(BV):J=F(J/1000) + if H.isdigit():H='第'+H+C[C9] + E=D.get('badge',B) + if not L.session_vip.cookies and E=='会员'and L.userConfig[CN]or E=='付费'and L.userConfig[CO]:C[B2]=U=V + if L.session_vip.cookies:E=E.replace('会员',B) + if E==BW:E=E.replace(BW,B);Y=V + if E:E='【'+E+'】' + G=D.get('long_title',B) + if not E and G:G=A7+G + Q=H+E+G;Q=Q.replace(I,A2).replace(w,AJ) + if B3 in C: + if Q in C[B3]:Q+=f"_av{N}" + else:C[B3].append(Q) + K=f"{Q}${N}_{S}_{O}_{J}_" + if M:K+='@'+M + if f"{N}_{S}"in C:W=C[B4];W[0]=K+BX;K=I.join(W);C[B4]=W + Z=C.get(o,B) + if Z==AT+A(O):C[BY]=K + b=C.get(B5,B) + if b: + if Y:return K,B + if U: + if G:G='【解析】'+G + H+=G;R=f"{H}${N}_{S}_{O}_{J}_{U}" + if M:R+='@'+M + if Z==AT+A(O):C[BY]=R+I+C[BY] + else:R=K + return K,R + else:return K + def get_ugc_season(D,section,season_title,sec_len,array): + F=season_title;E=section + if sec_len>1:A=F+A7+E[P] + else:A=F + A=A.replace(I,A2).replace(w,AJ);G=E.get(BO);C=I.join(J(D.get_normal_episodes,J(lambda e:D.add_this_array(e,array),G))) + if BX in C:C=C.replace(BX,B);return A,C,0 + return A,C + def get_vodReply(K,oid,pg=B): + W='member';V='rpid';X=K.encrypt_wbi(type=1,ps=30,oid=A(oid))[0];Y=f"https://api.bilibili.com/x/v2/reply/wbi/main?{X}";L=K._get_sth(Y).json();O=B + if L[M]==0: + I=L[E].get('replies');Q=L[E].get('top_replies') + if Q and I:I=Q+I + if I: + Z=L[E]['upper'][s];R=[] + for F in I: + a=F[V];J=F[W]['sex'] + if J and J=='女':J='👧' + else:J='👦' + S=F[s];H=F[W][A6] + if S==Z:H='🆙'+H + b='👍'+K.zh(F[Am]);H=Ak+f'{S}_pubdate_getupvideos","name": "'+H.replace(AA,AH)+Az+b+J+H+A_+':';G=F[Av][Ci].strip() + if'/note-app/'in G:continue + if U(G)>400 or G.count(D)>24:G=K.cleanSpace(G) + c=F[Av].get('jump_url',{}) + for(C,N)in c.items(): + d=C + if not N.get('app_url_schema')and not N.get('pc_url'): + if C.startswith('https://www.bilibili.com/')or C.startswith('https://b23.tv/'): + C=A(C).split('?')[0].split(AR) + while C[-1]==B:C.pop(-1) + C=C[-1] + if C.startswith(m)or C.startswith('BV')or C.startswith(AT)or C.startswith(A9):a=A(F[V]);T=N[P].replace(AA,AH);e=Ak+C+'_clicklink","name": "'+T+Az+'▶'+T+A_;G=G.replace(d,e) + f=H+G;R.append(f) + O=An.join(R) + return O + def add_this_array(A,e,array):e[AI]=array;return e + detailContent_args={} + def detailContent(H,array): + A8='tag_name';A3='up_info';A1='relation';A0='vodReply';L=array;H.pool.submit(H.stop_heartbeat);L=L[0] + if L.startswith(Cj): + n=L.split(N) + if n[1]=='tab&filter':return H.setting_tab_filter_detailContent() + elif n[1]=='liveExtra':return H.setting_liveExtra_detailContent() + elif n[1]=='login':return H.setting_login_detailContent(n[2]) + if L.startswith(G):return H.series_detailContent(L) + if L.isdigit():return H.live_detailContent(L) + if L.startswith(Ai):return H.up_detailContent(L) + H.detailContent_args[L]=K={AI:L,**H.detailContent_args.get(L,{})};AO=K.get(B6) + if AO:return H.interaction_detailContent(K) + f=id=A4=p=B;V=K.get(T);AD=K.get(o) + if V: + L=f"av{V}" + if AD:L=AD + f=1 + K['_notfirst']=f + if L.startswith(A9)or L.startswith(AT):return H.ysContent(K) + for d in L.split(N): + if d.startswith(m):id=d.replace(m,B);p=H.encrypt_wbi(aid=id)[0] + elif d.startswith('BV'):id=d;p=H.encrypt_wbi(bvid=d)[0] + elif d.startswith(Af):A4=d.replace(Af,B) + if not A0 in K:K[A0]=H.pool.submit(H.get_vodReply,id) + if not A1 in K:K[A1]=H.pool.submit(H.get_vod_relation,p) + AP=f"https://api.bilibili.com/x/web-interface/wbi/view/detail?{p}";q=H._get_sth(AP,Q).json() + if q[M]!=0:return{} + W=q[E]['View'];AE=W.get(AV,B) + if C8 in AE:K[o]=id=H.find_bangumi_id(AE);return H.ysContent(K) + L=K[AI];Z=A(W[Ah][s]);K[T]=V=A(W.get(T));r=W.get(v) + if not A3 in K:K[A3]=H.pool.submit(H.get_up_info,mid=Z,data=q[E].get('Card')) + AF=H.cleanCharacters(W[P]);AQ=W[Au];j=W[Al].strip();AR=W['tname'];AS=X.strftime('%Y%m%d',X.localtime(W[Ay]));k=W[BM];t=W['rights'].get('is_stein_gate',0);g=[];g.append('▶'+H.zh(k[BN]));g.append('💬'+H.zh(k[BR]));g.append('👍'+H.zh(k[Am]));g.append('💰'+H.zh(k[BS]));g.append('⭐'+H.zh(k[Cf]));e={R:m+A(V),O:AF,S:AQ,BH:AR,CA:AS};e[Y]=AU.join(g) + if f"{V}_{r}"in K:K.pop(f"{V}_{r}") + A5=W[B4] + if A5:K[B4]=z(J(H.get_normal_episodes,J(lambda e:H.add_this_array(e,L),A5))) + a=[];c=[];u=[];A6=[];h=W.get(B3) + if h: + K[B3]=[];K[f"{V}_{r}"]=B;AG=h['sections'] + for AX in AG:b=H.pool.submit(H.get_ugc_season,AX,h[P],U(AG),L);A6.append(b) + for b in Bj(A6): + if b.result()[-1]==0: + a.insert(0,b.result()[0]);c.insert(0,b.result()[1]) + if not I in b.result()[1]:f=1 + else:a.append(b.result()[0]);c.append(b.result()[1]) + A6.remove(b) + u.append(T) + if not f:u+=[A0,A1,A3,f"{V}_{r}"] + else: + a=['B站'] + if t:a[0]='互动视频' + if not h or not f: + if A5:a=[a[0]];AY=I.join(K[B4]).replace(BX,B);c=[AY] + if H.userid: + AZ=f"➕关注${V}_{Z}__1__notplay_follow";Aa=f"➖取关${V}_{Z}__2__notplay_follow";Ab=f"👍点赞${V}_{Z}__1__notplay_like";Ac=f"👍🏻取消点赞${V}_{Z}__2__notplay_like";Ad=f"👍💰投币${V}_{Z}__1__notplay_coin";Ae=f"👍💰💰${V}_{Z}__2__notplay_coin";Ag=f"👍💰⭐三连${V}_{Z}____notplay_triple";A7=[AZ,Ag,Ab,Ad,Ae,Aa,Ac] + if A4:Ao=f"☆取消收藏${V}_{Z}__{A4}_del_notplay_fav";A7.append(Ao) + for x in H.userConfig.get(CQ,[]):Ap=x[D].replace(I,A2).replace(w,AJ);Aq=x[C];x=f"⭐{Ap}${V}_{Z}__{Aq}_add_notplay_fav";A7.append(x) + Ar=I.join(A7);a.insert(1,'做点什么');c.insert(1,Ar) + if t:c[0]='片头$'+c[0].split(w)[1] + e[AB]=i.join(a);e[AC]=i.join(c) + if not h or f: + y=[Ak+A(V)+'_related","name":"'+AF.replace(AA,AH)+'"}/]相关推荐[/a]'] + if U(j)<60 and j.count(D)<4:j+=An*F(3-U(j)/29) + y.append(j);As=';'.join(sorted(J(lambda x:Ak+x[A8].replace(AA,AH)+'_clicklink","name":"'+x[A8].replace(AA,AH)+Az+A2+x[A8]+A2+A_,q[E].get('Tags',[])),key=U));y.append(As);l=K.get(A3);AK=K.get(A1) + if l and AK:l=l.result();e[BZ]=Ba+l[Bz]+Ck+l[Aj]+AU+AU.join(AK.result()) + AL=K.get(A0) + if AL:y.append(AL.result()) + e[AW]=An.join(y) + if t:K[CB]=a.copy();K[Bb]=c.copy();K[CC]=e.copy();u+=[T,CB,Bb,CC] + if not h and not t:H.detailContent_args.pop(L) + else: + AM={} + for(AN,At)in K.items(): + if AN in u:AM[AN]=At + H.detailContent_args[L]=AM.copy() + Av={G:[e]};return Av + def interaction_detailContent(V,array): + C=array;M=C.get(AI);N=C.get(T);W=C.get(v,0);O=C.get(CD,0);X=C.get(B6);Y=f"https://api.bilibili.com/x/stein/edgeinfo_v2?aid={N}&graph_version={X}&edge_id={O}";F=V._get_sth(Y,Q).json().get(E);R={} + if F: + S=C.get(CB).copy();D=C.get(Bb).copy();H=C.get(CC) + if O:J=A(F[P]).replace(I,A2).replace(w,AJ);D[0]+=f"#{J}${N}_{W}___@{M}" + else:D[0]=D[0].split(I)[0] + C[Bb]=D.copy();Z=F['edges'].get('questions',[]);K=[] + for U in Z: + a=U.get(P,B) + for L in U.get('choices',[]):b=L[t];c=L[v];d=L.get('option',B);J=A7.join([a,d]).replace(I,A2).replace(w,AJ);K.append(f"{J}${b}_{c}_interaction@{M}") + if K:S.insert(1,'选项');D.insert(1,I.join(K)) + else:C.pop(CD);C.pop(v) + H[AB]=i.join(S);H[AC]=i.join(D);R[G]=[H] + return R + def series_detailContent(C,array): + U='archives';K=array;L,type,V=K.replace('list_',B).split(N);D=1;M=99;A={R:K,AB:'B站'};S=[] + while Ab: + W='https://api.bilibili.com/x/series/archives?mid=%s&series_id=%s&pn=%s&ps=%s'%(L,V,D,M);X=C._get_sth(W,Q).json();F=X.get(E) + if not A.get(O):A[O]=F[U][0][P] + Y=I.join(J(C.get_normal_episodes,F.get(U)));S.append(Y);Z=F[a][c] + if M*D>=Z:break + D+=1 + A[AC]=I.join(S);T=C.up_info[L];A[BZ]=Ba+T[H]+AU+T[AS];b={G:[A]};return b + def up_detailContent(L,array): + F=array.replace(Ai,B);L.get_up_info_event.clear();L.pool.submit(L.get_up_info,F);Q=f"关注$_{F}__1__notplay_follow";R=f"取消关注$_{F}__2__notplay_follow";T=f"特别关注$_{F}__-10_special_notplay_follow";U=f"取消特别关注$_{F}__0_special_notplay_follow";P=[Q,T,R,U];P='做点什么$ $$$'+I.join(P);L.get_up_info_event.wait();E=L.up_info[F];M={O:E[H]+Cc,S:E[A5],BZ:Ba+E[H]+AU+E[AS]+'\u3000UID:'+A(F),Y:'👥 '+E[Aj]+'\u3000🎬 '+E[BP]+'\u3000👍 '+E[B_],AW:E[Al]} + if L.userid:M[AB]='做点什么$$$关注TA';M[AC]=P + V=L.config[l].get(AN);M[Cl]=A7.join(J(lambda x:Ak+A(F)+N+x[C]+'_getupvideos","name": "'+E[H].replace(AA,AH)+' '+x[D]+Az+x[D]+A_,V[-1][K]));W={G:[M]};return W + def setting_login_detailContent(J,key): + M=key;E='f';D='d';C='c';W=J.cookie_dic_tmp.get(M,B);K=B + if not W:K=J.get_cookies(M) + if K:K=f"【{K}】通过手机客户端扫码确认登录后点击相应按钮设置账号" + else:K='【已扫码并确认登录】请点击相应按钮设置当前获取的账号为:' + Q={O:'登录与设置',AW:'通过手机客户端扫码并确认登录后,点击相应按钮设置cookie,设置后不需要管嗅探结果,直接返回二维码页面刷新,查看是否显示已登录,已登录即可重新打开APP以加载全部标签'};T=['登录$$$退出登录'];P=[];X=K+'$ ';Y='设置为主账号,动态收藏关注等内容源于此$'+A(M)+'_master_login_setting';Z='设置为备用的VIP账号,仅用于播放会员番剧$'+A(M)+'_vip_login_setting';P.append(I.join([X,Y,Z]));a='点击相应按钮退出账号>>>$ ';b='退出主账号$master_logout_setting';c='退出备用的VIP账号$vip_logout_setting';P.append(I.join([a,b,c]));d=[{E:'主页站点推荐栏',C:Bl,D:{AL:'3图',AQ:'4图','6':'6图','8':'8图','9':'9图'}},{E:'视频画质',C:Bn,D:J.vod_qn_id},{E:'视频编码',C:Bo,D:J.vod_codec_id},{E:'音频码率',C:Ap,D:J.vod_audio_id},{E:'收藏默认显示',C:Bm,D:{L:'默认收藏夹',V:'追番',A0:'追剧'}},{E:'上传播放进度',C:BA,D:{L:'关','15':'开'}},{E:'直播筛选细化',C:Bp,D:{L:'关',V:'开'}}] + for H in d: + T.append(H[E]);R=H[D][A(F(J.userConfig[H[C]]))] + if Ap==H[C]:R=A(R).replace(BV,'k') + U=['当前:'+R+'$ '] + for(id,S)in H[D].items(): + if Ap==H[C]:S=A(S).replace(BV,'k') + U.append(S+w+A(id)+N+H[C]+'_setting') + P.append(I.join(U)) + Q[AB]=i.join(T);Q[AC]=i.join(P);e={G:[Q]};return e + def setting_tab_filter_detailContent(K): + L={O:'标签与筛选',AW:'依次点击各标签,同一标签第一次点击为添加,第二次删除,可以返回到二维码页后重进本页查看预览,最后点击保存,未选择的将追加到末尾,如果未保存就重启app,将丢失未保存的配置'};M=[];P=[];V=[{D:BB,C:'标签'},{D:BC,C:'推荐[分区]'},{D:BD,C:'推荐[排行榜]'},{D:r,C:Ad}] + for Q in V: + F=Q[D];M.append(Q[C]);E=K.userConfig.get(A(F)+AD,[]);R=B + if E:R='【未保存】' + else:E=K.userConfig.get(F,[]) + if not E:E=K.defaultConfig.get(F) + if E and Aa(E[0])==d:E=z(J(lambda x:x[D],E)) + S=['当前: '+','.join(E)+'$ ',f"{R}点击这里保存$_{F}_save_setting",f"点击这里恢复默认并保存$_{F}_clear_setting"];T=K.defaultConfig[F].copy() + if F==r:W=K.userConfig.get(A4,[]);T.extend(W.copy()) + for H in T: + U=A(H) + if Aa(H)==d:U=H[D]+Ao+H[C].replace(N,Ao);H=H[D] + S.append(f"{H}${U}_{F}_setting") + P.append(I.join(S)) + L[AB]=i.join(M);L[AC]=i.join(P);X={G:[L]};return X + def setting_liveExtra_detailContent(H): + Q='_liveFilter_setting';F={O:CS,AW:'点击想要添加的标签,同一标签第一次点击为添加,第二次删除,完成后在[标签与筛选]页继续操作,以添加到直播筛选分区列中'};J=['已添加'];R=H.userConfig.get(A4,[]);E=['点击相应标签(只)可以删除$ #清空$clear_liveFilter_setting'] + for B in R:S=B[C];B=B[D];E.append(B+w+'del_'+B+N+S+Q) + E=[I.join(E)];T=H.userConfig.get(AO,{}) + for(V,W)in T.items(): + L=W[K][K] + if U(L)==1:continue + J.append(V);M=[] + for P in L:B=A(P[D]).replace(N,'-').replace(I,A2).replace(w,AJ);id=A(P[C]).replace(N,Ao).replace(I,A2).replace(w,AJ);M.append(B+'$add_'+B+N+id+Q) + E.append(I.join(M)) + F[AB]=i.join(J);F[AC]=i.join(E);X={G:[F]};return X + def get_all_season(C,season): + B=season;D=A(B[A8]);E=B[CE];F=C.detailContent_args[B[AI]] + if D==F[B5]:F[B7]=E + G=B[h];H=B[Ax][Aw];I={R:A9+D,O:E,S:C.format_img(G),Y:H};return I + def get_bangumi_section(B,section,array): + A=section;C=A[P].replace(I,A2).replace(w,AJ);D=A[k] + if D in[1,2]and U(A['episode_ids'])==0:E=A[BO];F=z(J(lambda x:B.get_normal_episodes(x)[0],J(lambda e:B.add_this_array(e,array),E)));return C,F + def ysContent(E,this_array): + r='rating';C=this_array;F=C[AI];X=C.get(T);d=C.get(o) + if d:F=d;C.pop(o) + if AT in F:X='ep_id='+F.replace(AT,B);C[o]=F + else:X='season_id='+F.replace(A9,B) + F=C[AI];s='https://api.bilibili.com/pgc/view/web/season?{0}'.format(X);D=E._get_sth(s,Q).json().get(u,{});C[B5]=Z=A(D[A8]);e=D[P];C[B7]=D[CE];C[C9]='集' + if D[k]in[1,4]:C[C9]='话' + N=D[Ax][Al] + if r in D:N=A(D[r]['score'])+'分 '+N + M=D.get(C6) + if U(M)==1:C[B7]=M[0][CE];M=0 + elif U(M)>1:C[C6]=z(J(E.get_all_season,J(lambda e:E.add_this_array(e,F),M)));N+=' [a=cr:{"id": "'+F+'_getbangumiseasons","name": "'+e.replace(AA,AH)+'"}/]更多系列[/a]' + f=D.get(BO);g=[] + for H in D.get('section',[]): + if H:a=E.pool.submit(E.get_bangumi_section,H,F);g.append(a) + t=D[h];v=D['share_sub_title'];w=D['publish']['pub_time'][0:4];x=D['evaluate'];j=D[BM];y='▶'+E.zh(j['views'])+'\u3000❤'+E.zh(j['favorites']);V={R:A9+Z,O:e,S:t,BH:v,CA:w,Cl:y,AW:x};V[Y]=N;W=[];K=[];L=[] + if f: + b=[];c=[] + for(l,m)in J(E.get_normal_episodes,J(lambda e:E.add_this_array(e,F),f)): + if m:b.append(l);c.append(m) + else:W.append(l) + if C.get(B2)and c:K.append(A(C[B7])+'【解析】');L.append(I.join(c)) + if b:K.append(A(C[B7]));L.append(I.join(b)) + n=[];p=[] + for a in Bj(g): + H=a.result() + if H: + if H[0]==BW:W+=H[1] + else:n.append(H[0]);p.append(I.join(H[1])) + if W:K.append(BW);L.append(I.join(W)) + K+=n;L+=p;q=C.get(BY) + if q:K.insert(0,'B站');L.insert(0,q) + if E.userid:A0='做点什么';A1=f"❤追番剧$__{Z}_add__notplay_zhui#💔取消追番剧$__{Z}_del__notplay_zhui";K.insert(1,A0);L.insert(1,A1) + V[AB]=i.join(K);V[AC]=i.join(L);A2={G:[V]};return A2 + def get_live_api2_playurl(W,room_id): + K=room_id;O=[];P=[];G='https://api.live.bilibili.com/xlive/web-room/v2/index/getRoomPlayInfo?room_id={0}&qn=0&platform=web&protocol=0,1&format=0,1,2&codec=0,1&dolby=5&panorama=1'.format(K);Q=W._get_sth(G,AF).json() + if Q[M]==0: + H=Q[E].get(Cm,B) + if H: + X=H[CF][Cn];C={Bc:{'avc':L,'hevc':V},AX:{'flv':L,'ts':V,'fmp4':A0}};C[AY]=d(J(lambda x:(x[AY],x[Al]),H[CF]['g_qn_desc']));R=[] + for Y in X:R.extend(Y[AX]) + D={} + for S in R: + format=A(S.get('format_name')) + for T in S[Bc]: + U=A(T.get('codec_name'));Z=T.get('accept_qn') + for F in Z: + G=format+N+U+f"$live_{K}_"+A(F)+N+C[AX][format]+N+C[Bc][U] + if not D.get(C[AY][F]):D[C[AY][F]]=[] + D[C[AY][F]].append(G) + for(a,b)in D.items():O.append(a);P.append(I.join(b)) + c=O,P;return c + def live_detailContent(C,room_id): + J=room_id;L=C.pool.submit(C.get_live_api2_playurl,J);W='https://api.live.bilibili.com/room/v1/Room/get_info?room_id='+A(J);N=C._get_sth(W,Q).json();T={} + if N.get(M)==0: + B=N[E];K=A(B['uid']);H=C.pool.submit(C.get_up_info,K);X=C.cleanCharacters(B[P]);Y=B.get(CX);Z=B.get(CY);a=B.get('parent_area_name')+'-'+B.get('area_name');D={R:J,O:X,S:Y,BH:a,AW:Z} + if F(B.get(CT)):D[CA]=B.get('live_time').replace('-','.') + U=L.result()[0];V=L.result()[1] + if C.userid:b='关注TA';c='是否关注$ ';d=f"➕关注$_{K}__1__notplay_follow";e=f"➖取关$_{K}__2__notplay_follow";f=[c,d,e];g=I.join(f);U.insert(1,b);V.insert(1,g) + D[AB]=i.join(U);D[AC]=i.join(V);H=H.result();D[BZ]=Ba+H[Bz]+Ck+C.zh(B.get(Ce))+AU+H[AS];T[G]=[D] + return T + search_key=B + def searchContent(A,key,quick,pg=V): + E=key + if not A.session_fake.cookies:A.pool.submit(A.getFakeCookie,Ab) + for C in A.task_pool:C.cancel() + if F(pg)>1:return A.get_search_content(key=E,pg=pg,duration_diff=0,order=B,type=p,ps=A.userConfig[Z]) + A.task_pool=[];A.search_key=E;I={p:B,Co:'番剧: ',Cp:'影视: ',C7:'用户: ',AG:'直播: '} + for(type,J)in I.items():C=A.pool.submit(A.get_search_content,key=E,pg=J,duration_diff=0,order=B,type=type,ps=A.userConfig[Z]);A.task_pool.append(C) + D={};H=[] + for C in Bj(A.task_pool):K=C.result().get(G,[]);H.extend(K);A.task_pool.remove(C) + if U(H):D[G]=H;D[a]=pg;D[f]=9999;D[g]=99;D[c]=999999 + return D + stop_heartbeat_event=x.Event() + def stop_heartbeat(A): + try: + for B in A.task_pool:B.cancel() + finally:A.stop_heartbeat_event.set() + def start_heartbeat(B,aid,cid,ssid,epid,duration,played_time): + E=played_time;G=F(B.userConfig[BA]) + if not B.userid or not G:return + H=F((duration-E)/G)+1;I='https://api.bilibili.com/x/click-interface/web/heartbeat';C={T:A(aid),v:A(cid),Bd:A(B.csrf)} + if ssid:C['sid']=A(ssid);C[o]=A(epid);C[k]=AQ + D=0;B.stop_heartbeat_event.clear() + while Ab: + if D==G or B.stop_heartbeat_event.is_set():E+=D;D=0 + if not D: + H-=1 + if not H:E=-1;B.stop_heartbeat_event.set() + C['played_time']=A(E);C=B.encrypt_wbi(**C)[1];B.pool.submit(B._post_sth,url=I,data=C) + if B.stop_heartbeat_event.is_set():break + X.sleep(1);D+=1 + wbi_key={} + def get_wbiKey(A,hour):D='wbi_img';C=A.fetch(CP,headers=A.header);F=C.json()[E][D]['img_url'];G=C.json()[E][D]['sub_url'];H=[46,47,18,2,53,8,23,32,15,50,10,31,58,3,45,35,27,43,5,49,33,9,42,19,29,28,14,39,12,38,41,13,37,48,7,16,24,55,40,61,26,17,0,1,60,51,30,4,22,25,54,21,56,59,6,63,57,62,11,36,20,34,44,52];I=F.split(AR)[-1].split('.')[0]+G.split(AR)[-1].split('.')[0];J=reduce(lambda s,i:s+I[i],H,B)[:32];A.wbi_key={W:J,'hour':hour} + def encrypt_wbi(D,**C): + E=AK(X.time());F=X.gmtime(E).tm_hour + if not D.wbi_key or F!=D.wbi_key['hour']:D.get_wbiKey(F) + C['wts']=E;G='ABCDEFGHIJK';C['dm_img_list']='[]';C['dm_img_str']=B.join(B8.sample(G,2));C['dm_cover_img_str']=B.join(B8.sample(G,2));C['dm_img_inter']='{"ds":[],"wh":[0,0,0],"of":[0,0,0]}';C=d(sorted(C.items()));C={C:B.join(filter(lambda chr:chr not in"!'()*",A(D)))for(C,D)in C.items()};H=Bk(C);I=hashlib.md5((H+D.wbi_key[W]).encode(encoding=Br)).hexdigest();C['w_rid']=I;return[H+'&w_rid='+I,C] + def _get_sth(A,url,_type=e,**C): + E=_type;B=url + if E==AF and A.session_vip.cookies:D=A.session_vip.get(B,headers=A.header,**C) + elif E==Q: + if not A.session_fake.cookies:A.getFakeCookie_event.wait() + D=A.session_fake.get(B,headers=A.header,**C) + else:D=A.session_master.get(B,headers=A.header,**C) + return D + def _post_sth(A,url,data):return A.session_master.post(url,headers=A.header,data=data) + def post_live_history(B,room_id):C={Cg:A(room_id),'platform':'pc',Bd:A(B.csrf)};D='https://api.live.bilibili.com/xlive/web-room/v1/index/roomEntryAction';B._post_sth(url=D,data=C) + def do_notplay(E,ids): + L='triple';K='fav';H,I,M,G,J,N,F=ids;C={Bd:A(E.csrf)};O=D=B + if F=='follow': + if J==C2:C.update({'fids':A(I),'tagids':A(G)});D='https://api.bilibili.com/x/relation/tags/addUsers' + else:C.update({'fid':A(I),'act':A(G)});D='https://api.bilibili.com/x/relation/modify' + elif F=='zhui':C.update({A8:A(M)});D='https://api.bilibili.com/pgc/web/follow/'+A(G) + elif F==Am:C.update({T:A(H),Am:A(G)});D='https://api.bilibili.com/x/web-interface/archive/like' + elif F==BS:C.update({T:A(H),'multiply':A(G),'select_like':V});D='https://api.bilibili.com/x/web-interface/coin/add' + elif F==K:C.update({'rid':A(H),k:A0});C[J+'_media_ids']=A(G);D='https://api.bilibili.com/x/v3/fav/resource/deal' + elif F==L:C.update({T:A(H)});D='https://api.bilibili.com/x/web-interface/archive/like/triple' + E._post_sth(url=D,data=C) + if F in[Am,BS,K,L]:C={T:A(H),Bd:A(E.csrf),'csrf_token':A(E.csrf)};D='https://api.bilibili.com/x/web-interface/share/add';E.pool.submit(E._post_sth,url=D,data=C) + E._refreshDetail() + def get_cid(D,aid,cid): + C=cid;G=f"https://api.bilibili.com/x/web-interface/view?aid={aid}&cid={C}";A=D._get_sth(G).json().get(E,{}) + if not C:C=A[v] + H=A[n];F=B + if AV in A and C8 in A[AV]:F=D.find_bangumi_id(A[AV]) + return C,H,F + cookie_dic_tmp={} + def get_cookies(A,key): + D='https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key='+key;B=A._get_sth(D,Q).json() + if B[M]==0: + C=B[E][Ci] + if not C:A.cookie_dic_tmp[key]=d(A.session_fake.cookies);A.pool.submit(A.getFakeCookie) + return C + return'网络错误' + def set_cookie(A,key,_type): + D=_type;C=key;F=A.cookie_dic_tmp.get(C,B) + if not F: + G=A.get_cookies(C) + if G:return + E=A.userConfig.get(j,{});E[D]={AE:A.cookie_dic_tmp.get(C,{})};A.userConfig.update({j:E});A.getCookie(D);A.dump_config() + def unset_cookie(A,_type): + C=_type + if C==AF:A.session_vip.cookies.clear() + else:A.session_master.cookies=A.session_fake.cookies;A.userid=A.csrf=B + if C in A.userConfig.get(j,{}):A.userConfig[j].pop(C);A.dump_config() + def set_normal_default(B,id,type):B.userConfig[type]=A(id);B.dump_config() + def set_normal_cateManual(B,name,_List,action): + H=action;F=name;E=_List;G=B.userConfig.get(A(E)+AD) + if not G:G=B.userConfig[A(E)+AD]=[] + if H=='save': + for I in B.defaultConfig[E]: + if not I in G.copy():B.userConfig[A(E)+AD].append(I) + B.userConfig[E]=B.userConfig[A(E)+AD].copy();B.userConfig.pop(E+AD);B.dump_config() + elif H=='clear':B.userConfig[E]=B.defaultConfig[E].copy();B.userConfig.pop(A(E)+AD);B.dump_config() + else: + if E==r: + F=F.split(Ao) + if U(F)==3:F[1]+=N+A(F[2]) + F={D:F[0],C:A(F[1])} + if F in G:B.userConfig[A(E)+AD].remove(F) + else:B.userConfig[A(E)+AD].append(F) + def add_cateManualLiveExtra(A,action,name,id): + F='cateManualLive_tmp';G=A.userConfig.get(A4,[]) + if not G:G=A.userConfig[A4]=[] + if action=='clear': + for E in G: + E[C]=E[C].replace(Ao,N) + if E in A.userConfig.get(r,[]):A.userConfig[r].remove(E) + if E in A.userConfig.get(F,[]):A.userConfig[F].remove(E) + A.userConfig.pop(A4) + elif id in z(J(lambda x:x[C],A.userConfig.get(A4,[]))): + B={D:name,C:id};A.userConfig[A4].remove(B);B[C]=id.replace(Ao,N) + if B in A.userConfig.get(r,[]):A.userConfig[r].remove(B) + if B in A.userConfig.get(F,[]):A.userConfig[F].remove(B) + else:B={D:name,C:id};A.userConfig[A4].append(B) + A.dump_config() + vod_qn_id={'127':'8K','126':'杜比视界','125':'HDR','120':'4K','116':'1080P60帧','112':'1080P+','80':'1080P','64':'720P'};vod_codec_id={'7':'avc','12':'hevc','13':'av1'};vod_audio_id={'30251':'Hi-Res无损','30250':'杜比全景声','30280':Cq,'30232':'132000','30216':'64000'} + def get_dash_media(E,media,aid,cid,qn): + I='SegmentBase';C=media;F=A(C.get(t));J=C.get(CG,B);K=C.get('codecs');L=C.get('bandwidth');M=C.get('startWithSap');H=C.get(Cr);O=C[I].get('indexRange');P=C[I].get('Initialization');D=H.split(AR)[0];G=B + if D==p:Q=C.get('frameRate');R=C.get('sar');S=C.get('width');T=C.get('height');G=f"height='{T}' width='{S}' frameRate='{Q}' sar='{R}'" + elif D==AZ:U=E.vod_audio_id.get(F,Cq);G=f"numChannels='2' sampleRate='{U}'" + V=f"{E.localProxyUrl}{D}&aid={aid}&cid={cid}&qn={qn}".replace('&','&');F+=N+A(J);W=f''' + <Representation id="{F}" bandwidth="{L}" codecs="{K}" mimeType="{H}" {G} startWithSAP="{M}"> + <BaseURL>{V}</BaseURL> + <SegmentBase indexRange="{O}"> + <Initialization range="{P}"/> + </SegmentBase> + </Representation>''';E.pC_urlDic[f"{aid}_{cid}"][D]=C;return W + def get_dash_media_list(E,media_lis,aid,cid,qn): + F=media_lis + if not F:return B + G=F[0][Cr].split(AR)[0] + if G==p:I=A(qn);H=A(E.userConfig[Bo]) + else:I=A(E.userConfig[Ap]);H=L + C={} + for D in F: + if G==AZ and not C:C=D + if A(D[t])==I: + if not C or A(D[CG])==H: + C=D + if A(D[CG])==H:break + J=f'\n <AdaptationSet>\n <ContentComponent contentType="{G}"/>{E.get_dash_media(C,aid,cid,qn)}\n </AdaptationSet>';return J + def get_dash(B,ja,aid,cid,qn): + A=ja;D=A.get(n);G=A.get('minBufferTime');H=B.pool.submit(B.get_dash_media_list,A.get(p),aid,cid,qn);C=A.get(AZ,[]);E=A.get('dolby',{}).get(AZ) + if E:C=E+C + F=A.get('flac') + if Aa(F)==d:C.insert(0,F.get(AZ)) + I=B.pool.submit(B.get_dash_media_list,C,aid,cid,qn);J=f'<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:dash:schema:mpd:2011" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" type="static" mediaPresentationDuration="PT{D}S" minBufferTime="PT{G}S" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011">\n <Period duration="PT{D}S" start="PT0S">{H.result()}{I.result()}\n </Period>\n</MPD>';return J + def miao(B,m): + m=A(m).partition('.')[2] + if U(m)==0:m=BV + if U(m)==1:m=m+'00' + if U(m)==2:m=m+L + return m + def down_sub(C,url): + I=C._get_sth(url,Q).json()['body'];E=B;F=1 + for D in I:G=AK(D['from'],3);H=AK(D['to'],3);J=D[Av];K=X.strftime(Bw,X.gmtime(G))+','+C.miao(G);L=X.strftime(Bw,X.gmtime(H))+','+C.miao(H);E+=A(F)+An+K+A7+'-->'+A7+L+An+J+'\n\n';F+=1 + return E + localProxyUrl='http://127.0.0.1:9978/proxy?do=py&siteType=3&siteKey=py_bilibili&type=' + def get_subs(J,aid,cid): + L='application/x-subrip';I=[];M=J.encrypt_wbi(aid=aid,cid=cid)[0];N=f"https://api.bilibili.com/x/player/wbi/v2?{M}";D=J._get_sth(N,e).json().get(E) + if D: + for K in D[Cs].get('subtitles',[]): + O=A(K.get('lan_doc',B));C=K.get('subtitle_url') + if C.startswith('//'):C=CR+C + C=CK(C);I.append({b:f"{J.localProxyUrl}subtitle&url={C}",H:O,AX:L}) + if I:I.insert(0,{b:B,H:A7,AX:L}) + G=0 + if F(D.get('last_play_cid',0))==F(cid): + G=F(D.get('last_play_time')) + if G>0:G=F(G/1000) + P=D.get(Ct,{}).get(B6,B);return I,G,P + pC_urlDic={} + def _get_playerContent(G,result,aid,cid,epid): + e='durls';c='video_info';W='quality';P=epid;N=cid;L=aid;C=result;G.pC_urlDic[f"{L}_{N}"]=I={**G.pC_urlDic.get(f"{L}_{N}",{}),T:L,v:N,o:P};R=G.userConfig[Bn] + if P:H='https://api.bilibili.com/pgc/player/web/v2/playurl?aid={}&cid={}&qn={}&fnval=4048&fnver=0&fourk=1&from_client=BROWSER'.format(L,N,R) + else: + X={'avid':L,v:N,AY:R,'fnval':4048,'fnver':0,'fourk':1,'from_client':'BROWSER'} + if not G.session_vip.cookies:X['try_look']=1 + f=G.encrypt_wbi(**X)[0];H=f"https://api.bilibili.com/x/player/wbi/playurl?{f}" + Q=G._get_sth(H,AF).json();S=B + if Q[M]==0: + if E in Q:D=Q[E] + elif u in Q: + D=Q[u] + if c in D:Y=D['view_info']['report'];S=Y[A8];P=Y['ep_id'];D=D[c] + else:return C + else:return C + I[B5]=S;I[o]=P;Z=d(J(lambda x:(x[W],x['new_description']),D['support_formats']));C[b]=[];U=D.get('dash');V=f"&aid={L}&cid={N}&qn=" + if U: + I[CH]=U;C[AX]=Cu + for g in U[p]: + id=g[t];O=Z[id] + if not O in C[b]: + H=f"{G.localProxyUrl}dash{V}{id}" + if id==F(R):C[b]=[O,H]+C[b] + else:C[b].extend([O,H]) + elif e in D: + for a in D[e]: + K=a[W];O=Z[K];H=f"{G.localProxyUrl}durl{V}{K}" + if K==F(R):C[b]=[O,H]+C[b] + else:C[b].extend([O,H]) + I[A(K)]=a[Be][0] + else:K=D[W];I[A(K)]=D[Be][0];C[b]=f"{G.localProxyUrl}durl{V}{K}" + I[u]={**I.get(u,{}),**C};return C,S,P + def _refreshDetail(A,t=0):X.sleep(F(t));A.fetch('http://127.0.0.1:9978/action?do=refresh&type=detail') + def playerContent(C,flag,id,vipFlags): + C.pool.submit(C.stop_heartbeat);D={};P=B + if'@'in id:id,P=id.split('@') + I=C.detailContent_args.get(P,C.detailContent_args);H=id.split(N) + if U(H)<2:return D + if AG==H[0]:return C.live_playerContent(id) + G=H[0];E=H[1] + if Cj in H: + if'liveFilter'in H:id=H[2];C.add_cateManualLiveExtra(G,E,id) + elif E in[BB,r,BC,BD]:S=H[2];C.set_normal_cateManual(G,E,S) + elif'login'in H:C.set_cookie(G,E) + elif'logout'in H:C.unset_cookie(G) + else:C.set_normal_default(G,E) + return D + elif'notplay'in H:C.pool.submit(C.do_notplay,H);return D + elif Ct in H:I[CD]=G;I[v]=E;C.pool.submit(C._refreshDetail);return D + G,E,J,M,W=id.split(N) + if not E or not M:E,M,J=C.get_cid(G,E) + D[BR]='https://api.bilibili.com/x/v1/dm/list.so?oid='+A(E) + if W:X='https://www.bilibili.com/bangumi/play/ep'+A(J);D[b]=X;D['flag']='bilibili';D[B2]=V;D['jx']=V;D[CI]={AP:C.header[AP]};return D + Y=C.pool.submit(C.get_subs,G,E);K=C.pC_urlDic.get(f"{G}_{E}") + if K:D,Q,J=K[u],K[B5],K[o] + else:D[B2]=L;D[CJ]=B;D[CI]=C.header;D,Q,J=C._get_playerContent(D,G,E,J) + D['subs'],Z,O=Y.result();a=I.get(B6,B);R=I.get(T) + if R and G!=R or f"{G}_{E}"in I:I[T]=G;C.pool.submit(C._refreshDetail,2) + elif O and a!=O:I[B6]=O;C.pool.submit(C._refreshDetail) + else:c=C.pool.submit(C.start_heartbeat,G,E,Q,J,F(M),Z);C.task_pool.append(c) + return D + def live_playerContent(D,id): + K='url_info';T,I,O,format,G=id.split(N) + if D.userid and F(D.userConfig[BA])>0:D.pool.submit(D.post_live_history,I) + P='https://api.live.bilibili.com/xlive/web-room/v2/index/getRoomPlayInfo?room_id={0}&protocol=0,1&format={1}&codec={2}&qn={3}&ptype=8&platform=web&dolby=5&panorama=1'.format(I,format,G,O);J=D._get_sth(P,AF).json();C={} + if J[M]==0: + try:H=J[E][Cm].get(CF);G=H[Cn][0][AX][0][Bc][0] + except:return C + Q=A(G['base_url']);R=A(G[K][0]['host']);S=A(G[K][0]['extra']);H=R+Q+S;C[b]=H;C[CJ]=B + if'.flv'in H:C[CJ]='video/x-flv' + else:return C + C[B2]=L;C[CI]={Cv:'https://live.bilibili.com',AP:D.header[AP]};return C + def _testUrl(A,url,id,mediaType): + B=head(url,headers=A.header,timeout=5).status_code + if B!=200:A.pC_urlDic[id][mediaType].pop(url) + def get_fastesUrl(C,ja,id,mediaType): + E=mediaType;A=ja;D=A + if Aa(A)==d:C.pC_urlDic[id][E]=D=[A.get('baseUrl',A.get(b,B))];D.extend(A.get('backup_url',[]));C.pC_urlDic[id][Bf]=F(d(J(lambda x:x.split('=')[:2],D[0].split('?')[1].split('&'))).get(Bf,0)) + for G in D:C.pool.submit(C._testUrl,G,id,E) + def localProxy(D,param): + N='range';M='application/octet-stream';E=param;A=E.get(k) + if A==Cs:O=D.down_sub(E[b]);return[200,M,O] + F=E.get(T);G=E.get(v);H=E.get(AY);C=D.pC_urlDic[f"{F}_{G}"] + if A=='dash':P=D.get_dash(C[CH],F,G,H);return[200,Cu,P] + if A in[Be,p,AZ]: + if A==Be:A=H + K=AK(X.time());I=C.get(Bf) + if Aa(C[A])==d or(I-K)%10==0:D.get_fastesUrl(C[A],f"{F}_{G}",A);I=C.get(Bf) + J=B8.choice(C[A]) + if not J or A!=AZ and I-K<1800: + D._get_playerContent({},F,G,C[o]);C=D.pC_urlDic[f"{F}_{G}"] + if A==p:D.get_dash(C[CH],F,G,H) + D.get_fastesUrl(C[A],f"{F}_{G}",A);J=B8.choice(C[A]) + L=D.header.copy() + if N in E:L['Range']=E[N] + Q=D.fetch(J,headers=L,stream=Ab);return[206,M,Q.content] + return[404,'text/plain',B] + config={'player':{},l:{BE:[{W:C5,H:'分类',K:[{D:B0,C:B0},{D:BT,C:BT},{D:C0,C:C0},{D:B1,C:B1},{D:C1,C:C1},{D:'我的粉丝',C:'我的粉丝'}]}],AN:[{W:q,H:'别人投稿排序',K:[{D:'最新发布',C:Ay},{D:'最多播放',C:'click'},{D:'最多收藏',C:'stow'},{D:'最早发布',C:CZ},{D:'合集和列表',C:Ca}]}],Ac:[{W:A1,H:'分类',K:[{D:'番剧',C:V},{D:'国创',C:AQ},{D:'电影',C:A0},{D:'电视剧',C:'5'},{D:'纪录片',C:AL},{D:'综艺',C:'7'}]},{W:q,H:'排序',K:[{D:A3,C:A3},{D:'播放数量',C:A0},{D:'更新时间',C:L},{D:'最高评分',C:AQ},{D:'弹幕数量',C:V},{D:'追看人数',C:AL},{D:'开播时间',C:'5'},{D:'上映时间',C:'6'}]},{W:C4,H:'付费',K:[{D:'全部',C:'-1'},{D:'免费',C:V},{D:'付费',C:'2%2C6'},{D:'大会员',C:'4%2C6'}]}],Ae:[{W:q,H:'排序',K:[{D:'收藏时间',C:'mtime'},{D:'播放量',C:BN},{D:'投稿时间',C:'pubtime'}]}],BF:[{W:k,H:'分类',K:[{D:'视频',C:BK},{D:Ad,C:AG},{D:BJ,C:BJ},{D:BQ,C:BQ}]}],Aq:[{W:k,H:'类型',K:[{D:'视频',C:p},{D:'番剧',C:Co},{D:Ac,C:Cp},{D:Ad,C:AG},{D:'用户',C:C7}]},{W:q,H:'视频排序',K:[{D:'综合排序',C:Ch},{D:'最多点击',C:'click'},{D:'最新发布',C:Ay},{D:'最多收藏',C:'stow'},{D:'最多弹幕',C:'dm'}]},{W:n,H:'视频时长',K:[{D:'全部',C:L},{D:'60分钟以上',C:AQ},{D:'30~60分钟',C:AL},{D:'5~30分钟',C:A0},{D:'5分钟以下',C:V}]}]}};header={'Origin':'https://www.bilibili.com',Cv:'https://space.bilibili.com',AP:'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0'} \ No newline at end of file diff --git a/py/py_hitv.py b/py/py_hitv.py new file mode 100644 index 0000000..252fde4 --- /dev/null +++ b/py/py_hitv.py @@ -0,0 +1,151 @@ +# coding=utf-8 +# !/usr/bin/python +# 嗷呜 +import os +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 = self.fetch(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 = self.fetch(self.host + path, headers=self.headers).json() + print(data) + 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 = self.fetch(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 = self.fetch(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_次元.py b/py/py_次元.py new file mode 100644 index 0000000..2054183 --- /dev/null +++ b/py/py_次元.py @@ -0,0 +1,205 @@ +# coding=utf-8 +# !/usr/bin/python +# 嗷呜 +import sys + +sys.path.append("..") +import re +from base.spider import Spider +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad +from base64 import b64encode, b64decode +import json +import time + + +class Spider(Spider): + + def getName(self): + return "次元" + + def init(self, extend=""): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def action(self, action): + pass + + def destroy(self): + pass + + host = "http://www.ecydm.top" + t = str(int(time.time())) + + def header(self): + header = { + "User-Agent": "okhttp/3.14.9", + "app-version-code": "167", + "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", + } + return header + + def homeContent(self, filter): + data = self.fetch( + "{0}/api.php/getappapi.index/initV119".format(self.host), + headers=self.header(), + ).json() + data1 = self.aes("decrypt", data["data"]) + dy = { + "class": "类型", + "area": "地区", + "lang": "语言", + "year": "年份", + "letter": "字母", + "by": "排序", + "sort": "排序", + } + + filters = {} + classes = [] + json_data = json.loads(data1)["type_list"] + self.homedata = json.loads(data1)["banner_list"] + for item in json_data: + if item["type_name"] == "全部": + continue + has_non_empty_field = False + jsontype_extend = json.loads(item["type_extend"]) + self.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 + return result + + def homeVideoContent(self): + result = {"list": self.homedata} + return result + + 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 = {} + url = "{0}/api.php/getappapi.index/typeFilterVodList".format(self.host) + data = self.post(url, headers=self.header(), data=body).json() + data1 = self.aes("decrypt", data["data"]) + result["list"] = json.loads(data1)["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]}" + url = "{0}/api.php/getappapi.index/vodDetail".format(self.host) + data = self.post(url, headers=self.header(), data=body).json() + data1 = json.loads(self.aes("decrypt", data["data"])) + vod = data1["vod"] + play = [] + names = [] + + for itt in data1["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).text + data1 = json.loads(data)["data"] + data2 = self.aes("decrypt", data1) + result = {"list": json.loads(data2)["search_list",'page':pg]} + 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 + p = 0 + if "m3u8" not in url and "mp4" not in url: + try: + body = edu(url) + data = self.post("{0}/api.php/getappapi.index/vodParse".format(self.host), headers=self.header(), + data=body).json() + data1 = json.loads(self.aes("decrypt", data["data"]))["json"] + url = json.loads(data1)["url"] + except Exception as e: + url = id + p = 1 + if not url.startswith("https://www.yemu.xyz"): + url = "https://www.yemu.xyz/?url={0}".format(id) + result = {} + headers = self.header().copy() + del headers["Content-Type"] + result["parse"] = p + result["url"] = url + result["header"] = headers + return result + def localProxy(self, param): + pass + + def aes(self, operation, text): + key = "20c79c979da8db0f".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") diff --git a/py/py_电影猎手.py b/py/py_电影猎手.py new file mode 100644 index 0000000..1a6a4d7 --- /dev/null +++ b/py/py_电影猎手.py @@ -0,0 +1,279 @@ +# coding=utf-8 +# !/usr/bin/python +# by嗷呜(finally) +import sys +import os +sys.path.append("..") +import re +import hashlib +import hmac +import random +import string +from Crypto.Util.Padding import unpad +from concurrent.futures import ThreadPoolExecutor +from Crypto.PublicKey import RSA +from Crypto.Cipher import PKCS1_v1_5, AES +from base64 import b64encode, b64decode +import json +import time +from base.spider import Spider + +class Spider(Spider): + + def getName(self): + return "电影猎手" + + def init(self, extend=""): + self.device = self.device_id() + self.host = self.gethost() + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def action(self, action): + pass + + def destroy(self): + pass + + t = str(int(time.time())) + + def homeContent(self, filter): + result = {} + filters = {} + classes = [] + bba = self.url() + data = self.fetch(f"{self.host}/api/v1/app/config?pack={bba[0]}&signature={bba[1]}", headers=self.header()).text + data1 = self.aes(data) + dy = {"class":"类型","area":"地区","lang":"语言","year":"年份","letter":"字母","by":"排序","sort":"排序"} + data1['data']['movie_screen']['sort'].pop(0) + for item in data1['data']['movie_screen']['sort']: + item['n'] = item.pop('name') + item['v'] = item.pop('value') + for item in data1['data']['movie_screen']['filter']: + has_non_empty_field = False + classes.append({"type_name": item["name"], "type_id": str(item["id"])}) + for key in dy: + if key in item and item[key]: + has_non_empty_field = True + break + if has_non_empty_field: + filters[str(item["id"])] = [] + filters[str(item["id"])].append( + {"key": 'sort', "name": '排序', "value": data1['data']['movie_screen']['sort']}) + for dkey in item: + if dkey in dy and item[dkey]: + item[dkey].pop(0) + value_array = [ + {"n": value.strip(), "v": value.strip()} + for value in item[dkey] + if value.strip() != "" + ] + filters[str(item["id"])].append( + {"key": dkey, "name": dy[dkey], "value": value_array} + ) + result["class"] = classes + result["filters"] = filters + return result + + def homeVideoContent(self): + bba = self.url() + url = f'{self.host}/api/v1/movie/index_recommend?pack={bba[0]}&signature={bba[1]}' + data = self.fetch(url, headers=self.header()).json() + videos = [] + for item in data['data']: + if len(item['list']) > 0: + for it in item['list']: + try: + videos.append(self.voides(it)) + except Exception as e: + continue + result = {"list": videos} + return result + + def categoryContent(self, tid, pg, filter, extend): + body = {"type_id": tid, "sort": extend.get("sort", "by_default"), "class": extend.get("class", "类型"), + "area": extend.get("area", "地区"), "year": extend.get("year", "年份"), "page": str(pg), + "pageSize": "21"} + result = {} + list = [] + bba = self.url(body) + url = f"{self.host}/api/v1/movie/screen/list?pack={bba[0]}&signature={bba[1]}" + data = self.fetch(url, headers=self.header()).json()['data']['list'] + for item in data: + list.append(self.voides(item)) + result["list"] = list + result["page"] = pg + result["pagecount"] = 9999 + result["limit"] = 90 + result["total"] = 999999 + return result + + def detailContent(self, ids): + body = {"id": ids[0]} + bba = self.url(body) + url = f'{self.host}/api/v1/movie/detail?pack={bba[0]}&signature={bba[1]}' + data = self.fetch(url, headers=self.header()).json()['data'] + video = {'vod_name': data.get('name'),'type_name': data.get('type_name'),'vod_year': data.get('year'),'vod_area': data.get('area'),'vod_remarks': data.get('dynami'),'vod_content': data.get('content')} + play = [] + names = [] + tasks = [] + for itt in data["play_from"]: + name = itt["name"] + a = [] + if len(itt["list"]) > 0: + names.append(name) + play.append(self.playeach(itt['list'])) + else: + tasks.append({"movie_id": ids[0], "from_code": itt["code"]}) + names.append(name) + if tasks: + with ThreadPoolExecutor(max_workers=len(tasks)) as executor: + results = executor.map(self.playlist, tasks) + for result in results: + if result: + play.append(result) + else: + play.append("") + video["vod_play_from"] = "$$$".join(names) + video["vod_play_url"] = "$$$".join(play) + result = {"list": [video]} + return result + + def searchContent(self, key, quick, pg=1): + body = {"keyword": key, "sort": "", "type_id": "0", "page": str(pg), "pageSize": "10", + "res_type": "by_movie_name"} + bba = self.url(body) + url = f"{self.host}/api/v1/movie/search?pack={bba[0]}&signature={bba[1]}" + data = self.fetch(url, headers=self.header()).json()['data'].get('list') + videos = [] + for it in data: + try: + videos.append(self.voides(it)) + except Exception as e: + continue + result = {"list": videos, "page": pg} + return result + + def playerContent(self, flag, id, vipFlags): + url = id + if "m3u8" not in url and "mp4" not in url: + try: + add = id.split('|||') + data = {"from_code": add[0], "play_url": add[1], "episode_id": add[2], "type": "play"} + bba = self.url(data) + data2 = self.fetch(f"{self.host}/api/v1/movie_addr/parse_url?pack={bba[0]}&signature={bba[1]}", + headers=self.header()).json()['data'] + url = data2.get('play_url') or data2.get('download_url') + try: + url1 = self.fetch(url, headers=self.header(), allow_redirects=False).headers['Location'] + if url1 and "http" in 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/4.9.2'} + return result + + def localProxy(self, param): + url = b64decode(param["url"]).decode('utf-8') + durl = url[:url.rfind('/')] + data = self.fetch(url, headers=self.header()).content.decode("utf-8") + lines = data.strip().split('\n') + for index, string in enumerate(lines): + # if 'URI="' in string and 'http' not in string: + # lines[index] = index + # 暂时预留,貌似用不到 + 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 device_id(self): + characters = string.ascii_lowercase + string.digits + random_string = ''.join(random.choices(characters, k=32)) + return random_string + + def gethost(self): + headers = { + 'User-Agent': 'okhttp/4.9.2', + 'Connection': 'Keep-Alive', + } + response = self.fetch('https://app-site.ecoliving168.com/domain_v5.json', headers=headers).json() + url = response['api_service'].replace('/api/', '') + return url + + def header(self): + headers = { + 'User-Agent': 'Android', + 'Accept': 'application/prs.55App.v2+json', + 'timestamp': self.t, + 'x-client-setting': '{"pure-mode":1}', + 'x-client-uuid': '{"device_id":' + self.device + '}, "type":1,"brand":"Redmi", "model":"M2012K10C", "system_version":30, "sdk_version":"3.1.0.7"}', + 'x-client-version': '3096 ' + } + return headers + + def url(self, id=None): + if not id: + id = {} + id["timestamp"] = self.t + public_key = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA02F/kPg5A2NX4qZ5JSns+bjhVMCC6JbTiTKpbgNgiXU+Kkorg6Dj76gS68gB8llhbUKCXjIdygnHPrxVHWfzmzisq9P9awmXBkCk74Skglx2LKHa/mNz9ivg6YzQ5pQFUEWS0DfomGBXVtqvBlOXMCRxp69oWaMsnfjnBV+0J7vHbXzUIkqBLdXSNfM9Ag5qdRDrJC3CqB65EJ3ARWVzZTTcXSdMW9i3qzEZPawPNPe5yPYbMZIoXLcrqvEZnRK1oak67/ihf7iwPJqdc+68ZYEmmdqwunOvRdjq89fQMVelmqcRD9RYe08v+xDxG9Co9z7hcXGTsUquMxkh29uNawIDAQAB' + encrypted_text = json.dumps(id) + public_key = RSA.import_key(b64decode(public_key)) + cipher = PKCS1_v1_5.new(public_key) + encrypted_message = cipher.encrypt(encrypted_text.encode('utf-8')) + encrypted_message_base64 = b64encode(encrypted_message).decode('utf-8') + result = encrypted_message_base64.replace('+', '-').replace('/', '_').replace('=', '') + key = '635a580fcb5dc6e60caa39c31a7bde48' + sign = hmac.new(key.encode(), result.encode(), hashlib.md5).hexdigest() + return result, sign + + def playlist(self, body): + try: + bba = self.url(body) + url = f'{self.host}/api/v1/movie_addr/list?pack={bba[0]}&signature={bba[1]}' + data = self.fetch(url, headers=self.header()).json()['data'] + return self.playeach(data) + except Exception: + return [] + + def playeach(self,data): + play_urls = [] + for it in data: + if re.search(r"mp4|m3u8", it["play_url"]): + play_urls.append(f"{it['episode_name']}${it['play_url']}") + else: + play_urls.append( + f"{it['episode_name']}${it['from_code']}|||{it['play_url']}|||{it['episode_id']}" + ) + return '#'.join(play_urls) + + def voides(self, item): + if item['name'] or item['title']: + voide = { + "vod_id": item.get('id') or item.get('click'), + 'vod_name': item.get('name') or item.get('title'), + 'vod_pic': item.get('cover') or item.get('image'), + 'vod_year': item.get('year') or item.get('label'), + 'vod_remarks': item.get('dynamic') or item.get('sub_title') + } + return voide + + def aes(self, text): + text = text.replace('-', '+').replace('_', '/') + '==' + key = b"e6d5de5fcc51f53d" + iv = b"2f13eef7dfc6c613" + cipher = AES.new(key, AES.MODE_CBC, iv) + pt = unpad(cipher.decrypt(b64decode(text)), AES.block_size).decode("utf-8") + return json.loads(pt) diff --git a/py/py_黑料.py b/py/py_黑料.py new file mode 100644 index 0000000..cbc379f --- /dev/null +++ b/py/py_黑料.py @@ -0,0 +1,270 @@ +# coding=utf-8 +# !/usr/bin/python +import sys +import requests +from bs4 import BeautifulSoup +import re +import base64 +from base.spider import Spider +import random + +sys.path.append('..') +xurl = "https://heiliaowang-44.buzz" +headerx = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36', + +} +class Spider(Spider): + global xurl + global headerx + + + def getName(self): + return "首页" + + def init(self, extend): + pass + + def destroy(self): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def homeContent(self, filter): + res = requests.get(xurl, headers=headerx) + res.encoding = "utf-8" + doc = BeautifulSoup(res.text, "html.parser") + sourcediv = doc.find('div', class_='nav') + vod = sourcediv.find_all('dd') + string_list = ["首页", "激情图漫", "激情小说", + "情色小说", "随机推荐", "顶级资源"] + + result = {} + result['class'] = [] + result['class'].append({'type_id': "/type/328", 'type_name': "国产视频"}) + result['class'].append({'type_id': "/type/329", 'type_name': "中文字幕"}) + result['class'].append({'type_id': "/type/331", 'type_name': "日本有码"}) + result['class'].append({'type_id': "/type/332", 'type_name': "日本无码"}) + result['class'].append({'type_id': "/type/333", 'type_name': "欧美无码"}) + result['class'].append({'type_id': "/type/334", 'type_name': "强奸乱轮"}) + result['class'].append({'type_id': "/type/335", 'type_name': "制服诱惑"}) + result['class'].append({'type_id': "/type/336", 'type_name': "直播主播"}) + result['class'].append({'type_id': "/type/338", 'type_name': "明星换脸"}) + result['class'].append({'type_id': "/type/339", 'type_name': "抖阴视频"}) + result['class'].append({'type_id': "/type/340", 'type_name': "女优明星"}) + result['class'].append({'type_id': "/type/343", 'type_name': "网爆门"}) + result['class'].append({'type_id': "/type/345", 'type_name': "伦理三级"}) + result['class'].append({'type_id': "/type/346", 'type_name': "AV解说"}) + result['class'].append({'type_id': "/type/347", 'type_name': "SM调教"}) + result['class'].append({'type_id': "/type/348", 'type_name': "萝莉少女"}) + result['class'].append({'type_id': "/type/349", 'type_name': "极品媚黑"}) + result['class'].append({'type_id': "/type/350", 'type_name': "女同性恋"}) + result['class'].append({'type_id': "/type/351", 'type_name': "玩偶姐姐"}) + result['class'].append({'type_id': "/type/353", 'type_name': "人妖系列"}) + result['class'].append({'type_id': "/type/373", 'type_name': "韩国主播"}) + result['class'].append({'type_id': "/type/378", 'type_name': "VR视角"}) + for item in vod: + name = item.find('a').text + if name in string_list: + continue + + id = item.find('a')['href'] + id = id.replace(".html", "") + result['class'].append({'type_id': id, 'type_name': name}) + + return result + def homeVideoContent(self): + videos = [] + try: + res = requests.get(xurl, headers=headerx) + res.encoding = "utf-8" + doc = BeautifulSoup(res.text, "html.parser") + sourcediv = doc.find_all('div', class_='pic') + for vod in sourcediv: + ul_elements = vod.find_all('ul') + for item in ul_elements: + name = item.select_one("li a")['title'] + pic = item.select_one("li a img")["data-src"] + remark = item.select_one("li a span").text + id = item.select_one("li a")['href'] + video = { + "vod_id": id, + "vod_name": name, + "vod_pic": pic, + "vod_remarks": remark + } + videos.append(video) + except: + pass + result = {'list': videos} + return result + + def categoryContent(self, cid, pg, filter, ext): + result = {} + videos = [] + if not pg: + pg = 1 + + url = xurl +cid + "/" + str(pg) + ".html" + detail = requests.get(url=url, headers=headerx) + detail.encoding = "utf-8" + doc = BeautifulSoup(detail.text, "html.parser") + sourcediv = doc.find_all('div', class_='pic') + for vod in sourcediv: + ul_elements = vod.find_all('ul') + for item in ul_elements: + name = item.select_one("li a")['title'] + pic = item.select_one("li a img")["src"] + remark = item.select_one("li a span").text + id = item.select_one("li a")['href'] + video = { + "vod_id": id, + "vod_name": name, + "vod_pic": pic, + "vod_remarks": remark + } + videos.append(video) + + result['list'] = videos + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + did = ids[0] + videos = [] + result = {} + res = requests.get(url=xurl + did, headers=headerx) + res.encoding = "utf-8" + doc = BeautifulSoup(res.text, "html.parser") + sourcediv = doc.find('div', style='padding-bottom: 10px;') + vod = sourcediv.find_all('a') + play_from = "" + play_url = "" + for item in vod: + play_from = play_from + item.text + "$$$" + play_url = play_url + item['href'] + "$$$" + while play_url[-1] == "#" or play_url[-1] == "$": + play_url = play_url[:-1] + + while play_from[-1] == "#" or play_from[-1] == "$": + play_from = play_from[:-1] + + source_match = re.search(r"<li>播放地址:<strong>(.*?)</strong></li>", res.text) + if source_match: + tx = source_match.group(1) + + videos.append({ + "vod_id": did, + "vod_name": tx, + "vod_pic": "", + "type_name": "ぃぅおか🍬 คิดถึง", + "vod_year": "", + "vod_area": "", + "vod_remarks": "", + "vod_actor": "", + "vod_director": "", + "vod_content": "", + "vod_play_from": play_from, + "vod_play_url": play_url + }) + + result['list'] = videos + return result + + def playerContent(self, flag, id, vipFlags): + result = {} + res = requests.get(url=xurl + id, headers=headerx) + res.encoding = "utf-8" + if '"rid"' in res.text: + decoded_str = '' + while not decoded_str: + source_match3 = re.search(r'"rid" : "(.*?)"', res.text) + if source_match3: + id = source_match3.group(1) + + data = "rid=" + id + header = { + "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36", + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' + } + res2 = requests.post(url="https://heiliaowang-44.buzz/fetchPlayUrl3", headers=header, data=data) + + source_match4 = re.search(r'"returnData"\s*:\s*"([^"]+)"', res2.text) + if source_match4: + decoded_str = source_match4.group(1) + + + else: + source_match = re.search(r"http:(.*?)\.m3u8", res.text) + decoded_str = "" + if source_match: + str3 = source_match.group(1) + if "aHR0c" in str3: + padding_needed = len(str3) % 4 + if padding_needed: + str3 += '=' * (4 - padding_needed) + decoded_str = base64.b64decode(str3).decode("utf-8") + if not decoded_str: + source_match2 = re.search(r"'(.*?)\.m3u8';", res.text) + if source_match2: + decoded_str = source_match2.group(1) + ".m3u8" + + result["parse"] = 0 + result["playUrl"] = '' + result["url"] = decoded_str + result["header"] = headerx + return result + + def searchContent(self, key, quick): + return self.searchContentPage(key, quick, '1') + + def searchContentPage(self, key, quick, page): + + result = {} + videos = [] + if not page: + page = 1 + + + url = xurl +"/search/"+ key +"/n/" + str(page)+".html" + detail = requests.get(url=url, headers=headerx) + detail.encoding = "utf-8" + doc = BeautifulSoup(detail.text, "html.parser") + sourcediv = doc.find_all('div', class_='pic') + for vod in sourcediv: + ul_elements = vod.find_all('ul') + for item in ul_elements: + name = item.select_one("li a")['title'] + pic = item.select_one("li a img")["src"] + remark = item.select_one("li a span").text + id = item.select_one("li a")['href'] + video = { + "vod_id": id, + "vod_name": name, + "vod_pic": pic, + "vod_remarks": remark + } + videos.append(video) + + result['list'] = videos + result['page'] = page + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def localProxy(self, params): + if params['type'] == "m3u8": + return self.proxyM3u8(params) + elif params['type'] == "media": + return self.proxyMedia(params) + elif params['type'] == "ts": + return self.proxyTs(params) + return None diff --git a/py/五五短剧.py b/py/五五短剧.py new file mode 100644 index 0000000..f2c4653 --- /dev/null +++ b/py/五五短剧.py @@ -0,0 +1,373 @@ +""" + +作者 凯悦宾馆 🚓 内容均从互联网收集而来 仅供交流学习使用 版权归原创者所有 如侵犯了您的权益 请通知作者 将及时删除侵权内容 + ====================kaiyuebinguan==================== + +""" + +import requests +from bs4 import BeautifulSoup +import re +from base.spider import Spider +import sys +import json +import base64 +import urllib.parse + +sys.path.append('..') + +xurl = "http://www.45b7.com" + +headerx1 = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36' + } + +headerx = { + 'User-Agent': 'Linux; Android 12; Pixel 3 XL) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.101 Mobile Safari/537.36' + } + +pm = '' + +class Spider(Spider): + global xurl + global headerx + + def getName(self): + return "首页" + + def init(self, extend): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def extract_middle_text(self, text, start_str, end_str, pl, start_index1: str = '', end_index2: str = ''): + if pl == 3: + plx = [] + while True: + start_index = text.find(start_str) + if start_index == -1: + break + end_index = text.find(end_str, start_index + len(start_str)) + if end_index == -1: + break + middle_text = text[start_index + len(start_str):end_index] + plx.append(middle_text) + text = text.replace(start_str + middle_text + end_str, '') + if len(plx) > 0: + purl = '' + for i in range(len(plx)): + matches = re.findall(start_index1, plx[i]) + output = "" + for match in matches: + match3 = re.search(r'(?:^|[^0-9])(\d+)(?:[^0-9]|$)', match[1]) + if match3: + number = match3.group(1) + else: + number = 0 + if 'http' not in match[0]: + output += f"#{'📽️拾光👉' + match[1]}${number}{xurl}{match[0]}" + else: + output += f"#{'📽️拾光👉' + match[1]}${number}{match[0]}" + output = output[1:] + purl = purl + output + "$$$" + purl = purl[:-3] + return purl + else: + return "" + else: + start_index = text.find(start_str) + if start_index == -1: + return "" + end_index = text.find(end_str, start_index + len(start_str)) + if end_index == -1: + return "" + + if pl == 0: + middle_text = text[start_index + len(start_str):end_index] + return middle_text.replace("\\", "") + + if pl == 1: + middle_text = text[start_index + len(start_str):end_index] + matches = re.findall(start_index1, middle_text) + if matches: + jg = ' '.join(matches) + return jg + + if pl == 2: + middle_text = text[start_index + len(start_str):end_index] + matches = re.findall(start_index1, middle_text) + if matches: + new_list = [f'✨拾光👉{item}' for item in matches] + jg = '$$$'.join(new_list) + return jg + + def homeContent(self, filter): + result = {} + result = {"class": [{"type_id": "都市", "type_name": "都市🌠"}, + {"type_id": "赘婿", "type_name": "赘婿🌠"}, + {"type_id": "战神", "type_name": "战神🌠"}, + {"type_id": "古代", "type_name": "古代🌠"}, + {"type_id": "现代", "type_name": "现代🌠"}, + {"type_id": "历史", "type_name": "历史🌠"}, + {"type_id": "脑洞", "type_name": "脑洞🌠"}, + {"type_id": "玄幻", "type_name": "玄幻🌠"}, + {"type_id": "搞笑", "type_name": "搞笑🌠"}, + {"type_id": "喜剧", "type_name": "喜剧🌠"}, + {"type_id": "萌宝", "type_name": "萌宝🌠"}, + {"type_id": "神豪", "type_name": "神豪🌠"}, + {"type_id": "致富", "type_name": "致富🌠"}, + {"type_id": "奇幻", "type_name": "奇幻🌠"}, + {"type_id": "超能", "type_name": "超能🌠"}, + {"type_id": "强者", "type_name": "强者🌠"}, + {"type_id": "甜宠", "type_name": "甜宠🌠"}, + {"type_id": "励志", "type_name": "励志🌠"}, + {"type_id": "豪门", "type_name": "豪门🌠"}, + {"type_id": "复仇", "type_name": "复仇🌠"}, + {"type_id": "长生", "type_name": "长生🌠"}, + {"type_id": "神医", "type_name": "神医🌠"}, + {"type_id": "马甲", "type_name": "马甲🌠"}, + {"type_id": "亲情", "type_name": "亲情🌠"}, + {"type_id": "人物", "type_name": "人物🌠"}, + {"type_id": "奇幻", "type_name": "奇幻🌠"}, + {"type_id": "无敌", "type_name": "无敌🌠"}, + {"type_id": "现实", "type_name": "现实🌠"}, + {"type_id": "重生", "type_name": "重生🌠"}, + {"type_id": "闪婚", "type_name": "闪婚🌠"}, + {"type_id": "职场", "type_name": "职场🌠"}, + {"type_id": "穿越", "type_name": "穿越🌠"}, + {"type_id": "年代", "type_name": "年代🌠"}, + {"type_id": "权谋", "type_name": "权谋🌠"}, + {"type_id": "高手", "type_name": "高手🌠"}, + {"type_id": "悬疑", "type_name": "悬疑🌠"}, + {"type_id": "情仇", "type_name": "情仇🌠"}, + {"type_id": "虐恋", "type_name": "虐恋🌠"}, + {"type_id": "古装", "type_name": "古装🌠"}, + {"type_id": "时空", "type_name": "时空🌠"}, + {"type_id": "玄幻", "type_name": "玄幻🌠"}, + {"type_id": "欢喜", "type_name": "欢喜🌠"}, + {"type_id": "觉醒", "type_name": "觉醒🌠"}, + {"type_id": "情感", "type_name": "情感🌠"}, + {"type_id": "逆袭", "type_name": "逆袭🌠"}, + {"type_id": "家庭", "type_name": "家庭🌠"}] + } + + return result + + def homeVideoContent(self): + videos = [] + try: + detail = requests.get(url=xurl, headers=headerx) + detail.encoding = "utf-8" + res = detail.text + doc = BeautifulSoup(res, "lxml") + + soups = doc.find_all('div', class_="FeaturedList_featuredBox") + + for soup in soups: + vods = soup.find_all('div', class_="FeaturedList_featuredItem") + + for vod in vods: + names = vod.find('a', class_="FeaturedList_bookName") + name = names.text.strip() + + ids = vod.find('a', class_="FeaturedList_bookName") + id = ids['href'] + + pics = vod.find('a', class_="image_imageBox") + pic = pics.find('img')['src'] + + if 'http' not in pic: + pic = xurl + pic + + remarks = vod.find('a', class_="FeaturedList_lastChapter") + remark = remarks.text.strip() + + video = { + "vod_id": id, + "vod_name": name, + "vod_pic": pic, + "vod_remarks": '拾光推荐📽️' + remark + } + videos.append(video) + + result = {'list': videos} + return result + except: + pass + + def categoryContent(self, cid, pg, filter, ext): + result = {} + if pg: + page = int(pg) + else: + page = 1 + page = int(pg) + videos = [] + + if page == '1': + url = f'{xurl}/vodshow/1---{cid}--------.html' + + else: + url = f'{xurl}/vodshow/1---{cid}-----{str(page)}---.html' + + try: + detail = requests.get(url=url, headers=headerx1) + detail.encoding = "utf-8" + res = detail.text + doc = BeautifulSoup(res, "lxml") + + soups = doc.find_all('div', class_="BrowseList_listBox") + + for soup in soups: + vods = soup.find_all('div', class_="BrowseList_itemBox") + + for vod in vods: + names = vod.find('a', class_="image_imageScaleBox") + name = names.find('img')['alt'] + + ids = vod.find('a', class_="image_imageScaleBox") + id = ids['href'] + + pics = vod.find('a', class_="image_imageScaleBox") + pic = pics.find('img')['src'] + + if 'http' not in pic: + pic = xurl + pic + + remarks = vod.find('a', class_="BrowseList_totalChapterNum") + remark = remarks.text.strip() + + video = { + "vod_id": id, + "vod_name": name, + "vod_pic": pic, + "vod_remarks": '集多推荐📽️' + remark + } + videos.append(video) + + except: + pass + result = {'list': videos} + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + global pm + did = ids[0] + result = {} + videos = [] + playurl = '' + if 'http' not in did: + did = xurl + did + res1 = requests.get(url=did, headers=headerx) + res1.encoding = "utf-8" + res = res1.text + + content = '😸拾光趣乐屋🎉为您介绍剧情📢本资源来源于网络🚓侵权请联系删除👉' + self.extract_middle_text(res,'name="description" content=','/>', 0) + content = content.replace('\r', '').replace('\n', '').replace(' ', '') + + bofang = self.extract_middle_text(res, '<div class="adm-swiper-item', '</div>', 3,'href="(.*?)">\s+(.*?)\s+</a>') + bofang = bofang.replace('$$$', '#') + + videos.append({ + "vod_id": did, + "vod_actor": '😸拾光', + "vod_director": '😸拾光', + "vod_content": content, + "vod_play_from": '😸拾光专线', + "vod_play_url": bofang + }) + + result['list'] = videos + return result + + def playerContent(self, flag, id, vipFlags): + parts = id.split("http") + xiutan = 0 + if xiutan == 0: + if len(parts) > 1: + before_https, after_https = parts[0], 'http' + parts[1] + res = requests.get(url=after_https, headers=headerx) + res = res.text + + url = self.extract_middle_text(res, '},"url":"', '"', 0).replace('\\', '') + + result = {} + result["parse"] = xiutan + result["playUrl"] = '' + result["url"] = url + result["header"] = headerx + return result + + def searchContentPage(self, key, quick, page): + result = {} + videos = [] + if not page: + page = '1' + if page == '1': + url = f'{xurl}/vodsearch/-------------.html?wd={key}' + + else: + url = f'{xurl}/vodsearch/{key}----------{str(page)}---.html' + + detail = requests.get(url=url, headers=headerx) + detail.encoding = "utf-8" + res = detail.text + doc = BeautifulSoup(res, "lxml") + + soups = doc.find_all('div', class_="MTagBookList_tagBookItem") + + for vod in soups: + names = vod.find('a', class_="MTagBookList_bookName") + name = names.text.strip() + + ids = vod.find('a', class_="MTagBookList_bookName") + id = ids['href'] + + pics = vod.find('a', class_="image_imageScaleBox") + pic = pics.find('img')['src'] + + if 'http' not in pic: + pic = xurl + pic + + remarks = vod.find('a', class_="image_imageScaleBox") + remark = remarks.find('img')['alt'] + + video = { + "vod_id": id, + "vod_name": name, + "vod_pic": pic, + "vod_remarks": '拾光推荐📽️' + remark + } + videos.append(video) + + result['list'] = videos + result['page'] = page + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def searchContent(self, key, quick): + return self.searchContentPage(key, quick, '1') + + def localProxy(self, params): + if params['type'] == "m3u8": + return self.proxyM3u8(params) + elif params['type'] == "media": + return self.proxyMedia(params) + elif params['type'] == "ts": + return self.proxyTs(params) + return None + + + + + diff --git a/py/光速.py b/py/光速.py new file mode 100644 index 0000000..005919c --- /dev/null +++ b/py/光速.py @@ -0,0 +1,193 @@ +# 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 b/py/奈飞影视.py new file mode 100644 index 0000000..d91d7f2 --- /dev/null +++ b/py/奈飞影视.py @@ -0,0 +1,377 @@ +""" + +作者 凯悦宾馆 🚓 内容均从互联网收集而来 仅供交流学习使用 版权归原创者所有 如侵犯了您的权益 请通知作者 将及时删除侵权内容 + ====================kaiyuebinguan==================== + +""" + +import requests +from bs4 import BeautifulSoup +import re +from base.spider import Spider +import sys +import json +import base64 +import urllib.parse + +sys.path.append('..') + +xurl = "https://www.netfly.tv" + +headerx = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' + 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36'} + +pm = '' + + +class Spider(Spider): + global xurl + global headerx + + def getName(self): + return "首页" + + def init(self, extend): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def extract_middle_text(self, text, start_str, end_str, pl, start_index1: str = '', end_index2: str = ''): + if pl == 3: + plx = [] + while True: + start_index = text.find(start_str) + if start_index == -1: + break + end_index = text.find(end_str, start_index + len(start_str)) + if end_index == -1: + break + middle_text = text[start_index + len(start_str):end_index] + plx.append(middle_text) + text = text.replace(start_str + middle_text + end_str, '') + if len(plx) > 0: + purl = '' + for i in range(len(plx)): + matches = re.findall(start_index1, plx[i]) + output = "" + for match in matches: + match3 = re.search(r'(?:^|[^0-9])(\d+)(?:[^0-9]|$)', match[1]) + if match3: + number = match3.group(1) + else: + number = 0 + if 'http' not in match[0]: + output += f"#{'📽️丢丢👉' + match[1]}${number}{xurl}{match[0]}" + else: + output += f"#{'📽️丢丢👉' + match[1]}${number}{match[0]}" + output = output[1:] + purl = purl + output + "$$$" + purl = purl[:-3] + return purl + else: + return "" + else: + start_index = text.find(start_str) + if start_index == -1: + return "" + end_index = text.find(end_str, start_index + len(start_str)) + if end_index == -1: + return "" + + if pl == 0: + middle_text = text[start_index + len(start_str):end_index] + return middle_text.replace("\\", "") + + if pl == 1: + middle_text = text[start_index + len(start_str):end_index] + matches = re.findall(start_index1, middle_text) + if matches: + jg = ' '.join(matches) + return jg + + if pl == 2: + middle_text = text[start_index + len(start_str):end_index] + matches = re.findall(start_index1, middle_text) + if matches: + new_list = [f'✨丢丢👉{item}' for item in matches] + jg = '$$$'.join(new_list) + return jg + + def homeContent(self, filter): + result = {} + result = {"class": [{"type_id": "/vod/show/1", "type_name": "丢丢电影🌠"}, + {"type_id": "/vod/show/2", "type_name": "丢丢剧集🌠"}, + {"type_id": "/vod/show/3", "type_name": "丢丢综艺🌠"}, + {"type_id": "/vod/show/4", "type_name": "丢丢动漫🌠"}, + {"type_id": "/vod/show/5", "type_name": "丢丢其他🌠"}], + + "list": [], + "filters": {"/vod/show/1": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": ""}, + {"n": "2024", "v": "2024"}, + {"n": "2023", "v": "2023"}, + {"n": "2022", "v": "2022"}, + {"n": "2021", "v": "2021"}, + {"n": "2020", "v": "2020"}, + {"n": "2019", "v": "2019"}, + {"n": "2018", "v": "2018"}]}], + "/vod/show/2": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": ""}, + {"n": "2024", "v": "2024"}, + {"n": "2023", "v": "2023"}, + {"n": "2022", "v": "2022"}, + {"n": "2021", "v": "2021"}, + {"n": "2020", "v": "2020"}, + {"n": "2019", "v": "2019"}, + {"n": "2018", "v": "2018"}]}], + "/vod/show/3": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": ""}, + {"n": "2024", "v": "2024"}, + {"n": "2023", "v": "2023"}, + {"n": "2022", "v": "2022"}, + {"n": "2021", "v": "2021"}, + {"n": "2020", "v": "2020"}, + {"n": "2019", "v": "2019"}, + {"n": "2018", "v": "2018"}]}], + "/vod/show/4": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": ""}, + {"n": "2024", "v": "2024"}, + {"n": "2023", "v": "2023"}, + {"n": "2022", "v": "2022"}, + {"n": "2021", "v": "2021"}, + {"n": "2020", "v": "2020"}, + {"n": "2019", "v": "2019"}, + {"n": "2018", "v": "2018"}]}], + "/vod/show/5": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": ""}, + {"n": "2024", "v": "2024"}, + {"n": "2023", "v": "2023"}, + {"n": "2022", "v": "2022"}, + {"n": "2021", "v": "2021"}, + {"n": "2020", "v": "2020"}, + {"n": "2019", "v": "2019"}, + {"n": "2018", "v": "2018"}]}]}} + + return result + + def homeVideoContent(self): + videos = [] + try: + detail = requests.get(url=xurl, headers=headerx) + detail.encoding = "utf-8" + res = detail.text + res = self.extract_middle_text(res, '<div class="module">', '专题片单', 0) + doc = BeautifulSoup(res, "lxml") + + soups = doc.find_all('div', class_="module-items") + + for soup in soups: + vods = soup.find_all('a') + + for vod in vods: + + name = vod['title'] + + id = vod['href'] + + pics = vod.find('div', class_="module-item-pic") + pic = pics.find('img')['data-original'] + + if 'http' not in pic: + pic = xurl + pic + + remark = self.extract_middle_text(str(vod), 'module-item-note">', '</div>', 0) + + video = { + "vod_id": id, + "vod_name": '丢丢📽️' + name, + "vod_pic": pic, + "vod_remarks": '丢丢▶️' + remark + } + videos.append(video) + + result = {'list': videos} + return result + except: + pass + + def categoryContent(self, cid, pg, filter, ext): + result = {} + if pg: + page = int(pg) + else: + page = 1 + page = int(pg) + videos = [] + + if '年代' in ext.keys(): + NdType = ext['年代'] + else: + NdType = '' + + if page == '1': + url = f'{xurl}{cid}-----------.html' + + else: + url = f'{xurl}{cid}--------{str(page)}---{NdType}.html' + + try: + detail = requests.get(url=url, headers=headerx) + detail.encoding = "utf-8" + res = detail.text + res = self.extract_middle_text(res, '按评分排序', '<div id="page">', 0) + doc = BeautifulSoup(res, "lxml") + + for soup in doc: + vods = soup.find_all('a') + + for vod in vods: + + name = vod['title'] + + id = vod['href'] + + pics = vod.find('div', class_="module-item-pic") + pic = pics.find('img')['data-original'] + + if 'http' not in pic: + pic = xurl + pic + + remark = self.extract_middle_text(str(vod), 'module-item-note">', '</div>', 0) + + video = { + "vod_id": id, + "vod_name": '丢丢📽️' + name, + "vod_pic": pic, + "vod_remarks": '丢丢▶️' + remark + } + videos.append(video) + + except: + pass + result = {'list': videos} + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + global pm + did = ids[0] + result = {} + videos = [] + playurl = '' + if 'http' not in did: + did = xurl + did + res1 = requests.get(url=did, headers=headerx) + res1.encoding = "utf-8" + res = res1.text + + content = '😸丢丢🎉为您介绍剧情📢本资源来源于网络🚓侵权请联系删除👉' + self.extract_middle_text(res,'20px;">','</p>', 0) + content = content.replace('\u3000', '').replace(' ', '').replace('<p>', '').replace('\n', '') + + xianlu = self.extract_middle_text(res, '<div class="module-tab-items-box hisSwiper"','<div class="shortcuts-mobile-overlay">',2, 'data-dropdown-value=".*?"><span>(.*?)</span>') + + bofang = self.extract_middle_text(res, '<div class="module-play-list-content', '</div>', 3,'href="(.*?)" title=".*?"><span>(.*?)</span>') + + videos.append({ + "vod_id": did, + "vod_actor": '😸皮皮 😸灰灰', + "vod_director": '😸丢丢', + "vod_content": content, + "vod_play_from": xianlu, + "vod_play_url": bofang + }) + + result['list'] = videos + return result + + def playerContent(self, flag, id, vipFlags): + parts = id.split("http") + xiutan = 0 + if xiutan == 0: + if len(parts) > 1: + before_https, after_https = parts[0], 'http' + parts[1] + res = requests.get(url=after_https, headers=headerx) + + url = self.extract_middle_text(res.text, '},"url":"', '"', 0).replace('\\', '') + + result = {} + result["parse"] = xiutan + result["playUrl"] = '' + result["url"] = url + result["header"] = headerx + return result + + + def searchContentPage(self, key, quick, page): + result = {} + videos = [] + if not page: + page = '1' + if page == '1': + url = f'{xurl}/vod/search/-------------.html?wd={key}' + + else: + url = f'{xurl}/vod/search/{key}----------{str(page)}---.html' + + detail = requests.get(url=url, headers=headerx) + detail.encoding = "utf-8" + res = detail.text + doc = BeautifulSoup(res, "lxml") + + soups = doc.find_all('div', class_="module-card-item") + + for vod in soups: + names = vod.find('div', class_="module-item-pic") + name = names.find('img')['alt'] + + ids = vod.find('div', class_="module-card-item-title") + id = ids.find('a')['href'] + + pics = vod.find('div', class_="module-item-pic") + pic = pics.find('img')['data-original'] + + if 'http' not in pic: + pic = xurl + pic + + remark = self.extract_middle_text(str(vod), 'module-item-note">', '</div>', 0) + + video = { + "vod_id": id, + "vod_name": '丢丢📽️' + name, + "vod_pic": pic, + "vod_remarks": '丢丢▶️' + remark + } + videos.append(video) + + result['list'] = videos + result['page'] = page + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def searchContent(self, key, quick): + return self.searchContentPage(key, quick, '1') + + def localProxy(self, params): + if params['type'] == "m3u8": + return self.proxyM3u8(params) + elif params['type'] == "media": + return self.proxyMedia(params) + elif params['type'] == "ts": + return self.proxyTs(params) + return None + + diff --git a/py/新视觉.py b/py/新视觉.py new file mode 100644 index 0000000..168a0b9 --- /dev/null +++ b/py/新视觉.py @@ -0,0 +1,238 @@ +# coding=utf-8 +# !/usr/bin/python +# by嗷呜 +import sys +sys.path.append("..") +import re +import os +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.fetch(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 b/py/映播.py new file mode 100644 index 0000000..eda2481 --- /dev/null +++ b/py/映播.py @@ -0,0 +1,411 @@ +""" + +作者 凯悦推荐 🚓 内容均从互联网收集而来 仅供交流学习使用 版权归原创者所有 如侵犯了您的权益 请通知作者 将及时删除侵权内容 + ====================kaiyuebinguan==================== + +""" + +from Crypto.Util.Padding import unpad +from urllib.parse import unquote +from Crypto.Cipher import ARC4 +from base.spider import Spider +from bs4 import BeautifulSoup +import urllib.request +import urllib.parse +import binascii +import requests +import base64 +import json +import time +import sys +import re +import os + +sys.path.append('..') + +xurl = "https://www.ingbo.tv" + +headerx = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36' + } + +pm = '' + +class Spider(Spider): + global xurl + global headerx + + def getName(self): + return "首页" + + def init(self, extend): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def extract_middle_text(self, text, start_str, end_str, pl, start_index1: str = '', end_index2: str = ''): + if pl == 3: + plx = [] + while True: + start_index = text.find(start_str) + if start_index == -1: + break + end_index = text.find(end_str, start_index + len(start_str)) + if end_index == -1: + break + middle_text = text[start_index + len(start_str):end_index] + plx.append(middle_text) + text = text.replace(start_str + middle_text + end_str, '') + if len(plx) > 0: + purl = '' + for i in range(len(plx)): + matches = re.findall(start_index1, plx[i]) + output = "" + for match in matches: + match3 = re.search(r'(?:^|[^0-9])(\d+)(?:[^0-9]|$)', match[1]) + if match3: + number = match3.group(1) + else: + number = 0 + if 'http' not in match[0]: + output += f"#{'📽️集多👉' + match[1]}${number}{xurl}{match[0]}" + else: + output += f"#{'📽️集多👉' + match[1]}${number}{match[0]}" + output = output[1:] + purl = purl + output + "$$$" + purl = purl[:-3] + return purl + else: + return "" + else: + start_index = text.find(start_str) + if start_index == -1: + return "" + end_index = text.find(end_str, start_index + len(start_str)) + if end_index == -1: + return "" + + if pl == 0: + middle_text = text[start_index + len(start_str):end_index] + return middle_text.replace("\\", "") + + if pl == 1: + middle_text = text[start_index + len(start_str):end_index] + matches = re.findall(start_index1, middle_text) + if matches: + jg = ' '.join(matches) + return jg + + if pl == 2: + middle_text = text[start_index + len(start_str):end_index] + matches = re.findall(start_index1, middle_text) + if matches: + new_list = [f'✨集多👉{item}' for item in matches] + jg = '$$$'.join(new_list) + return jg + + def homeContent(self, filter): + result = {} + result = {"class": [{"type_id": "1", "type_name": "集多电影🌠"}, + {"type_id": "2", "type_name": "集多剧集🌠"}, + {"type_id": "4", "type_name": "集多动漫🌠"}, + {"type_id": "40", "type_name": "集多短剧🌠"}, + {"type_id": "3", "type_name": "集多综艺🌠"}], + + "list": [], + "filters": {"1": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": "0"}, + {"n": "2024", "v": "3"}, + {"n": "2023", "v": "4"}, + {"n": "2022", "v": "5"}, + {"n": "2021", "v": "6"}, + {"n": "2020", "v": "7"}, + {"n": "2019", "v": "8"}, + {"n": "2018", "v": "9"}]}], + "2": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": "0"}, + {"n": "2024", "v": "3"}, + {"n": "2023", "v": "4"}, + {"n": "2022", "v": "5"}, + {"n": "2021", "v": "6"}, + {"n": "2020", "v": "7"}, + {"n": "2019", "v": "8"}, + {"n": "2018", "v": "9"}]}], + "3": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": "0"}, + {"n": "2024", "v": "3"}, + {"n": "2023", "v": "4"}, + {"n": "2022", "v": "5"}, + {"n": "2021", "v": "6"}, + {"n": "2020", "v": "7"}, + {"n": "2019", "v": "8"}, + {"n": "2018", "v": "9"}]}], + "40": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": "0"}, + {"n": "2024", "v": "3"}, + {"n": "2023", "v": "4"}, + {"n": "2022", "v": "5"}, + {"n": "2021", "v": "6"}, + {"n": "2020", "v": "7"}, + {"n": "2019", "v": "8"}, + {"n": "2018", "v": "9"}]}], + "4": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": "0"}, + {"n": "2024", "v": "3"}, + {"n": "2023", "v": "4"}, + {"n": "2022", "v": "5"}, + {"n": "2021", "v": "6"}, + {"n": "2020", "v": "7"}, + {"n": "2019", "v": "8"}, + {"n": "2018", "v": "9"}]}]}} + + return result + + def homeVideoContent(self): + videos = [] + + try: + + detail = requests.get(url=xurl, headers=headerx) + detail.encoding = "utf-8" + res = detail.text + res = self.extract_middle_text(res, '<span>热门电影</span>', '<span>推荐明星</span>', 0) + doc = BeautifulSoup(res, "lxml") + + soups = doc.find_all('div', class_="swiper-wrapper cms-list-swiper") + + for soup in soups: + vods = soup.find_all('div', class_="public-list-box") + + for vod in vods: + names = vod.find('div', class_="public-list-div") + name = names.find('a')['title'] + + ids = vod.find('div', class_="public-list-div") + id = ids.find('a')['href'] + + pics = vod.find('a', class_="public-list-exp") + pic = pics.find('img')['src'] + + if 'http' not in pic: + pic = xurl + pic + + remark = self.extract_middle_text(str(vod), 'hide ft2">', '</span>', 0) + remark = remark.replace('\n', '').replace(' ', '') + + video = { + "vod_id": id, + "vod_name": name, + "vod_pic": pic, + "vod_remarks": '集多▶️' + remark + } + videos.append(video) + + result = {'list': videos} + return result + except: + pass + + def categoryContent(self, cid, pg, filter, ext): + result = {} + videos = [] + + if pg: + page = int(pg) + else: + page = 1 + + if '年代' in ext.keys(): + NdType = ext['年代'] + else: + NdType = '' + + if page == '1': + url = f'{xurl}/vod/1/{cid}/0/0/0/0/0/0' + + else: + url = f'{xurl}/vod/list/{str(page)}/{cid}/0/{NdType}/0/0/0/0' + + try: + detail = requests.get(url=url, headers=headerx) + detail.encoding = "utf-8" + res = detail.text + doc = BeautifulSoup(res, "lxml") + + soups = doc.find_all('div', class_="public-pic-b") + + for vod in soups: + names = vod.find('a', class_="public-list-exp") + name = names.find('img')['alt'] + + ids = vod.find('div', class_="public-list-div") + id = ids.find('a')['href'] + + pics = vod.find('a', class_="public-list-exp") + pic = pics.find('img')['data-original'] + + if 'http' not in pic: + pic = xurl + pic + + remark = self.extract_middle_text(str(vod), 'hide ft2">', '</span>', 0) + remark = remark.replace('\n', '').replace(' ', '') + + video = { + "vod_id": id, + "vod_name": name, + "vod_pic": pic, + "vod_remarks": '集多▶️' + remark + } + videos.append(video) + + except: + pass + result = {'list': videos} + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + global pm + did = ids[0] + result = {} + videos = [] + + if 'http' not in did: + did = xurl + did + + res1 = requests.get(url=did, headers=headerx) + res1.encoding = "utf-8" + res = res1.text + + url = 'https://fs-im-kefu.7moor-fs1.com/ly/4d2c3f00-7d4c-11e5-af15-41bf63ae4ea0/1732707176882/jiduo.txt' + response = requests.get(url) + response.encoding = 'utf-8' + code = response.text + name = self.extract_middle_text(code, "s1='", "'", 0) + Jumps = self.extract_middle_text(code, "s2='", "'", 0) + + content = '😸集多🎉为您介绍剧情📢本资源来源于网络🚓侵权请联系删除👉' + self.extract_middle_text(res,'描述:</strong>','</div>', 0) + content = content.replace('\n', '').replace(' ', '') + + if name not in content: + bofang = Jumps + else: + bofang = self.extract_middle_text(res, '<ul class="anthology-list-play size">', '</ul>', 3,'href="(.*?)" class="hide">\s+(.*?)\s+</a>') + + xianlu = self.extract_middle_text(res, '<div class="title-tab flex switch-button">','</div>',2, 'href=".*?" title=".*?">(.*?)</a>') + + videos.append({ + "vod_id": did, + "vod_actor": '集多和他的朋友们', + "vod_director": '集多', + "vod_content": content, + "vod_play_from": xianlu, + "vod_play_url": bofang + }) + + result['list'] = videos + return result + + def playerContent(self, flag, id, vipFlags): + parts = id.split("http") + + xiutan = 0 + + if xiutan == 0: + if len(parts) > 1: + before_https, after_https = parts[0], 'http' + parts[1] + + if '/tp/jd.m3u8' in after_https: + url = after_https + else: + res = requests.get(url=after_https, headers=headerx) + res = res.text + + url = self.extract_middle_text(res, 'u0026url=', "'", 0).replace('\\', '') + + result = {} + result["parse"] = xiutan + result["playUrl"] = '' + result["url"] = url + result["header"] = headerx + return result + + def searchContentPage(self, key, quick, page): + result = {} + videos = [] + + if not page: + page = '1' + if page == '1': + url = f'{xurl}/public/auto/search1.html?keyword={key}' + + else: + url = f'{xurl}/public/auto/search1.html?keyword={key}&page={str(page)}' + + detail = requests.get(url=url, headers=headerx) + detail.encoding = "utf-8" + res = detail.text + doc = BeautifulSoup(res, "lxml") + + soups = doc.find_all('div', class_="public-list-box") + + for vod in soups: + names = vod.find('a', class_="public-list-exp") + name = names.find('img')['alt'] + + ids = vod.find('div', class_="public-list-div") + id = ids.find('a')['href'] + + pics = vod.find('a', class_="public-list-exp") + pic = pics.find('img')['data-original'] + + if 'http' not in pic: + pic = xurl + pic + + remark = self.extract_middle_text(str(vod), 'hide ft2">', '</span>', 0) + remark = remark.replace('\n', '').replace(' ', '') + + video = { + "vod_id": id, + "vod_name": name, + "vod_pic": pic, + "vod_remarks": '集多▶️' + remark + } + videos.append(video) + + result['list'] = videos + result['page'] = page + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def searchContent(self, key, quick): + return self.searchContentPage(key, quick, '1') + + def localProxy(self, params): + if params['type'] == "m3u8": + return self.proxyM3u8(params) + elif params['type'] == "media": + return self.proxyMedia(params) + elif params['type'] == "ts": + return self.proxyTs(params) + return None + + + + + + diff --git a/py/策驰影院.py b/py/策驰影院.py new file mode 100644 index 0000000..db08806 --- /dev/null +++ b/py/策驰影院.py @@ -0,0 +1,377 @@ +""" + +作者 凯悦宾馆 🚓 内容均从互联网收集而来 仅供交流学习使用 版权归原创者所有 如侵犯了您的权益 请通知作者 将及时删除侵权内容 + ====================kaiyuebinguan==================== + +""" + +from Crypto.Util.Padding import unpad +from urllib.parse import unquote +from Crypto.Cipher import ARC4 +from base.spider import Spider +from bs4 import BeautifulSoup +import urllib.request +import urllib.parse +import binascii +import requests +import base64 +import json +import time +import sys +import re +import os + +sys.path.append('..') + +xurl = "https://www.ccy1.com" + +headerx = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36' + } + +pm = '' + +class Spider(Spider): + global xurl + global headerx + + def getName(self): + return "首页" + + def init(self, extend): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def extract_middle_text(self, text, start_str, end_str, pl, start_index1: str = '', end_index2: str = ''): + if pl == 3: + plx = [] + while True: + start_index = text.find(start_str) + if start_index == -1: + break + end_index = text.find(end_str, start_index + len(start_str)) + if end_index == -1: + break + middle_text = text[start_index + len(start_str):end_index] + plx.append(middle_text) + text = text.replace(start_str + middle_text + end_str, '') + if len(plx) > 0: + purl = '' + for i in range(len(plx)): + matches = re.findall(start_index1, plx[i]) + output = "" + for match in matches: + match3 = re.search(r'(?:^|[^0-9])(\d+)(?:[^0-9]|$)', match[1]) + if match3: + number = match3.group(1) + else: + number = 0 + if 'http' not in match[0]: + output += f"#{'📽️丢丢👉' + match[1]}${number}{xurl}{match[0]}" + else: + output += f"#{'📽️丢丢👉' + match[1]}${number}{match[0]}" + output = output[1:] + purl = purl + output + "$$$" + purl = purl[:-3] + return purl + else: + return "" + else: + start_index = text.find(start_str) + if start_index == -1: + return "" + end_index = text.find(end_str, start_index + len(start_str)) + if end_index == -1: + return "" + + if pl == 0: + middle_text = text[start_index + len(start_str):end_index] + return middle_text.replace("\\", "") + + if pl == 1: + middle_text = text[start_index + len(start_str):end_index] + matches = re.findall(start_index1, middle_text) + if matches: + jg = ' '.join(matches) + return jg + + if pl == 2: + middle_text = text[start_index + len(start_str):end_index] + matches = re.findall(start_index1, middle_text) + if matches: + new_list = [f'✨丢丢👉{item}' for item in matches] + jg = '$$$'.join(new_list) + return jg + + def homeContent(self, filter): + result = {} + result = {"class": [{"type_id": "77", "type_name": "丢丢电影🌠"}, + {"type_id": "78", "type_name": "丢丢剧集🌠"}, + {"type_id": "80", "type_name": "丢丢动漫🌠"}, + {"type_id": "164", "type_name": "丢丢4K🌠"}, + {"type_id": "79", "type_name": "丢丢综艺🌠"}, + {"type_id": "166", "type_name": "丢丢体育🌠"}, + {"type_id": "170", "type_name": "丢丢演唱会🌠"}, + {"type_id": "165", "type_name": "丢丢短剧🌠"}], + + "list": [], + "filters": {"77": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": "0"}, + {"n": "80年代", "v": "42"}, + {"n": "90年代", "v": "43"}, + {"n": "00年代", "v": "44"}, + {"n": "10年代", "v": "45"}, + {"n": "20年代", "v": "46"}]}], + "78": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": "0"}, + {"n": "80年代", "v": "42"}, + {"n": "90年代", "v": "43"}, + {"n": "00年代", "v": "44"}, + {"n": "10年代", "v": "45"}, + {"n": "20年代", "v": "46"}]}], + "80": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": "0"}, + {"n": "80年代", "v": "42"}, + {"n": "90年代", "v": "43"}, + {"n": "00年代", "v": "44"}, + {"n": "10年代", "v": "45"}, + {"n": "20年代", "v": "46"}]}], + "164": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": "0"}, + {"n": "80年代", "v": "42"}, + {"n": "90年代", "v": "43"}, + {"n": "00年代", "v": "44"}, + {"n": "10年代", "v": "45"}, + {"n": "20年代", "v": "46"}]}], + "79": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": "0"}, + {"n": "80年代", "v": "42"}, + {"n": "90年代", "v": "43"}, + {"n": "00年代", "v": "44"}, + {"n": "10年代", "v": "45"}, + {"n": "20年代", "v": "46"}]}], + "166": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": "0"}, + {"n": "80年代", "v": "42"}, + {"n": "90年代", "v": "43"}, + {"n": "00年代", "v": "44"}, + {"n": "10年代", "v": "45"}, + {"n": "20年代", "v": "46"}]}], + "170": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": "0"}, + {"n": "80年代", "v": "42"}, + {"n": "90年代", "v": "43"}, + {"n": "00年代", "v": "44"}, + {"n": "10年代", "v": "45"}, + {"n": "20年代", "v": "46"}]}], + "165": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": "0"}, + {"n": "80年代", "v": "42"}, + {"n": "90年代", "v": "43"}, + {"n": "00年代", "v": "44"}, + {"n": "10年代", "v": "45"}, + {"n": "20年代", "v": "46"}]}]}} + + return result + + def homeVideoContent(self): + videos = [] + + try: + detail = requests.get(url=xurl, headers=headerx) + detail.encoding = "utf-8" + res = detail.text + + doc = BeautifulSoup(res, "lxml") + + soups = doc.find_all('div', class_="hide-b-20") + + if soups and len(soups) > 1: + soups = soups[1] + vods = soups.find_all('div', class_="public-list-box") + + for vod in vods: + names = vod.find('div', class_="public-list-div") + name = names.find('a')['title'] + + id = names.find('a')['href'] + + pic = vod.find('img')['data-src'] + + if 'http' not in pic: + pic = xurl + pic + + remarks = vod.find('span', class_="public-list-prb hide") + remark = remarks.text.strip() + + video = { + "vod_id": id, + "vod_name": '丢丢📽️' + name, + "vod_pic": pic, + "vod_remarks": '丢丢▶️' + remark + } + videos.append(video) + + result = {'list': videos} + return result + except: + pass + + def categoryContent(self, cid, pg, filter, ext): + result = {} + videos = [] + + if pg: + page = int(pg) + else: + page = 1 + + if '年代' in ext.keys(): + NdType = ext['年代'] + else: + NdType = '' + + if page == '1': + url = f'{xurl}/vod/list/1/{cid}/0/0/0/0/0/0' + + else: + url = f'{xurl}/vod/list/{str(page)}/{cid}/0/{NdType}/0/0/0/0' + + try: + detail = requests.get(url=url, headers=headerx) + detail.encoding = "utf-8" + res = detail.text + doc = BeautifulSoup(res, "lxml") + + soups = doc.find_all('div', class_="border-box") + + for soup in soups: + vods = soup.find_all('div', class_="public-list-box") + + for vod in vods: + + name = vod.find('img')['alt'] + + ids = vod.find('a', class_="public-list-exp") + id = ids['href'] + + pic = vod.find('img')['src'] + + if 'http' not in pic: + pic = xurl + pic + + remarks = vod.find('span', class_="public-list-prb") + remark = remarks.text.strip() + + video = { + "vod_id": id, + "vod_name": '丢丢📽️' + name, + "vod_pic": pic, + "vod_remarks": '丢丢▶️' + remark + } + videos.append(video) + + except: + pass + result = {'list': videos} + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + global pm + did = ids[0] + result = {} + videos = [] + + if 'http' not in did: + did = xurl + did + + res1 = requests.get(url=did, headers=headerx) + res1.encoding = "utf-8" + res = res1.text + + url = 'https://fs-im-kefu.7moor-fs1.com/ly/4d2c3f00-7d4c-11e5-af15-41bf63ae4ea0/1732697392729/didiu.txt' + response = requests.get(url) + response.encoding = 'utf-8' + code = response.text + name = self.extract_middle_text(code, "s1='", "'", 0) + Jumps = self.extract_middle_text(code, "s2='", "'", 0) + + content = '😸丢丢🎉为您介绍剧情📢本资源来源于网络🚓侵权请联系删除👉' + self.extract_middle_text(res,'class="text cor3" >','</div>', 0) + + if name not in content: + bofang = Jumps + else: + bofang = self.extract_middle_text(res, '<ul class="anthology-list-play', '</ul>', 3, 'href="(.*?)" class="hide" style="width:100px">\s+(.*?)\s+</a>') + + xianlu = self.extract_middle_text(res, '<div class="swiper-wrapper"','</div>',2, '</i> (.*?)</a>') + + videos.append({ + "vod_id": did, + "vod_actor": '😸皮皮 😸灰灰', + "vod_director": '😸丢丢', + "vod_content": content, + "vod_play_from": xianlu, + "vod_play_url": bofang + }) + + result['list'] = videos + return result + + def playerContent(self, flag, id, vipFlags): + parts = id.split("http") + + xiutan = 0 + + if xiutan == 0: + if len(parts) > 1: + before_https, after_https = parts[0], 'http' + parts[1] + + if '239755956819.mp4' in after_https: + url = after_https + else: + res = requests.get(url=after_https, headers=headerx) + res = res.text + + url = self.extract_middle_text(res, '?url=', "'", 0).replace('\\', '') + + result = {} + result["parse"] = xiutan + result["playUrl"] = '' + result["url"] = url + result["header"] = headerx + return result + + def searchContent(self, key, quick): + return self.searchContentPage(key, quick, '1') + + def localProxy(self, params): + if params['type'] == "m3u8": + return self.proxyM3u8(params) + elif params['type'] == "media": + return self.proxyMedia(params) + elif params['type'] == "ts": + return self.proxyTs(params) + return None + + + + + + diff --git a/py/素白白.py b/py/素白白.py new file mode 100644 index 0000000..815592b --- /dev/null +++ b/py/素白白.py @@ -0,0 +1,356 @@ +""" + +作者 凯悦推荐 🚓 内容均从互联网收集而来 仅供交流学习使用 版权归原创者所有 如侵犯了您的权益 请通知作者 将及时删除侵权内容 + ====================kaiyuebinguan==================== + +""" + +from Crypto.Util.Padding import unpad +from urllib.parse import unquote +from Crypto.Cipher import ARC4 +from base.spider import Spider +from bs4 import BeautifulSoup +import urllib.request +import urllib.parse +import binascii +import requests +import base64 +import json +import time +import sys +import re +import os + +sys.path.append('..') + +xurl = "https://www.subaibai.com" + +headerx = { + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36', + 'authority': 'www.subaibai.com', + 'Referer': 'https://www.subaibai.com/', + 'Origin': 'https://www.subaibai.com/' + } + +pm = '' + +class Spider(Spider): + global xurl + global headerx + + def getName(self): + return "首页" + + def init(self, extend): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def extract_middle_text(self, text, start_str, end_str, pl, start_index1: str = '', end_index2: str = ''): + if pl == 3: + plx = [] + while True: + start_index = text.find(start_str) + if start_index == -1: + break + end_index = text.find(end_str, start_index + len(start_str)) + if end_index == -1: + break + middle_text = text[start_index + len(start_str):end_index] + plx.append(middle_text) + text = text.replace(start_str + middle_text + end_str, '') + if len(plx) > 0: + purl = '' + for i in range(len(plx)): + matches = re.findall(start_index1, plx[i]) + output = "" + for match in matches: + match3 = re.search(r'(?:^|[^0-9])(\d+)(?:[^0-9]|$)', match[1]) + if match3: + number = match3.group(1) + else: + number = 0 + if 'http' not in match[0]: + output += f"#{'📽️集多👉' + match[1]}${number}{xurl}{match[0]}" + else: + output += f"#{'📽️集多👉' + match[1]}${number}{match[0]}" + output = output[1:] + purl = purl + output + "$$$" + purl = purl[:-3] + return purl + else: + return "" + else: + start_index = text.find(start_str) + if start_index == -1: + return "" + end_index = text.find(end_str, start_index + len(start_str)) + if end_index == -1: + return "" + + if pl == 0: + middle_text = text[start_index + len(start_str):end_index] + return middle_text.replace("\\", "") + + if pl == 1: + middle_text = text[start_index + len(start_str):end_index] + matches = re.findall(start_index1, middle_text) + if matches: + jg = ' '.join(matches) + return jg + + if pl == 2: + middle_text = text[start_index + len(start_str):end_index] + matches = re.findall(start_index1, middle_text) + if matches: + new_list = [f'✨集多👉{item}' for item in matches] + jg = '$$$'.join(new_list) + return jg + + def homeContent(self, filter): + result = {} + result = {"class": [{"type_id": "new-movie", "type_name": "集多电影🌠"}, + {"type_id": "tv-drama", "type_name": "集多剧集🌠"}, + {"type_id": "hot-month", "type_name": "集多热门电影🌠"}, + {"type_id": "high-movie", "type_name": "集多高分电影🌠"}, + {"type_id": "cartoon-movie", "type_name": "集多动漫电影🌠"}, + {"type_id": "hongkong-movie", "type_name": "集多香港经典🌠"}, + {"type_id": "domestic-drama", "type_name": "集多国产剧🌠"}, + {"type_id": "american-drama", "type_name": "集多欧美剧🌠"}, + {"type_id": "korean-drama", "type_name": "集多韩剧🌠"}, + {"type_id": "anime-drama", "type_name": "集多动漫剧🌠"}] + } + + return result + + def homeVideoContent(self): + videos = [] + + try: + + detail = requests.get(url=xurl, headers=headerx) + detail.encoding = "utf-8" + res = detail.text + + doc = BeautifulSoup(res, "lxml") + + soups = doc.find_all('div', class_="bt_img") + + for soup in soups: + vods = soup.find_all('li') + + for vod in vods: + + name = vod.find('img')['alt'] + + ids = vod.find('h3', class_="dytit") + id = ids.find('a')['href'] + id = id.replace('www.subaibaiys.com', 'www.subaibai.com') + + pic = vod.find('img')['data-original'] + + if 'http' not in pic: + pic = xurl + pic + + remark = self.extract_middle_text(str(vod), 'class="rating">', '</div>', 0) + + video = { + "vod_id": id, + "vod_name": name, + "vod_pic": pic, + "vod_remarks": '集多▶️' + remark + } + videos.append(video) + + result = {'list': videos} + return result + except: + pass + + def categoryContent(self, cid, pg, filter, ext): + result = {} + videos = [] + + if pg: + page = int(pg) + else: + page = 1 + + if page == '1': + url = f'{xurl}/{cid}' + + else: + url = f'{xurl}/{cid}/page/{str(page)}' + + try: + detail = requests.get(url=url, headers=headerx) + detail.encoding = "utf-8" + res = detail.text + doc = BeautifulSoup(res, "lxml") + + soups = doc.find_all('div', class_="bt_img") + + for soup in soups: + vods = soup.find_all('li') + + for vod in vods: + + name = vod.find('img')['alt'] + + ids = vod.find('h3', class_="dytit") + id = ids.find('a')['href'] + id = id.replace('www.subaibaiys.com', 'www.subaibai.com') + + pic = vod.find('img')['data-original'] + + if 'http' not in pic: + pic = xurl + pic + + remark = self.extract_middle_text(str(vod), 'class="rating">', '</div>', 0) + + video = { + "vod_id": id, + "vod_name": name, + "vod_pic": pic, + "vod_remarks": '集多▶️' + remark + } + videos.append(video) + + except: + pass + result = {'list': videos} + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + global pm + did = ids[0] + result = {} + videos = [] + + if 'http' not in did: + did = xurl + did + + res1 = requests.get(url=did, headers=headerx) + res1.encoding = "utf-8" + res = res1.text + + url = 'https://fs-im-kefu.7moor-fs1.com/ly/4d2c3f00-7d4c-11e5-af15-41bf63ae4ea0/1732707176882/jiduo.txt' + response = requests.get(url) + response.encoding = 'utf-8' + code = response.text + name = self.extract_middle_text(code, "s1='", "'", 0) + Jumps = self.extract_middle_text(code, "s2='", "'", 0) + + content = '集多🎉为您介绍剧情📢本资源来源于网络🚓侵权请联系删除👉' + self.extract_middle_text(res,'<div class="yp_context">','</p>', 0) + content = content.replace('\t', '').replace('<p>', '').replace(' ', '').replace('\n', '') + + if name not in content: + bofang = Jumps + else: + bofang = self.extract_middle_text(res, '<div class="paly_list_btn">', '</div>', 3, 'href="(.*?)">(.*?)</a>') + bofang = bofang.replace('www.subaibaiys.com', 'www.subaibai.com').replace('立即播放  ', '') + + videos.append({ + "vod_id": did, + "vod_actor": '集多和他的朋友们', + "vod_director": '集多', + "vod_content": content, + "vod_play_from": '集多专线', + "vod_play_url": bofang + }) + + result['list'] = videos + return result + + def playerContent(self, flag, id, vipFlags): + parts = id.split("http") + + xiutan = 1 + + if xiutan == 1: + if len(parts) > 1: + before_https, after_https = parts[0], 'http' + parts[1] + result = {} + result["parse"] = xiutan + result["playUrl"] = '' + result["url"] = after_https + result["header"] = headerx + return result + + def searchContentPage(self, key, quick, page): + result = {} + videos = [] + + if not page: + page = '1' + if page == '1': + url = f'{xurl}/?s={key}' + + else: + url = f'{xurl}//page/{str(page)}?s={key}' + + detail = requests.get(url=url, headers=headerx) + detail.encoding = "utf-8" + res = detail.text + doc = BeautifulSoup(res, "lxml") + + soups = doc.find_all('div', class_="bt_img") + + for soup in soups: + vods = soup.find_all('li') + + for vod in vods: + + name = vod.find('img')['alt'] + + ids = vod.find('h3', class_="dytit") + id = ids.find('a')['href'] + id = id.replace('www.subaibaiys.com', 'www.subaibai.com') + + pic = vod.find('img')['data-original'] + + if 'http' not in pic: + pic = xurl + pic + + remark = self.extract_middle_text(str(vod), 'class="rating">', '</div>', 0) + + video = { + "vod_id": id, + "vod_name": name, + "vod_pic": pic, + "vod_remarks": '集多▶️' + remark + } + videos.append(video) + + result['list'] = videos + result['page'] = page + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def searchContent(self, key, quick): + return self.searchContentPage(key, quick, '1') + + def localProxy(self, params): + if params['type'] == "m3u8": + return self.proxyM3u8(params) + elif params['type'] == "media": + return self.proxyMedia(params) + elif params['type'] == "ts": + return self.proxyTs(params) + return None + + + + + + diff --git a/py/荐片.py b/py/荐片.py new file mode 100644 index 0000000..b69f85b --- /dev/null +++ b/py/荐片.py @@ -0,0 +1,368 @@ +""" + +作者 凯悦宾馆 🚓 内容均从互联网收集而来 仅供交流学习使用 版权归原创者所有 如侵犯了您的权益 请通知作者 将及时删除侵权内容 + ====================kaiyuebinguan==================== + +""" + +from Crypto.Util.Padding import unpad +from urllib.parse import unquote +from Crypto.Cipher import ARC4 +from base.spider import Spider +from bs4 import BeautifulSoup +import urllib.request +import urllib.parse +import binascii +import requests +import base64 +import json +import time +import sys +import re +import os + +sys.path.append('..') + +xurl = "http://img.shifen.me" + +headerx = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36' + } + +pm = '' + +class Spider(Spider): + global xurl + global headerx + + def getName(self): + return "首页" + + def init(self, extend): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def extract_middle_text(self, text, start_str, end_str, pl, start_index1: str = '', end_index2: str = ''): + if pl == 3: + plx = [] + while True: + start_index = text.find(start_str) + if start_index == -1: + break + end_index = text.find(end_str, start_index + len(start_str)) + if end_index == -1: + break + middle_text = text[start_index + len(start_str):end_index] + plx.append(middle_text) + text = text.replace(start_str + middle_text + end_str, '') + if len(plx) > 0: + purl = '' + for i in range(len(plx)): + matches = re.findall(start_index1, plx[i]) + output = "" + for match in matches: + match3 = re.search(r'(?:^|[^0-9])(\d+)(?:[^0-9]|$)', match[1]) + if match3: + number = match3.group(1) + else: + number = 0 + if 'http' not in match[0]: + output += f"#{'📽️丢丢👉' + match[1]}${number}{xurl}{match[0]}" + else: + output += f"#{'📽️丢丢👉' + match[1]}${number}{match[0]}" + output = output[1:] + purl = purl + output + "$$$" + purl = purl[:-3] + return purl + else: + return "" + else: + start_index = text.find(start_str) + if start_index == -1: + return "" + end_index = text.find(end_str, start_index + len(start_str)) + if end_index == -1: + return "" + + if pl == 0: + middle_text = text[start_index + len(start_str):end_index] + return middle_text.replace("\\", "") + + if pl == 1: + middle_text = text[start_index + len(start_str):end_index] + matches = re.findall(start_index1, middle_text) + if matches: + jg = ' '.join(matches) + return jg + + if pl == 2: + middle_text = text[start_index + len(start_str):end_index] + matches = re.findall(start_index1, middle_text) + if matches: + new_list = [f'✨丢丢👉{item}' for item in matches] + jg = '$$$'.join(new_list) + return jg + + def homeContent(self, filter): + result = {} + result = {"class": [{"type_id": "5", "type_name": "电影🌠"}, + {"type_id": "14", "type_name": "剧集🌠"}, + {"type_id": "19", "type_name": "动漫🌠"}, + {"type_id": "23", "type_name": "综艺🌠"}] + } + return result + + def homeVideoContent(self): + videos = [] + + try: + xurl1 = "http://42.194.235.17:20000/api/bt/list?genere_id&order&lang&keywords&code=unknownec1280db12795506&category_id=1&limit=24&channel=wandoujia&page=1&sort=update" + detail = requests.get(url=xurl1, headers=headerx) + detail.encoding = "utf-8" + if detail.status_code == 200: + data = detail.json() + + for vod in data['data']: + + name = vod['title'] + + id = vod['id'] + id = f"http://42.194.235.17:20000/api/node/detail?channel=wandoujia&id={vod['id']}" + + pic = vod['images']['poster'] + + remark = vod['torrents']['zh'][0]['title'] + + video = { + "vod_id": id, + "vod_name": name, + "vod_pic": pic, + "vod_remarks": remark + } + videos.append(video) + + result = {'list': videos} + return result + except: + pass + + def categoryContent(self, cid, pg, filter, ext): + result = {} + videos = [] + + if pg: + page = int(pg) + else: + page = 1 + + if page == '1': + url = f'http://42.194.235.17:20000/api/bt/list?genere_id&order&lang&keywords&code=unknownec1280db12795506&category_id={cid}&limit=24&channel=wandoujia&page={str(page)}&sort=update' + + else: + url = f'http://42.194.235.17:20000/api/bt/list?genere_id&order&lang&keywords&code=unknownec1280db12795506&category_id={cid}&limit=24&channel=wandoujia&page={str(page)}&sort=update' + + try: + detail = requests.get(url=url, headers=headerx) + detail.encoding = "utf-8" + if detail.status_code == 200: + data = detail.json() + + for vod in data['data']: + + name = vod['title'] + + id = vod['id'] + + id = f"http://42.194.235.17:20000/api/node/detail?channel=wandoujia&id={id}" + + pic = vod['images']['poster'] + + remark = vod['torrents']['zh'][0]['title'] + + video = { + "vod_id": id, + "vod_name": name, + "vod_pic": pic, + "vod_remarks": remark + } + videos.append(video) + + except: + pass + result = {'list': videos} + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + global pm + did = ids[0] + result = {} + videos = [] + purl = '' + + detail = requests.get(url=did, headers=headerx) + detail.encoding = "utf-8" + if detail.status_code == 200: + data = detail.json() + + content = data['data']['description'] + content = content.replace('\u3000', '').replace(' ', '').replace('\r', '').replace('\n', '') + + for vod in data['data']['btbo_downlist']: + + name = vod['title'] + + url1 = vod['url'] + + purl = purl + name + '$' + url1 + '#' + + purl = purl[:-1] + + videos.append({ + "vod_id": did, + "vod_actor": '😸皮皮 😸灰灰', + "vod_director": '😸丢丢', + "vod_content": content, + "vod_play_from": '丢丢专线', + "vod_play_url": purl + }) + + result['list'] = videos + return result + + def playerContent(self, flag, id, vipFlags): + + result = {} + result["parse"] = 0 + result["playUrl"] = '' + result["url"] = id + result["header"] = headerx + return result + + def searchContentPage(self, key, quick, page): + result = {} + videos = [] + + if not page: + page = '1' + if page == '1': + url = f'http://42.194.235.17:20000/api/video/search?page={str(page)}&key={key}' + + else: + url = f'http://42.194.235.17:20000/api/video/search?page={str(page)}&key={key}' + + detail = requests.get(url=url, headers=headerx) + detail.encoding = "utf-8" + if detail.status_code == 200: + data = detail.json() + + for vod in data['data']: + + name = vod['title'] + + id = vod['id'] + id = f"http://42.194.235.17:20000/api/node/detail?channel=wandoujia&id={vod['id']}" + + pic = vod['thumbnail'] + + remark = vod['mask'] + + video = { + "vod_id": id, + "vod_name": name, + "vod_pic": pic, + "vod_remarks": remark + } + videos.append(video) + + result['list'] = videos + result['page'] = page + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def searchContent(self, key, quick): + return self.searchContentPage(key, quick, '1') + + def localProxy(self, params): + if params['type'] == "m3u8": + return self.proxyM3u8(params) + elif params['type'] == "media": + return self.proxyMedia(params) + elif params['type'] == "ts": + return self.proxyTs(params) + return None + + +""" + + ======================================= + + 换行 \n 零个或者多个空格 \s+ 数字型 int 文本型 str 分页{} '年代':'2021' + + 性能要求高"lxml" 处理不规范的HTML"html5lib" 简单应用"html.parser" 解析XML"xml" + + ======================================= + + /rss/index.xml?wd=爱情&page=1 搜索有验证 + + /index.php/ajax/suggest?mid=1&wd=爱情&page=1&limit=30 搜索有验证 + + /index.php/ajax/data?mid=1&tid={cateId}&class={class}&area={area}&page={catePg}&limit=30 分类有验证 + + /index.php/vod/type/class/{cid}/id/41/page/{str(page)}/year/{NdType}.html 隐藏分类 + + /{cateId}-{area}-{by}-{class}-{lang}-{letter}---{catePg}---{year}.html + + 短剧 穿越 古装 仙侠 女频 恋爱 反转 现代 都市 剧情 玄幻 脑洞 悬疑 + + ======================================= + + aaa = self.extract_middle_text(res, 'bbb', 'ccc', 0) + aaa = aaa.replace('aaa', '').replace('bbb', '') 替换多余 + 取头 取尾 (不循环) 截取项 (不循环) 长用于直链 二次截取 0号子程序 + + aaa =self.extract_middle_text(res, 'bbb', 'ccc',1,'html">(.*?)<') + aaa = aaa.replace('aaa', '').replace('bbb', '') 替换多余 + 取头 取尾 (不循环) 截取项 (循环) 长用于详情 和2号区别没有$$$ 1号子程序 + + aaa = self.extract_middle_text(res, 'bbb','ccc', 2,'<span class=".*?" id=".*?">(.*?)</span>') + aaa = aaa.replace('aaa', '').replace('bbb', '') 替换多余 + 取头 取尾 (不循环) 截取项 (循环) 只能用于线路数组 里面包含$$$ 2号子程序 + + aaa = self.extract_middle_text(res, 'bbb', 'ccc', 3,'href="(.*?)" class=".*?">(.*?)</a>') + aaa = aaa.replace('aaa', '').replace('bbb', '') 替换多余 + 取头 取尾 (循环) 截取项 (循环) 长用于播放数组 3号子程序 + + ======================================= + +""" + +if __name__ == '__main__': + spider_instance = Spider() + + # res=spider_instance.homeContent('filter') # 分类🚨 + + # res = spider_instance.homeVideoContent() # 首页🚨 + + # res=spider_instance.categoryContent('5', 1, 'filter', {}) # 分页🚨 + + res = spider_instance.detailContent(['http://42.194.235.17:20000/api/node/detail?channel=wandoujia&id=559366']) # 详情页🚨 + + # res = spider_instance.playerContent('1', 'http://42.194.235.17:20000/api/nUser/commentList?url_id=176366&page=1&token=', 'vipFlags') # 播放页🚨 + + # res = spider_instance.searchContentPage('爱情', 'quick', '1') # 搜索页🚨 + + print(res) + + + diff --git a/py/酷云.py b/py/酷云.py new file mode 100644 index 0000000..9979aa9 --- /dev/null +++ b/py/酷云.py @@ -0,0 +1,199 @@ +# coding=utf-8 +# !/usr/bin/python +# by嗷呜 + +import sys +sys.path.append("..") +import re +from base.spider import Spider +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad +from base64 import b64encode, b64decode +import json +import time + + +class Spider(Spider): + + def getName(self): + return "酷云" + + def init(self, extend=""): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def action(self, action): + pass + + def destroy(self): + pass + + host = "https://sc1080.top" + t = str(int(time.time())) + + def header(self): + header = { + "User-Agent": "okhttp/3.14.9", + "app-version-code": "134", + "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", + } + return header + + def homeContent(self, filter): + data = self.fetch( + "{0}/api.php/getappapi.index/initV119".format(self.host), + headers=self.header(), + ).json() + data1 = self.aes("decrypt", data["data"]) + dy = { + "class": "类型", + "area": "地区", + "lang": "语言", + "year": "年份", + "letter": "字母", + "by": "排序", + "sort": "排序", + } + + filters = {} + classes = [] + json_data = json.loads(data1)["type_list"] + self.homedata = json.loads(data1)["banner_list"] + for item in json_data: + if item["type_name"] == "全部": + continue + has_non_empty_field = False + jsontype_extend = json.loads(item["type_extend"]) + self.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 + return result + + def homeVideoContent(self): + result = {"list": self.homedata} + return result + + 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 = {} + url = "{0}/api.php/getappapi.index/typeFilterVodList".format(self.host) + data = self.post(url, headers=self.header(), data=body).json() + data1 = self.aes("decrypt", data["data"]) + result["list"] = json.loads(data1)["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]}" + url = "{0}/api.php/getappapi.index/vodDetail".format(self.host) + data = self.post(url, headers=self.header(), data=body).json() + data1 = json.loads(self.aes("decrypt", data["data"])) + vod = data1["vod"] + play = [] + names = [] + + for itt in data1["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']}") + 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).text + data1 = json.loads(data)["data"] + data2 = self.aes("decrypt", data1) + result = {"list": json.loads(data2)["search_list"],'page':pg} + 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 + if "m3u8" not in url and "mp4" not in url: + try: + body = edu(url) + data = self.post("{0}/api.php/getappapi.index/vodParse".format(self.host), headers=self.header(), + data=body).json() + data1 = json.loads(self.aes("decrypt", data["data"]))["json"] + url = json.loads(data1)["url"] + except Exception as e: + url = id + result = {} + headers = self.header().copy() + del headers["Content-Type"] + result["parse"] = 0 + result["url"] = url + result["header"] = headers + return result + def localProxy(self, param): + pass + + def aes(self, operation, text): + key = "e59d44b2eef03ba2".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") diff --git a/py/首映网.py b/py/首映网.py new file mode 100644 index 0000000..822fa8c --- /dev/null +++ b/py/首映网.py @@ -0,0 +1,373 @@ +""" + +作者 凯悦宾馆 🚓 内容均从互联网收集而来 仅供交流学习使用 版权归原创者所有 如侵犯了您的权益 请通知作者 将及时删除侵权内容 + ====================kaiyuebinguan==================== + +""" + +import requests +from bs4 import BeautifulSoup +import re +from base.spider import Spider +import sys +import json +import base64 +import urllib.parse + +sys.path.append('..') + +xurl = "https://www.tpua.vip" + +headerx = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36' + } + +pm = '' + +class Spider(Spider): + global xurl + global headerx + + def getName(self): + return "首页" + + def init(self, extend): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def extract_middle_text(self, text, start_str, end_str, pl, start_index1: str = '', end_index2: str = ''): + if pl == 3: + plx = [] + while True: + start_index = text.find(start_str) + if start_index == -1: + break + end_index = text.find(end_str, start_index + len(start_str)) + if end_index == -1: + break + middle_text = text[start_index + len(start_str):end_index] + plx.append(middle_text) + text = text.replace(start_str + middle_text + end_str, '') + if len(plx) > 0: + purl = '' + for i in range(len(plx)): + matches = re.findall(start_index1, plx[i]) + output = "" + for match in matches: + match3 = re.search(r'(?:^|[^0-9])(\d+)(?:[^0-9]|$)', match[1]) + if match3: + number = match3.group(1) + else: + number = 0 + if 'http' not in match[0]: + output += f"#{'📽️丢丢👉' + match[1]}${number}{xurl}{match[0]}" + else: + output += f"#{'📽️丢丢👉' + match[1]}${number}{match[0]}" + output = output[1:] + purl = purl + output + "$$$" + purl = purl[:-3] + return purl + else: + return "" + else: + start_index = text.find(start_str) + if start_index == -1: + return "" + end_index = text.find(end_str, start_index + len(start_str)) + if end_index == -1: + return "" + + if pl == 0: + middle_text = text[start_index + len(start_str):end_index] + return middle_text.replace("\\", "") + + if pl == 1: + middle_text = text[start_index + len(start_str):end_index] + matches = re.findall(start_index1, middle_text) + if matches: + jg = ' '.join(matches) + return jg + + if pl == 2: + middle_text = text[start_index + len(start_str):end_index] + matches = re.findall(start_index1, middle_text) + if matches: + new_list = [f'✨丢丢👉{item}' for item in matches] + jg = '$$$'.join(new_list) + return jg + + def homeContent(self, filter): + result = {} + result = {"class": [{"type_id": "dianying", "type_name": "丢丢电影🌠"}, + {"type_id": "dianshiju", "type_name": "丢丢剧集🌠"}, + {"type_id": "zongyi", "type_name": "丢丢动漫🌠"}, + {"type_id": "duanju", "type_name": "丢丢短剧🌠"}, + {"type_id": "dongman", "type_name": "丢丢综艺🌠"}], + + "list": [], + "filters": {"dianying": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": ""}, + {"n": "2024", "v": "2024"}, + {"n": "2023", "v": "2023"}, + {"n": "2022", "v": "2022"}, + {"n": "2021", "v": "2021"}, + {"n": "2020", "v": "2020"}, + {"n": "2019", "v": "2019"}, + {"n": "2018", "v": "2018"}]}], + "dianshiju": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": ""}, + {"n": "2024", "v": "2024"}, + {"n": "2023", "v": "2023"}, + {"n": "2022", "v": "2022"}, + {"n": "2021", "v": "2021"}, + {"n": "2020", "v": "2020"}, + {"n": "2019", "v": "2019"}, + {"n": "2018", "v": "2018"}]}], + "duanju": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": ""}, + {"n": "2024", "v": "2024"}, + {"n": "2023", "v": "2023"}, + {"n": "2022", "v": "2022"}, + {"n": "2021", "v": "2021"}, + {"n": "2020", "v": "2020"}, + {"n": "2019", "v": "2019"}, + {"n": "2018", "v": "2018"}]}], + "zongyi": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": ""}, + {"n": "2024", "v": "2024"}, + {"n": "2023", "v": "2023"}, + {"n": "2022", "v": "2022"}, + {"n": "2021", "v": "2021"}, + {"n": "2020", "v": "2020"}, + {"n": "2019", "v": "2019"}, + {"n": "2018", "v": "2018"}]}], + "dongman": [{"key": "年代", + "name": "年代", + "value": [{"n": "全部", "v": ""}, + {"n": "2024", "v": "2024"}, + {"n": "2023", "v": "2023"}, + {"n": "2022", "v": "2022"}, + {"n": "2021", "v": "2021"}, + {"n": "2020", "v": "2020"}, + {"n": "2019", "v": "2019"}, + {"n": "2018", "v": "2018"}]}]}} + + return result + + def homeVideoContent(self): + videos = [] + try: + detail = requests.get(url=xurl, headers=headerx) + detail.encoding = "utf-8" + res = detail.text + doc = BeautifulSoup(res, "lxml") + + soups = doc.find_all('ul', class_="clearfix") + + for soup in soups: + vods = soup.find_all('a', class_="video-pic") + + for vod in vods: + + name = vod['title'] + + id = vod['href'] + + pic = vod['data-original'] + + if 'http' not in pic: + pic = xurl + pic + + remarks = vod.find('span', class_="note") + remark = remarks.text.strip() + + video = { + "vod_id": id, + "vod_name": '丢丢📽️' + name, + "vod_pic": pic, + "vod_remarks": '丢丢▶️' + remark + } + videos.append(video) + + result = {'list': videos} + return result + except: + pass + + def categoryContent(self, cid, pg, filter, ext): + result = {} + if pg: + page = int(pg) + else: + page = 1 + page = int(pg) + videos = [] + + if '年代' in ext.keys(): + NdType = ext['年代'] + else: + NdType = '' + + if page == '1': + url = f'{xurl}/list/{cid}___2024__.html' + + else: + url = f'{xurl}/list/{cid}___{NdType}___{str(page)}.html' + + try: + detail = requests.get(url=url, headers=headerx) + detail.encoding = "utf-8" + res = detail.text + doc = BeautifulSoup(res, "lxml") + + soups = doc.find_all('ul', class_="clearfix") + + for soup in soups: + vods = soup.find_all('a', class_="video-pic") + + for vod in vods: + + name = vod['title'] + + id = vod['href'] + + pic = vod['data-original'] + + if 'http' not in pic: + pic = xurl + pic + + remarks = vod.find('span', class_="note") + remark = remarks.text.strip() + + video = { + "vod_id": id, + "vod_name": '丢丢📽️' + name, + "vod_pic": pic, + "vod_remarks": '丢丢▶️' + remark + } + videos.append(video) + + except: + pass + result = {'list': videos} + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + global pm + did = ids[0] + result = {} + videos = [] + playurl = '' + if 'http' not in did: + did = xurl + did + res1 = requests.get(url=did, headers=headerx) + res1.encoding = "utf-8" + res = res1.text + + content = '😸丢丢🎉为您介绍剧情📢本资源来源于网络🚓侵权请联系删除👉' + self.extract_middle_text(res,'details-content-all collapse">','</span>', 0) + content = content.replace('<p>', '').replace('<br/>', '').replace(' ', '').replace('</p>', '').replace('\u3000', '') + + xianlu = self.extract_middle_text(res, '<ul class="nav nav-tabs hidden-xs"','</ul>',2, 'data-toggle=".*?">(.*?)</a>') + + bofang = self.extract_middle_text(res, '<ul class="clearfix fade in active"', '</ul>', 3,'href="(.*?)">(.*?)<') + + videos.append({ + "vod_id": did, + "vod_actor": '😸皮皮 😸灰灰', + "vod_director": '😸丢丢', + "vod_content": content, + "vod_play_from": xianlu, + "vod_play_url": bofang + }) + + result['list'] = videos + return result + + def playerContent(self, flag, id, vipFlags): + parts = id.split("http") + xiutan = 0 + if xiutan == 0: + if len(parts) > 1: + before_https, after_https = parts[0], 'http' + parts[1] + res = requests.get(url=after_https, headers=headerx) + res = res.text + + url = self.extract_middle_text(res, '","url":"', '"', 0).replace('\\', '') + + result = {} + result["parse"] = xiutan + result["playUrl"] = '' + result["url"] = url + result["header"] = headerx + return result + + def searchContentPage(self, key, quick, page): + result = {} + videos = [] + if not page: + page = '1' + if page == '1': + url = f'{xurl}/search/{key}-1.html' + + else: + url = f'{xurl}/search/{key}-{str(page)}.html' + + detail = requests.get(url=url, headers=headerx) + detail.encoding = "utf-8" + res = detail.text + doc = BeautifulSoup(res, "lxml") + + soups = doc.find_all('div', class_="details-info-min") + + for vod in soups: + names = vod.find('a', class_="video-pic") + name = names['title'] + + ids = vod.find('a', class_="video-pic") + id = ids['href'] + + pics = vod.find('a', class_="video-pic") + pic = pics['data-original'] + + remark = self.extract_middle_text(str(vod), '状态:</span>', '</li>', 0) + + video = { + "vod_id": id, + "vod_name": '丢丢📽️' + name, + "vod_pic": pic, + "vod_remarks": '丢丢▶️' + remark + } + videos.append(video) + + result['list'] = videos + result['page'] = page + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def searchContent(self, key, quick): + return self.searchContentPage(key, quick, '1') + + def localProxy(self, params): + if params['type'] == "m3u8": + return self.proxyM3u8(params) + elif params['type'] == "media": + return self.proxyMedia(params) + elif params['type'] == "ts": + return self.proxyTs(params) + return None + diff --git a/py/黑料.py b/py/黑料.py new file mode 100644 index 0000000..ee328a4 --- /dev/null +++ b/py/黑料.py @@ -0,0 +1,270 @@ +# coding=utf-8 +# !/usr/bin/python +import sys +import requests +from bs4 import BeautifulSoup +import re +import base64 +from base.spider import Spider +import random + +sys.path.append('..') +xurl = "https://heiliaowang-44.buzz" +headerx = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36', + +} +class Spider(Spider): + global xurl + global headerx + + + def getName(self): + return "首页" + + def init(self, extend): + pass + + def destroy(self): + pass + + def isVideoFormat(self, url): + pass + + def manualVideoCheck(self): + pass + + def homeContent(self, filter): + res = requests.get(xurl, headers=headerx) + res.encoding = "utf-8" + doc = BeautifulSoup(res.text, "html.parser") + sourcediv = doc.find('div', class_='nav') + vod = sourcediv.find_all('dd') + string_list = ["首页", "激情图漫", "激情小说", + "情色小说", "随机推荐", "顶级资源"] + + result = {} + result['class'] = [] + result['class'].append({'type_id': "/type/328", 'type_name': "国产视频"}) + result['class'].append({'type_id': "/type/329", 'type_name': "中文字幕"}) + result['class'].append({'type_id': "/type/331", 'type_name': "日本有码"}) + result['class'].append({'type_id': "/type/332", 'type_name': "日本无码"}) + result['class'].append({'type_id': "/type/333", 'type_name': "欧美无码"}) + result['class'].append({'type_id': "/type/334", 'type_name': "强奸乱轮"}) + result['class'].append({'type_id': "/type/335", 'type_name': "制服诱惑"}) + result['class'].append({'type_id': "/type/336", 'type_name': "直播主播"}) + result['class'].append({'type_id': "/type/338", 'type_name': "明星换脸"}) + result['class'].append({'type_id': "/type/339", 'type_name': "抖阴视频"}) + result['class'].append({'type_id': "/type/340", 'type_name': "女优明星"}) + result['class'].append({'type_id': "/type/343", 'type_name': "网爆门"}) + result['class'].append({'type_id': "/type/345", 'type_name': "伦理三级"}) + result['class'].append({'type_id': "/type/346", 'type_name': "AV解说"}) + result['class'].append({'type_id': "/type/347", 'type_name': "SM调教"}) + result['class'].append({'type_id': "/type/348", 'type_name': "萝莉少女"}) + result['class'].append({'type_id': "/type/349", 'type_name': "极品媚黑"}) + result['class'].append({'type_id': "/type/350", 'type_name': "女同性恋"}) + result['class'].append({'type_id': "/type/351", 'type_name': "玩偶姐姐"}) + result['class'].append({'type_id': "/type/353", 'type_name': "人妖系列"}) + result['class'].append({'type_id': "/type/373", 'type_name': "韩国主播"}) + result['class'].append({'type_id': "/type/378", 'type_name': "VR视角"}) + for item in vod: + name = item.find('a').text + if name in string_list: + continue + + id = item.find('a')['href'] + id = id.replace(".html", "") + result['class'].append({'type_id': id, 'type_name': name}) + + return result + def homeVideoContent(self): + videos = [] + try: + res = requests.get(xurl, headers=headerx) + res.encoding = "utf-8" + doc = BeautifulSoup(res.text, "html.parser") + sourcediv = doc.find_all('div', class_='pic') + for vod in sourcediv: + ul_elements = vod.find_all('ul') + for item in ul_elements: + name = item.select_one("li a")['title'] + pic = item.select_one("li a img")["src"] + remark = item.select_one("li a span").text + id = item.select_one("li a")['href'] + video = { + "vod_id": id, + "vod_name": name, + "vod_pic": pic, + "vod_remarks": remark + } + videos.append(video) + except: + pass + result = {'list': videos} + return result + + def categoryContent(self, cid, pg, filter, ext): + result = {} + videos = [] + if not pg: + pg = 1 + + url = xurl +cid + "/" + str(pg) + ".html" + detail = requests.get(url=url, headers=headerx) + detail.encoding = "utf-8" + doc = BeautifulSoup(detail.text, "html.parser") + sourcediv = doc.find_all('div', class_='pic') + for vod in sourcediv: + ul_elements = vod.find_all('ul') + for item in ul_elements: + name = item.select_one("li a")['title'] + pic = item.select_one("li a img")["src"] + remark = item.select_one("li a span").text + id = item.select_one("li a")['href'] + video = { + "vod_id": id, + "vod_name": name, + "vod_pic": pic, + "vod_remarks": remark + } + videos.append(video) + + result['list'] = videos + result['page'] = pg + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def detailContent(self, ids): + did = ids[0] + videos = [] + result = {} + res = requests.get(url=xurl + did, headers=headerx) + res.encoding = "utf-8" + doc = BeautifulSoup(res.text, "html.parser") + sourcediv = doc.find('div', style='padding-bottom: 10px;') + vod = sourcediv.find_all('a') + play_from = "" + play_url = "" + for item in vod: + play_from = play_from + item.text + "$$$" + play_url = play_url + item['href'] + "$$$" + while play_url[-1] == "#" or play_url[-1] == "$": + play_url = play_url[:-1] + + while play_from[-1] == "#" or play_from[-1] == "$": + play_from = play_from[:-1] + + source_match = re.search(r"<li>播放地址:<strong>(.*?)</strong></li>", res.text) + if source_match: + tx = source_match.group(1) + + videos.append({ + "vod_id": did, + "vod_name": tx, + "vod_pic": "", + "type_name": "ぃぅおか🍬 คิดถึง", + "vod_year": "", + "vod_area": "", + "vod_remarks": "", + "vod_actor": "", + "vod_director": "", + "vod_content": "", + "vod_play_from": play_from, + "vod_play_url": play_url + }) + + result['list'] = videos + return result + + def playerContent(self, flag, id, vipFlags): + result = {} + res = requests.get(url=xurl + id, headers=headerx) + res.encoding = "utf-8" + if '"rid"' in res.text: + decoded_str = '' + while not decoded_str: + source_match3 = re.search(r'"rid" : "(.*?)"', res.text) + if source_match3: + id = source_match3.group(1) + + data = "rid=" + id + header = { + "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36", + 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' + } + res2 = requests.post(url="https://heiliaowang-44.buzz/fetchPlayUrl3", headers=header, data=data) + + source_match4 = re.search(r'"returnData"\s*:\s*"([^"]+)"', res2.text) + if source_match4: + decoded_str = source_match4.group(1) + + + else: + source_match = re.search(r"http:(.*?)\.m3u8", res.text) + decoded_str = "" + if source_match: + str3 = source_match.group(1) + if "aHR0c" in str3: + padding_needed = len(str3) % 4 + if padding_needed: + str3 += '=' * (4 - padding_needed) + decoded_str = base64.b64decode(str3).decode("utf-8") + if not decoded_str: + source_match2 = re.search(r"'(.*?)\.m3u8';", res.text) + if source_match2: + decoded_str = source_match2.group(1) + ".m3u8" + + result["parse"] = 0 + result["playUrl"] = '' + result["url"] = decoded_str + result["header"] = headerx + return result + + def searchContent(self, key, quick): + return self.searchContentPage(key, quick, '1') + + def searchContentPage(self, key, quick, page): + + result = {} + videos = [] + if not page: + page = 1 + + + url = xurl +"/search/"+ key +"/n/" + str(page)+".html" + detail = requests.get(url=url, headers=headerx) + detail.encoding = "utf-8" + doc = BeautifulSoup(detail.text, "html.parser") + sourcediv = doc.find_all('div', class_='pic') + for vod in sourcediv: + ul_elements = vod.find_all('ul') + for item in ul_elements: + name = item.select_one("li a")['title'] + pic = item.select_one("li a img")["src"] + remark = item.select_one("li a span").text + id = item.select_one("li a")['href'] + video = { + "vod_id": id, + "vod_name": name, + "vod_pic": pic, + "vod_remarks": remark + } + videos.append(video) + + result['list'] = videos + result['page'] = page + result['pagecount'] = 9999 + result['limit'] = 90 + result['total'] = 999999 + return result + + def localProxy(self, params): + if params['type'] == "m3u8": + return self.proxyM3u8(params) + elif params['type'] == "media": + return self.proxyMedia(params) + elif params['type'] == "ts": + return self.proxyTs(params) + return None