🐞 fix(bilibili.js): 修复 av 号无法解析问题 [#86]

This commit is contained in:
zhiyu1998 2025-08-10 15:11:37 +08:00
parent 9845705a4d
commit f8f9d77376
4 changed files with 27 additions and 46 deletions

View File

@ -66,7 +66,6 @@ import NeteaseMusicInfo from '../model/neteaseMusicInfo.js';
import * as aBogus from "../utils/a-bogus.cjs"; import * as aBogus from "../utils/a-bogus.cjs";
import { downloadM3u8Videos, mergeAcFileToMp4, parseM3u8, parseUrl } from "../utils/acfun.js"; import { downloadM3u8Videos, mergeAcFileToMp4, parseM3u8, parseUrl } from "../utils/acfun.js";
import { startBBDown } from "../utils/bbdown-util.js"; import { startBBDown } from "../utils/bbdown-util.js";
import { av2BV } from "../utils/bilibili-bv-av-convert.js";
import { import {
BILI_HEADER, BILI_HEADER,
downloadBFile, downloadBFile,
@ -879,11 +878,6 @@ export class tools extends plugin {
} }
// 补充https // 补充https
url = url.startsWith("https://") ? url : "https://" + url; url = url.startsWith("https://") ? url : "https://" + url;
// av处理
const matched = url.match(/\/(AV|av)(\w+)/);
if (matched) {
url = url.replace(matched[0].replace("\/", ""), av2BV(Number(matched[2])));
}
// 直播间分享 // 直播间分享
// logger.info(url) // logger.info(url)
if (url.includes("live.bilibili.com")) { if (url.includes("live.bilibili.com")) {

View File

@ -1,5 +1,5 @@
- { - {
version: 1.10.0-rc2, version: 1.10.1,
data: data:
[ [
新增<span class="cmd">RBS查看哔哩哔哩状态</span>功能, 新增<span class="cmd">RBS查看哔哩哔哩状态</span>功能,

View File

@ -1,29 +0,0 @@
const table = 'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'
const tr = Array.from(table).reduce((o, c, i) => Object.assign(o, { [c]: i }), {})
const s = [11, 10, 3, 8, 4, 6]
const xor = 177451812
const add = 8728348608
/** 算法来源https://www.zhihu.com/question/381784377/answer/1099438784 **/
/**
* Convert a BV string to AV number
* @param {string} bv The BV string to be converted to AV number
* @returns {number} The AV number converted from the provided BV string
*/
function bv2AV(bv) {
return (new Uint8Array(6).reduce((r, _, i) => r + tr[bv[s[i]]] * 58 ** i, 0) - add) ^ xor
}
/**
* Convert a AV number to BV string
* @param {number} av The AV number to be converted to BV string
* @returns {string} The BV string converted from the provided AV number
*/
function av2BV(av) {
return (new Uint8Array(6).reduce((r, _, i) => { r.splice(s[i], 1, table[Math.floor(((av ^ xor) + add) / 58 ** i % 58)]); return r }, Array.from('BV1 4 1 7 '))).join('')
}
export {
bv2AV,
av2BV
}

View File

@ -204,7 +204,15 @@ async function axelDownloadBFile(url, fullFileName, progressCallback, videoDownl
* @returns {Promise<any>} * @returns {Promise<any>}
*/ */
export async function getDownloadUrl(url, SESSDATA, qn) { export async function getDownloadUrl(url, SESSDATA, qn) {
const videoId = /video\/[^\?\/ ]+/.exec(url)[0].split("/")[1]; let videoId = /video\/[^\?\/ ]+/.exec(url)[0].split("/")[1];
// AV号特殊处理
let cid = "";
if (videoId.toLowerCase().startsWith('av')) {
// 将 AV 转换为 BV
const { bvid, cid: newCid } = await getVideoInfo(url);
videoId = bvid;
cid = newCid;
}
// 转换画质数字为分辨率 // 转换画质数字为分辨率
let qualityText; let qualityText;
switch(parseInt(qn)) { switch(parseInt(qn)) {
@ -215,7 +223,7 @@ export async function getDownloadUrl(url, SESSDATA, qn) {
default: qualityText = "480P"; break; default: qualityText = "480P"; break;
} }
logger.info(`[R插件][BILI下载] 开始获取视频下载链接视频ID: ${videoId}, 请求画质: ${qualityText}`); logger.info(`[R插件][BILI下载] 开始获取视频下载链接视频ID: ${videoId}, 请求画质: ${qualityText}`);
const dash = await getBiliVideoWithSession(videoId, "", SESSDATA, qn); const dash = await getBiliVideoWithSession(videoId, cid, SESSDATA, qn);
// 获取关键信息 // 获取关键信息
const { video, audio } = dash; const { video, audio } = dash;
@ -388,7 +396,7 @@ export async function m4sToMp3(m4sUrl, path) {
export async function getBiliAudio(bvid, cid) { export async function getBiliAudio(bvid, cid) {
// 转换cid // 转换cid
if (!cid) if (!cid)
cid = await fetchCID(bvid).catch((err) => console.log(err)) cid = await fetchCID(bvid).catch((err) => logger.info(err))
// 返回一个fetch的promise // 返回一个fetch的promise
return (new Promise((resolve, reject) => { return (new Promise((resolve, reject) => {
@ -447,11 +455,11 @@ export async function getBiliVideoWithSession(bvid, cid, SESSDATA, qn) {
* @returns {Promise<*>} * @returns {Promise<*>}
*/ */
export const fetchCID = async (bvid) => { export const fetchCID = async (bvid) => {
//console.log('Data.js Calling fetchCID:' + URL_BVID_TO_CID.replace("{bvid}", bvid)) //logger.info('Data.js Calling fetchCID:' + URL_BVID_TO_CID.replace("{bvid}", bvid))
const res = await fetch(BILI_BVID_TO_CID.replace("{bvid}", bvid)) const res = await fetch(BILI_BVID_TO_CID.replace("{bvid}", bvid));
const json = await res.json() const json = await res.json();
const cid = json.data[0].cid const cid = json.data[0].cid;
return cid return cid;
} }
/** /**
@ -462,8 +470,16 @@ export const fetchCID = async (bvid) => {
export async function getVideoInfo(url) { export async function getVideoInfo(url) {
// const baseVideoInfo = "http://api.bilibili.com/x/web-interface/view"; // const baseVideoInfo = "http://api.bilibili.com/x/web-interface/view";
const videoId = /video\/[^\?\/ ]+/.exec(url)[0].split("/")[1]; const videoId = /video\/[^\?\/ ]+/.exec(url)[0].split("/")[1];
// 如果匹配到的是AV号特殊处理
let finalUrl = `${ BILI_VIDEO_INFO }?`;
if (videoId.toLowerCase().startsWith('av')) {
finalUrl += `aid=${ videoId.slice(2) }`;
} else {
finalUrl += `bvid=${ videoId }`;
}
logger.debug(finalUrl);
// 获取视频信息,然后发送 // 获取视频信息,然后发送
return fetch(`${ BILI_VIDEO_INFO }?bvid=${ videoId }`) return fetch(finalUrl)
.then(async resp => { .then(async resp => {
const respJson = await resp.json(); const respJson = await resp.json();
const respData = respJson.data; const respData = respJson.data;
@ -521,7 +537,7 @@ export async function getDynamic(dynamicId, SESSDATA) {
} }
} }
} }
// console.log(dynamic_src) // logger.info(dynamic_src)
return { return {
dynamicSrc, dynamicSrc,
dynamicDesc dynamicDesc