vox4ai 0.1.0__tar.gz
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.
- vox4ai-0.1.0/.gitignore +22 -0
- vox4ai-0.1.0/LICENSE +21 -0
- vox4ai-0.1.0/PKG-INFO +153 -0
- vox4ai-0.1.0/README.md +121 -0
- vox4ai-0.1.0/pyproject.toml +75 -0
- vox4ai-0.1.0/src/vox4ai/__init__.py +0 -0
- vox4ai-0.1.0/src/vox4ai/cli.py +229 -0
- vox4ai-0.1.0/src/vox4ai/config.py +75 -0
- vox4ai-0.1.0/tests/test_cli.py +99 -0
- vox4ai-0.1.0/tests/test_config.py +194 -0
- vox4ai-0.1.0/tts-plugin-bridge/tts_plugin_bridge/__init__.py +98 -0
- vox4ai-0.1.0/tts-plugin-bridge/tts_plugin_bridge/__init__.py.meta +12 -0
- vox4ai-0.1.0/uv.lock +1920 -0
vox4ai-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*$py.class
|
|
4
|
+
.venv/
|
|
5
|
+
venv/
|
|
6
|
+
ENV/
|
|
7
|
+
.idea/
|
|
8
|
+
.vscode/
|
|
9
|
+
build/
|
|
10
|
+
dist/
|
|
11
|
+
.mypy_cache/
|
|
12
|
+
.ruff_cache/
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
*.log
|
|
15
|
+
.env
|
|
16
|
+
.env.local
|
|
17
|
+
|
|
18
|
+
# Generated
|
|
19
|
+
.coverage
|
|
20
|
+
GEMINI.md
|
|
21
|
+
*.bak
|
|
22
|
+
.mutmut-tmp.toml
|
vox4ai-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 vox4ai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
vox4ai-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vox4ai
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: TTS 音声合成 CLI - vox / vox4ai コマンド
|
|
5
|
+
Project-URL: Homepage, https://github.com/vox4ai/vox4ai
|
|
6
|
+
Project-URL: Repository, https://github.com/vox4ai/vox4ai
|
|
7
|
+
Project-URL: Issues, https://github.com/vox4ai/vox4ai/issues
|
|
8
|
+
Author-email: utenadev <utena.cross+pypi@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: cli,edgetts,speech,synthesis,tts,tts-plugin-bridge,voice,vox4ai
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
24
|
+
Classifier: Topic :: Utilities
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Requires-Dist: pydantic-settings>=2.14.1
|
|
27
|
+
Requires-Dist: pyyaml>=6.0
|
|
28
|
+
Requires-Dist: tts-plugin-bridge
|
|
29
|
+
Requires-Dist: tts-plugin-edgetts
|
|
30
|
+
Requires-Dist: vox4ai-skill-lib
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# vox4ai
|
|
34
|
+
|
|
35
|
+
<p align="center">
|
|
36
|
+
<img src="https://via.placeholder.com/1200x400/1a1a1a/ffffff?text=vox4ai" alt="vox4ai Banner" width="1200">
|
|
37
|
+
</p>
|
|
38
|
+
|
|
39
|
+
<p align="center">
|
|
40
|
+
<img src="https://img.shields.io/badge/pypi-latest-blue.svg" alt="PyPI version">
|
|
41
|
+
<img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License">
|
|
42
|
+
<img src="https://img.shields.io/badge/python-3.10%2B-yellow.svg" alt="Python Version">
|
|
43
|
+
<img src="https://img.shields.io/badge/maintained%3F-yes-brightgreen.svg" alt="Maintained">
|
|
44
|
+
</p>
|
|
45
|
+
|
|
46
|
+
<p align="center">
|
|
47
|
+
<a href="https://github.com/vox4ai/vox4ai">Website</a> •
|
|
48
|
+
<a href="https://github.com/vox4ai/vox4ai/issues">Report Bug</a> •
|
|
49
|
+
<a href="https://github.com/vox4ai/vox4ai/contributing">Contributing</a>
|
|
50
|
+
</p>
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 🚀 Overview
|
|
55
|
+
|
|
56
|
+
TTS 音声合成 CLI。`tts-plugin-bridge` のプラグインシステムに対応した TTS Engine を
|
|
57
|
+
統一的に操作できます。`vox` と `vox4ai` 両方のコマンド名でインストールされます。
|
|
58
|
+
|
|
59
|
+
## 📦 Installation
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
uv add vox4ai
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
インストールするだけで `tts-plugin-bridge` + `tts-plugin-edgetts`(クラウドデフォルト)が
|
|
66
|
+
入り、追加のローカルサーバーなしで音声合成を試せます。
|
|
67
|
+
|
|
68
|
+
他の TTS エンジンを追加する場合:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
uv add tts-plugin-aivisspeech # AivisSpeech Engine
|
|
72
|
+
uv add tts-plugin-piperplus # Piper Plus (HTTP server)
|
|
73
|
+
uv add tts-plugin-voisonatalk # VoiSona Talk
|
|
74
|
+
uv add tts-plugin-kokoro # Kokoro (local)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## 🛠 Usage
|
|
78
|
+
|
|
79
|
+
`vox` と `vox4ai` は同義。短い `vox` を推奨。
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
# テキストを読み上げる(デフォルト: edgetts)
|
|
83
|
+
vox "こんにちは"
|
|
84
|
+
|
|
85
|
+
# 音声ファイルに保存
|
|
86
|
+
vox save "こんにちは" -o output.wav
|
|
87
|
+
|
|
88
|
+
# engine を明示指定
|
|
89
|
+
vox "こんにちは" -e aivisspeech --style-id 888753760
|
|
90
|
+
|
|
91
|
+
# 利用可能な TTS エンジン一覧
|
|
92
|
+
vox list
|
|
93
|
+
|
|
94
|
+
# 接続テスト
|
|
95
|
+
vox test -e aivisspeech --server-url http://localhost:10101
|
|
96
|
+
|
|
97
|
+
# 環境診断
|
|
98
|
+
vox --doctor
|
|
99
|
+
|
|
100
|
+
# 現在の設定
|
|
101
|
+
vox config
|
|
102
|
+
|
|
103
|
+
# サブコマンド一覧
|
|
104
|
+
vox --commands
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## ⚙️ Configuration
|
|
108
|
+
|
|
109
|
+
`vox4ai say "こんにちは"` のように engine を省略しても、設定ファイルからデフォルト値を読み込みます。
|
|
110
|
+
|
|
111
|
+
**場所**: `~/.config/vox4ai/config.yaml`(XDG_CONFIG_HOME 準拠)
|
|
112
|
+
|
|
113
|
+
**設定例**:
|
|
114
|
+
|
|
115
|
+
```yaml
|
|
116
|
+
# デフォルトの TTS Engine
|
|
117
|
+
engine: edgetts
|
|
118
|
+
|
|
119
|
+
# デフォルトの音声モデル
|
|
120
|
+
model: ja-JP-NanamiNeural
|
|
121
|
+
|
|
122
|
+
# TTS サーバーURL(aivisspeech などで必要)
|
|
123
|
+
server_url: http://localhost:10101
|
|
124
|
+
|
|
125
|
+
# デフォルトの話速(0.5 〜 2.0、デフォルト 1.0)
|
|
126
|
+
speed: 1.2
|
|
127
|
+
|
|
128
|
+
# 話者スタイル ID(aivisspeech など)
|
|
129
|
+
style_id: 888753760
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**優先順位**: `CLI 引数 > config.yaml > デフォルト値`
|
|
133
|
+
|
|
134
|
+
## ⌨️ Subcommands
|
|
135
|
+
|
|
136
|
+
| Command | Description |
|
|
137
|
+
|---------|-------------|
|
|
138
|
+
| `say` | テキストを読み上げる(ffplay ストリーミング優先 → paplay/aplay) |
|
|
139
|
+
| `save` | テキストを音声ファイルに保存 |
|
|
140
|
+
| `list` | 利用可能なTTSプラグインを一覧表示 |
|
|
141
|
+
| `test` | TTSエンジンへの接続テスト |
|
|
142
|
+
| `config` | 現在の設定 (config.yaml) を表示 |
|
|
143
|
+
|
|
144
|
+
## 📦 Dependencies
|
|
145
|
+
|
|
146
|
+
- `tts-plugin-bridge` — コアフレームワーク
|
|
147
|
+
- `tts-plugin-edgetts` — Microsoft Edge TTS(クラウド、デフォルト)
|
|
148
|
+
- `vox4ai-skill-lib` — 内部ライブラリ
|
|
149
|
+
- 任意の TTS プラグイン (`tts-plugin-aivisspeech`, `tts-plugin-piperplus` など)
|
|
150
|
+
|
|
151
|
+
## 📜 License
|
|
152
|
+
|
|
153
|
+
MIT License
|
vox4ai-0.1.0/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# vox4ai
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://via.placeholder.com/1200x400/1a1a1a/ffffff?text=vox4ai" alt="vox4ai Banner" width="1200">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<img src="https://img.shields.io/badge/pypi-latest-blue.svg" alt="PyPI version">
|
|
9
|
+
<img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License">
|
|
10
|
+
<img src="https://img.shields.io/badge/python-3.10%2B-yellow.svg" alt="Python Version">
|
|
11
|
+
<img src="https://img.shields.io/badge/maintained%3F-yes-brightgreen.svg" alt="Maintained">
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://github.com/vox4ai/vox4ai">Website</a> •
|
|
16
|
+
<a href="https://github.com/vox4ai/vox4ai/issues">Report Bug</a> •
|
|
17
|
+
<a href="https://github.com/vox4ai/vox4ai/contributing">Contributing</a>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 🚀 Overview
|
|
23
|
+
|
|
24
|
+
TTS 音声合成 CLI。`tts-plugin-bridge` のプラグインシステムに対応した TTS Engine を
|
|
25
|
+
統一的に操作できます。`vox` と `vox4ai` 両方のコマンド名でインストールされます。
|
|
26
|
+
|
|
27
|
+
## 📦 Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
uv add vox4ai
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
インストールするだけで `tts-plugin-bridge` + `tts-plugin-edgetts`(クラウドデフォルト)が
|
|
34
|
+
入り、追加のローカルサーバーなしで音声合成を試せます。
|
|
35
|
+
|
|
36
|
+
他の TTS エンジンを追加する場合:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
uv add tts-plugin-aivisspeech # AivisSpeech Engine
|
|
40
|
+
uv add tts-plugin-piperplus # Piper Plus (HTTP server)
|
|
41
|
+
uv add tts-plugin-voisonatalk # VoiSona Talk
|
|
42
|
+
uv add tts-plugin-kokoro # Kokoro (local)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 🛠 Usage
|
|
46
|
+
|
|
47
|
+
`vox` と `vox4ai` は同義。短い `vox` を推奨。
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# テキストを読み上げる(デフォルト: edgetts)
|
|
51
|
+
vox "こんにちは"
|
|
52
|
+
|
|
53
|
+
# 音声ファイルに保存
|
|
54
|
+
vox save "こんにちは" -o output.wav
|
|
55
|
+
|
|
56
|
+
# engine を明示指定
|
|
57
|
+
vox "こんにちは" -e aivisspeech --style-id 888753760
|
|
58
|
+
|
|
59
|
+
# 利用可能な TTS エンジン一覧
|
|
60
|
+
vox list
|
|
61
|
+
|
|
62
|
+
# 接続テスト
|
|
63
|
+
vox test -e aivisspeech --server-url http://localhost:10101
|
|
64
|
+
|
|
65
|
+
# 環境診断
|
|
66
|
+
vox --doctor
|
|
67
|
+
|
|
68
|
+
# 現在の設定
|
|
69
|
+
vox config
|
|
70
|
+
|
|
71
|
+
# サブコマンド一覧
|
|
72
|
+
vox --commands
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## ⚙️ Configuration
|
|
76
|
+
|
|
77
|
+
`vox4ai say "こんにちは"` のように engine を省略しても、設定ファイルからデフォルト値を読み込みます。
|
|
78
|
+
|
|
79
|
+
**場所**: `~/.config/vox4ai/config.yaml`(XDG_CONFIG_HOME 準拠)
|
|
80
|
+
|
|
81
|
+
**設定例**:
|
|
82
|
+
|
|
83
|
+
```yaml
|
|
84
|
+
# デフォルトの TTS Engine
|
|
85
|
+
engine: edgetts
|
|
86
|
+
|
|
87
|
+
# デフォルトの音声モデル
|
|
88
|
+
model: ja-JP-NanamiNeural
|
|
89
|
+
|
|
90
|
+
# TTS サーバーURL(aivisspeech などで必要)
|
|
91
|
+
server_url: http://localhost:10101
|
|
92
|
+
|
|
93
|
+
# デフォルトの話速(0.5 〜 2.0、デフォルト 1.0)
|
|
94
|
+
speed: 1.2
|
|
95
|
+
|
|
96
|
+
# 話者スタイル ID(aivisspeech など)
|
|
97
|
+
style_id: 888753760
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**優先順位**: `CLI 引数 > config.yaml > デフォルト値`
|
|
101
|
+
|
|
102
|
+
## ⌨️ Subcommands
|
|
103
|
+
|
|
104
|
+
| Command | Description |
|
|
105
|
+
|---------|-------------|
|
|
106
|
+
| `say` | テキストを読み上げる(ffplay ストリーミング優先 → paplay/aplay) |
|
|
107
|
+
| `save` | テキストを音声ファイルに保存 |
|
|
108
|
+
| `list` | 利用可能なTTSプラグインを一覧表示 |
|
|
109
|
+
| `test` | TTSエンジンへの接続テスト |
|
|
110
|
+
| `config` | 現在の設定 (config.yaml) を表示 |
|
|
111
|
+
|
|
112
|
+
## 📦 Dependencies
|
|
113
|
+
|
|
114
|
+
- `tts-plugin-bridge` — コアフレームワーク
|
|
115
|
+
- `tts-plugin-edgetts` — Microsoft Edge TTS(クラウド、デフォルト)
|
|
116
|
+
- `vox4ai-skill-lib` — 内部ライブラリ
|
|
117
|
+
- 任意の TTS プラグイン (`tts-plugin-aivisspeech`, `tts-plugin-piperplus` など)
|
|
118
|
+
|
|
119
|
+
## 📜 License
|
|
120
|
+
|
|
121
|
+
MIT License
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "vox4ai"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "TTS 音声合成 CLI - vox / vox4ai コマンド"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "utenadev", email = "utena.cross+pypi@gmail.com"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["tts", "tts-plugin-bridge", "edgetts", "voice", "speech", "synthesis", "cli", "vox4ai"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"Intended Audience :: End Users/Desktop",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3.10",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Programming Language :: Python :: 3.13",
|
|
28
|
+
"Topic :: Multimedia :: Sound/Audio :: Speech",
|
|
29
|
+
"Topic :: Utilities",
|
|
30
|
+
]
|
|
31
|
+
dependencies = [
|
|
32
|
+
"tts-plugin-bridge",
|
|
33
|
+
"tts-plugin-edgetts",
|
|
34
|
+
"vox4ai-skill-lib",
|
|
35
|
+
"pyyaml>=6.0",
|
|
36
|
+
"pydantic-settings>=2.14.1",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://github.com/vox4ai/vox4ai"
|
|
41
|
+
Repository = "https://github.com/vox4ai/vox4ai"
|
|
42
|
+
Issues = "https://github.com/vox4ai/vox4ai/issues"
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
vox = "vox4ai.cli:main"
|
|
46
|
+
vox4ai = "vox4ai.cli:main"
|
|
47
|
+
|
|
48
|
+
[tool.uv.sources]
|
|
49
|
+
tts-plugin-bridge = { path = "../tts-plugin-bridge", editable = true }
|
|
50
|
+
tts-plugin-edgetts = { path = "../tts-plugin-edgetts", editable = true }
|
|
51
|
+
vox4ai-skill-lib = { path = "../vox4ai-skill-lib", editable = true }
|
|
52
|
+
|
|
53
|
+
[dependency-groups]
|
|
54
|
+
dev = [
|
|
55
|
+
"build>=1.5.0",
|
|
56
|
+
"pytest>=9.0.3",
|
|
57
|
+
"pytest-asyncio>=1.3.0",
|
|
58
|
+
"pytest-cov>=6.0.0",
|
|
59
|
+
"ruff>=0.15.12",
|
|
60
|
+
"twine>=6.2.0",
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
[tool.coverage.run]
|
|
64
|
+
source = ["vox4ai"]
|
|
65
|
+
branch = true
|
|
66
|
+
omit = ["*/tests/*", "*/__pycache__/*"]
|
|
67
|
+
|
|
68
|
+
[tool.coverage.report]
|
|
69
|
+
show_missing = true
|
|
70
|
+
fail_under = 50
|
|
71
|
+
|
|
72
|
+
[tool.mutmut]
|
|
73
|
+
paths_to_mutate = ["src/vox4ai/config.py"]
|
|
74
|
+
tests_dir = ["tests/test_config.py"]
|
|
75
|
+
backup = false
|
|
File without changes
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import asyncio
|
|
3
|
+
import shutil
|
|
4
|
+
|
|
5
|
+
from tts_plugin_bridge import ConnectorFactory
|
|
6
|
+
from vox4ai_skill_lib.api import (
|
|
7
|
+
list_engines,
|
|
8
|
+
synthesize_text,
|
|
9
|
+
play_text,
|
|
10
|
+
test_connection,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from vox4ai.config import load as load_config, merge_cli, show as show_config
|
|
14
|
+
|
|
15
|
+
_EPILOG = """
|
|
16
|
+
使用例:
|
|
17
|
+
vox4ai say "こんにちは" # テキストを読む(ストリーミング優先)
|
|
18
|
+
vox4ai say "Hello" -e edgetts # Edge TTS で読む
|
|
19
|
+
vox4ai save "こんにちは" -o output.wav # WAV ファイルに保存
|
|
20
|
+
vox4ai list # 利用可能エンジン一覧
|
|
21
|
+
vox4ai test -e aivisspeech # 接続テスト
|
|
22
|
+
vox4ai --commands # 使用可能なサブコマンド & エンジン一覧
|
|
23
|
+
vox4ai --doctor # 環境診断
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
_COMMANDS = {
|
|
27
|
+
"say": "テキストを読み上げる(ストリーミング優先、ffplay/paplay/aplay)",
|
|
28
|
+
"save": "テキストを音声ファイルに保存",
|
|
29
|
+
"list": "利用可能なTTSプラグインを一覧表示",
|
|
30
|
+
"test": "TTSエンジンへの接続をテスト",
|
|
31
|
+
"config": "現在の設定(config.yaml)を表示",
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def main():
|
|
36
|
+
return asyncio.run(_async_main())
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def _async_main():
|
|
40
|
+
parser = argparse.ArgumentParser(
|
|
41
|
+
description="vox4ai - 音声合成 CLI",
|
|
42
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
43
|
+
epilog=_EPILOG,
|
|
44
|
+
add_help=False,
|
|
45
|
+
)
|
|
46
|
+
parser.add_argument(
|
|
47
|
+
"--commands",
|
|
48
|
+
action="store_true",
|
|
49
|
+
help="利用可能なサブコマンド & TTSエンジン一覧を表示",
|
|
50
|
+
)
|
|
51
|
+
parser.add_argument(
|
|
52
|
+
"--doctor",
|
|
53
|
+
action="store_true",
|
|
54
|
+
help="環境の動作確認",
|
|
55
|
+
)
|
|
56
|
+
parser.add_argument(
|
|
57
|
+
"-h",
|
|
58
|
+
"--help",
|
|
59
|
+
action="store_true",
|
|
60
|
+
help="このヘルプを表示",
|
|
61
|
+
)
|
|
62
|
+
parser.add_argument(
|
|
63
|
+
"--tts-plugin-list",
|
|
64
|
+
action="store_true",
|
|
65
|
+
help=argparse.SUPPRESS,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
known, remaining = parser.parse_known_args()
|
|
69
|
+
|
|
70
|
+
if known.help:
|
|
71
|
+
parser.print_help()
|
|
72
|
+
return 0
|
|
73
|
+
|
|
74
|
+
if known.commands:
|
|
75
|
+
print("利用可能なサブコマンド:")
|
|
76
|
+
for name, desc in _COMMANDS.items():
|
|
77
|
+
print(f" {name:8s} {desc}")
|
|
78
|
+
print()
|
|
79
|
+
print("TTS エンジン:")
|
|
80
|
+
engines = ConnectorFactory.list_available()
|
|
81
|
+
if engines:
|
|
82
|
+
for eng in engines:
|
|
83
|
+
print(f" {eng}")
|
|
84
|
+
else:
|
|
85
|
+
print(" (none found)")
|
|
86
|
+
print()
|
|
87
|
+
print("詳細: vox4ai <コマンド> --help")
|
|
88
|
+
return 0
|
|
89
|
+
|
|
90
|
+
if known.doctor:
|
|
91
|
+
return await _doctor()
|
|
92
|
+
|
|
93
|
+
if known.tts_plugin_list:
|
|
94
|
+
return await list_engines()
|
|
95
|
+
|
|
96
|
+
sub = parser.add_subparsers(dest="command", help="利用可能なコマンド")
|
|
97
|
+
sub.add_parser("list", help="利用可能なTTSプラグインを一覧表示")
|
|
98
|
+
sub.add_parser("config", help="現在の設定表示")
|
|
99
|
+
|
|
100
|
+
say_parser = sub.add_parser(
|
|
101
|
+
"say", help="テキストを読み上げる(ストリーミング優先)"
|
|
102
|
+
)
|
|
103
|
+
say_parser.add_argument("text", help="読み上げるテキスト")
|
|
104
|
+
say_parser.add_argument("--engine", "-e", help="TTSエンジン名")
|
|
105
|
+
say_parser.add_argument("--speed", "-s", type=float, default=1.0, help="話速")
|
|
106
|
+
say_parser.add_argument("--volume", "-v", type=float, help="音量")
|
|
107
|
+
say_parser.add_argument("--pitch", "-p", type=float, help="ピッチ補正")
|
|
108
|
+
say_parser.add_argument("--server-url", help="TTSサーバーURL")
|
|
109
|
+
say_parser.add_argument("--style-id", type=int, help="話者スタイルID")
|
|
110
|
+
say_parser.add_argument("--model", help="音声モデル名")
|
|
111
|
+
|
|
112
|
+
save_parser = sub.add_parser("save", help="テキストを音声ファイルに保存")
|
|
113
|
+
save_parser.add_argument("text", help="合成するテキスト")
|
|
114
|
+
save_parser.add_argument("--engine", "-e", help="TTSエンジン名")
|
|
115
|
+
save_parser.add_argument("--speed", "-s", type=float, default=1.0, help="話速")
|
|
116
|
+
save_parser.add_argument("--volume", "-v", type=float, help="音量")
|
|
117
|
+
save_parser.add_argument("--pitch", "-p", type=float, help="ピッチ補正")
|
|
118
|
+
save_parser.add_argument("--server-url", help="TTSサーバーURL")
|
|
119
|
+
save_parser.add_argument("--style-id", type=int, help="話者スタイルID")
|
|
120
|
+
save_parser.add_argument(
|
|
121
|
+
"--output", "-o", help="出力ファイルパス(省略時はBase64表示)"
|
|
122
|
+
)
|
|
123
|
+
save_parser.add_argument("--play", action="store_true", help="保存後に再生")
|
|
124
|
+
|
|
125
|
+
test_parser = sub.add_parser("test", help="TTS接続をテスト")
|
|
126
|
+
test_parser.add_argument("--engine", "-e", help="TTSエンジン名")
|
|
127
|
+
test_parser.add_argument("--server-url", help="TTSサーバーURL")
|
|
128
|
+
test_parser.add_argument("--style-id", type=int, help="話者スタイルID")
|
|
129
|
+
|
|
130
|
+
args = parser.parse_args(remaining)
|
|
131
|
+
|
|
132
|
+
if not args.command:
|
|
133
|
+
parser.print_help()
|
|
134
|
+
return 1
|
|
135
|
+
|
|
136
|
+
if args.command == "config":
|
|
137
|
+
print(show_config())
|
|
138
|
+
return 0
|
|
139
|
+
|
|
140
|
+
cfg = load_config()
|
|
141
|
+
if args.command in ("say", "save", "test"):
|
|
142
|
+
merged = merge_cli(cfg, args)
|
|
143
|
+
else:
|
|
144
|
+
merged = {}
|
|
145
|
+
|
|
146
|
+
engine_kwargs = {}
|
|
147
|
+
if merged.get("server_url"):
|
|
148
|
+
engine_kwargs["server_url"] = merged["server_url"]
|
|
149
|
+
|
|
150
|
+
engine = merged.get("engine") or None
|
|
151
|
+
model = merged.get("model") or None
|
|
152
|
+
speed = merged.get("speed", 1.0)
|
|
153
|
+
volume = merged.get("volume")
|
|
154
|
+
pitch = merged.get("pitch")
|
|
155
|
+
style_id = merged.get("style_id")
|
|
156
|
+
|
|
157
|
+
if args.command == "list":
|
|
158
|
+
return await list_engines()
|
|
159
|
+
elif args.command == "say":
|
|
160
|
+
return await play_text(
|
|
161
|
+
args.text,
|
|
162
|
+
engine,
|
|
163
|
+
speed,
|
|
164
|
+
volume,
|
|
165
|
+
pitch,
|
|
166
|
+
style_id,
|
|
167
|
+
model,
|
|
168
|
+
engine_kwargs,
|
|
169
|
+
)
|
|
170
|
+
elif args.command == "save":
|
|
171
|
+
return await synthesize_text(
|
|
172
|
+
args.text,
|
|
173
|
+
engine,
|
|
174
|
+
speed,
|
|
175
|
+
volume,
|
|
176
|
+
pitch,
|
|
177
|
+
style_id,
|
|
178
|
+
getattr(args, "output", None),
|
|
179
|
+
engine_kwargs,
|
|
180
|
+
False,
|
|
181
|
+
getattr(args, "play", False),
|
|
182
|
+
)
|
|
183
|
+
elif args.command == "test":
|
|
184
|
+
return await test_connection(
|
|
185
|
+
engine,
|
|
186
|
+
style_id,
|
|
187
|
+
engine_kwargs,
|
|
188
|
+
)
|
|
189
|
+
else:
|
|
190
|
+
parser.print_help()
|
|
191
|
+
return 1
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
async def _doctor():
|
|
195
|
+
print("vox4ai 環境診断")
|
|
196
|
+
print("=" * 40)
|
|
197
|
+
|
|
198
|
+
print("\n[再生コマンド]")
|
|
199
|
+
for cmd in ("ffplay", "paplay", "aplay"):
|
|
200
|
+
found = shutil.which(cmd) is not None
|
|
201
|
+
print(f" {cmd:12s} {'found' if found else 'not found'}")
|
|
202
|
+
|
|
203
|
+
print("\n[TTS プラグイン]")
|
|
204
|
+
try:
|
|
205
|
+
engines = ConnectorFactory.list_available()
|
|
206
|
+
if engines:
|
|
207
|
+
for eng in engines:
|
|
208
|
+
print(f" {eng:15s} registered")
|
|
209
|
+
else:
|
|
210
|
+
print(" (none found)")
|
|
211
|
+
except Exception as e:
|
|
212
|
+
print(f" error: {e}")
|
|
213
|
+
|
|
214
|
+
print("\n[Python パッケージ]")
|
|
215
|
+
for pkg in ("tts-plugin-bridge", "edge-tts", "aiohttp", "pydantic"):
|
|
216
|
+
try:
|
|
217
|
+
__import__(pkg.replace("-", "_"))
|
|
218
|
+
print(f" {pkg:20s} installed")
|
|
219
|
+
except ImportError:
|
|
220
|
+
print(f" {pkg:20s} not installed")
|
|
221
|
+
|
|
222
|
+
print()
|
|
223
|
+
return 0
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
if __name__ == "__main__":
|
|
227
|
+
import sys
|
|
228
|
+
|
|
229
|
+
sys.exit(main())
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import yaml
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _config_dir() -> Path:
|
|
10
|
+
xdg = os.environ.get("XDG_CONFIG_HOME")
|
|
11
|
+
if xdg:
|
|
12
|
+
return Path(xdg) / "vox4ai"
|
|
13
|
+
return Path.home() / ".config" / "vox4ai"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _config_path() -> Path:
|
|
17
|
+
return _config_dir() / "config.yaml"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
_DEFAULTS: dict[str, Any] = {
|
|
21
|
+
"engine": "",
|
|
22
|
+
"model": "",
|
|
23
|
+
"server_url": "",
|
|
24
|
+
"style_id": None,
|
|
25
|
+
"speed": 1.0,
|
|
26
|
+
"volume": None,
|
|
27
|
+
"pitch": None,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def defaults() -> dict[str, Any]:
|
|
32
|
+
return dict(_DEFAULTS)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def load() -> dict[str, Any]:
|
|
36
|
+
"""Load ~/.config/vox4ai/config.yaml, falling back to defaults."""
|
|
37
|
+
cfg = defaults()
|
|
38
|
+
path = _config_path()
|
|
39
|
+
if not path.exists():
|
|
40
|
+
return cfg
|
|
41
|
+
try:
|
|
42
|
+
raw = yaml.safe_load(path.read_text())
|
|
43
|
+
if isinstance(raw, dict):
|
|
44
|
+
for k in cfg:
|
|
45
|
+
if k in raw:
|
|
46
|
+
cfg[k] = raw[k]
|
|
47
|
+
except Exception:
|
|
48
|
+
pass
|
|
49
|
+
return cfg
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def show() -> str:
|
|
53
|
+
path = _config_path()
|
|
54
|
+
if not path.exists():
|
|
55
|
+
return "(no config file)"
|
|
56
|
+
return path.read_text().strip() or "(empty)"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def merge_cli(cfg: dict[str, Any], args: argparse.Namespace) -> dict[str, Any]:
|
|
60
|
+
"""CLI args override config values. Returns merged copy."""
|
|
61
|
+
merged = dict(cfg)
|
|
62
|
+
cli_map = {
|
|
63
|
+
"engine": "engine",
|
|
64
|
+
"model": "model",
|
|
65
|
+
"server_url": "server_url",
|
|
66
|
+
"style_id": "style_id",
|
|
67
|
+
"speed": "speed",
|
|
68
|
+
"volume": "volume",
|
|
69
|
+
"pitch": "pitch",
|
|
70
|
+
}
|
|
71
|
+
for cli_attr, cfg_key in cli_map.items():
|
|
72
|
+
val = getattr(args, cli_attr, None)
|
|
73
|
+
if val is not None and val != "":
|
|
74
|
+
merged[cfg_key] = val
|
|
75
|
+
return merged
|