"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AliyunTtsProvider = void 0; const axios_1 = __importDefault(require("axios")); const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const config_1 = require("../../config"); // 阿里云百炼 Qwen TTS Provider // 文档: https://help.aliyun.com/zh/model-studio/qwen-tts class AliyunTtsProvider { apiKey; model; voice; baseUrl = 'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation'; constructor() { this.apiKey = config_1.config.dashscope.apiKey; this.model = config_1.config.dashscope.model; this.voice = config_1.config.dashscope.voice; } // 语音合成(带重试机制) async synthesize(text, voiceId, params, outputPath, retries = 3, modelOverride) { const activeModel = modelOverride || this.model; let lastError = null; for (let attempt = 1; attempt <= retries; attempt++) { try { // 构建请求 const requestBody = { model: activeModel, input: { text: text, voice: voiceId || this.voice, language_type: 'Chinese', }, }; console.log(`📤 [Aliyun TTS] 尝试 ${attempt}/${retries}`); console.log(` model: ${activeModel}, voice: ${voiceId || this.voice}`); console.log(` text length: ${text.length}`); console.log(` params: ${JSON.stringify(params)}`); // 使用 instruct 模型时支持指令控制 if (activeModel.includes('instruct')) { const instructions = []; if (params.speed !== 1) { const speedDesc = params.speed > 1 ? '较快' : '较慢'; instructions.push(`语速${speedDesc}`); } if (params.pitch !== 0) { const pitchDesc = params.pitch > 0 ? '较高' : '较低'; instructions.push(`音调${pitchDesc}`); } if (instructions.length > 0) { requestBody.parameters = { instructions: instructions.join(','), }; } } // 发送请求 const response = await axios_1.default.post(this.baseUrl, requestBody, { headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', }, timeout: 60000, }); // 检查响应 if (response.status !== 200) { throw new Error(`Qwen TTS 请求失败: ${response.status}`); } const data = response.data; if (data.code) { throw new Error(`Qwen TTS 错误: ${data.message || JSON.stringify(data)}`); } // 获取音频 URL const audioUrl = data.output?.audio?.url; console.log('🔗 音频 URL:', audioUrl); if (!audioUrl) { throw new Error('Qwen TTS 未返回音频 URL'); } // 尝试下载音频文件,如果失败则返回 URL try { const audioResponse = await axios_1.default.get(audioUrl, { responseType: 'arraybuffer', timeout: 60000, }); // 保存文件 const dir = path_1.default.dirname(outputPath); if (!fs_1.default.existsSync(dir)) { fs_1.default.mkdirSync(dir, { recursive: true }); } // 确保输出路径以 .wav 结尾 const finalPath = outputPath.endsWith('.wav') ? outputPath : outputPath.replace(/\.[^.]+$/, '.wav'); fs_1.default.writeFileSync(finalPath, audioResponse.data); console.log(`✅ Qwen TTS 生成成功: ${finalPath}`); return finalPath; } catch (downloadError) { console.warn('⚠️ 音频下载失败,返回云端 URL:', downloadError.message); // 返回一个特殊的路径标记,表示使用云端 URL return `cloud:${audioUrl}`; } } catch (error) { const errorDetails = error.response?.data || error.message; const isRateLimit = error.response?.status === 429 || errorDetails?.code === 'Throttling.RateQuota'; const isServerError = error.response?.status >= 500; console.error(`❌ Qwen TTS 调用失败 (尝试 ${attempt}/${retries}):`, JSON.stringify(errorDetails, null, 2)); if (isRateLimit && attempt < retries) { // 速率限制:等待后重试(指数退避) const waitTime = Math.pow(2, attempt) * 1000; console.warn(`⏳ 速率限制,等待 ${waitTime}ms 后重试...`); await new Promise(resolve => setTimeout(resolve, waitTime)); lastError = new Error(`Qwen TTS 速率限制: ${error.message}`); continue; } if (isServerError && attempt < retries) { // 服务器错误:等待后重试 const waitTime = Math.pow(2, attempt) * 1000; console.warn(`⏳ 服务器错误,等待 ${waitTime}ms 后重试...`); await new Promise(resolve => setTimeout(resolve, waitTime)); lastError = new Error(`Qwen TTS 服务器错误: ${error.message}`); continue; } // 达到最大重试次数或不可重试的错误 throw new Error(`Qwen TTS 服务调用失败: ${error.message}, 详情: ${JSON.stringify(errorDetails)}`); } } // 理论上不会到达这里,但为了类型安全 throw lastError || new Error('Qwen TTS 服务调用失败'); } } exports.AliyunTtsProvider = AliyunTtsProvider;