py_1712024bilibilivd.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728
  1. #coding=utf-8
  2. #!/usr/bin/python
  3. import sys
  4. import json
  5. import time
  6. from datetime import datetime
  7. from urllib.parse import quote, unquote
  8. import requests
  9. sys.path.append('..')
  10. from base.spider import Spider
  11. class Spider(Spider): # 元类 默认的元类 type
  12. def getName(self):
  13. return "B站视频"
  14. def init(self, extend):
  15. try:
  16. self.extendDict = json.loads(extend)
  17. except:
  18. self.extendDict = {}
  19. def destroy(self):
  20. pass
  21. def isVideoFormat(self, url):
  22. pass
  23. def manualVideoCheck(self):
  24. pass
  25. def homeContent(self, filter):
  26. result = {}
  27. result['filters'] = {}
  28. cookie = ''
  29. if 'cookie' in self.extendDict:
  30. cookie = self.extendDict['cookie']
  31. if 'json' in self.extendDict:
  32. r = self.fetch(self.extendDict['json'], timeout=10)
  33. if 'cookie' in r.json():
  34. cookie = r.json()['cookie']
  35. if cookie == '':
  36. cookie = '{}'
  37. elif type(cookie) == str and cookie.startswith('http'):
  38. cookie = self.fetch(cookie, timeout=10).text.strip()
  39. try:
  40. if type(cookie) == dict:
  41. cookie = json.dumps(cookie, ensure_ascii=False)
  42. except:
  43. pass
  44. _, _, _ = self.getCookie(cookie)
  45. bblogin = self.getCache('bblogin')
  46. if bblogin:
  47. result['class'] = []
  48. else:
  49. result['class'] = []
  50. if 'json' in self.extendDict:
  51. r = self.fetch(self.extendDict['json'], timeout=10)
  52. params = r.json()
  53. if 'classes' in params:
  54. result['class'] = result['class'] + params['classes']
  55. if filter:
  56. if 'filter' in params:
  57. result['filters'] = params['filter']
  58. elif 'categories' in self.extendDict or 'type' in self.extendDict:
  59. if 'categories' in self.extendDict:
  60. cateList = self.extendDict['categories'].split('#')
  61. else:
  62. cateList = self.extendDict['type'].split('#')
  63. for cate in cateList:
  64. result['class'].append({'type_name': cate, 'type_id': cate})
  65. if not 'class' in result:
  66. result['class'] = {"type_name": "沙雕动漫", "type_id": "沙雕动漫"}
  67. return result
  68. def homeVideoContent(self):
  69. result = {}
  70. cookie = ''
  71. if 'cookie' in self.extendDict:
  72. cookie = self.extendDict['cookie']
  73. if 'json' in self.extendDict:
  74. r = self.fetch(self.extendDict['json'], timeout=10)
  75. if 'cookie' in r.json():
  76. cookie = r.json()['cookie']
  77. if cookie == '':
  78. cookie = '{}'
  79. elif type(cookie) == str and cookie.startswith('http'):
  80. cookie = self.fetch(cookie, timeout=10).text.strip()
  81. try:
  82. if type(cookie) == dict:
  83. cookie = json.dumps(cookie, ensure_ascii=False)
  84. except:
  85. pass
  86. cookie, imgKey, subKey = self.getCookie(cookie)
  87. url = 'https://api.bilibili.com/x/web-interface/index/top/feed/rcmd?&y_num=1&fresh_type=3&feed_version=SEO_VIDEO&fresh_idx_1h=1&fetch_row=1&fresh_idx=1&brush=0&homepage_ver=1&ps=20'
  88. r = self.fetch(url, cookies=cookie, headers=self.header, timeout=5)
  89. data = json.loads(self.cleanText(r.text))
  90. try:
  91. result['list'] = []
  92. vodList = data['data']['item']
  93. for vod in vodList:
  94. aid = str(vod['id']).strip()
  95. title = self.removeHtmlTags(vod['title']).strip()
  96. img = vod['pic'].strip()
  97. remark = time.strftime('%H:%M:%S', time.gmtime(vod['duration']))
  98. if remark.startswith('00:'):
  99. remark = remark[3:]
  100. if remark == '00:00':
  101. continue
  102. result['list'].append({
  103. 'vod_id': aid,
  104. 'vod_name': title,
  105. 'vod_pic': img,
  106. 'vod_remarks': remark
  107. })
  108. except:
  109. pass
  110. return result
  111. def categoryContent(self, cid, page, filter, ext):
  112. page = int(page)
  113. result = {}
  114. videos = []
  115. cookie = ''
  116. pagecount = page
  117. if 'cookie' in self.extendDict:
  118. cookie = self.extendDict['cookie']
  119. if 'json' in self.extendDict:
  120. r = self.fetch(self.extendDict['json'], timeout=10)
  121. if 'cookie' in r.json():
  122. cookie = r.json()['cookie']
  123. if cookie == '':
  124. cookie = '{}'
  125. elif type(cookie) == str and cookie.startswith('http'):
  126. cookie = self.fetch(cookie, timeout=10).text.strip()
  127. try:
  128. if type(cookie) == dict:
  129. cookie = json.dumps(cookie, ensure_ascii=False)
  130. except:
  131. pass
  132. cookie, imgKey, subKey = self.getCookie(cookie)
  133. if cid == '动态':
  134. if page > 1:
  135. offset = self.getCache('offset')
  136. if not offset:
  137. offset = ''
  138. url = f'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all?timezone_offset=-480&type=all&offset={offset}&page={page}'
  139. else:
  140. url = f'https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all?timezone_offset=-480&type=all&page={page}'
  141. r = self.fetch(url, cookies=cookie, headers=self.header, timeout=5)
  142. data = json.loads(self.cleanText(r.text))
  143. self.setCache('offset', data['data']['offset'])
  144. vodList = data['data']['items']
  145. if data['data']['has_more']:
  146. pagecount = page + 1
  147. for vod in vodList:
  148. if vod['type'] != 'DYNAMIC_TYPE_AV':
  149. continue
  150. vid = str(vod['modules']['module_dynamic']['major']['archive']['aid']).strip()
  151. remark = vod['modules']['module_dynamic']['major']['archive']['duration_text'].strip()
  152. title = self.removeHtmlTags(vod['modules']['module_dynamic']['major']['archive']['title']).strip()
  153. img = vod['modules']['module_dynamic']['major']['archive']['cover']
  154. videos.append({
  155. "vod_id": vid,
  156. "vod_name": title,
  157. "vod_pic": img,
  158. "vod_remarks": remark
  159. })
  160. elif cid == "收藏夹":
  161. userid = self.getUserid(cookie)
  162. if userid is None:
  163. return {}, 1
  164. url = f'http://api.bilibili.com/x/v3/fav/folder/created/list-all?up_mid={userid}&jsonp=jsonp'
  165. r = self.fetch(url, cookies=cookie, headers=self.header, timeout=5)
  166. data = json.loads(self.cleanText(r.text))
  167. vodList = data['data']['list']
  168. pagecount = page
  169. for vod in vodList:
  170. vid = vod['id']
  171. title = vod['title'].strip()
  172. remark = vod['media_count']
  173. img = 'https://api-lmteam.koyeb.app/files/shoucang.png'
  174. videos.append({
  175. "vod_id": f'fav&&&{vid}',
  176. "vod_name": title,
  177. "vod_pic": img,
  178. "vod_tag": 'folder',
  179. "vod_remarks": remark
  180. })
  181. elif cid.startswith('fav&&&'):
  182. cid = cid[6:]
  183. url = f'http://api.bilibili.com/x/v3/fav/resource/list?media_id={cid}&pn={page}&ps=20&platform=web&type=0'
  184. r = self.fetch(url, cookies=cookie, headers=self.header, timeout=5)
  185. data = json.loads(self.cleanText(r.text))
  186. if data['data']['has_more']:
  187. pagecount = page + 1
  188. else:
  189. pagecount = page
  190. vodList = data['data']['medias']
  191. for vod in vodList:
  192. vid = str(vod['id']).strip()
  193. title = self.removeHtmlTags(vod['title']).replace(""", '"')
  194. img = vod['cover'].strip()
  195. remark = time.strftime('%H:%M:%S', time.gmtime(vod['duration']))
  196. if remark.startswith('00:'):
  197. remark = remark[3:]
  198. videos.append({
  199. "vod_id": vid,
  200. "vod_name": title,
  201. "vod_pic": img,
  202. "vod_remarks": remark
  203. })
  204. elif cid.startswith('UP主&&&'):
  205. cid = cid[6:]
  206. params = {'mid': cid, 'ps': 30, 'pn': page}
  207. params = self.encWbi(params, imgKey, subKey)
  208. url = 'https://api.bilibili.com/x/space/wbi/arc/search?'
  209. for key in params:
  210. url += f'&{key}={quote(params[key])}'
  211. r = self.fetch(url, cookies=cookie, headers=self.header, timeout=5)
  212. data = json.loads(self.cleanText(r.text))
  213. if page < data['data']['page']['count']:
  214. pagecount = page + 1
  215. else:
  216. pagecount = page
  217. if page == 1:
  218. videos = [{"vod_id": f'UP主&&&{tid}', "vod_name": '播放列表'}]
  219. vodList = data['data']['list']['vlist']
  220. for vod in vodList:
  221. vid = str(vod['aid']).strip()
  222. title = self.removeHtmlTags(vod['title']).replace("&quot;", '"')
  223. img = vod['pic'].strip()
  224. remarkinfos = vod['length'].split(':')
  225. minutes = int(remarkinfos[0])
  226. if minutes >= 60:
  227. hours = str(minutes // 60)
  228. minutes = str(minutes % 60)
  229. if len(hours) == 1:
  230. hours = '0' + hours
  231. if len(minutes) == 1:
  232. minutes = '0' + minutes
  233. remark = hours + ':' + minutes + ':' + remarkinfos[1]
  234. else:
  235. remark = vod['length']
  236. videos.append({
  237. "vod_id": vid,
  238. "vod_name": title,
  239. "vod_pic": img,
  240. "vod_remarks": remark
  241. })
  242. elif cid == '历史记录':
  243. url = f'http://api.bilibili.com/x/v2/history?pn={page}'
  244. r = self.fetch(url, cookies=cookie, headers=self.header, timeout=5)
  245. data = json.loads(self.cleanText(r.text))
  246. if len(data['data']) == 300:
  247. pagecount = page + 1
  248. else:
  249. pagecount = page
  250. vodList = data['data']
  251. for vod in vodList:
  252. if vod['duration'] <= 0:
  253. continue
  254. vid = str(vod["aid"]).strip()
  255. img = vod["pic"].strip()
  256. title = self.removeHtmlTags(vod["title"]).replace("&quot;", '"')
  257. if vod['progress'] != -1:
  258. process = time.strftime('%H:%M:%S', time.gmtime(vod['progress']))
  259. totalTime = time.strftime('%H:%M:%S', time.gmtime(vod['duration']))
  260. if process.startswith('00:'):
  261. process = process[3:]
  262. if totalTime.startswith('00:'):
  263. totalTime = totalTime[3:]
  264. remark = process + '|' + totalTime
  265. videos.append({
  266. "vod_id": vid,
  267. "vod_name": title,
  268. "vod_pic": img,
  269. "vod_remarks": remark
  270. })
  271. else:
  272. url = 'https://api.bilibili.com/x/web-interface/search/type?search_type=video&keyword={}&page={}'
  273. for key in ext:
  274. if key == 'tid':
  275. cid = ext[key]
  276. continue
  277. url += f'&{key}={ext[key]}'
  278. url = url.format(cid, page)
  279. r = self.fetch(url, cookies=cookie, headers=self.header, timeout=5)
  280. data = json.loads(self.cleanText(r.text))
  281. pagecount = data['data']['numPages']
  282. vodList = data['data']['result']
  283. for vod in vodList:
  284. if vod['type'] != 'video':
  285. continue
  286. vid = str(vod['aid']).strip()
  287. title = self.removeHtmlTags(self.cleanText(vod['title']))
  288. img = 'https:' + vod['pic'].strip()
  289. remarkinfo = vod['duration'].split(':')
  290. minutes = int(remarkinfo[0])
  291. seconds = remarkinfo[1]
  292. if len(seconds) == 1:
  293. seconds = '0' + seconds
  294. if minutes >= 60:
  295. hour = str(minutes // 60)
  296. minutes = str(minutes % 60)
  297. if len(hour) == 1:
  298. hour = '0' + hour
  299. if len(minutes) == 1:
  300. minutes = '0' + minutes
  301. remark = f'{hour}:{minutes}:{seconds}'
  302. else:
  303. minutes = str(minutes)
  304. if len(minutes) == 1:
  305. minutes = '0' + minutes
  306. remark = f'{minutes}:{seconds}'
  307. videos.append({
  308. "vod_id": vid,
  309. "vod_name": title,
  310. "vod_pic": img,
  311. "vod_remarks": remark
  312. })
  313. lenvideos = len(videos)
  314. result['list'] = videos
  315. result['page'] = page
  316. result['pagecount'] = pagecount
  317. result['limit'] = lenvideos
  318. result['total'] = lenvideos
  319. return result
  320. def detailContent(self, did):
  321. aid = did[0]
  322. if aid.startswith('UP主&&&'):
  323. bizId = aid[6:]
  324. oid = ''
  325. url = f'https://api.bilibili.com/x/v2/medialist/resource/list?mobi_app=web&type=1&oid={oid}&biz_id={bizId}&otype=1&ps=100&direction=false&desc=true&sort_field=1&tid=0&with_current=false'
  326. r = self.fetch(url, headers=self.header, timeout=5)
  327. videoList = r.json()['data']['media_list']
  328. vod = {
  329. "vod_id": aid,
  330. "vod_name": '播放列表',
  331. 'vod_play_from': 'B站视频'
  332. }
  333. playUrl = ''
  334. for video in videoList:
  335. remark = time.strftime('%H:%M:%S', time.gmtime(video['duration']))
  336. name = self.removeHtmlTags(video['title']).strip().replace("#", "-").replace('$', '*')
  337. if remark.startswith('00:'):
  338. remark = remark[3:]
  339. playUrl += f"[{remark}]/{name}$bvid&&&{video['bv_id']}#"
  340. vod['vod_play_url'] = playUrl.strip('#')
  341. result = {'list': [vod]}
  342. return result
  343. url = f"https://api.bilibili.com/x/web-interface/view?aid={aid}"
  344. r = self.fetch(url, headers=self.header, timeout=10)
  345. data = json.loads(self.cleanText(r.text))
  346. if "staff" in data['data']:
  347. director = ''
  348. for staff in data['data']['staff']:
  349. director += '[a=cr:{{"id":"UP主&&&{}","name":"{}"}}/]{}[/a],'.format(staff['mid'], staff['name'], staff['name'])
  350. else:
  351. director = '[a=cr:{{"id":"UP主&&&{}","name":"{}"}}/]{}[/a]'.format(data['data']['owner']['mid'], data['data']['owner']['name'], data['data']['owner']['name'])
  352. vod = {
  353. "vod_id": aid,
  354. "vod_name": self.removeHtmlTags(data['data']['title']),
  355. "vod_pic": data['data']['pic'],
  356. "type_name": data['data']['tname'],
  357. "vod_year": datetime.fromtimestamp(data['data']['pubdate']).strftime('%Y-%m-%d %H:%M:%S'),
  358. "vod_content": data['data']['desc'].replace('\xa0', ' ').replace('\n\n', '\n').strip(),
  359. "vod_director": director
  360. }
  361. videoList = data['data']['pages']
  362. playUrl = ''
  363. for video in videoList:
  364. remark = time.strftime('%H:%M:%S', time.gmtime(video['duration']))
  365. name = self.removeHtmlTags(video['part']).strip().replace("#", "-").replace('$', '*')
  366. if remark.startswith('00:'):
  367. remark = remark[3:]
  368. playUrl = playUrl + f"[{remark}]/{name}${aid}_{video['cid']}#"
  369. url = f'https://api.bilibili.com/x/web-interface/archive/related?aid={aid}'
  370. r = self.fetch(url, headers=self.header, timeout=5)
  371. data = json.loads(self.cleanText(r.text))
  372. videoList = data['data']
  373. playUrl = playUrl.strip('#') + '$$$'
  374. for video in videoList:
  375. remark = time.strftime('%H:%M:%S', time.gmtime(video['duration']))
  376. if remark.startswith('00:'):
  377. remark = remark[3:]
  378. name = self.removeHtmlTags(video['title']).strip().replace("#", "-").replace('$', '*')
  379. playUrl = playUrl + '[{}]/{}${}_{}#'.format(remark, name, video['aid'], video['cid'])
  380. vod['vod_play_from'] = 'B站视频$$$相关视频'
  381. vod['vod_play_url'] = playUrl.strip('#')
  382. result = {
  383. 'list': [
  384. vod
  385. ]
  386. }
  387. return result
  388. def searchContent(self, key, quick):
  389. return self.searchContentPage(key, quick, '1')
  390. def searchContentPage(self, key, quick, page):
  391. videos = []
  392. if quick:
  393. result = {
  394. 'list': videos
  395. }
  396. return result, 14400
  397. cookie = ''
  398. if 'cookie' in self.extendDict:
  399. cookie = self.extendDict['cookie']
  400. if 'json' in self.extendDict:
  401. r = self.fetch(self.extendDict['json'], timeout=10)
  402. if 'cookie' in r.json():
  403. cookie = r.json()['cookie']
  404. if cookie == '':
  405. cookie = '{}'
  406. elif type(cookie) == str and cookie.startswith('http'):
  407. cookie = self.fetch(cookie, timeout=10).text.strip()
  408. try:
  409. if type(cookie) == dict:
  410. cookie = json.dumps(cookie, ensure_ascii=False)
  411. except:
  412. pass
  413. cookie, _, _ = self.getCookie(cookie)
  414. url = f'https://api.bilibili.com/x/web-interface/search/type?search_type=video&keyword={key}&page={page}'
  415. r = self.fetch(url, headers=self.header, cookies=cookie, timeout=5)
  416. jo = json.loads(self.cleanText(r.text))
  417. if 'result' not in jo['data']:
  418. return {'list': videos}, 1
  419. vodList = jo['data']['result']
  420. for vod in vodList:
  421. aid = str(vod['aid']).strip()
  422. title = self.removeHtmlTags(self.cleanText(vod['title']))
  423. img = 'https:' + vod['pic'].strip()
  424. remarkinfo = vod['duration'].split(':')
  425. minutes = int(remarkinfo[0])
  426. seconds = remarkinfo[1]
  427. if len(seconds) == 1:
  428. seconds = '0' + seconds
  429. if minutes >= 60:
  430. hour = str(minutes // 60)
  431. minutes = str(minutes % 60)
  432. if len(hour) == 1:
  433. hour = '0' + hour
  434. if len(minutes) == 1:
  435. minutes = '0' + minutes
  436. remark = f'{hour}:{minutes}:{seconds}'
  437. else:
  438. minutes = str(minutes)
  439. if len(minutes) == 1:
  440. minutes = '0' + minutes
  441. remark = f'{minutes}:{seconds}'
  442. videos.append({
  443. "vod_id": aid,
  444. "vod_name": title,
  445. "vod_pic": img,
  446. "vod_remarks": remark
  447. })
  448. result = {
  449. 'list': videos
  450. }
  451. return result
  452. def playerContent(self, flag, pid, vipFlags):
  453. result = {}
  454. if pid.startswith('bvid&&&'):
  455. url = "https://api.bilibili.com/x/web-interface/view?bvid={}".format(pid[7:])
  456. r = self.fetch(url, headers=self.header, timeout=10)
  457. data = r.json()['data']
  458. aid = data['aid']
  459. cid = data['cid']
  460. else:
  461. idList = pid.split("_")
  462. aid = idList[0]
  463. cid = idList[1]
  464. url = 'https://api.bilibili.com/x/player/playurl?avid={}&cid={}&qn=120&fnval=4048&fnver=0&fourk=1'.format(aid, cid)
  465. cookie = ''
  466. extendDict = self.extendDict
  467. if 'cookie' in extendDict:
  468. cookie = extendDict['cookie']
  469. if 'json' in extendDict:
  470. r = self.fetch(extendDict['json'], timeout=10)
  471. if 'cookie' in r.json():
  472. cookie = r.json()['cookie']
  473. if cookie == '':
  474. cookie = '{}'
  475. elif type(cookie) == str and cookie.startswith('http'):
  476. cookie = self.fetch(cookie, timeout=10).text.strip()
  477. try:
  478. if type(cookie) == dict:
  479. cookie = json.dumps(cookie, ensure_ascii=False)
  480. except:
  481. pass
  482. cookiesDict, _, _ = self.getCookie(cookie)
  483. cookies = quote(json.dumps(cookiesDict))
  484. if 'thread' in extendDict:
  485. thread = str(extendDict['thread'])
  486. else:
  487. thread = '0'
  488. result["parse"] = 0
  489. result["playUrl"] = ''
  490. result["url"] = f'http://127.0.0.1:9978/proxy?do=py&type=mpd&cookies={cookies}&url={quote(url)}&aid={aid}&cid={cid}&thread={thread}'
  491. result["header"] = self.header
  492. result['danmaku'] = 'https://api.bilibili.com/x/v1/dm/list.so?oid={}'.format(cid)
  493. result["format"] = 'application/dash+xml'
  494. return result
  495. def localProxy(self, params):
  496. if params['type'] == "mpd":
  497. return self.proxyMpd(params)
  498. if params['type'] == "media":
  499. return self.proxyMedia(params)
  500. return None
  501. def proxyMpd(self, params):
  502. content, durlinfos, mediaType = self.getDash(params)
  503. if mediaType == 'mpd':
  504. return [200, "application/dash+xml", content]
  505. else:
  506. url = ''
  507. urlList = [content] + durlinfos['durl'][0]['backup_url'] if 'backup_url' in durlinfos['durl'][0] and durlinfos['durl'][0]['backup_url'] else [content]
  508. for url in urlList:
  509. if 'mcdn.bilivideo.cn' not in url:
  510. break
  511. header = self.header.copy()
  512. if 'range' in params:
  513. header['Range'] = params['range']
  514. if '127.0.0.1:7777' in url:
  515. header["Location"] = url
  516. return [302, "video/MP2T", None, header]
  517. r = requests.get(url, headers=header, stream=True)
  518. return [206, "application/octet-stream", r.content]
  519. def proxyMedia(self, params, forceRefresh=False):
  520. _, dashinfos, _ = self.getDash(params)
  521. if 'videoid' in params:
  522. videoid = int(params['videoid'])
  523. dashinfo = dashinfos['video'][videoid]
  524. elif 'audioid' in params:
  525. audioid = int(params['audioid'])
  526. dashinfo = dashinfos['audio'][audioid]
  527. else:
  528. return [404, "text/plain", ""]
  529. url = ''
  530. urlList = [dashinfo['baseUrl']] + dashinfo['backupUrl'] if 'backupUrl' in dashinfo and dashinfo['backupUrl'] else [dashinfo['baseUrl']]
  531. for url in urlList:
  532. if 'mcdn.bilivideo.cn' not in url:
  533. break
  534. if url == "":
  535. return [404, "text/plain", ""]
  536. header = self.header.copy()
  537. if 'range' in params:
  538. header['Range'] = params['range']
  539. r = requests.get(url, headers=header, stream=True)
  540. return [206, "application/octet-stream", r.content]
  541. def getDash(self, params, forceRefresh=False):
  542. aid = params['aid']
  543. cid = params['cid']
  544. url = unquote(params['url'])
  545. if 'thread' in params:
  546. thread = params['thread']
  547. else:
  548. thread = 0
  549. header = self.header.copy()
  550. cookieDict = json.loads(params['cookies'])
  551. key = f'bilivdmpdcache_{aid}_{cid}'
  552. if forceRefresh:
  553. self.delCache(key)
  554. else:
  555. data = self.getCache(key)
  556. if data:
  557. return data['content'], data['dashinfos'], data['type']
  558. cookies = cookieDict.copy()
  559. r = self.fetch(url, cookies=cookies, headers=header, timeout=5)
  560. data = json.loads(self.cleanText(r.text))
  561. if data['code'] != 0:
  562. return '', {}, ''
  563. if not 'dash' in data['data']:
  564. purl = data['data']['durl'][0]['url']
  565. try:
  566. expiresAt = int(self.regStr(reg='deadline=(\d+)', src=purl).group(1)) - 60
  567. except:
  568. expiresAt = int(time.time()) + 600
  569. if int(thread) > 0:
  570. try:
  571. self.fetch('http://127.0.0.1:7777')
  572. except:
  573. self.fetch('http://127.0.0.1:9978/go')
  574. purl = f'http://127.0.0.1:7777?url={quote(purl)}&thread={thread}'
  575. self.setCache(key, {'content': purl, 'type': 'mp4', 'dashinfos': data['data'], 'expiresAt': expiresAt})
  576. return purl, data['data'], 'mp4'
  577. dashinfos = data['data']['dash']
  578. duration = dashinfos['duration']
  579. minBufferTime = dashinfos['minBufferTime']
  580. videoinfo = ''
  581. videoid = 0
  582. deadlineList = []
  583. for video in dashinfos['video']:
  584. try:
  585. deadline = int(self.regStr(reg='deadline=(\d+)', src=video['baseUrl']).group(1))
  586. except:
  587. deadline = int(time.time()) + 600
  588. deadlineList.append(deadline)
  589. codecs = video['codecs']
  590. bandwidth = video['bandwidth']
  591. frameRate = video['frameRate']
  592. height = video['height']
  593. width = video['width']
  594. void = video['id']
  595. vidparams = params.copy()
  596. vidparams['videoid'] = videoid
  597. baseUrl = f'http://127.0.0.1:9978/proxy?do=py&type=media&cookies={quote(json.dumps(cookies))}&url={quote(url)}&aid={aid}&cid={cid}&videoid={videoid}'
  598. videoinfo = videoinfo + f""" <Representation bandwidth="{bandwidth}" codecs="{codecs}" frameRate="{frameRate}" height="{height}" id="{void}" width="{width}">
  599. <BaseURL>{baseUrl}</BaseURL>
  600. <SegmentBase indexRange="{video['SegmentBase']['indexRange']}">
  601. <Initialization range="{video['SegmentBase']['Initialization']}"/>
  602. </SegmentBase>
  603. </Representation>\n"""
  604. videoid += 1
  605. audioinfo = ''
  606. audioid = 0
  607. # audioList = sorted(dashinfos['audio'], key=lambda x: x['bandwidth'], reverse=True)
  608. for audio in dashinfos['audio']:
  609. try:
  610. deadline = int(self.regStr(reg='deadline=(\d+)', src=audio['baseUrl']).group(1))
  611. except:
  612. deadline = int(time.time()) + 600
  613. deadlineList.append(deadline)
  614. bandwidth = audio['bandwidth']
  615. codecs = audio['codecs']
  616. aoid = audio['id']
  617. aidparams = params.copy()
  618. aidparams['audioid'] = audioid
  619. baseUrl = f'http://127.0.0.1:9978/proxy?do=py&type=media&cookies={quote(json.dumps(cookies))}&url={quote(url)}&aid={aid}&cid={cid}&audioid={audioid}'
  620. audioinfo = audioinfo + f""" <Representation audioSamplingRate="44100" bandwidth="{bandwidth}" codecs="{codecs}" id="{aoid}">
  621. <BaseURL>{baseUrl}</BaseURL>
  622. <SegmentBase indexRange="{audio['SegmentBase']['indexRange']}">
  623. <Initialization range="{audio['SegmentBase']['Initialization']}"/>
  624. </SegmentBase>
  625. </Representation>\n"""
  626. audioid += 1
  627. mpd = f"""<?xml version="1.0" encoding="UTF-8"?>
  628. <MPD xmlns="urn:mpeg:dash:schema:mpd:2011" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" type="static" mediaPresentationDuration="PT{duration}S" minBufferTime="PT{minBufferTime}S">
  629. <Period>
  630. <AdaptationSet mimeType="video/mp4" startWithSAP="1" scanType="progressive" segmentAlignment="true">
  631. {videoinfo.strip()}
  632. </AdaptationSet>
  633. <AdaptationSet mimeType="audio/mp4" startWithSAP="1" segmentAlignment="true" lang="und">
  634. {audioinfo.strip()}
  635. </AdaptationSet>
  636. </Period>
  637. </MPD>"""
  638. expiresAt = min(deadlineList) - 60
  639. self.setCache(key, {'type': 'mpd', 'content': mpd.replace('&', '&amp;'), 'dashinfos': dashinfos, 'expiresAt': expiresAt})
  640. return mpd.replace('&', '&amp;'), dashinfos, 'mpd'
  641. def getCookie(self, cookie):
  642. if '{' in cookie and '}' in cookie:
  643. cookies = json.loads(cookie)
  644. else:
  645. cookies = dict([co.strip().split('=', 1) for co in cookie.strip(';').split(';')])
  646. bblogin = self.getCache('bblogin')
  647. if bblogin:
  648. imgKey = bblogin['imgKey']
  649. subKey = bblogin['subKey']
  650. return cookies, imgKey, subKey
  651. header = {
  652. "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36"
  653. }
  654. r = self.fetch("http://api.bilibili.com/x/web-interface/nav", cookies=cookies, headers=header, timeout=10)
  655. data = json.loads(r.text)
  656. code = data["code"]
  657. if code == 0:
  658. imgKey = data['data']['wbi_img']['img_url'].rsplit('/', 1)[1].split('.')[0]
  659. subKey = data['data']['wbi_img']['sub_url'].rsplit('/', 1)[1].split('.')[0]
  660. self.setCache('bblogin', {'imgKey': imgKey, 'subKey': subKey, 'expiresAt': int(time.time()) + 1200})
  661. return cookies, imgKey, subKey
  662. r = self.fetch("https://www.bilibili.com/", headers=header, timeout=5)
  663. cookies = r.cookies.get_dict()
  664. imgKey = ''
  665. subKey = ''
  666. return cookies, imgKey, subKey
  667. def getUserid(self, cookie):
  668. # 获取自己的userid(cookies拥有者)
  669. url = 'http://api.bilibili.com/x/space/myinfo'
  670. r = self.fetch(url, cookies=cookie, headers=self.header, timeout=5)
  671. data = json.loads(self.cleanText(r.text))
  672. if data['code'] == 0:
  673. return data['data']['mid']
  674. def removeHtmlTags(self, src):
  675. from re import sub, compile
  676. clean = compile('<.*?>')
  677. return sub(clean, '', src)
  678. def encWbi(self, params, imgKey, subKey):
  679. from hashlib import md5
  680. from functools import reduce
  681. from urllib.parse import urlencode
  682. mixinKeyEncTab = [46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40, 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11, 36, 20, 34, 44, 52]
  683. orig = imgKey + subKey
  684. mixinKey = reduce(lambda s, i: s + orig[i], mixinKeyEncTab, '')[:32]
  685. params['wts'] = round(time.time()) # 添加 wts 字段
  686. params = dict(sorted(params.items())) # 按照 key 重排参数
  687. # 过滤 value 中的 "!'()*" 字符
  688. params = {
  689. k: ''.join(filter(lambda chr: chr not in "!'()*", str(v)))
  690. for k, v
  691. in params.items()
  692. }
  693. query = urlencode(params) # 序列化参数
  694. params['w_rid'] = md5((query + mixinKey).encode()).hexdigest() # 计算 w_rid
  695. return params
  696. retry = 0
  697. header = {
  698. "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36",
  699. "Referer": "https://www.bilibili.com"
  700. }