fix: 彻底完成迁移

This commit is contained in:
手瓜一十雪
2025-01-26 20:42:05 +08:00
parent 0afbfffd1d
commit 519a9cb34c
8 changed files with 101 additions and 169 deletions

View File

@@ -5,7 +5,7 @@ import { randomUUID } from 'crypto';
import { EncodeResult, getDuration, getWavFileInfo, isSilk, isWav } from 'silk-wasm';
import { LogWrapper } from '@/common/log';
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];
@@ -32,7 +32,7 @@ async function handleWavFile(
): Promise<{ input: Buffer; sampleRate: number }> {
const { fmt } = getWavFileInfo(file);
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 };
}
@@ -46,7 +46,7 @@ export async function encodeSilk(filePath: string, TEMP_DIR: string, logger: Log
const pcmPath = `${pttPath}.pcm`;
const { input, sampleRate } = isWav(file)
? (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 });
await fsPromise.writeFile(pttPath, Buffer.from(silk.data));
logger.log(`语音文件${filePath}转换成功!`, pttPath, '时长:', silk.duration);

View File

@@ -1,104 +1,131 @@
import { FFmpeg } from '@ffmpeg.wasm/main';
import { randomUUID } from 'crypto';
import { readFileSync, writeFileSync } from 'fs';
import { readFileSync, statSync, writeFileSync } from 'fs';
import { LogWrapper } from './log';
class FFmpegService {
private ffmpegRef: FFmpeg;
constructor(ffmpegRef: FFmpeg) {
this.ffmpegRef = ffmpegRef;
}
public async extractThumbnail(videoPath: string, thumbnailPath: string): Promise<void> {
import { VideoInfo } from './video';
import { fileTypeFromFile } from 'file-type';
import imageSize from 'image-size';
export class FFmpegService {
public static async extractThumbnail(videoPath: string, thumbnailPath: string): Promise<void> {
const ffmpegInstance = await FFmpeg.create({ core: '@ffmpeg.wasm/core-mt' });
const videoFileName = `${randomUUID()}.mp4`;
const outputFileName = `${randomUUID()}.jpg`;
try {
this.ffmpegRef.fs.writeFile(videoFileName, readFileSync(videoPath));
let code = await this.ffmpegRef.run('-i', videoFileName, '-ss', '00:00:01.000', '-vframes', '1', outputFileName);
if (code! === 0) {
ffmpegInstance.fs.writeFile(videoFileName, readFileSync(videoPath));
let code = await ffmpegInstance.run('-i', videoFileName, '-ss', '00:00:01.000', '-vframes', '1', outputFileName);
if (code !== 0) {
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);
} catch (error) {
console.error('Error extracting thumbnail:', error);
throw error;
} finally {
try {
this.ffmpegRef.fs.unlink(outputFileName);
ffmpegInstance.fs.unlink(outputFileName);
} catch (unlinkError) {
console.error('Error unlinking output file:', unlinkError);
}
try {
this.ffmpegRef.fs.unlink(videoFileName);
ffmpegInstance.fs.unlink(videoFileName);
} catch (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 outputFileName = `${randomUUID()}.${format}`;
try {
this.ffmpegRef.fs.writeFile(inputFileName, readFileSync(inputFile));
ffmpegInstance.fs.writeFile(inputFileName, readFileSync(inputFile));
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, outputFileName];
let code = await this.ffmpegRef.run(...params);
if (code! === 0) {
let code = await ffmpegInstance.run(...params);
if (code !== 0) {
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);
} catch (error) {
console.error('Error converting file:', error);
throw error;
} finally {
try {
this.ffmpegRef.fs.unlink(outputFileName);
ffmpegInstance.fs.unlink(outputFileName);
} catch (unlinkError) {
console.error('Error unlinking output file:', unlinkError);
}
try {
this.ffmpegRef.fs.unlink(inputFileName);
ffmpegInstance.fs.unlink(inputFileName);
} catch (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 outputFileName = `${randomUUID()}.pcm`;
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];
let code = await this.ffmpegRef.run(...params);
if (code! === 0) {
let code = await ffmpegInstance.run(...params);
if (code !== 0) {
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);
return Buffer.from(outputData);
return Buffer.from(outputData);
} catch (error: any) {
logger.log('FFmpeg处理转换出错: ', error.message);
throw error;
throw new Error('FFmpeg处理转换出错: ' + error.message);
} finally {
try {
this.ffmpegRef.fs.unlink(outputFileName);
ffmpegInstance.fs.unlink(outputFileName);
} catch (unlinkError) {
logger.log('Error unlinking output file:', unlinkError);
}
try {
this.ffmpegRef.fs.unlink(inputFileName);
ffmpegInstance.fs.unlink(inputFileName);
} catch (unlinkError) {
logger.log('Error unlinking input file:', unlinkError);
}
}
}
}
const ffmpegInstance = await FFmpeg.create({ core: '@ffmpeg.wasm/core-mt' });
export const ffmpegService = new FFmpegService(ffmpegInstance);
public static async getVideoInfo(videoPath: string, thumbnailPath: string): Promise<VideoInfo> {
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