# FishAudio Coze 私有插件导入包

本目录用于不安装官方商店插件、希望在自己 Coze 空间创建私有插件的客户。

## 文件

- `fishaudio-tts.openapi.yaml`：`FishAudio TTS 文字转语音` 插件，一个 `generate_tts` 工具。
- `fishaudio-media-sync.openapi.yaml`：`FishAudio 图片/视频对口型` 插件，包含 `create_media_sync` 和 `get_media_sync_job`。
- `fishaudio-video-dubbing.openapi.yaml`：`FishAudio 文字对口型` 插件，包含 `create_text_lip_sync` 和 `get_text_lip_sync_job`。
- `fishaudio-personal-voice.openapi.yaml`：`FishAudio 专属语音合成`私有插件，包含 `create_personal_voice` 和 `get_personal_voice_job` 两个工具。
- `*.ai-plugin.json`：在扣子 `</>` 代码创建页面左侧 `ai_plugin` 输入框中使用的插件元信息；每个 OpenAPI YAML 都有一份同名前缀的配套 JSON。
- `fishaudio-tts-icon.png`：文字转语音插件头像，FishAudio Logo 加 `TTS` 角标。
- `fishaudio-media-sync-icon.png`：图片/视频对口型插件头像，FishAudio Logo 加 `LS` 角标。
- `fishaudio-video-dubbing-icon.png`：文字对口型插件头像，FishAudio Logo 加 `VD` 角标。
- `fishaudio-personal-voice-icon.png`：专属语音合成插件头像，FishAudio Logo 加 `PVS` 角标。
- `manifest.json`：FishAudio 交付包版本、端点和文件清单。**它不是扣子的 `ai_plugin` 文件，请勿粘贴到 `ai_plugin` 输入框。**

当前交付包包含两款商店公开插件和两款私有导入插件：`FishAudio TTS 文字转语音`和`FishAudio 图片/视频对口型`可从商店安装；`FishAudio 文字对口型`和`FishAudio 专属语音合成`仅供客户导入自己的 Coze 空间。

## 推荐：使用“导入”功能

1. 进入「资源库」并新建插件。
2. 类型选择「云端插件」。
3. 创建方式选择「云侧插件 - 基于已有服务创建」。
4. 点击页面右上角「导入」，上传或粘贴对应的 `*.openapi.yaml`。此入口只需要 OpenAPI YAML，不需要填写 `ai_plugin`。
5. 插件 URL 应为 `https://fishaudio.org`。
6. 授权方式选择「不需要授权」。`Authorization` 由每位客户在工具运行时填写。
7. 启用导入的工具，使用客户自己的 Fish Audio API Key 完成试运行。
8. 发布到客户自己的空间；私有使用不需要等待插件商店审核。

导入后工具列表显示“调试状态：失败”不一定表示导入失败；它通常表示该工具尚未用有效参数成功试运行。进入每个工具，填入有效参数完成一次试运行后再判断具体错误。

## 如果页面显示 `ai_plugin` 和 `openapi` 两个编辑框

这表示进入了 `</>` 代码创建页面，两个输入框都要填写，`ai_plugin` **不能留空**：

1. 左侧 `ai_plugin（填写 json）`：粘贴与插件同名前缀的 `*.ai-plugin.json`。
2. 右侧 `openapi（填写 yaml）`：粘贴配套的 `*.openapi.yaml`。
3. 不要把 `manifest.json` 粘贴到左侧；它只是 FishAudio 下载包清单。

| 插件                      | 左侧 `ai_plugin`                          | 右侧 `openapi`                          |
| ------------------------- | ----------------------------------------- | --------------------------------------- |
| FishAudio TTS 文字转语音  | `fishaudio-tts.ai-plugin.json`            | `fishaudio-tts.openapi.yaml`            |
| FishAudio 图片/视频对口型 | `fishaudio-media-sync.ai-plugin.json`     | `fishaudio-media-sync.openapi.yaml`     |
| FishAudio 文字对口型      | `fishaudio-video-dubbing.ai-plugin.json`  | `fishaudio-video-dubbing.openapi.yaml`  |
| FishAudio 专属语音合成    | `fishaudio-personal-voice.ai-plugin.json` | `fishaudio-personal-voice.openapi.yaml` |

配套 JSON 中的 `auth.type` 为 `none` 是预期配置。客户自己的 Fish Audio API Key 仍在工具运行时通过 `Authorization: Bearer ...` 填写，不应写入 JSON 或 YAML。

