# GPT-SoVITS Node.js SDK

基于fetch API实现的GPT-SoVITS API客户端，提供了简单易用的接口，用于调用GPT-SoVITS的文本转语音服务。

## 功能特点

- 完整的TypeScript类型支持
- 支持所有GPT-SoVITS API功能
- 简洁的Promise-based API
- 内置错误处理和重试机制
- 支持直接保存音频文件
- 详细的调试日志
- 支持统一的API响应格式
- 支持驼峰命名法和下划线命名法的参数，增强与API的兼容性

## 安装

使用npm安装:

```bash
npm install gpt-sovits-sdk
```

或使用yarn:

```bash
yarn add gpt-sovits-sdk
```

## 快速开始

### 基本用法

```typescript
import { createClient, TextLanguage, MediaType } from 'gpt-sovits-sdk';

// 创建客户端实例
const client = createClient({
  baseUrl: 'http://127.0.0.1:9880', // 默认API地址
  timeout: 30000 // 超时时间（毫秒）
});

// 文本转语音
async function textToSpeech() {
  try {
    const result = await client.textToSpeech({
      text: '你好，这是一个测试文本。',
      textLang: TextLanguage.CHINESE,
      refAudioPath: 'emotions/卡齐娜/中文/【中立_neutral】那个人是希巴拉克大人？可是，他看起来好年轻啊。.wav',
      promptLang: TextLanguage.CHINESE,
      mediaType: MediaType.WAV
    });
    
    console.log('TTS结果:', result);
    // 输出: { audio_path: '1740768286_faafc45f-2eae-422f-b7da-9b2e4ccdbea4.MediaType.WAV', duration: 10.24, media_type: 'wav' }
    
    // 下载生成的音频文件
    const audioData = await client.getAudio(result.audio_path, './output.wav');
    console.log('音频已保存到:', audioData);
  } catch (error) {
    console.error('TTS失败:', error);
  }
}

// 直接获取音频数据
async function directTTS() {
  try {
    // 直接保存到文件
    const outputPath = await client.textToSpeechDirect({
      text: '这是直接返回音频数据的测试。',
      textLang: TextLanguage.CHINESE,
      refAudioPath: 'emotions/卡齐娜/中文/【中立_neutral】那个人是希巴拉克大人？可是，他看起来好年轻啊。.wav',
      promptLang: TextLanguage.CHINESE,
      mediaType: MediaType.WAV
    }, './direct_output.wav');
    
    console.log('音频已保存到:', outputPath);
  } catch (error) {
    console.error('直接TTS失败:', error);
  }
}
```

### 获取可用资源

```typescript
// 获取可用模型
async function getModels() {
  const modelsResponse = await client.getModels();
  console.log('响应状态:', modelsResponse.code, modelsResponse.message);
  
  if (modelsResponse.data) {
    console.log('GPT模型:', modelsResponse.data.gpt_models);
    console.log('SoVITS模型:', modelsResponse.data.sovits_models);
  }
}

// 获取参考音频
async function getReferenceAudios() {
  const audiosResponse = await client.getReferenceAudios();
  console.log('响应状态:', audiosResponse.code, audiosResponse.message);
  
  if (audiosResponse.data) {
    console.log('参考音频:', audiosResponse.data.audios);
  }
}

// 获取情感音频
async function getEmotionAudios() {
  const emotionResponse = await client.getEmotionAudios({
    character: '卡齐娜',
    language: '中文'
  });
  console.log('响应状态:', emotionResponse.code, emotionResponse.message);
  
  if (emotionResponse.data) {
    console.log('情感音频:', emotionResponse.data.referenceAudios);
  }
}
```

### 设置模型

```typescript
// 设置GPT模型
async function setGptModel() {
  await client.setGptModel('GPT_weights_v2/纳塔-e10.ckpt');
}

// 设置SoVITS模型
async function setSovitsModel() {
  await client.setSovitsModel('SoVITS_weights_v2/纳塔-e10.pth');
}
```

## API响应格式

GPT-SoVITS API使用统一的响应格式，所有API接口（除了直接返回二进制数据的接口外）都使用以下格式：

```typescript
{
  "code": 200,  // HTTP状态码
  "message": "操作成功",  // 操作结果描述
  "data": {  // 实际返回的数据
    // 具体数据内容，根据接口不同而变化
  }
}
```

错误响应格式：

```typescript
{
  "code": 400,  // HTTP错误码
  "error": "ERROR_TYPE",  // 错误类型
  "message": "错误描述信息",  // 错误描述
  "detail": null  // 详细错误信息（可选）
}
```

SDK中的`APIResponse<T>`泛型接口用于处理这种统一响应格式：

```typescript
interface APIResponse<T = any> {
  code?: number;      // 状态码
  message?: string;   // 状态消息
  data?: T;           // 响应数据
  success?: boolean;  // 是否成功
}
```

## API参考

### 创建客户端

```typescript
// 使用工厂函数
const client = createClient(options);

// 或直接实例化
const client = new GPTSoVITSClient(options);
```

#### 选项

- `baseUrl`: API基础URL，默认为`http://127.0.0.1:9880`
- `timeout`: 请求超时时间（毫秒），默认为30000
- `debug`: 是否在控制台输出调试信息，默认为false
- `headers`: 自定义请求头
- `retries`: 请求失败时的重试次数，默认为0

### 主要方法

#### 文本转语音

