Minimax MCP Tools
by PsychArch
Verified
import fetch from 'node-fetch';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
/**
* Generate speech from text using Minimax API and save it to a file
* @param {string} text - The text to convert to speech
* @param {string} apiKey - The Minimax API key
* @param {string} groupId - The Minimax group ID
* @param {Object} options - Additional options for speech generation
* @param {string} outputDir - Directory to save the generated audio
* @param {string} outputFile - Absolute path to save the generated audio file
* @returns {Promise<Object>} - Object containing the audio information
*/
export async function generateSpeech(text, apiKey, groupId, options = {}, outputDir = 'generated-audio', outputFile = null) {
if (!apiKey) {
throw new Error('Minimax API key is required');
}
if (!groupId) {
throw new Error('Minimax group ID is required');
}
const url = `https://api.minimax.chat/v1/t2a_v2?GroupId=${groupId}`;
// Default voice settings
const voiceSettings = {
voice_id: options.voiceId || "male-qn-qingse",
speed: options.speed || 1.0,
vol: options.volume || 1.0,
pitch: options.pitch || 0,
emotion: options.emotion || "neutral"
};
// Add latex_read if provided
if (options.latexRead !== undefined) {
voiceSettings.latex_read = options.latexRead;
}
// Default audio settings
const audioSettings = {
sample_rate: options.sampleRate || 32000,
bitrate: options.bitrate || 128000,
format: options.format || "mp3",
channel: options.channel || 1
};
// Build the request payload
const payload = {
model: options.model || "speech-01-turbo",
text: text,
stream: options.stream || false,
voice_setting: voiceSettings,
audio_setting: audioSettings
};
// Add pronunciation dictionary if provided
if (options.pronunciationDict && options.pronunciationDict.length > 0) {
payload.pronunciation_dict = {
tone: options.pronunciationDict
};
}
// Add timber weights if provided
if (options.timberWeights && options.timberWeights.length > 0) {
payload.timber_weights = options.timberWeights;
// Remove voice_id from voice_setting when using timber_weights
delete payload.voice_setting.voice_id;
}
// Add language boost if provided
if (options.languageBoost) {
payload.language_boost = options.languageBoost;
}
// Add subtitle enable if provided
if (options.subtitleEnable !== undefined) {
payload.subtitle_enable = options.subtitleEnable;
}
const headers = {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
};
try {
console.error(`Generating speech for text: ${text.substring(0, 50)}${text.length > 50 ? '...' : ''}`);
const response = await fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify(payload)
});
const data = await response.json();
if (data.base_resp && data.base_resp.status_code !== 0) {
throw new Error(`Minimax API error: ${data.base_resp.status_msg}`);
}
// Create output directory if it doesn't exist
const absoluteOutputDir = outputFile ? path.dirname(outputFile) : path.resolve(__dirname, outputDir);
if (!fs.existsSync(absoluteOutputDir)) {
fs.mkdirSync(absoluteOutputDir, { recursive: true });
}
// Save the audio file
if (data.data && data.data.audio) {
const timestamp = Date.now();
const format = audioSettings.format;
// Use outputFile if provided, otherwise generate a filename
let filename;
let filePath;
if (outputFile) {
filePath = outputFile;
filename = path.basename(outputFile);
} else {
filename = `speech_${timestamp}.${format}`;
filePath = path.join(absoluteOutputDir, filename);
}
// Convert hex string to buffer and save
const buffer = Buffer.from(data.data.audio, 'hex');
fs.writeFileSync(filePath, buffer);
// Save subtitle file if available
let subtitlePath = null;
if (data.subtitle_file) {
const subtitleFilename = `subtitle_${timestamp}.json`;
subtitlePath = path.join(absoluteOutputDir, subtitleFilename);
// Download subtitle file
try {
const subtitleResponse = await fetch(data.subtitle_file);
const subtitleData = await subtitleResponse.text();
fs.writeFileSync(subtitlePath, subtitleData);
} catch (error) {
console.error('Error downloading subtitle file:', error);
}
}
return {
success: true,
traceId: data.trace_id,
audioInfo: {
localPath: filePath,
filename: filename,
format: format,
length: data.extra_info?.audio_length || 0, // in milliseconds
size: data.extra_info?.audio_size || 0, // in bytes
subtitlePath: subtitlePath
},
extraInfo: data.extra_info,
rawResponse: data
};
} else {
throw new Error('No audio data returned from the API');
}
} catch (error) {
console.error('Error generating speech:', error);
return {
success: false,
error: error.message
};
}
}