🎈 pref: 优化 哔哩哔哩 专栏发送逻辑 && 优化直播切片

This commit is contained in:
zhiyu1998 2024-10-01 13:37:21 +08:00
parent 5782a3ad22
commit e285ab042e
3 changed files with 83 additions and 17 deletions

View File

@ -8,6 +8,8 @@ import fs from "node:fs";
import PQueue from 'p-queue'; import PQueue from 'p-queue';
import path from "path"; import path from "path";
import querystring from "querystring"; import querystring from "querystring";
import { pipeline } from 'stream';
import { promisify } from 'util';
import { import {
BILI_CDN_SELECT_LIST, BILI_CDN_SELECT_LIST,
BILI_DEFAULT_INTRO_LEN_LIMIT, BILI_DEFAULT_INTRO_LEN_LIMIT,
@ -28,6 +30,7 @@ import {
import { import {
ANIME_SERIES_SEARCH_LINK, ANIME_SERIES_SEARCH_LINK,
ANIME_SERIES_SEARCH_LINK2, ANIME_SERIES_SEARCH_LINK2,
BILI_ARTICLE_INFO,
BILI_EP_INFO, BILI_EP_INFO,
BILI_ONLINE, BILI_ONLINE,
BILI_SSID_INFO, BILI_SSID_INFO,
@ -362,7 +365,6 @@ export class tools extends plugin {
// 普通类型 // 普通类型
dyApi = DY_INFO.replace("{}", douId); dyApi = DY_INFO.replace("{}", douId);
} }
logger.info(dyApi);
// a-bogus参数 // a-bogus参数
const abParam = aBogus.generate_a_bogus( const abParam = aBogus.generate_a_bogus(
new URLSearchParams(new URL(dyApi).search).toString(), new URLSearchParams(new URL(dyApi).search).toString(),
@ -481,28 +483,27 @@ export class tools extends plugin {
* @param second * @param second
*/ */
async sendStreamSegment(e, stream_url, second = this.streamDuration) { async sendStreamSegment(e, stream_url, second = this.streamDuration) {
const pipelineAsync = promisify(pipeline);
const outputFilePath = `${ this.getCurDownloadPath(e) }/stream_10s.flv`; const outputFilePath = `${ this.getCurDownloadPath(e) }/stream_10s.flv`;
await checkAndRemoveFile(outputFilePath); await checkAndRemoveFile(outputFilePath);
const file = fs.createWriteStream(outputFilePath);
axios.get(stream_url, {
responseType: 'stream'
}).then(response => {
logger.info("[R插件][发送直播流] 正在下载直播流...");
// 将流数据写入文件
response.data.pipe(file);
// 设置 10 秒后停止下载 try {
const response = await axios.get(stream_url, { responseType: 'stream' });
logger.info("[R插件][发送直播流] 正在下载直播流...");
const file = fs.createWriteStream(outputFilePath);
await pipelineAsync(response.data, file);
// 设置 streamDuration 秒后停止下载
setTimeout(async () => { setTimeout(async () => {
logger.info(`[R插件][发送直播流] 直播下载 ${ this.streamDuration } 秒钟到,停止下载!`); logger.info(`[R插件][发送直播流] 直播下载 ${ this.streamDuration } 秒钟到,停止下载!`);
response.data.destroy(); // 销毁流 response.data.destroy(); // 销毁流
await this.sendVideoToUpload(e, outputFilePath); await this.sendVideoToUpload(e, outputFilePath);
file.close(); // 关闭文件流 }, second * 1000);
}, second * 1000); // 10秒 = 10000毫秒 } catch (error) {
}).catch(error => {
logger.error(`下载失败: ${ error.message }`); logger.error(`下载失败: ${ error.message }`);
fs.unlink(outputFilePath, () => { await fs.promises.unlink(outputFilePath); // 下载失败时删除文件
}); // 下载失败时删除文件 }
});
} }
/** /**
@ -648,6 +649,7 @@ export class tools extends plugin {
} }
// 处理专栏 // 处理专栏
if (e.msg !== undefined && e.msg.includes("read\/cv")) { if (e.msg !== undefined && e.msg.includes("read\/cv")) {
await this.biliArticle(e);
this.linkShareSummary(e); this.linkShareSummary(e);
return true; return true;
} }
@ -708,6 +710,31 @@ export class tools extends plugin {
return true; return true;
} }
/**
* 提取哔哩哔哩专栏
* @param e
* @returns {Promise<void>}
*/
async biliArticle(e) {
const cvid = e.msg.match(/read\/cv(\d+)/)?.[1];
logger.info(BILI_ARTICLE_INFO.replace("{}", cvid));
const articleResp = await fetch(BILI_ARTICLE_INFO.replace("{}", cvid), {
headers: BILI_HEADER
});
const articleData = (await articleResp.json()).data;
const { title, author_name, origin_image_urls } = articleData;
e.reply(`${ this.identifyPrefix }识别:哔哩哔哩专栏,${ title }-${ author_name }\n`);
if (origin_image_urls) {
await e.reply(Bot.makeForwardMsg(origin_image_urls.map(item => {
return {
message: segment.image(item),
nickname: e.sender.card || e.user_id,
user_id: e.user_id,
}
})))
}
}
/** /**
* 构造哔哩哔哩信息 * 构造哔哩哔哩信息
* @param videoInfo * @param videoInfo

View File

@ -89,6 +89,13 @@ export const BILI_EP_INFO = "https://api.bilibili.com/pgc/view/web/season?ep_id=
*/ */
export const BILI_SSID_INFO = "https://api.bilibili.com/pgc/web/season/section?season_id={}" export const BILI_SSID_INFO = "https://api.bilibili.com/pgc/web/season/section?season_id={}"
/**
* 专栏信息
* https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/article/info.md
* @type {string}
*/
export const BILI_ARTICLE_INFO = "https://api.bilibili.com/x/article/viewinfo?id={}"
/** /**
* 米游社网页端获取文章 * 米游社网页端获取文章
* https://github.com/UIGF-org/mihoyo-api-collect/blob/main/hoyolab/article/article.md#%E8%8E%B7%E5%8F%96%E5%AE%8C%E6%95%B4%E6%96%87%E7%AB%A0%E4%BF%A1%E6%81%AF * https://github.com/UIGF-org/mihoyo-api-collect/blob/main/hoyolab/article/article.md#%E8%8E%B7%E5%8F%96%E5%AE%8C%E6%95%B4%E6%96%87%E7%AB%A0%E4%BF%A1%E6%81%AF

View File

@ -1,7 +1,13 @@
import path from 'path'; import path from 'path';
import { exec } from 'child_process'; import { exec } from 'child_process';
// 写一个执行ffmpeg提取视频关键帧的函数例如ffmpeg -i input.mp4 -vf "select=eq(pict_type\,I)" -vsync drop -vframes 5 -qscale:v 2 keyframes\keyframe_%03d.jpg /**
* 提取关键帧
* @param inputFilePath
* @param outputFolderPath
* @param frameCount
* @returns {Promise<unknown>}
*/
export async function extractKeyframes(inputFilePath, outputFolderPath, frameCount = 20) { export async function extractKeyframes(inputFilePath, outputFolderPath, frameCount = 20) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// 创建输出文件夹路径 // 创建输出文件夹路径
@ -21,3 +27,29 @@ export async function extractKeyframes(inputFilePath, outputFolderPath, frameCou
}); });
}); });
} }
/**
* 使用 ffmpeg FLV 文件转换为 MP4 文件
* @param {string} inputFilePath - 输入的 FLV 文件路径
* @param {string} outputFilePath - 输出的 MP4 文件路径
* @returns {Promise<string>} - 返回一个 Promise成功时返回输出文件路径失败时返回错误信息
*/
function convertFlvToMp4(inputFilePath, outputFilePath) {
return new Promise((resolve, reject) => {
const command = `ffmpeg -i ${inputFilePath} ${outputFilePath}`;
logger.info(`[R插件][ffmpeg工具]执行命令:${command}`);
exec(command, (error, stdout, stderr) => {
if (error) {
reject(`执行 ffmpeg 命令时出错: ${error.message}`);
return;
}
if (stderr) {
reject(`ffmpeg 标准错误输出: ${stderr}`);
return;
}
resolve(outputFilePath);
});
});
}