diff --git a/src/common/config.ts b/src/common/config.ts index 39269cb..d8336e2 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -6,7 +6,7 @@ import path from "node:path"; import {selfInfo} from "./data"; import {DATA_DIR} from "./utils"; -export const HOOK_LOG = false; +export const HOOK_LOG = true; export const ALLOW_SEND_TEMP_MSG = false; diff --git a/src/common/utils/file.ts b/src/common/utils/file.ts index edf579c..530cc43 100644 --- a/src/common/utils/file.ts +++ b/src/common/utils/file.ts @@ -11,7 +11,7 @@ import {getConfigUtil} from "../config"; import {dbUtil} from "../db"; import * as fileType from "file-type"; import {net} from "electron"; -import ClientRequestConstructorOptions = Electron.Main.ClientRequestConstructorOptions; + export function isGIF(path: string) { 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((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 { return new Promise((resolve, reject) => { diff --git a/src/common/utils/index.ts b/src/common/utils/index.ts index 58c3afa..5898e52 100644 --- a/src/common/utils/index.ts +++ b/src/common/utils/index.ts @@ -12,4 +12,5 @@ export const TEMP_DIR = path.join(DATA_DIR, "temp"); export const PLUGIN_DIR = global.LiteLoader.plugins["LLOneBot"].path.plugin; if (!fs.existsSync(TEMP_DIR)) { fs.mkdirSync(TEMP_DIR); -} \ No newline at end of file +} +export {getVideoInfo} from "./video"; \ No newline at end of file diff --git a/src/common/utils/video.ts b/src/common/utils/video.ts index d3d6c30..83a5f20 100644 --- a/src/common/utils/video.ts +++ b/src/common/utils/video.ts @@ -1,5 +1,63 @@ +import {log} from "./log"; +import ffmpeg from "fluent-ffmpeg"; +import fs from "fs"; const defaultVideoThumbB64 = "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" export const defaultVideoThumb = Buffer.from(defaultVideoThumbB64, 'base64') +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) { + 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((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 +} \ No newline at end of file diff --git a/src/main/main.ts b/src/main/main.ts index 42257d0..d16b8c1 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -94,7 +94,7 @@ function onLoad() { } ipcMain.handle(CHANNEL_ERROR, async (event, arg) => { 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 error = `${otherError}\n${httpServerError}\n${wsServerError}\n${ffmpegError}` error = error.replace("\n\n", "\n") diff --git a/src/ntqqapi/constructor.ts b/src/ntqqapi/constructor.ts index 3674d73..57d4c23 100644 --- a/src/ntqqapi/constructor.ts +++ b/src/ntqqapi/constructor.ts @@ -14,10 +14,9 @@ import { import {promises as fs} from "node:fs"; import ffmpeg from "fluent-ffmpeg" 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 {sleep} from "../common/utils/helper"; -import pathLib from "path"; +import {defaultVideoThumb, getVideoInfo} from "../common/utils/video"; export class SendMsgElementConstructor { @@ -109,36 +108,45 @@ export class SendMsgElementConstructor { return element; } - static async video(filePath: string, fileName: string = "", diyThumbPath: string=""): Promise { + static async video(filePath: string, fileName: string = "", diyThumbPath: string = ""): Promise { let {fileName: _fileName, path, fileSize, md5} = await NTQQFileApi.uploadFile(filePath, ElementType.VIDEO); if (fileSize === 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"); let thumb = path.replace(`${pathLib.sep}Ori${pathLib.sep}`, `${pathLib.sep}Thumb${pathLib.sep}`) thumb = pathLib.dirname(thumb) // log("thumb 目录", thumb) - const videoInfo = await getVideoInfo(path); - log("视频信息", videoInfo) + let videoInfo ={ + width: 1920, height: 1080, + time: 15, + format: "mp4", + size: fileSize, + filePath + }; + try { + videoInfo = await getVideoInfo(path); + log("视频信息", videoInfo) + }catch (e) { + log("获取视频信息失败", e) + } const createThumb = new Promise((resolve, reject) => { const thumbFileName = `${md5}_0.png` const thumbPath = pathLib.join(thumb, thumbFileName) - if (diyThumbPath) { - fs.copyFile(diyThumbPath, pathLib.join(thumb, thumbFileName)).then(() => { - resolve(thumbPath); - }) - return; - } ffmpeg(filePath) .on("end", () => { }) .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({ timestamps: [0], diff --git a/src/renderer/style.css b/src/renderer/style.css index 243c8a8..842b702 100644 --- a/src/renderer/style.css +++ b/src/renderer/style.css @@ -157,8 +157,8 @@ ob-setting-select::part(option-list) { } #llonebot-error { - color: red; - height: 100px; + padding-top: 10px; + padding-bottom: 10px; overflow: visible; display: flex; align-items: center;