import { GetFileBase, GetFilePayload, GetFileResponse } from './GetFile'; import { ActionName } from '../types'; import { spawn } from 'node:child_process'; import { promises as fs } from 'fs'; import { decode } from 'silk-wasm'; const FFMPEG_PATH = process.env.FFMPEG_PATH || 'ffmpeg'; const out_format = ['mp3' , 'amr' , 'wma' , 'm4a' , 'spx' , 'ogg' , 'wav' , 'flac']; type Payload = { out_format : string } & GetFilePayload export default class GetRecord extends GetFileBase { actionName = ActionName.GetRecord; async _handle(payload: Payload): Promise { const res = await super._handle(payload); if (payload.out_format && typeof payload.out_format === 'string') { const inputFile = res.file; if (!inputFile) throw new Error('file not found'); if (!out_format.includes(payload.out_format)) { throw new Error('转换失败 out_format 字段可能格式不正确'); } const pcmFile = `${inputFile}.pcm`; const outputFile = `${inputFile}.${payload.out_format}`; try { await fs.access(inputFile); try { await fs.access(outputFile); } catch (error) { await this.decodeFile(inputFile, pcmFile); await this.convertFile(pcmFile, outputFile, payload.out_format); } const base64Data = await fs.readFile(outputFile, { encoding: 'base64' }); res.file = outputFile; res.url = outputFile; res.base64 = base64Data; } catch (error) { console.error('Error processing file:', error); throw error; // 重新抛出错误以便调用者可以处理 } } return res; } private async decodeFile(inputFile: string, outputFile: string): Promise { try { const inputData = await fs.readFile(inputFile); const decodedData = await decode(inputData, 24000); await fs.writeFile(outputFile, Buffer.from(decodedData.data)); } catch (error) { console.error('Error decoding file:', error); throw error; // 重新抛出错误以便调用者可以处理 } } private convertFile(inputFile: string, outputFile: string, format: string): Promise { return new Promise((resolve, reject) => { const params = format === 'amr' ? ['-f', 's16le', '-ar', '24000', '-ac', '1', '-i', inputFile, '-ar', '8000', '-b:a', '12.2k', outputFile] : ['-f', 's16le', '-ar', '24000', '-ac', '1', '-i', inputFile, outputFile]; const ffmpeg = spawn(FFMPEG_PATH, params); ffmpeg.on('close', (code) => { if (code === 0) { resolve(); } else { reject(new Error(`ffmpeg process exited with code ${code}`)); } }); ffmpeg.on('error', (error: Error) => { reject(error); }); }); } }