extra.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. # Copyright (C) 2023 MikuInvidious Team
  2. #
  3. # MikuInvidious is free software; you can redistribute it and/or
  4. # modify it under the terms of the GNU General Public License as
  5. # published by the Free Software Foundation; either version 3 of
  6. # the License, or (at your option) any later version.
  7. #
  8. # MikuInvidious is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. # General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with MikuInvidious. If not, see <http://www.gnu.org/licenses/>.
  15. '''Bilibili extra apis'''
  16. import subprocess
  17. from bilibili_api.utils.network import Api
  18. from bilibili_api.exceptions import ArgsException
  19. from bs4 import BeautifulSoup
  20. from shared import appconf
  21. '''Format the result returned by cv link.'''
  22. def article_to_html(article_text):
  23. article_soup = BeautifulSoup(article_text, features='lxml')
  24. article_body = article_soup.find('div', id='read-article-holder')
  25. article_body.attrs = {}
  26. article_body['id'] = 'main-article'
  27. del_elems = []
  28. purge_elems = []
  29. for child in article_body.descendants:
  30. if not child.name:
  31. continue
  32. if child.name.startswith('h'):
  33. child.name = f'h{int(child.name[1:])+1}'
  34. if child.name == 'strong' and child.parent.name.startswith('h'):
  35. purge_elems.append(child.parent)
  36. if not hasattr(child, 'attrs'):
  37. continue
  38. if child.name == 'a':
  39. if 'href' not in child:
  40. continue
  41. child['href'] = child['href'].split('//')[1].strip('www.bilibili.com')
  42. continue
  43. elif child.name == 'img':
  44. try:
  45. if appconf['proxy']['image']:
  46. child['src'] = '/proxy/pic/' + child['data-src'].split('//')[1]
  47. else:
  48. child['src'] = child['data-src']
  49. del child['data-src']
  50. del child['data-size']
  51. except:
  52. pass
  53. continue
  54. elif child.name == 'span' and not child.parent in purge_elems:
  55. purge_elems.append(child.parent)
  56. child.attrs = {}
  57. for purge_elem in purge_elems:
  58. try:
  59. purge_elem.string = purge_elem.get_text()
  60. except:
  61. pass
  62. for del_elem in del_elems:
  63. try:
  64. del_elem.extract()
  65. except:
  66. pass
  67. return str(article_body)
  68. '''Convert the article to any file.'''
  69. def article_to_any(article_text, dest_fmt):
  70. cmd = ['pandoc', '-f', 'html', '-t', dest_fmt, '-']
  71. p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
  72. output, _ = p.communicate(input=article_to_html(article_text).encode("utf-8"))
  73. return output.decode("utf-8")
  74. async def video_get_src_for_qn(vi, idx, quality = 16):
  75. '''Get a specific available source for video.'''
  76. cid = await vi.get_cid(idx)
  77. api = Api('https://api.bilibili.com/x/player/playurl', 'GET',
  78. verify=(not not vi.credential.sessdata),
  79. credential=vi.credential)
  80. api.params={ 'avid': vi.get_aid(), 'cid': cid, 'qn': quality }
  81. return await api.request()
  82. async def video_get_dash_for_qn(vi, idx):
  83. '''Get a specific available source for video.'''
  84. cid = await vi.get_cid(idx)
  85. api = Api('https://api.bilibili.com/x/player/playurl', 'GET',
  86. verify=(not not vi.credential.sessdata),
  87. json_body=True,
  88. credential=vi.credential)
  89. api.params = { 'avid': vi.get_aid(), 'cid': cid, 'fnval': '16' }
  90. return await api.request()
  91. # The following algorithm is adopted from bilibili-API-collect.
  92. # https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/other/bvid_desc.md
  93. table = 'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'
  94. itable = { table[i]: i for i in range(len(table)) }
  95. s = [11, 10, 3, 8, 4, 6]
  96. XOR = 177451812
  97. ADD = 8728348608
  98. def bv2av(x):
  99. r = 0
  100. for i in range(6):
  101. r += itable[x[s[i]]] * 58 ** i
  102. return (r - ADD) ^ XOR
  103. def av2bv(x):
  104. try:
  105. x = int(x[2:] if str(x).startswith('av') else x)
  106. x = (x ^ XOR) + ADD
  107. r = list('BV1 4 1 7 ')
  108. for i in range(6):
  109. r[s[i]] = table[x // 58 ** i % 58]
  110. return ''. join(r)
  111. except ValueError:
  112. raise ArgsException("avid 提供错误,必须是以 av 开头的数字组成的字符串。")