create-dwy 0.16.1 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/bin/index.js +23 -19
- package/package.json +1 -1
- package/src/cli-help.js +44 -0
- package/src/sync-all.js +303 -59
- package/src/sync-global-skills.js +167 -0
- package/src/sync.js +21 -11
- package/templates/ai-tools/CHANGELOG.md +134 -0
- package/templates/ai-tools/VERSION +1 -1
- package/templates/ai-tools/rules/Android/dwy-android-core.md +65 -1
- package/templates/ai-tools/rules/Python/dwy-python-core.md +47 -0
- package/templates/ai-tools/rules/Vue/dwy-vue-core.md +55 -0
- package/templates/ai-tools/rules/iOS/dwy-swift-core.md +46 -0
- package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-oss-commercial-license.md +128 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/SKILL.md +65 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/assets/appstore-submission.yaml +43 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/references/api-workflow.md +17 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/references/in-app-purchase-review.md +16 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/references/screenshot-workflow.md +16 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/scripts/appstore_config.sh +140 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/SKILL.md +11 -8
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/android.md +2 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/backend-deploy.md +2 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/changelog.md +24 -4
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/frontend-deploy.md +2 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/generic-release.md +2 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/git-tag.md +82 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/harmony.md +2 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/ios.md +3 -2
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/monitor-notify.md +2 -2
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-semver/SKILL.md +1 -1
- package/templates/ai-tools/skills//345/237/272/347/241/200/345/272/223/dwy-eapi/references/tasks-integration-guide.md +2 -2
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/README.md +57 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/SKILL.md +147 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/references/api.md +43 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/references/config.example.yaml +33 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/scripts/global_config.py +89 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/scripts/setup_config.py +175 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/scripts/synthesize.py +339 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/media-platform-packaging/SKILL.md +127 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/media-platform-packaging/assets/cover-style-master-v1.png +0 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/media-platform-packaging/references/platform-copy-examples.md +37 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/media-platform-packaging/references/platform-posting-times.md +193 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0005-ideation/SKILL.md +42 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0005-ideation/templates/topic-backlog.template.md +50 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0005-ideation/templates//351/200/211/351/242/230/346/261/240.template.md +16 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0010-product/SKILL.md +106 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0010-product/templates/channel-profile.template.md +52 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0010-product/templates//344/272/247/345/223/201/345/215/241.template.md +132 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0030-facts/SKILL.md +60 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0030-facts/scripts/build_facts_html.py +430 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0030-facts/scripts/generate_html.py +1287 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0030-facts/scripts/generate_report.py +189 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0030-facts/templates//344/272/213/345/256/236/350/265/204/346/226/231/345/272/223.template.md +55 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0040-script/SKILL.md +36 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0040-script/templates/README.md +10 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0040-script/templates//344/272/213/345/256/236.template.md +82 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0040-script/templates//345/215/225/351/233/206/346/226/207/346/241/243.template.md +175 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0040-script/templates//350/204/232/346/234/254.template.md +69 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0040-script/templates//351/252/250/346/236/266-v1.1.md +174 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0050-design/SKILL.md +55 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0060-assets/SKILL.md +29 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0070-package/SKILL.md +42 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0070-package/scripts/validate_shots.py +207 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0070-package/shots.schema.json +186 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0070-package//344/272/272/345/256/241/345/214/205.template.md +1 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0070-package//344/272/272/345/256/241/350/257/264/346/230/216.md +15 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0070-package//346/210/220/347/211/207/345/214/205.template.md +49 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0080-factory/SKILL.md +58 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-comic-kit/SKILL.md +424 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-comic-kit/evals.md +41 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-flow/CONTRACT.md +123 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-flow/SKILL.md +81 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-flow//346/211/247/350/241/214/346/270/205/345/215/225.template.md +60 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/SKILL.md +78 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/agents/openai.yaml +4 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/assets/knowledge-town-paper-film-v1.json +80 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/assets/remotion/AudioTracks.tsx +20 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/assets/remotion/CaptionLayer.tsx +18 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/assets/remotion/CueTimeline.ts +24 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/assets/remotion/KnowledgeTownComposition.tsx +21 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/references/cinematic-direction.md +73 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/references/sticker-asset-production.md +30 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/references/style-guide.md +43 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/scripts/build_cue_timeline.py +75 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/scripts/validate_cue_timeline.py +68 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/scripts/validate_style_preset.py +22 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/SKILL.md +0 -511
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/checks-data.md +0 -126
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/checks-network.md +0 -88
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/checks-runtime.md +0 -195
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/security-doc-search.md +0 -179
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_base.sh +0 -39
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_capacity.sh +0 -278
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_db.sh +0 -147
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_docker.sh +0 -230
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_env.sh +0 -64
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_https.sh +0 -60
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_logs.sh +0 -209
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_nginx.sh +0 -100
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_resilience.sh +0 -157
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_secrets.sh +0 -265
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_services.sh +0 -94
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_ssh.sh +0 -56
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/run_all.sh +0 -105
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
- `github-action` → 引 `../dwy-github-action-publish`(先过通用检查)
|
|
19
19
|
- `local` → 缓存的本地构建/发布命令
|
|
20
20
|
|
|
21
|
-
## changelog / 安全检查 / 监控
|
|
21
|
+
## changelog / tag / 安全检查 / 监控
|
|
22
22
|
|
|
23
23
|
- changelog → `changelog.md`(写 `harmony/CHANGELOG.md`)
|
|
24
|
+
- tag → `git-tag.md`(每次 bump 必打;版本串与 changelog `##` 标题一致)
|
|
24
25
|
- 安全检查 → `../dwy-sdk-spec`
|
|
25
26
|
- 监控 + 结果 → `monitor-notify.md`
|
|
@@ -21,12 +21,13 @@
|
|
|
21
21
|
|
|
22
22
|
按缓存 method 分流:
|
|
23
23
|
- `testflight` → 用项目实际的 TestFlight 上传命令(fastlane lane / Transporter / altool)
|
|
24
|
-
- `cocoapods` / `spm` → 用项目实际的 pod
|
|
24
|
+
- `cocoapods` / `spm` → 用项目实际的 pod 发布命令(SPM 消费 git tag;tag 走统一 `git-tag.md`,版本串 = changelog = `CFBundleShortVersionString`)
|
|
25
25
|
- `github-action` → 引 `../dwy-github-action-publish`(先过其通用检查)
|
|
26
26
|
- `local` → 缓存的本地命令
|
|
27
27
|
|
|
28
|
-
## changelog / 安全检查 / 监控
|
|
28
|
+
## changelog / tag / 安全检查 / 监控
|
|
29
29
|
|
|
30
30
|
- changelog → `changelog.md`(写 `ios/CHANGELOG.md`)
|
|
31
|
+
- tag → `git-tag.md`(每次 bump 必打;版本串与 changelog `##` 标题一致)
|
|
31
32
|
- 安全检查 → `../dwy-sdk-spec`(SDK 发布尤其要查泄露)
|
|
32
33
|
- 监控 + 结果 → `monitor-notify.md`
|
|
@@ -30,7 +30,7 @@ verify 失败,发布不算完成。
|
|
|
30
30
|
|
|
31
31
|
发布终态后在对话里输出结果摘要,不接外部通知:
|
|
32
32
|
|
|
33
|
-
- **成功**:平台 /
|
|
34
|
-
- **失败**:失败阶段、关键报错、下一步建议(重发 / bump /
|
|
33
|
+
- **成功**:平台 / 应用目录、版本号、**git tag 全名**、产物、渠道、verify 结果
|
|
34
|
+
- **失败**:失败阶段、关键报错、下一步建议(重发 / bump / 修配置 / 重打 tag)
|
|
35
35
|
|
|
36
36
|
涉及删除远端 release / tag 的重发,必须先确认用户允许。
|
|
@@ -120,7 +120,7 @@ python3 scripts/bump.py 2.0.0-rc.1 major # -> 2.0.0 (预发布转
|
|
|
120
120
|
## 与 dwy-publish 的边界
|
|
121
121
|
|
|
122
122
|
- **dwy-semver**(本 skill):只回答"该升哪一位、新号是多少"
|
|
123
|
-
- **dwy-publish**:拿到版本号后执行发版流程(测试 → bump 版本文件 → changelog →
|
|
123
|
+
- **dwy-publish**:拿到版本号后执行发版流程(测试 → bump 版本文件 → changelog → 安全检查 → **打 tag(版本串与 changelog 标题一致)** → build → publish)
|
|
124
124
|
|
|
125
125
|
发版时若不确定版本号,先用本 skill 决策,再交给 dwy-publish 落地。
|
|
126
126
|
|
|
@@ -143,7 +143,7 @@ curl -X POST http://localhost:8000/tasks \
|
|
|
143
143
|
|
|
144
144
|
```json
|
|
145
145
|
{
|
|
146
|
-
"code":
|
|
146
|
+
"code": "SUCCESS",
|
|
147
147
|
"message": "success",
|
|
148
148
|
"data": {
|
|
149
149
|
"id": "task_a1b2c3d4...",
|
|
@@ -184,7 +184,7 @@ curl "http://localhost:8000/tasks?task_type=process_data"
|
|
|
184
184
|
curl -X POST http://localhost:8000/tasks/task_a1b2c3d4.../cancel
|
|
185
185
|
```
|
|
186
186
|
|
|
187
|
-
仅 `pending` 和 `running`
|
|
187
|
+
仅 `pending` 和 `running` 状态的任务可取消。已结束的任务抛 `BusinessError`,HTTP **422**(`TASK_ALREADY_FINISHED`)。
|
|
188
188
|
|
|
189
189
|
### 自定义路由前缀
|
|
190
190
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# 自媒体 skills(uc-media)
|
|
2
|
+
|
|
3
|
+
## 处理流程
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
跨集 channel-profile · topic-backlog · design-defaults?
|
|
7
|
+
|
|
8
|
+
有主题 ─────────────────────┐
|
|
9
|
+
无主题 → 0005 ★人选 ────────┤
|
|
10
|
+
▼
|
|
11
|
+
【0010 产品卡 · 一步】
|
|
12
|
+
① 竞品 + 差异化
|
|
13
|
+
② AI 自动推荐演讲地图
|
|
14
|
+
(讲/不讲 · 内容柱 · 时长)
|
|
15
|
+
★ 不做则停;可改 §3 地图
|
|
16
|
+
▼
|
|
17
|
+
【0030 事实】按内容柱扩展深库
|
|
18
|
+
▼
|
|
19
|
+
【0040 脚本】按地图蒸馏口播
|
|
20
|
+
▼
|
|
21
|
+
0050→0060→0070→0080→render
|
|
22
|
+
可选 0090 包装
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**因果:** 主题 → **看清赛道并自动生成讲什么** → 为地图备事实 → 蒸馏成片。
|
|
26
|
+
|
|
27
|
+
| 编号 | 决定什么 | 主文档 |
|
|
28
|
+
|------|----------|--------|
|
|
29
|
+
| 0005 | 拍哪个主题(可选) | topic-backlog.md |
|
|
30
|
+
| **0010** | 竞品差在哪 + **AI 推荐讲什么** | `0010-product/产品卡-*.md` |
|
|
31
|
+
| 0030 | 按地图扩事实 | `0030-facts/事实-*.md` |
|
|
32
|
+
| 0040 | 口播分镜 | `0040-script/脚本-*.md` |
|
|
33
|
+
| 0050–0080 | 设计·素材·成片·工厂 | … |
|
|
34
|
+
| 0090 | 多平台包装(可选) | 0070-package/ |
|
|
35
|
+
|
|
36
|
+
| 模式 | 行为 |
|
|
37
|
+
|------|------|
|
|
38
|
+
| **fast** | 0010→0030 连跑后停;0040/0050 停;连 0060–0070;0080 停 |
|
|
39
|
+
| **standard** | 每步确认(含是否改 0010 地图) |
|
|
40
|
+
|
|
41
|
+
## Skills
|
|
42
|
+
|
|
43
|
+
| Skill | 作用 |
|
|
44
|
+
|-------|------|
|
|
45
|
+
| uc-media-flow | 编排 |
|
|
46
|
+
| uc-media-0005-ideation | 选题发现 |
|
|
47
|
+
| **uc-media-0010-product** | **竞品 + AI 演讲地图(合并)** |
|
|
48
|
+
| uc-media-0030-facts | 按地图扩事实 |
|
|
49
|
+
| uc-media-0040-script … 0080-factory | 脚本与制作 |
|
|
50
|
+
| media-platform-packaging | 0090 可选 |
|
|
51
|
+
| dwy-doubao-tts | 口播配音(豆包声音复刻 2.0,可选) |
|
|
52
|
+
|
|
53
|
+
契约:[`uc-media-flow/CONTRACT.md`](./uc-media-flow/CONTRACT.md)
|
|
54
|
+
|
|
55
|
+
## 兼容
|
|
56
|
+
|
|
57
|
+
旧分步 `0010-topic` + `0020-product` → 单一 `0010-product`;无 0020 目录。
|
package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/SKILL.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dwy-doubao-tts
|
|
3
|
+
description: >
|
|
4
|
+
豆包配音:用火山引擎豆包声音复刻 2.0(seed-icl-2.0)HTTP Chunked 单向流式接口,
|
|
5
|
+
为自媒体口播生成 MP3 音频。API Key / 音色 ID 只读用户全局 ~/.dwy/config.yaml,
|
|
6
|
+
禁止写入 skill 或任何项目目录。触发:豆包配音、口播配音、TTS、语音合成、
|
|
7
|
+
生成口播音频、/doubao-tts、/dwy-doubao-tts。
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# 豆包配音(dwy-doubao-tts)
|
|
11
|
+
|
|
12
|
+
自媒体口播:读全局密钥与音色 → 收口播文案 → 按语境选音色 → 流式合成 MP3 → 回报路径。
|
|
13
|
+
|
|
14
|
+
`SKILL_DIR` = 本 `SKILL.md` 所在目录(`dwy` 同步后随平台落在 `.claude/skills/dwy-doubao-tts` 或 `.agents/skills/dwy-doubao-tts`)。脚本一律用 `$SKILL_DIR/scripts/`,禁止写 `~/.grok/skills`。
|
|
15
|
+
|
|
16
|
+
## 硬约束
|
|
17
|
+
|
|
18
|
+
1. **私人配置唯一位置**:`~/.dwy/config.yaml`(权限 `600`),豆包段为顶层 key `doubao_tts`
|
|
19
|
+
2. **禁止**把 `api_key`、音色 ID 写入 skill、项目、commit;禁止回显完整 Key
|
|
20
|
+
3. 输出音频默认 `doubao_tts.output_dir`(默认 `~/Movies/doubao-tts`),不落到仓库内
|
|
21
|
+
4. 模型资源:`X-Api-Resource-Id: seed-icl-2.0`
|
|
22
|
+
5. 接口:`POST https://openspeech.bytedance.com/api/v3/tts/unidirectional`
|
|
23
|
+
|
|
24
|
+
## 全局配置约定(`~/.dwy/config.yaml`)
|
|
25
|
+
|
|
26
|
+
用户级统一配置文件,多业务共用,各业务独立 section:
|
|
27
|
+
|
|
28
|
+
```yaml
|
|
29
|
+
version: 1
|
|
30
|
+
|
|
31
|
+
doubao_tts:
|
|
32
|
+
api_key: "<仅本机>"
|
|
33
|
+
resource_id: seed-icl-2.0
|
|
34
|
+
endpoint: https://openspeech.bytedance.com/api/v3/tts/unidirectional
|
|
35
|
+
default_voice: young_female
|
|
36
|
+
voices:
|
|
37
|
+
finance:
|
|
38
|
+
id: "<音色ID>"
|
|
39
|
+
label: 金融财经
|
|
40
|
+
young_female:
|
|
41
|
+
id: "<音色ID>"
|
|
42
|
+
label: 年轻女性
|
|
43
|
+
audio:
|
|
44
|
+
format: mp3
|
|
45
|
+
sample_rate: 24000
|
|
46
|
+
output_dir: ~/Movies/doubao-tts
|
|
47
|
+
|
|
48
|
+
# 其它业务可继续追加顶层 key,互不覆盖
|
|
49
|
+
# other_service:
|
|
50
|
+
# ...
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
结构说明见 `references/config.example.yaml`(示例无真实密钥)。
|
|
54
|
+
|
|
55
|
+
## 启动流程(每次触发必做)
|
|
56
|
+
|
|
57
|
+
### 1. 检查全局配置
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
python3 -c "
|
|
61
|
+
from pathlib import Path
|
|
62
|
+
import sys
|
|
63
|
+
sys.path.insert(0, str(Path(r'$SKILL_DIR') / 'scripts'))
|
|
64
|
+
from global_config import CONFIG_PATH, SECTION_DOUBAO_TTS, get_section
|
|
65
|
+
c = get_section(SECTION_DOUBAO_TTS)
|
|
66
|
+
assert c.get('api_key') and c.get('voices'), 'missing'
|
|
67
|
+
print('ok', CONFIG_PATH)
|
|
68
|
+
"
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
- 失败 → 「首次配置 / 迁移」,**不要**继续合成
|
|
72
|
+
- 成功 → 步骤 2
|
|
73
|
+
|
|
74
|
+
### 2. 收集用户输入
|
|
75
|
+
|
|
76
|
+
消息里若无完整口播正文,必须提问补齐,禁止瞎编:
|
|
77
|
+
|
|
78
|
+
| 字段 | 必选 | 说明 |
|
|
79
|
+
|------|------|------|
|
|
80
|
+
| `text` | 是 | 口播全文 |
|
|
81
|
+
| `voice` | 否 | `finance` / `young_female` / 配置其它 key;不传则自动选 |
|
|
82
|
+
| `output` | 否 | 输出绝对路径 |
|
|
83
|
+
| `speech_rate` / `loudness_rate` | 否 | `[-50,100]` |
|
|
84
|
+
| `context_texts` | 否 | 语音指令 |
|
|
85
|
+
|
|
86
|
+
### 3. 自动选音色
|
|
87
|
+
|
|
88
|
+
音色 ID **只从** `~/.dwy/config.yaml` 的 `doubao_tts.voices` 读,skill 内不硬编码真实 ID。
|
|
89
|
+
|
|
90
|
+
| 角色 key | 适用语境 |
|
|
91
|
+
|----------|----------|
|
|
92
|
+
| `finance` | 财经、股市、基金、理财、利率、宏观、投研 |
|
|
93
|
+
| `young_female` | 生活、种草、日常、泛娱乐、默认兜底 |
|
|
94
|
+
|
|
95
|
+
1. 用户指定 `voice` → 用该 key
|
|
96
|
+
2. 否则文案命中财经关键词 → `finance`
|
|
97
|
+
3. 否则 → `default_voice`
|
|
98
|
+
4. 两可 → 先问用户
|
|
99
|
+
|
|
100
|
+
### 4. 执行合成
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
python3 "$SKILL_DIR/scripts/synthesize.py" \
|
|
104
|
+
--text-file /tmp/doubao_tts_input.txt \
|
|
105
|
+
--voice <finance|young_female|...> \
|
|
106
|
+
[--output /absolute/path/out.mp3]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
- 长文优先 `--text-file`;单次建议 ≤ 1000 汉字
|
|
110
|
+
- 成功 stdout JSON:`{"ok":true,"path":"...","voice":"...","bytes":N}`
|
|
111
|
+
|
|
112
|
+
### 5. 回报用户
|
|
113
|
+
|
|
114
|
+
绝对路径、音色 key/label、文件大小;可 `open "<path>"` 试听。**禁止**回显 API Key。
|
|
115
|
+
|
|
116
|
+
## 首次配置 / 迁移
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# 从旧路径 ~/.config/doubao-tts/config.json 迁到 ~/.dwy/config.yaml
|
|
120
|
+
python3 "$SKILL_DIR/scripts/setup_config.py" --migrate-legacy
|
|
121
|
+
|
|
122
|
+
# 或写入 / 更新 Key(可同时补音色)
|
|
123
|
+
python3 "$SKILL_DIR/scripts/setup_config.py" \
|
|
124
|
+
--api-key '<USER_KEY>' \
|
|
125
|
+
--voice 'finance=<ID>,金融财经' \
|
|
126
|
+
--voice 'young_female=<ID>,年轻女性'
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
- 写入:`~/.dwy/config.yaml` → `doubao_tts`
|
|
130
|
+
- 成功后删除旧 JSON,避免双份密钥
|
|
131
|
+
- **永不**写入当前 git 仓库或 skill 目录
|
|
132
|
+
|
|
133
|
+
仅换 Key:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
python3 "$SKILL_DIR/scripts/setup_config.py" --api-key '<NEW>' --update-key-only
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## 协议要点
|
|
140
|
+
|
|
141
|
+
见 `references/api.md`。Header:`X-Api-Key` / `X-Api-Resource-Id` / `X-Api-Request-Id`;流式 `code==0` 拼 base64,结束码 `20000000`。
|
|
142
|
+
|
|
143
|
+
## 安全
|
|
144
|
+
|
|
145
|
+
- 私人信息只在 `~/.dwy/config.yaml`
|
|
146
|
+
- 不在项目建 `.env` / 本地 config
|
|
147
|
+
- 用户要求拷进项目 → 拒绝
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# 豆包单向流式 TTS(HTTP Chunked)摘要
|
|
2
|
+
|
|
3
|
+
权威字段以火山引擎控制台文档为准。实现以 `scripts/synthesize.py` 为准。
|
|
4
|
+
|
|
5
|
+
## 配置
|
|
6
|
+
|
|
7
|
+
私人信息只读:`~/.dwy/config.yaml` → 顶层 key `doubao_tts`
|
|
8
|
+
示例骨架:`config.example.yaml`(无真实密钥)
|
|
9
|
+
|
|
10
|
+
## Endpoint
|
|
11
|
+
|
|
12
|
+
`POST https://openspeech.bytedance.com/api/v3/tts/unidirectional`
|
|
13
|
+
|
|
14
|
+
## Headers
|
|
15
|
+
|
|
16
|
+
| Header | 必选 | 说明 |
|
|
17
|
+
|--------|------|------|
|
|
18
|
+
| `X-Api-Key` | 是 | `doubao_tts.api_key` |
|
|
19
|
+
| `X-Api-Resource-Id` | 是 | 默认 `seed-icl-2.0` |
|
|
20
|
+
| `X-Api-Request-Id` | 是 | 客户端 uuid |
|
|
21
|
+
|
|
22
|
+
## Body
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"req_params": {
|
|
27
|
+
"text": "口播正文",
|
|
28
|
+
"speaker": "<音色ID>",
|
|
29
|
+
"audio_params": {
|
|
30
|
+
"format": "mp3",
|
|
31
|
+
"sample_rate": 24000,
|
|
32
|
+
"speech_rate": 0,
|
|
33
|
+
"loudness_rate": 0
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 响应(Chunked)
|
|
40
|
+
|
|
41
|
+
- `code == 0`:`data` 为 base64 音频片
|
|
42
|
+
- `code == 20000000`:结束
|
|
43
|
+
- Header `X-Tt-Logid` 排障用
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# 示例:复制结构到 ~/.dwy/config.yaml(勿提交真实密钥)
|
|
2
|
+
# 路径:/Users/<you>/.dwy/config.yaml
|
|
3
|
+
# 权限:chmod 600 ~/.dwy/config.yaml
|
|
4
|
+
|
|
5
|
+
version: 1
|
|
6
|
+
|
|
7
|
+
# 豆包配音(声音复刻 2.0)
|
|
8
|
+
doubao_tts:
|
|
9
|
+
api_key: "<从火山控制台粘贴>"
|
|
10
|
+
resource_id: seed-icl-2.0
|
|
11
|
+
endpoint: https://openspeech.bytedance.com/api/v3/tts/unidirectional
|
|
12
|
+
default_voice: young_female
|
|
13
|
+
voices:
|
|
14
|
+
finance:
|
|
15
|
+
id: "<金融财经音色ID>"
|
|
16
|
+
label: 金融财经
|
|
17
|
+
desc: 财经、投研、商业分析口播
|
|
18
|
+
young_female:
|
|
19
|
+
id: "<年轻女性音色ID>"
|
|
20
|
+
label: 年轻女性
|
|
21
|
+
desc: 生活、种草、日常、泛娱乐口播
|
|
22
|
+
audio:
|
|
23
|
+
format: mp3
|
|
24
|
+
sample_rate: 24000
|
|
25
|
+
speech_rate: 0
|
|
26
|
+
loudness_rate: 0
|
|
27
|
+
disable_markdown_filter: true
|
|
28
|
+
disable_emoji_filter: true
|
|
29
|
+
output_dir: ~/Movies/doubao-tts
|
|
30
|
+
|
|
31
|
+
# 其它业务配置可并列追加,例如:
|
|
32
|
+
# lark:
|
|
33
|
+
# app_id: "..."
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""用户全局配置读写:~/.dwy/config.yaml
|
|
3
|
+
|
|
4
|
+
私人信息(API Key、音色 ID 等)只放在此文件,不进 skill、不进项目仓库。
|
|
5
|
+
多业务共用一个 YAML,各业务占独立顶层 key(如 doubao_tts)。
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import stat
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
import yaml
|
|
16
|
+
|
|
17
|
+
# 用户主目录全局配置根(非项目 .dwy)
|
|
18
|
+
DWY_HOME = Path.home() / ".dwy"
|
|
19
|
+
CONFIG_PATH = DWY_HOME / "config.yaml"
|
|
20
|
+
|
|
21
|
+
# 豆包配音在全局 YAML 中的 section 名
|
|
22
|
+
SECTION_DOUBAO_TTS = "doubao_tts"
|
|
23
|
+
|
|
24
|
+
# 旧路径:迁移用
|
|
25
|
+
LEGACY_JSON = Path.home() / ".config" / "doubao-tts" / "config.json"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def ensure_dwy_home() -> None:
|
|
29
|
+
"""确保 ~/.dwy 存在且仅本人可访问。"""
|
|
30
|
+
DWY_HOME.mkdir(parents=True, exist_ok=True)
|
|
31
|
+
os.chmod(DWY_HOME, stat.S_IRWXU)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def load_root() -> dict[str, Any]:
|
|
35
|
+
"""加载整份全局 YAML;不存在返回带 version 的空结构。"""
|
|
36
|
+
if not CONFIG_PATH.is_file():
|
|
37
|
+
return {"version": 1}
|
|
38
|
+
data = yaml.safe_load(CONFIG_PATH.read_text(encoding="utf-8"))
|
|
39
|
+
if data is None:
|
|
40
|
+
return {"version": 1}
|
|
41
|
+
if not isinstance(data, dict):
|
|
42
|
+
raise ValueError(f"全局配置格式错误(需 mapping): {CONFIG_PATH}")
|
|
43
|
+
data.setdefault("version", 1)
|
|
44
|
+
return data
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def save_root(root: dict[str, Any]) -> Path:
|
|
48
|
+
"""原子写入 ~/.dwy/config.yaml,chmod 600。"""
|
|
49
|
+
ensure_dwy_home()
|
|
50
|
+
root = dict(root)
|
|
51
|
+
root.setdefault("version", 1)
|
|
52
|
+
text = yaml.safe_dump(
|
|
53
|
+
root,
|
|
54
|
+
allow_unicode=True,
|
|
55
|
+
default_flow_style=False,
|
|
56
|
+
sort_keys=False,
|
|
57
|
+
)
|
|
58
|
+
tmp = CONFIG_PATH.with_suffix(".yaml.tmp")
|
|
59
|
+
tmp.write_text(text, encoding="utf-8")
|
|
60
|
+
tmp.replace(CONFIG_PATH)
|
|
61
|
+
os.chmod(CONFIG_PATH, stat.S_IRUSR | stat.S_IWUSR)
|
|
62
|
+
return CONFIG_PATH
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def get_section(name: str) -> dict[str, Any]:
|
|
66
|
+
"""读取某一业务 section;缺失返回 {}。"""
|
|
67
|
+
root = load_root()
|
|
68
|
+
sec = root.get(name)
|
|
69
|
+
if sec is None:
|
|
70
|
+
return {}
|
|
71
|
+
if not isinstance(sec, dict):
|
|
72
|
+
raise ValueError(f"section {name!r} 必须是 mapping: {CONFIG_PATH}")
|
|
73
|
+
return sec
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def set_section(name: str, section: dict[str, Any]) -> Path:
|
|
77
|
+
"""覆盖写入某一业务 section,保留其它业务配置。"""
|
|
78
|
+
root = load_root()
|
|
79
|
+
root[name] = section
|
|
80
|
+
return save_root(root)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def try_load_legacy_doubao_json() -> dict[str, Any]:
|
|
84
|
+
"""读取旧版 ~/.config/doubao-tts/config.json(若存在)。"""
|
|
85
|
+
if not LEGACY_JSON.is_file():
|
|
86
|
+
return {}
|
|
87
|
+
import json
|
|
88
|
+
|
|
89
|
+
return json.loads(LEGACY_JSON.read_text(encoding="utf-8"))
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""初始化 / 更新豆包配音配置到用户全局 ~/.dwy/config.yaml(doubao_tts section)。
|
|
3
|
+
|
|
4
|
+
禁止写入 skill 目录与任何 git 项目。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import copy
|
|
11
|
+
import json
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
# 同目录模块
|
|
16
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
17
|
+
|
|
18
|
+
from global_config import ( # noqa: E402
|
|
19
|
+
LEGACY_JSON,
|
|
20
|
+
SECTION_DOUBAO_TTS,
|
|
21
|
+
get_section,
|
|
22
|
+
set_section,
|
|
23
|
+
try_load_legacy_doubao_json,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# 结构默认值:不含真实 api_key / 音色 ID(私人信息只由用户写入全局 YAML)
|
|
27
|
+
DEFAULT_SECTION: dict = {
|
|
28
|
+
"api_key": "",
|
|
29
|
+
"resource_id": "seed-icl-2.0",
|
|
30
|
+
"endpoint": "https://openspeech.bytedance.com/api/v3/tts/unidirectional",
|
|
31
|
+
"voices": {},
|
|
32
|
+
"default_voice": "young_female",
|
|
33
|
+
"audio": {
|
|
34
|
+
"format": "mp3",
|
|
35
|
+
"sample_rate": 24000,
|
|
36
|
+
"speech_rate": 0,
|
|
37
|
+
"loudness_rate": 0,
|
|
38
|
+
"disable_markdown_filter": True,
|
|
39
|
+
"disable_emoji_filter": True,
|
|
40
|
+
},
|
|
41
|
+
"output_dir": str(Path.home() / "Movies" / "doubao-tts"),
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def deep_merge(base: dict, overlay: dict) -> dict:
|
|
46
|
+
"""浅层 + 一层 dict 合并;overlay 优先。"""
|
|
47
|
+
out = copy.deepcopy(base)
|
|
48
|
+
for k, v in overlay.items():
|
|
49
|
+
if isinstance(v, dict) and isinstance(out.get(k), dict):
|
|
50
|
+
merged = dict(out[k])
|
|
51
|
+
merged.update(v)
|
|
52
|
+
out[k] = merged
|
|
53
|
+
else:
|
|
54
|
+
out[k] = copy.deepcopy(v)
|
|
55
|
+
return out
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def load_doubao_section() -> dict:
|
|
59
|
+
"""优先 ~/.dwy/config.yaml#doubao_tts,其次旧 JSON。"""
|
|
60
|
+
sec = get_section(SECTION_DOUBAO_TTS)
|
|
61
|
+
if sec:
|
|
62
|
+
return sec
|
|
63
|
+
return try_load_legacy_doubao_json()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def main() -> int:
|
|
67
|
+
parser = argparse.ArgumentParser(description="写入豆包配音到 ~/.dwy/config.yaml")
|
|
68
|
+
parser.add_argument("--api-key", help="火山引擎语音 API Key")
|
|
69
|
+
parser.add_argument(
|
|
70
|
+
"--update-key-only",
|
|
71
|
+
action="store_true",
|
|
72
|
+
help="仅更新 api_key",
|
|
73
|
+
)
|
|
74
|
+
parser.add_argument("--default-voice", help="覆盖 default_voice(如 finance / young_female)")
|
|
75
|
+
parser.add_argument("--output-dir", help="覆盖口播输出目录")
|
|
76
|
+
parser.add_argument(
|
|
77
|
+
"--voice",
|
|
78
|
+
action="append",
|
|
79
|
+
metavar="KEY=ID[,label]",
|
|
80
|
+
help="写入/更新音色,可重复。例: --voice finance=S_xxx,金融财经",
|
|
81
|
+
)
|
|
82
|
+
parser.add_argument(
|
|
83
|
+
"--migrate-legacy",
|
|
84
|
+
action="store_true",
|
|
85
|
+
help="强制从 ~/.config/doubao-tts/config.json 迁移到 ~/.dwy/config.yaml",
|
|
86
|
+
)
|
|
87
|
+
args = parser.parse_args()
|
|
88
|
+
|
|
89
|
+
existing = load_doubao_section()
|
|
90
|
+
legacy = try_load_legacy_doubao_json()
|
|
91
|
+
|
|
92
|
+
if args.migrate_legacy and legacy:
|
|
93
|
+
existing = deep_merge(existing or {}, legacy)
|
|
94
|
+
|
|
95
|
+
if args.update_key_only:
|
|
96
|
+
if not args.api_key:
|
|
97
|
+
print("error: --update-key-only 需要 --api-key", file=sys.stderr)
|
|
98
|
+
return 2
|
|
99
|
+
if not existing:
|
|
100
|
+
print("error: 尚无 doubao_tts 配置可更新", file=sys.stderr)
|
|
101
|
+
return 2
|
|
102
|
+
sec = copy.deepcopy(existing)
|
|
103
|
+
sec["api_key"] = args.api_key.strip()
|
|
104
|
+
else:
|
|
105
|
+
sec = deep_merge(DEFAULT_SECTION, existing or {})
|
|
106
|
+
# 首次且无 voices:若有 legacy 整段带入
|
|
107
|
+
if not sec.get("voices") and legacy.get("voices"):
|
|
108
|
+
sec = deep_merge(sec, legacy)
|
|
109
|
+
if args.api_key:
|
|
110
|
+
sec["api_key"] = args.api_key.strip()
|
|
111
|
+
elif not (sec.get("api_key") or "").strip():
|
|
112
|
+
if legacy.get("api_key"):
|
|
113
|
+
sec["api_key"] = legacy["api_key"]
|
|
114
|
+
else:
|
|
115
|
+
print("error: 需要 --api-key(或先有可迁移的旧配置)", file=sys.stderr)
|
|
116
|
+
return 2
|
|
117
|
+
|
|
118
|
+
if args.default_voice:
|
|
119
|
+
sec["default_voice"] = args.default_voice
|
|
120
|
+
if args.output_dir:
|
|
121
|
+
sec["output_dir"] = str(Path(args.output_dir).expanduser())
|
|
122
|
+
|
|
123
|
+
if args.voice:
|
|
124
|
+
voices = dict(sec.get("voices") or {})
|
|
125
|
+
for item in args.voice:
|
|
126
|
+
if "=" not in item:
|
|
127
|
+
print(f"error: 无效 --voice {item!r},需 KEY=ID[,label]", file=sys.stderr)
|
|
128
|
+
return 2
|
|
129
|
+
key, rest = item.split("=", 1)
|
|
130
|
+
key = key.strip()
|
|
131
|
+
if "," in rest:
|
|
132
|
+
vid, label = rest.split(",", 1)
|
|
133
|
+
voices[key] = {"id": vid.strip(), "label": label.strip()}
|
|
134
|
+
else:
|
|
135
|
+
prev = voices.get(key) if isinstance(voices.get(key), dict) else {}
|
|
136
|
+
voices[key] = {**prev, "id": rest.strip()}
|
|
137
|
+
sec["voices"] = voices
|
|
138
|
+
|
|
139
|
+
if not (sec.get("api_key") or "").strip():
|
|
140
|
+
print("error: api_key 为空", file=sys.stderr)
|
|
141
|
+
return 2
|
|
142
|
+
|
|
143
|
+
path = set_section(SECTION_DOUBAO_TTS, sec)
|
|
144
|
+
|
|
145
|
+
removed_legacy = False
|
|
146
|
+
if LEGACY_JSON.is_file() and (args.migrate_legacy or legacy):
|
|
147
|
+
try:
|
|
148
|
+
LEGACY_JSON.unlink()
|
|
149
|
+
removed_legacy = True
|
|
150
|
+
parent = LEGACY_JSON.parent
|
|
151
|
+
if parent.is_dir() and not any(parent.iterdir()):
|
|
152
|
+
parent.rmdir()
|
|
153
|
+
except OSError:
|
|
154
|
+
pass
|
|
155
|
+
|
|
156
|
+
print(
|
|
157
|
+
json.dumps(
|
|
158
|
+
{
|
|
159
|
+
"ok": True,
|
|
160
|
+
"path": str(path),
|
|
161
|
+
"section": SECTION_DOUBAO_TTS,
|
|
162
|
+
"voices": list((sec.get("voices") or {}).keys()),
|
|
163
|
+
"default_voice": sec.get("default_voice"),
|
|
164
|
+
"resource_id": sec.get("resource_id"),
|
|
165
|
+
"has_api_key": bool(sec.get("api_key")),
|
|
166
|
+
"removed_legacy": removed_legacy,
|
|
167
|
+
},
|
|
168
|
+
ensure_ascii=False,
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
return 0
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
if __name__ == "__main__":
|
|
175
|
+
raise SystemExit(main())
|