## Authorization

每个工具都要求：

```text
Bearer YOUR_FISH_AUDIO_API_KEY
```

`Bearer` 后面必须有一个空格。不要把真实 Key 写入 YAML、截图或对外分享的工作流模板。建议为 Coze 单独创建 Key，并按需撤销或轮换。

## TTS 注意事项

TTS 文件调用：

```text
POST https://fishaudio.org/api/open/v1/speech/tts
```

请求中的 `cache` 必须保持为 `true`。这样接口返回包含 `audio_url` 的 JSON；如果改为 `false`，接口会返回音频二进制，Coze 可能报响应解析错误。

`audio_url` 是临时链接，目前保留三天。请及时下载或转存。

## 音视频同步注意事项

创建工具返回 `data.id`。必须保存该值，并把它作为查询工具的 `jobId`：

```text
create_media_sync → data.id → 等待 15～30 秒 → get_media_sync_job
```

- `processing`：继续查询同一个 `jobId`。
- `completed`：读取 `data.result_url`。
- `failed`：读取 `data.error_message`。

任务仍在处理或查询超时时不要重新调用 `create_media_sync`，否则可能重复创建任务并重复扣费。

视频和音频 URL 必须允许 Fish Audio 服务端直接下载。网页地址、本地路径和过期的签名 URL 不能作为媒体输入。

视频模式默认按视频、音频中较短的时长向上取整计费。创建响应会返回 `billing_duration_seconds` 和 `credits_used`；任务成功后价格不再变化，失败则全额退款。只有明确需要把视频延长到音频长度时才设置 `video_extension: true`。

## 文字对口型注意事项

该插件调用网站工作台“视频配音”使用的二合一接口：服务端先用指定音色把文字生成音频，再自动创建口型同步任务：

```text
create_text_lip_sync → data.id → 等待 15～30 秒 → get_text_lip_sync_job
```

- 输入 `video_url`、目标 `text` 和已有音色 `reference_id`，无需预先生成或提供 `audio_url`。
- 每次新任务填写新的 `Idempotency-Key`；只有重试完全相同的请求时才复用原值。
- 创建响应中的 `data.audio_url` 是中间 TTS 音频；最终视频读取查询响应中的 `data.result_url`。
- `credits_used` 分别返回 `tts`、`lip_sync` 和 `total`。
- 查询同一个 `jobId` 不会重新生成语音或重复扣费；任务处理中不得再次创建。

## FishAudio 专属语音合成注意事项

该插件把“创建持久私有音色”和“使用新音色合成文本”组合为一次创建操作，再通过查询工具取得音频：

```text
create_personal_voice → data.id → 等待 → get_personal_voice_job
```

- 输入 `source_audio_url` 和目标 `text`，不需要参考音频原文。
- `source_audio_url` 必须可由 Fish Audio 服务端直接下载，最大 10 MB。
- 每次新任务填写新的 `Idempotency-Key`；只有重试完全相同的请求时才复用原值。
- 创建成功后会保留一个私有音色，`data.voice_id` 可用于后续普通 TTS。
- `completed` 时读取 `data.audio_url`；该临时链接目前保留三天。
- 克隆和 TTS 分别计费。若音色已创建但 TTS 启动失败，错误会返回 `clone_completed: true` 和 `voice_id`，已创建音色及其费用会保留。

## 工作流中上传本地参考音频

`create_personal_voice` 的单独试运行页面中，`source_audio_url` 是文本 URL 参数，不能直接选择电脑文件。若希望最终使用者从电脑上传音频，请使用 Coze **工作流**：在开始节点配置 **Audio** 输入，再将它连接到该工具。

> Coze 上传控件可能显示最大 500 MB，但 Fish Audio 此接口只接受不超过 **10 MB** 的参考音频。建议先用较短的 MP3、WAV 或 M4A 文件测试，并确保拥有该声音的使用授权。

### 第一阶段：先跑通最小工作流

1. 新建工作流，在开始节点添加以下必填输入：

   | 变量名            | 类型   | 用途                                          |
   | ----------------- | ------ | --------------------------------------------- |
   | `source_audio`    | Audio  | 客户从电脑选择的参考音频                      |
   | `text`            | String | 要生成的目标文本                              |
   | `authorization`   | String | `Bearer ` 加客户自己的 Fish Audio API Key     |
   | `idempotency_key` | String | 每次新生成使用一个新值，例如 `coze-voice-001` |

