py_bilibili_20240409.py 117 KB

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