feat: send video not need ffmpeg

This commit is contained in:
linyuchen 2024-03-19 20:35:30 +08:00
parent 8afe0af940
commit a298377717
7 changed files with 91 additions and 85 deletions

View File

@ -6,7 +6,7 @@ import path from "node:path";
import {selfInfo} from "./data"; import {selfInfo} from "./data";
import {DATA_DIR} from "./utils"; import {DATA_DIR} from "./utils";
export const HOOK_LOG = false; export const HOOK_LOG = true;
export const ALLOW_SEND_TEMP_MSG = false; export const ALLOW_SEND_TEMP_MSG = false;

View File

@ -11,7 +11,7 @@ import {getConfigUtil} from "../config";
import {dbUtil} from "../db"; import {dbUtil} from "../db";
import * as fileType from "file-type"; import * as fileType from "file-type";
import {net} from "electron"; import {net} from "electron";
import ClientRequestConstructorOptions = Electron.Main.ClientRequestConstructorOptions;
export function isGIF(path: string) { export function isGIF(path: string) {
const buffer = Buffer.alloc(4); const buffer = Buffer.alloc(4);
@ -191,68 +191,7 @@ export async function encodeSilk(filePath: string) {
} }
} }
export async function getVideoInfo(filePath: string) {
const size = fs.statSync(filePath).size;
return new Promise<{
width: number,
height: number,
time: number,
format: string,
size: number,
filePath: string
}>((resolve, reject) => {
ffmpeg(filePath).ffprobe((err, metadata) => {
if (err) {
resolve({
width: 720, height: 1080,
time: 15,
format: "mp4",
size: fs.statSync(filePath).size,
filePath
})
// reject(err);
} else {
const videoStream = metadata.streams.find(s => s.codec_type === 'video');
if (videoStream) {
console.log(`视频尺寸: ${videoStream.width}x${videoStream.height}`);
} else {
console.log('未找到视频流信息。');
}
resolve({
width: videoStream.width, height: videoStream.height,
time: parseInt(videoStream.duration),
format: metadata.format.format_name,
size,
filePath
});
}
});
})
}
export async function encodeMp4(filePath: string) {
let videoInfo = await getVideoInfo(filePath);
log("视频信息", videoInfo)
if (videoInfo.format.indexOf("mp4") === -1) {
log("视频需要转换为MP4格式", filePath)
// 转成mp4
const newPath: string = await new Promise<string>((resolve, reject) => {
const newPath = filePath + ".mp4"
ffmpeg(filePath)
.toFormat('mp4')
.on('error', (err) => {
reject(`转换视频格式失败: ${err.message}`);
})
.on('end', () => {
log('视频转换为MP4格式完成');
resolve(newPath); // 返回转换后的文件路径
})
.save(newPath);
});
return await getVideoInfo(newPath)
}
return videoInfo
}
export function calculateFileMD5(filePath: string): Promise<string> { export function calculateFileMD5(filePath: string): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

View File

@ -13,3 +13,4 @@ export const PLUGIN_DIR = global.LiteLoader.plugins["LLOneBot"].path.plugin;
if (!fs.existsSync(TEMP_DIR)) { if (!fs.existsSync(TEMP_DIR)) {
fs.mkdirSync(TEMP_DIR); fs.mkdirSync(TEMP_DIR);
} }
export {getVideoInfo} from "./video";

File diff suppressed because one or more lines are too long

View File

@ -94,7 +94,7 @@ function onLoad() {
} }
ipcMain.handle(CHANNEL_ERROR, async (event, arg) => { ipcMain.handle(CHANNEL_ERROR, async (event, arg) => {
const ffmpegOk = await checkFfmpeg(getConfigUtil().getConfig().ffmpeg) const ffmpegOk = await checkFfmpeg(getConfigUtil().getConfig().ffmpeg)
llonebotError.ffmpegError = ffmpegOk ? "" : "没有找到ffmpeg,音频只能发送wav和silk,视频无法发送" llonebotError.ffmpegError = ffmpegOk ? "" : "没有找到ffmpeg,音频只能发送wav和silk,视频尺寸可能异常"
let {httpServerError, wsServerError, otherError, ffmpegError} = llonebotError; let {httpServerError, wsServerError, otherError, ffmpegError} = llonebotError;
let error = `${otherError}\n${httpServerError}\n${wsServerError}\n${ffmpegError}` let error = `${otherError}\n${httpServerError}\n${wsServerError}\n${ffmpegError}`
error = error.replace("\n\n", "\n") error = error.replace("\n\n", "\n")

View File

@ -14,10 +14,9 @@ import {
import {promises as fs} from "node:fs"; import {promises as fs} from "node:fs";
import ffmpeg from "fluent-ffmpeg" import ffmpeg from "fluent-ffmpeg"
import {NTQQFileApi} from "./api/file"; import {NTQQFileApi} from "./api/file";
import {calculateFileMD5, encodeSilk, getVideoInfo, isGIF} from "../common/utils/file"; import {calculateFileMD5, encodeSilk, isGIF} from "../common/utils/file";
import {log} from "../common/utils/log"; import {log} from "../common/utils/log";
import {sleep} from "../common/utils/helper"; import {defaultVideoThumb, getVideoInfo} from "../common/utils/video";
import pathLib from "path";
export class SendMsgElementConstructor { export class SendMsgElementConstructor {
@ -114,31 +113,40 @@ export class SendMsgElementConstructor {
if (fileSize === 0) { if (fileSize === 0) {
throw "文件异常大小为0"; throw "文件异常大小为0";
} }
// const videoInfo = await encodeMp4(path);
// path = videoInfo.filePath
// md5 = videoInfo.md5;
// fileSize = videoInfo.size;
// log("上传视频", md5, path, fileSize, fileName || _fileName)
const pathLib = require("path"); const pathLib = require("path");
let thumb = path.replace(`${pathLib.sep}Ori${pathLib.sep}`, `${pathLib.sep}Thumb${pathLib.sep}`) let thumb = path.replace(`${pathLib.sep}Ori${pathLib.sep}`, `${pathLib.sep}Thumb${pathLib.sep}`)
thumb = pathLib.dirname(thumb) thumb = pathLib.dirname(thumb)
// log("thumb 目录", thumb) // log("thumb 目录", thumb)
const videoInfo = await getVideoInfo(path); let videoInfo ={
width: 1920, height: 1080,
time: 15,
format: "mp4",
size: fileSize,
filePath
};
try {
videoInfo = await getVideoInfo(path);
log("视频信息", videoInfo) log("视频信息", videoInfo)
}catch (e) {
log("获取视频信息失败", e)
}
const createThumb = new Promise<string>((resolve, reject) => { const createThumb = new Promise<string>((resolve, reject) => {
const thumbFileName = `${md5}_0.png` const thumbFileName = `${md5}_0.png`
const thumbPath = pathLib.join(thumb, thumbFileName) const thumbPath = pathLib.join(thumb, thumbFileName)
if (diyThumbPath) {
fs.copyFile(diyThumbPath, pathLib.join(thumb, thumbFileName)).then(() => {
resolve(thumbPath);
})
return;
}
ffmpeg(filePath) ffmpeg(filePath)
.on("end", () => { .on("end", () => {
}) })
.on("error", (err) => { .on("error", (err) => {
reject(err); log("获取视频封面失败,使用默认封面", err)
if (diyThumbPath) {
fs.copyFile(diyThumbPath, thumbPath).then(() => {
resolve(thumbPath);
}).catch(reject)
} else {
fs.writeFile(thumbPath, defaultVideoThumb).then(() => {
resolve(thumbPath);
}).catch(reject)
}
}) })
.screenshots({ .screenshots({
timestamps: [0], timestamps: [0],

View File

@ -157,8 +157,8 @@ ob-setting-select::part(option-list) {
} }
#llonebot-error { #llonebot-error {
color: red; padding-top: 10px;
height: 100px; padding-bottom: 10px;
overflow: visible; overflow: visible;
display: flex; display: flex;
align-items: center; align-items: center;