| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133 |
- "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;
|