2. 添加 `FishAudio 专属语音合成` 的 `create_personal_voice` 工具，并按下表绑定参数：

   | 工具参数           | 工作流变量        |
   | ------------------ | ----------------- |
   | `Authorization`    | `authorization`   |
   | `Idempotency-Key`  | `idempotency_key` |
   | `source_audio_url` | `source_audio`    |
   | `text`             | `text`            |

   Coze 会把 Audio 变量传递为临时可访问的文件 URL；不要在开始节点再额外要求客户填写 `source_audio_url`。

3. 添加结束节点，先输出创建响应中的 `data.voice_id` 和 `data.id`（后者是 `jobId`）。最小工作流如下：

   ```text
   开始（上传 Audio + 填写文本、Key、幂等键）
     → create_personal_voice
     → 结束（voice_id + jobId）
   ```

4. 点击“试运行”，填写：
   - `authorization`：严格使用 `Bearer <客户自己的 API Key>`；
   - `idempotency_key`：每次新生成使用一个新值；
   - `source_audio`：上传不超过 10 MB 的本地音频；
   - `text`：先使用一小段目标文本。

如果运行日志中的工具输入已经显示类似 `source_audio_url: https://...byteimg.com/...` 的地址，说明“本地上传 → 临时 URL → 工具参数”的转换已经成功。此后出现 `401 ERR_UNAUTHORIZED` 是 API Key 无效、已撤销或格式错误，不是上传失败。

创建成功并返回 `data.voice_id` 和 `data.id` 后，再选择下面一种查询方式。

### 第二阶段：查询生成结果

最容易搭建的方式是等待 15～30 秒后，手动试运行 `get_personal_voice_job`，传入同一个 `Authorization` 和创建响应中的 `data.id`。

需要自动查询时，添加一个“循环”节点并将循环次数设为 20。在循环体内依次放置：

1.  Python 代码节点，使用 `await asyncio.sleep(15)` 等待 15 秒；
2.  `get_personal_voice_job`，其中 `Authorization` 仍绑定 `authorization`，`jobId` 绑定创建响应的 `data.id`；
3.  选择器节点：当状态为 `completed` 或 `failed` 时进入“终止循环”，否则继续下一轮。

循环输出绑定查询工具最后一次返回的 `data`：`completed` 时输出 `audio_url`，`failed` 时输出 `error_message`。20 轮后仍为 `processing` 时返回超时提示和原 `jobId`，供后续继续查询；不得重新创建任务。

等待代码节点可使用：

```python
import asyncio

async def main(args: Args) -> Output:
    await asyncio.sleep(15)
    return {"waited": True}
```

### 日志判读与常见错误

| 现象                                    | 含义与处理                                                                            |
| --------------------------------------- | ------------------------------------------------------------------------------------- |
| 输入中出现 `https://...byteimg.com/...` | Coze 已成功上传文件并把 Audio 转成 URL。                                              |
| `401 ERR_UNAUTHORIZED`                  | Key 无效、已撤销、缺少 `Bearer ` 或格式错误；更换 Key 后重试。                        |
| `413` 或文件过大                        | Fish Audio 限制为 10 MB，即使 Coze 上传控件显示更大的上限也不能超过该限制。           |
| 媒体下载失败或 `415`                    | 临时 URL 已过期、服务端无法下载，或文件类型不受支持；换用较短的常见音频格式重新上传。 |
| 一直是 `processing`                     | 继续查询同一个 `jobId`，不要重新创建任务。                                            |

同一个 `Idempotency-Key` 只用于重试**完全相同**的请求。重新上传文件后临时 URL 会变化，应使用新的幂等键。`Authorization` 和 `idempotency_key` 是普通工作流输入，可能出现在运行记录中；不要保存或分享包含真实 Key 的测试集、模板或截图。

## 发布前检查

- 导入后工具数量正确：TTS 为 1 个，图片/视频对口型为 2 个，文字对口型为 2 个，专属语音合成为 2 个。
- TTS 试运行返回非空 `audio_url`。
- 创建同步任务返回非空 `data.id`。
- 查询工具能返回 `status`、`progress`、`result_url` 和 `error_message`。
- 文字对口型创建工具返回 `data.id` 和 `data.audio_url`，查询完成后返回 `data.result_url`。
- 专属语音合成创建工具返回 `voice_id` 和 `data.id`，查询完成后返回 `audio_url`。
- 插件或使用示例中没有真实 API Key。
