From 2f794fd80b3ff27a9c5342ba26a67d216cb28d52 Mon Sep 17 00:00:00 2001 From: zhiyu <542716863@qq.com> Date: Thu, 8 Feb 2024 00:23:47 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9E=20fix:=20V1.3.3=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8Dbili=E9=9F=B3=E4=B9=90=E9=80=BB=E8=BE=91=20=EF=BC=88?= =?UTF-8?q?=E7=8E=B0=E5=9C=A8=E5=8D=B3=E5=8F=AF=E4=BD=BF=E7=94=A8=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=EF=BC=9Abili=E9=9F=B3=E4=B9=90+=E9=93=BE=E6=8E=A5?= =?UTF-8?q?=EF=BC=8C=E4=B8=8B=E8=BD=BD=E9=9F=B3=E4=B9=90=E5=95=A6=EF=BC=81?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 将m4s发送转换为mp3发送格式 2. 提升了bili解析的扩展性 3. 整理了path获取的方式 --- apps/tools.js | 107 +++++++++++++----------- config/version.yaml | 2 +- constants/tools.js | 21 +++++ utils/biliInfo.js | 57 ------------- utils/bilibili.js | 193 +++++++++++++++++++++++++++++++++++++------- 5 files changed, 246 insertions(+), 134 deletions(-) delete mode 100644 utils/biliInfo.js diff --git a/apps/tools.js b/apps/tools.js index cfed092..4d24446 100644 --- a/apps/tools.js +++ b/apps/tools.js @@ -6,35 +6,54 @@ import { Buffer } from 'node:buffer'; import axios from "axios"; import _ from "lodash"; import tunnel from "tunnel"; -import HttpProxyAgent, { HttpsProxyAgent } from "https-proxy-agent"; -import { mkdirIfNotExists, checkAndRemoveFile, deleteFolderRecursive } from "../utils/file.js"; -import { downloadBFile, getAudioUrl, getDownloadUrl, mergeFileToMp4 } from "../utils/bilibili.js"; -import { parseUrl, parseM3u8, downloadM3u8Videos, mergeAcFileToMp4 } from "../utils/acfun.js"; +import HttpProxyAgent from "https-proxy-agent"; +import { checkAndRemoveFile, deleteFolderRecursive, mkdirIfNotExists } from "../utils/file.js"; +import { + downloadBFile, + getBiliAudio, + getDownloadUrl, + getDynamic, + getVideoInfo, + m4sToMp3, + mergeFileToMp4 +} from "../utils/bilibili.js"; +import { downloadM3u8Videos, mergeAcFileToMp4, parseM3u8, parseUrl } from "../utils/acfun.js"; import { - transMap, - douyinTypeMap, DIVIDING_LINE, - XHS_NO_WATERMARK_HEADER, + douyinTypeMap, REDIS_YUNZAI_ISOVERSEA, + transMap, TWITTER_BEARER_TOKEN, + XHS_NO_WATERMARK_HEADER, } from "../constants/constant.js"; import { containsChinese, formatBiliInfo, getIdVideo, secondsToTime } from "../utils/common.js"; import config from "../model/index.js"; import Translate from "../utils/trans-strategy.js"; import * as xBogus from "../utils/x-bogus.cjs"; -import { getVideoInfo, getDynamic } from "../utils/biliInfo.js"; -import { getBodianAudio, getBodianMv, getBodianMusicInfo } from "../utils/bodian.js"; +import { getBodianAudio, getBodianMusicInfo, getBodianMv } from "../utils/bodian.js"; import { av2BV } from "../utils/bilibili-bv-av-convert.js"; import querystring from "querystring"; import TokenBucket from "../utils/token-bucket.js"; import { getWbi } from "../utils/biliWbi.js"; -import { BILI_SUMMARY, DY_INFO, TIKTOK_INFO, TWITTER_TWEET_INFO, XHS_REQ_LINK } from "../constants/tools.js"; -import { XHS_VIDEO } from "../constants/tools.js"; +import { BILI_SUMMARY, DY_INFO, TIKTOK_INFO, TWITTER_TWEET_INFO, XHS_REQ_LINK, XHS_VIDEO } from "../constants/tools.js"; import child_process from 'node:child_process' import { getAudio, getVideo } from "../utils/y2b.js"; import { processTikTokUrl } from "../utils/tiktok.js"; export class tools extends plugin { + /** + * 构造安全的命令 + * @type {{existsPromptKey: string, existsTransKey: string}} + */ + static Constants = { + existsTransKey: Object.keys(transMap).join("|"), + }; + /** + * 构造令牌桶,防止解析致使服务器宕机(默认限制5s调用一次) + * @type {TokenBucket} + */ + static #tokenBucket = new TokenBucket(1, 1, 5); + constructor() { super({ name: "R插件工具和学习类", @@ -197,9 +216,7 @@ export class tools extends plugin { "http", "https", ); - const path = `${ this.defaultPath }${ - this.e.group_id || this.e.user_id - }/temp.mp4`; + const path = `${ this.getCurDownloadPath(e) }/temp.mp4`; await this.downloadVideo(resUrl).then(() => { e.reply(segment.video(path)); }); @@ -257,7 +274,7 @@ export class tools extends plugin { this.downloadVideo(data.video.play_addr.url_list[0], !isOversea).then(video => { e.reply( segment.video( - `${ this.defaultPath }${ this.e.group_id || this.e.user_id }/temp.mp4`, + `${ this.getCurDownloadPath(e) }/temp.mp4`, ), ); }); @@ -296,7 +313,7 @@ export class tools extends plugin { } // 只提取音乐处理 if (e.msg !== undefined && e.msg.includes("bili音乐")) { - await this.biliMusic(url, e); + await this.biliMusic(e, url); return true; } // 动态处理 @@ -346,7 +363,7 @@ export class tools extends plugin { } // 创建文件,如果不存在 - const path = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }/`; + const path = `${ this.getCurDownloadPath(e) }/`; await mkdirIfNotExists(path); // 下载文件 getDownloadUrl(url) @@ -367,9 +384,13 @@ export class tools extends plugin { return true; } - async biliMusic(url, e) { - const { audioUrl } = await getAudioUrl(url); - e.reply(segment.record(audioUrl)) + // 下载哔哩哔哩音乐 + async biliMusic(e, url) { + const videoId = /video\/[^\?\/ ]+/.exec(url)[0].split("/")[1]; + getBiliAudio(videoId, "").then(async audioUrl => { + const path = this.getCurDownloadPath(e); + e.reply(segment.record(await m4sToMp3(audioUrl, path))); + }) return true } @@ -399,6 +420,8 @@ export class tools extends plugin { return url; } + // 小蓝鸟解析:停止更新 + /** * 哔哩哔哩总结 * @author zhiyu1998 @@ -492,7 +515,6 @@ export class tools extends plugin { return true; } - // 小蓝鸟解析:停止更新 // 例子:https://twitter.com/chonkyanimalx/status/1595834168000204800 async twitter(e) { // 配置参数及解析 @@ -512,14 +534,14 @@ export class tools extends plugin { await fetch(TWITTER_TWEET_INFO.replace("{}", id), { headers: { "User-Agent": "v2TweetLookupJS", - "authorization": `Bearer ${Buffer.from(TWITTER_BEARER_TOKEN, "base64").toString()}` + "authorization": `Bearer ${ Buffer.from(TWITTER_BEARER_TOKEN, "base64").toString() }` }, ...params, agent: !isOversea ? '' : new HttpProxyAgent(this.myProxy), }).then(async resp => { logger.info(resp) e.reply(`识别:小蓝鸟学习版,${ resp.data.text }`); - const downloadPath = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }`; + const downloadPath = `${ this.getCurDownloadPath(e) }`; // 创建文件夹(如果没有过这个群) if (!fs.existsSync(downloadPath)) { mkdirsSync(downloadPath); @@ -569,7 +591,7 @@ export class tools extends plugin { // acfun解析 async acfun(e) { - const path = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }/temp/`; + const path = `${ this.getCurDownloadPath(e) }/temp/`; await mkdirIfNotExists(path); let inputMsg = e.msg; @@ -619,9 +641,9 @@ export class tools extends plugin { } else { id = /explore\/(\w+)/.exec(msgUrl)?.[1] || /discovery\/item\/(\w+)/.exec(msgUrl)?.[1]; } - const downloadPath = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }`; + const downloadPath = `${ this.getCurDownloadPath(e) }`; // 获取信息 - fetch(`${XHS_REQ_LINK}${ id }`, { + fetch(`${ XHS_REQ_LINK }${ id }`, { headers: XHS_NO_WATERMARK_HEADER, }).then(async resp => { const xhsHtml = await resp.text(); @@ -641,7 +663,7 @@ export class tools extends plugin { this.downloadVideo(xhsVideoUrl).then(path => { if (path === undefined) { // 创建文件,如果不存在 - path = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }/`; + path = `${ this.getCurDownloadPath(e) }/`; } e.reply(segment.video(path + "/temp.mp4")); }); @@ -730,7 +752,7 @@ export class tools extends plugin { const API = `https://imginn.com/${ suffix }`; // logger.info(API); let imgPromise = []; - const downloadPath = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }`; + const downloadPath = `${ this.getCurDownloadPath(e) }`; // 判断是否是海外服务器 const isOversea = await this.isOverseasServer(); // 简单封装图片下载 @@ -813,7 +835,7 @@ export class tools extends plugin { segment.image(albumPic120), ]); if (e.msg.includes("musicId")) { - const path = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }`; + const path = `${ this.getCurDownloadPath(e) }`; await getBodianAudio(id, path).then(_ => { Bot.acquireGfs(e.group_id).upload( fs.readFileSync(path + "/temp.mp3"), @@ -968,12 +990,12 @@ export class tools extends plugin { const format = `${ bestVideo.id }x${ bestAudio.id }` // 下载地址格式化 const path = `${ v }${ p ? `/p${ p }` : '' }`; - const fullpath = `${ this.defaultPath }${ this.e.group_id || this.e.user_id }/${ path }`; + const fullpath = `${ this.getCurDownloadPath(e) }/${ path }`; // 创建下载文件夹 await mkdirIfNotExists(fullpath); // yt-dlp下载 let cmd = //`cd '${__dirname}' && (cd tmp > /dev/null || (mkdir tmp && cd tmp)) &&` + - `yt-dlp ${ this.y2bCk !== undefined ? `--cookies ${ this.y2bCk }` : '' } ${url} -f ${ format.replace('x', '+') } ` + + `yt-dlp ${ this.y2bCk !== undefined ? `--cookies ${ this.y2bCk }` : '' } ${ url } -f ${ format.replace('x', '+') } ` + `-o '${ fullpath }/${ v }.%(ext)s' ${ isProxy ? `--proxy ${ this.proxyAddr }:${ this.proxyPort }` : '' } -k --write-info-json`; logger.mark(cmd) try { @@ -1099,6 +1121,15 @@ export class tools extends plugin { } } + /** + * 获取当前发送人/群的下载路径 + * @param e Yunzai 机器人事件 + * @returns {string} + */ + getCurDownloadPath(e) { + return `${ this.defaultPath }${ e.group_id || e.user_id }` + } + /** * 提取视频下载位置 * @returns {{groupPath: string, target: string}} @@ -1208,18 +1239,4 @@ export class tools extends plugin { logger.warn(`解析被限制使用`); } } - - /** - * 构造安全的命令 - * @type {{existsPromptKey: string, existsTransKey: string}} - */ - static Constants = { - existsTransKey: Object.keys(transMap).join("|"), - }; - - /** - * 构造令牌桶,防止解析致使服务器宕机(默认限制5s调用一次) - * @type {TokenBucket} - */ - static #tokenBucket = new TokenBucket(1, 1, 5); } diff --git a/config/version.yaml b/config/version.yaml index 71b98af..cb0999d 100644 --- a/config/version.yaml +++ b/config/version.yaml @@ -1,5 +1,5 @@ - { - version: 1.3.2, + version: 1.3.3, data: [ 新增油管解析功能, diff --git a/constants/tools.js b/constants/tools.js index d3026cf..7357b68 100644 --- a/constants/tools.js +++ b/constants/tools.js @@ -5,6 +5,27 @@ */ export const BILI_SUMMARY = "https://api.bilibili.com/x/web-interface/view/conclusion/get" +/** + * 视频流URL + * https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/video/videostream_url.md + * @type {string} + */ +export const BILI_PLAY_STREAM = "https://api.bilibili.com/x/player/playurl?cid={cid}&bvid={bvid}&qn=64&fnval=16" + +/** + * 动态信息 + * https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/dynamic/content.md + * @type {string} + */ +export const BILI_DYNAMIC = "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/get_dynamic_detail?dynamic_id={}" + +/** + * BVID -> CID + * https://github.com/SocialSisterYi/bilibili-API-collect/blob/33bde6f6afcac2ff8c6f7069f08ce84065a6cff6/docs/video/info.md?plain=1#L4352 + * @type {string} + */ +export const BILI_BVID_TO_CID = "https://api.bilibili.com/x/player/pagelist?bvid={bvid}&jsonp=jsonp" + /** * 视频基本信息API * https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/video/info.md diff --git a/utils/biliInfo.js b/utils/biliInfo.js deleted file mode 100644 index 535a87f..0000000 --- a/utils/biliInfo.js +++ /dev/null @@ -1,57 +0,0 @@ -import fetch from "node-fetch"; -import axios from "axios"; -import { BILI_VIDEO_INFO } from "../constants/tools.js"; - -async function getVideoInfo(url) { - // const baseVideoInfo = "http://api.bilibili.com/x/web-interface/view"; - const videoId = /video\/[^\?\/ ]+/.exec(url)[0].split("/")[1]; - // 获取视频信息,然后发送 - return fetch(`${BILI_VIDEO_INFO}?bvid=${videoId}`) - .then(async resp => { - const respJson = await resp.json(); - const respData = respJson.data; - return { - title: respData.title, - pic: respData.pic, - desc: respData.desc, - duration: respData.duration, - dynamic: respJson.data.dynamic, - stat: respData.stat, - bvid: respData.bvid, - aid: respData.aid, - cid: respData.pages?.[0].cid, - owner: respData.owner, - pages: respData?.pages, - }; - }); -} - -async function getDynamic(dynamicId) { - const dynamicApi = `https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/get_dynamic_detail?dynamic_id=${dynamicId}` - return axios.get(dynamicApi, { - headers: { - 'User-Agent': - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36', - 'referer': 'https://www.bilibili.com', - } - }).then(resp => { - const dynamicData = resp.data.data.card - const card = JSON.parse(dynamicData.card) - const dynamicOrigin = card.item - const dynamicDesc = dynamicOrigin.description - - const pictures = dynamicOrigin.pictures - let dynamicSrc = [] - for (let pic of pictures) { - const img_src = pic.img_src - dynamicSrc.push(img_src) - } - // console.log(dynamic_src) - return { - dynamicSrc, - dynamicDesc - } - }) -} - -export { getVideoInfo, getDynamic }; diff --git a/utils/bilibili.js b/utils/bilibili.js index 431f0a3..2951c10 100644 --- a/utils/bilibili.js +++ b/utils/bilibili.js @@ -2,8 +2,17 @@ import fs from "node:fs"; import axios from 'axios' import child_process from 'node:child_process' import util from "util"; +import { BILI_BVID_TO_CID, BILI_DYNAMIC, BILI_PLAY_STREAM, BILI_VIDEO_INFO } from "../constants/tools.js"; +import { mkdirIfNotExists } from "./file.js"; -async function downloadBFile (url, fullFileName, progressCallback) { +/** + * 下载单个bili文件 + * @param url + * @param fullFileName + * @param progressCallback + * @returns {Promise} + */ +export async function downloadBFile(url, fullFileName, progressCallback) { return axios .get(url, { responseType: 'stream', @@ -35,7 +44,12 @@ async function downloadBFile (url, fullFileName, progressCallback) { }); } -async function getDownloadUrl (url) { +/** + * 获取下载链接 + * @param url + * @returns {Promise} + */ +export async function getDownloadUrl(url) { return axios .get(url, { headers: { @@ -65,34 +79,15 @@ async function getDownloadUrl (url) { }); } -async function getAudioUrl (url) { - return axios - .get(url, { - headers: { - 'User-Agent': - 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36', - referer: 'https://www.bilibili.com', - }, - }) - .then(({ data }) => { - const info = JSON.parse( - data.match(/