feat: 增加哔哩哔哩解析自适应时间解析算法

This commit is contained in:
zhiyu1998 2023-03-26 21:12:00 +08:00
parent 4ab03fef25
commit 8aac44c724
4 changed files with 38 additions and 38 deletions

View File

@ -10,7 +10,7 @@ import HttpProxyAgent from "https-proxy-agent";
import { mkdirsSync } from "../utils/file.js"; import { mkdirsSync } from "../utils/file.js";
import { downloadBFile, getDownloadUrl, mergeFileToMp4, getDynamic } from "../utils/bilibili.js"; import { downloadBFile, getDownloadUrl, mergeFileToMp4, getDynamic } from "../utils/bilibili.js";
import { parseUrl, parseM3u8, downloadM3u8Videos, mergeAcFileToMp4 } from "../utils/acfun.js"; import { parseUrl, parseM3u8, downloadM3u8Videos, mergeAcFileToMp4 } from "../utils/acfun.js";
import { transMap, douyinTypeMap, XHS_CK, TEN_THOUSAND } from "../utils/constant.js"; import { transMap, douyinTypeMap, XHS_CK, TEN_THOUSAND, BILI_DURATION } from "../utils/constant.js";
import { getIdVideo, generateRandomStr } from "../utils/common.js"; import { getIdVideo, generateRandomStr } from "../utils/common.js";
import config from "../model/index.js"; import config from "../model/index.js";
import Translate from "../utils/trans-strategy.js"; import Translate from "../utils/trans-strategy.js";
@ -332,7 +332,7 @@ export class tools extends plugin {
// 视频信息获取例子http://api.bilibili.com/x/web-interface/view?bvid=BV1hY411m7cB // 视频信息获取例子http://api.bilibili.com/x/web-interface/view?bvid=BV1hY411m7cB
// 请求视频信息 // 请求视频信息
const videoInfo = await getVideoInfo(url); const videoInfo = await getVideoInfo(url);
const { title, desc, dynamic, stat, aid, cid } = videoInfo; const { title, desc, duration, dynamic, stat, aid, cid } = videoInfo;
// 视频信息 // 视频信息
let { view, danmaku, reply, favorite, coin, share, like } = stat; let { view, danmaku, reply, favorite, coin, share, like } = stat;
// 数据处理 // 数据处理
@ -341,7 +341,7 @@ export class tools extends plugin {
}; };
// 格式化数据 // 格式化数据
const combineContent = const combineContent =
`点赞:${dataProcessing(like)} | 硬币:${dataProcessing(coin)} | 收藏:${dataProcessing( `\n点赞:${dataProcessing(like)} | 硬币:${dataProcessing(coin)} | 收藏:${dataProcessing(
favorite, favorite,
)} | 分享${dataProcessing(share)}\n` + )} | 分享${dataProcessing(share)}\n` +
`总播放量:${dataProcessing(view)} | 弹幕数量:${dataProcessing( `总播放量:${dataProcessing(view)} | 弹幕数量:${dataProcessing(
@ -350,11 +350,11 @@ export class tools extends plugin {
`简介:${desc}`; `简介:${desc}`;
e.reply([`识别:哔哩哔哩:${title}`, combineContent]); e.reply([`识别:哔哩哔哩:${title}`, combineContent]);
await getDownloadUrl(url) await getDownloadUrl(url, duration > BILI_DURATION)
.then(data => { .then(data => {
this.downBili(`${path}temp`, data.videoUrl, data.audioUrl) this.downBili(`${path}temp`, data.videoUrl, data.audioUrl)
.then(data => { .then(_ => {
e.reply(segment.video(`${path}temp.mp4`)); e.reply(segment.video(`${path}temp.mp4`));
}) })
.catch(err => { .catch(err => {
logger.error(err); logger.error(err);
@ -966,8 +966,7 @@ export class tools extends plugin {
title + "-video.m4s", title + "-video.m4s",
_.throttle( _.throttle(
value => value =>
logger.mark("download-progress", { logger.mark("视频下载进度", {
type: "video",
data: value, data: value,
}), }),
1000, 1000,
@ -978,15 +977,14 @@ export class tools extends plugin {
title + "-audio.m4s", title + "-audio.m4s",
_.throttle( _.throttle(
value => value =>
logger.mark("download-progress", { logger.mark("音频下载进度", {
type: "audio",
data: value, data: value,
}), }),
1000, 1000,
), ),
), ),
]).then(data => { ]).then(data => {
return mergeFileToMp4(data[0].fullFileName, data[1].fullFileName, title + ".mp4"); return mergeFileToMp4(data[0].fullFileName, data[1].fullFileName, `${title}.mp4`);
}); });
} }

View File

@ -14,6 +14,7 @@ async function getVideoInfo(url) {
return { return {
title: respData.title, title: respData.title,
desc: respData.desc, desc: respData.desc,
duration: respData.duration,
dynamic: respJson.data.dynamic, dynamic: respJson.data.dynamic,
stat: respData.stat, stat: respData.stat,
aid: respData.aid, aid: respData.aid,

View File

@ -1,8 +1,9 @@
import fs from "node:fs"; import fs from "node:fs";
import axios from 'axios' import axios from 'axios'
import child_process from 'node:child_process' import child_process from 'node:child_process'
import util from "util";
function downloadBFile (url, fullFileName, progressCallback) { async function downloadBFile (url, fullFileName, progressCallback) {
return axios return axios
.get(url, { .get(url, {
responseType: 'stream', responseType: 'stream',
@ -34,7 +35,7 @@ function downloadBFile (url, fullFileName, progressCallback) {
}); });
} }
function getDownloadUrl (url) { async function getDownloadUrl (url, isLargeVideo=false) {
return axios return axios
.get(url, { .get(url, {
headers: { headers: {
@ -47,12 +48,16 @@ function getDownloadUrl (url) {
const info = JSON.parse( const info = JSON.parse(
data.match(/<script>window\.__playinfo__=({.*})<\/script><script>/)?.[1], data.match(/<script>window\.__playinfo__=({.*})<\/script><script>/)?.[1],
); );
// 如果是大视频直接最低分辨率
const level = isLargeVideo ? (Math.min(info?.data?.dash?.video.length - 1, info?.data?.dash?.audio.length - 1)) : 0;
const videoUrl = const videoUrl =
info?.data?.dash?.video?.[0]?.baseUrl ?? info?.data?.dash?.video?.[0]?.backupUrl?.[0]; info?.data?.dash?.video?.[level]?.baseUrl ?? info?.data?.dash?.video?.[level]?.backupUrl?.[0];
const audioUrl = const audioUrl =
info?.data?.dash?.audio?.[0]?.baseUrl ?? info?.data?.dash?.audio?.[0]?.backupUrl?.[0]; info?.data?.dash?.audio?.[level]?.baseUrl ?? info?.data?.dash?.audio?.[level]?.backupUrl?.[0];
const title = data.match(/title="(.*?)"/)?.[1]?.replaceAll?.(/\\|\/|:|\*|\?|"|<|>|\|/g, ''); const title = data.match(/title="(.*?)"/)?.[1]?.replaceAll?.(/\\|\/|:|\*|\?|"|<|>|\|/g, '');
if (videoUrl && audioUrl) { if (videoUrl && audioUrl) {
return { videoUrl, audioUrl, title }; return { videoUrl, audioUrl, title };
} }
@ -61,9 +66,7 @@ function getDownloadUrl (url) {
}); });
} }
function mergeFileToMp4 (vFullFileName, aFullFileName, outputFileName, shouldDelete = true) { async function mergeFileToMp4 (vFullFileName, aFullFileName, outputFileName, shouldDelete = true) {
let cmd = 'ffmpeg';
// 判断当前环境 // 判断当前环境
let env; let env;
if (process.platform === "win32") { if (process.platform === "win32") {
@ -74,30 +77,26 @@ function mergeFileToMp4 (vFullFileName, aFullFileName, outputFileName, shouldDel
PATH: '/usr/local/bin:' + child_process.execSync('echo $PATH').toString(), PATH: '/usr/local/bin:' + child_process.execSync('echo $PATH').toString(),
}; };
} else { } else {
console.log("暂时不支持当前操作系统!") logger.error("暂时不支持当前操作系统!")
} }
const execFile = util.promisify(child_process.execFile);
try {
const cmd = 'ffmpeg';
const args = ['-y', '-i', vFullFileName, '-i', aFullFileName, '-c', 'copy', outputFileName];
await execFile(cmd, args, { env });
return new Promise((resolve, reject) => { if (shouldDelete) {
child_process.exec( await fs.promises.unlink(vFullFileName);
`${ cmd } -y -i "${ vFullFileName }" -i "${ aFullFileName }" -c copy "${ outputFileName }"`, await fs.promises.unlink(aFullFileName);
{ env }, }
err => {
if (shouldDelete) {
fs.unlink(vFullFileName, f => f);
fs.unlink(aFullFileName, f => f);
}
if (err) { return { outputFileName };
reject(err); } catch (err) {
} throw err;
}
resolve({ outputFileName });
},
);
});
} }
function getDynamic(dynamicId) { async function getDynamic(dynamicId) {
const dynamicApi = `https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/get_dynamic_detail?dynamic_id=${dynamicId}` const dynamicApi = `https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/get_dynamic_detail?dynamic_id=${dynamicId}`
return axios.get(dynamicApi, { return axios.get(dynamicApi, {
headers: { headers: {
@ -125,4 +124,4 @@ function getDynamic(dynamicId) {
}) })
} }
export { downloadBFile, getDownloadUrl, mergeFileToMp4, getDynamic } export { downloadBFile, getDownloadUrl, mergeFileToMp4, getDynamic }

View File

@ -39,3 +39,5 @@ export const TEN_THOUSAND = 10000;
export const CAT_LIMIT = 10; export const CAT_LIMIT = 10;
export const XHS_CK = 'eGhzVHJhY2tlcklkPTczODhhYmY2LTI0MDgtNGU5YS04MTUyLTE0MGVhOGY1MTQ5ZjsgeGhzVHJhY2tlcklkLnNpZz1UcGUxTkNaX3B3UkFYdG01SVJmVEs0SWUxM0xBaGZuNmNZU2N4Vi1JYWxFOyBhMT0xODY2ZDkwMDM0NmI2NmppcjMzcGpxZ2MwM3JvcG1mczAydXMxdWNoeDEwMDAwMTM1MDUzOyB3ZWJJZD1mMTNkOGJkYjhiZGM3ZGE0MzY0NjA4NWJjYzQ1MDQ1YTsgZ2lkPXlZS0tmajg4SzA4MnlZS0tmajg4cUo3UzRLREtLVjNGcXFVVjd4Q0FrUzhxRk15OGxVNmlNeTg4OHlxMjgycThmMlk0UzAySjsgZ2lkLnNpZ249YlpzcFFzSUxEUmN5akZLQmN2L1FMWVhkU3lvPTsgd2ViX3Nlc3Npb249MDMwMDM3YTRjMDQyYjE1ZTVjMTg4OTUwOGIyNDRhZDExM2UwNTM7IHhoc1RyYWNrZXI9dXJsPW5vdGVEZXRhaWwmeGhzc2hhcmU9V2VpeGluU2Vzc2lvbjsgeGhzVHJhY2tlci5zaWc9YzdmcDVRclk2SGNvVERhUzluX2N3Z2RCRHh2MFZmWnpSU1NTcnlzbG5lQTsgZXh0cmFfZXhwX2lkcz1oNV8yMzAyMDExX29yaWdpbixoNV8xMjA4X2NsdCxoNV8xMTMwX2NsdCxpb3Nfd3hfbGF1bmNoX29wZW5fYXBwX2V4cCxoNV92aWRlb191aV9leHAzLHd4X2xhdW5jaF9vcGVuX2FwcF9kdXJhdGlvbl9vcmlnaW4scXVlc19jbHQyOyBleHRyYV9leHBfaWRzLnNpZz1DVUdrR3NYT3lBZmpVSXkyVGo3SjN4YmRNakFfSnpoR1JkYWd6cVlkbmJnOyB3ZWJCdWlsZD0xLjEuMjE7IHhzZWNhcHBpZD14aHMtcGMtd2ViOyB3ZWJzZWN0aWdhPTU5ZDNlZjFlNjBjNGFhMzdhN2RmM2MyMzQ2N2JkNDZkN2YxZGEwYjE5MThjZjMzNWVlN2YyZTllNTJhYzA0Y2Y7IHNlY19wb2lzb25faWQ9MTI0OTE1NWQtOWU5ZS00MzkyLTg2NTgtNTA1Yzc0YTUzMTM1' export const XHS_CK = 'eGhzVHJhY2tlcklkPTczODhhYmY2LTI0MDgtNGU5YS04MTUyLTE0MGVhOGY1MTQ5ZjsgeGhzVHJhY2tlcklkLnNpZz1UcGUxTkNaX3B3UkFYdG01SVJmVEs0SWUxM0xBaGZuNmNZU2N4Vi1JYWxFOyBhMT0xODY2ZDkwMDM0NmI2NmppcjMzcGpxZ2MwM3JvcG1mczAydXMxdWNoeDEwMDAwMTM1MDUzOyB3ZWJJZD1mMTNkOGJkYjhiZGM3ZGE0MzY0NjA4NWJjYzQ1MDQ1YTsgZ2lkPXlZS0tmajg4SzA4MnlZS0tmajg4cUo3UzRLREtLVjNGcXFVVjd4Q0FrUzhxRk15OGxVNmlNeTg4OHlxMjgycThmMlk0UzAySjsgZ2lkLnNpZ249YlpzcFFzSUxEUmN5akZLQmN2L1FMWVhkU3lvPTsgd2ViX3Nlc3Npb249MDMwMDM3YTRjMDQyYjE1ZTVjMTg4OTUwOGIyNDRhZDExM2UwNTM7IHhoc1RyYWNrZXI9dXJsPW5vdGVEZXRhaWwmeGhzc2hhcmU9V2VpeGluU2Vzc2lvbjsgeGhzVHJhY2tlci5zaWc9YzdmcDVRclk2SGNvVERhUzluX2N3Z2RCRHh2MFZmWnpSU1NTcnlzbG5lQTsgZXh0cmFfZXhwX2lkcz1oNV8yMzAyMDExX29yaWdpbixoNV8xMjA4X2NsdCxoNV8xMTMwX2NsdCxpb3Nfd3hfbGF1bmNoX29wZW5fYXBwX2V4cCxoNV92aWRlb191aV9leHAzLHd4X2xhdW5jaF9vcGVuX2FwcF9kdXJhdGlvbl9vcmlnaW4scXVlc19jbHQyOyBleHRyYV9leHBfaWRzLnNpZz1DVUdrR3NYT3lBZmpVSXkyVGo3SjN4YmRNakFfSnpoR1JkYWd6cVlkbmJnOyB3ZWJCdWlsZD0xLjEuMjE7IHhzZWNhcHBpZD14aHMtcGMtd2ViOyB3ZWJzZWN0aWdhPTU5ZDNlZjFlNjBjNGFhMzdhN2RmM2MyMzQ2N2JkNDZkN2YxZGEwYjE5MThjZjMzNWVlN2YyZTllNTJhYzA0Y2Y7IHNlY19wb2lzb25faWQ9MTI0OTE1NWQtOWU5ZS00MzkyLTg2NTgtNTA1Yzc0YTUzMTM1'
export const BILI_DURATION = 400;