mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
fix: 彻底完成迁移
This commit is contained in:
@@ -30,7 +30,6 @@
|
|||||||
"@types/cors": "^2.8.17",
|
"@types/cors": "^2.8.17",
|
||||||
"@sinclair/typebox": "^0.34.9",
|
"@sinclair/typebox": "^0.34.9",
|
||||||
"@types/express": "^5.0.0",
|
"@types/express": "^5.0.0",
|
||||||
"@types/fluent-ffmpeg": "^2.1.24",
|
|
||||||
"@types/node": "^22.0.1",
|
"@types/node": "^22.0.1",
|
||||||
"@types/qrcode-terminal": "^0.12.2",
|
"@types/qrcode-terminal": "^0.12.2",
|
||||||
"@types/ws": "^8.5.12",
|
"@types/ws": "^8.5.12",
|
||||||
@@ -58,7 +57,6 @@
|
|||||||
"@ffmpeg.wasm/core-mt": "^0.13.2",
|
"@ffmpeg.wasm/core-mt": "^0.13.2",
|
||||||
"@ffmpeg.wasm/main": "^0.13.1",
|
"@ffmpeg.wasm/main": "^0.13.1",
|
||||||
"express": "^5.0.0",
|
"express": "^5.0.0",
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
|
||||||
"piscina": "^4.7.0",
|
"piscina": "^4.7.0",
|
||||||
"qrcode-terminal": "^0.12.0",
|
"qrcode-terminal": "^0.12.0",
|
||||||
"silk-wasm": "^3.6.1",
|
"silk-wasm": "^3.6.1",
|
||||||
|
@@ -5,7 +5,7 @@ import { randomUUID } from 'crypto';
|
|||||||
import { EncodeResult, getDuration, getWavFileInfo, isSilk, isWav } from 'silk-wasm';
|
import { EncodeResult, getDuration, getWavFileInfo, isSilk, isWav } from 'silk-wasm';
|
||||||
import { LogWrapper } from '@/common/log';
|
import { LogWrapper } from '@/common/log';
|
||||||
import { EncodeArgs } from "@/common/audio-worker";
|
import { EncodeArgs } from "@/common/audio-worker";
|
||||||
import { ffmpegService } from "@/common/ffmpeg";
|
import { FFmpegService } from "@/common/ffmpeg";
|
||||||
|
|
||||||
const ALLOW_SAMPLE_RATE = [8000, 12000, 16000, 24000, 32000, 44100, 48000];
|
const ALLOW_SAMPLE_RATE = [8000, 12000, 16000, 24000, 32000, 44100, 48000];
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ async function handleWavFile(
|
|||||||
): Promise<{ input: Buffer; sampleRate: number }> {
|
): Promise<{ input: Buffer; sampleRate: number }> {
|
||||||
const { fmt } = getWavFileInfo(file);
|
const { fmt } = getWavFileInfo(file);
|
||||||
if (!ALLOW_SAMPLE_RATE.includes(fmt.sampleRate)) {
|
if (!ALLOW_SAMPLE_RATE.includes(fmt.sampleRate)) {
|
||||||
return { input: await ffmpegService.convert(filePath, pcmPath, logger), sampleRate: 24000 };
|
return { input: await FFmpegService.convert(filePath, pcmPath, logger), sampleRate: 24000 };
|
||||||
}
|
}
|
||||||
return { input: file, sampleRate: fmt.sampleRate };
|
return { input: file, sampleRate: fmt.sampleRate };
|
||||||
}
|
}
|
||||||
@@ -46,7 +46,7 @@ export async function encodeSilk(filePath: string, TEMP_DIR: string, logger: Log
|
|||||||
const pcmPath = `${pttPath}.pcm`;
|
const pcmPath = `${pttPath}.pcm`;
|
||||||
const { input, sampleRate } = isWav(file)
|
const { input, sampleRate } = isWav(file)
|
||||||
? (await handleWavFile(file, filePath, pcmPath, logger))
|
? (await handleWavFile(file, filePath, pcmPath, logger))
|
||||||
: { input: await ffmpegService.convert(filePath, pcmPath, logger), sampleRate: 24000 };
|
: { input: await FFmpegService.convert(filePath, pcmPath, logger), sampleRate: 24000 };
|
||||||
const silk = await piscina.run({ input: input, sampleRate: sampleRate });
|
const silk = await piscina.run({ input: input, sampleRate: sampleRate });
|
||||||
await fsPromise.writeFile(pttPath, Buffer.from(silk.data));
|
await fsPromise.writeFile(pttPath, Buffer.from(silk.data));
|
||||||
logger.log(`语音文件${filePath}转换成功!`, pttPath, '时长:', silk.duration);
|
logger.log(`语音文件${filePath}转换成功!`, pttPath, '时长:', silk.duration);
|
||||||
|
@@ -1,104 +1,131 @@
|
|||||||
import { FFmpeg } from '@ffmpeg.wasm/main';
|
import { FFmpeg } from '@ffmpeg.wasm/main';
|
||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
import { readFileSync, writeFileSync } from 'fs';
|
import { readFileSync, statSync, writeFileSync } from 'fs';
|
||||||
import { LogWrapper } from './log';
|
import { LogWrapper } from './log';
|
||||||
|
import { VideoInfo } from './video';
|
||||||
class FFmpegService {
|
import { fileTypeFromFile } from 'file-type';
|
||||||
private ffmpegRef: FFmpeg;
|
import imageSize from 'image-size';
|
||||||
|
export class FFmpegService {
|
||||||
constructor(ffmpegRef: FFmpeg) {
|
public static async extractThumbnail(videoPath: string, thumbnailPath: string): Promise<void> {
|
||||||
this.ffmpegRef = ffmpegRef;
|
const ffmpegInstance = await FFmpeg.create({ core: '@ffmpeg.wasm/core-mt' });
|
||||||
}
|
|
||||||
|
|
||||||
public async extractThumbnail(videoPath: string, thumbnailPath: string): Promise<void> {
|
|
||||||
const videoFileName = `${randomUUID()}.mp4`;
|
const videoFileName = `${randomUUID()}.mp4`;
|
||||||
const outputFileName = `${randomUUID()}.jpg`;
|
const outputFileName = `${randomUUID()}.jpg`;
|
||||||
try {
|
try {
|
||||||
this.ffmpegRef.fs.writeFile(videoFileName, readFileSync(videoPath));
|
ffmpegInstance.fs.writeFile(videoFileName, readFileSync(videoPath));
|
||||||
let code = await this.ffmpegRef.run('-i', videoFileName, '-ss', '00:00:01.000', '-vframes', '1', outputFileName);
|
let code = await ffmpegInstance.run('-i', videoFileName, '-ss', '00:00:01.000', '-vframes', '1', outputFileName);
|
||||||
if (code! === 0) {
|
if (code !== 0) {
|
||||||
throw new Error('Error extracting thumbnail: FFmpeg process exited with code ' + code);
|
throw new Error('Error extracting thumbnail: FFmpeg process exited with code ' + code);
|
||||||
}
|
}
|
||||||
const thumbnail = this.ffmpegRef.fs.readFile(outputFileName);
|
const thumbnail = ffmpegInstance.fs.readFile(outputFileName);
|
||||||
writeFileSync(thumbnailPath, thumbnail);
|
writeFileSync(thumbnailPath, thumbnail);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error extracting thumbnail:', error);
|
console.error('Error extracting thumbnail:', error);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
this.ffmpegRef.fs.unlink(outputFileName);
|
ffmpegInstance.fs.unlink(outputFileName);
|
||||||
} catch (unlinkError) {
|
} catch (unlinkError) {
|
||||||
console.error('Error unlinking output file:', unlinkError);
|
console.error('Error unlinking output file:', unlinkError);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this.ffmpegRef.fs.unlink(videoFileName);
|
ffmpegInstance.fs.unlink(videoFileName);
|
||||||
} catch (unlinkError) {
|
} catch (unlinkError) {
|
||||||
console.error('Error unlinking video file:', unlinkError);
|
console.error('Error unlinking video file:', unlinkError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async convertFile(inputFile: string, outputFile: string, format: string): Promise<void> {
|
public static async convertFile(inputFile: string, outputFile: string, format: string): Promise<void> {
|
||||||
|
const ffmpegInstance = await FFmpeg.create({ core: '@ffmpeg.wasm/core-mt' });
|
||||||
const inputFileName = `${randomUUID()}.pcm`;
|
const inputFileName = `${randomUUID()}.pcm`;
|
||||||
const outputFileName = `${randomUUID()}.${format}`;
|
const outputFileName = `${randomUUID()}.${format}`;
|
||||||
try {
|
try {
|
||||||
this.ffmpegRef.fs.writeFile(inputFileName, readFileSync(inputFile));
|
ffmpegInstance.fs.writeFile(inputFileName, readFileSync(inputFile));
|
||||||
const params = format === 'amr'
|
const params = format === 'amr'
|
||||||
? ['-f', 's16le', '-ar', '24000', '-ac', '1', '-i', inputFileName, '-ar', '8000', '-b:a', '12.2k', outputFileName]
|
? ['-f', 's16le', '-ar', '24000', '-ac', '1', '-i', inputFileName, '-ar', '8000', '-b:a', '12.2k', outputFileName]
|
||||||
: ['-f', 's16le', '-ar', '24000', '-ac', '1', '-i', inputFileName, outputFileName];
|
: ['-f', 's16le', '-ar', '24000', '-ac', '1', '-i', inputFileName, outputFileName];
|
||||||
let code = await this.ffmpegRef.run(...params);
|
let code = await ffmpegInstance.run(...params);
|
||||||
if (code! === 0) {
|
if (code !== 0) {
|
||||||
throw new Error('Error extracting thumbnail: FFmpeg process exited with code ' + code);
|
throw new Error('Error extracting thumbnail: FFmpeg process exited with code ' + code);
|
||||||
}
|
}
|
||||||
const outputData = this.ffmpegRef.fs.readFile(outputFileName);
|
const outputData = ffmpegInstance.fs.readFile(outputFileName);
|
||||||
writeFileSync(outputFile, outputData);
|
writeFileSync(outputFile, outputData);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error converting file:', error);
|
console.error('Error converting file:', error);
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
this.ffmpegRef.fs.unlink(outputFileName);
|
ffmpegInstance.fs.unlink(outputFileName);
|
||||||
} catch (unlinkError) {
|
} catch (unlinkError) {
|
||||||
console.error('Error unlinking output file:', unlinkError);
|
console.error('Error unlinking output file:', unlinkError);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this.ffmpegRef.fs.unlink(inputFileName);
|
ffmpegInstance.fs.unlink(inputFileName);
|
||||||
} catch (unlinkError) {
|
} catch (unlinkError) {
|
||||||
console.error('Error unlinking input file:', unlinkError);
|
console.error('Error unlinking input file:', unlinkError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async convert(filePath: string, pcmPath: string, logger: LogWrapper): Promise<Buffer> {
|
public static async convert(filePath: string, pcmPath: string, logger: LogWrapper): Promise<Buffer> {
|
||||||
|
const ffmpegInstance = await FFmpeg.create({ core: '@ffmpeg.wasm/core-mt' });
|
||||||
const inputFileName = `${randomUUID()}.input`;
|
const inputFileName = `${randomUUID()}.input`;
|
||||||
const outputFileName = `${randomUUID()}.pcm`;
|
const outputFileName = `${randomUUID()}.pcm`;
|
||||||
try {
|
try {
|
||||||
this.ffmpegRef.fs.writeFile(inputFileName, readFileSync(filePath));
|
ffmpegInstance.fs.writeFile(inputFileName, readFileSync(filePath));
|
||||||
const params = ['-y', '-i', inputFileName, '-ar', '24000', '-ac', '1', '-f', 's16le', outputFileName];
|
const params = ['-y', '-i', inputFileName, '-ar', '24000', '-ac', '1', '-f', 's16le', outputFileName];
|
||||||
let code = await this.ffmpegRef.run(...params);
|
let code = await ffmpegInstance.run(...params);
|
||||||
if (code! === 0) {
|
if (code !== 0) {
|
||||||
throw new Error('FFmpeg process exited with code ' + code);
|
throw new Error('FFmpeg process exited with code ' + code);
|
||||||
}
|
}
|
||||||
const outputData = this.ffmpegRef.fs.readFile(outputFileName);
|
const outputData = ffmpegInstance.fs.readFile(outputFileName);
|
||||||
writeFileSync(pcmPath, outputData);
|
writeFileSync(pcmPath, outputData);
|
||||||
return Buffer.from(outputData);
|
return Buffer.from(outputData);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
logger.log('FFmpeg处理转换出错: ', error.message);
|
throw new Error('FFmpeg处理转换出错: ' + error.message);
|
||||||
throw error;
|
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
this.ffmpegRef.fs.unlink(outputFileName);
|
ffmpegInstance.fs.unlink(outputFileName);
|
||||||
} catch (unlinkError) {
|
} catch (unlinkError) {
|
||||||
logger.log('Error unlinking output file:', unlinkError);
|
logger.log('Error unlinking output file:', unlinkError);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
this.ffmpegRef.fs.unlink(inputFileName);
|
ffmpegInstance.fs.unlink(inputFileName);
|
||||||
} catch (unlinkError) {
|
} catch (unlinkError) {
|
||||||
logger.log('Error unlinking input file:', unlinkError);
|
logger.log('Error unlinking input file:', unlinkError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const ffmpegInstance = await FFmpeg.create({ core: '@ffmpeg.wasm/core-mt' });
|
public static async getVideoInfo(videoPath: string, thumbnailPath: string): Promise<VideoInfo> {
|
||||||
export const ffmpegService = new FFmpegService(ffmpegInstance);
|
await FFmpegService.extractThumbnail(videoPath, thumbnailPath);
|
||||||
|
let fileType = (await fileTypeFromFile(videoPath))?.ext ?? 'mp4';
|
||||||
|
const inputFileName = `${randomUUID()}.${fileType}`;
|
||||||
|
const ffmpegInstance = await FFmpeg.create({ core: '@ffmpeg.wasm/core-mt' });
|
||||||
|
ffmpegInstance.fs.writeFile(inputFileName, readFileSync(videoPath));
|
||||||
|
ffmpegInstance.setLogging(true);
|
||||||
|
let duration = 60;
|
||||||
|
ffmpegInstance.setLogger((level, ...msg) => {
|
||||||
|
const message = msg.join(' ');
|
||||||
|
const durationMatch = message.match(/Duration: (\d+):(\d+):(\d+\.\d+)/);
|
||||||
|
if (durationMatch) {
|
||||||
|
const hours = parseInt(durationMatch[1], 10);
|
||||||
|
const minutes = parseInt(durationMatch[2], 10);
|
||||||
|
const seconds = parseFloat(durationMatch[3]);
|
||||||
|
duration = hours * 3600 + minutes * 60 + seconds;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
await ffmpegInstance.run('-i', inputFileName);
|
||||||
|
let image = imageSize(thumbnailPath);
|
||||||
|
ffmpegInstance.fs.unlink(inputFileName);
|
||||||
|
const fileSize = statSync(videoPath).size;
|
||||||
|
return {
|
||||||
|
width: image.width ?? 100,
|
||||||
|
height: image.height ?? 100,
|
||||||
|
time: duration,
|
||||||
|
format: fileType,
|
||||||
|
size: fileSize,
|
||||||
|
filePath: videoPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
@@ -22,11 +22,11 @@ import { ISizeCalculationResult } from 'image-size/dist/types/interface';
|
|||||||
import { RkeyManager } from '@/core/helper/rkey';
|
import { RkeyManager } from '@/core/helper/rkey';
|
||||||
import { calculateFileMD5 } from '@/common/file';
|
import { calculateFileMD5 } from '@/common/file';
|
||||||
import pathLib from 'node:path';
|
import pathLib from 'node:path';
|
||||||
import { defaultVideoThumbB64, getVideoInfo } from '@/common/video';
|
import { defaultVideoThumbB64 } from '@/common/video';
|
||||||
import ffmpeg from 'fluent-ffmpeg';
|
|
||||||
import { encodeSilk } from '@/common/audio';
|
import { encodeSilk } from '@/common/audio';
|
||||||
import { SendMessageContext } from '@/onebot/api';
|
import { SendMessageContext } from '@/onebot/api';
|
||||||
import { getFileTypeForSendType } from '../helper/msg';
|
import { getFileTypeForSendType } from '../helper/msg';
|
||||||
|
import { FFmpegService } from '@/common/ffmpeg';
|
||||||
|
|
||||||
export class NTQQFileApi {
|
export class NTQQFileApi {
|
||||||
context: InstanceContext;
|
context: InstanceContext;
|
||||||
@@ -149,12 +149,6 @@ export class NTQQFileApi {
|
|||||||
size: 0,
|
size: 0,
|
||||||
filePath,
|
filePath,
|
||||||
};
|
};
|
||||||
try {
|
|
||||||
videoInfo = await getVideoInfo(filePath, this.context.logger);
|
|
||||||
} catch (e) {
|
|
||||||
this.context.logger.logError('获取视频信息失败,将使用默认值', e);
|
|
||||||
}
|
|
||||||
|
|
||||||
let fileExt = 'mp4';
|
let fileExt = 'mp4';
|
||||||
try {
|
try {
|
||||||
const tempExt = (await fileTypeFromFile(filePath))?.ext;
|
const tempExt = (await fileTypeFromFile(filePath))?.ext;
|
||||||
@@ -162,53 +156,29 @@ export class NTQQFileApi {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.context.logger.logError('获取文件类型失败', e);
|
this.context.logger.logError('获取文件类型失败', e);
|
||||||
}
|
}
|
||||||
const newFilePath = filePath + '.' + fileExt;
|
const newFilePath = `${filePath}.${fileExt}`;
|
||||||
fs.copyFileSync(filePath, newFilePath);
|
fs.copyFileSync(filePath, newFilePath);
|
||||||
context.deleteAfterSentFiles.push(newFilePath);
|
context.deleteAfterSentFiles.push(newFilePath);
|
||||||
filePath = newFilePath;
|
filePath = newFilePath;
|
||||||
|
|
||||||
const { fileName: _fileName, path, fileSize, md5 } = await this.core.apis.FileApi.uploadFile(filePath, ElementType.VIDEO);
|
const { fileName: _fileName, path, fileSize, md5 } = await this.core.apis.FileApi.uploadFile(filePath, ElementType.VIDEO);
|
||||||
if (fileSize === 0) {
|
if (fileSize === 0) {
|
||||||
throw new Error('文件异常,大小为0');
|
throw new Error('文件异常,大小为0');
|
||||||
}
|
}
|
||||||
videoInfo.size = fileSize;
|
const thumbDir = 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}`);
|
const thumbPath = pathLib.join(pathLib.dirname(thumbDir), `${md5}_0.png`);
|
||||||
thumb = pathLib.dirname(thumb);
|
|
||||||
|
|
||||||
const thumbPath = new Map();
|
|
||||||
const _thumbPath = await new Promise<string | undefined>((resolve, reject) => {
|
|
||||||
const thumbFileName = `${md5}_0.png`;
|
|
||||||
const thumbPath = pathLib.join(thumb, thumbFileName);
|
|
||||||
ffmpeg(filePath)
|
|
||||||
.on('error', (err) => {
|
|
||||||
try {
|
try {
|
||||||
this.context.logger.logDebug('获取视频封面失败,使用默认封面', err);
|
await FFmpegService.getVideoInfo(filePath, thumbPath);
|
||||||
if (diyThumbPath) {
|
|
||||||
fsPromises.copyFile(diyThumbPath, thumbPath).then(() => {
|
|
||||||
resolve(thumbPath);
|
|
||||||
}).catch(reject);
|
|
||||||
} else {
|
|
||||||
fs.writeFileSync(thumbPath, Buffer.from(defaultVideoThumbB64, 'base64'));
|
|
||||||
resolve(thumbPath);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.context.logger.logError('获取视频封面失败,使用默认封面失败', error);
|
fs.writeFileSync(thumbPath, Buffer.from(defaultVideoThumbB64, 'base64'));
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.screenshots({
|
videoInfo.size = fileSize;
|
||||||
timestamps: [0],
|
const thumbSize = (await fsPromises.stat(thumbPath)).size;
|
||||||
filename: thumbFileName,
|
const thumbMd5 = await calculateFileMD5(thumbPath);
|
||||||
folder: thumb,
|
|
||||||
size: videoInfo.width + 'x' + videoInfo.height,
|
|
||||||
})
|
|
||||||
.on('end', () => {
|
|
||||||
resolve(thumbPath);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
const thumbSize = _thumbPath ? (await fsPromises.stat(_thumbPath)).size : 0;
|
|
||||||
thumbPath.set(0, _thumbPath);
|
|
||||||
const thumbMd5 = _thumbPath ? await calculateFileMD5(_thumbPath) : '';
|
|
||||||
context.deleteAfterSentFiles.push(path);
|
context.deleteAfterSentFiles.push(path);
|
||||||
const uploadName = (fileName || _fileName).toLocaleLowerCase().endsWith('.' + fileExt.toLocaleLowerCase()) ? (fileName || _fileName) : (fileName || _fileName) + '.' + fileExt;
|
|
||||||
|
const uploadName = (fileName || _fileName).toLocaleLowerCase().endsWith(`.${fileExt.toLocaleLowerCase()}`) ? (fileName || _fileName) : `${fileName || _fileName}.${fileExt}`;
|
||||||
return {
|
return {
|
||||||
elementType: ElementType.VIDEO,
|
elementType: ElementType.VIDEO,
|
||||||
elementId: '',
|
elementId: '',
|
||||||
@@ -218,15 +188,14 @@ export class NTQQFileApi {
|
|||||||
videoMd5: md5,
|
videoMd5: md5,
|
||||||
thumbMd5,
|
thumbMd5,
|
||||||
fileTime: videoInfo.time,
|
fileTime: videoInfo.time,
|
||||||
thumbPath: thumbPath,
|
thumbPath: new Map([[0, thumbPath]]),
|
||||||
thumbSize,
|
thumbSize,
|
||||||
thumbWidth: videoInfo.width,
|
thumbWidth: videoInfo.width,
|
||||||
thumbHeight: videoInfo.height,
|
thumbHeight: videoInfo.height,
|
||||||
fileSize: '' + fileSize,
|
fileSize: fileSize.toString(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async createValidSendPttElement(pttPath: string): Promise<SendPttElement> {
|
async createValidSendPttElement(pttPath: string): Promise<SendPttElement> {
|
||||||
|
|
||||||
const { converted, path: silkPath, duration } = await encodeSilk(pttPath, this.core.NapCatTempPath, this.core.context.logger);
|
const { converted, path: silkPath, duration } = await encodeSilk(pttPath, this.core.NapCatTempPath, this.core.context.logger);
|
||||||
|
@@ -2,7 +2,7 @@ import { GetFileBase, GetFilePayload, GetFileResponse } from './GetFile';
|
|||||||
import { ActionName } from '@/onebot/action/router';
|
import { ActionName } from '@/onebot/action/router';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import { decode } from 'silk-wasm';
|
import { decode } from 'silk-wasm';
|
||||||
import { ffmpegService } from '@/common/ffmpeg';
|
import { FFmpegService } from '@/common/ffmpeg';
|
||||||
|
|
||||||
const out_format = ['mp3' , 'amr' , 'wma' , 'm4a' , 'spx' , 'ogg' , 'wav' , 'flac'];
|
const out_format = ['mp3' , 'amr' , 'wma' , 'm4a' , 'spx' , 'ogg' , 'wav' , 'flac'];
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ export default class GetRecord extends GetFileBase {
|
|||||||
await fs.access(outputFile);
|
await fs.access(outputFile);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await this.decodeFile(inputFile, pcmFile);
|
await this.decodeFile(inputFile, pcmFile);
|
||||||
await ffmpegService.convertFile(pcmFile, outputFile, payload.out_format);
|
await FFmpegService.convertFile(pcmFile, outputFile, payload.out_format);
|
||||||
}
|
}
|
||||||
const base64Data = await fs.readFile(outputFile, { encoding: 'base64' });
|
const base64Data = await fs.readFile(outputFile, { encoding: 'base64' });
|
||||||
res.file = outputFile;
|
res.file = outputFile;
|
||||||
|
@@ -29,7 +29,7 @@ import { InitWebUi } from '@/webui';
|
|||||||
import { WebUiDataRuntime } from '@/webui/src/helper/Data';
|
import { WebUiDataRuntime } from '@/webui/src/helper/Data';
|
||||||
import { napCatVersion } from '@/common/version';
|
import { napCatVersion } from '@/common/version';
|
||||||
import { NodeIO3MiscListener } from '@/core/listeners/NodeIO3MiscListener';
|
import { NodeIO3MiscListener } from '@/core/listeners/NodeIO3MiscListener';
|
||||||
import { ffmpegService } from '@/common/ffmpeg';
|
import { FFmpegService } from '@/common/ffmpeg';
|
||||||
// NapCat Shell App ES 入口文件
|
// NapCat Shell App ES 入口文件
|
||||||
async function handleUncaughtExceptions(logger: LogWrapper) {
|
async function handleUncaughtExceptions(logger: LogWrapper) {
|
||||||
process.on('uncaughtException', (err) => {
|
process.on('uncaughtException', (err) => {
|
||||||
@@ -264,7 +264,8 @@ async function initializeSession(
|
|||||||
|
|
||||||
export async function NCoreInitShell() {
|
export async function NCoreInitShell() {
|
||||||
try {
|
try {
|
||||||
await ffmpegService.extractThumbnail("F:\\BVideo\\123.mp4","F:\\BVideo\\123.jpg");
|
let info = await FFmpegService.getVideoInfo("F:\\BVideo\\123.mp4","F:\\BVideo\\1.jpg");
|
||||||
|
console.log(info);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
@@ -4,7 +4,7 @@ import { resolve } from 'path';
|
|||||||
import nodeResolve from '@rollup/plugin-node-resolve';
|
import nodeResolve from '@rollup/plugin-node-resolve';
|
||||||
import { builtinModules } from 'module';
|
import { builtinModules } from 'module';
|
||||||
//依赖排除
|
//依赖排除
|
||||||
const external = ['silk-wasm', 'ws', 'express', 'qrcode-terminal', 'fluent-ffmpeg', 'piscina', '@ffmpeg.wasm/core-mt', "@ffmpeg.wasm/main"];
|
const external = ['silk-wasm', 'ws', 'express', 'qrcode-terminal', 'piscina', '@ffmpeg.wasm/core-mt', "@ffmpeg.wasm/main"];
|
||||||
const nodeModules = [...builtinModules, builtinModules.map((m) => `node:${m}`)].flat();
|
const nodeModules = [...builtinModules, builtinModules.map((m) => `node:${m}`)].flat();
|
||||||
|
|
||||||
let startScripts: string[] | undefined = undefined;
|
let startScripts: string[] | undefined = undefined;
|
||||||
@@ -79,7 +79,6 @@ const UniversalBaseConfig = () =>
|
|||||||
alias: {
|
alias: {
|
||||||
'@/core': resolve(__dirname, './src/core'),
|
'@/core': resolve(__dirname, './src/core'),
|
||||||
'@': resolve(__dirname, './src'),
|
'@': resolve(__dirname, './src'),
|
||||||
'./lib-cov/fluent-ffmpeg': './lib/fluent-ffmpeg',
|
|
||||||
'@webapi': resolve(__dirname, './src/webui/src'),
|
'@webapi': resolve(__dirname, './src/webui/src'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -109,7 +108,6 @@ const ShellBaseConfig = () =>
|
|||||||
alias: {
|
alias: {
|
||||||
'@/core': resolve(__dirname, './src/core'),
|
'@/core': resolve(__dirname, './src/core'),
|
||||||
'@': resolve(__dirname, './src'),
|
'@': resolve(__dirname, './src'),
|
||||||
'./lib-cov/fluent-ffmpeg': './lib/fluent-ffmpeg',
|
|
||||||
'@webapi': resolve(__dirname, './src/webui/src'),
|
'@webapi': resolve(__dirname, './src/webui/src'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -138,7 +136,6 @@ const FrameworkBaseConfig = () =>
|
|||||||
alias: {
|
alias: {
|
||||||
'@/core': resolve(__dirname, './src/core'),
|
'@/core': resolve(__dirname, './src/core'),
|
||||||
'@': resolve(__dirname, './src'),
|
'@': resolve(__dirname, './src'),
|
||||||
'./lib-cov/fluent-ffmpeg': './lib/fluent-ffmpeg',
|
|
||||||
'@webapi': resolve(__dirname, './src/webui/src'),
|
'@webapi': resolve(__dirname, './src/webui/src'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user