py_bilibili5.py 123 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868
  1. # coding=utf-8
  2. # !/usr/bin/python
  3. import sys, os, json
  4. from base.spider import Spider
  5. from requests import session, utils, get as requests_get
  6. from requests.adapters import HTTPAdapter, Retry
  7. from concurrent.futures import ThreadPoolExecutor, as_completed
  8. import threading
  9. import hashlib
  10. import time
  11. import random
  12. from functools import reduce
  13. from urllib.parse import quote, urlencode
  14. sys.path.append('..')
  15. dirname, filename = os.path.split(os.path.abspath(__file__))
  16. sys.path.append(dirname)
  17. class Spider(Spider):
  18. #默认设置
  19. defaultConfig = {
  20. 'currentVersion': "20231003_1",
  21. #【建议通过扫码确认】设置Cookie,在双引号内填写
  22. 'raw_cookie_line': "http://127.0.0.1:9978/file/tvbox/bilicookie.txt",
  23. #如果主cookie没有vip,可以设置第二cookie,仅用于播放会员番剧,所有的操作、记录还是在主cookie,不会同步到第二cookie
  24. 'raw_cookie_vip': "",
  25. #主页默认显示3图
  26. 'maxHomeVideoContent': '3',
  27. #收藏标签默认显示追番1,追剧2,默认收藏夹0
  28. 'favMode': '0',
  29. #部分视频列表分页,限制每次加载数量
  30. 'page_size': 10,
  31. #上传播放进度间隔时间,单位秒,b站默认间隔15,0则不上传播放历史
  32. 'heartbeatInterval': '15',
  33. #视频默认画质ID
  34. 'vodDefaultQn': '80',
  35. #视频默认解码ID
  36. 'vodDefaultCodec': '7',
  37. #音频默认码率ID
  38. 'vodDefaultAudio': '30280',
  39. #获取视频热门评论
  40. 'show_vod_hot_reply': True,
  41. #从正片中拆分出番剧的预告
  42. 'hide_bangumi_preview': True,
  43. #登陆会员账号后,影视播放页不显示会员专享的标签,更简洁
  44. 'hide_bangumi_vip_badge': True,
  45. #番剧(热门)列表使用横图
  46. 'bangumi_horizontal_cover': True,
  47. #非会员播放会员专享视频时,添加一个页面可以使用解析源,解析源自行解决
  48. 'bangumi_vip_parse': True,
  49. #付费视频添加一个页面可以使用解析,解析源自行解决
  50. 'bangumi_pay_parse': True,
  51. #是否显示直播标签筛选中分区的细化标签, 0为不显示,1为显示
  52. 'showLiveFilterTag': '0',
  53. #主页标签排序, 未登录或cookie失效时自动隐藏动态、收藏、关注、历史
  54. 'cateManual': [
  55. "动态",
  56. "推荐",
  57. "搜索",
  58. "影视",
  59. "直播",
  60. "频道",
  61. "收藏",
  62. "关注",
  63. "历史",
  64. ],
  65. #自定义推荐标签的筛选
  66. 'tuijianLis': [
  67. "热门",
  68. "推荐",
  69. "排行榜",
  70. "每周必看",
  71. "入站必刷",
  72. "番剧时间表",
  73. "国创时间表"
  74. ],
  75. 'rankingLis': [
  76. "动画",
  77. "音乐",
  78. "舞蹈",
  79. "游戏",
  80. "鬼畜",
  81. "知识",
  82. "科技",
  83. "运动",
  84. "生活",
  85. "美食",
  86. "动物",
  87. "汽车",
  88. "时尚",
  89. "娱乐",
  90. "影视",
  91. "原创",
  92. "新人",
  93. ],
  94. }
  95. #在动态标签的筛选中固定显示他,n为用户名或任意都可以,v必须为准确的UID
  96. focus_on_up_list = [
  97. #{"n":"徐云流浪中国", "v":"697166795"},
  98. ]
  99. #在搜索标签的筛选中固定显示搜索词
  100. focus_on_search_key = [
  101. '抖音热歌',
  102. '经典无损音乐合集',
  103. '萌七',
  104. '王雨檬',
  105. 'Minana呀',
  106. '贝拉小姐姐',
  107. ]
  108. def getName(self):
  109. return "哔哩哔哩"
  110. def load_config(self):
  111. try:
  112. with open(f"{dirname}/config.json",encoding="utf-8") as f:
  113. self.userConfig = json.load(f)
  114. old_config = {
  115. 'master': 'cookie_dic',
  116. 'vip': 'cookie_vip_dic',
  117. 'fake': 'cookie_fake_dic',
  118. }
  119. for _type, old in old_config.items():
  120. old = self.userConfig.get(old)
  121. if old:
  122. if not self.userConfig.get('users'):
  123. self.userConfig['users'] = {}
  124. self.userConfig['users'][_type] = {'cookies_dic': old}
  125. users = self.userConfig.get('users', {})
  126. if users.get('master') and users['master'].get('cookies_dic'):
  127. self.session_master.cookies = utils.cookiejar_from_dict(users['master']['cookies_dic'])
  128. self.userid = users['master']['userid']
  129. if users.get('fake') and users['fake'].get('cookies_dic'):
  130. self.session_fake.cookies = utils.cookiejar_from_dict(users['fake']['cookies_dic'])
  131. except:
  132. self.userConfig = {}
  133. self.userConfig = {**self.defaultConfig, **self.userConfig}
  134. dump_config_lock = threading.Lock()
  135. def dump_config(self):
  136. needSaveConfig = ['users', 'channel_list', 'cateLive', 'cateManualLive', 'cateManualLiveExtra']
  137. userConfig_new = {}
  138. for key, value in self.userConfig.items():
  139. dafalutValue = self.defaultConfig.get(key)
  140. if dafalutValue != None and value != dafalutValue or key in needSaveConfig:
  141. userConfig_new[key] = value
  142. self.dump_config_lock.acquire()
  143. with open(f"{dirname}/config.json", 'w', encoding="utf-8") as f:
  144. data = json.dumps(userConfig_new, indent=1, ensure_ascii=False)
  145. f.write(data)
  146. self.dump_config_lock.release()
  147. pool = ThreadPoolExecutor(max_workers=8)
  148. task_pool = []
  149. # 主页
  150. def homeContent(self, filter):
  151. self.pool.submit(self.add_live_filter)
  152. self.pool.submit(self.add_channel_filter)
  153. self.pool.submit(self.add_search_key)
  154. self.pool.submit(self.add_focus_on_up_filter)
  155. self.pool.submit(self.get_tuijian_filter)
  156. self.pool.submit(self.add_fav_filter)
  157. #self.pool.submit(self.homeVideoContent)
  158. needLogin = ['动态', '收藏', '关注', '历史']
  159. cateManual = self.userConfig['cateManual']
  160. if not self.userid and not 'UP' in cateManual or not '动态' in cateManual and not 'UP' in cateManual:
  161. cateManual += ['UP']
  162. classes = []
  163. for k in cateManual:
  164. if k in needLogin and not self.userid:
  165. continue
  166. classes.append({
  167. 'type_name': k,
  168. 'type_id': k
  169. })
  170. self.add_focus_on_up_filter_event.wait()
  171. if 'UP' in cateManual:
  172. self.config["filter"].update({'UP': self.config["filter"].pop('动态')})
  173. result = {'class': classes}
  174. self.add_live_filter_event.wait()
  175. self.add_channel_filter_event.wait()
  176. self.add_fav_filter_event.wait()
  177. self.add_search_key_event.wait()
  178. if filter:
  179. result['filters'] = self.config['filter']
  180. self.pool.submit(self.dump_config)
  181. self.pool.submit(self.test_mirror_site)
  182. return result
  183. # 用户cookies
  184. userid = csrf = ''
  185. session_master = session()
  186. session_vip = session()
  187. session_fake = session()
  188. con = threading.Condition()
  189. getCookie_event = threading.Event()
  190. retries = Retry(total=5,
  191. #status_forcelist=[ 500, 502, 503, 504 ],
  192. backoff_factor=0.1)
  193. adapter = HTTPAdapter(max_retries=retries)
  194. session_master.mount('https://', adapter)
  195. session_vip.mount('https://', adapter)
  196. session_fake.mount('https://', adapter)
  197. def getCookie_dosth(self, co):
  198. c = co.strip().split('=', 1)
  199. if not '%' in c[1]:
  200. c[1] = quote(c[1])
  201. return c
  202. def getCookie(self, _type='master'):
  203. raw_cookie = 'raw_cookie_line'
  204. if _type == 'vip':
  205. raw_cookie = 'raw_cookie_vip'
  206. raw_cookie = self.userConfig.get(raw_cookie)
  207. users = self.userConfig.get('users', {})
  208. user = users.get(_type, {})
  209. if not raw_cookie and not user:
  210. if _type == 'master':
  211. self.getCookie_event.set()
  212. with self.con:
  213. self.con.notifyAll()
  214. return
  215. cookies_dic = user.get('cookies_dic', {})
  216. if raw_cookie:
  217. cookies_dic = dict(map(self.getCookie_dosth, raw_cookie.split(';')))
  218. cookies = utils.cookiejar_from_dict(cookies_dic)
  219. url = 'https://api.bilibili.com/x/web-interface/nav'
  220. content = self.fetch(url, headers=self.header, cookies=cookies)
  221. res = json.loads(content.text)
  222. user['isLogin'] = 0
  223. if res["code"] == 0:
  224. user['isLogin'] = 1
  225. user['userid'] = res["data"]['mid']
  226. user['face'] = res['data']['face']
  227. user['uname'] = res['data']['uname']
  228. user['cookies_dic'] = cookies_dic
  229. user['isVIP'] = int(res['data']['vipStatus'])
  230. if _type == 'master':
  231. self.session_master.cookies = cookies
  232. self.userid = user['userid']
  233. self.csrf = cookies_dic['bili_jct']
  234. if user['isVIP']:
  235. self.session_vip.cookies = cookies
  236. else:
  237. self.userid = ''
  238. users[_type] = user
  239. with self.con:
  240. if len(user) > 1:
  241. self.userConfig.update({'users': users})
  242. if _type == 'master':
  243. self.getCookie_event.set()
  244. getFakeCookie_event = threading.Event()
  245. def getFakeCookie(self, fromSearch=None):
  246. if self.session_fake.cookies:
  247. self.getFakeCookie_event.set()
  248. header = {}
  249. header['User-Agent'] = self.header['User-Agent']
  250. rsp = self.fetch('https://www.bilibili.com', headers=header)
  251. self.session_fake.cookies = rsp.cookies
  252. self.getFakeCookie_event.set()
  253. with self.con:
  254. users = self.userConfig.get('users', {})
  255. users['fake'] = {'cookies_dic': dict(rsp.cookies)}
  256. self.userConfig.update({'users': users})
  257. if not fromSearch:
  258. self.getCookie_event.wait()
  259. if not self.session_master.cookies:
  260. self.session_master.cookies = rsp.cookies
  261. def get_fav_list_dict(self, fav):
  262. fav_dict = {
  263. 'n': fav['title'].replace("<em class=\"keyword\">", "").replace("</em>", "").replace("&quot;",'"').strip(),
  264. 'v': fav['id']}
  265. return fav_dict
  266. add_fav_filter_event = threading.Event()
  267. def add_fav_filter(self):
  268. users = self.userConfig.get('users', {})
  269. if users.get('master') and users['master'].get('userid'):
  270. userid = self.userConfig['users']['master']['userid']
  271. else:
  272. self.getCookie_event.wait()
  273. userid = self.userid
  274. fav_list = []
  275. if userid:
  276. url = 'https://api.bilibili.com/x/v3/fav/folder/created/list-all?up_mid=%s&jsonp=jsonp' % str(userid)
  277. rsp = self._get_sth(url)
  278. jo = json.loads(rsp.text)
  279. if jo['code'] == 0 and jo.get('data'):
  280. fav = jo['data'].get('list')
  281. fav_list = list(map(self.get_fav_list_dict, fav))
  282. fav_top = [{"n": "追番", "v": "1"},{"n": "追剧", "v": "2"}]
  283. fav_config = self.config["filter"].get('收藏')
  284. if fav_config:
  285. fav_config.insert(0, {
  286. "key": "mlid",
  287. "name": "分区",
  288. "value": fav_top + fav_list,
  289. })
  290. self.add_fav_filter_event.set()
  291. self.userConfig["fav_list"] = fav_list
  292. def get_channel_list_dict(self, channel):
  293. channel_dict = {
  294. 'n': channel['name'].replace("<em class=\"keyword\">", "").replace("</em>", "").replace("&quot;",'"').strip(),
  295. 'v': channel['id']}
  296. return channel_dict
  297. def get_channel_list(self):
  298. url = 'https://api.bilibili.com/x/web-interface/web/channel/category/channel/list?id=100&offset=0&page_size=95'
  299. rsp = self._get_sth(url, 'fake')
  300. jo = json.loads(rsp.text)
  301. channel_list = []
  302. if jo['code'] == 0:
  303. channel = jo['data'].get('channels')
  304. self.userConfig['channel_list'] = list(map(self.get_channel_list_dict, channel))
  305. return self.userConfig['channel_list']
  306. add_channel_filter_event = threading.Event()
  307. def add_channel_filter(self):
  308. channel_list = self.userConfig.get('channel_list', '')
  309. channel_list_task = self.pool.submit(self.get_channel_list)
  310. if not channel_list:
  311. channel_list = channel_list_task.result()
  312. channel_config = self.config["filter"].get('频道', [])
  313. if channel_config:
  314. channel_config.insert(0, {
  315. "key": "cid",
  316. "name": "分区",
  317. "value": channel_list,
  318. })
  319. self.config["filter"]['频道'] = channel_config
  320. self.add_channel_filter_event.set()
  321. add_focus_on_up_filter_event = threading.Event()
  322. def add_focus_on_up_filter(self):
  323. first_list = [{"n": "上个视频的UP主", "v": "上个视频的UP主"}]
  324. up_list = []
  325. if not self.session_master.cookies:
  326. self.getCookie_event.wait()
  327. if self.session_master.cookies:
  328. url = 'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all?timezone_offset=-480&type=video&page=1'
  329. rsp = self._get_sth(url)
  330. jo = json.loads(rsp.text)
  331. if jo['code'] == 0 and jo.get('data'):
  332. up = jo['data'].get('items', [])
  333. up_list = list(map(lambda x: {'n': x['modules']["module_author"]['name'], 'v': str(x['modules']["module_author"]['mid'])}, up))
  334. if len(self.focus_on_up_list) > 0:
  335. focus_on_up_list_mid = list(map(lambda x: x['v'], self.focus_on_up_list))
  336. for item in up_list:
  337. if item['v'] in focus_on_up_list_mid:
  338. up_list.remove(item)
  339. up_list.extend(self.focus_on_up_list)
  340. last_list = [{"n": "登录与设置", "v": "登录"}]
  341. up_list = first_list + up_list + last_list
  342. dynamic_config = self.config["filter"].get('动态', [])
  343. if dynamic_config:
  344. dynamic_config.insert(0, {
  345. "key": "mid",
  346. "name": "UP主",
  347. "value": up_list,
  348. })
  349. self.config["filter"]['动态'] = dynamic_config
  350. self.add_focus_on_up_filter_event.set()
  351. def get_live_parent_area_list(self, parent_area):
  352. name = parent_area['name']
  353. id = str(parent_area['id'])
  354. area = parent_area['list']
  355. area_dict = list(map(lambda area: {'n': area['name'], 'v': str(area['parent_id']) + '_' + str(area['id'])}, area))
  356. live_area = {'key': 'tid', 'name': name, 'value': area_dict}
  357. cateLive_name = {'id': id + '_0', 'value': live_area}
  358. return (name, cateLive_name)
  359. def get_live_list(self):
  360. url = 'https://api.live.bilibili.com/xlive/web-interface/v1/index/getWebAreaList?source_id=2'
  361. rsp = self._get_sth(url, 'fake')
  362. jo = json.loads(rsp.text)
  363. cateLive = {}
  364. if jo['code'] == 0:
  365. parent = jo['data']['data']
  366. self.userConfig['cateLive'] = dict(self.pool.map(self.get_live_parent_area_list, parent))
  367. return self.userConfig['cateLive']
  368. def set_default_cateManualLive(self):
  369. cateManualLive = [{'n': '推荐', 'v': '推荐'},]
  370. for name in self.userConfig['cateLive']:
  371. area_dict = {'n': name, 'v': self.userConfig['cateLive'][name]['id']}
  372. cateManualLive.append(area_dict)
  373. self.defaultConfig['cateManualLive'] = cateManualLive
  374. return cateManualLive
  375. add_live_filter_event = threading.Event()
  376. def add_live_filter(self):
  377. cateLive = self.userConfig.get('cateLive', {})
  378. cateLive_task = self.pool.submit(self.get_live_list)
  379. if not cateLive:
  380. cateLive = cateLive_task.result()
  381. default_cateManualLive_task = self.pool.submit(self.set_default_cateManualLive)
  382. self.config["filter"]['直播'] = []
  383. #分区栏
  384. cateManualLive = self.userConfig.get('cateManualLive', [])
  385. if not cateManualLive:
  386. cateManualLive = default_cateManualLive_task.result()
  387. if cateManualLive:
  388. live_area = {'key': 'tid', 'name': '分区', 'value': cateManualLive}
  389. self.config["filter"]['直播'].append(live_area)
  390. #显示分区细分
  391. if int(self.userConfig['showLiveFilterTag']):
  392. for name in cateLive.values():
  393. if len(name['value']['value']) == 1:
  394. continue
  395. self.config["filter"]['直播'].append(name['value'])
  396. self.add_live_filter_event.set()
  397. add_search_key_event = threading.Event()
  398. def add_search_key(self):
  399. focus_on_search_key = self.focus_on_search_key
  400. url = 'https://api.bilibili.com/x/web-interface/search/square?limit=10&platform=web'
  401. rsp = self._get_sth(url, 'fake')
  402. jo = json.loads(rsp.text)
  403. cateLive = {}
  404. if jo['code'] == 0:
  405. trending = jo['data']['trending'].get('list', [])
  406. focus_on_search_key += list(map(lambda x:x['keyword'], trending))
  407. keyword = {"key": "keyword", "name": "搜索词","value": []}
  408. keyword["value"] = list(map(lambda i: {'n': i, 'v': i}, focus_on_search_key))
  409. self.config["filter"]['搜索'].insert(0, keyword)
  410. self.add_search_key_event.set()
  411. def get_tuijian_filter(self):
  412. tuijian_filter = {"番剧时间表": "10001", "国创时间表": "10004", "排行榜": "0", "动画": "1", "音乐": "3", "舞蹈": "129", "游戏": "4", "鬼畜": "119", "知识": "36", "科技": "188", "运动": "234", "生活": "160", "美食": "211", "动物": "217", "汽车": "223", "时尚": "155", "娱乐": "5", "影视": "181", "原创": "origin", "新人": "rookie"}
  413. _dic = [{'n': 'tuijianLis', 'v': '分区'}, {'n': 'rankingLis', 'v': '排行榜'}]
  414. filter_lis = []
  415. for d in _dic:
  416. _filter = {"key": "tid" ,'name': d['v'],"value": []}
  417. t_lis = self.userConfig.get(d['n'], [])
  418. for t in t_lis:
  419. tf = tuijian_filter.get(t)
  420. if not tf:
  421. tf = t
  422. tf_dict = {'n': t, 'v': tf}
  423. _filter["value"].append(tf_dict)
  424. filter_lis.append(_filter)
  425. self.config["filter"]['推荐'] = filter_lis
  426. def __init__(self):
  427. self.load_config()
  428. self.pool.submit(self.getCookie)
  429. self.pool.submit(self.getFakeCookie)
  430. self.pool.submit(self.getCookie, 'vip')
  431. def init(self, extend=""):
  432. print("============{0}============".format(extend))
  433. pass
  434. def isVideoFormat(self, url):
  435. pass
  436. def manualVideoCheck(self):
  437. pass
  438. # 降低内存占用
  439. def format_img(self, img):
  440. img += "@672w_378h_1c.webp"
  441. if not img.startswith('http'):
  442. img = 'https:' + img
  443. return img
  444. def pagination(self, array, pg):
  445. max_number = self.userConfig['page_size'] * int(pg)
  446. min_number = max_number - self.userConfig['page_size']
  447. return array[min_number:max_number]
  448. # 将超过10000的数字换成成以万和亿为单位
  449. def zh(self, num):
  450. if int(num) >= 100000000:
  451. p = round(float(num) / float(100000000), 1)
  452. p = str(p) + '亿'
  453. else:
  454. if int(num) >= 10000:
  455. p = round(float(num) / float(10000), 1)
  456. p = str(p) + '万'
  457. else:
  458. p = str(num)
  459. return p
  460. # 将秒数转化为 时分秒的格式
  461. def second_to_time(self, a):
  462. a = int(a)
  463. if a < 3600:
  464. result = time.strftime("%M:%S", time.gmtime(a))
  465. else:
  466. result = time.strftime("%H:%M:%S", time.gmtime(a))
  467. if str(result).startswith('0'):
  468. result = str(result).replace('0', '', 1)
  469. return result
  470. # 字符串时分秒以及分秒形式转换成秒
  471. def str2sec(self, x):
  472. x = str(x)
  473. try:
  474. h, m, s = x.strip().split(':') # .split()函数将其通过':'分隔开,.strip()函数用来除去空格
  475. return int(h) * 3600 + int(m) * 60 + int(s) # int()函数转换成整数运算
  476. except:
  477. m, s = x.strip().split(':') # .split()函数将其通过':'分隔开,.strip()函数用来除去空格
  478. return int(m) * 60 + int(s) # int()函数转换成整数运算
  479. # 按时间过滤
  480. def filter_duration(self, vodlist, key):
  481. if key == '0':
  482. return vodlist
  483. else:
  484. vod_list_new = [i for i in vodlist if
  485. self.time_diff1[key][0] <= self.str2sec(str(i["vod_remarks"])) < self.time_diff1[key][1]]
  486. return vod_list_new
  487. # 提取番剧id
  488. def find_bangumi_id(self, url):
  489. aid = str(url).strip().split(r"/")[-1]
  490. if not aid:
  491. aid = str(url).strip().split(r"/")[-2]
  492. aid = aid.split(r"?")[0]
  493. return aid
  494. def test_mirror_site(self):
  495. mirror_site = [
  496. 'http://jm92swf.s1002.xrea.com',
  497. 'http://above-mentioned-ice.000webhostapp.com'
  498. ]
  499. time = 9
  500. result = mirror_site[0]
  501. for s in mirror_site:
  502. try:
  503. r = requests_get(s + '/index.php/update.json', timeout=2)
  504. except:
  505. continue
  506. t = r.elapsed.total_seconds()
  507. if t < time:
  508. time = t
  509. result = s
  510. self.mirror_site = result
  511. self.pool.submit(self._checkUpdate, '0')
  512. # 登录二维码
  513. def get_Login_qrcode(self, pg):
  514. result = {}
  515. if int(pg) != 1:
  516. return result
  517. video = [{
  518. "vod_id": 'setting_tab&filter',
  519. "vod_name": '标签与筛选',
  520. "vod_pic": 'https://www.bilibili.com/favicon.ico'
  521. },{
  522. "vod_id": 'setting_liveExtra',
  523. "vod_name": '查看直播细化标签',
  524. "vod_pic": 'https://www.bilibili.com/favicon.ico'
  525. }]
  526. url = 'https://passport.bilibili.com/x/passport-login/web/qrcode/generate'
  527. rsp = self._get_sth(url, 'fake')
  528. jo = json.loads(rsp.text)
  529. if jo['code'] == 0:
  530. id = jo['data']['qrcode_key']
  531. url = jo['data']['url']
  532. account = {'master': '主账号', 'vip': '副账号'}
  533. isLogin = {0: '未登录', 1: '已登录'}
  534. isVIP = {0: '', 1: '👑'}
  535. users = self.userConfig.get('users', {})
  536. for _type, typeName in account.items():
  537. user = users.get(_type)
  538. if user:
  539. video.append({
  540. "vod_id": 'setting_login_' + id,
  541. "vod_name": user['uname'],
  542. "vod_pic": self.format_img(user['face']),
  543. "vod_remarks": isVIP[user['isVIP']] + typeName + ' ' + isLogin[user['isLogin']]
  544. })
  545. #pic_url = {'qrcode': url}
  546. pic_url = {'qrcode': 'https://passport.bilibili.com/h5-app/passport/login/scan?qrcode_key=' + id + '&navhide=1'}
  547. #if not dirname.startswith('/data/'):
  548. # pic_url['qr_chs'] = '208x117'
  549. pic_url['qr_chs'] = '208x117'
  550. video.append({
  551. "vod_id": 'setting_login_' + id,
  552. "vod_name": '有效期3分钟,确认后点这里',
  553. 'vod_pic': self.mirror_site + '/?' + urlencode(pic_url),
  554. })
  555. result['list'] = video
  556. result['page'] = 1
  557. result['pagecount'] = 1
  558. result['limit'] = 1
  559. result['total'] = 1
  560. return result
  561. time_diff1 = {'1': [0, 300],
  562. '2': [300, 900], '3': [900, 1800], '4': [1800, 3600],
  563. '5': [3600, 99999999999999999999999999999999]
  564. }
  565. time_diff = '0'
  566. dynamic_offset = ''
  567. def get_dynamic(self, pg, mid, order):
  568. if mid == '0':
  569. result = {}
  570. if int(pg) == 1:
  571. self.dynamic_offset = ''
  572. 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)
  573. rsp = self._get_sth(url)
  574. jo = json.loads(rsp.text)
  575. if jo['code'] == 0:
  576. self.dynamic_offset = jo['data'].get('offset')
  577. videos = []
  578. vodList = jo['data']['items']
  579. for vod in vodList:
  580. if not vod['visible']:
  581. continue
  582. up = vod['modules']["module_author"]['name']
  583. ivod = vod['modules']['module_dynamic']['major']['archive']
  584. aid = str(ivod['aid']).strip()
  585. title = ivod['title'].strip().replace("<em class=\"keyword\">", "").replace("</em>", "")
  586. img = ivod['cover'].strip()
  587. # remark = str(ivod['duration_text']).strip()
  588. remark = str(self.second_to_time(self.str2sec(ivod['duration_text']))).strip() + ' 🆙' + str(
  589. up).strip() # 显示分钟数+up主名字
  590. videos.append({
  591. "vod_id": 'av' + aid,
  592. "vod_name": title,
  593. "vod_pic": self.format_img(img),
  594. "vod_remarks": remark
  595. })
  596. result['list'] = videos
  597. result['page'] = pg
  598. result['pagecount'] = 9999
  599. result['limit'] = 99
  600. result['total'] = 999999
  601. return result
  602. else:
  603. return self.get_up_videos(mid=mid, pg=pg, order=order)
  604. def get_found_vod(self, vod):
  605. aid = vod.get('aid', '')
  606. if not aid:
  607. aid = vod.get('id', '')
  608. goto = vod.get('goto', '')
  609. if not goto or goto and goto == 'av':
  610. aid = 'av' + str(aid).strip()
  611. elif goto == 'ad':
  612. return []
  613. title = vod['title'].strip()
  614. img = vod['pic'].strip()
  615. is_followed = vod.get('is_followed')
  616. if goto == 'live':
  617. room_info = vod['room_info']
  618. remark = ''
  619. live_status = room_info.get('live_status', '')
  620. if live_status:
  621. remark = '直播中 '
  622. else:
  623. return []
  624. remark += '👁' + room_info['watched_show']['text_small'] + ' 🆙' + vod['owner']['name'].strip()
  625. else:
  626. rcmd_reason = vod.get('rcmd_reason', '')
  627. if rcmd_reason and type(rcmd_reason) == dict and rcmd_reason.get('content'):
  628. reason= ' 🔥' + rcmd_reason['content'].strip()
  629. if '人气飙升' in reason:
  630. reason= ' 🔥人气飙升'
  631. elif is_followed:
  632. reason = ' 已关注'
  633. else:
  634. #reason = " 💬" + self.zh(vod['stat']['danmaku'])
  635. reason = ' 🆙' + vod['owner']['name'].strip()
  636. remark = str(self.second_to_time(vod['duration'])).strip() + " ▶" + self.zh(vod['stat']['view']) + reason
  637. video = [{
  638. "vod_id": aid,
  639. "vod_name": title,
  640. "vod_pic": self.format_img(img),
  641. "vod_remarks": remark
  642. }]
  643. for v in self.pool.map(self.get_found_vod, vod.get('others', [])):
  644. video.extend(v)
  645. return video
  646. _popSeriesInit = 0
  647. def get_found(self, tid, rid, pg):
  648. result = {}
  649. if tid == '推荐':
  650. query = self.encrypt_wbi(fresh_type=4, feed_version='V3', brush=1, fresh_idx=pg, fresh_idx_1h=pg, ps=self.userConfig['page_size'])
  651. url = f'https://api.bilibili.com/x/web-interface/wbi/index/top/feed/rcmd?{query}'
  652. else:
  653. url = 'https://api.bilibili.com/x/web-interface/ranking/v2?rid={0}&type={1}'.format(rid, tid)
  654. if tid == '热门':
  655. url = 'https://api.bilibili.com/x/web-interface/popular?pn={0}&ps={1}'.format(pg, self.userConfig['page_size'])
  656. elif tid == "入站必刷":
  657. url = 'https://api.bilibili.com/x/web-interface/popular/precious'
  658. elif tid == "每周必看":
  659. if not self._popSeriesInit or int(pg) == 1:
  660. url = 'https://api.bilibili.com/x/web-interface/popular/series/list'
  661. rsp = self._get_sth(url, 'fake')
  662. jo = json.loads(rsp.text)
  663. number = self._popSeriesInit = jo['data']['list'][0]['number']
  664. self._popSeriesNum = [int(number), 1]
  665. else:
  666. number = self._popSeriesNum[0]
  667. url = 'https://api.bilibili.com/x/web-interface/popular/series/one?number=' + str(number)
  668. rsp = self._get_sth(url)
  669. jo = json.loads(rsp.text)
  670. if jo['code'] == 0:
  671. videos = []
  672. vodList = jo['data'].get('item')
  673. if not vodList:
  674. vodList = jo['data']['list']
  675. if len(vodList) > self.userConfig['page_size']:
  676. if tid == "每周必看":
  677. _tmp_pg = int(self._popSeriesNum[1])
  678. value = len(vodList) / self.userConfig['page_size'] - _tmp_pg
  679. if value > 0:
  680. value += 1
  681. if not int(value):
  682. self._popSeriesNum = [int(number) - 1, 1]
  683. else:
  684. self._popSeriesNum[1] = _tmp_pg + 1
  685. else:
  686. _tmp_pg = pg
  687. vodList = self.pagination(vodList, _tmp_pg)
  688. for v in self.pool.map(self.get_found_vod, vodList):
  689. videos.extend(v)
  690. result['list'] = videos
  691. result['page'] = pg
  692. result['pagecount'] = 9999
  693. result['limit'] = 99
  694. result['total'] = 999999
  695. return result
  696. def get_bangumi(self, tid, pg, order, season_status):
  697. result = {}
  698. if order == '追番剧':
  699. 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'])
  700. rsp = self._get_sth(url)
  701. else:
  702. 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'])
  703. if order == '热门':
  704. if tid == '1':
  705. url = 'https://api.bilibili.com/pgc/web/rank/list?season_type={0}&day=3'.format(tid)
  706. else:
  707. url = 'https://api.bilibili.com/pgc/season/rank/web/list?season_type={0}&day=3'.format(tid)
  708. rsp = self._get_sth(url, 'fake')
  709. jo = json.loads(rsp.text)
  710. if jo['code'] == 0:
  711. if 'data' in jo:
  712. vodList = jo['data']['list']
  713. else:
  714. vodList = jo['result']['list']
  715. if len(vodList) > self.userConfig['page_size']:
  716. vodList = self.pagination(vodList, pg)
  717. videos = []
  718. for vod in vodList:
  719. aid = str(vod['season_id']).strip()
  720. title = vod['title']
  721. img = vod.get('ss_horizontal_cover')
  722. if not img or tid == '1' and not self.userConfig['bangumi_horizontal_cover']:
  723. if vod.get('first_ep_info') and 'cover' in vod['first_ep_info']:
  724. img = vod['first_ep_info']['cover']
  725. elif vod.get('first_ep') and 'cover' in vod['first_ep']:
  726. img = vod['first_ep']['cover']
  727. else:
  728. img = vod['cover'].strip()
  729. remark = vod.get('index_show', '')
  730. if not remark and vod.get('new_ep') and vod['new_ep'].get('index_show'):
  731. remark = vod['new_ep']['index_show']
  732. remark = remark.replace('更新至', '🆕')
  733. stat = vod.get('stat')
  734. if stat:
  735. remark = '▶' + self.zh(stat.get('view')) + ' ' + remark
  736. videos.append({
  737. "vod_id": 'ss' + aid,
  738. "vod_name": title,
  739. "vod_pic": self.format_img(img),
  740. "vod_remarks": remark
  741. })
  742. result['list'] = videos
  743. result['page'] = pg
  744. result['pagecount'] = 9999
  745. result['limit'] = 90
  746. result['total'] = 999999
  747. return result
  748. def get_timeline(self, tid, pg):
  749. result = {}
  750. url = 'https://api.bilibili.com/pgc/web/timeline/v2?season_type={0}&day_before=2&day_after=4'.format(tid)
  751. rsp = self._get_sth(url, 'fake')
  752. content = rsp.text
  753. jo = json.loads(content)
  754. if jo['code'] == 0:
  755. videos1 = []
  756. vodList = jo['result']['latest']
  757. for vod in vodList:
  758. aid = str(vod['season_id']).strip()
  759. title = vod['title'].strip()
  760. img = vod['ep_cover'].strip()
  761. remark = '🆕' + vod['pub_index'] + ' ❤ ' + vod['follows'].replace('系列', '').replace('追番', '')
  762. videos1.append({
  763. "vod_id": 'ss' + aid,
  764. "vod_name": title,
  765. "vod_pic": self.format_img(img),
  766. "vod_remarks": remark
  767. })
  768. videos2 = []
  769. vodList2 = jo['result']['timeline']
  770. for i in range(len(vodList2)):
  771. vodList = vodList2[i]['episodes']
  772. for vod in vodList:
  773. if str(vod['published']) == "0":
  774. aid = str(vod['season_id']).strip()
  775. title = str(vod['title']).strip()
  776. img = str(vod['ep_cover']).strip()
  777. date = str(time.strftime("%m-%d %H:%M", time.localtime(vod['pub_ts'])))
  778. remark = date + " " + vod['pub_index']
  779. videos2.append({
  780. "vod_id": 'ss' + aid,
  781. "vod_name": title,
  782. "vod_pic": self.format_img(img),
  783. "vod_remarks": remark
  784. })
  785. result['list'] = videos2 + videos1
  786. result['page'] = 1
  787. result['pagecount'] = 1
  788. result['limit'] = 90
  789. result['total'] = 999999
  790. return result
  791. def get_live(self, pg, parent_area_id, area_id):
  792. result = {}
  793. if parent_area_id == '推荐':
  794. url = 'https://api.live.bilibili.com/xlive/web-interface/v1/webMain/getList?platform=web&page=%s' % pg
  795. rsp = self._get_sth(url)
  796. else:
  797. 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)
  798. if parent_area_id == '热门':
  799. url = 'https://api.live.bilibili.com/room/v1/room/get_user_recommend?page=%s&page_size=%s' % (pg, self.userConfig['page_size'])
  800. rsp = self._get_sth(url, 'fake')
  801. jo = json.loads(rsp.text)
  802. if jo['code'] == 0:
  803. videos = []
  804. vodList = jo['data']
  805. if 'recommend_room_list' in vodList:
  806. vodList = vodList['recommend_room_list']
  807. elif 'list' in vodList:
  808. vodList = vodList['list']
  809. for vod in vodList:
  810. aid = str(vod['roomid']).strip()
  811. title = vod['title'].replace("<em class=\"keyword\">", "").replace("</em>", "").replace("&quot;", '"')
  812. img = vod.get('user_cover')
  813. if not img:
  814. img = vod.get('cover')
  815. remark = '👁' + vod['watched_show']['text_small'].strip() + " 🆙" + vod['uname'].strip()
  816. videos.append({
  817. "vod_id": aid,
  818. "vod_name": title,
  819. "vod_pic": self.format_img(img),
  820. "vod_remarks": remark
  821. })
  822. result['list'] = videos
  823. result['page'] = pg
  824. result['pagecount'] = 9999
  825. result['limit'] = 99
  826. result['total'] = 999999
  827. return result
  828. get_up_videos_result = {}
  829. def get_up_videos(self, mid, pg, order):
  830. result = {}
  831. if not mid.isdigit():
  832. if int(pg) == 1:
  833. self.get_up_videos_mid = mid = self.detailContent_args.get('mid', '')
  834. if not mid in self.get_up_videos_result:
  835. self.get_up_videos_result.clear()
  836. self.get_up_videos_result[mid] = []
  837. else:
  838. mid = self.get_up_videos_mid
  839. if int(pg) == 1:
  840. self.get_up_info_event.clear()
  841. self.pool.submit(self.get_up_info, mid)
  842. Space = order2 = ''
  843. if order == 'oldest':
  844. order2 = order
  845. order = 'pubdate'
  846. elif order == 'quicksearch':
  847. Space = '投稿: '
  848. videos = self.get_up_videos_result.get(mid, [])
  849. if videos:
  850. result['list'] = videos
  851. return result
  852. tmp_pg = pg
  853. if order2:
  854. self.get_up_info_event.wait()
  855. tmp_pg = self.up_info[mid]['vod_pc'] - int(pg) + 1
  856. query = self.encrypt_wbi(mid=mid, pn=tmp_pg, ps=self.userConfig['page_size'], order=order)
  857. url = f'https://api.bilibili.com/x/space/wbi/arc/search?{query}'
  858. rsp = self._get_sth(url, 'fake')
  859. content = rsp.text
  860. jo = json.loads(content)
  861. videos = []
  862. if jo['code'] == 0:
  863. vodList = jo['data']['list']['vlist']
  864. for vod in vodList:
  865. aid = str(vod['aid']).strip()
  866. title = vod['title'].strip().replace("<em class=\"keyword\">", "").replace("</em>", "")
  867. img = vod['pic'].strip()
  868. remark = self.second_to_time(self.str2sec(str(vod['length']).strip())) + " ▶" + self.zh(vod['play'])
  869. if not Space:
  870. remark += " 💬" + self.zh(vod['video_review'])
  871. videos.append({
  872. "vod_id": 'av' + aid,
  873. "vod_name": Space + title,
  874. "vod_pic": self.format_img(img),
  875. "vod_remarks": remark
  876. })
  877. if order2:
  878. videos.reverse()
  879. if int(pg) == 1:
  880. self.get_up_info_event.wait()
  881. vodname = self.up_info[mid]['name'] + " 个人主页"
  882. if Space:
  883. vodname = 'UP: ' + self.up_info[mid]['name']
  884. gotoUPHome={
  885. "vod_id": 'up' + str(mid),
  886. "vod_name": vodname,
  887. "vod_pic": self.format_img(self.up_info[mid]['face']),
  888. "vod_remarks": self.up_info[mid]['following'] + ' 👥' + self.up_info[mid]['fans'] + ' 🎬' + str(self.up_info[mid]['vod_count'])
  889. }
  890. videos.insert(0, gotoUPHome)
  891. if Space:
  892. self.get_up_videos_result[mid] = videos
  893. result['list'] = videos
  894. result['page'] = pg
  895. result['pagecount'] = 99
  896. result['limit'] = 99
  897. result['total'] = 999999
  898. return result
  899. history_view_at = 0
  900. def get_history(self, type, pg):
  901. result = {}
  902. if int(pg) == 1:
  903. self.history_view_at = 0
  904. 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)
  905. if type == '稍后再看':
  906. url = 'https://api.bilibili.com/x/v2/history/toview'
  907. rsp = self._get_sth(url)
  908. jo = json.loads(rsp.text)
  909. if jo['code'] == 0:
  910. videos = []
  911. vodList = jo['data'].get('list', [])
  912. if type == '稍后再看':
  913. vodList = self.pagination(vodList, pg)
  914. else:
  915. self.history_view_at = jo['data']['cursor']['view_at']
  916. for vod in vodList:
  917. history = vod.get('history', '')
  918. if history:
  919. business = history['business']
  920. aid = str(history['oid']).strip()
  921. img = vod['cover'].strip()
  922. part = str(history['part']).strip()
  923. else:
  924. business = 'archive'
  925. aid = str(vod["aid"]).strip()
  926. img = vod['pic'].strip()
  927. part = str(vod['page']['part']).strip()
  928. if business == 'article':
  929. continue
  930. elif business == 'pgc':
  931. aid = 'ep' + str(history['epid'])
  932. _total = vod['total']
  933. part = vod.get('show_title')
  934. elif business == 'archive':
  935. aid = 'av' + aid
  936. _total = vod['videos']
  937. title = vod['title'].replace("<em class=\"keyword\">", "").replace("</em>", "").replace("&quot;", '"')
  938. if business == 'live':
  939. live_status = vod.get('badge', '')
  940. remark = live_status + ' 🆙' + vod['author_name'].strip()
  941. else:
  942. if str(vod['progress']) == '-1':
  943. remark = '已看完'
  944. elif str(vod['progress']) == '0':
  945. remark = '刚开始看'
  946. else:
  947. process = str(self.second_to_time(vod['progress'])).strip()
  948. remark = '看到 ' + process
  949. if not _total in [0, 1] and part:
  950. remark += ' (' + str(part) + ')'
  951. videos.append({
  952. "vod_id": aid,
  953. "vod_name": title,
  954. "vod_pic": self.format_img(img),
  955. "vod_remarks": remark
  956. })
  957. result['list'] = videos
  958. result['page'] = pg
  959. result['pagecount'] = 9999
  960. result['limit'] = 90
  961. result['total'] = 999999
  962. return result
  963. def get_fav_detail(self, pg, mlid, order):
  964. result = {}
  965. 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)
  966. rsp = self._get_sth(url)
  967. content = rsp.text
  968. jo = json.loads(content)
  969. if jo['code'] == 0:
  970. videos = []
  971. vodList = jo['data'].get('medias', [])
  972. for vod in vodList:
  973. # 只展示类型为 视频的条目
  974. # 过滤去掉收藏中的 已失效视频;如果不喜欢可以去掉这个 if条件
  975. if vod.get('type') in [2] and vod.get('title') != '已失效视频':
  976. aid = str(vod['id']).strip()
  977. title = vod['title'].replace("<em class=\"keyword\">", "").replace("</em>", "").replace("&quot;",
  978. '"')
  979. img = vod['cover'].strip()
  980. remark = str(self.second_to_time(vod['duration'])).strip() + " ▶" + self.zh(vod['cnt_info']['play']) + " 💬" + self.zh(vod['cnt_info']['danmaku'])
  981. videos.append({
  982. "vod_id": 'av' + aid + '_mlid' + str(mlid),
  983. "vod_name": title,
  984. "vod_pic": self.format_img(img),
  985. "vod_remarks": remark
  986. })
  987. # videos=self.filter_duration(videos, duration_diff)
  988. result['list'] = videos
  989. result['page'] = pg
  990. result['pagecount'] = 9999
  991. result['limit'] = 99
  992. result['total'] = 999999
  993. return result
  994. get_up_info_event = threading.Event()
  995. up_info = {}
  996. def get_up_info(self, mid, **kwargs):
  997. if mid in self.up_info:
  998. self.get_up_info_event.set()
  999. data = kwargs.get('data')
  1000. if not data:
  1001. url = "https://api.bilibili.com/x/web-interface/card?mid={0}".format(mid)
  1002. rsp = self._get_sth(url)
  1003. jRoot = json.loads(rsp.text)
  1004. if jRoot['code'] == 0:
  1005. data = jRoot['data']
  1006. else:
  1007. self.get_up_info_event.set()
  1008. return
  1009. jo = data['card']
  1010. info = {}
  1011. info['following'] = '未关注'
  1012. if data['following']:
  1013. info['following'] = '已关注'
  1014. info['name'] = jo['name'].replace("<em class=\"keyword\">", "").replace("</em>", "")
  1015. info['face'] = jo['face']
  1016. info['fans'] = self.zh(jo['fans'])
  1017. info['like_num'] = self.zh(data['like_num'])
  1018. info['vod_count'] = str(data['archive_count']).strip()
  1019. info['desc'] = jo['Official']['desc'] + " " + jo['Official']['title']
  1020. pc = divmod(int(info['vod_count']), self.userConfig['page_size'])
  1021. info['vod_pc'] =pc[0]
  1022. if pc[1] != 0:
  1023. info['vod_pc'] += 1
  1024. self.up_info[mid] = info
  1025. self.get_up_info_event.set()
  1026. def get_vod_relation(self, id):
  1027. if id.isdigit():
  1028. urlarg = 'aid=' + str(id)
  1029. elif '=' in id:
  1030. urlarg = id
  1031. else:
  1032. urlarg = 'bvid=' + id
  1033. url = 'https://api.bilibili.com/x/web-interface/archive/relation?' + urlarg
  1034. rsp = self._get_sth(url)
  1035. jo = json.loads(rsp.text)
  1036. relation = []
  1037. if jo['code'] == 0:
  1038. jo = jo['data']
  1039. if jo['attention']:
  1040. relation.append('已关注')
  1041. else:
  1042. relation.append('未关注')
  1043. triple = []
  1044. if jo['favorite']:
  1045. triple.append('⭐')
  1046. if jo['like']:
  1047. triple.append('👍')
  1048. coin = jo.get('coin')
  1049. if coin:
  1050. triple.append('💰'*coin)
  1051. if len(triple) == 3:
  1052. relation.append('👍💰⭐')
  1053. else:
  1054. relation.extend(triple)
  1055. if jo['dislike']:
  1056. relation.append('👎')
  1057. if jo['season_fav']:
  1058. relation.append('已订阅合集')
  1059. return relation
  1060. def get_channel(self, pg, cid, order):
  1061. result = {}
  1062. if str(pg) == '1':
  1063. self.channel_offset = ''
  1064. if order == "featured":
  1065. 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'])
  1066. else:
  1067. 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'])
  1068. rsp = self._get_sth(url, 'fake')
  1069. jo = json.loads(rsp.text)
  1070. if jo.get('code') == 0:
  1071. self.channel_offset = jo['data'].get('offset')
  1072. videos = []
  1073. vodList = jo['data']['list']
  1074. if pg == '1' and 'items' in vodList[0]:
  1075. vodList_rank = vodList[0]['items']
  1076. del (vodList[0])
  1077. vodList = vodList_rank + vodList
  1078. for vod in vodList:
  1079. if 'uri' in vod and 'bangumi' in vod['uri']:
  1080. aid = self.find_bangumi_id(vod['uri'])
  1081. else:
  1082. aid = 'av' + str(vod['id']).strip()
  1083. title = vod['name'].replace("<em class=\"keyword\">", "").replace("</em>", "").replace("&quot;", '"')
  1084. img = vod['cover'].strip()
  1085. remark = "▶" + str(vod['view_count'])
  1086. duration = vod.get('duration', '')
  1087. if duration:
  1088. remark = str(self.second_to_time(self.str2sec(duration))).strip() + ' ' + remark
  1089. danmaku = vod.get('danmaku', '')
  1090. like_count = vod.get('like_count', '')
  1091. follow_count = vod.get('follow_count', '')
  1092. if danmaku:
  1093. remark += " 💬" + self.zh(danmaku)
  1094. elif like_count:
  1095. remark += " 👍" + str(like_count)
  1096. elif follow_count:
  1097. remark += " ❤" + str(follow_count)
  1098. videos.append({
  1099. "vod_id": aid,
  1100. "vod_name": title,
  1101. "vod_pic": self.format_img(img),
  1102. "vod_remarks": remark
  1103. })
  1104. result['list'] = videos
  1105. result['page'] = pg
  1106. result['pagecount'] = 9999
  1107. result['limit'] = 99
  1108. result['total'] = 999999
  1109. return result
  1110. def get_follow(self, pg, sort):
  1111. result = {}
  1112. if sort == "最常访问":
  1113. url = 'https://api.bilibili.com/x/relation/followings?vmid={0}&pn={1}&ps=10&order=desc&order_type=attention' .format(self.userid, pg)
  1114. elif sort == "最近关注":
  1115. url = 'https://api.bilibili.com/x/relation/followings?vmid={0}&pn={1}&ps=10&order=desc&order_type='.format(self.userid, pg)
  1116. elif sort == "正在直播":
  1117. url = 'https://api.live.bilibili.com/xlive/web-ucenter/v1/xfetter/GetWebList?page={0}&page_size=10'.format(pg)
  1118. elif sort == "最近访问":
  1119. url = 'https://api.bilibili.com/x/v2/history?pn={0}&ps=15'.format(pg)
  1120. elif sort == "特别关注":
  1121. url = 'https://api.bilibili.com/x/relation/tag?mid={0}&tagid=-10&pn={1}&ps=10'.format(self.userid, pg)
  1122. elif sort == "悄悄关注":
  1123. url = 'https://api.bilibili.com/x/relation/whispers?pn={0}&ps=10'.format(pg)
  1124. else:
  1125. url = 'https://api.bilibili.com/x/relation/followers?vmid={0}&pn={1}&ps=10&order=desc&order_type=attention'.format(self.userid, pg)
  1126. rsp = self._get_sth(url)
  1127. jo = json.loads(rsp.text)
  1128. if jo['code'] != 0:
  1129. return result
  1130. if sort == "特别关注" or sort == "最近访问":
  1131. vodList = jo['data']
  1132. elif sort == "正在直播":
  1133. vodList = jo['data']['rooms']
  1134. else:
  1135. vodList = jo['data']['list']
  1136. if int(pg) == 1:
  1137. self.recently_up_list = []
  1138. follow = []
  1139. for f in vodList:
  1140. remark = ''
  1141. if sort == "最近访问":
  1142. mid = 'up' + str(f['owner']['mid'])
  1143. if mid in self.recently_up_list:
  1144. continue
  1145. self.recently_up_list.append(mid)
  1146. title = str(f['owner']['name']).strip()
  1147. img = str(f['owner']['face']).strip()
  1148. elif sort == "正在直播":
  1149. mid = str(f['room_id'])
  1150. title = f['title'].replace("<em class=\"keyword\">", "").replace("</em>", "").replace("&quot;", '"')
  1151. img = f['cover_from_user'].strip()
  1152. remark = f['uname'].strip()
  1153. else:
  1154. mid = 'up' + str(f['mid'])
  1155. title = str(f['uname']).strip()
  1156. img = str(f['face']).strip()
  1157. if 'special' in f and f['special'] == 1:
  1158. remark = '特别关注'
  1159. follow.append({
  1160. "vod_id": mid,
  1161. "vod_name": title,
  1162. "vod_pic": self.format_img(img),
  1163. "vod_remarks": remark
  1164. })
  1165. result['list'] = follow
  1166. result['page'] = pg
  1167. result['pagecount'] = 9999
  1168. result['limit'] = 99
  1169. result['total'] = 999999
  1170. return result
  1171. homeVideoContent_result = {}
  1172. def homeVideoContent(self):
  1173. if not self.homeVideoContent_result:
  1174. videos = self.get_found(rid='0', tid='all', pg=1)['list'][0:int(self.userConfig['maxHomeVideoContent'])]
  1175. self.homeVideoContent_result['list'] = videos
  1176. return self.homeVideoContent_result
  1177. def categoryContent(self, tid, pg, filter, extend):
  1178. self.stop_heartbeat_event.set()
  1179. if tid == "推荐":
  1180. if 'tid' in extend:
  1181. tid = extend['tid']
  1182. if tid.isdigit():
  1183. tid = int(tid)
  1184. if tid > 10000:
  1185. tid -= 10000
  1186. return self.get_timeline(tid=tid, pg=pg)
  1187. rid = tid
  1188. tid = 'all'
  1189. return self.get_found(tid=tid, rid=rid, pg=pg)
  1190. rid = '0'
  1191. return self.get_found(tid=tid, rid=rid, pg=pg)
  1192. elif tid == "影视":
  1193. tid = '1'
  1194. order = '热门'
  1195. season_status = '-1'
  1196. if 'tid' in extend:
  1197. tid = extend['tid']
  1198. if 'order' in extend:
  1199. order = extend['order']
  1200. if 'season_status' in extend:
  1201. if order == '热门':
  1202. order = '2'
  1203. season_status = extend['season_status']
  1204. return self.get_bangumi(tid, pg, order, season_status)
  1205. elif tid == "动态":
  1206. mid = '0'
  1207. order = 'pubdate'
  1208. if 'mid' in extend:
  1209. mid = extend['mid']
  1210. if 'order' in extend:
  1211. order = extend['order']
  1212. if mid == '0' and not self.userid or mid == '登录':
  1213. return self.get_Login_qrcode(pg)
  1214. return self.get_dynamic(pg=pg, mid=mid, order=order)
  1215. elif tid == '频道':
  1216. order = 'featured'
  1217. cid = random.choice(self.userConfig['channel_list'])
  1218. cid = cid['v']
  1219. if 'order' in extend:
  1220. order = extend['order']
  1221. if 'cid' in extend:
  1222. cid = extend['cid']
  1223. return self.get_channel(pg=pg, cid=cid, order=order)
  1224. elif tid == '直播':
  1225. tid = "热门"
  1226. area_id = '0'
  1227. if 'tid' in extend:
  1228. tid = extend['tid']
  1229. if '_' in tid:
  1230. tids = tid.split('_')
  1231. tid = tids[0]
  1232. area_id = tids[1]
  1233. return self.get_live(pg=pg, parent_area_id=tid, area_id=area_id)
  1234. elif tid == "UP":
  1235. mid = self.detailContent_args.get('mid', '')
  1236. if 'mid' in extend:
  1237. mid = extend['mid']
  1238. if not mid or mid == '登录':
  1239. return self.get_Login_qrcode(pg)
  1240. up_config = self.config["filter"].get('UP')
  1241. if not mid and up_config:
  1242. for i in up_config:
  1243. if i['key'] == 'mid':
  1244. if len(i['value']) > 1:
  1245. mid = i['value'][1]['v']
  1246. break
  1247. order = 'pubdate'
  1248. if 'order' in extend:
  1249. order = extend['order']
  1250. return self.get_up_videos(mid=mid, pg=pg, order=order)
  1251. elif tid == "关注":
  1252. sort = "最常访问"
  1253. if 'sort' in extend:
  1254. sort = extend['sort']
  1255. return self.get_follow(pg, sort)
  1256. elif tid == "收藏":
  1257. mlid = str(self.userConfig['favMode'])
  1258. if 'mlid' in extend:
  1259. mlid = extend['mlid']
  1260. fav_config = self.config["filter"].get('收藏')
  1261. if mlid in ['1', '2']:
  1262. return self.get_bangumi(tid=mlid, pg=pg, order='追番剧', season_status='')
  1263. elif mlid == '0' and fav_config:
  1264. for i in fav_config:
  1265. if i['key'] == 'mlid':
  1266. if len(i['value']) > 1:
  1267. mlid = i['value'][2]['v']
  1268. break
  1269. order = 'mtime'
  1270. if 'order' in extend:
  1271. order = extend['order']
  1272. return self.get_fav_detail(pg=pg, mlid=mlid, order=order)
  1273. elif tid == '历史':
  1274. type = 'all'
  1275. if 'type' in extend:
  1276. type = extend['type']
  1277. if type == 'UP主':
  1278. return self.get_follow(pg=pg, sort='最近访问')
  1279. return self.get_history(type=type, pg=pg)
  1280. else:
  1281. duration_diff = '0'
  1282. if 'duration' in extend:
  1283. duration_diff = extend['duration']
  1284. type = 'video'
  1285. if 'type' in extend:
  1286. type = extend['type']
  1287. order = 'totalrank'
  1288. if 'order' in extend:
  1289. order = extend['order']
  1290. keyword = str(self.search_key)
  1291. search_config = self.config["filter"].get('搜索')
  1292. if not keyword and search_config:
  1293. for i in search_config:
  1294. if i['key'] == 'keyword':
  1295. if len(i['value']) > 0:
  1296. keyword = i['value'][0]['v']
  1297. break
  1298. if 'keyword' in extend:
  1299. keyword = extend['keyword']
  1300. return self.get_search_content(key=keyword, pg=pg, duration_diff=duration_diff, order=order, type=type, ps=self.userConfig['page_size'])
  1301. def get_search_content(self, key, pg, duration_diff, order, type, ps):
  1302. value = None
  1303. if not pg.isdigit():
  1304. value = pg
  1305. pg = 1
  1306. query = self.encrypt_wbi(keyword=key, page=pg, duration=duration_diff, order=order, search_type=type, page_size=ps)
  1307. url = f'https://api.bilibili.com/x/web-interface/wbi/search/type?{query}'
  1308. rsp = self._get_sth(url, 'fake')
  1309. content = rsp.text
  1310. jo = json.loads(content)
  1311. result = {}
  1312. if jo.get('code') == 0 and 'result' in jo['data']:
  1313. videos = []
  1314. vodList = jo['data'].get('result')
  1315. if vodList and type == 'live':
  1316. vodList = vodList.get('live_room')
  1317. if not vodList:
  1318. return result
  1319. for vod in vodList:
  1320. title = ''
  1321. if type == 'bili_user':
  1322. aid = 'up' + str(vod['mid']).strip()
  1323. img = vod['upic'].strip()
  1324. remark = '👥' + self.zh(vod['fans']) + " 🎬" + self.zh(vod['videos'])
  1325. title = vod['uname']
  1326. elif type == 'live':
  1327. aid = str(vod['roomid']).strip()
  1328. img = vod['cover'].strip()
  1329. remark = '👁' + self.zh(vod['online']) + ' 🆙' + vod['uname']
  1330. elif 'media' in type:
  1331. aid = str(vod['season_id']).strip()
  1332. if self.detailContent_args:
  1333. seasons = self.detailContent_args.get('seasons')
  1334. if seasons:
  1335. bangumi_seasons_id = []
  1336. for ss in self.detailContent_args['seasons']:
  1337. bangumi_seasons_id.append(ss['vod_id'])
  1338. if aid + 'ss' in bangumi_seasons_id:
  1339. continue
  1340. aid = 'ss' + aid
  1341. img = vod['cover'].strip()
  1342. remark = str(vod['index_show']).strip().replace('更新至', '🆕')
  1343. else:
  1344. aid = 'av' + str(vod['aid']).strip()
  1345. img = vod['pic'].strip()
  1346. remark = str(self.second_to_time(self.str2sec(vod['duration']))).strip() + " ▶" + self.zh(vod['play'])
  1347. if value == None:
  1348. remark += " 💬" + self.zh(vod['danmaku'])
  1349. if not title:
  1350. title = vod['title'].replace("<em class=\"keyword\">", "").replace("</em>", "").replace("&quot;",'"').replace('&amp;', '&')
  1351. if value:
  1352. title = value + title
  1353. videos.append({
  1354. "vod_id": aid,
  1355. "vod_name": title,
  1356. "vod_pic": self.format_img(img),
  1357. "vod_remarks": remark
  1358. })
  1359. result['list'] = videos
  1360. result['page'] = pg
  1361. result['pagecount'] = 9999
  1362. result['limit'] = 99
  1363. result['total'] = 999999
  1364. return result
  1365. def cleanSpace(self, str):
  1366. return str.replace('\n', '').replace('\t', '').replace('\r', '').replace(' ', '')
  1367. def get_normal_episodes(self, episode):
  1368. ssid = epid = ''
  1369. aid = episode.get('aid', '')
  1370. if not aid:
  1371. aid = self.detailContent_args['aid']
  1372. cid = episode.get('cid', '')
  1373. ep_title = episode.get('title', '')
  1374. if not ep_title:
  1375. ep_title = episode.get('part', '')
  1376. duration = episode.get('duration', '')
  1377. if not duration:
  1378. page = episode.get('page', '')
  1379. if page:
  1380. duration = page['duration']
  1381. badge = long_title = preview = parse = ''
  1382. ssid = self.detailContent_args.get('ssid', '')
  1383. if ssid:
  1384. ssid = '_ss' + ssid
  1385. epid = episode.get('id', '')
  1386. if epid:
  1387. epid = '_ep' + str(epid)
  1388. if duration and str(duration).endswith('000'):
  1389. duration = int(duration / 1000)
  1390. if ep_title.isdigit():
  1391. ep_title = '第' + ep_title + self.detailContent_args['title_type']
  1392. badge = episode.get('badge', '')
  1393. if not self.session_vip.cookies and badge == '会员' and self.userConfig['bangumi_vip_parse'] or badge == '付费' and self.userConfig['bangumi_pay_parse']:
  1394. parse = '_parse'
  1395. if self.session_vip.cookies and self.userConfig['hide_bangumi_vip_badge']:
  1396. badge = badge.replace('会员', '')
  1397. if self.userConfig['hide_bangumi_preview'] and badge == '预告':
  1398. badge = badge.replace('预告', '')
  1399. preview = 1
  1400. if badge:
  1401. badge = '【' + badge + '】'
  1402. long_title = episode.get('long_title', '')
  1403. if not badge and long_title:
  1404. long_title = ' ' + long_title
  1405. title = ep_title + badge + long_title
  1406. title = title.replace("#", "﹟").replace("$", "﹩")
  1407. if duration:
  1408. duration = '_dur' + str(duration)
  1409. url = '{0}${1}_{2}{3}{4}{5}'.format(title, aid, cid, ssid, epid, duration)
  1410. fromep = self.detailContent_args.get('fromep', '')
  1411. if '_' + str(fromep) == epid:
  1412. self.detailContent_args['fromep'] = url
  1413. replyList = self.detailContent_args.get('Reply')
  1414. if '_' + str(fromep) == epid or not fromep and replyList == None:
  1415. self.detailContent_args['Reply'] = ''
  1416. if self.userConfig['show_vod_hot_reply']:
  1417. self.get_vod_hot_reply_event.clear()
  1418. self.pool.submit(self.get_vod_hot_reply, aid)
  1419. if ssid:
  1420. if preview:
  1421. return url, ''
  1422. if parse:
  1423. self.detailContent_args['parse'] = 1
  1424. if long_title:
  1425. long_title = '【解析】' + long_title
  1426. ep_title += long_title
  1427. parseurl = '{0}${1}_{2}{3}{4}{5}{6}'.format(ep_title, aid, cid, ssid, epid, duration, parse)
  1428. if '_' + str(fromep) == epid:
  1429. self.detailContent_args['fromep'] += '#' + parseurl
  1430. else:
  1431. parseurl = url
  1432. return url, parseurl
  1433. else:
  1434. return url
  1435. def get_ugc_season(self, section, sections_len):
  1436. if sections_len > 1:
  1437. sec_title = self.detailContent_args['season_title'] + ' ' + section['title']
  1438. else:
  1439. sec_title = self.detailContent_args['season_title']
  1440. sec_title = sec_title.replace("#", "﹟").replace("$", "﹩")
  1441. episodes = section.get('episodes')
  1442. playUrl = '#'.join(map(self.get_normal_episodes, episodes))
  1443. result = (sec_title, playUrl)
  1444. return result
  1445. get_vod_hot_reply_event = threading.Event()
  1446. def get_vod_hot_reply(self, oid):
  1447. url = 'http://api.bilibili.com/x/v2/reply/main?type=1&ps=30&oid=' + str(oid)
  1448. rsp = self._get_sth(url, 'fake')
  1449. jRoot = json.loads(rsp.text)
  1450. if jRoot['code'] == 0:
  1451. replies = jRoot['data'].get('replies')
  1452. top_replies = jRoot['data'].get('top_replies')
  1453. if replies and top_replies:
  1454. replies = top_replies + replies
  1455. if replies:
  1456. up_mid = jRoot['data']['upper']['mid']
  1457. ReplyList = []
  1458. Reply_jump = []
  1459. for r in replies:
  1460. rpid = r['rpid']
  1461. sex = r['member']['sex']
  1462. if sex and sex == '女':
  1463. sex = '👧'
  1464. else:
  1465. sex = '👦'
  1466. name = sex + r['member']['uname'] + ':'
  1467. mid = r['mid']
  1468. if mid == up_mid:
  1469. name = '🆙' + name
  1470. like = '👍' + self.zh(r['like'])
  1471. message = r['content']['message']
  1472. if '/note-app/' in message:
  1473. continue
  1474. content = like + ' ' + name + message
  1475. content = content.replace("#", "﹟").replace("$", "﹩")
  1476. content += '$' + str(oid) + '_' + str(rpid) + '_notplay_reply'
  1477. ReplyList.append(content)
  1478. jump_url = r['content'].get('jump_url',{})
  1479. for key, value in jump_url.items():
  1480. if not value.get('app_url_schema') and not value.get('pc_url'):
  1481. if key.startswith('https://www.bilibili.com/'):
  1482. key = str(key).split('?')[0].split('/')
  1483. while key[-1] == '':
  1484. key.pop(-1)
  1485. key = key[-1]
  1486. if key.startswith('https://b23.tv/') or key.startswith('BV') or key.startswith('ep') or key.startswith('ss'):
  1487. title = str(value['title']).replace("#", "﹟").replace("$", "﹩")
  1488. vod = {'vod_id': str(key), 'vod_name': '评论:' + title}
  1489. if not vod in Reply_jump:
  1490. Reply_jump.append(vod)
  1491. title = '快搜:' + str(key) +' ' + title
  1492. content = title + '$ '
  1493. ReplyList.append(content)
  1494. self.detailContent_args['Reply'] = '#'.join(ReplyList)
  1495. self.detailContent_args['Reply_jump'] = Reply_jump
  1496. self.get_vod_hot_reply_event.set()
  1497. detailContent_args = {}
  1498. def detailContent(self, array):
  1499. self.stop_heartbeat_event.set()
  1500. aid = array[0]
  1501. if aid.startswith('edgeid'):
  1502. return self.interaction_detailContent(aid)
  1503. self.detailContent_args = {}
  1504. if aid.startswith('https://b23.tv/'):
  1505. try:
  1506. r = requests_get(url=aid, headers=self.header, allow_redirects=False)
  1507. url = r.headers['Location'].split('?')[0].split('/')
  1508. while url[-1] == '':
  1509. url.pop(-1)
  1510. aid = url[-1]
  1511. if not aid.startswith('BV', 0, 2):
  1512. return {}
  1513. except:
  1514. return {}
  1515. id = mlid = urlargs = ''
  1516. self.get_vod_hot_reply_event.set()
  1517. if aid.startswith('setting'):
  1518. aid = aid.split('_')
  1519. if aid[1] == 'tab&filter':
  1520. return self.setting_tab_filter_detailContent()
  1521. elif aid[1] == 'liveExtra':
  1522. return self.setting_liveExtra_detailContent()
  1523. elif aid[1] == 'login':
  1524. key = aid[2]
  1525. return self.setting_login_detailContent(key)
  1526. elif aid.startswith('av') or aid.startswith('BV'):
  1527. for i in aid.split('_'):
  1528. if i.startswith('av'):
  1529. id = i.replace('av', '', 1)
  1530. urlargs = 'aid=' + str(id)
  1531. elif i.startswith('BV'):
  1532. id = i
  1533. urlargs = 'bvid=' + id
  1534. elif i.startswith('mlid'):
  1535. mlid = i.replace('mlid', '', 1)
  1536. #获取热门评论
  1537. if self.userConfig['show_vod_hot_reply']:
  1538. self.detailContent_args['Reply'] = ''
  1539. self.get_vod_hot_reply_event.clear()
  1540. self.pool.submit(self.get_vod_hot_reply, id)
  1541. elif 'up' in aid:
  1542. return self.up_detailContent(array)
  1543. elif 'ss' in aid or 'ep' in aid:
  1544. return self.ysContent(array)
  1545. elif aid.isdigit():
  1546. return self.live_detailContent(array)
  1547. relation = self.pool.submit(self.get_vod_relation, urlargs)
  1548. url = 'https://api.bilibili.com/x/web-interface/view/detail?' + urlargs
  1549. rsp = self._get_sth(url, 'fake')
  1550. jRoot = json.loads(rsp.text)
  1551. if jRoot['code'] != 0:
  1552. return {}
  1553. jo = jRoot['data']['View']
  1554. redirect_url = jo.get('redirect_url', '')
  1555. if 'bangumi' in redirect_url:
  1556. ep_id = self.find_bangumi_id(redirect_url)
  1557. new_array = []
  1558. for i in array:
  1559. new_array.append(i)
  1560. new_array[0] = ep_id
  1561. return self.ysContent(new_array)
  1562. self.detailContent_args['mid'] = up_mid = str(jo['owner']['mid'])
  1563. self.detailContent_args['aid'] = aid = jo.get('aid')
  1564. self.pool.submit(self.get_up_info, mid=up_mid, data=jRoot['data'].get('Card'))
  1565. #相关合集
  1566. ugc_season = jo.get('ugc_season')
  1567. if ugc_season:
  1568. self.detailContent_args['season_title'] = ugc_season['title']
  1569. sections = ugc_season['sections']
  1570. sections_len = len(sections)
  1571. ugc_season_task = []
  1572. for section in sections:
  1573. t = self.pool.submit(self.get_ugc_season, section, sections_len)
  1574. ugc_season_task.append(t)
  1575. #相关推荐
  1576. jo_Related = jRoot['data'].get('Related')
  1577. #正片
  1578. pages = jo['pages']
  1579. title = jo['title'].replace("<em class=\"keyword\">", "").replace("</em>", "")
  1580. pic = jo['pic']
  1581. up_name = jo['owner']['name']
  1582. desc = jo['desc'].strip()
  1583. typeName = jo['tname']
  1584. year = time.strftime("%Y%m%d", time.localtime(jo['pubdate'])) # 投稿时间本地年月日表示
  1585. date = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(jo['pubdate'])) # 投稿时间本地年月日表示
  1586. stat = jo['stat']
  1587. # 演员项展示视频状态,包括以下内容:
  1588. status = []
  1589. status.append('▶' + self.zh(stat['view']))
  1590. status.append('💬' + self.zh(stat['danmaku']))
  1591. status.append('👍' + self.zh(stat['like']))
  1592. honor = jo.get('honor_reply')
  1593. if honor:
  1594. status.insert(0, '🏅' + honor['honor'][0]['desc'])
  1595. if not honor or honor and honor['honor'][0]['type'] == 4:
  1596. status.append('💰' + self.zh(stat['coin']))
  1597. status.append('⭐' + self.zh(stat['favorite']))
  1598. remark = str(jo['duration']).strip()
  1599. duration = jo['duration']
  1600. _is_stein_gate = jo['rights'].get('is_stein_gate', 0)
  1601. vod = {
  1602. "vod_id": 'av' + str(aid),
  1603. "vod_name": title,
  1604. "vod_pic": pic,
  1605. "type_name": typeName,
  1606. "vod_year": year,
  1607. "vod_area": "bilidanmu",
  1608. "vod_remarks": remark, # 不会显示
  1609. # 'vod_tag': 'folder', # 不会显示
  1610. "vod_actor": " ".join(status),
  1611. "vod_content": ' ❤️日期:'+date+'❤️ '+desc
  1612. }
  1613. secondP = []
  1614. if self.userid:
  1615. #做点什么
  1616. follow = '➕关注$1_notplay_follow'
  1617. unfollow = '➖取关$2_notplay_follow'
  1618. like = '👍点赞$1_notplay_like'
  1619. unlike = '👍🏻取消点赞$2_notplay_like'
  1620. coin1 = '👍💰投币$1_notplay_coin'
  1621. coin2 = '👍💰💰投2币$2_notplay_coin'
  1622. triple = '👍💰⭐三连$notplay_triple'
  1623. secondPList = [follow, triple, like, coin1, coin2, unfollow, unlike]
  1624. if mlid:
  1625. favdel = f"☆取消收藏${mlid}_del_notplay_fav"
  1626. secondPList.append(favdel)
  1627. for fav in self.userConfig.get("fav_list", []):
  1628. folder = fav['n'].replace("#", "﹟").replace("$", "﹩")
  1629. ids = fav['v']
  1630. fav = '⭐{}${}_add_notplay_fav'.format(folder, ids)
  1631. secondPList.append(fav)
  1632. defaultQn = int(self.userConfig['vodDefaultQn'])
  1633. if defaultQn > 116:
  1634. secondPList.append('⚠️限高1080$116_notplay_vodTMPQn')
  1635. secondP = ['#'.join(secondPList)]
  1636. AllPt = []
  1637. AllPu = []
  1638. if pages:
  1639. AllPt = ['B站']
  1640. if _is_stein_gate:
  1641. AllPt = ['互动视频【快搜继续】']
  1642. AllPu = ['#'.join(self.pool.map(self.get_normal_episodes, pages))]
  1643. if secondP:
  1644. AllPt.append('做点什么')
  1645. AllPu.extend(secondP)
  1646. if jo_Related:
  1647. AllPt.append('相关推荐')
  1648. AllPu.append('#'.join(self.pool.map(self.get_normal_episodes, jo_Related)))
  1649. if self.userConfig['show_vod_hot_reply']:
  1650. self.get_vod_hot_reply_event.wait()
  1651. replyList = self.detailContent_args.get('Reply', '')
  1652. if replyList:
  1653. AllPt.append('热门评论')
  1654. AllPu.extend([replyList])
  1655. if ugc_season:
  1656. for t in as_completed(ugc_season_task):
  1657. AllPt.append(t.result()[0])
  1658. AllPu.append(t.result()[1])
  1659. vod['vod_play_from'] = "$$$".join(AllPt)
  1660. vod['vod_play_url'] = "$$$".join(AllPu)
  1661. #视频关系
  1662. vod['vod_director'] = '🆙 ' + up_name + ' 👥 ' + self.up_info[up_mid]['fans'] + ' ' + ' '.join(relation.result())
  1663. #互动视频套用
  1664. if _is_stein_gate:
  1665. self.detailContent_args['AllPt'] = AllPt.copy()
  1666. self.detailContent_args['AllPu'] = AllPu.copy()
  1667. self.detailContent_args['vod_list'] = vod.copy()
  1668. result = {
  1669. 'list': [
  1670. vod
  1671. ]
  1672. }
  1673. return result
  1674. def interaction_detailContent(self, array=''):
  1675. array = array.split('_')
  1676. cid = edgeid = 0
  1677. for i in array:
  1678. if i.startswith('edgeid'):
  1679. edgeid = i.replace('edgeid', '')
  1680. elif i.startswith('cid'):
  1681. cid = i.replace('cid', '')
  1682. aid = self.detailContent_args.get('aid')
  1683. graph_version = self.detailContent_args.get('graph_version')
  1684. url = 'https://api.bilibili.com/x/stein/edgeinfo_v2?aid={0}&graph_version={1}&edge_id={2}'.format(aid, graph_version, edgeid)
  1685. rsp = self._get_sth(url, 'fake')
  1686. jo = json.loads(rsp.text)
  1687. data = jo.get('data')
  1688. result = {}
  1689. if data:
  1690. questions = data['edges'].get('questions', [])
  1691. choice_lis = []
  1692. for question in questions:
  1693. q_title = str(question.get('title', ''))
  1694. if q_title:
  1695. q_title += ' '
  1696. for choice in question.get('choices', []):
  1697. c_edgeid = str(choice['id'])
  1698. c_cid = str(choice['cid'])
  1699. option = str(choice.get('option', ''))
  1700. choice_lis.append({
  1701. "vod_id": 'edgeid' + c_edgeid + '_' + 'cid' + c_cid,
  1702. "vod_name": '互动:' + q_title + option,
  1703. })
  1704. self.detailContent_args['interaction'] = choice_lis.copy()
  1705. if edgeid:
  1706. AllPt = self.detailContent_args['AllPt'].copy()
  1707. if not choice_lis:
  1708. AllPt[0] = '互动视频'
  1709. AllPu = self.detailContent_args['AllPu'].copy()
  1710. title = str(data['title']).replace("#", "﹟").replace("$", "﹩")
  1711. url = '{0}${1}_{2}'.format(title, aid, cid)
  1712. AllPu[0] = url
  1713. vod = self.detailContent_args['vod_list'].copy()
  1714. vod['vod_play_from'] = "$$$".join(AllPt)
  1715. vod['vod_play_url'] = "$$$".join(AllPu)
  1716. result['list'] = [vod]
  1717. return result
  1718. def up_detailContent(self, array):
  1719. self.detailContent_args['mid'] = mid = array[0].replace('up', '')
  1720. self.get_up_info_event.clear()
  1721. self.pool.submit(self.get_up_info, mid)
  1722. first = '是否关注$ '
  1723. follow = '关注$1_notplay_follow'
  1724. unfollow = '取消关注$2_notplay_follow'
  1725. qqfollow = '悄悄关注$3_notplay_follow'
  1726. spfollow = '特别关注$-10_notplay_special_follow'
  1727. unspfollow = '取消特别关注$0_notplay_special_follow'
  1728. doWhat = [first, follow, qqfollow, spfollow, unfollow, unspfollow]
  1729. doWhat = '#'.join(doWhat)
  1730. self.get_up_info_event.wait()
  1731. up_info = self.up_info[mid]
  1732. vod = {
  1733. "vod_id": 'up' + str(mid),
  1734. "vod_name": up_info['name'] + " 个人主页",
  1735. "vod_pic": up_info['face'],
  1736. "vod_remarks": "", # 不会显示
  1737. "vod_tags": 'mv', # 不会显示
  1738. "vod_actor": "👥 " + up_info['fans'] + " 🎬 " + up_info['vod_count'] + " 👍 " + up_info['like_num'],
  1739. "vod_director": '🆙 ' + up_info['name'] + " " + up_info['following'] + ' UID:' +str(mid),
  1740. "vod_content": up_info['desc'],
  1741. 'vod_play_from': '关注TA$$$视频投稿在动态标签——筛选——上个UP,选择后查看'
  1742. }
  1743. vod['vod_play_url'] = doWhat
  1744. result = {
  1745. 'list': [
  1746. vod
  1747. ]
  1748. }
  1749. return result
  1750. def setting_login_detailContent(self, key):
  1751. cookie_dic_tmp = self.cookie_dic_tmp.get(key, '')
  1752. message = ''
  1753. if not cookie_dic_tmp:
  1754. message = self.get_cookies(key)
  1755. if message:
  1756. message = f"【{message}】通过手机客户端扫码确认登录后点击相应按钮设置账号"
  1757. else:
  1758. message = '【已扫码并确认登录】请点击相应按钮设置当前获取的账号为:'
  1759. vod = {
  1760. "vod_name": "登录与设置",
  1761. "vod_content": '通过手机客户端扫码并确认登录后,点击相应按钮设置cookie,设置后不需要管嗅探结果,直接返回二维码页面刷新,查看是否显示已登录,已登录即可重新打开APP以加载全部标签',
  1762. }
  1763. vod_play_from = ['登录$$$退出登录']
  1764. vod_play_url = []
  1765. first = message + '$ '
  1766. login = '设置为主账号,动态收藏关注等内容源于此$' + str(key) + '_master_login_setting'
  1767. login_vip = '设置为备用的VIP账号,仅用于播放会员番剧$' + str(key) + '_vip_login_setting'
  1768. vod_play_url.append('#'.join([first, login, login_vip]))
  1769. second = '点击相应按钮退出账号>>>$ '
  1770. logout = '退出主账号$master_logout_setting'
  1771. logout_vip = '退出备用的VIP账号$vip_logout_setting'
  1772. vod_play_url.append('#'.join([second, logout, logout_vip]))
  1773. cate_lis = [{
  1774. 'f': '主页站点推荐栏',
  1775. 'c': 'maxHomeVideoContent',
  1776. 'd': {
  1777. '3': '3图',
  1778. '4': '4图',
  1779. '6': '6图',
  1780. '8': '8图',
  1781. '10': '10图',
  1782. }
  1783. },{
  1784. 'f': '视频画质',
  1785. 'c': 'vodDefaultQn',
  1786. 'd': self.vod_qn_id
  1787. },{
  1788. 'f': '视频编码',
  1789. 'c': 'vodDefaultCodec',
  1790. 'd': self.vod_codec_id
  1791. },{
  1792. 'f': '音频码率',
  1793. 'c': 'vodDefaultAudio',
  1794. 'd': self.vod_audio_id
  1795. },{
  1796. 'f': '收藏默认显示',
  1797. 'c': 'favMode',
  1798. 'd': {
  1799. '0': '默认收藏夹',
  1800. '1': '追番',
  1801. '2': '追剧',
  1802. }
  1803. },{
  1804. 'f': '上传播放进度',
  1805. 'c': 'heartbeatInterval',
  1806. 'd': {
  1807. '0': '关',
  1808. '15': '开',
  1809. }
  1810. },{
  1811. 'f': '直播筛选细化',
  1812. 'c': 'showLiveFilterTag',
  1813. 'd': {
  1814. '0': '关',
  1815. '1': '开',
  1816. }
  1817. }]
  1818. #检查更新
  1819. update_dic = {
  1820. 'f': '检查更新',
  1821. 'c': 'checkUpdate'
  1822. }
  1823. newVersion = self.userConfig.get('newVersion', '检查失败')
  1824. updateStatus = actionCode = 0
  1825. if newVersion != '检查失败':
  1826. newVersion = '远端:' + str(self.userConfig['newVersion']['ver'])
  1827. actionCode = 1
  1828. updateStatus = self.userConfig['newVersion'].get('status')
  1829. update_dic['d'] = {str(actionCode): newVersion}
  1830. if updateStatus:
  1831. update_dic['d'][' '] = updateStatus
  1832. cate_lis.insert(0, update_dic)
  1833. for cate in cate_lis:
  1834. vod_play_from.append(cate['f'])
  1835. if cate['c'] == 'checkUpdate':
  1836. defaultConfig = self.userConfig['currentVersion']
  1837. else:
  1838. defaultConfig = cate['d'][str(int(self.userConfig[cate['c']]))]
  1839. if 'vodDefaultAudio' == cate['c']:
  1840. defaultConfig = str(defaultConfig).replace('000', 'k')
  1841. url = ['当前:' + defaultConfig + '$ ']
  1842. for id, name in cate['d'].items():
  1843. if 'vodDefaultAudio' == cate['c']:
  1844. name = str(name).replace('000', 'k')
  1845. url.append(name + '$' + str(id) + '_' + cate['c'] + '_setting')
  1846. vod_play_url.append('#'.join(url))
  1847. vod['vod_play_from'] = '$$$'.join(vod_play_from)
  1848. vod['vod_play_url'] = '$$$'.join(vod_play_url)
  1849. result = {
  1850. 'list': [
  1851. vod
  1852. ]
  1853. }
  1854. return result
  1855. def setting_tab_filter_detailContent(self):
  1856. vod = {
  1857. "vod_name": "标签与筛选",
  1858. "vod_content": '依次点击各标签,同一标签第一次点击为添加,第二次删除,可以返回到二维码页后重进本页查看预览,最后点击保存,未选择的将追加到末尾,如果未保存就重启app,将丢失未保存的配置',
  1859. }
  1860. vod_play_from = []
  1861. vod_play_url = []
  1862. cate_lis = [
  1863. {'n': 'cateManual', 'v': '标签'},
  1864. {'n': 'tuijianLis', 'v': '推荐[分区]'},
  1865. {'n': 'rankingLis', 'v': '推荐[排行榜]'},
  1866. {'n': 'cateManualLive', 'v': '直播'},
  1867. ]
  1868. for cate in cate_lis:
  1869. _List = cate['n']
  1870. vod_play_from.append(cate['v'])
  1871. List_tmp = self.userConfig.get(str(_List) + '_tmp', [])
  1872. status = ''
  1873. if List_tmp:
  1874. status = '【未保存】'
  1875. else:
  1876. List_tmp = self.userConfig.get(_List, [])
  1877. if not List_tmp:
  1878. List_tmp = self.defaultConfig.get(_List)
  1879. if List_tmp and type(List_tmp[0]) == dict:
  1880. List_tmp = list(map(lambda x:x['n'], List_tmp))
  1881. url = ['当前: ' + ','.join(List_tmp) + '$ ', f"{status}点击这里保存$_{_List}_save_setting", f"点击这里恢复默认并保存$_{_List}_clear_setting"]
  1882. defaultConfig = self.defaultConfig[_List].copy()
  1883. if _List == 'cateManual' and not 'UP' in defaultConfig:
  1884. defaultConfig.append('UP')
  1885. elif _List == 'cateManualLive':
  1886. extra_live_filter = self.userConfig.get('cateManualLiveExtra', [])
  1887. defaultConfig.extend(extra_live_filter.copy())
  1888. for name in defaultConfig:
  1889. value = str(name)
  1890. if type(name) == dict:
  1891. value = name['n'] + '@@@' + name['v'].replace('_', '@@@')
  1892. name = name['n']
  1893. url.append(f"{name}${value}_{_List}_setting")
  1894. vod_play_url.append('#'.join(url))
  1895. vod['vod_play_from'] = '$$$'.join(vod_play_from)
  1896. vod['vod_play_url'] = '$$$'.join(vod_play_url)
  1897. result = {
  1898. 'list': [
  1899. vod
  1900. ]
  1901. }
  1902. return result
  1903. def setting_liveExtra_detailContent(self):
  1904. vod = {
  1905. "vod_name": "查看直播细化标签",
  1906. "vod_content": '点击想要添加的标签,同一标签第一次点击为添加,第二次删除,完成后在[标签与筛选]页继续操作,以添加到直播筛选分区列中',
  1907. }
  1908. vod_play_from = ['已添加']
  1909. cateManualLiveExtra = self.userConfig.get('cateManualLiveExtra', [])
  1910. vod_play_url = ['点击相应标签(只)可以删除$ #清空$clear_liveFilter_setting']
  1911. for name in cateManualLiveExtra:
  1912. value = name['v']
  1913. name = name['n']
  1914. vod_play_url.append(name + '$' + 'del_' + name + '_' + value + '_liveFilter_setting')
  1915. vod_play_url = ['#'.join(vod_play_url)]
  1916. cateLive = self.userConfig.get('cateLive', {})
  1917. for parent, parent_dic in cateLive.items():
  1918. area_dic = parent_dic['value']['value']
  1919. if len(area_dic) == 1:
  1920. continue
  1921. vod_play_from.append(parent)
  1922. url = []
  1923. for area in area_dic:
  1924. name = str(area['n']).replace('_', '-').replace("#", "﹟").replace("$", "﹩")
  1925. id = str(area['v']).replace('_', '@@@').replace("#", "﹟").replace("$", "﹩")
  1926. url.append(name + '$add_' + name + '_' + id + '_liveFilter_setting')
  1927. vod_play_url.append('#'.join(url))
  1928. vod['vod_play_from'] = '$$$'.join(vod_play_from)
  1929. vod['vod_play_url'] = '$$$'.join(vod_play_url)
  1930. result = {
  1931. 'list': [
  1932. vod
  1933. ]
  1934. }
  1935. return result
  1936. def get_all_season(self, season):
  1937. season_id = str(season['season_id'])
  1938. season_title = season['season_title']
  1939. if season_id == self.detailContent_args['ssid']:
  1940. self.detailContent_args['s_title'] = season_title
  1941. pic = season['cover']
  1942. remark = season['new_ep']['index_show']
  1943. result = {
  1944. "vod_id": season_id + 'ss',
  1945. "vod_name": '系列:' + season_title,
  1946. "vod_pic": self.format_img(pic),
  1947. "vod_remarks": remark}
  1948. return result
  1949. def get_bangumi_section(self, section):
  1950. sec_title = section['title'].replace("#", "﹟").replace("$", "﹩")
  1951. sec_type = section['type']
  1952. if sec_type in [1, 2] and len(section['episode_ids']) == 0:
  1953. episodes = section['episodes']
  1954. playUrl = '#'.join(map(lambda x: self.get_normal_episodes(x)[0], episodes))
  1955. return (sec_title, playUrl)
  1956. def ysContent(self, array):
  1957. aid = array[0]
  1958. if 'ep' in aid:
  1959. self.detailContent_args['fromep'] = aid
  1960. aid = 'ep_id=' + aid.replace('ep', '')
  1961. elif 'ss' in aid:
  1962. aid = 'season_id=' + aid.replace('ss', '')
  1963. url = "https://api.bilibili.com/pgc/view/web/season?{0}".format(aid)
  1964. rsp = self._get_sth(url, 'fake')
  1965. jRoot = json.loads(rsp.text)
  1966. jo = jRoot['result']
  1967. self.detailContent_args['ssid'] = str(jo['season_id'])
  1968. title = jo['title']
  1969. self.detailContent_args['s_title'] = jo['season_title']
  1970. self.detailContent_args['title_type'] = '集'
  1971. if jo['type'] in [1, 4]:
  1972. self.detailContent_args['title_type'] = '话'
  1973. #添加系列到搜索
  1974. seasons = jo.get('seasons')
  1975. if len(seasons) == 1:
  1976. self.detailContent_args['s_title'] = seasons[0]['season_title']
  1977. seasons = 0
  1978. else:
  1979. self.detailContent_args['seasons'] = list(self.pool.map(self.get_all_season, seasons))
  1980. #获取正片
  1981. episodes = jo.get('episodes')
  1982. #获取花絮
  1983. section_task = []
  1984. for s in jo.get('section', []):
  1985. if s:
  1986. t = self.pool.submit(self.get_bangumi_section, s)
  1987. section_task.append(t)
  1988. pic = jo['cover']
  1989. typeName = jo['share_sub_title']
  1990. date = jo['publish']['pub_time'][0:4]
  1991. dec = jo['evaluate']
  1992. remark = jo['new_ep']['desc']
  1993. stat = jo['stat']
  1994. # 演员和导演框展示视频状态,包括以下内容:
  1995. status = "▶" + self.zh(stat['views']) + " 💬" + self.zh(stat['danmakus']) + " 👍" + self.zh(stat['likes']) + " 💰" + self.zh(
  1996. stat['coins']) + " ❤" + self.zh(stat['favorites'])
  1997. if 'rating' in jo:
  1998. status = str(jo['rating']['score']) + '分 ' + status
  1999. vod = {
  2000. "vod_id": 'ss' + self.detailContent_args['ssid'],
  2001. "vod_name": title,
  2002. "vod_pic": pic,
  2003. "type_name": typeName,
  2004. "vod_year": date,
  2005. "vod_area": "bilidanmu",
  2006. "vod_remarks": remark,
  2007. "vod_actor": status,
  2008. #"vod_director": score,
  2009. "vod_content": dec
  2010. }
  2011. ZhuiPf = []
  2012. ZhuiPu = []
  2013. if self.userid:
  2014. ZhuiPf = ['做点什么']
  2015. ZhuiPu = '是否追番剧$ #❤追番剧$add_notplay_zhui#💔取消追番剧$del_notplay_zhui'
  2016. defaultQn = int(self.userConfig['vodDefaultQn'])
  2017. if defaultQn > 116:
  2018. ZhuiPu += '#⚠️限高1080$116_notplay_vodTMPQn'
  2019. ZhuiPu = [ZhuiPu]
  2020. if seasons:
  2021. ZhuiPf.append('更多系列')
  2022. ZhuiPu.append('更多系列在快速搜索中查看$ #')
  2023. FirstPf = []
  2024. FirstPu = []
  2025. PreviewPf = []
  2026. PreviewPu = []
  2027. ParsePf = []
  2028. ParsePu = []
  2029. if episodes:
  2030. for x, y in self.pool.map(self.get_normal_episodes, episodes):
  2031. if y:
  2032. FirstPu.append(x)
  2033. ParsePu.append(y)
  2034. else:
  2035. PreviewPu.append(x)
  2036. if FirstPu:
  2037. FirstPf = [self.detailContent_args['s_title']]
  2038. FirstPu = ['#'.join(FirstPu)]
  2039. if PreviewPu:
  2040. PreviewPf = ['预告']
  2041. PreviewPu = ['#'.join(PreviewPu)]
  2042. if not self.detailContent_args.get('parse'):
  2043. ParsePu = []
  2044. if ParsePu:
  2045. ParsePf = [str(self.detailContent_args['s_title']) + '【解析】']
  2046. ParsePu = ['#'.join(ParsePu)]
  2047. fromL = ParsePf + FirstPf + PreviewPf
  2048. urlL = ParsePu + FirstPu + PreviewPu
  2049. for t in as_completed(section_task):
  2050. s = t.result()
  2051. if s:
  2052. fromL.append(s[0])
  2053. urlL.append(s[1])
  2054. fromep = self.detailContent_args.get('fromep', '')
  2055. if '_' in fromep:
  2056. fromL = ['B站'] + fromL
  2057. urlL = [fromep] + urlL
  2058. if self.userConfig['show_vod_hot_reply']:
  2059. self.get_vod_hot_reply_event.wait()
  2060. ReplyPu = self.detailContent_args.get('Reply', '')
  2061. if ReplyPu:
  2062. ZhuiPf.append('热门评论')
  2063. ZhuiPu.append(ReplyPu)
  2064. fromL.insert(1, '$$$'.join(ZhuiPf))
  2065. urlL.insert(1, '$$$'.join(ZhuiPu))
  2066. vod['vod_play_from'] = '$$$'.join(fromL)
  2067. vod['vod_play_url'] = '$$$'.join(urlL)
  2068. result = {
  2069. 'list': [
  2070. vod
  2071. ]
  2072. }
  2073. return result
  2074. def get_live_api2_playurl(self, room_id):
  2075. playFrom = []
  2076. playUrl = []
  2077. 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)
  2078. rsp = self._get_sth(url, 'fake')
  2079. jo = json.loads(rsp.text)
  2080. if jo['code'] == 0:
  2081. playurl_info = jo['data'].get('playurl_info', '')
  2082. if playurl_info:
  2083. stream = playurl_info['playurl']['stream']
  2084. liveDic = {
  2085. 'codec': {'avc': '0', 'hevc': '1'},
  2086. 'format': {'flv': '0', 'ts': '1', 'fmp4': '2'},
  2087. }
  2088. liveDic['qn'] = dict(self.pool.map(lambda x:(x['qn'], x['desc']), playurl_info['playurl']['g_qn_desc']))
  2089. vodList = []
  2090. for i in stream:
  2091. vodList.extend(i['format'])
  2092. api2_playUrl = {}
  2093. for v in vodList:
  2094. format = str(v.get('format_name'))
  2095. for c in v['codec']:
  2096. codec = str(c.get('codec_name'))
  2097. accept_qn = c.get('accept_qn')
  2098. for qn in accept_qn:
  2099. url = format + '_' + codec + '$liveapi2_' + str(qn) + '_' + liveDic['format'][format] + '_' + liveDic['codec'][codec] + '_' + str(room_id)
  2100. if not api2_playUrl.get(liveDic['qn'][qn]):
  2101. api2_playUrl[liveDic['qn'][qn]] = []
  2102. api2_playUrl[liveDic['qn'][qn]].append(url)
  2103. for key, value in api2_playUrl.items():
  2104. playFrom.append(key)
  2105. playUrl.append('#'.join(value))
  2106. result = {'From': playFrom, 'url': playUrl}
  2107. return result
  2108. def live_detailContent(self, array):
  2109. room_id = array[0]
  2110. get_live_api2_playurl = self.pool.submit(self.get_live_api2_playurl, room_id)
  2111. url = "https://api.live.bilibili.com/room/v1/Room/get_info?room_id=" + str(room_id)
  2112. rsp = self._get_sth(url, 'fake')
  2113. jRoot = json.loads(rsp.text)
  2114. result = {}
  2115. if jRoot.get('code') == 0:
  2116. jo = jRoot['data']
  2117. self.detailContent_args['mid'] = mid = str(jo["uid"])
  2118. self.get_up_info_event.clear()
  2119. self.pool.submit(self.get_up_info, mid)
  2120. title = jo['title'].replace("<em class=\"keyword\">", "").replace("</em>", "")
  2121. pic = jo.get("user_cover")
  2122. desc = jo.get('description')
  2123. typeName = jo.get('parent_area_name') + '--' + jo.get('area_name')
  2124. live_status = jo.get('live_status', '')
  2125. if live_status:
  2126. live_status = "开播时间:" + jo.get('live_time').replace('-', '.')
  2127. else:
  2128. live_status = "未开播"
  2129. vod = {
  2130. "vod_id": room_id,
  2131. "vod_name": title,
  2132. "vod_pic": pic,
  2133. "type_name": typeName,
  2134. "vod_year": "",
  2135. "vod_area": "bililivedanmu",
  2136. "vod_actor": "房间号:" + room_id + " UID:" + mid + " " + live_status,
  2137. "vod_content": desc,
  2138. }
  2139. secondPFrom = ''
  2140. secondP = ''
  2141. if self.userid:
  2142. secondPFrom = '关注Ta'
  2143. first = '是否关注$ '
  2144. follow = '➕关注$1_notplay_follow'
  2145. unfollow = '➖取关$2_notplay_follow'
  2146. secondPList = [first, follow, unfollow]
  2147. secondP = '#'.join(secondPList)
  2148. playFrom = get_live_api2_playurl.result().get('From', [])
  2149. playUrl = get_live_api2_playurl.result().get('url', [])
  2150. if playFrom:
  2151. api1_playFrom = 'API_1'
  2152. 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
  2153. playFrom.append(api1_playFrom)
  2154. playUrl.append(api1_playUrl)
  2155. if secondPFrom:
  2156. playFrom.insert(1, secondPFrom)
  2157. playUrl.insert(1, secondP)
  2158. vod['vod_play_from'] = '$$$'.join(playFrom)
  2159. vod['vod_play_url'] = '$$$'.join(playUrl)
  2160. self.get_up_info_event.wait()
  2161. up_info = self.up_info[mid]
  2162. vod["vod_director"] = '🆙 ' + up_info['name'] + " 👥 " + self.zh(jo.get('attention')) + ' ' + up_info['following']
  2163. result['list'] = [vod]
  2164. return result
  2165. search_key = ''
  2166. def searchContent(self, key, quick):
  2167. if not self.session_fake.cookies:
  2168. self.pool.submit(self.getFakeCookie, True)
  2169. for t in self.task_pool:
  2170. t.cancel()
  2171. self.task_pool = []
  2172. self.search_key = key
  2173. mid = self.detailContent_args.get('mid', '')
  2174. if quick and mid:
  2175. get_up_videos = self.pool.submit(self.get_up_videos, mid, 1, 'quicksearch')
  2176. types = {'video': '','media_bangumi': '番剧: ', 'media_ft': '影视: ', 'bili_user': '用户: ', 'live': '直播: '}
  2177. for type, value in types.items():
  2178. t = self.pool.submit(self.get_search_content, key = key, pg = value, duration_diff = 0, order = '', type = type, ps = self.userConfig['page_size'])
  2179. self.task_pool.append(t)
  2180. result = {'list': []}
  2181. for t in as_completed(self.task_pool):
  2182. res = t.result().get('list', [])
  2183. result['list'].extend(res)
  2184. self.task_pool.remove(t)
  2185. if quick:
  2186. if mid:
  2187. result['list'] = self.detailContent_args.get('interaction', []) + get_up_videos.result().get('list', []) + self.detailContent_args.get('Reply_jump', []) + result['list']
  2188. else:
  2189. result['list'] = self.detailContent_args.get('seasons', []) + result['list']
  2190. return result
  2191. stop_heartbeat_event = threading.Event()
  2192. def start_heartbeat(self, aid, cid, ids):
  2193. duration = ssid = epid = ''
  2194. for i in ids:
  2195. if 'ss' in i:
  2196. ssid = i.replace('ss', '')
  2197. elif 'ep' in i:
  2198. epid = i.replace('ep', '')
  2199. elif 'dur' in i:
  2200. duration = int(i.replace('dur', ''))
  2201. url = 'https://api.bilibili.com/x/player/v2?aid={0}&cid={1}'.format(aid, cid)
  2202. rsp = self._get_sth(url)
  2203. jo = json.loads(rsp.text)
  2204. data = jo.get('data',{})
  2205. interaction = data.get('interaction', {})
  2206. if interaction.get('graph_version'):
  2207. graph_version = interaction.get('graph_version')
  2208. old = self.detailContent_args.get('graph_version')
  2209. if old != graph_version:
  2210. self.detailContent_args['graph_version'] = graph_version
  2211. self.pool.submit(self.interaction_detailContent)
  2212. heartbeatInterval = int(self.userConfig['heartbeatInterval'])
  2213. if not self.userid or not heartbeatInterval:
  2214. return
  2215. if not duration:
  2216. url = 'https://api.bilibili.com/x/web-interface/view?aid={0}&cid={1}'.format(aid, cid)
  2217. rsp = self._get_sth(url, 'fake')
  2218. jRoot = json.loads(rsp.text)
  2219. duration = jRoot['data']['duration']
  2220. played_time = 0
  2221. if int(data.get('last_play_cid', 0)) == int(cid):
  2222. last_play_time = int(data.get('last_play_time'))
  2223. if last_play_time > 0:
  2224. played_time = int(last_play_time / 1000)
  2225. heartbeat_times = int((duration - played_time) / heartbeatInterval) + 1
  2226. url = 'https://api.bilibili.com/x/click-interface/web/heartbeat'
  2227. data = {'aid': str(aid), 'cid': str(cid), 'csrf': str(self.csrf)}
  2228. if ssid:
  2229. data['sid'] = str(ssid)
  2230. data['epid'] = str(epid)
  2231. data['type'] = '4'
  2232. heartbeat_count = 0
  2233. self.stop_heartbeat_event.clear()
  2234. while True:
  2235. if heartbeat_count == heartbeatInterval or self.stop_heartbeat_event.is_set():
  2236. played_time += heartbeat_count
  2237. heartbeat_count = 0
  2238. if not heartbeat_count:
  2239. heartbeat_times -= 1
  2240. if not heartbeat_times:
  2241. #播完为-1
  2242. played_time = -1
  2243. self.stop_heartbeat_event.set()
  2244. data['played_time'] = str(played_time)
  2245. self.pool.submit(self._post_sth, url=url, data=data)
  2246. if self.stop_heartbeat_event.is_set():
  2247. break
  2248. time.sleep(1)
  2249. heartbeat_count += 1
  2250. wbi_key = {}
  2251. def get_wbiKey(self, wts):
  2252. r = self.fetch("https://api.bilibili.com/x/web-interface/nav", headers=self.header)
  2253. wbi_img_url = r.json()['data']['wbi_img']['img_url']
  2254. wbi_sub_url = r.json()['data']['wbi_img']['sub_url']
  2255. 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,
  2256. 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,
  2257. 11, 36, 20, 34, 44, 52]
  2258. ae = wbi_img_url.split("/")[-1].split(".")[0] + wbi_sub_url.split("/")[-1].split(".")[0]
  2259. le = reduce(lambda s, i: s + ae[i], oe, "")
  2260. self.wbi_key = {
  2261. "key": le[:32],
  2262. "wts": wts
  2263. }
  2264. def encrypt_wbi(self, **params):
  2265. wts = round(time.time())
  2266. if not self.wbi_key or abs(self.wbi_key['wts']) < 30:
  2267. self.get_wbiKey(wts)
  2268. params["wts"] = wts
  2269. params = dict(sorted(params.items()))
  2270. params = {k : ''.join(filter(lambda chr: chr not in "!'()*", str(v))) for k, v in params.items()}
  2271. Ae = urlencode(params)
  2272. return Ae + "&w_rid=" + hashlib.md5((Ae + self.wbi_key['key']).encode(encoding='utf-8')).hexdigest()
  2273. def _get_sth(self, url, _type='master'):
  2274. if _type == 'vip' and self.session_vip.cookies:
  2275. rsp = self.session_vip.get(url, headers=self.header)
  2276. elif _type == 'fake':
  2277. if not self.session_fake.cookies:
  2278. self.getFakeCookie_event.wait()
  2279. rsp = self.session_fake.get(url, headers=self.header)
  2280. else:
  2281. rsp = self.session_master.get(url, headers=self.header)
  2282. return rsp
  2283. def _post_sth(self, url, data):
  2284. return self.session_master.post(url, headers=self.header, data=data)
  2285. def post_live_history(self, room_id):
  2286. data = {'room_id': str(room_id), 'platform': 'pc', 'csrf': str(self.csrf)}
  2287. url = 'https://api.live.bilibili.com/xlive/web-room/v1/index/roomEntryAction'
  2288. self._post_sth(url=url, data=data)
  2289. def do_notplay(self, ids):
  2290. aid = self.detailContent_args.get('aid')
  2291. mid = self.detailContent_args.get('mid')
  2292. ssid = self.detailContent_args.get('ssid')
  2293. data = {'csrf': str(self.csrf)}
  2294. url = ''
  2295. if 'vodTMPQn' in ids:
  2296. self.detailContent_args['vodTMPQn'] = str(ids[0])
  2297. return
  2298. elif 'follow' in ids:
  2299. if 'special' in ids:
  2300. data.update({'fids': str(mid), 'tagids': str(ids[0])})
  2301. url = 'https://api.bilibili.com/x/relation/tags/addUsers'
  2302. else:
  2303. data.update({'fid': str(mid), 'act': str(ids[0])})
  2304. url = 'https://api.bilibili.com/x/relation/modify'
  2305. elif 'zhui' in ids:
  2306. data.update({'season_id': str(ssid)})
  2307. url = 'https://api.bilibili.com/pgc/web/follow/' + str(ids[0])
  2308. elif 'like' in ids:
  2309. data.update({'aid': str(aid), 'like': str(ids[0])})
  2310. url = 'https://api.bilibili.com/x/web-interface/archive/like'
  2311. elif 'coin' in ids:
  2312. data.update({'aid': str(aid), 'multiply': str(ids[0]), 'select_like': '1'})
  2313. url = 'https://api.bilibili.com/x/web-interface/coin/add'
  2314. elif 'fav' in ids:
  2315. data.update({'rid': str(aid), 'type': '2'})
  2316. data[ids[1] + '_media_ids'] = str(ids[0])
  2317. url = 'https://api.bilibili.com/x/v3/fav/resource/deal'
  2318. elif 'triple' in ids:
  2319. data.update({'aid': str(aid)})
  2320. url = 'https://api.bilibili.com/x/web-interface/archive/like/triple'
  2321. elif 'reply' in ids:
  2322. data.update({'oid': str(ids[0]), 'rpid': str(ids[1]), 'type': '1', 'action': '1'})
  2323. url = 'http://api.bilibili.com/x/v2/reply/action'
  2324. self._post_sth(url=url, data=data)
  2325. def get_cid(self, video):
  2326. url = "https://api.bilibili.com/x/web-interface/view?aid=%s" % str(video['aid'])
  2327. rsp = self._get_sth(url)
  2328. jRoot = json.loads(rsp.text)
  2329. jo = jRoot['data']
  2330. video['cid'] = jo['cid']
  2331. video['duration'] = jo['duration']
  2332. if 'redirect_url' in jo and 'bangumi' in jo['redirect_url']:
  2333. video['ep'] = self.find_bangumi_id(jo['redirect_url'])
  2334. cookie_dic_tmp = {}
  2335. def get_cookies(self, key):
  2336. url = 'https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key=' + key
  2337. rsp = self._get_sth(url, 'fake')
  2338. jo = json.loads(rsp.text)
  2339. if jo['code'] == 0:
  2340. message = jo['data']['message']
  2341. if not message:
  2342. self.cookie_dic_tmp[key] = dict(self.session_fake.cookies)
  2343. self.pool.submit(self.getFakeCookie)
  2344. return message
  2345. return '网络错误'
  2346. def set_cookie(self, key, _type):
  2347. cookie_dic_tmp = self.cookie_dic_tmp.get(key, '')
  2348. if not cookie_dic_tmp:
  2349. message = self.get_cookies(key)
  2350. if message:
  2351. return
  2352. users = self.userConfig.get('users', {})
  2353. users[_type] = {'cookies_dic': self.cookie_dic_tmp.get(key, {})}
  2354. self.userConfig.update({'users': users})
  2355. self.getCookie(_type)
  2356. self.dump_config()
  2357. def unset_cookie(self, _type):
  2358. if _type == 'vip':
  2359. self.session_vip.cookies.clear()
  2360. else:
  2361. self.session_master.cookies = self.session_fake.cookies
  2362. self.userid = self.csrf = ''
  2363. if _type in self.userConfig.get('users', {}):
  2364. self.userConfig['users'].pop(_type)
  2365. self.dump_config()
  2366. def set_normal_default(self, id, type):
  2367. self.userConfig[type] = str(id)
  2368. self.dump_config()
  2369. def set_normal_cateManual(self, name, _List, action):
  2370. List_tmp = self.userConfig.get(str(_List) + '_tmp')
  2371. if not List_tmp:
  2372. List_tmp = self.userConfig[str(_List) + '_tmp'] = []
  2373. if action == 'save':
  2374. for _item in self.defaultConfig[_List]:
  2375. if not _item in List_tmp.copy():
  2376. self.userConfig[str(_List) + '_tmp'].append(_item)
  2377. self.userConfig[_List] = self.userConfig[str(_List) + '_tmp'].copy()
  2378. self.userConfig.pop(_List + '_tmp')
  2379. self.dump_config()
  2380. elif action == 'clear':
  2381. self.userConfig[_List] = self.defaultConfig[_List].copy()
  2382. self.userConfig.pop(str(_List) + '_tmp')
  2383. self.dump_config()
  2384. else:
  2385. if _List == 'cateManualLive':
  2386. name = name.split('@@@')
  2387. if len(name) == 3:
  2388. name[1] += '_' + str(name[2])
  2389. name = {'n': name[0], 'v': str(name[1])}
  2390. if name in List_tmp:
  2391. self.userConfig[str(_List) + '_tmp'].remove(name)
  2392. else:
  2393. self.userConfig[str(_List) + '_tmp'].append(name)
  2394. def add_cateManualLiveExtra(self, action, name, id):
  2395. _Extra = self.userConfig.get('cateManualLiveExtra', [])
  2396. if not _Extra:
  2397. _Extra = self.userConfig['cateManualLiveExtra'] = []
  2398. if action == 'clear':
  2399. for _ext in _Extra:
  2400. _ext['v'] = _ext['v'].replace('@@@', '_')
  2401. if _ext in self.userConfig.get('cateManualLive', []):
  2402. self.userConfig['cateManualLive'].remove(_ext)
  2403. if _ext in self.userConfig.get('cateManualLive_tmp', []):
  2404. self.userConfig['cateManualLive_tmp'].remove(_ext)
  2405. self.userConfig.pop('cateManualLiveExtra')
  2406. elif id in list(map(lambda x:x['v'], self.userConfig.get('cateManualLiveExtra', []))):
  2407. area_dict = {'n': name, 'v': id}
  2408. self.userConfig['cateManualLiveExtra'].remove(area_dict)
  2409. area_dict['v'] = id.replace('@@@', '_')
  2410. if area_dict in self.userConfig.get('cateManualLive', []):
  2411. self.userConfig['cateManualLive'].remove(area_dict)
  2412. if area_dict in self.userConfig.get('cateManualLive_tmp', []):
  2413. self.userConfig['cateManualLive_tmp'].remove(area_dict)
  2414. else:
  2415. area_dict = {'n': name, 'v': id}
  2416. self.userConfig['cateManualLiveExtra'].append(area_dict)
  2417. self.dump_config()
  2418. def _checkUpdate(self, action):
  2419. header = {"User-Agent": self.header["User-Agent"]}
  2420. if int(action):
  2421. newVersion = self.userConfig.get('newVersion')
  2422. if newVersion and newVersion['ver'] != self.userConfig['currentVersion']:
  2423. self.userConfig['newVersion']['status'] = '正在更新'
  2424. url = newVersion['url']
  2425. rsp = requests_get(url=url, headers=header, timeout=(3, 5))
  2426. if rsp.status_code == 200:
  2427. filename = url.split('/')
  2428. with open(f"{dirname}/{filename[-1]}", 'w', encoding="utf-8") as f:
  2429. f.write(rsp.text)
  2430. self.userConfig['newVersion']['status'] = '更新完成'
  2431. else:
  2432. self.userConfig['newVersion']['status'] = '更新失败'
  2433. else:
  2434. url = self.mirror_site + '/index.php/update.json'
  2435. rsp = self.fetch(url, headers=header)
  2436. jo = json.loads(rsp.text)
  2437. ver = jo.get('ver')
  2438. if ver:
  2439. self.userConfig['newVersion'] = jo
  2440. vod_qn_id = {
  2441. '127': "8K",
  2442. '126': "杜比视界",
  2443. '125': "HDR",
  2444. '120': "4K",
  2445. '116': "1080P60帧",
  2446. '112': "1080P+",
  2447. '80': "1080P",
  2448. '64': "720P",
  2449. }
  2450. vod_codec_id = {
  2451. '7': 'avc',
  2452. '12': 'hevc',
  2453. '13': 'av1',
  2454. }
  2455. vod_audio_id = {
  2456. '30280': '192000',
  2457. '30232': '132000',
  2458. '30216': '64000',
  2459. }
  2460. def get_dash_media(self, video):
  2461. qnid = str(video.get('id'))
  2462. codecid = video.get('codecid')
  2463. media_codecs = video.get('codecs')
  2464. media_bandwidth = video.get('bandwidth')
  2465. media_startWithSAP = video.get('startWithSap')
  2466. media_mimeType = video.get('mimeType')
  2467. media_BaseURL = video.get('baseUrl').replace('&', '&amp;')
  2468. media_SegmentBase_indexRange = video['SegmentBase'].get('indexRange')
  2469. media_SegmentBase_Initialization = video['SegmentBase'].get('Initialization')
  2470. mediaType = media_mimeType.split('/')[0]
  2471. if mediaType == 'video':
  2472. media_frameRate = video.get('frameRate')
  2473. media_sar = video.get('sar')
  2474. media_width = video.get('width')
  2475. media_height = video.get('height')
  2476. media_type_params = f"height='{media_height}' width='{media_width}' frameRate='{media_frameRate}' sar='{media_sar}'"
  2477. elif mediaType == 'audio':
  2478. audioSamplingRate = self.vod_audio_id.get(qnid, '192000')
  2479. media_type_params = f"numChannels='2' sampleRate='{audioSamplingRate}'"
  2480. if codecid:
  2481. qnid += '_' + str(codecid)
  2482. result = f"""
  2483. <Representation id="{qnid}" bandwidth="{media_bandwidth}" codecs="{media_codecs}" mimeType="{media_mimeType}" {media_type_params} startWithSAP="{media_startWithSAP}">
  2484. <BaseURL>{media_BaseURL}</BaseURL>
  2485. <SegmentBase indexRange="{media_SegmentBase_indexRange}">
  2486. <Initialization range="{media_SegmentBase_Initialization}"/>
  2487. </SegmentBase>
  2488. </Representation>"""
  2489. return result
  2490. def get_dash_media_list(self, media_lis):
  2491. if not media_lis:
  2492. return ""
  2493. mediaType = media_lis[0]['mimeType'].split('/')[0]
  2494. defaultQn = defaultCodec = ''
  2495. if mediaType == 'video':
  2496. defaultQn = vodTMPQn = self.detailContent_args.get('vodTMPQn', '')
  2497. if vodTMPQn:
  2498. vodTMPQn = int(vodTMPQn)
  2499. else:
  2500. defaultQn = str(self.userConfig['vodDefaultQn'])
  2501. vodTMPQn = 120
  2502. defaultCodec = str(self.userConfig['vodDefaultCodec'])
  2503. elif mediaType == 'audio':
  2504. defaultQn = str(self.userConfig['vodDefaultAudio'])
  2505. vodTMPQn = int(defaultQn)
  2506. defaultCodec = '0'
  2507. qn_codec = list(map(lambda x: str(x['id']) + '_' + str(x['codecid']), media_lis))
  2508. Qn_available_lis = []
  2509. #按设定的质量和设定的编码找
  2510. if defaultQn + '_' + defaultCodec in qn_codec:
  2511. Qn_available_lis.append(media_lis[qn_codec.index(defaultQn + '_' + defaultCodec)])
  2512. #按设定的质量找推荐的编码
  2513. if not Qn_available_lis and mediaType == 'video':
  2514. for c in self.vod_codec_id.keys():
  2515. if defaultQn + '_' + str(c) in qn_codec:
  2516. Qn_available_lis.append(media_lis[qn_codec.index(defaultQn + '_' + str(c))])
  2517. #找4K及以下最高可用画质/音质
  2518. if not Qn_available_lis:
  2519. qn_top = ''
  2520. for q in qn_codec:
  2521. q_c = q.split('_')
  2522. if qn_top and int(qn_top) > int(q_c[0]):
  2523. break
  2524. 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:
  2525. qn_top = int(q_c[0])
  2526. #匹配设定的编码,否则全部
  2527. if mediaType == 'video' and str(q_c[1]) == defaultCodec:
  2528. Qn_available_lis = [media_lis[qn_codec.index(str(q))]]
  2529. break
  2530. Qn_available_lis.append(media_lis[qn_codec.index(str(q))])
  2531. result = f"""
  2532. <AdaptationSet>
  2533. <ContentComponent contentType="{mediaType}"/>{''.join(map(self.get_dash_media, Qn_available_lis))}
  2534. </AdaptationSet>"""
  2535. return result
  2536. get_dash_event = threading.Event()
  2537. def get_dash(self, ja):
  2538. duration = ja.get('duration')
  2539. minBufferTime = ja.get('minBufferTime')
  2540. video_list = self.pool.submit(self.get_dash_media_list, ja.get('video'))
  2541. audio_list = self.pool.submit(self.get_dash_media_list, ja.get('audio'))
  2542. 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">
  2543. <Period duration="PT{duration}S" start="PT0S">{video_list.result()}{audio_list.result()}
  2544. </Period>
  2545. </MPD>"""
  2546. with open(f"{dirname}/playurl.mpd", 'w', encoding="utf-8") as f:
  2547. f.write(mpd)
  2548. self.get_dash_event.set()
  2549. time.sleep(3)
  2550. os.remove(f"{dirname}/playurl.mpd")
  2551. def get_durl(self, ja):
  2552. maxSize = -1
  2553. position = -1
  2554. for i in range(len(ja)):
  2555. tmpJo = ja[i]
  2556. if maxSize < int(tmpJo['size']):
  2557. maxSize = int(tmpJo['size'])
  2558. position = i
  2559. url = ''
  2560. if len(ja) > 0:
  2561. if position == -1:
  2562. position = 0
  2563. url = ja[position]['url']
  2564. return url
  2565. def playerContent(self, flag, id, vipFlags):
  2566. self.stop_heartbeat_event.set()
  2567. result = {'playUrl': '', 'url': ''}
  2568. ids = id.split("_")
  2569. if 'web' in id or 'liveapi2' == ids[0]:
  2570. return self.live_playerContent(flag, id, vipFlags)
  2571. if len(ids) < 2:
  2572. return result
  2573. aid = ids[0]
  2574. cid = ids[1]
  2575. if 'setting' in ids:
  2576. if 'liveFilter' in id:
  2577. id = ids[2]
  2578. self.add_cateManualLiveExtra(aid, cid, id)
  2579. elif cid == 'checkUpdate':
  2580. self._checkUpdate(aid)
  2581. elif cid in ['cateManual', 'cateManualLive', 'tuijianLis', 'rankingLis']:
  2582. action = ids[2]
  2583. self.set_normal_cateManual(aid, cid, action)
  2584. elif 'login' in id:
  2585. self.set_cookie(aid, cid)
  2586. elif 'logout' in id:
  2587. self.unset_cookie(aid)
  2588. else:
  2589. self.set_normal_default(aid, cid)
  2590. return result
  2591. elif 'notplay' in ids:
  2592. self.pool.submit(self.do_notplay, ids)
  2593. return result
  2594. elif cid == 'cid':
  2595. video = {'aid': str(aid)}
  2596. self.get_cid(video, )
  2597. cid = video['cid']
  2598. ids.append('dur' + str(video['duration']))
  2599. ep = video.get('ep')
  2600. if ep:
  2601. id += '_' + ep
  2602. ids.append(ep)
  2603. query = self.encrypt_wbi(avid=aid, cid=cid, fnval=4048, fnver=0, fourk=1)
  2604. url = f'https://api.bilibili.com/x/player/wbi/playurl?{query}'
  2605. if 'ep' in id:
  2606. if 'parse' in id:
  2607. test = list(x for x in map(lambda x: x if 'ep' in x else None, ids) if x is not None)
  2608. url = 'https://www.bilibili.com/bangumi/play/' + test[0]
  2609. result["url"] = url
  2610. result["flag"] = 'bilibili'
  2611. result["parse"] = '1'
  2612. result['jx'] = '1'
  2613. result["header"] = str({"User-Agent": self.header["User-Agent"]})
  2614. result["danmaku"] = 'https://api.bilibili.com/x/v1/dm/list.so?oid=' + str(cid)
  2615. return result
  2616. url = 'https://api.bilibili.com/pgc/player/web/playurl?aid={}&cid={}&fnval=4048&fnver=0&fourk=1'.format(aid, cid)
  2617. rsp = self._get_sth(url, 'vip')
  2618. jRoot = json.loads(rsp.text)
  2619. if jRoot['code'] == 0:
  2620. if 'data' in jRoot:
  2621. jo = jRoot['data']
  2622. elif 'result' in jRoot:
  2623. jo = jRoot['result']
  2624. else:
  2625. return result
  2626. else:
  2627. return result
  2628. ja = jo.get('dash')
  2629. if ja:
  2630. self.get_dash_event.clear()
  2631. get_dash = self.pool.submit(self.get_dash, ja)
  2632. self.get_dash_event.wait()
  2633. result["url"] = f"file://{dirname}/playurl.mpd"
  2634. else:
  2635. result["url"] = self.get_durl(jo.get('durl', {}))
  2636. result["parse"] = '0'
  2637. result["contentType"] = ''
  2638. result["header"] = self.header
  2639. result["danmaku"] = 'https://api.bilibili.com/x/v1/dm/list.so?oid=' + str(cid)
  2640. #回传播放记录
  2641. self.pool.submit(self.start_heartbeat, aid, cid, ids)
  2642. return result
  2643. def live_playerContent(self, flag, id, vipFlags):
  2644. result = {'playUrl': '', 'url': ''}
  2645. ids = id.split("_")
  2646. if len(ids) < 2:
  2647. return result
  2648. # 回传观看直播记录
  2649. if self.userid and int(self.userConfig['heartbeatInterval']) > 0:
  2650. self.pool.submit(self.post_live_history, ids[-1])
  2651. if ids[0] == 'liveapi2':
  2652. qn = int(ids[1])
  2653. format = int(ids[2])
  2654. codec = int(ids[3])
  2655. room_id = int(ids[-1])
  2656. 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)
  2657. rsp = self._get_sth(url, 'fake')
  2658. jo = json.loads(rsp.text)
  2659. if jo['code'] == 0:
  2660. try:
  2661. playurl = jo['data']['playurl_info'].get('playurl')
  2662. codec = playurl['stream'][0]['format'][0]['codec'][0]
  2663. except:
  2664. return result
  2665. base_url = str(codec['base_url'])
  2666. host = str(codec['url_info'][0]['host'])
  2667. extra = str(codec['url_info'][0]['extra'])
  2668. playurl = host + base_url + extra
  2669. result["url"] = playurl
  2670. if ".flv" in playurl:
  2671. result["contentType"] = 'video/x-flv'
  2672. else:
  2673. result["contentType"] = ''
  2674. else:
  2675. return result
  2676. else:
  2677. url = 'https://api.live.bilibili.com/room/v1/Room/playUrl?cid=%s&%s' % (ids[1], ids[0])
  2678. # raise Exception(url)
  2679. try:
  2680. rsp = self._get_sth(url)
  2681. except:
  2682. return result
  2683. jRoot = json.loads(rsp.text)
  2684. if jRoot['code'] == 0:
  2685. jo = jRoot['data']
  2686. ja = jo['durl']
  2687. if len(ja) > 0:
  2688. result["url"] = ja[0]['url']
  2689. if "h5" in ids[0]:
  2690. result["contentType"] = ''
  2691. else:
  2692. result["contentType"] = 'video/x-flv'
  2693. else:
  2694. return result
  2695. result["parse"] = '0'
  2696. # result['type'] ="m3u8"
  2697. result["header"] = {
  2698. "Referer": "https://live.bilibili.com",
  2699. "User-Agent": self.header["User-Agent"]
  2700. }
  2701. return result
  2702. config = {
  2703. "player": {},
  2704. "filter": {
  2705. "关注": [{"key": "sort", "name": "分类",
  2706. "value": [{"n": "正在直播", "v": "正在直播"},
  2707. {"n": "最近关注", "v": "最近关注"}, {"n": "特别关注", "v": "特别关注"},
  2708. {"n": "悄悄关注", "v": "悄悄关注"}, {"n": "我的粉丝", "v": "我的粉丝"}]}],
  2709. "动态": [{"key": "order", "name": "个人动态排序",
  2710. "value": [{"n": "最新发布", "v": "pubdate"}, {"n": "最多播放", "v": "click"},
  2711. {"n": "最多收藏", "v": "stow"}, {"n": "最早发布", "v": "oldest"}]}, ],
  2712. "影视": [{"key": "tid", "name": "分类",
  2713. "value": [{"n": "番剧", "v": "1"}, {"n": "国创", "v": "4"}, {"n": "电影", "v": "2"},
  2714. {"n": "电视剧", "v": "5"}, {"n": "纪录片", "v": "3"}, {"n": "综艺", "v": "7"}]},
  2715. {"key": "order", "name": "排序",
  2716. "value": [{"n": "热门", "v": "热门"}, {"n": "播放数量", "v": "2"}, {"n": "更新时间", "v": "0"},
  2717. {"n": "最高评分", "v": "4"}, {"n": "弹幕数量", "v": "1"}, {"n": "追看人数", "v": "3"},
  2718. {"n": "开播时间", "v": "5"}, {"n": "上映时间", "v": "6"}]},
  2719. {"key": "season_status", "name": "付费",
  2720. "value": [{"n": "全部", "v": "-1"}, {"n": "免费", "v": "1"},
  2721. {"n": "付费", "v": "2%2C6"}, {"n": "大会员", "v": "4%2C6"}]}],
  2722. "频道": [{"key": "order", "name": "排序",
  2723. "value": [{"n": "近期热门", "v": "hot"}, {"n": "月播放量", "v": "view"},
  2724. {"n": "最新投稿", "v": "new"}, {"n": "频道精选", "v": "featured"}, ]}, ],
  2725. "收藏": [{"key": "order", "name": "排序",
  2726. "value": [{"n": "收藏时间", "v": "mtime"}, {"n": "播放量", "v": "view"},
  2727. {"n": "投稿时间", "v": "pubtime"}]}, ],
  2728. "历史": [{"key": "type", "name": "分类",
  2729. "value": [{"n": "视频", "v": "archive"}, {"n": "直播", "v": "live"}, {"n": "UP主", "v": "UP主"}, {"n": "稍后再看", "v": "稍后再看"}]}, ],
  2730. "搜索": [{"key": "type", "name": "类型",
  2731. "value": [{"n": "视频", "v": "video"}, {"n": "番剧", "v": "media_bangumi"}, {"n": "影视", "v": "media_ft"},
  2732. {"n": "直播", "v": "live"}, {"n": "用户", "v": "bili_user"}]},
  2733. {"key": "order", "name": "视频排序",
  2734. "value": [{"n": "综合排序", "v": "totalrank"}, {"n": "最多点击", "v": "click"}, {"n": "最新发布", "v": "pubdate"},
  2735. {"n": "最多收藏", "v": "stow"}, {"n": "最多弹幕", "v": "dm"}]},
  2736. {"key": "duration", "name": "视频时长",
  2737. "value": [{"n": "全部", "v": "0"}, {"n": "60分钟以上", "v": "4"}, {"n": "30~60分钟", "v": "3"},
  2738. {"n": "5~30分钟", "v": "2"}, {"n": "5分钟以下", "v": "1"}]}],
  2739. }
  2740. }
  2741. header = {
  2742. 'Origin': 'https://www.bilibili.com',
  2743. 'Referer': 'https://www.bilibili.com',
  2744. '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'
  2745. }
  2746. def localProxy(self, param):
  2747. return [200, "video/MP2T", action, ""]