2975 lines
128 KiB
Python
2975 lines
128 KiB
Python
# coding=utf-8
|
||
# !/usr/bin/python
|
||
import sys, os, json
|
||
from base.spider import Spider
|
||
from requests import session, utils, get as requests_get
|
||
from requests.adapters import HTTPAdapter, Retry
|
||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||
import threading
|
||
import hashlib
|
||
import time
|
||
import random
|
||
import base64
|
||
from functools import reduce
|
||
from urllib.parse import quote, urlencode
|
||
|
||
sys.path.append('..')
|
||
dirname, filename = os.path.split(os.path.abspath(__file__))
|
||
sys.path.append(dirname)
|
||
|
||
class Spider(Spider):
|
||
#默认设置
|
||
defaultConfig = {
|
||
'currentVersion': "20231213_1",
|
||
#【建议通过扫码确认】设置Cookie,在双引号内填写
|
||
'raw_cookie_line': "",
|
||
#如果主cookie没有vip,可以设置第二cookie,仅用于播放会员番剧,所有的操作、记录还是在主cookie,不会同步到第二cookie
|
||
'raw_cookie_vip': "",
|
||
#主页默认显示3图
|
||
'maxHomeVideoContent': '3',
|
||
#收藏标签默认显示追番1,追剧2,默认收藏夹0
|
||
'favMode': '0',
|
||
#部分视频列表分页,限制每次加载数量
|
||
'page_size': 12,
|
||
#上传播放进度间隔时间,单位秒,b站默认间隔15,0则不上传播放历史
|
||
'heartbeatInterval': '15',
|
||
#视频默认画质ID
|
||
'vodDefaultQn': '80',
|
||
#视频默认解码ID
|
||
'vodDefaultCodec': '7',
|
||
#音频默认码率ID
|
||
'vodDefaultAudio': '30280',
|
||
#获取视频热门评论
|
||
'show_vod_hot_reply': True,
|
||
#从正片中拆分出番剧的预告
|
||
'hide_bangumi_preview': True,
|
||
#登陆会员账号后,影视播放页不显示会员专享的标签,更简洁
|
||
'hide_bangumi_vip_badge': True,
|
||
#番剧(热门)列表使用横图
|
||
'bangumi_horizontal_cover': True,
|
||
#非会员播放会员专享视频时,添加一个页面可以使用解析源,解析源自行解决
|
||
'bangumi_vip_parse': True,
|
||
#付费视频添加一个页面可以使用解析,解析源自行解决
|
||
'bangumi_pay_parse': True,
|
||
#是否显示直播标签筛选中分区的细化标签, 0为不显示,1为显示
|
||
'showLiveFilterTag': '0',
|
||
#主页标签排序, 未登录或cookie失效时自动隐藏动态、收藏、关注、历史
|
||
'cateManual': [
|
||
"推荐",
|
||
"影视",
|
||
"直播",
|
||
"动态",
|
||
"频道",
|
||
"收藏",
|
||
"关注",
|
||
"历史",
|
||
"搜索",
|
||
],
|
||
#自定义推荐标签的筛选
|
||
'tuijianLis': [
|
||
"热门",
|
||
"排行榜",
|
||
"每周必看",
|
||
"入站必刷",
|
||
"番剧时间表",
|
||
"国创时间表"
|
||
],
|
||
'rankingLis': [
|
||
"动画",
|
||
"音乐",
|
||
"舞蹈",
|
||
"游戏",
|
||
"鬼畜",
|
||
"知识",
|
||
"科技",
|
||
"运动",
|
||
"生活",
|
||
"美食",
|
||
"动物",
|
||
"汽车",
|
||
"时尚",
|
||
"娱乐",
|
||
"影视",
|
||
"原创",
|
||
"新人",
|
||
],
|
||
}
|
||
|
||
#在动态标签的筛选中固定显示他,n为用户名或任意都可以,v必须为准确的UID
|
||
focus_on_up_list = [
|
||
#{"n":"徐云流浪中国", "v":"697166795"},
|
||
]
|
||
|
||
#在搜索标签的筛选中固定显示搜索词
|
||
focus_on_search_key = []
|
||
|
||
def getName(self):
|
||
return "哔哩哔哩"
|
||
|
||
def load_config(self):
|
||
try:
|
||
with open(f"{self.configdir}/config.json",encoding="utf-8") as f:
|
||
self.userConfig = json.load(f)
|
||
old_config = {
|
||
'master': 'cookie_dic',
|
||
'vip': 'cookie_vip_dic',
|
||
'fake': 'cookie_fake_dic',
|
||
}
|
||
for _type, old in old_config.items():
|
||
old = self.userConfig.get(old)
|
||
if old:
|
||
if not self.userConfig.get('users'):
|
||
self.userConfig['users'] = {}
|
||
self.userConfig['users'][_type] = {'cookies_dic': old}
|
||
users = self.userConfig.get('users', {})
|
||
if users.get('master') and users['master'].get('cookies_dic'):
|
||
self.session_master.cookies = utils.cookiejar_from_dict(users['master']['cookies_dic'])
|
||
self.userid = users['master']['userid']
|
||
if users.get('fake') and users['fake'].get('cookies_dic'):
|
||
self.session_fake.cookies = utils.cookiejar_from_dict(users['fake']['cookies_dic'])
|
||
except:
|
||
self.userConfig = {}
|
||
self.userConfig = {**self.defaultConfig, **self.userConfig}
|
||
|
||
dump_config_lock = threading.Lock()
|
||
|
||
def dump_config(self):
|
||
needSaveConfig = ['users', 'channel_list', 'cateLive', 'cateManualLive', 'cateManualLiveExtra']
|
||
userConfig_new = {}
|
||
for key, value in self.userConfig.items():
|
||
dafalutValue = self.defaultConfig.get(key)
|
||
if dafalutValue != None and value != dafalutValue or key in needSaveConfig:
|
||
userConfig_new[key] = value
|
||
self.dump_config_lock.acquire()
|
||
with open(f"{self.configdir}/config.json", 'w', encoding="utf-8") as f:
|
||
data = json.dumps(userConfig_new, indent=1, ensure_ascii=False)
|
||
f.write(data)
|
||
self.dump_config_lock.release()
|
||
|
||
pool = ThreadPoolExecutor(max_workers=8)
|
||
task_pool = []
|
||
# 主页
|
||
def homeContent(self, filter):
|
||
self.pool.submit(self.add_live_filter)
|
||
self.pool.submit(self.add_channel_filter)
|
||
self.pool.submit(self.add_search_key)
|
||
self.pool.submit(self.add_focus_on_up_filter)
|
||
self.pool.submit(self.get_tuijian_filter)
|
||
self.pool.submit(self.add_fav_filter)
|
||
#self.pool.submit(self.homeVideoContent)
|
||
needLogin = ['频道', '动态', '收藏', '关注', '历史']
|
||
cateManual = self.userConfig['cateManual']
|
||
if not self.userid and not 'UP' in cateManual or not '动态' in cateManual and not 'UP' in cateManual:
|
||
cateManual += ['UP']
|
||
classes = []
|
||
for k in cateManual:
|
||
if k in needLogin and not self.userid:
|
||
continue
|
||
classes.append({
|
||
'type_name': k,
|
||
'type_id': k
|
||
})
|
||
self.add_focus_on_up_filter_event.wait()
|
||
if 'UP' in cateManual:
|
||
self.config["filter"].update({'UP': self.config["filter"].pop('动态')})
|
||
result = {'class': classes}
|
||
self.add_live_filter_event.wait()
|
||
self.add_channel_filter_event.wait()
|
||
self.add_fav_filter_event.wait()
|
||
self.add_search_key_event.wait()
|
||
if filter:
|
||
result['filters'] = self.config['filter']
|
||
self.pool.submit(self.dump_config)
|
||
return result
|
||
|
||
# 用户cookies
|
||
userid = csrf = ''
|
||
session_master = session()
|
||
session_vip = session()
|
||
session_fake = session()
|
||
con = threading.Condition()
|
||
getCookie_event = threading.Event()
|
||
retries = Retry(total=5,
|
||
#status_forcelist=[ 500, 502, 503, 504 ],
|
||
backoff_factor=0.1)
|
||
adapter = HTTPAdapter(max_retries=retries)
|
||
session_master.mount('https://', adapter)
|
||
session_vip.mount('https://', adapter)
|
||
session_fake.mount('https://', adapter)
|
||
|
||
def getCookie_dosth(self, co):
|
||
c = co.strip().split('=', 1)
|
||
if not '%' in c[1]:
|
||
c[1] = quote(c[1])
|
||
return c
|
||
|
||
def getCookie(self, _type='master'):
|
||
raw_cookie = 'raw_cookie_line'
|
||
if _type == 'vip':
|
||
raw_cookie = 'raw_cookie_vip'
|
||
raw_cookie = self.userConfig.get(raw_cookie)
|
||
users = self.userConfig.get('users', {})
|
||
user = users.get(_type, {})
|
||
if not raw_cookie and not user:
|
||
if _type == 'master':
|
||
self.getCookie_event.set()
|
||
with self.con:
|
||
self.con.notifyAll()
|
||
return
|
||
cookies_dic = user.get('cookies_dic', {})
|
||
if raw_cookie:
|
||
cookies_dic = dict(map(self.getCookie_dosth, raw_cookie.split(';')))
|
||
cookies = utils.cookiejar_from_dict(cookies_dic)
|
||
url = 'https://api.bilibili.com/x/web-interface/nav'
|
||
content = self.fetch(url, headers=self.header, cookies=cookies)
|
||
res = json.loads(content.text)
|
||
user['isLogin'] = 0
|
||
if res["code"] == 0:
|
||
user['isLogin'] = 1
|
||
user['userid'] = res["data"]['mid']
|
||
user['face'] = res['data']['face']
|
||
user['uname'] = res['data']['uname']
|
||
user['cookies_dic'] = cookies_dic
|
||
user['isVIP'] = int(res['data']['vipStatus'])
|
||
if _type == 'master':
|
||
self.session_master.cookies = cookies
|
||
self.userid = user['userid']
|
||
self.csrf = cookies_dic['bili_jct']
|
||
if user['isVIP']:
|
||
self.session_vip.cookies = cookies
|
||
else:
|
||
self.userid = ''
|
||
users[_type] = user
|
||
with self.con:
|
||
if len(user) > 1:
|
||
self.userConfig.update({'users': users})
|
||
if _type == 'master':
|
||
self.getCookie_event.set()
|
||
|
||
getFakeCookie_event = threading.Event()
|
||
|
||
def getFakeCookie(self, fromSearch=None):
|
||
if self.session_fake.cookies:
|
||
self.getFakeCookie_event.set()
|
||
header = {}
|
||
header['User-Agent'] = self.header['User-Agent']
|
||
rsp = self.fetch('https://space.bilibili.com/2/video', headers=header)
|
||
self.session_fake.cookies = rsp.cookies
|
||
self.getFakeCookie_event.set()
|
||
with self.con:
|
||
users = self.userConfig.get('users', {})
|
||
users['fake'] = {'cookies_dic': dict(rsp.cookies)}
|
||
self.userConfig.update({'users': users})
|
||
if not fromSearch:
|
||
self.getCookie_event.wait()
|
||
if not self.session_master.cookies:
|
||
self.session_master.cookies = rsp.cookies
|
||
|
||
def get_fav_list_dict(self, fav):
|
||
fav_dict = {
|
||
'n': fav['title'].replace("<em class=\"keyword\">", "").replace("</em>", "").replace(""",'"').strip(),
|
||
'v': fav['id']}
|
||
return fav_dict
|
||
|
||
add_fav_filter_event = threading.Event()
|
||
|
||
def add_fav_filter(self):
|
||
users = self.userConfig.get('users', {})
|
||
if users.get('master') and users['master'].get('userid'):
|
||
userid = self.userConfig['users']['master']['userid']
|
||
else:
|
||
self.getCookie_event.wait()
|
||
userid = self.userid
|
||
fav_list = []
|
||
if userid:
|
||
url = 'https://api.bilibili.com/x/v3/fav/folder/created/list-all?up_mid=%s&jsonp=jsonp' % str(userid)
|
||
rsp = self._get_sth(url)
|
||
jo = json.loads(rsp.text)
|
||
if jo['code'] == 0 and jo.get('data'):
|
||
fav = jo['data'].get('list')
|
||
fav_list = list(map(self.get_fav_list_dict, fav))
|
||
fav_top = [{"n": "追番", "v": "1"},{"n": "追剧", "v": "2"}]
|
||
fav_config = self.config["filter"].get('收藏')
|
||
if fav_config:
|
||
fav_config.insert(0, {
|
||
"key": "mlid",
|
||
"name": "分区",
|
||
"value": fav_top + fav_list,
|
||
})
|
||
self.add_fav_filter_event.set()
|
||
self.userConfig["fav_list"] = fav_list
|
||
|
||
def get_channel_list_dict(self, channel):
|
||
channel_dict = {
|
||
'n': channel['name'].replace("<em class=\"keyword\">", "").replace("</em>", "").replace(""",'"').strip(),
|
||
'v': channel['id']}
|
||
return channel_dict
|
||
|
||
def get_channel_list(self):
|
||
url = 'https://api.bilibili.com/x/web-interface/web/channel/category/channel/list?id=100&offset=0&page_size=15'
|
||
rsp = self._get_sth(url, 'fake')
|
||
jo = json.loads(rsp.text)
|
||
channel_list = []
|
||
if jo['code'] == 0:
|
||
channel = jo['data'].get('channels')
|
||
self.userConfig['channel_list'] = list(map(self.get_channel_list_dict, channel))
|
||
return self.userConfig['channel_list']
|
||
|
||
add_channel_filter_event = threading.Event()
|
||
|
||
def add_channel_filter(self):
|
||
channel_list = self.userConfig.get('channel_list', '')
|
||
channel_list_task = self.pool.submit(self.get_channel_list)
|
||
if not channel_list:
|
||
channel_list = channel_list_task.result()
|
||
channel_config = self.config["filter"].get('频道', [])
|
||
if channel_config:
|
||
channel_config.insert(0, {
|
||
"key": "cid",
|
||
"name": "分区",
|
||
"value": channel_list,
|
||
})
|
||
self.config["filter"]['频道'] = channel_config
|
||
self.add_channel_filter_event.set()
|
||
|
||
add_focus_on_up_filter_event = threading.Event()
|
||
|
||
def add_focus_on_up_filter(self):
|
||
first_list = [{"n": "上个视频的UP主", "v": "上个视频的UP主"}]
|
||
up_list = self.focus_on_up_list
|
||
if not self.session_master.cookies:
|
||
self.getCookie_event.wait()
|
||
focus_on_up_list_mid = list(map(lambda x: x['v'], up_list))
|
||
if self.session_master.cookies:
|
||
url = 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all?timezone_offset=-480&type=video&page=1'
|
||
rsp = self._get_sth(url)
|
||
jo = json.loads(rsp.text)
|
||
if jo['code'] == 0 and jo.get('data'):
|
||
up = jo['data'].get('items', [])
|
||
for u in map(lambda x: {'n': x['modules']["module_author"]['name'], 'v': str(x['modules']["module_author"]['mid'])}, up):
|
||
if not u in up_list and not u['v'] in focus_on_up_list_mid:
|
||
up_list.append(u)
|
||
last_list = [{"n": "登录与设置", "v": "登录"}]
|
||
if not self.isFongmi:
|
||
up_list = first_list + up_list
|
||
up_list += last_list
|
||
dynamic_config = self.config["filter"].get('动态', [])
|
||
if dynamic_config:
|
||
dynamic_config.insert(0, {
|
||
"key": "mid",
|
||
"name": "UP主",
|
||
"value": up_list,
|
||
})
|
||
self.config["filter"]['动态'] = dynamic_config
|
||
self.add_focus_on_up_filter_event.set()
|
||
|
||
def get_live_parent_area_list(self, parent_area):
|
||
name = parent_area['name']
|
||
id = str(parent_area['id'])
|
||
area = parent_area['list']
|
||
area_dict = list(map(lambda area: {'n': area['name'], 'v': str(area['parent_id']) + '_' + str(area['id'])}, area))
|
||
live_area = {'key': 'tid', 'name': name, 'value': area_dict}
|
||
cateLive_name = {'id': id + '_0', 'value': live_area}
|
||
return (name, cateLive_name)
|
||
|
||
def get_live_list(self):
|
||
url = 'https://api.live.bilibili.com/xlive/web-interface/v1/index/getWebAreaList?source_id=2'
|
||
rsp = self._get_sth(url, 'fake')
|
||
jo = json.loads(rsp.text)
|
||
cateLive = {}
|
||
if jo['code'] == 0:
|
||
parent = jo['data']['data']
|
||
self.userConfig['cateLive'] = dict(self.pool.map(self.get_live_parent_area_list, parent))
|
||
return self.userConfig['cateLive']
|
||
|
||
def set_default_cateManualLive(self):
|
||
cateManualLive = [{'n': '推荐', 'v': '推荐'},]
|
||
for name in self.userConfig['cateLive']:
|
||
area_dict = {'n': name, 'v': self.userConfig['cateLive'][name]['id']}
|
||
cateManualLive.append(area_dict)
|
||
self.defaultConfig['cateManualLive'] = cateManualLive
|
||
return cateManualLive
|
||
|
||
add_live_filter_event = threading.Event()
|
||
|
||
def add_live_filter(self):
|
||
cateLive = self.userConfig.get('cateLive', {})
|
||
cateLive_task = self.pool.submit(self.get_live_list)
|
||
if not cateLive:
|
||
cateLive = cateLive_task.result()
|
||
default_cateManualLive_task = self.pool.submit(self.set_default_cateManualLive)
|
||
self.config["filter"]['直播'] = []
|
||
#分区栏
|
||
cateManualLive = self.userConfig.get('cateManualLive', [])
|
||
if not cateManualLive:
|
||
cateManualLive = default_cateManualLive_task.result()
|
||
if cateManualLive:
|
||
live_area = {'key': 'tid', 'name': '分区', 'value': cateManualLive}
|
||
self.config["filter"]['直播'].append(live_area)
|
||
#显示分区细分
|
||
if int(self.userConfig['showLiveFilterTag']):
|
||
for name in cateLive.values():
|
||
if len(name['value']['value']) == 1:
|
||
continue
|
||
self.config["filter"]['直播'].append(name['value'])
|
||
self.add_live_filter_event.set()
|
||
|
||
add_search_key_event = threading.Event()
|
||
|
||
def add_search_key(self):
|
||
focus_on_search_key = self.focus_on_search_key
|
||
url = 'https://api.bilibili.com/x/web-interface/search/square?limit=10&platform=web'
|
||
rsp = self._get_sth(url, 'fake')
|
||
jo = json.loads(rsp.text)
|
||
cateLive = {}
|
||
if jo['code'] == 0:
|
||
trending = jo['data']['trending'].get('list', [])
|
||
focus_on_search_key += list(map(lambda x:x['keyword'], trending))
|
||
keyword = {"key": "keyword", "name": "搜索词","value": []}
|
||
keyword["value"] = list(map(lambda i: {'n': i, 'v': i}, focus_on_search_key))
|
||
self.config["filter"]['搜索'].insert(0, keyword)
|
||
self.add_search_key_event.set()
|
||
|
||
def get_tuijian_filter(self):
|
||
tuijian_filter = {"番剧时间表": "10001", "国创时间表": "10004", "排行榜": "0", "动画": "1", "音乐": "3", "舞蹈": "129", "游戏": "4", "鬼畜": "119", "知识": "36", "科技": "188", "运动": "234", "生活": "160", "美食": "211", "动物": "217", "汽车": "223", "时尚": "155", "娱乐": "5", "影视": "181", "原创": "origin", "新人": "rookie"}
|
||
_dic = [{'n': 'tuijianLis', 'v': '分区'}, {'n': 'rankingLis', 'v': '排行榜'}]
|
||
filter_lis = []
|
||
for d in _dic:
|
||
_filter = {"key": "tid" ,'name': d['v'],"value": []}
|
||
t_lis = self.userConfig.get(d['n'], [])
|
||
for t in t_lis:
|
||
tf = tuijian_filter.get(t)
|
||
if not tf:
|
||
tf = t
|
||
tf_dict = {'n': t, 'v': tf}
|
||
_filter["value"].append(tf_dict)
|
||
filter_lis.append(_filter)
|
||
self.config["filter"]['推荐'] = filter_lis
|
||
|
||
isFongmi = False
|
||
|
||
def __init__(self):
|
||
self.configdir = dirname
|
||
if dirname.startswith('/data/'):
|
||
self.isFongmi = True
|
||
configdir = os.path.abspath(os.path.join(dirname, ".."))
|
||
configdir = os.path.abspath(os.path.join(configdir, ".."))
|
||
self.configdir = f"{configdir}/files"
|
||
self.load_config()
|
||
self.pool.submit(self.getCookie)
|
||
self.pool.submit(self.getFakeCookie)
|
||
self.pool.submit(self.getCookie, 'vip')
|
||
wts = round(time.time())
|
||
self.pool.submit(self.get_wbiKey, wts)
|
||
|
||
def init(self, extend=""):
|
||
print("============{0}============".format(extend))
|
||
pass
|
||
|
||
def isVideoFormat(self, url):
|
||
pass
|
||
|
||
def manualVideoCheck(self):
|
||
pass
|
||
|
||
# 降低内存占用
|
||
def format_img(self, img):
|
||
img += "@672w_378h_1c.webp"
|
||
if not img.startswith('http'):
|
||
img = 'https:' + img
|
||
return img
|
||
|
||
def pagination(self, array, pg):
|
||
max_number = self.userConfig['page_size'] * int(pg)
|
||
min_number = max_number - self.userConfig['page_size']
|
||
return array[min_number:max_number]
|
||
|
||
# 将超过10000的数字换成成以万和亿为单位
|
||
def zh(self, num):
|
||
if int(num) >= 100000000:
|
||
p = round(float(num) / float(100000000), 1)
|
||
p = str(p) + '亿'
|
||
else:
|
||
if int(num) >= 10000:
|
||
p = round(float(num) / float(10000), 1)
|
||
p = str(p) + '万'
|
||
else:
|
||
p = str(num)
|
||
return p
|
||
|
||
# 将秒数转化为 时分秒的格式
|
||
def second_to_time(self, a):
|
||
a = int(a)
|
||
if a < 3600:
|
||
result = time.strftime("%M:%S", time.gmtime(a))
|
||
else:
|
||
result = time.strftime("%H:%M:%S", time.gmtime(a))
|
||
if str(result).startswith('0'):
|
||
result = str(result).replace('0', '', 1)
|
||
return result
|
||
|
||
# 字符串时分秒以及分秒形式转换成秒
|
||
def str2sec(self, x):
|
||
x = str(x)
|
||
try:
|
||
h, m, s = x.strip().split(':') # .split()函数将其通过':'分隔开,.strip()函数用来除去空格
|
||
return int(h) * 3600 + int(m) * 60 + int(s) # int()函数转换成整数运算
|
||
except:
|
||
m, s = x.strip().split(':') # .split()函数将其通过':'分隔开,.strip()函数用来除去空格
|
||
return int(m) * 60 + int(s) # int()函数转换成整数运算
|
||
|
||
# 按时间过滤
|
||
def filter_duration(self, vodlist, key):
|
||
if key == '0':
|
||
return vodlist
|
||
else:
|
||
vod_list_new = [i for i in vodlist if
|
||
self.time_diff1[key][0] <= self.str2sec(str(i["vod_remarks"])) < self.time_diff1[key][1]]
|
||
return vod_list_new
|
||
|
||
# 提取番剧id
|
||
def find_bangumi_id(self, url):
|
||
aid = str(url).strip().split(r"/")[-1]
|
||
if not aid:
|
||
aid = str(url).strip().split(r"/")[-2]
|
||
aid = aid.split(r"?")[0]
|
||
return aid
|
||
|
||
# 登录二维码
|
||
def get_Login_qrcode(self, pg):
|
||
result = {}
|
||
if int(pg) != 1:
|
||
return result
|
||
video = [{
|
||
"vod_id": 'setting_tab&filter',
|
||
"vod_name": '标签与筛选',
|
||
"vod_pic": 'https://www.bilibili.com/favicon.ico'
|
||
},{
|
||
"vod_id": 'setting_liveExtra',
|
||
"vod_name": '查看直播细化标签',
|
||
"vod_pic": 'https://www.bilibili.com/favicon.ico'
|
||
}]
|
||
url = 'https://passport.bilibili.com/x/passport-login/web/qrcode/generate'
|
||
rsp = self._get_sth(url, 'fake')
|
||
jo = json.loads(rsp.text)
|
||
if jo['code'] == 0:
|
||
id = jo['data']['qrcode_key']
|
||
url = jo['data']['url']
|
||
account = {'master': '主账号', 'vip': '副账号'}
|
||
isLogin = {0: '未登录', 1: '已登录'}
|
||
isVIP = {0: '', 1: '👑'}
|
||
users = self.userConfig.get('users', {})
|
||
for _type, typeName in account.items():
|
||
user = users.get(_type)
|
||
if user:
|
||
video.append({
|
||
"vod_id": 'setting_login_' + id,
|
||
"vod_name": user['uname'],
|
||
"vod_pic": self.format_img(user['face']),
|
||
"vod_remarks": isVIP[user['isVIP']] + typeName + ' ' + isLogin[user['isLogin']]
|
||
})
|
||
#pic_url = {'qrcode': url}
|
||
pic_url = {'qrcode': 'https://passport.bilibili.com/h5-app/passport/login/scan?qrcode_key=' + id + '&navhide=1'}
|
||
#if not dirname.startswith('/data/'):
|
||
# pic_url['qr_chs'] = '208x117'
|
||
pic_url['qr_chs'] = '208x117'
|
||
video.append({
|
||
"vod_id": 'setting_login_' + id,
|
||
#"vod_name": '扫码后点击',
|
||
'vod_pic': 'http://jm92swf.s1002.xrea.com/?' + urlencode(pic_url),
|
||
})
|
||
result['list'] = video
|
||
result['page'] = 1
|
||
result['pagecount'] = 1
|
||
result['limit'] = 1
|
||
result['total'] = 1
|
||
return result
|
||
|
||
time_diff1 = {'1': [0, 300],
|
||
'2': [300, 900], '3': [900, 1800], '4': [1800, 3600],
|
||
'5': [3600, 99999999999999999999999999999999]
|
||
}
|
||
time_diff = '0'
|
||
|
||
dynamic_offset = ''
|
||
|
||
def get_dynamic(self, pg, mid, order):
|
||
if mid == '0':
|
||
result = {}
|
||
if int(pg) == 1:
|
||
self.dynamic_offset = ''
|
||
url = 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all?timezone_offset=-480&type=video&offset=%s&page=%s' % (self.dynamic_offset, pg)
|
||
rsp = self._get_sth(url)
|
||
jo = json.loads(rsp.text)
|
||
if jo['code'] == 0:
|
||
self.dynamic_offset = jo['data'].get('offset')
|
||
videos = []
|
||
vodList = jo['data']['items']
|
||
for vod in vodList:
|
||
if not vod['visible']:
|
||
continue
|
||
up = vod['modules']["module_author"]['name']
|
||
ivod = vod['modules']['module_dynamic']['major']['archive']
|
||
aid = str(ivod['aid']).strip()
|
||
title = ivod['title'].strip().replace("<em class=\"keyword\">", "").replace("</em>", "")
|
||
img = ivod['cover'].strip()
|
||
# remark = str(ivod['duration_text']).strip()
|
||
remark = str(self.second_to_time(self.str2sec(ivod['duration_text']))).strip() + ' 🆙' + str(
|
||
up).strip() # 显示分钟数+up主名字
|
||
videos.append({
|
||
"vod_id": 'av' + aid,
|
||
"vod_name": title,
|
||
"vod_pic": self.format_img(img),
|
||
"vod_remarks": remark
|
||
})
|
||
result['list'] = videos
|
||
result['page'] = pg
|
||
result['pagecount'] = 9999
|
||
result['limit'] = 99
|
||
result['total'] = 999999
|
||
return result
|
||
else:
|
||
return self.get_up_videos(mid=mid, pg=pg, order=order)
|
||
|
||
def get_found_vod(self, vod):
|
||
aid = vod.get('aid', '')
|
||
if not aid:
|
||
aid = vod.get('id', '')
|
||
goto = vod.get('goto', '')
|
||
if not goto or goto and goto == 'av':
|
||
aid = 'av' + str(aid).strip()
|
||
elif goto == 'ad':
|
||
return []
|
||
title = vod['title'].strip()
|
||
img = vod['pic'].strip()
|
||
is_followed = vod.get('is_followed')
|
||
if goto == 'live':
|
||
room_info = vod['room_info']
|
||
remark = ''
|
||
live_status = room_info.get('live_status', '')
|
||
if live_status:
|
||
remark = '直播中 '
|
||
else:
|
||
return []
|
||
remark += '👁' + room_info['watched_show']['text_small'] + ' 🆙' + vod['owner']['name'].strip()
|
||
else:
|
||
rcmd_reason = vod.get('rcmd_reason', '')
|
||
if rcmd_reason and type(rcmd_reason) == dict and rcmd_reason.get('content'):
|
||
reason= ' 🔥' + rcmd_reason['content'].strip()
|
||
if '人气飙升' in reason:
|
||
reason= ' 🔥人气飙升'
|
||
elif is_followed:
|
||
reason = ' 已关注'
|
||
else:
|
||
#reason = " 💬" + self.zh(vod['stat']['danmaku'])
|
||
reason = ' 🆙' + vod['owner']['name'].strip()
|
||
remark = str(self.second_to_time(vod['duration'])).strip() + " ▶" + self.zh(vod['stat']['view']) + reason
|
||
video = [{
|
||
"vod_id": aid,
|
||
"vod_name": title,
|
||
"vod_pic": self.format_img(img),
|
||
"vod_remarks": remark
|
||
}]
|
||
for v in self.pool.map(self.get_found_vod, vod.get('others', [])):
|
||
video.extend(v)
|
||
return video
|
||
|
||
_popSeriesInit = 0
|
||
|
||
def get_found(self, tid, rid, pg):
|
||
result = {}
|
||
if tid == '推荐':
|
||
query = self.encrypt_wbi(fresh_type=4, feed_version='V3', brush=1, fresh_idx=pg, fresh_idx_1h=pg, ps=self.userConfig['page_size'])
|
||
url = f'https://api.bilibili.com/x/web-interface/wbi/index/top/feed/rcmd?{query}'
|
||
else:
|
||
url = 'https://api.bilibili.com/x/web-interface/ranking/v2?rid={0}&type={1}'.format(rid, tid)
|
||
if tid == '热门':
|
||
url = 'https://api.bilibili.com/x/web-interface/popular?pn={0}&ps={1}'.format(pg, self.userConfig['page_size'])
|
||
elif tid == "入站必刷":
|
||
url = 'https://api.bilibili.com/x/web-interface/popular/precious'
|
||
elif tid == "每周必看":
|
||
if not self._popSeriesInit or int(pg) == 1:
|
||
url = 'https://api.bilibili.com/x/web-interface/popular/series/list'
|
||
rsp = self._get_sth(url, 'fake')
|
||
jo = json.loads(rsp.text)
|
||
number = self._popSeriesInit = jo['data']['list'][0]['number']
|
||
self._popSeriesNum = [int(number), 1]
|
||
else:
|
||
number = self._popSeriesNum[0]
|
||
url = 'https://api.bilibili.com/x/web-interface/popular/series/one?number=' + str(number)
|
||
rsp = self._get_sth(url)
|
||
jo = json.loads(rsp.text)
|
||
if jo['code'] == 0:
|
||
videos = []
|
||
vodList = jo['data'].get('item')
|
||
if not vodList:
|
||
vodList = jo['data']['list']
|
||
if len(vodList) > self.userConfig['page_size']:
|
||
if tid == "每周必看":
|
||
_tmp_pg = int(self._popSeriesNum[1])
|
||
value = len(vodList) / self.userConfig['page_size'] - _tmp_pg
|
||
if value > 0:
|
||
value += 1
|
||
if not int(value):
|
||
self._popSeriesNum = [int(number) - 1, 1]
|
||
else:
|
||
self._popSeriesNum[1] = _tmp_pg + 1
|
||
else:
|
||
_tmp_pg = pg
|
||
vodList = self.pagination(vodList, _tmp_pg)
|
||
for v in self.pool.map(self.get_found_vod, vodList):
|
||
videos.extend(v)
|
||
result['list'] = videos
|
||
result['page'] = pg
|
||
result['pagecount'] = 9999
|
||
result['limit'] = 99
|
||
result['total'] = 999999
|
||
return result
|
||
|
||
def get_bangumi(self, tid, pg, order, season_status):
|
||
result = {}
|
||
if order == '追番剧':
|
||
url = 'https://api.bilibili.com/x/space/bangumi/follow/list?type={0}&vmid={1}&pn={2}&ps={3}'.format(tid, self.userid, pg, self.userConfig['page_size'])
|
||
rsp = self._get_sth(url)
|
||
else:
|
||
url = 'https://api.bilibili.com/pgc/season/index/result?type=1&season_type={0}&page={1}&order={2}&season_status={3}&pagesize={4}'.format(tid, pg, order, season_status, self.userConfig['page_size'])
|
||
if order == '热门':
|
||
if tid == '1':
|
||
url = 'https://api.bilibili.com/pgc/web/rank/list?season_type={0}&day=3'.format(tid)
|
||
else:
|
||
url = 'https://api.bilibili.com/pgc/season/rank/web/list?season_type={0}&day=3'.format(tid)
|
||
rsp = self._get_sth(url, 'fake')
|
||
jo = json.loads(rsp.text)
|
||
if jo['code'] == 0:
|
||
if 'data' in jo:
|
||
vodList = jo['data']['list']
|
||
else:
|
||
vodList = jo['result']['list']
|
||
if len(vodList) > self.userConfig['page_size']:
|
||
vodList = self.pagination(vodList, pg)
|
||
videos = []
|
||
for vod in vodList:
|
||
aid = str(vod['season_id']).strip()
|
||
title = vod['title']
|
||
img = vod.get('ss_horizontal_cover')
|
||
if not img or tid == '1' and not self.userConfig['bangumi_horizontal_cover']:
|
||
if vod.get('first_ep_info') and 'cover' in vod['first_ep_info']:
|
||
img = vod['first_ep_info']['cover']
|
||
elif vod.get('first_ep') and 'cover' in vod['first_ep']:
|
||
img = vod['first_ep']['cover']
|
||
else:
|
||
img = vod['cover'].strip()
|
||
remark = vod.get('index_show', '')
|
||
if not remark and vod.get('new_ep') and vod['new_ep'].get('index_show'):
|
||
remark = vod['new_ep']['index_show']
|
||
remark = remark.replace('更新至', '🆕')
|
||
stat = vod.get('stat')
|
||
if stat:
|
||
remark = '▶' + self.zh(stat.get('view')) + ' ' + remark
|
||
videos.append({
|
||
"vod_id": 'ss' + aid,
|
||
"vod_name": title,
|
||
"vod_pic": self.format_img(img),
|
||
"vod_remarks": remark
|
||
})
|
||
result['list'] = videos
|
||
result['page'] = pg
|
||
result['pagecount'] = 9999
|
||
result['limit'] = 90
|
||
result['total'] = 999999
|
||
return result
|
||
|
||
def get_timeline(self, tid, pg):
|
||
result = {}
|
||
url = 'https://api.bilibili.com/pgc/web/timeline/v2?season_type={0}&day_before=2&day_after=4'.format(tid)
|
||
rsp = self._get_sth(url, 'fake')
|
||
content = rsp.text
|
||
jo = json.loads(content)
|
||
if jo['code'] == 0:
|
||
videos1 = []
|
||
vodList = jo['result']['latest']
|
||
for vod in vodList:
|
||
aid = str(vod['season_id']).strip()
|
||
title = vod['title'].strip()
|
||
img = vod['ep_cover'].strip()
|
||
remark = '🆕' + vod['pub_index'] + ' ❤ ' + vod['follows'].replace('系列', '').replace('追番', '')
|
||
videos1.append({
|
||
"vod_id": 'ss' + aid,
|
||
"vod_name": title,
|
||
"vod_pic": self.format_img(img),
|
||
"vod_remarks": remark
|
||
})
|
||
videos2 = []
|
||
vodList2 = jo['result']['timeline']
|
||
for i in range(len(vodList2)):
|
||
vodList = vodList2[i]['episodes']
|
||
for vod in vodList:
|
||
if str(vod['published']) == "0":
|
||
aid = str(vod['season_id']).strip()
|
||
title = str(vod['title']).strip()
|
||
img = str(vod['ep_cover']).strip()
|
||
date = str(time.strftime("%m-%d %H:%M", time.localtime(vod['pub_ts'])))
|
||
remark = date + " " + vod['pub_index']
|
||
videos2.append({
|
||
"vod_id": 'ss' + aid,
|
||
"vod_name": title,
|
||
"vod_pic": self.format_img(img),
|
||
"vod_remarks": remark
|
||
})
|
||
result['list'] = videos2 + videos1
|
||
result['page'] = 1
|
||
result['pagecount'] = 1
|
||
result['limit'] = 90
|
||
result['total'] = 999999
|
||
return result
|
||
|
||
def get_live(self, pg, parent_area_id, area_id):
|
||
result = {}
|
||
if parent_area_id == '推荐':
|
||
url = 'https://api.live.bilibili.com/xlive/web-interface/v1/webMain/getList?platform=web&page=%s' % pg
|
||
rsp = self._get_sth(url)
|
||
else:
|
||
url = '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' % (parent_area_id, area_id, pg)
|
||
if parent_area_id == '热门':
|
||
url = 'https://api.live.bilibili.com/room/v1/room/get_user_recommend?page=%s&page_size=%s' % (pg, self.userConfig['page_size'])
|
||
rsp = self._get_sth(url, 'fake')
|
||
jo = json.loads(rsp.text)
|
||
if jo['code'] == 0:
|
||
videos = []
|
||
vodList = jo['data']
|
||
if 'recommend_room_list' in vodList:
|
||
vodList = vodList['recommend_room_list']
|
||
elif 'list' in vodList:
|
||
vodList = vodList['list']
|
||
for vod in vodList:
|
||
aid = str(vod['roomid']).strip()
|
||
title = vod['title'].replace("<em class=\"keyword\">", "").replace("</em>", "").replace(""", '"')
|
||
img = vod.get('user_cover')
|
||
if not img:
|
||
img = vod.get('cover')
|
||
remark = '👁' + vod['watched_show']['text_small'].strip() + " 🆙" + vod['uname'].strip()
|
||
videos.append({
|
||
"vod_id": aid,
|
||
"vod_name": title,
|
||
"vod_pic": self.format_img(img),
|
||
"vod_remarks": remark
|
||
})
|
||
result['list'] = videos
|
||
result['page'] = pg
|
||
result['pagecount'] = 9999
|
||
result['limit'] = 99
|
||
result['total'] = 999999
|
||
return result
|
||
|
||
def get_up_series(self, mid, pg):
|
||
result = {}
|
||
url = 'https://api.bilibili.com/x/polymer/web-space/seasons_series_list?mid=%s&page_num=%s&page_size=%s' % (mid, pg, self.userConfig['page_size'])
|
||
rsp = self._get_sth(url, 'fake')
|
||
jo = json.loads(rsp.text)
|
||
if jo['code'] == 0:
|
||
videos = []
|
||
jo = jo['data']['items_lists']
|
||
vodList = jo['seasons_list'] + jo['series_list']
|
||
for vod in vodList:
|
||
vod = vod.get('meta')
|
||
aid = str(vod.get('season_id', '')).strip()
|
||
if aid:
|
||
aid = 'list_' + str(mid) + '_season_' + aid
|
||
else:
|
||
aid = 'list_' + str(mid) + '_series_' + str(vod.get('series_id', '')).strip()
|
||
title = vod['name'].replace("<em class=\"keyword\">", "").replace("</em>", "").replace(""", '"')
|
||
img = vod.get('cover')
|
||
remark = vod.get('description', '').strip()
|
||
videos.append({
|
||
"vod_id": aid,
|
||
"vod_name": title,
|
||
"vod_pic": self.format_img(img),
|
||
"vod_remarks": remark
|
||
})
|
||
result['list'] = videos
|
||
result['page'] = pg
|
||
result['pagecount'] = 9999
|
||
result['limit'] = 99
|
||
result['total'] = 999999
|
||
return result
|
||
|
||
get_up_videos_result = {}
|
||
|
||
def get_up_videos(self, mid, pg, order):
|
||
result = {}
|
||
if not mid.isdigit():
|
||
if int(pg) == 1:
|
||
self.get_up_videos_mid = mid = self.detailContent_args.get('mid', '')
|
||
if not mid in self.get_up_videos_result:
|
||
self.get_up_videos_result.clear()
|
||
self.get_up_videos_result[mid] = []
|
||
else:
|
||
mid = self.get_up_videos_mid
|
||
if int(pg) == 1:
|
||
self.get_up_videoNum_event.clear()
|
||
self.get_up_info_event.clear()
|
||
self.pool.submit(self.get_up_info, mid)
|
||
Space = order2 = ''
|
||
if order == 'oldest':
|
||
order2 = order
|
||
order = 'pubdate'
|
||
elif order == 'quicksearch':
|
||
Space = '投稿: '
|
||
videos = self.get_up_videos_result.get(mid, [])
|
||
if videos:
|
||
result['list'] = videos
|
||
return result
|
||
elif order == 'series':
|
||
return self.get_up_series(mid=mid, pg=pg)
|
||
tmp_pg = pg
|
||
if order2:
|
||
self.get_up_videoNum_event.wait()
|
||
tmp_pg = self.up_info[mid]['vod_pc'] - int(pg) + 1
|
||
query = self.encrypt_wbi(mid=mid, pn=tmp_pg, ps=self.userConfig['page_size'], order=order)
|
||
url = f'https://api.bilibili.com/x/space/wbi/arc/search?{query}'
|
||
rsp = self._get_sth(url, 'fake')
|
||
content = rsp.text
|
||
jo = json.loads(content)
|
||
videos = []
|
||
if jo['code'] == 0:
|
||
vodList = jo['data']['list']['vlist']
|
||
for vod in vodList:
|
||
aid = str(vod['aid']).strip()
|
||
title = vod['title'].strip().replace("<em class=\"keyword\">", "").replace("</em>", "")
|
||
img = vod['pic'].strip()
|
||
remark = self.second_to_time(self.str2sec(str(vod['length']).strip())) + " ▶" + self.zh(vod['play'])
|
||
if not Space:
|
||
remark += " 💬" + self.zh(vod['video_review'])
|
||
videos.append({
|
||
"vod_id": 'av' + aid,
|
||
"vod_name": Space + title,
|
||
"vod_pic": self.format_img(img),
|
||
"vod_remarks": remark
|
||
})
|
||
if order2:
|
||
videos.reverse()
|
||
if int(pg) == 1:
|
||
self.get_up_info_event.wait()
|
||
vodname = self.up_info[mid]['name'] + " 个人主页"
|
||
if Space:
|
||
vodname = 'UP: ' + self.up_info[mid]['name']
|
||
gotoUPHome={
|
||
"vod_id": 'up' + str(mid),
|
||
"vod_name": vodname,
|
||
"vod_pic": self.format_img(self.up_info[mid]['face']),
|
||
"vod_remarks": self.up_info[mid]['following'] + ' 👥' + self.up_info[mid]['fans'] + ' 🎬' + str(self.up_info[mid]['vod_count'])
|
||
}
|
||
videos.insert(0, gotoUPHome)
|
||
if Space:
|
||
self.get_up_videos_result[mid] = videos
|
||
result['list'] = videos
|
||
result['page'] = pg
|
||
result['pagecount'] = 99
|
||
result['limit'] = 99
|
||
result['total'] = 999999
|
||
return result
|
||
|
||
history_view_at = 0
|
||
|
||
def get_history(self, type, pg):
|
||
result = {}
|
||
if int(pg) == 1:
|
||
self.history_view_at = 0
|
||
url = 'https://api.bilibili.com/x/web-interface/history/cursor?ps={0}&view_at={1}&type={2}'.format(self.userConfig['page_size'], self.history_view_at, type)
|
||
if type == '稍后再看':
|
||
url = 'https://api.bilibili.com/x/v2/history/toview'
|
||
rsp = self._get_sth(url)
|
||
jo = json.loads(rsp.text)
|
||
if jo['code'] == 0:
|
||
videos = []
|
||
vodList = jo['data'].get('list', [])
|
||
if type == '稍后再看':
|
||
vodList = self.pagination(vodList, pg)
|
||
else:
|
||
self.history_view_at = jo['data']['cursor']['view_at']
|
||
for vod in vodList:
|
||
history = vod.get('history', '')
|
||
if history:
|
||
business = history['business']
|
||
aid = str(history['oid']).strip()
|
||
img = vod['cover'].strip()
|
||
part = str(history['part']).strip()
|
||
else:
|
||
business = 'archive'
|
||
aid = str(vod["aid"]).strip()
|
||
img = vod['pic'].strip()
|
||
part = str(vod['page']['part']).strip()
|
||
if business == 'article':
|
||
continue
|
||
elif business == 'pgc':
|
||
aid = 'ep' + str(history['epid'])
|
||
_total = vod['total']
|
||
part = vod.get('show_title')
|
||
elif business == 'archive':
|
||
aid = 'av' + aid
|
||
_total = vod['videos']
|
||
title = vod['title'].replace("<em class=\"keyword\">", "").replace("</em>", "").replace(""", '"')
|
||
if business == 'live':
|
||
live_status = vod.get('badge', '')
|
||
remark = live_status + ' 🆙' + vod['author_name'].strip()
|
||
else:
|
||
if str(vod['progress']) == '-1':
|
||
remark = '已看完'
|
||
elif str(vod['progress']) == '0':
|
||
remark = '刚开始看'
|
||
else:
|
||
process = str(self.second_to_time(vod['progress'])).strip()
|
||
remark = '看到 ' + process
|
||
if not _total in [0, 1] and part:
|
||
remark += ' (' + str(part) + ')'
|
||
videos.append({
|
||
"vod_id": aid,
|
||
"vod_name": title,
|
||
"vod_pic": self.format_img(img),
|
||
"vod_remarks": remark
|
||
})
|
||
result['list'] = videos
|
||
result['page'] = pg
|
||
result['pagecount'] = 9999
|
||
result['limit'] = 90
|
||
result['total'] = 999999
|
||
return result
|
||
|
||
def get_fav_detail(self, pg, mlid, order):
|
||
result = {}
|
||
url = '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)
|
||
rsp = self._get_sth(url)
|
||
content = rsp.text
|
||
jo = json.loads(content)
|
||
if jo['code'] == 0:
|
||
videos = []
|
||
vodList = jo['data'].get('medias', [])
|
||
for vod in vodList:
|
||
# 只展示类型为 视频的条目
|
||
# 过滤去掉收藏中的 已失效视频;如果不喜欢可以去掉这个 if条件
|
||
if vod.get('type') in [2] and vod.get('title') != '已失效视频':
|
||
aid = str(vod['id']).strip()
|
||
title = vod['title'].replace("<em class=\"keyword\">", "").replace("</em>", "").replace(""",
|
||
'"')
|
||
img = vod['cover'].strip()
|
||
remark = str(self.second_to_time(vod['duration'])).strip() + " ▶" + self.zh(vod['cnt_info']['play']) + " 💬" + self.zh(vod['cnt_info']['danmaku'])
|
||
videos.append({
|
||
"vod_id": 'av' + aid + '_mlid' + str(mlid),
|
||
"vod_name": title,
|
||
"vod_pic": self.format_img(img),
|
||
"vod_remarks": remark
|
||
})
|
||
# videos=self.filter_duration(videos, duration_diff)
|
||
result['list'] = videos
|
||
result['page'] = pg
|
||
result['pagecount'] = 9999
|
||
result['limit'] = 99
|
||
result['total'] = 999999
|
||
return result
|
||
|
||
get_up_videoNum_event = threading.Event()
|
||
|
||
def get_up_videoNum(self, mid):
|
||
url = "https://api.bilibili.com/x/space/navnum?mid={0}".format(mid)
|
||
rsp = self._get_sth(url)
|
||
jRoot = json.loads(rsp.text)
|
||
if jRoot['code'] == 0:
|
||
return jRoot['data']['video']
|
||
else:
|
||
return 0
|
||
|
||
get_up_info_event = threading.Event()
|
||
up_info = {}
|
||
|
||
def get_up_info(self, mid, **kwargs):
|
||
if mid in self.up_info:
|
||
self.get_up_info_event.set()
|
||
get_up_videoNum = self.pool.submit(self.get_up_videoNum, mid)
|
||
data = kwargs.get('data')
|
||
if not data:
|
||
url = "https://api.bilibili.com/x/web-interface/card?mid={0}".format(mid)
|
||
rsp = self._get_sth(url)
|
||
jRoot = json.loads(rsp.text)
|
||
if jRoot['code'] == 0:
|
||
data = jRoot['data']
|
||
else:
|
||
self.get_up_info_event.set()
|
||
return
|
||
jo = data['card']
|
||
info = {}
|
||
info['following'] = '未关注'
|
||
if data['following']:
|
||
info['following'] = '已关注'
|
||
info['name'] = info['crname'] = jo['name'].replace("<em class=\"keyword\">", "").replace("</em>", "")
|
||
if self.isFongmi:
|
||
info['crname'] = '[a=cr:{"id": "' + mid + '_pubdate_getupvideos","name": "' + info['name'].replace('"', '\\"') + '"}/]' + info['name'] + '[/a]'
|
||
info['face'] = jo['face']
|
||
info['fans'] = self.zh(jo['fans'])
|
||
info['like_num'] = self.zh(data['like_num'])
|
||
info['desc'] = jo['Official']['desc'] + " " + jo['Official']['title']
|
||
info['vod_count'] = str(data['archive_count']).strip()
|
||
self.up_info[mid] = info
|
||
self.get_up_info_event.set()
|
||
self.up_info[mid]['vod_count'] = str(get_up_videoNum.result()).strip()
|
||
pc = divmod(int(info['vod_count']), self.userConfig['page_size'])
|
||
vod_pc = pc[0]
|
||
if pc[1] != 0:
|
||
vod_pc += 1
|
||
self.up_info[mid]['vod_pc'] = vod_pc
|
||
self.get_up_videoNum_event.set()
|
||
|
||
def get_vod_relation(self, id):
|
||
if id.isdigit():
|
||
urlarg = 'aid=' + str(id)
|
||
elif '=' in id:
|
||
urlarg = id
|
||
else:
|
||
urlarg = 'bvid=' + id
|
||
url = 'https://api.bilibili.com/x/web-interface/archive/relation?' + urlarg
|
||
rsp = self._get_sth(url)
|
||
jo = json.loads(rsp.text)
|
||
relation = []
|
||
if jo['code'] == 0:
|
||
jo = jo['data']
|
||
if jo['attention']:
|
||
relation.append('已关注')
|
||
else:
|
||
relation.append('未关注')
|
||
triple = []
|
||
if jo['favorite']:
|
||
triple.append('⭐')
|
||
if jo['like']:
|
||
triple.append('👍')
|
||
coin = jo.get('coin')
|
||
if coin:
|
||
triple.append('💰'*coin)
|
||
if len(triple) == 3:
|
||
relation.append('👍💰⭐')
|
||
else:
|
||
relation.extend(triple)
|
||
if jo['dislike']:
|
||
relation.append('👎')
|
||
if jo['season_fav']:
|
||
relation.append('已订阅合集')
|
||
return relation
|
||
|
||
def get_channel(self, pg, cid, order):
|
||
result = {}
|
||
if str(pg) == '1':
|
||
self.channel_offset = ''
|
||
if order == "featured":
|
||
url = 'https://api.bilibili.com/x/web-interface/web/channel/featured/list?channel_id={0}&filter_type=0&offset={1}&page_size={2}'.format(cid, self.channel_offset, self.userConfig['page_size'])
|
||
else:
|
||
url = 'https://api.bilibili.com/x/web-interface/web/channel/multiple/list?channel_id={0}&sort_type={1}&offset={2}&page_size={3}'.format(cid, order, self.channel_offset, self.userConfig['page_size'])
|
||
rsp = self._get_sth(url, 'master')
|
||
jo = json.loads(rsp.text)
|
||
if jo.get('code') == 0:
|
||
self.channel_offset = jo['data'].get('offset')
|
||
videos = []
|
||
vodList = jo['data']['list']
|
||
if pg == '1' and 'items' in vodList[0]:
|
||
vodList_rank = vodList[0]['items']
|
||
del (vodList[0])
|
||
vodList = vodList_rank + vodList
|
||
for vod in vodList:
|
||
if 'uri' in vod and 'bangumi' in vod['uri']:
|
||
aid = self.find_bangumi_id(vod['uri'])
|
||
else:
|
||
aid = 'av' + str(vod['id']).strip()
|
||
title = vod['name'].replace("<em class=\"keyword\">", "").replace("</em>", "").replace(""", '"')
|
||
img = vod['cover'].strip()
|
||
remark = "▶" + str(vod['view_count'])
|
||
duration = vod.get('duration', '')
|
||
if duration:
|
||
remark = str(self.second_to_time(self.str2sec(duration))).strip() + ' ' + remark
|
||
danmaku = vod.get('danmaku', '')
|
||
like_count = vod.get('like_count', '')
|
||
follow_count = vod.get('follow_count', '')
|
||
if danmaku:
|
||
remark += " 💬" + self.zh(danmaku)
|
||
elif like_count:
|
||
remark += " 👍" + str(like_count)
|
||
elif follow_count:
|
||
remark += " ❤" + str(follow_count)
|
||
videos.append({
|
||
"vod_id": aid,
|
||
"vod_name": title,
|
||
"vod_pic": self.format_img(img),
|
||
"vod_remarks": remark
|
||
})
|
||
result['list'] = videos
|
||
result['page'] = pg
|
||
result['pagecount'] = 9999
|
||
result['limit'] = 99
|
||
result['total'] = 999999
|
||
return result
|
||
|
||
def get_follow(self, pg, sort):
|
||
result = {}
|
||
if sort == "最常访问":
|
||
url = 'https://api.bilibili.com/x/relation/followings?vmid={0}&pn={1}&ps=10&order=desc&order_type=attention' .format(self.userid, pg)
|
||
elif sort == "最近关注":
|
||
url = 'https://api.bilibili.com/x/relation/followings?vmid={0}&pn={1}&ps=10&order=desc&order_type='.format(self.userid, pg)
|
||
elif sort == "正在直播":
|
||
url = 'https://api.live.bilibili.com/xlive/web-ucenter/v1/xfetter/GetWebList?page={0}&page_size=10'.format(pg)
|
||
elif sort == "最近访问":
|
||
url = 'https://api.bilibili.com/x/v2/history?pn={0}&ps=15'.format(pg)
|
||
elif sort == "特别关注":
|
||
url = 'https://api.bilibili.com/x/relation/tag?mid={0}&tagid=-10&pn={1}&ps=10'.format(self.userid, pg)
|
||
elif sort == "悄悄关注":
|
||
url = 'https://api.bilibili.com/x/relation/whispers?pn={0}&ps=10'.format(pg)
|
||
else:
|
||
url = 'https://api.bilibili.com/x/relation/followers?vmid={0}&pn={1}&ps=10&order=desc&order_type=attention'.format(self.userid, pg)
|
||
rsp = self._get_sth(url)
|
||
jo = json.loads(rsp.text)
|
||
if jo['code'] != 0:
|
||
return result
|
||
if sort == "特别关注" or sort == "最近访问":
|
||
vodList = jo['data']
|
||
elif sort == "正在直播":
|
||
vodList = jo['data']['rooms']
|
||
else:
|
||
vodList = jo['data']['list']
|
||
if int(pg) == 1:
|
||
self.recently_up_list = []
|
||
follow = []
|
||
for f in vodList:
|
||
remark = ''
|
||
if sort == "最近访问":
|
||
mid = 'up' + str(f['owner']['mid'])
|
||
if mid in self.recently_up_list:
|
||
continue
|
||
self.recently_up_list.append(mid)
|
||
title = str(f['owner']['name']).strip()
|
||
img = str(f['owner']['face']).strip()
|
||
elif sort == "正在直播":
|
||
mid = str(f['room_id'])
|
||
title = f['title'].replace("<em class=\"keyword\">", "").replace("</em>", "").replace(""", '"')
|
||
img = f['cover_from_user'].strip()
|
||
remark = f['uname'].strip()
|
||
else:
|
||
mid = 'up' + str(f['mid'])
|
||
title = str(f['uname']).strip()
|
||
img = str(f['face']).strip()
|
||
if 'special' in f and f['special'] == 1:
|
||
remark = '特别关注'
|
||
follow.append({
|
||
"vod_id": mid,
|
||
"vod_name": title,
|
||
"vod_pic": self.format_img(img),
|
||
"vod_remarks": remark
|
||
})
|
||
result['list'] = follow
|
||
result['page'] = pg
|
||
result['pagecount'] = 9999
|
||
result['limit'] = 99
|
||
result['total'] = 999999
|
||
return result
|
||
|
||
homeVideoContent_result = {}
|
||
def homeVideoContent(self):
|
||
if not self.homeVideoContent_result:
|
||
videos = self.get_found(rid='0', tid='all', pg=1)['list'][0:int(self.userConfig['maxHomeVideoContent'])]
|
||
self.homeVideoContent_result['list'] = videos
|
||
return self.homeVideoContent_result
|
||
|
||
def categoryContent(self, tid, pg, filter, extend):
|
||
self.stop_heartbeat_event.set()
|
||
if tid == "推荐":
|
||
if 'tid' in extend:
|
||
tid = extend['tid']
|
||
if tid.isdigit():
|
||
tid = int(tid)
|
||
if tid > 10000:
|
||
tid -= 10000
|
||
return self.get_timeline(tid=tid, pg=pg)
|
||
rid = tid
|
||
tid = 'all'
|
||
return self.get_found(tid=tid, rid=rid, pg=pg)
|
||
rid = '0'
|
||
return self.get_found(tid=tid, rid=rid, pg=pg)
|
||
elif tid == "影视":
|
||
tid = '1'
|
||
order = '热门'
|
||
season_status = '-1'
|
||
if 'tid' in extend:
|
||
tid = extend['tid']
|
||
if 'order' in extend:
|
||
order = extend['order']
|
||
if 'season_status' in extend:
|
||
if order == '热门':
|
||
order = '2'
|
||
season_status = extend['season_status']
|
||
return self.get_bangumi(tid, pg, order, season_status)
|
||
elif tid == "动态":
|
||
mid = '0'
|
||
order = 'pubdate'
|
||
if 'mid' in extend:
|
||
mid = extend['mid']
|
||
if 'order' in extend:
|
||
order = extend['order']
|
||
if mid == '0' and not self.userid or mid == '登录':
|
||
return self.get_Login_qrcode(pg)
|
||
return self.get_dynamic(pg=pg, mid=mid, order=order)
|
||
elif tid == '频道':
|
||
order = 'hot'
|
||
cid = random.choice(self.userConfig['channel_list'])
|
||
cid = cid['v']
|
||
if 'order' in extend:
|
||
order = extend['order']
|
||
if 'cid' in extend:
|
||
cid = extend['cid']
|
||
return self.get_channel(pg=pg, cid=cid, order=order)
|
||
elif tid == '直播':
|
||
tid = "热门"
|
||
area_id = '0'
|
||
if 'tid' in extend:
|
||
tid = extend['tid']
|
||
if '_' in tid:
|
||
tids = tid.split('_')
|
||
tid = tids[0]
|
||
area_id = tids[1]
|
||
return self.get_live(pg=pg, parent_area_id=tid, area_id=area_id)
|
||
elif tid == "UP":
|
||
mid = self.detailContent_args.get('mid', '')
|
||
if 'mid' in extend:
|
||
mid = extend['mid']
|
||
if not mid or mid == '登录':
|
||
return self.get_Login_qrcode(pg)
|
||
up_config = self.config["filter"].get('UP')
|
||
if not mid and up_config:
|
||
for i in up_config:
|
||
if i['key'] == 'mid':
|
||
if len(i['value']) > 1:
|
||
mid = i['value'][1]['v']
|
||
break
|
||
order = 'pubdate'
|
||
if 'order' in extend:
|
||
order = extend['order']
|
||
return self.get_up_videos(mid=mid, pg=pg, order=order)
|
||
elif tid == "关注":
|
||
sort = "最常访问"
|
||
if 'sort' in extend:
|
||
sort = extend['sort']
|
||
return self.get_follow(pg, sort)
|
||
elif tid == "收藏":
|
||
mlid = str(self.userConfig['favMode'])
|
||
if 'mlid' in extend:
|
||
mlid = extend['mlid']
|
||
fav_config = self.config["filter"].get('收藏')
|
||
if mlid in ['1', '2']:
|
||
return self.get_bangumi(tid=mlid, pg=pg, order='追番剧', season_status='')
|
||
elif mlid == '0' and fav_config:
|
||
for i in fav_config:
|
||
if i['key'] == 'mlid':
|
||
if len(i['value']) > 1:
|
||
mlid = i['value'][2]['v']
|
||
break
|
||
order = 'mtime'
|
||
if 'order' in extend:
|
||
order = extend['order']
|
||
return self.get_fav_detail(pg=pg, mlid=mlid, order=order)
|
||
elif tid == '历史':
|
||
type = 'all'
|
||
if 'type' in extend:
|
||
type = extend['type']
|
||
if type == 'UP主':
|
||
return self.get_follow(pg=pg, sort='最近访问')
|
||
return self.get_history(type=type, pg=pg)
|
||
elif tid.endswith('_getbangumiseasons'):
|
||
if int(pg) == 1:
|
||
return {'list': self.detailContent_args.get('seasons', [])}
|
||
elif tid.endswith('_getupvideos'):
|
||
mid, order, clicklink = tid.split('_')
|
||
return self.get_up_videos(pg=pg, mid=mid, order=order)
|
||
elif tid.endswith('_clicklink'):
|
||
keyword = tid.replace('_clicklink', '')
|
||
duration_diff = '0'
|
||
if 'duration' in extend:
|
||
duration_diff = extend['duration']
|
||
return self.get_search_content(key=keyword, pg=pg, duration_diff=duration_diff, order='', type='video', ps=self.userConfig['page_size'])
|
||
else:
|
||
duration_diff = '0'
|
||
if 'duration' in extend:
|
||
duration_diff = extend['duration']
|
||
type = 'video'
|
||
if 'type' in extend:
|
||
type = extend['type']
|
||
order = 'totalrank'
|
||
if 'order' in extend:
|
||
order = extend['order']
|
||
keyword = str(self.search_key)
|
||
search_config = self.config["filter"].get('搜索')
|
||
if not keyword and search_config:
|
||
for i in search_config:
|
||
if i['key'] == 'keyword':
|
||
if len(i['value']) > 0:
|
||
keyword = i['value'][0]['v']
|
||
break
|
||
if 'keyword' in extend:
|
||
keyword = extend['keyword']
|
||
return self.get_search_content(key=keyword, pg=pg, duration_diff=duration_diff, order=order, type=type, ps=self.userConfig['page_size'])
|
||
|
||
def get_search_content(self, key, pg, duration_diff, order, type, ps):
|
||
value = None
|
||
if not pg.isdigit():
|
||
value = pg
|
||
pg = 1
|
||
query = self.encrypt_wbi(keyword=key, page=pg, duration=duration_diff, order=order, search_type=type, page_size=ps)
|
||
url = f'https://api.bilibili.com/x/web-interface/wbi/search/type?{query}'
|
||
rsp = self._get_sth(url, 'fake')
|
||
content = rsp.text
|
||
jo = json.loads(content)
|
||
result = {}
|
||
if jo.get('code') == 0 and 'result' in jo['data']:
|
||
videos = []
|
||
vodList = jo['data'].get('result')
|
||
if vodList and type == 'live':
|
||
vodList = vodList.get('live_room')
|
||
if not vodList:
|
||
return result
|
||
for vod in vodList:
|
||
if type != vod['type']:
|
||
continue
|
||
title = ''
|
||
if type == 'bili_user':
|
||
aid = 'up' + str(vod['mid']).strip()
|
||
img = vod['upic'].strip()
|
||
remark = '👥' + self.zh(vod['fans']) + " 🎬" + self.zh(vod['videos'])
|
||
title = vod['uname']
|
||
elif type == 'live':
|
||
aid = str(vod['roomid']).strip()
|
||
img = vod['cover'].strip()
|
||
remark = '👁' + self.zh(vod['online']) + ' 🆙' + vod['uname']
|
||
elif 'media' in type:
|
||
aid = str(vod['season_id']).strip()
|
||
if self.detailContent_args:
|
||
seasons = self.detailContent_args.get('seasons')
|
||
if seasons:
|
||
bangumi_seasons_id = []
|
||
for ss in self.detailContent_args['seasons']:
|
||
bangumi_seasons_id.append(ss['vod_id'])
|
||
if aid + 'ss' in bangumi_seasons_id:
|
||
continue
|
||
aid = 'ss' + aid
|
||
img = vod['cover'].strip()
|
||
remark = str(vod['index_show']).strip().replace('更新至', '🆕')
|
||
else:
|
||
aid = 'av' + str(vod['aid']).strip()
|
||
img = vod['pic'].strip()
|
||
remark = str(self.second_to_time(self.str2sec(vod['duration']))).strip() + " ▶" + self.zh(vod['play'])
|
||
if value == None:
|
||
remark += " 💬" + self.zh(vod['danmaku'])
|
||
if not title:
|
||
title = vod['title'].replace("<em class=\"keyword\">", "").replace("</em>", "").replace(""",'"').replace('&', '&')
|
||
if value:
|
||
title = value + title
|
||
videos.append({
|
||
"vod_id": aid,
|
||
"vod_name": title,
|
||
"vod_pic": self.format_img(img),
|
||
"vod_remarks": remark
|
||
})
|
||
result['list'] = videos
|
||
result['page'] = pg
|
||
result['pagecount'] = 9999
|
||
result['limit'] = 99
|
||
result['total'] = 999999
|
||
return result
|
||
|
||
def cleanSpace(self, str):
|
||
return str.replace('\n', '').replace('\t', '').replace('\r', '').replace(' ', '')
|
||
|
||
def get_normal_episodes(self, episode):
|
||
ssid = epid = ''
|
||
aid = episode.get('aid', '')
|
||
if not aid:
|
||
aid = self.detailContent_args['aid']
|
||
cid = episode.get('cid', '')
|
||
ep_title = episode.get('title', '')
|
||
if not ep_title:
|
||
ep_title = episode.get('part', '')
|
||
duration = episode.get('duration', '')
|
||
if not duration:
|
||
page = episode.get('page', '')
|
||
if page:
|
||
duration = page['duration']
|
||
badge = long_title = preview = parse = ''
|
||
ssid = self.detailContent_args.get('ssid', '')
|
||
if ssid:
|
||
ssid = '_ss' + ssid
|
||
epid = episode.get('id', '')
|
||
if epid:
|
||
epid = '_ep' + str(epid)
|
||
if duration and str(duration).endswith('000'):
|
||
duration = int(duration / 1000)
|
||
if ep_title.isdigit():
|
||
ep_title = '第' + ep_title + self.detailContent_args['title_type']
|
||
badge = episode.get('badge', '')
|
||
if not self.session_vip.cookies and badge == '会员' and self.userConfig['bangumi_vip_parse'] or badge == '付费' and self.userConfig['bangumi_pay_parse']:
|
||
parse = '_parse'
|
||
if self.session_vip.cookies and self.userConfig['hide_bangumi_vip_badge']:
|
||
badge = badge.replace('会员', '')
|
||
if self.userConfig['hide_bangumi_preview'] and badge == '预告':
|
||
badge = badge.replace('预告', '')
|
||
preview = 1
|
||
if badge:
|
||
badge = '【' + badge + '】'
|
||
long_title = episode.get('long_title', '')
|
||
if not badge and long_title:
|
||
long_title = ' ' + long_title
|
||
title = ep_title + badge + long_title
|
||
title = title.replace("#", "﹟").replace("$", "﹩")
|
||
if duration:
|
||
duration = '_dur' + str(duration)
|
||
url = '{0}${1}_{2}{3}{4}{5}'.format(title, aid, cid, ssid, epid, duration)
|
||
fromep = self.detailContent_args.get('fromep', '')
|
||
if '_' + str(fromep) == epid:
|
||
self.detailContent_args['fromep'] = url
|
||
replyList = self.detailContent_args.get('Reply')
|
||
if '_' + str(fromep) == epid or not fromep and replyList == None:
|
||
self.detailContent_args['Reply'] = ''
|
||
if self.userConfig['show_vod_hot_reply']:
|
||
self.get_vod_hot_reply_event.clear()
|
||
self.pool.submit(self.get_vod_hot_reply, aid)
|
||
if ssid:
|
||
if preview:
|
||
return url, ''
|
||
if parse:
|
||
self.detailContent_args['parse'] = 1
|
||
if long_title:
|
||
long_title = '【解析】' + long_title
|
||
ep_title += long_title
|
||
parseurl = '{0}${1}_{2}{3}{4}{5}{6}'.format(ep_title, aid, cid, ssid, epid, duration, parse)
|
||
if '_' + str(fromep) == epid:
|
||
self.detailContent_args['fromep'] += '#' + parseurl
|
||
else:
|
||
parseurl = url
|
||
return url, parseurl
|
||
else:
|
||
return url
|
||
|
||
def get_ugc_season(self, section, sections_len):
|
||
if sections_len > 1:
|
||
sec_title = self.detailContent_args['season_title'] + ' ' + section['title']
|
||
else:
|
||
sec_title = self.detailContent_args['season_title']
|
||
sec_title = sec_title.replace("#", "﹟").replace("$", "﹩")
|
||
episodes = section.get('episodes')
|
||
playUrl = '#'.join(map(self.get_normal_episodes, episodes))
|
||
result = (sec_title, playUrl)
|
||
return result
|
||
|
||
get_vod_hot_reply_event = threading.Event()
|
||
|
||
def get_vod_hot_reply(self, oid):
|
||
query = self.encrypt_wbi(type=1, ps=30, oid=str(oid))
|
||
url = f'http://api.bilibili.com/x/v2/reply/main?{query}'
|
||
rsp = self._get_sth(url, 'fake')
|
||
jRoot = json.loads(rsp.text)
|
||
if jRoot['code'] == 0:
|
||
replies = jRoot['data'].get('replies')
|
||
top_replies = jRoot['data'].get('top_replies')
|
||
if replies and top_replies:
|
||
replies = top_replies + replies
|
||
if replies:
|
||
up_mid = jRoot['data']['upper']['mid']
|
||
ReplyList = []
|
||
Reply_jump = []
|
||
for r in replies:
|
||
rpid = r['rpid']
|
||
sex = r['member']['sex']
|
||
if sex and sex == '女':
|
||
sex = '👧'
|
||
else:
|
||
sex = '👦'
|
||
name = sex + r['member']['uname'] + ':'
|
||
mid = r['mid']
|
||
if mid == up_mid:
|
||
name = '🆙' + name
|
||
like = '👍' + self.zh(r['like'])
|
||
message = r['content']['message']
|
||
if '/note-app/' in message:
|
||
continue
|
||
content = like + ' ' + name + message
|
||
content = content.replace("#", "﹟").replace("$", "﹩")
|
||
content += '$' + str(oid) + '_' + str(rpid) + '_notplay_reply'
|
||
ReplyList.append(content)
|
||
jump_url = r['content'].get('jump_url',{})
|
||
for key, value in jump_url.items():
|
||
if not value.get('app_url_schema') and not value.get('pc_url'):
|
||
if key.startswith('https://www.bilibili.com/'):
|
||
key = str(key).split('?')[0].split('/')
|
||
while key[-1] == '':
|
||
key.pop(-1)
|
||
key = key[-1]
|
||
if key.startswith('https://b23.tv/') or key.startswith('BV') or key.startswith('ep') or key.startswith('ss'):
|
||
title = str(value['title']).replace("#", "﹟").replace("$", "﹩")
|
||
vod = {'vod_id': str(key), 'vod_name': '评论:' + title}
|
||
if not vod in Reply_jump:
|
||
Reply_jump.append(vod)
|
||
title = '快搜:' + str(key) +' ' + title
|
||
content = title + '$ '
|
||
ReplyList.append(content)
|
||
self.detailContent_args['Reply'] = '#'.join(ReplyList)
|
||
self.detailContent_args['Reply_jump'] = Reply_jump
|
||
self.get_vod_hot_reply_event.set()
|
||
|
||
detailContent_args = {}
|
||
|
||
def detailContent(self, array):
|
||
self.stop_heartbeat_event.set()
|
||
aid = array[0]
|
||
if aid.startswith('edgeid'):
|
||
return self.interaction_detailContent(aid)
|
||
elif aid.startswith('list'):
|
||
return self.series_detailContent(aid)
|
||
self.detailContent_args = {}
|
||
if aid.startswith('https://b23.tv/'):
|
||
try:
|
||
r = requests_get(url=aid, headers=self.header, allow_redirects=False)
|
||
url = r.headers['Location'].split('?')[0].split('/')
|
||
while url[-1] == '':
|
||
url.pop(-1)
|
||
aid = url[-1]
|
||
if not aid.startswith('BV', 0, 2):
|
||
return {}
|
||
except:
|
||
return {}
|
||
id = mlid = urlargs = ''
|
||
self.get_vod_hot_reply_event.set()
|
||
if aid.startswith('setting'):
|
||
aid = aid.split('_')
|
||
if aid[1] == 'tab&filter':
|
||
return self.setting_tab_filter_detailContent()
|
||
elif aid[1] == 'liveExtra':
|
||
return self.setting_liveExtra_detailContent()
|
||
elif aid[1] == 'login':
|
||
key = aid[2]
|
||
return self.setting_login_detailContent(key)
|
||
elif aid.startswith('av') or aid.startswith('BV'):
|
||
for i in aid.split('_'):
|
||
if i.startswith('av'):
|
||
id = i.replace('av', '', 1)
|
||
urlargs = 'aid=' + str(id)
|
||
elif i.startswith('BV'):
|
||
id = i
|
||
urlargs = 'bvid=' + id
|
||
elif i.startswith('mlid'):
|
||
mlid = i.replace('mlid', '', 1)
|
||
#获取热门评论
|
||
if self.userConfig['show_vod_hot_reply']:
|
||
self.detailContent_args['Reply'] = ''
|
||
self.get_vod_hot_reply_event.clear()
|
||
self.pool.submit(self.get_vod_hot_reply, id)
|
||
elif 'up' in aid:
|
||
return self.up_detailContent(array)
|
||
elif 'ss' in aid or 'ep' in aid:
|
||
return self.ysContent(array)
|
||
elif aid.isdigit():
|
||
return self.live_detailContent(array)
|
||
relation = self.pool.submit(self.get_vod_relation, urlargs)
|
||
url = 'https://api.bilibili.com/x/web-interface/view/detail?' + urlargs
|
||
rsp = self._get_sth(url, 'fake')
|
||
jRoot = json.loads(rsp.text)
|
||
if jRoot['code'] != 0:
|
||
return {}
|
||
jo = jRoot['data']['View']
|
||
redirect_url = jo.get('redirect_url', '')
|
||
if 'bangumi' in redirect_url:
|
||
ep_id = self.find_bangumi_id(redirect_url)
|
||
new_array = []
|
||
for i in array:
|
||
new_array.append(i)
|
||
new_array[0] = ep_id
|
||
return self.ysContent(new_array)
|
||
self.detailContent_args['mid'] = up_mid = str(jo['owner']['mid'])
|
||
self.detailContent_args['aid'] = aid = jo.get('aid')
|
||
self.pool.submit(self.get_up_info, mid=up_mid, data=jRoot['data'].get('Card'))
|
||
#相关合集
|
||
ugc_season = jo.get('ugc_season')
|
||
if ugc_season:
|
||
self.detailContent_args['season_title'] = ugc_season['title']
|
||
sections = ugc_season['sections']
|
||
sections_len = len(sections)
|
||
ugc_season_task = []
|
||
for section in sections:
|
||
t = self.pool.submit(self.get_ugc_season, section, sections_len)
|
||
ugc_season_task.append(t)
|
||
#相关推荐
|
||
jo_Related = jRoot['data'].get('Related')
|
||
#正片
|
||
pages = jo['pages']
|
||
title = jo['title'].replace("<em class=\"keyword\">", "").replace("</em>", "")
|
||
pic = jo['pic']
|
||
desc = jo['desc'].strip()
|
||
typeName = jo['tname']
|
||
date = time.strftime("%Y%m%d", time.localtime(jo['pubdate'])) # 投稿时间本地年月日表示
|
||
stat = jo['stat']
|
||
# 演员项展示视频状态,包括以下内容:
|
||
remark = []
|
||
remark.append('▶' + self.zh(stat['view']))
|
||
remark.append('💬' + self.zh(stat['danmaku']))
|
||
remark.append('👍' + self.zh(stat['like']))
|
||
remark.append('💰' + self.zh(stat['coin']))
|
||
remark.append('⭐' + self.zh(stat['favorite']))
|
||
_is_stein_gate = jo['rights'].get('is_stein_gate', 0)
|
||
vod = {
|
||
"vod_id": 'av' + str(aid),
|
||
"vod_name": title,
|
||
"vod_pic": pic,
|
||
"type_name": typeName,
|
||
"vod_year": date,
|
||
"vod_content": desc
|
||
}
|
||
if self.isFongmi:
|
||
vod['vod_remarks'] = " ".join(remark)
|
||
vod_tags = ', '.join(sorted(map(lambda x: '[a=cr:{"id": "' + x['tag_name'].replace('"', '\\"') + '_clicklink","name":"' + x['tag_name'].replace('"', '\\"') + '"}/]' + x['tag_name'] + '[/a]', jRoot['data'].get('Tags', [])), key=len))
|
||
vod['vod_content'] = vod_tags + ' \n' + desc
|
||
self.userConfig['show_vod_hot_reply'] = False
|
||
else:
|
||
vod['vod_actor'] = " ".join(remark)
|
||
vod['vod_area'] = "bilidanmu"
|
||
secondP = []
|
||
if self.userid:
|
||
#做点什么
|
||
follow = '➕关注$1_notplay_follow'
|
||
unfollow = '➖取关$2_notplay_follow'
|
||
like = '👍点赞$1_notplay_like'
|
||
unlike = '👍🏻取消点赞$2_notplay_like'
|
||
coin1 = '👍💰投币$1_notplay_coin'
|
||
coin2 = '👍💰💰$2_notplay_coin'
|
||
triple = '👍💰⭐三连$notplay_triple'
|
||
secondPList = [follow, triple, like, coin1, coin2, unfollow, unlike]
|
||
if mlid:
|
||
favdel = f"☆取消收藏${mlid}_del_notplay_fav"
|
||
secondPList.append(favdel)
|
||
for fav in self.userConfig.get("fav_list", []):
|
||
folder = fav['n'].replace("#", "﹟").replace("$", "﹩")
|
||
ids = fav['v']
|
||
fav = '⭐{}${}_add_notplay_fav'.format(folder, ids)
|
||
secondPList.append(fav)
|
||
defaultQn = int(self.userConfig['vodDefaultQn'])
|
||
if defaultQn > 116:
|
||
secondPList.append('⚠️限高1080$116_notplay_vodTMPQn')
|
||
secondP = ['#'.join(secondPList)]
|
||
AllPt = []
|
||
AllPu = []
|
||
if pages:
|
||
AllPt = ['B站']
|
||
if _is_stein_gate:
|
||
AllPt = ['互动视频【快搜继续】']
|
||
AllPu = ['#'.join(self.pool.map(self.get_normal_episodes, pages))]
|
||
if secondP:
|
||
AllPt.append('做点什么')
|
||
AllPu.extend(secondP)
|
||
if jo_Related:
|
||
AllPt.append('相关推荐')
|
||
AllPu.append('#'.join(self.pool.map(self.get_normal_episodes, jo_Related)))
|
||
if self.userConfig['show_vod_hot_reply']:
|
||
self.get_vod_hot_reply_event.wait()
|
||
replyList = self.detailContent_args.get('Reply', '')
|
||
if replyList:
|
||
AllPt.append('热门评论')
|
||
AllPu.extend([replyList])
|
||
if ugc_season:
|
||
for t in as_completed(ugc_season_task):
|
||
AllPt.append(t.result()[0])
|
||
AllPu.append(t.result()[1])
|
||
vod['vod_play_from'] = "$$$".join(AllPt)
|
||
vod['vod_play_url'] = "$$$".join(AllPu)
|
||
#视频关系
|
||
vod['vod_director'] = '🆙 ' + self.up_info[up_mid]['crname'] + ' 👥 ' + self.up_info[up_mid]['fans'] + ' ' + ' '.join(relation.result())
|
||
#互动视频套用
|
||
if _is_stein_gate:
|
||
self.detailContent_args['AllPt'] = AllPt.copy()
|
||
self.detailContent_args['AllPu'] = AllPu.copy()
|
||
self.detailContent_args['vod_list'] = vod.copy()
|
||
result = {
|
||
'list': [
|
||
vod
|
||
]
|
||
}
|
||
return result
|
||
|
||
def series_detailContent(self, array):
|
||
mid, type, sid = array.replace('list_', '').split('_')
|
||
pg = 1
|
||
ps = 99
|
||
vod = {"vod_id": array, "vod_area": "bilidanmu", "vod_play_from": "B站"}
|
||
urlL = []
|
||
while True:
|
||
if type == 'season':
|
||
url = 'https://api.bilibili.com/x/polymer/web-space/seasons_archives_list?mid=%s&season_id=%s&page_num=%s&page_size=%s' % (mid, sid, pg, ps)
|
||
else:
|
||
url = 'https://api.bilibili.com/x/series/archives?mid=%s&series_id=%s&pn=%s&ps=%s' % (mid, sid, pg, ps)
|
||
rsp = self._get_sth(url, 'fake')
|
||
jo = json.loads(rsp.text)
|
||
data = jo.get('data')
|
||
if not vod.get("vod_name"):
|
||
if data.get('meta'):
|
||
vod["vod_name"] = data['meta']['name']
|
||
vod["vod_pic"] = data['meta']['cover']
|
||
vod["vod_content"] = data['meta']['description']
|
||
else:
|
||
vod["vod_name"] = data['archives'][0]['title']
|
||
playUrl = '#'.join(map(self.get_normal_episodes, data.get('archives')))
|
||
urlL.append(playUrl)
|
||
total = data['page']['total']
|
||
if (ps * pg) >= total:
|
||
break
|
||
pg += 1
|
||
vod['vod_play_url'] = '#'.join(urlL)
|
||
result = {
|
||
'list': [
|
||
vod
|
||
]
|
||
}
|
||
return result
|
||
|
||
def interaction_detailContent(self, array=''):
|
||
array = array.split('_')
|
||
cid = edgeid = 0
|
||
for i in array:
|
||
if i.startswith('edgeid'):
|
||
edgeid = i.replace('edgeid', '')
|
||
elif i.startswith('cid'):
|
||
cid = i.replace('cid', '')
|
||
aid = self.detailContent_args.get('aid')
|
||
graph_version = self.detailContent_args.get('graph_version')
|
||
url = 'https://api.bilibili.com/x/stein/edgeinfo_v2?aid={0}&graph_version={1}&edge_id={2}'.format(aid, graph_version, edgeid)
|
||
rsp = self._get_sth(url, 'fake')
|
||
jo = json.loads(rsp.text)
|
||
data = jo.get('data')
|
||
result = {}
|
||
if data:
|
||
questions = data['edges'].get('questions', [])
|
||
choice_lis = []
|
||
for question in questions:
|
||
q_title = str(question.get('title', ''))
|
||
if q_title:
|
||
q_title += ' '
|
||
for choice in question.get('choices', []):
|
||
c_edgeid = str(choice['id'])
|
||
c_cid = str(choice['cid'])
|
||
option = str(choice.get('option', ''))
|
||
choice_lis.append({
|
||
"vod_id": 'edgeid' + c_edgeid + '_' + 'cid' + c_cid,
|
||
"vod_name": '互动:' + q_title + option,
|
||
})
|
||
self.detailContent_args['interaction'] = choice_lis.copy()
|
||
if edgeid:
|
||
AllPt = self.detailContent_args['AllPt'].copy()
|
||
if not choice_lis:
|
||
AllPt[0] = '互动视频'
|
||
AllPu = self.detailContent_args['AllPu'].copy()
|
||
title = str(data['title']).replace("#", "﹟").replace("$", "﹩")
|
||
url = '{0}${1}_{2}'.format(title, aid, cid)
|
||
AllPu[0] = url
|
||
vod = self.detailContent_args['vod_list'].copy()
|
||
vod['vod_play_from'] = "$$$".join(AllPt)
|
||
vod['vod_play_url'] = "$$$".join(AllPu)
|
||
result['list'] = [vod]
|
||
return result
|
||
|
||
def up_detailContent(self, array):
|
||
self.detailContent_args['mid'] = mid = array[0].replace('up', '')
|
||
self.get_up_info_event.clear()
|
||
self.pool.submit(self.get_up_info, mid)
|
||
first = '是否关注$ '
|
||
follow = '关注$1_notplay_follow'
|
||
unfollow = '取消关注$2_notplay_follow'
|
||
qqfollow = '悄悄关注$3_notplay_follow'
|
||
spfollow = '特别关注$-10_notplay_special_follow'
|
||
unspfollow = '取消特别关注$0_notplay_special_follow'
|
||
doWhat = [first, follow, qqfollow, spfollow, unfollow, unspfollow]
|
||
doWhat = '#'.join(doWhat)
|
||
self.get_up_info_event.wait()
|
||
up_info = self.up_info[mid]
|
||
vod = {
|
||
"vod_id": 'up' + str(mid),
|
||
"vod_name": up_info['name'] + " 个人主页",
|
||
"vod_pic": up_info['face'],
|
||
"vod_director": '🆙 ' + up_info['name'] + " " + up_info['following'] + ' UID:' + str(mid),
|
||
"vod_content": up_info['desc'],
|
||
'vod_play_from': '关注TA$$$视频投稿在动态标签——筛选——上个UP,选择后查看',
|
||
'vod_play_url': doWhat
|
||
}
|
||
remark = "👥 " + up_info['fans'] + " 🎬 " + up_info['vod_count'] + " 👍 " + up_info['like_num']
|
||
if self.isFongmi:
|
||
vod["vod_remarks"] = remark
|
||
tabfilter = self.config['filter'].get('动态')
|
||
if not tabfilter:
|
||
tabfilter = self.config['filter'].get('UP', [])
|
||
vod["vod_actor"] = ' '.join(map(lambda x: '[a=cr:{"id": "' + str(mid) + '_' + x['v'] +'_getupvideos","name": "' + up_info['name'].replace('"', '\\"') + ' ' + x['n'] + '"}/]' + x['n'] + '[/a]', tabfilter[-1]['value']))
|
||
else:
|
||
vod["vod_actor"] = remark
|
||
result = {
|
||
'list': [
|
||
vod
|
||
]
|
||
}
|
||
return result
|
||
|
||
def setting_login_detailContent(self, key):
|
||
cookie_dic_tmp = self.cookie_dic_tmp.get(key, '')
|
||
message = ''
|
||
if not cookie_dic_tmp:
|
||
message = self.get_cookies(key)
|
||
if message:
|
||
message = f"【{message}】通过手机客户端扫码确认登录后点击相应按钮设置账号"
|
||
else:
|
||
message = '【已扫码并确认登录】请点击相应按钮设置当前获取的账号为:'
|
||
vod = {
|
||
"vod_name": "登录与设置",
|
||
"vod_content": '通过手机客户端扫码并确认登录后,点击相应按钮设置cookie,设置后不需要管嗅探结果,直接返回二维码页面刷新,查看是否显示已登录,已登录即可重新打开APP以加载全部标签',
|
||
}
|
||
vod_play_from = ['登录$$$退出登录']
|
||
vod_play_url = []
|
||
first = message + '$ '
|
||
login = '设置为主账号,动态收藏关注等内容源于此$' + str(key) + '_master_login_setting'
|
||
login_vip = '设置为备用的VIP账号,仅用于播放会员番剧$' + str(key) + '_vip_login_setting'
|
||
vod_play_url.append('#'.join([first, login, login_vip]))
|
||
second = '点击相应按钮退出账号>>>$ '
|
||
logout = '退出主账号$master_logout_setting'
|
||
logout_vip = '退出备用的VIP账号$vip_logout_setting'
|
||
vod_play_url.append('#'.join([second, logout, logout_vip]))
|
||
cate_lis = [{
|
||
'f': '主页站点推荐栏',
|
||
'c': 'maxHomeVideoContent',
|
||
'd': {
|
||
'3': '3图',
|
||
'4': '4图',
|
||
'6': '6图',
|
||
'8': '8图',
|
||
'10': '10图',
|
||
}
|
||
},{
|
||
'f': '视频画质',
|
||
'c': 'vodDefaultQn',
|
||
'd': self.vod_qn_id
|
||
},{
|
||
'f': '视频编码',
|
||
'c': 'vodDefaultCodec',
|
||
'd': self.vod_codec_id
|
||
},{
|
||
'f': '音频码率',
|
||
'c': 'vodDefaultAudio',
|
||
'd': self.vod_audio_id
|
||
},{
|
||
'f': '收藏默认显示',
|
||
'c': 'favMode',
|
||
'd': {
|
||
'0': '默认收藏夹',
|
||
'1': '追番',
|
||
'2': '追剧',
|
||
}
|
||
},{
|
||
|
||
'f': '上传播放进度',
|
||
'c': 'heartbeatInterval',
|
||
'd': {
|
||
'0': '关',
|
||
'15': '开',
|
||
}
|
||
},{
|
||
|
||
'f': '直播筛选细化',
|
||
'c': 'showLiveFilterTag',
|
||
'd': {
|
||
'0': '关',
|
||
'1': '开',
|
||
}
|
||
}]
|
||
for cate in cate_lis:
|
||
vod_play_from.append(cate['f'])
|
||
defaultConfig = cate['d'][str(int(self.userConfig[cate['c']]))]
|
||
if 'vodDefaultAudio' == cate['c']:
|
||
defaultConfig = str(defaultConfig).replace('000', 'k')
|
||
url = ['当前:' + defaultConfig + '$ ']
|
||
for id, name in cate['d'].items():
|
||
if 'vodDefaultAudio' == cate['c']:
|
||
name = str(name).replace('000', 'k')
|
||
url.append(name + '$' + str(id) + '_' + cate['c'] + '_setting')
|
||
vod_play_url.append('#'.join(url))
|
||
vod['vod_play_from'] = '$$$'.join(vod_play_from)
|
||
vod['vod_play_url'] = '$$$'.join(vod_play_url)
|
||
result = {
|
||
'list': [
|
||
vod
|
||
]
|
||
}
|
||
return result
|
||
|
||
def setting_tab_filter_detailContent(self):
|
||
vod = {
|
||
"vod_name": "标签与筛选",
|
||
"vod_content": '依次点击各标签,同一标签第一次点击为添加,第二次删除,可以返回到二维码页后重进本页查看预览,最后点击保存,未选择的将追加到末尾,如果未保存就重启app,将丢失未保存的配置',
|
||
}
|
||
vod_play_from = []
|
||
vod_play_url = []
|
||
cate_lis = [
|
||
{'n': 'cateManual', 'v': '标签'},
|
||
{'n': 'tuijianLis', 'v': '推荐[分区]'},
|
||
{'n': 'rankingLis', 'v': '推荐[排行榜]'},
|
||
{'n': 'cateManualLive', 'v': '直播'},
|
||
]
|
||
for cate in cate_lis:
|
||
_List = cate['n']
|
||
vod_play_from.append(cate['v'])
|
||
List_tmp = self.userConfig.get(str(_List) + '_tmp', [])
|
||
status = ''
|
||
if List_tmp:
|
||
status = '【未保存】'
|
||
else:
|
||
List_tmp = self.userConfig.get(_List, [])
|
||
if not List_tmp:
|
||
List_tmp = self.defaultConfig.get(_List)
|
||
if List_tmp and type(List_tmp[0]) == dict:
|
||
List_tmp = list(map(lambda x:x['n'], List_tmp))
|
||
url = ['当前: ' + ','.join(List_tmp) + '$ ', f"{status}点击这里保存$_{_List}_save_setting", f"点击这里恢复默认并保存$_{_List}_clear_setting"]
|
||
defaultConfig = self.defaultConfig[_List].copy()
|
||
if _List == 'cateManual' and not 'UP' in defaultConfig:
|
||
defaultConfig.append('UP')
|
||
elif _List == 'cateManualLive':
|
||
extra_live_filter = self.userConfig.get('cateManualLiveExtra', [])
|
||
defaultConfig.extend(extra_live_filter.copy())
|
||
for name in defaultConfig:
|
||
value = str(name)
|
||
if type(name) == dict:
|
||
value = name['n'] + '@@@' + name['v'].replace('_', '@@@')
|
||
name = name['n']
|
||
url.append(f"{name}${value}_{_List}_setting")
|
||
vod_play_url.append('#'.join(url))
|
||
vod['vod_play_from'] = '$$$'.join(vod_play_from)
|
||
vod['vod_play_url'] = '$$$'.join(vod_play_url)
|
||
result = {
|
||
'list': [
|
||
vod
|
||
]
|
||
}
|
||
return result
|
||
|
||
def setting_liveExtra_detailContent(self):
|
||
vod = {
|
||
"vod_name": "查看直播细化标签",
|
||
"vod_content": '点击想要添加的标签,同一标签第一次点击为添加,第二次删除,完成后在[标签与筛选]页继续操作,以添加到直播筛选分区列中',
|
||
}
|
||
vod_play_from = ['已添加']
|
||
cateManualLiveExtra = self.userConfig.get('cateManualLiveExtra', [])
|
||
vod_play_url = ['点击相应标签(只)可以删除$ #清空$clear_liveFilter_setting']
|
||
for name in cateManualLiveExtra:
|
||
value = name['v']
|
||
name = name['n']
|
||
vod_play_url.append(name + '$' + 'del_' + name + '_' + value + '_liveFilter_setting')
|
||
vod_play_url = ['#'.join(vod_play_url)]
|
||
cateLive = self.userConfig.get('cateLive', {})
|
||
for parent, parent_dic in cateLive.items():
|
||
area_dic = parent_dic['value']['value']
|
||
if len(area_dic) == 1:
|
||
continue
|
||
vod_play_from.append(parent)
|
||
url = []
|
||
for area in area_dic:
|
||
name = str(area['n']).replace('_', '-').replace("#", "﹟").replace("$", "﹩")
|
||
id = str(area['v']).replace('_', '@@@').replace("#", "﹟").replace("$", "﹩")
|
||
url.append(name + '$add_' + name + '_' + id + '_liveFilter_setting')
|
||
vod_play_url.append('#'.join(url))
|
||
vod['vod_play_from'] = '$$$'.join(vod_play_from)
|
||
vod['vod_play_url'] = '$$$'.join(vod_play_url)
|
||
result = {
|
||
'list': [
|
||
vod
|
||
]
|
||
}
|
||
return result
|
||
|
||
def get_all_season(self, season):
|
||
season_id = str(season['season_id'])
|
||
season_title = season['season_title']
|
||
if season_id == self.detailContent_args['ssid']:
|
||
self.detailContent_args['s_title'] = season_title
|
||
pic = season['cover']
|
||
remark = season['new_ep']['index_show']
|
||
result = {
|
||
"vod_id": season_id + 'ss',
|
||
"vod_name": '系列:' + season_title,
|
||
"vod_pic": self.format_img(pic),
|
||
"vod_remarks": remark}
|
||
return result
|
||
|
||
def get_bangumi_section(self, section):
|
||
sec_title = section['title'].replace("#", "﹟").replace("$", "﹩")
|
||
sec_type = section['type']
|
||
if sec_type in [1, 2] and len(section['episode_ids']) == 0:
|
||
episodes = section['episodes']
|
||
playUrl = '#'.join(map(lambda x: self.get_normal_episodes(x)[0], episodes))
|
||
return (sec_title, playUrl)
|
||
|
||
def ysContent(self, array):
|
||
aid = array[0]
|
||
if 'ep' in aid:
|
||
self.detailContent_args['fromep'] = aid
|
||
aid = 'ep_id=' + aid.replace('ep', '')
|
||
elif 'ss' in aid:
|
||
aid = 'season_id=' + aid.replace('ss', '')
|
||
url = "https://api.bilibili.com/pgc/view/web/season?{0}".format(aid)
|
||
rsp = self._get_sth(url, 'fake')
|
||
jRoot = json.loads(rsp.text)
|
||
jo = jRoot['result']
|
||
self.detailContent_args['ssid'] = str(jo['season_id'])
|
||
title = jo['title']
|
||
self.detailContent_args['s_title'] = jo['season_title']
|
||
self.detailContent_args['title_type'] = '集'
|
||
if jo['type'] in [1, 4]:
|
||
self.detailContent_args['title_type'] = '话'
|
||
#添加系列到搜索
|
||
seasons = jo.get('seasons')
|
||
if len(seasons) == 1:
|
||
self.detailContent_args['s_title'] = seasons[0]['season_title']
|
||
seasons = 0
|
||
else:
|
||
self.detailContent_args['seasons'] = list(self.pool.map(self.get_all_season, seasons))
|
||
#获取正片
|
||
episodes = jo.get('episodes')
|
||
#获取花絮
|
||
section_task = []
|
||
for s in jo.get('section', []):
|
||
if s:
|
||
t = self.pool.submit(self.get_bangumi_section, s)
|
||
section_task.append(t)
|
||
pic = jo['cover']
|
||
typeName = jo['share_sub_title']
|
||
date = jo['publish']['pub_time'][0:4]
|
||
dec = jo['evaluate']
|
||
remark = jo['new_ep']['desc']
|
||
stat = jo['stat']
|
||
# 演员和导演框展示视频状态,包括以下内容:
|
||
status = "▶" + self.zh(stat['views']) + " ❤" + self.zh(stat['favorites'])
|
||
vod = {
|
||
"vod_id": 'ss' + self.detailContent_args['ssid'],
|
||
"vod_name": title,
|
||
"vod_pic": pic,
|
||
"type_name": typeName,
|
||
"vod_year": date,
|
||
"vod_actor": status,
|
||
"vod_content": dec
|
||
}
|
||
if self.isFongmi:
|
||
if seasons:
|
||
remark += ' [a=cr:{"id": "_getbangumiseasons","name": "' + title.replace('"', '\\"') + '"}/]更多系列[/a]'
|
||
seasons = 0
|
||
if 'rating' in jo:
|
||
remark = str(jo['rating']['score']) + '分 ' + remark
|
||
self.userConfig['show_vod_hot_reply'] = False
|
||
else:
|
||
vod["vod_area"] = "bilidanmu"
|
||
vod["vod_remarks"] = remark
|
||
ZhuiPf = []
|
||
ZhuiPu = []
|
||
if self.userid:
|
||
ZhuiPf = ['做点什么']
|
||
ZhuiPu = '是否追番剧$ #❤追番剧$add_notplay_zhui#💔取消追番剧$del_notplay_zhui'
|
||
defaultQn = int(self.userConfig['vodDefaultQn'])
|
||
if defaultQn > 116:
|
||
ZhuiPu += '#⚠️限高1080$116_notplay_vodTMPQn'
|
||
ZhuiPu = [ZhuiPu]
|
||
if seasons:
|
||
ZhuiPf.append('更多系列')
|
||
ZhuiPu.append('更多系列在快速搜索中查看$ #')
|
||
FirstPf = []
|
||
FirstPu = []
|
||
PreviewPf = []
|
||
PreviewPu = []
|
||
ParsePf = []
|
||
ParsePu = []
|
||
if episodes:
|
||
for x, y in self.pool.map(self.get_normal_episodes, episodes):
|
||
if y:
|
||
FirstPu.append(x)
|
||
ParsePu.append(y)
|
||
else:
|
||
PreviewPu.append(x)
|
||
if FirstPu:
|
||
FirstPf = [self.detailContent_args['s_title']]
|
||
FirstPu = ['#'.join(FirstPu)]
|
||
if PreviewPu:
|
||
PreviewPf = ['预告']
|
||
PreviewPu = ['#'.join(PreviewPu)]
|
||
if not self.detailContent_args.get('parse'):
|
||
ParsePu = []
|
||
if ParsePu:
|
||
ParsePf = [str(self.detailContent_args['s_title']) + '【解析】']
|
||
ParsePu = ['#'.join(ParsePu)]
|
||
fromL = ParsePf + FirstPf + PreviewPf
|
||
urlL = ParsePu + FirstPu + PreviewPu
|
||
for t in as_completed(section_task):
|
||
s = t.result()
|
||
if s:
|
||
fromL.append(s[0])
|
||
urlL.append(s[1])
|
||
fromep = self.detailContent_args.get('fromep', '')
|
||
if '_' in fromep:
|
||
fromL = ['B站'] + fromL
|
||
urlL = [fromep] + urlL
|
||
if self.userConfig['show_vod_hot_reply']:
|
||
self.get_vod_hot_reply_event.wait()
|
||
ReplyPu = self.detailContent_args.get('Reply', '')
|
||
if ReplyPu:
|
||
ZhuiPf.append('热门评论')
|
||
ZhuiPu.append(ReplyPu)
|
||
fromL.insert(1, '$$$'.join(ZhuiPf))
|
||
urlL.insert(1, '$$$'.join(ZhuiPu))
|
||
vod['vod_play_from'] = '$$$'.join(fromL)
|
||
vod['vod_play_url'] = '$$$'.join(urlL)
|
||
result = {
|
||
'list': [
|
||
vod
|
||
]
|
||
}
|
||
return result
|
||
|
||
def get_live_api2_playurl(self, room_id):
|
||
playFrom = []
|
||
playUrl = []
|
||
url = 'https://api.live.bilibili.com/xlive/web-room/v2/index/getRoomPlayInfo?room_id={0}&no_playurl=0&mask=1&qn=0&platform=web&protocol=0,1&format=0,1,2&codec=0,1&dolby=5&panorama=1'.format(room_id)
|
||
rsp = self._get_sth(url, 'fake')
|
||
jo = json.loads(rsp.text)
|
||
if jo['code'] == 0:
|
||
playurl_info = jo['data'].get('playurl_info', '')
|
||
if playurl_info:
|
||
stream = playurl_info['playurl']['stream']
|
||
liveDic = {
|
||
'codec': {'avc': '0', 'hevc': '1'},
|
||
'format': {'flv': '0', 'ts': '1', 'fmp4': '2'},
|
||
}
|
||
liveDic['qn'] = dict(self.pool.map(lambda x:(x['qn'], x['desc']), playurl_info['playurl']['g_qn_desc']))
|
||
vodList = []
|
||
for i in stream:
|
||
vodList.extend(i['format'])
|
||
api2_playUrl = {}
|
||
for v in vodList:
|
||
format = str(v.get('format_name'))
|
||
for c in v['codec']:
|
||
codec = str(c.get('codec_name'))
|
||
accept_qn = c.get('accept_qn')
|
||
for qn in accept_qn:
|
||
url = format + '_' + codec + '$liveapi2_' + str(qn) + '_' + liveDic['format'][format] + '_' + liveDic['codec'][codec] + '_' + str(room_id)
|
||
if not api2_playUrl.get(liveDic['qn'][qn]):
|
||
api2_playUrl[liveDic['qn'][qn]] = []
|
||
api2_playUrl[liveDic['qn'][qn]].append(url)
|
||
for key, value in api2_playUrl.items():
|
||
playFrom.append(key)
|
||
playUrl.append('#'.join(value))
|
||
result = {'From': playFrom, 'url': playUrl}
|
||
return result
|
||
|
||
def live_detailContent(self, array):
|
||
room_id = array[0]
|
||
get_live_api2_playurl = self.pool.submit(self.get_live_api2_playurl, room_id)
|
||
url = "https://api.live.bilibili.com/room/v1/Room/get_info?room_id=" + str(room_id)
|
||
rsp = self._get_sth(url, 'fake')
|
||
jRoot = json.loads(rsp.text)
|
||
result = {}
|
||
if jRoot.get('code') == 0:
|
||
jo = jRoot['data']
|
||
self.detailContent_args['mid'] = mid = str(jo["uid"])
|
||
self.get_up_info_event.clear()
|
||
self.pool.submit(self.get_up_info, mid)
|
||
title = jo['title'].replace("<em class=\"keyword\">", "").replace("</em>", "")
|
||
pic = jo.get("user_cover")
|
||
desc = jo.get('description')
|
||
typeName = jo.get('parent_area_name') + '--' + jo.get('area_name')
|
||
live_status = jo.get('live_status', '')
|
||
if live_status:
|
||
live_status = "开播时间:" + jo.get('live_time').replace('-', '.')
|
||
else:
|
||
live_status = "未开播"
|
||
vod = {
|
||
"vod_id": room_id,
|
||
"vod_name": title,
|
||
"vod_pic": pic,
|
||
"type_name": typeName,
|
||
"vod_content": desc,
|
||
}
|
||
remark = "房间号:" + room_id + " " + live_status
|
||
if self.isFongmi:
|
||
vod["vod_remarks"] = remark
|
||
else:
|
||
vod["vod_actor"] = remark
|
||
vod["vod_area"] = "bililivedanmu"
|
||
secondPFrom = ''
|
||
secondP = ''
|
||
if self.userid:
|
||
secondPFrom = '关注Ta'
|
||
first = '是否关注$ '
|
||
follow = '➕关注$1_notplay_follow'
|
||
unfollow = '➖取关$2_notplay_follow'
|
||
secondPList = [first, follow, unfollow]
|
||
secondP = '#'.join(secondPList)
|
||
playFrom = get_live_api2_playurl.result().get('From', [])
|
||
playUrl = get_live_api2_playurl.result().get('url', [])
|
||
if playFrom:
|
||
api1_playFrom = 'API_1'
|
||
api1_playUrl = 'flv线路原画$platform=web&quality=4_' + room_id + '#flv线路高清$platform=web&quality=3_' + room_id + '#h5线路原画$platform=h5&quality=4_' + room_id + '#h5线路高清$platform=h5&quality=3_' + room_id
|
||
playFrom.append(api1_playFrom)
|
||
playUrl.append(api1_playUrl)
|
||
if secondPFrom:
|
||
playFrom.insert(1, secondPFrom)
|
||
playUrl.insert(1, secondP)
|
||
vod['vod_play_from'] = '$$$'.join(playFrom)
|
||
vod['vod_play_url'] = '$$$'.join(playUrl)
|
||
self.get_up_info_event.wait()
|
||
up_info = self.up_info[mid]
|
||
vod["vod_director"] = '🆙 ' + up_info['crname'] + " 👥 " + self.zh(jo.get('attention')) + ' ' + up_info['following']
|
||
result['list'] = [vod]
|
||
return result
|
||
|
||
search_key = ''
|
||
|
||
def searchContent(self, key, quick):
|
||
if not self.session_fake.cookies:
|
||
self.pool.submit(self.getFakeCookie, True)
|
||
for t in self.task_pool:
|
||
t.cancel()
|
||
self.task_pool = []
|
||
self.search_key = key
|
||
mid = self.detailContent_args.get('mid', '')
|
||
if quick and mid:
|
||
get_up_videos = self.pool.submit(self.get_up_videos, mid, 1, 'quicksearch')
|
||
types = {'video': '','media_bangumi': '番剧: ', 'media_ft': '影视: ', 'bili_user': '用户: ', 'live': '直播: '}
|
||
for type, value in types.items():
|
||
t = self.pool.submit(self.get_search_content, key = key, pg = value, duration_diff = 0, order = '', type = type, ps = self.userConfig['page_size'])
|
||
self.task_pool.append(t)
|
||
result = {'list': []}
|
||
for t in as_completed(self.task_pool):
|
||
res = t.result().get('list', [])
|
||
result['list'].extend(res)
|
||
self.task_pool.remove(t)
|
||
if quick:
|
||
if not self.isFongmi:
|
||
if mid:
|
||
result['list'] = self.detailContent_args.get('interaction', []) + get_up_videos.result().get('list', []) + self.detailContent_args.get('Reply_jump', []) + result['list']
|
||
else:
|
||
result['list'] = self.detailContent_args.get('seasons', []) + result['list']
|
||
return result
|
||
|
||
stop_heartbeat_event = threading.Event()
|
||
|
||
def start_heartbeat(self, aid, cid, ids):
|
||
duration = ssid = epid = ''
|
||
for i in ids:
|
||
if 'ss' in i:
|
||
ssid = i.replace('ss', '')
|
||
elif 'ep' in i:
|
||
epid = i.replace('ep', '')
|
||
elif 'dur' in i:
|
||
duration = int(i.replace('dur', ''))
|
||
url = 'https://api.bilibili.com/x/player/v2?aid={0}&cid={1}'.format(aid, cid)
|
||
rsp = self._get_sth(url)
|
||
jo = json.loads(rsp.text)
|
||
data = jo.get('data',{})
|
||
interaction = data.get('interaction', {})
|
||
if interaction.get('graph_version'):
|
||
graph_version = interaction.get('graph_version')
|
||
old = self.detailContent_args.get('graph_version')
|
||
if old != graph_version:
|
||
self.detailContent_args['graph_version'] = graph_version
|
||
self.pool.submit(self.interaction_detailContent)
|
||
heartbeatInterval = int(self.userConfig['heartbeatInterval'])
|
||
if not self.userid or not heartbeatInterval:
|
||
return
|
||
if not duration:
|
||
url = 'https://api.bilibili.com/x/web-interface/view?aid={0}&cid={1}'.format(aid, cid)
|
||
rsp = self._get_sth(url, 'fake')
|
||
jRoot = json.loads(rsp.text)
|
||
duration = jRoot['data']['duration']
|
||
played_time = 0
|
||
if int(data.get('last_play_cid', 0)) == int(cid):
|
||
last_play_time = int(data.get('last_play_time'))
|
||
if last_play_time > 0:
|
||
played_time = int(last_play_time / 1000)
|
||
heartbeat_times = int((duration - played_time) / heartbeatInterval) + 1
|
||
url = 'https://api.bilibili.com/x/click-interface/web/heartbeat'
|
||
data = {'aid': str(aid), 'cid': str(cid), 'csrf': str(self.csrf)}
|
||
if ssid:
|
||
data['sid'] = str(ssid)
|
||
data['epid'] = str(epid)
|
||
data['type'] = '4'
|
||
heartbeat_count = 0
|
||
self.stop_heartbeat_event.clear()
|
||
while True:
|
||
if heartbeat_count == heartbeatInterval or self.stop_heartbeat_event.is_set():
|
||
played_time += heartbeat_count
|
||
heartbeat_count = 0
|
||
if not heartbeat_count:
|
||
heartbeat_times -= 1
|
||
if not heartbeat_times:
|
||
#播完为-1
|
||
played_time = -1
|
||
self.stop_heartbeat_event.set()
|
||
data['played_time'] = str(played_time)
|
||
self.pool.submit(self._post_sth, url=url, data=data)
|
||
if self.stop_heartbeat_event.is_set():
|
||
break
|
||
time.sleep(1)
|
||
heartbeat_count += 1
|
||
|
||
wbi_key = {}
|
||
def get_wbiKey(self, wts):
|
||
r = self.fetch("https://api.bilibili.com/x/web-interface/nav", headers=self.header)
|
||
wbi_img_url = r.json()['data']['wbi_img']['img_url']
|
||
wbi_sub_url = r.json()['data']['wbi_img']['sub_url']
|
||
oe = [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]
|
||
ae = wbi_img_url.split("/")[-1].split(".")[0] + wbi_sub_url.split("/")[-1].split(".")[0]
|
||
le = reduce(lambda s, i: s + ae[i], oe, "")[:32]
|
||
self.wbi_key = {
|
||
"key": le,
|
||
"wts": wts
|
||
}
|
||
|
||
def encrypt_wbi(self, **params):
|
||
wts = round(time.time())
|
||
if not self.wbi_key or abs(self.wbi_key['wts']) < 120:
|
||
self.get_wbiKey(wts)
|
||
params["wts"] = wts
|
||
params["dm_img_list"] = []
|
||
dm_rand = ['QQ','Qg','Qw','RA','RQ']
|
||
params["dm_img_str"] = random.choice(dm_rand)
|
||
params["dm_cover_img_str"] = random.choice(dm_rand)
|
||
params = dict(sorted(params.items()))
|
||
params = {k : ''.join(filter(lambda chr: chr not in "!'()*", str(v))) for k, v in params.items()}
|
||
Ae = urlencode(params)
|
||
return Ae + "&w_rid=" + hashlib.md5((Ae + self.wbi_key['key']).encode(encoding='utf-8')).hexdigest()
|
||
|
||
def _get_sth(self, url, _type='master'):
|
||
if _type == 'vip' and self.session_vip.cookies:
|
||
rsp = self.session_vip.get(url, headers=self.header)
|
||
elif _type == 'fake':
|
||
if not self.session_fake.cookies:
|
||
self.getFakeCookie_event.wait()
|
||
rsp = self.session_fake.get(url, headers=self.header)
|
||
else:
|
||
rsp = self.session_master.get(url, headers=self.header)
|
||
return rsp
|
||
|
||
def _post_sth(self, url, data):
|
||
return self.session_master.post(url, headers=self.header, data=data)
|
||
|
||
def post_live_history(self, room_id):
|
||
data = {'room_id': str(room_id), 'platform': 'pc', 'csrf': str(self.csrf)}
|
||
url = 'https://api.live.bilibili.com/xlive/web-room/v1/index/roomEntryAction'
|
||
self._post_sth(url=url, data=data)
|
||
|
||
def do_notplay(self, ids):
|
||
aid = self.detailContent_args.get('aid')
|
||
mid = self.detailContent_args.get('mid')
|
||
ssid = self.detailContent_args.get('ssid')
|
||
data = {'csrf': str(self.csrf)}
|
||
url = ''
|
||
if 'vodTMPQn' in ids:
|
||
self.detailContent_args['vodTMPQn'] = str(ids[0])
|
||
return
|
||
elif 'follow' in ids:
|
||
if 'special' in ids:
|
||
data.update({'fids': str(mid), 'tagids': str(ids[0])})
|
||
url = 'https://api.bilibili.com/x/relation/tags/addUsers'
|
||
else:
|
||
data.update({'fid': str(mid), 'act': str(ids[0])})
|
||
url = 'https://api.bilibili.com/x/relation/modify'
|
||
elif 'zhui' in ids:
|
||
data.update({'season_id': str(ssid)})
|
||
url = 'https://api.bilibili.com/pgc/web/follow/' + str(ids[0])
|
||
elif 'like' in ids:
|
||
data.update({'aid': str(aid), 'like': str(ids[0])})
|
||
url = 'https://api.bilibili.com/x/web-interface/archive/like'
|
||
elif 'coin' in ids:
|
||
data.update({'aid': str(aid), 'multiply': str(ids[0]), 'select_like': '1'})
|
||
url = 'https://api.bilibili.com/x/web-interface/coin/add'
|
||
elif 'fav' in ids:
|
||
data.update({'rid': str(aid), 'type': '2'})
|
||
data[ids[1] + '_media_ids'] = str(ids[0])
|
||
url = 'https://api.bilibili.com/x/v3/fav/resource/deal'
|
||
elif 'triple' in ids:
|
||
data.update({'aid': str(aid)})
|
||
url = 'https://api.bilibili.com/x/web-interface/archive/like/triple'
|
||
elif 'reply' in ids:
|
||
data.update({'oid': str(ids[0]), 'rpid': str(ids[1]), 'type': '1', 'action': '1'})
|
||
url = 'http://api.bilibili.com/x/v2/reply/action'
|
||
self._post_sth(url=url, data=data)
|
||
|
||
def get_cid(self, video):
|
||
url = "https://api.bilibili.com/x/web-interface/view?aid=%s" % str(video['aid'])
|
||
rsp = self._get_sth(url)
|
||
jRoot = json.loads(rsp.text)
|
||
jo = jRoot['data']
|
||
video['cid'] = jo['cid']
|
||
video['duration'] = jo['duration']
|
||
if 'redirect_url' in jo and 'bangumi' in jo['redirect_url']:
|
||
video['ep'] = self.find_bangumi_id(jo['redirect_url'])
|
||
|
||
cookie_dic_tmp = {}
|
||
|
||
def get_cookies(self, key):
|
||
url = 'https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key=' + key
|
||
rsp = self._get_sth(url, 'fake')
|
||
jo = json.loads(rsp.text)
|
||
if jo['code'] == 0:
|
||
message = jo['data']['message']
|
||
if not message:
|
||
self.cookie_dic_tmp[key] = dict(self.session_fake.cookies)
|
||
self.pool.submit(self.getFakeCookie)
|
||
return message
|
||
return '网络错误'
|
||
|
||
def set_cookie(self, key, _type):
|
||
cookie_dic_tmp = self.cookie_dic_tmp.get(key, '')
|
||
if not cookie_dic_tmp:
|
||
message = self.get_cookies(key)
|
||
if message:
|
||
return
|
||
users = self.userConfig.get('users', {})
|
||
users[_type] = {'cookies_dic': self.cookie_dic_tmp.get(key, {})}
|
||
self.userConfig.update({'users': users})
|
||
self.getCookie(_type)
|
||
self.dump_config()
|
||
|
||
def unset_cookie(self, _type):
|
||
if _type == 'vip':
|
||
self.session_vip.cookies.clear()
|
||
else:
|
||
self.session_master.cookies = self.session_fake.cookies
|
||
self.userid = self.csrf = ''
|
||
if _type in self.userConfig.get('users', {}):
|
||
self.userConfig['users'].pop(_type)
|
||
self.dump_config()
|
||
|
||
def set_normal_default(self, id, type):
|
||
self.userConfig[type] = str(id)
|
||
self.dump_config()
|
||
|
||
def set_normal_cateManual(self, name, _List, action):
|
||
List_tmp = self.userConfig.get(str(_List) + '_tmp')
|
||
if not List_tmp:
|
||
List_tmp = self.userConfig[str(_List) + '_tmp'] = []
|
||
if action == 'save':
|
||
for _item in self.defaultConfig[_List]:
|
||
if not _item in List_tmp.copy():
|
||
self.userConfig[str(_List) + '_tmp'].append(_item)
|
||
self.userConfig[_List] = self.userConfig[str(_List) + '_tmp'].copy()
|
||
self.userConfig.pop(_List + '_tmp')
|
||
self.dump_config()
|
||
elif action == 'clear':
|
||
self.userConfig[_List] = self.defaultConfig[_List].copy()
|
||
self.userConfig.pop(str(_List) + '_tmp')
|
||
self.dump_config()
|
||
else:
|
||
if _List == 'cateManualLive':
|
||
name = name.split('@@@')
|
||
if len(name) == 3:
|
||
name[1] += '_' + str(name[2])
|
||
name = {'n': name[0], 'v': str(name[1])}
|
||
if name in List_tmp:
|
||
self.userConfig[str(_List) + '_tmp'].remove(name)
|
||
else:
|
||
self.userConfig[str(_List) + '_tmp'].append(name)
|
||
|
||
def add_cateManualLiveExtra(self, action, name, id):
|
||
_Extra = self.userConfig.get('cateManualLiveExtra', [])
|
||
if not _Extra:
|
||
_Extra = self.userConfig['cateManualLiveExtra'] = []
|
||
if action == 'clear':
|
||
for _ext in _Extra:
|
||
_ext['v'] = _ext['v'].replace('@@@', '_')
|
||
if _ext in self.userConfig.get('cateManualLive', []):
|
||
self.userConfig['cateManualLive'].remove(_ext)
|
||
if _ext in self.userConfig.get('cateManualLive_tmp', []):
|
||
self.userConfig['cateManualLive_tmp'].remove(_ext)
|
||
self.userConfig.pop('cateManualLiveExtra')
|
||
elif id in list(map(lambda x:x['v'], self.userConfig.get('cateManualLiveExtra', []))):
|
||
area_dict = {'n': name, 'v': id}
|
||
self.userConfig['cateManualLiveExtra'].remove(area_dict)
|
||
area_dict['v'] = id.replace('@@@', '_')
|
||
if area_dict in self.userConfig.get('cateManualLive', []):
|
||
self.userConfig['cateManualLive'].remove(area_dict)
|
||
if area_dict in self.userConfig.get('cateManualLive_tmp', []):
|
||
self.userConfig['cateManualLive_tmp'].remove(area_dict)
|
||
else:
|
||
area_dict = {'n': name, 'v': id}
|
||
self.userConfig['cateManualLiveExtra'].append(area_dict)
|
||
self.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 = {
|
||
'30280': '192000',
|
||
'30232': '132000',
|
||
'30216': '64000',
|
||
}
|
||
|
||
def get_dash_media(self, video):
|
||
qnid = str(video.get('id'))
|
||
codecid = video.get('codecid')
|
||
media_codecs = video.get('codecs')
|
||
media_bandwidth = video.get('bandwidth')
|
||
media_startWithSAP = video.get('startWithSap')
|
||
media_mimeType = video.get('mimeType')
|
||
media_BaseURL = video.get('baseUrl').replace('&', '&')
|
||
media_SegmentBase_indexRange = video['SegmentBase'].get('indexRange')
|
||
media_SegmentBase_Initialization = video['SegmentBase'].get('Initialization')
|
||
mediaType = media_mimeType.split('/')[0]
|
||
if mediaType == 'video':
|
||
media_frameRate = video.get('frameRate')
|
||
media_sar = video.get('sar')
|
||
media_width = video.get('width')
|
||
media_height = video.get('height')
|
||
media_type_params = f"height='{media_height}' width='{media_width}' frameRate='{media_frameRate}' sar='{media_sar}'"
|
||
elif mediaType == 'audio':
|
||
audioSamplingRate = self.vod_audio_id.get(qnid, '192000')
|
||
media_type_params = f"numChannels='2' sampleRate='{audioSamplingRate}'"
|
||
if codecid:
|
||
qnid += '_' + str(codecid)
|
||
result = f"""
|
||
<Representation id="{qnid}" bandwidth="{media_bandwidth}" codecs="{media_codecs}" mimeType="{media_mimeType}" {media_type_params} startWithSAP="{media_startWithSAP}">
|
||
<BaseURL>{media_BaseURL}</BaseURL>
|
||
<SegmentBase indexRange="{media_SegmentBase_indexRange}">
|
||
<Initialization range="{media_SegmentBase_Initialization}"/>
|
||
</SegmentBase>
|
||
</Representation>"""
|
||
return result
|
||
|
||
def get_dash_media_list(self, media_lis):
|
||
if not media_lis:
|
||
return ""
|
||
mediaType = media_lis[0]['mimeType'].split('/')[0]
|
||
defaultQn = defaultCodec = ''
|
||
if mediaType == 'video':
|
||
defaultQn = vodTMPQn = self.detailContent_args.get('vodTMPQn', '')
|
||
if vodTMPQn:
|
||
vodTMPQn = int(vodTMPQn)
|
||
else:
|
||
defaultQn = str(self.userConfig['vodDefaultQn'])
|
||
vodTMPQn = 120
|
||
defaultCodec = str(self.userConfig['vodDefaultCodec'])
|
||
elif mediaType == 'audio':
|
||
defaultQn = str(self.userConfig['vodDefaultAudio'])
|
||
vodTMPQn = int(defaultQn)
|
||
defaultCodec = '0'
|
||
qn_codec = list(map(lambda x: str(x['id']) + '_' + str(x['codecid']), media_lis))
|
||
Qn_available_lis = []
|
||
#按设定的质量和设定的编码找
|
||
if defaultQn + '_' + defaultCodec in qn_codec:
|
||
Qn_available_lis.append(media_lis[qn_codec.index(defaultQn + '_' + defaultCodec)])
|
||
#按设定的质量找推荐的编码
|
||
if not Qn_available_lis and mediaType == 'video':
|
||
for c in self.vod_codec_id.keys():
|
||
if defaultQn + '_' + str(c) in qn_codec:
|
||
Qn_available_lis.append(media_lis[qn_codec.index(defaultQn + '_' + str(c))])
|
||
#找4K及以下最高可用画质/音质
|
||
if not Qn_available_lis:
|
||
qn_top = ''
|
||
for q in qn_codec:
|
||
q_c = q.split('_')
|
||
if qn_top and int(qn_top) > int(q_c[0]):
|
||
break
|
||
elif mediaType == 'video' and int(q_c[0]) <= vodTMPQn and not qn_top or mediaType == 'audio' and not qn_top or int(q_c[0]) == qn_top:
|
||
qn_top = int(q_c[0])
|
||
#匹配设定的编码,否则全部
|
||
if mediaType == 'video' and str(q_c[1]) == defaultCodec:
|
||
Qn_available_lis = [media_lis[qn_codec.index(str(q))]]
|
||
break
|
||
Qn_available_lis.append(media_lis[qn_codec.index(str(q))])
|
||
result = f"""
|
||
<AdaptationSet>
|
||
<ContentComponent contentType="{mediaType}"/>{''.join(map(self.get_dash_media, Qn_available_lis))}
|
||
</AdaptationSet>"""
|
||
return result
|
||
|
||
get_dash_event = threading.Event()
|
||
def get_dash(self, ja):
|
||
duration = ja.get('duration')
|
||
minBufferTime = ja.get('minBufferTime')
|
||
video_list = self.pool.submit(self.get_dash_media_list, ja.get('video'))
|
||
audio_list = self.pool.submit(self.get_dash_media_list, ja.get('audio'))
|
||
mpd = 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{duration}S" minBufferTime="PT{minBufferTime}S" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011">
|
||
<Period duration="PT{duration}S" start="PT0S">{video_list.result()}{audio_list.result()}
|
||
</Period>
|
||
</MPD>"""
|
||
with open(f"{dirname}/playurl.mpd", 'w', encoding="utf-8") as f:
|
||
f.write(mpd)
|
||
self.get_dash_event.set()
|
||
#time.sleep(3)
|
||
#os.remove(f"{dirname}/playurl.mpd")
|
||
|
||
def get_durl(self, ja):
|
||
maxSize = -1
|
||
position = -1
|
||
for i in range(len(ja)):
|
||
tmpJo = ja[i]
|
||
if maxSize < int(tmpJo['size']):
|
||
maxSize = int(tmpJo['size'])
|
||
position = i
|
||
url = ''
|
||
if len(ja) > 0:
|
||
if position == -1:
|
||
position = 0
|
||
url = ja[position]['url']
|
||
return url
|
||
|
||
def miao(self, m):
|
||
m = str(m).partition('.')[2] #取小数部分
|
||
if len(m)==0:m = '000' #补齐三位小数
|
||
if len(m)==1:m = m + '00'
|
||
if len(m)==2:m = m + '0'
|
||
return m #返回标准三位的毫秒数
|
||
|
||
def down_sub(self, url):
|
||
rsp = self._get_sth(url, 'fake')
|
||
data = json.loads(rsp.text)['body']
|
||
srt = ''
|
||
for d in data:
|
||
f = round(d['from'],3) # 开始时间 (round(n,3)四舍五入为三位小数)
|
||
t = round(d['to'],3) # 结束时间
|
||
c = d['content'] # 字幕内容
|
||
ff = time.strftime("%H:%M:%S",time.gmtime(f)) + ',' + self.miao(f) # 开始时间,秒数转 时:分:秒 格式,加逗号、毫秒修正为三位
|
||
tt = time.strftime("%H:%M:%S",time.gmtime(t)) + ',' + self.miao(t) # 结束时间,处理方式同上
|
||
srt += '\n' + ff + ' ' + '-->' + ' ' + tt + '\n' + c + '\n\n' # 格式化为Srt字幕
|
||
return srt
|
||
|
||
localProxyUrl = 'http://127.0.0.1:UndCover/proxy'
|
||
|
||
def get_subs(self, aid, cid):
|
||
result = []
|
||
query = self.encrypt_wbi(aid=aid, cid=cid)
|
||
url = f'https://api.bilibili.com/x/player/wbi/v2?{query}'
|
||
rsp = self._get_sth(url, 'master')
|
||
jRoot = json.loads(rsp.text)
|
||
if jRoot['code'] == 0:
|
||
for sub in jRoot['data']['subtitle'].get('subtitles', []):
|
||
lanDoc = str(sub.get('lan_doc', ''))
|
||
lanUrl = sub.get('subtitle_url')
|
||
if lanUrl.startswith('//'):
|
||
lanUrl = 'https:' + lanUrl
|
||
lanUrl = base64.b64encode(lanUrl.encode('utf-8')).decode('utf-8')
|
||
result.append(
|
||
{
|
||
"url": f"{self.localProxyUrl}&siteType=3&siteKey=py_bilibili&type=subtitle&url={lanUrl}",
|
||
"name": lanDoc,
|
||
"format": "application/x-subrip"
|
||
}
|
||
)
|
||
return result
|
||
|
||
def playerContent(self, flag, id, vipFlags):
|
||
self.stop_heartbeat_event.set()
|
||
result = {'playUrl': '', 'url': ''}
|
||
ids = id.split("_")
|
||
if 'web' in id or 'liveapi2' == ids[0]:
|
||
return self.live_playerContent(flag, id, vipFlags)
|
||
if len(ids) < 2:
|
||
return result
|
||
aid = ids[0]
|
||
cid = ids[1]
|
||
if 'setting' in ids:
|
||
if 'liveFilter' in id:
|
||
id = ids[2]
|
||
self.add_cateManualLiveExtra(aid, cid, id)
|
||
elif cid in ['cateManual', 'cateManualLive', 'tuijianLis', 'rankingLis']:
|
||
action = ids[2]
|
||
self.set_normal_cateManual(aid, cid, action)
|
||
elif 'login' in id:
|
||
self.set_cookie(aid, cid)
|
||
elif 'logout' in id:
|
||
self.unset_cookie(aid)
|
||
else:
|
||
self.set_normal_default(aid, cid)
|
||
return result
|
||
elif 'notplay' in ids:
|
||
self.pool.submit(self.do_notplay, ids)
|
||
return result
|
||
elif cid == 'cid' or not cid:
|
||
video = {'aid': str(aid)}
|
||
self.get_cid(video, )
|
||
cid = video['cid']
|
||
ids.append('dur' + str(video['duration']))
|
||
ep = video.get('ep')
|
||
if ep:
|
||
id += '_' + ep
|
||
ids.append(ep)
|
||
query = self.encrypt_wbi(avid=aid, cid=cid, fnval=4048, fnver=0, fourk=1)
|
||
url = f'https://api.bilibili.com/x/player/wbi/playurl?{query}'
|
||
if 'ep' in id:
|
||
if 'parse' in id:
|
||
test = list(x for x in map(lambda x: x if 'ep' in x else None, ids) if x is not None)
|
||
url = 'https://www.bilibili.com/bangumi/play/' + test[0]
|
||
result["url"] = url
|
||
result["flag"] = 'bilibili'
|
||
result["parse"] = '1'
|
||
result['jx'] = '1'
|
||
result["header"] = str({"User-Agent": self.header["User-Agent"]})
|
||
result["danmaku"] = 'https://api.bilibili.com/x/v1/dm/list.so?oid=' + str(cid)
|
||
return result
|
||
url = 'https://api.bilibili.com/pgc/player/web/playurl?aid={}&cid={}&fnval=4048&fnver=0&fourk=1'.format(aid, cid)
|
||
rsp = self._get_sth(url, 'vip')
|
||
jRoot = json.loads(rsp.text)
|
||
if jRoot['code'] == 0:
|
||
if 'data' in jRoot:
|
||
jo = jRoot['data']
|
||
elif 'result' in jRoot:
|
||
jo = jRoot['result']
|
||
else:
|
||
return result
|
||
else:
|
||
return result
|
||
ja = jo.get('dash')
|
||
if ja:
|
||
self.get_dash_event.clear()
|
||
get_dash = self.pool.submit(self.get_dash, ja)
|
||
self.get_dash_event.wait()
|
||
result["url"] = f"{dirname}/playurl.mpd"
|
||
else:
|
||
result["url"] = self.get_durl(jo.get('durl', {}))
|
||
result["parse"] = '0'
|
||
result["contentType"] = ''
|
||
result["header"] = self.header
|
||
result["danmaku"] = 'https://api.bilibili.com/x/v1/dm/list.so?oid=' + str(cid)
|
||
if self.isFongmi:
|
||
result["subs"] = self.get_subs(aid, cid)
|
||
#回传播放记录
|
||
self.pool.submit(self.start_heartbeat, aid, cid, ids)
|
||
return result
|
||
|
||
def live_playerContent(self, flag, id, vipFlags):
|
||
result = {'playUrl': '', 'url': ''}
|
||
ids = id.split("_")
|
||
if len(ids) < 2:
|
||
return result
|
||
# 回传观看直播记录
|
||
if self.userid and int(self.userConfig['heartbeatInterval']) > 0:
|
||
self.pool.submit(self.post_live_history, ids[-1])
|
||
if ids[0] == 'liveapi2':
|
||
qn = int(ids[1])
|
||
format = int(ids[2])
|
||
codec = int(ids[3])
|
||
room_id = int(ids[-1])
|
||
url = '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&no_playurl=0&mask=1'.format(room_id, format, codec, qn)
|
||
rsp = self._get_sth(url, 'fake')
|
||
jo = json.loads(rsp.text)
|
||
if jo['code'] == 0:
|
||
try:
|
||
playurl = jo['data']['playurl_info'].get('playurl')
|
||
codec = playurl['stream'][0]['format'][0]['codec'][0]
|
||
except:
|
||
return result
|
||
base_url = str(codec['base_url'])
|
||
host = str(codec['url_info'][0]['host'])
|
||
extra = str(codec['url_info'][0]['extra'])
|
||
playurl = host + base_url + extra
|
||
result["url"] = playurl
|
||
if ".flv" in playurl:
|
||
result["contentType"] = 'video/x-flv'
|
||
else:
|
||
result["contentType"] = ''
|
||
else:
|
||
return result
|
||
else:
|
||
url = 'https://api.live.bilibili.com/room/v1/Room/playUrl?cid=%s&%s' % (ids[1], ids[0])
|
||
# raise Exception(url)
|
||
try:
|
||
rsp = self._get_sth(url)
|
||
except:
|
||
return result
|
||
jRoot = json.loads(rsp.text)
|
||
if jRoot['code'] == 0:
|
||
jo = jRoot['data']
|
||
ja = jo['durl']
|
||
if len(ja) > 0:
|
||
result["url"] = ja[0]['url']
|
||
if "h5" in ids[0]:
|
||
result["contentType"] = ''
|
||
else:
|
||
result["contentType"] = 'video/x-flv'
|
||
else:
|
||
return result
|
||
result["parse"] = '0'
|
||
# result['type'] ="m3u8"
|
||
result["header"] = {
|
||
"Referer": "https://live.bilibili.com",
|
||
"User-Agent": self.header["User-Agent"]
|
||
}
|
||
return result
|
||
|
||
config = {
|
||
"player": {},
|
||
"filter": {
|
||
"关注": [{"key": "sort", "name": "分类",
|
||
"value": [{"n": "正在直播", "v": "正在直播"}, {"n": "最常访问", "v": "最常访问"},
|
||
{"n": "最近关注", "v": "最近关注"}, {"n": "特别关注", "v": "特别关注"},
|
||
{"n": "悄悄关注", "v": "悄悄关注"}, {"n": "我的粉丝", "v": "我的粉丝"}]}],
|
||
"动态": [{"key": "order", "name": "别人投稿排序",
|
||
"value": [{"n": "最新发布", "v": "pubdate"}, {"n": "最多播放", "v": "click"},
|
||
{"n": "最多收藏", "v": "stow"}, {"n": "最早发布", "v": "oldest"}, {"n": "合集和列表", "v": "series"}]}, ],
|
||
"影视": [{"key": "tid", "name": "分类",
|
||
"value": [{"n": "番剧", "v": "1"}, {"n": "国创", "v": "4"}, {"n": "电影", "v": "2"},
|
||
{"n": "电视剧", "v": "5"}, {"n": "纪录片", "v": "3"}, {"n": "综艺", "v": "7"}]},
|
||
{"key": "order", "name": "排序",
|
||
"value": [{"n": "热门", "v": "热门"}, {"n": "播放数量", "v": "2"}, {"n": "更新时间", "v": "0"},
|
||
{"n": "最高评分", "v": "4"}, {"n": "弹幕数量", "v": "1"}, {"n": "追看人数", "v": "3"},
|
||
{"n": "开播时间", "v": "5"}, {"n": "上映时间", "v": "6"}]},
|
||
{"key": "season_status", "name": "付费",
|
||
"value": [{"n": "全部", "v": "-1"}, {"n": "免费", "v": "1"},
|
||
{"n": "付费", "v": "2%2C6"}, {"n": "大会员", "v": "4%2C6"}]}],
|
||
"频道": [{"key": "order", "name": "排序",
|
||
"value": [{"n": "近期热门", "v": "hot"}, {"n": "月播放量", "v": "view"},
|
||
{"n": "最新投稿", "v": "new"}, {"n": "频道精选", "v": "featured"}, ]}, ],
|
||
"收藏": [{"key": "order", "name": "排序",
|
||
"value": [{"n": "收藏时间", "v": "mtime"}, {"n": "播放量", "v": "view"},
|
||
{"n": "投稿时间", "v": "pubtime"}]}, ],
|
||
"历史": [{"key": "type", "name": "分类",
|
||
"value": [{"n": "全部", "v": "all"}, {"n": "视频", "v": "archive"}, {"n": "直播", "v": "live"}, {"n": "UP主", "v": "UP主"}, {"n": "稍后再看", "v": "稍后再看"}]}, ],
|
||
"搜索": [{"key": "type", "name": "类型",
|
||
"value": [{"n": "视频", "v": "video"}, {"n": "番剧", "v": "media_bangumi"}, {"n": "影视", "v": "media_ft"},
|
||
{"n": "直播", "v": "live"}, {"n": "用户", "v": "bili_user"}]},
|
||
{"key": "order", "name": "视频排序",
|
||
"value": [{"n": "综合排序", "v": "totalrank"}, {"n": "最多点击", "v": "click"}, {"n": "最新发布", "v": "pubdate"},
|
||
{"n": "最多收藏", "v": "stow"}, {"n": "最多弹幕", "v": "dm"}]},
|
||
{"key": "duration", "name": "视频时长",
|
||
"value": [{"n": "全部", "v": "0"}, {"n": "60分钟以上", "v": "4"}, {"n": "30~60分钟", "v": "3"},
|
||
{"n": "5~30分钟", "v": "2"}, {"n": "5分钟以下", "v": "1"}]}],
|
||
}
|
||
}
|
||
|
||
header = {
|
||
'Origin': 'https://www.bilibili.com',
|
||
'Referer': 'https://www.bilibili.com',
|
||
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 13_2_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Safari/605.1.15'
|
||
}
|
||
|
||
def localProxy(self, param):
|
||
action = {
|
||
'url': '',
|
||
'header': '',
|
||
'param': '',
|
||
'type': 'string',
|
||
'after': ''
|
||
}
|
||
if param['type'] == 'subtitle':
|
||
url = base64.b64decode(param['url']).decode('utf-8')
|
||
content = self.down_sub(url)
|
||
return [200, "application/octet-stream", action, content]
|
||
return [200, "video/MP2T", action, ""] |