@webgal-tools/voice
Version:
WebGAL GPT-SoVITS语音合成应用
306 lines (305 loc) • 12.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.VoiceConfigManager = void 0;
const fs_1 = require("fs");
const path_1 = require("path");
const config_1 = require("@webgal-tools/config");
const request_js_1 = require("./request.js");
class VoiceConfigManager {
constructor(workDirectory) {
this.config = null;
const workDir = workDirectory;
this.configPath = path_1.default.join(workDir, 'voice.config.json');
}
/**
* 加载语音配置文件
*/
loadConfig() {
if (this.config) {
return this.config;
}
// 尝试从新的配置系统加载
const voiceConfig = (0, config_1.getVoiceConfig)();
if (voiceConfig) {
// 转换配置格式以兼容本地类型
const convertedConfig = {
volume: voiceConfig.volume,
gpt_sovits_url: voiceConfig.gpt_sovits_url,
gpt_sovits_path: voiceConfig.gpt_sovits_path,
model_version: voiceConfig.model_version,
translate: voiceConfig.translate,
characters: voiceConfig.characters
};
this.config = convertedConfig;
console.error(`✅ 从配置系统加载语音配置`);
console.error(`配置了 ${voiceConfig.characters.length} 个角色`);
return convertedConfig;
}
// 回退到文件系统加载
if (!fs_1.default.existsSync(path_1.default.resolve(this.configPath))) {
throw new Error(`语音配置文件不存在: ${this.configPath}\n请创建 voice.config.json 文件或参考示例配置`);
}
try {
const configContent = fs_1.default.readFileSync(this.configPath, 'utf-8');
const config = JSON.parse(configContent);
// 验证配置文件格式
this.validateConfig(config);
this.config = config;
console.error(`✅ 成功加载语音配置: ${this.configPath}`);
console.error(`配置了 ${config.characters.length} 个角色`);
return config;
}
catch (error) {
throw new Error(`解析语音配置文件失败: ${error instanceof Error ? error.message : error}`);
}
}
/**
* 验证配置文件格式
*/
validateConfig(config) {
if (!config.volume || typeof config.volume !== 'number') {
throw new Error('配置文件缺少有效的 volume 设置');
}
if (!config.gpt_sovits_url || typeof config.gpt_sovits_url !== 'string') {
throw new Error('配置文件缺少有效的 gpt_sovits_url 设置');
}
if (!config.gpt_sovits_path || typeof config.gpt_sovits_path !== 'string') {
throw new Error('配置文件缺少有效的 gpt_sovits_path 设置');
}
if (!fs_1.default.existsSync(config.gpt_sovits_path)) {
throw new Error(`GPT-SoVITS项目路径不存在: ${config.gpt_sovits_path}`);
}
if (!config.model_version || typeof config.model_version !== 'string') {
throw new Error('配置文件缺少有效的 model_version 设置');
}
if (!config.translate) {
throw new Error('配置文件缺少 translate 配置');
}
this.validateTranslateConfig(config.translate);
if (!config.characters || !Array.isArray(config.characters)) {
throw new Error('配置文件缺少 characters 数组');
}
for (const [index, character] of config.characters.entries()) {
const prefix = `角色配置 [${index}]`;
if (!character.character_name) {
throw new Error(`${prefix} 缺少 character_name`);
}
// 验证auto模式配置
const isAutoMode = character.auto === true;
if (!character.gpt) {
throw new Error(`${prefix} 缺少 gpt 模型路径`);
}
if (isAutoMode) {
// 自动模式:验证文件夹路径
const gptDir = path_1.default.resolve(config.gpt_sovits_path, character.gpt);
if (!fs_1.default.existsSync(gptDir) || !fs_1.default.statSync(gptDir).isDirectory()) {
throw new Error(`${prefix} GPT模型文件夹不存在或不是目录: ${gptDir}`);
}
}
else {
// 非自动模式:验证文件路径
const gptPath = path_1.default.resolve(config.gpt_sovits_path, character.gpt);
if (!fs_1.default.existsSync(gptPath)) {
throw new Error(`${prefix} GPT模型文件不存在: ${gptPath}`);
}
}
if (!character.sovits) {
throw new Error(`${prefix} 缺少 sovits 模型路径`);
}
if (isAutoMode) {
// 自动模式:验证文件夹路径
const sovitsDir = path_1.default.resolve(config.gpt_sovits_path, character.sovits);
if (!fs_1.default.existsSync(sovitsDir) || !fs_1.default.statSync(sovitsDir).isDirectory()) {
throw new Error(`${prefix} SoVITS模型文件夹不存在或不是目录: ${sovitsDir}`);
}
}
else {
// 非自动模式:验证文件路径
const sovitsPath = path_1.default.resolve(config.gpt_sovits_path, character.sovits);
if (!fs_1.default.existsSync(sovitsPath)) {
throw new Error(`${prefix} SoVITS模型文件不存在: ${sovitsPath}`);
}
}
if (!character.ref_audio) {
throw new Error(`${prefix} 缺少参考音频路径`);
}
if (isAutoMode) {
// 自动模式:验证文件夹路径
if (!fs_1.default.existsSync(character.ref_audio) || !fs_1.default.statSync(character.ref_audio).isDirectory()) {
throw new Error(`${prefix} 参考音频文件夹不存在或不是目录: ${character.ref_audio}`);
}
}
else {
// 非自动模式:验证文件路径
if (!fs_1.default.existsSync(character.ref_audio)) {
throw new Error(`${prefix} 参考音频文件不存在: ${character.ref_audio}`);
}
}
if (!character.ref_text) {
throw new Error(`${prefix} 缺少参考文本 ref_text`);
}
// 验证推理配置
if (character.inferrence_config) {
this.validateInferenceConfig(character.inferrence_config, prefix);
}
}
}
/**
* 验证翻译配置
*/
validateTranslateConfig(config) {
if (typeof config.check !== 'boolean') {
throw new Error('配置文件的 translate.check 必须是布尔值');
}
// 如果禁用翻译,跳过其他验证
if (!config.check) {
return;
}
// 处理向后兼容性
if (config.ollama_endpoint && !config.base_url) {
config.base_url = config.ollama_endpoint;
config.model_type = 'ollama';
console.warn('⚠️ 检测到旧版配置格式,已自动转换为新格式。建议更新配置文件。');
}
if (!config.model_type) {
throw new Error('配置文件缺少 translate.model_type 设置');
}
const validModelTypes = ['ollama', 'openai', 'anthropic', 'google', 'mistral', 'cohere', 'custom'];
if (!validModelTypes.includes(config.model_type)) {
throw new Error(`无效的 model_type: ${config.model_type}。支持的类型: ${validModelTypes.join(', ')}`);
}
if (!config.base_url || typeof config.base_url !== 'string') {
throw new Error('配置文件缺少有效的 translate.base_url 设置');
}
if (!config.model_name || typeof config.model_name !== 'string') {
throw new Error('配置文件缺少有效的 translate.model_name 设置');
}
// 验证API密钥
if (this.requiresApiKey(config)) {
if (!config.api_key || typeof config.api_key !== 'string') {
throw new Error(`${config.model_type} 模型供应商需要提供 api_key`);
}
}
}
/**
* 检查是否需要API密钥
*/
requiresApiKey(config) {
// Ollama 本地服务不需要API密钥
if (config.model_type === 'ollama') {
const url = new URL(config.base_url);
return !(url.hostname === 'localhost' || url.hostname === '127.0.0.1' || url.hostname === '::1');
}
// 其他供应商都需要API密钥
return config.model_type !== 'custom';
}
/**
* 验证推理配置
*/
validateInferenceConfig(config, prefix) {
if (config.prompt_language && !(config.prompt_language in request_js_1.LANGUAGE_OPTIONS)) {
throw new Error(`${prefix} 无效的 prompt_language: ${config.prompt_language}`);
}
if (config.text_language && !(config.text_language in request_js_1.LANGUAGE_OPTIONS)) {
throw new Error(`${prefix} 无效的 text_language: ${config.text_language}`);
}
if (config.top_k !== undefined && (config.top_k < 1 || config.top_k > 100)) {
throw new Error(`${prefix} top_k 必须在 1-100 之间`);
}
if (config.top_p !== undefined && (config.top_p < 0 || config.top_p > 1)) {
throw new Error(`${prefix} top_p 必须在 0-1 之间`);
}
if (config.temperature !== undefined && (config.temperature < 0 || config.temperature > 1)) {
throw new Error(`${prefix} temperature 必须在 0-1 之间`);
}
if (config.speed !== undefined && (config.speed < 0.6 || config.speed > 1.65)) {
throw new Error(`${prefix} speed 必须在 0.6-1.65 之间`);
}
}
/**
* 获取角色配置
*/
getCharacterConfig(characterName) {
const config = this.loadConfig();
return config.characters.find(char => char.character_name === characterName) || null;
}
/**
* 获取所有角色名称
*/
getAllCharacterNames() {
const config = this.loadConfig();
return config.characters.map(char => char.character_name);
}
/**
* 获取默认音量设置
*/
getDefaultVolume() {
const config = this.loadConfig();
return config.volume;
}
/**
* 检查角色是否已配置
*/
hasCharacterConfig(characterName) {
return this.getCharacterConfig(characterName) !== null;
}
/**
* 获取配置文件路径
*/
getConfigPath() {
return this.configPath;
}
/**
* 获取GPT-SoVITS服务URL
*/
getGptSovitsUrl() {
const config = this.loadConfig();
return config.gpt_sovits_url;
}
/**
* 获取GPT-SoVITS项目路径
*/
getGptSovitsPath() {
const config = this.loadConfig();
return config.gpt_sovits_path;
}
/**
* 获取模型版本
*/
getModelVersion() {
const config = this.loadConfig();
return config.model_version;
}
/**
* 获取翻译配置
*/
getTranslateConfig() {
const config = this.loadConfig();
if (!config.translate) {
throw new Error('翻译配置未设置');
}
return config.translate;
}
/**
* 检查是否启用翻译
*/
isTranslateEnabled() {
const config = this.loadConfig();
return config.translate?.check ?? false;
}
/**
* 获取角色的翻译目标语言
*/
getCharacterTranslateTarget(characterName) {
const characterConfig = this.getCharacterConfig(characterName);
return characterConfig?.translate_to || null;
}
/**
* 重新加载配置
*/
reloadConfig() {
this.config = null;
return this.loadConfig();
}
}
exports.VoiceConfigManager = VoiceConfigManager;