```typescript
// 返回音频文件路径
client.textToSpeech(options: TTSRequestOptions): Promise<TTSResponse>

// 直接返回音频数据
client.textToSpeechDirect(options: TTSRequestOptions, outputPath?: string): Promise<ArrayBuffer | string>
```

> **注意**: OGG格式在非流式模式下不受支持。如果需要使用OGG格式，请使用流式API。

##### 参数命名兼容性

SDK支持两种参数命名风格：

1. **驼峰命名法**（推荐）：例如 `textLang`, `refAudioPath`, `promptLang`
2. **下划线命名法**（兼容API原始格式）：例如 `text_lang`, `ref_audio_path`, `prompt_lang`

您可以根据个人偏好选择使用任一种命名风格，SDK会自动处理参数转换：

```typescript
// 使用驼峰命名法
await client.textToSpeech({
  text: "这是一个测试",
  textLang: "zh",
  refAudioPath: "/path/to/audio.wav",
  promptLang: "zh"
});

// 使用下划线命名法
await client.textToSpeech({
  text: "这是一个测试",
  text_lang: "zh",
  ref_audio_path: "/path/to/audio.wav",
  prompt_lang: "zh"
});

// 混合使用（不推荐，但支持）
await client.textToSpeech({
  text: "这是一个测试",
  textLang: "zh",
  ref_audio_path: "/path/to/audio.wav",
  promptLang: "zh"
});
```

#### 获取资源

```typescript
// 获取API根信息
client.getRoot(): Promise<RootResponse>

// 获取API健康状态
client.getHealth(): Promise<HealthResponse>

// 获取可用模型列表
client.getModels(): Promise<APIResponse<ModelsResponse>>

// 获取参考音频列表
client.getReferenceAudios(subdir?: string): Promise<APIResponse<ReferenceAudiosResponse>>

// 获取情感音频列表
client.getEmotionAudios(options?: EmotionAudiosQueryOptions): Promise<APIResponse<EmotionAudiosResponse>>

// 获取生成的音频文件
client.getAudio(audioName: string, outputPath?: string): Promise<ArrayBuffer | string>
```

#### 设置模型

```typescript
// 设置GPT模型
client.setGptModel(modelPath: string): Promise<{ status: string }>

// 设置SoVITS模型
client.setSovitsModel(modelPath: string): Promise<{ status: string }>
```

#### 取消请求

```typescript
// 取消所有正在进行的请求
client.abort(): void
```

## 高级用法

### 错误处理

SDK使用`GPTSoVITSError`类表示错误，包含以下属性：

- `message`: 错误消息
- `code`: HTTP状态码
- `cause`: 原始错误
- `url`: 请求URL
- `method`: 请求方法

```typescript
try {
  await client.textToSpeech(/* ... */);
} catch (error) {
  if (error instanceof GPTSoVITSError) {
    console.error(`错误 (${error.code}): ${error.message}`);
    console.error(`请求: ${error.method} ${error.url}`);
  } else {
    console.error('未知错误:', error);
  }
}
```

### 批量处理

```typescript
async function batchProcess(texts: string[]) {
  // 获取可用模型
  const modelsResponse = await client.getModels();
  if (modelsResponse.data && modelsResponse.data.gpt_models.length > 0 && modelsResponse.data.sovits_models.length > 0) {
    // 设置模型
    const gptModel = modelsResponse.data.gpt_models[0].path;
    const sovitsModel = modelsResponse.data.sovits_models[0].path;
    
    await client.setGptModel(gptModel);
    await client.setSovitsModel(sovitsModel);
    
    console.log(`使用GPT模型: ${gptModel}`);
    console.log(`使用SoVITS模型: ${sovitsModel}`);
  } else {
    console.error('没有找到可用的模型');
    return;
  }
  
  // 批量处理
  for (const text of texts) {
    try {
      await client.textToSpeechDirect({
        text,
        textLang: TextLanguage.CHINESE,
        refAudioPath: 'emotions/卡齐娜/中文/【中立_neutral】那个人是希巴拉克大人？可是，他看起来好年轻啊。.wav',
        promptLang: TextLanguage.CHINESE,
        mediaType: MediaType.WAV
      }, `./output_${Date.now()}.wav`);
    } catch (error) {
      console.error(`处理文本 "${text}" 失败:`, error);
    }
  }
}
```

## 示例

查看`src/examples`目录中的示例：

- `basic.ts`: 基本用法示例
- `model_pairs.ts`: 模型配对和批量处理示例

## 许可证

MIT 

### 参数命名兼容性

SDK支持两种参数命名风格：

1. **驼峰命名法**（推荐）：例如 `textLang`, `refAudioPath`, `promptLang`
2. **下划线命名法**（兼容API原始格式）：例如 `text_lang`, `ref_audio_path`, `prompt_lang`

您可以根据个人偏好选择使用任一种命名风格，SDK会自动处理参数转换：

```typescript
// 使用驼峰命名法
await client.textToSpeech({
  text: "这是一个测试",
  textLang: "zh",
  refAudioPath: "/path/to/audio.wav",
  promptLang: "zh"
});

// 使用下划线命名法
await client.textToSpeech({
  text: "这是一个测试",
  text_lang: "zh",
  ref_audio_path: "/path/to/audio.wav",
  prompt_lang: "zh"
});

// 混合使用（不推荐，但支持）
await client.textToSpeech({
  text: "这是一个测试",
  textLang: "zh",
  ref_audio_path: "/path/to/audio.wav",
  promptLang: "zh"
});
```

#### 获取资源

```typescript
// ... existing code ...
``` 