create-dwy 0.16.1 → 0.17.0
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 +9 -0
- package/bin/index.js +1 -1
- package/package.json +1 -1
- 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
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())
|