# Copyright (C) 2023 MikuInvidious Team # # MikuInvidious is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 3 of # the License, or (at your option) any later version. # # MikuInvidious is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with MikuInvidious. If not, see . '''Bilibili extra apis''' import subprocess from bilibili_api.utils.network import Api from bilibili_api.exceptions import ArgsException from bs4 import BeautifulSoup from shared import appconf '''Format the result returned by cv link.''' def article_to_html(article_text): article_soup = BeautifulSoup(article_text, features='lxml') article_body = article_soup.find('div', id='read-article-holder') article_body.attrs = {} article_body['id'] = 'main-article' del_elems = [] purge_elems = [] for child in article_body.descendants: if not child.name: continue if child.name.startswith('h'): child.name = f'h{int(child.name[1:])+1}' if child.name == 'strong' and child.parent.name.startswith('h'): purge_elems.append(child.parent) if not hasattr(child, 'attrs'): continue if child.name == 'a': if 'href' not in child: continue child['href'] = child['href'].split('//')[1].strip('www.bilibili.com') continue elif child.name == 'img': try: if appconf['proxy']['image']: child['src'] = '/proxy/pic/' + child['data-src'].split('//')[1] else: child['src'] = child['data-src'] del child['data-src'] del child['data-size'] except: pass continue elif child.name == 'span' and not child.parent in purge_elems: purge_elems.append(child.parent) child.attrs = {} for purge_elem in purge_elems: try: purge_elem.string = purge_elem.get_text() except: pass for del_elem in del_elems: try: del_elem.extract() except: pass return str(article_body) '''Convert the article to any file.''' def article_to_any(article_text, dest_fmt): cmd = ['pandoc', '-f', 'html', '-t', dest_fmt, '-'] p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) output, _ = p.communicate(input=article_to_html(article_text).encode("utf-8")) return output.decode("utf-8") async def video_get_src_for_qn(vi, idx, quality = 16): '''Get a specific available source for video.''' cid = await vi.get_cid(idx) api = Api('https://api.bilibili.com/x/player/playurl', 'GET', verify=(not not vi.credential.sessdata), credential=vi.credential) api.params={ 'avid': vi.get_aid(), 'cid': cid, 'qn': quality } return await api.request() async def video_get_dash_for_qn(vi, idx): '''Get a specific available source for video.''' cid = await vi.get_cid(idx) api = Api('https://api.bilibili.com/x/player/playurl', 'GET', verify=(not not vi.credential.sessdata), json_body=True, credential=vi.credential) api.params = { 'avid': vi.get_aid(), 'cid': cid, 'fnval': '16' } return await api.request() # The following algorithm is adopted from bilibili-API-collect. # https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/other/bvid_desc.md table = 'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF' itable = { table[i]: i for i in range(len(table)) } s = [11, 10, 3, 8, 4, 6] XOR = 177451812 ADD = 8728348608 def bv2av(x): r = 0 for i in range(6): r += itable[x[s[i]]] * 58 ** i return (r - ADD) ^ XOR def av2bv(x): try: x = int(x[2:] if str(x).startswith('av') else x) x = (x ^ XOR) + ADD r = list('BV1 4 1 7 ') for i in range(6): r[s[i]] = table[x // 58 ** i % 58] return ''. join(r) except ValueError: raise ArgsException("avid 提供错误,必须是以 av 开头的数字组成的字符串。")