@rookiestar/eng-lang-tutor 1.1.3 → 1.1.5
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.
Potentially problematic release.
This version of @rookiestar/eng-lang-tutor might be problematic. Click here for more details.
- package/README.md +20 -2
- package/README_EN.md +20 -2
- package/package.json +1 -1
- package/scripts/audio/tts/manager.py +13 -10
- package/scripts/audio/tts/providers/__init__.py +2 -0
- package/scripts/audio/tts/providers/edge.py +111 -0
package/README.md
CHANGED
|
@@ -103,7 +103,16 @@ openclaw pairing approve discord YOUR_PAIRING_CODE
|
|
|
103
103
|
3. 选择导师风格(幽默/严谨/随意/专业)
|
|
104
104
|
4. 设置口语/书面语比例
|
|
105
105
|
5. 配置推送时间(知识点和测验时间)
|
|
106
|
-
6.
|
|
106
|
+
6. **语音教学配置** - 选择是否启用语音版知识点
|
|
107
|
+
- 如启用,选择语速(0.5-1.7,默认 0.9)
|
|
108
|
+
- 默认使用 Edge-TTS(免费,无需配置)
|
|
109
|
+
- 如需使用讯飞,请先在服务器上设置环境变量:
|
|
110
|
+
```bash
|
|
111
|
+
export TTS_PROVIDER=xunfei
|
|
112
|
+
export XUNFEI_APPID=your_appid
|
|
113
|
+
export XUNFEI_API_KEY=your_api_key
|
|
114
|
+
export XUNFEI_API_SECRET=your_api_secret
|
|
115
|
+
```
|
|
107
116
|
7. 确认您的设置并创建定时任务
|
|
108
117
|
|
|
109
118
|
## TTS 语音配置
|
|
@@ -255,7 +264,16 @@ eng-lang-tutor/
|
|
|
255
264
|
│ ├── constants.py # 共享常量(等级阈值)
|
|
256
265
|
│ ├── utils.py # 工具函数(安全除法、深度合并)
|
|
257
266
|
│ ├── cli.py # CLI 入口点
|
|
258
|
-
│ └──
|
|
267
|
+
│ └── audio/ # 音频模块
|
|
268
|
+
│ ├── tts/ # TTS 语音合成
|
|
269
|
+
│ │ ├── base.py # TTS 抽象基类
|
|
270
|
+
│ │ ├── manager.py # TTS 管理器
|
|
271
|
+
│ │ └── providers/ # TTS 提供者
|
|
272
|
+
│ │ ├── edge.py # Edge-TTS (默认)
|
|
273
|
+
│ │ └── xunfei.py # 讯飞 TTS
|
|
274
|
+
│ ├── composer.py # 音频合成
|
|
275
|
+
│ ├── converter.py # 格式转换
|
|
276
|
+
│ └── feishu_voice.py # 飞书语音发送
|
|
259
277
|
├── templates/
|
|
260
278
|
│ ├── state_schema.json # 状态 JSON Schema
|
|
261
279
|
│ ├── keypoint_schema.json # 知识点 JSON Schema
|
package/README_EN.md
CHANGED
|
@@ -103,7 +103,16 @@ When you first interact with the bot, it will guide you through a 7-step onboard
|
|
|
103
103
|
3. Select tutor style (humorous/rigorous/casual/professional)
|
|
104
104
|
4. Set oral vs written focus
|
|
105
105
|
5. Configure schedule (keypoint and quiz times)
|
|
106
|
-
6. Choose whether to enable
|
|
106
|
+
6. **Voice Teaching Configuration** - Choose whether to enable audio keypoints
|
|
107
|
+
- If enabled, select speech speed (0.5-1.7, default 0.9)
|
|
108
|
+
- Edge-TTS is used by default (free, no configuration needed)
|
|
109
|
+
- To use XunFei, set environment variables on your server first:
|
|
110
|
+
```bash
|
|
111
|
+
export TTS_PROVIDER=xunfei
|
|
112
|
+
export XUNFEI_APPID=your_appid
|
|
113
|
+
export XUNFEI_API_KEY=your_api_key
|
|
114
|
+
export XUNFEI_API_SECRET=your_api_secret
|
|
115
|
+
```
|
|
107
116
|
7. Confirm your settings and create cron jobs
|
|
108
117
|
|
|
109
118
|
## TTS Voice Configuration
|
|
@@ -255,7 +264,16 @@ eng-lang-tutor/
|
|
|
255
264
|
│ ├── constants.py # Shared constants (level thresholds)
|
|
256
265
|
│ ├── utils.py # Utility functions (safe divide, deep merge)
|
|
257
266
|
│ ├── cli.py # CLI entry point
|
|
258
|
-
│ └──
|
|
267
|
+
│ └── audio/ # Audio module
|
|
268
|
+
│ ├── tts/ # TTS voice synthesis
|
|
269
|
+
│ │ ├── base.py # TTS abstract base class
|
|
270
|
+
│ │ ├── manager.py # TTS manager
|
|
271
|
+
│ │ └── providers/ # TTS providers
|
|
272
|
+
│ │ ├── edge.py # Edge-TTS (default)
|
|
273
|
+
│ │ └── xunfei.py # XunFei TTS
|
|
274
|
+
│ ├── composer.py # Audio composition
|
|
275
|
+
│ ├── converter.py # Format conversion
|
|
276
|
+
│ └── feishu_voice.py # Feishu voice sender
|
|
259
277
|
├── templates/
|
|
260
278
|
│ ├── state_schema.json # State JSON Schema
|
|
261
279
|
│ ├── keypoint_schema.json # Keypoint JSON Schema
|
package/package.json
CHANGED
|
@@ -10,7 +10,7 @@ TTS 管理器 - 通用入口,支持多 Provider
|
|
|
10
10
|
|
|
11
11
|
# 方式 2:直接传入讯飞密钥
|
|
12
12
|
manager = TTSManager(
|
|
13
|
-
provider="
|
|
13
|
+
provider="edge-tts",
|
|
14
14
|
appid="xxx",
|
|
15
15
|
api_key="xxx",
|
|
16
16
|
api_secret="xxx"
|
|
@@ -33,6 +33,7 @@ import sys
|
|
|
33
33
|
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
|
34
34
|
|
|
35
35
|
from .base import TTSProvider, TTSConfig, TTSResult
|
|
36
|
+
from .providers.edge import EdgeTTSProvider
|
|
36
37
|
from .providers.xunfei import XunFeiProvider
|
|
37
38
|
|
|
38
39
|
try:
|
|
@@ -43,10 +44,8 @@ except ImportError:
|
|
|
43
44
|
|
|
44
45
|
# Provider 注册表
|
|
45
46
|
PROVIDERS: Dict[str, Type[TTSProvider]] = {
|
|
47
|
+
"edge-tts": EdgeTTSProvider,
|
|
46
48
|
"xunfei": XunFeiProvider,
|
|
47
|
-
# 可扩展更多 provider
|
|
48
|
-
# "edge-tts": EdgeTTSProvider,
|
|
49
|
-
# "minimax": MiniMaxProvider,
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
|
|
@@ -65,7 +64,7 @@ class TTSManager:
|
|
|
65
64
|
|
|
66
65
|
def __init__(
|
|
67
66
|
self,
|
|
68
|
-
provider: str = "
|
|
67
|
+
provider: str = "edge-tts",
|
|
69
68
|
data_dir: str = None,
|
|
70
69
|
config: Optional[TTSConfig] = None,
|
|
71
70
|
**credentials
|
|
@@ -74,16 +73,16 @@ class TTSManager:
|
|
|
74
73
|
初始化 TTS 管理器
|
|
75
74
|
|
|
76
75
|
Args:
|
|
77
|
-
provider: Provider
|
|
76
|
+
provider: Provider 名称(支持 "edge-tts" 或 "xunfei")
|
|
78
77
|
data_dir: 数据目录(默认使用 OPENCLAW_STATE_DIR 或 ~/.openclaw/state/eng-lang-tutor/)
|
|
79
78
|
config: TTS 配置
|
|
80
79
|
**credentials: Provider 认证信息
|
|
81
80
|
|
|
82
81
|
示例:
|
|
83
|
-
#
|
|
84
|
-
manager = TTSManager(provider="
|
|
82
|
+
# Edge-TTS(免费,无需密钥)
|
|
83
|
+
manager = TTSManager(provider="edge-tts")
|
|
85
84
|
|
|
86
|
-
#
|
|
85
|
+
# 讯飞(国内稳定)
|
|
87
86
|
manager = TTSManager(
|
|
88
87
|
provider="xunfei",
|
|
89
88
|
appid="xxx",
|
|
@@ -121,6 +120,10 @@ class TTSManager:
|
|
|
121
120
|
从环境变量创建 TTS 管理器
|
|
122
121
|
|
|
123
122
|
环境变量格式:
|
|
123
|
+
# Edge-TTS(默认,免费无需配置)
|
|
124
|
+
TTS_PROVIDER=edge-tts
|
|
125
|
+
|
|
126
|
+
# 讯飞(需配置密钥)
|
|
124
127
|
TTS_PROVIDER=xunfei
|
|
125
128
|
XUNFEI_APPID=xxx
|
|
126
129
|
XUNFEI_API_KEY=xxx
|
|
@@ -133,7 +136,7 @@ class TTSManager:
|
|
|
133
136
|
Returns:
|
|
134
137
|
TTSManager 实例
|
|
135
138
|
"""
|
|
136
|
-
provider = provider or os.getenv("TTS_PROVIDER", "
|
|
139
|
+
provider = provider or os.getenv("TTS_PROVIDER", "edge-tts")
|
|
137
140
|
return cls(provider=provider, **kwargs)
|
|
138
141
|
|
|
139
142
|
def switch_provider(self, provider: str, **credentials) -> None:
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Edge-TTS Provider 实现
|
|
4
|
+
|
|
5
|
+
Microsoft Edge TTS 服务:
|
|
6
|
+
- 完全免费,无需 API 密钥
|
|
7
|
+
- 高质量 24kHz 神经语音
|
|
8
|
+
- 支持多种美式英语发音人
|
|
9
|
+
- 国内网络可能需要代理
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
import edge_tts
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Optional, ClassVar, Dict
|
|
16
|
+
|
|
17
|
+
from ..base import TTSProvider, TTSConfig, TTSResult
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class EdgeTTSProvider(TTSProvider):
|
|
21
|
+
"""
|
|
22
|
+
Microsoft Edge TTS Provider
|
|
23
|
+
|
|
24
|
+
支持的美式英语发音人:
|
|
25
|
+
- en-US-JennyNeural: 女声,友好亲切(推荐)
|
|
26
|
+
- en-US-AriaNeural: 女声,自信清晰
|
|
27
|
+
- en-US-EricNeural: 男声,专业理性(推荐)
|
|
28
|
+
- en-US-GuyNeural: 男声,热情活力
|
|
29
|
+
- en-US-AnaNeural: 女声,可爱随和
|
|
30
|
+
- en-US-ChristopherNeural: 男声,权威可靠
|
|
31
|
+
|
|
32
|
+
无需认证信息,直接使用。
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
PROVIDER_NAME: ClassVar[str] = "edge-tts"
|
|
36
|
+
DEFAULT_FEMALE_VOICE: ClassVar[str] = "en-US-JennyNeural" # 友好亲切
|
|
37
|
+
DEFAULT_MALE_VOICE: ClassVar[str] = "en-US-EricNeural" # 专业理性
|
|
38
|
+
# 角色音色映射:旁白-女声,对话A-男声,对话B-女声
|
|
39
|
+
DEFAULT_NARRATOR_VOICE: ClassVar[str] = "en-US-JennyNeural" # 旁白 - 女声
|
|
40
|
+
DEFAULT_DIALOGUE_A_VOICE: ClassVar[str] = "en-US-EricNeural" # 对话 A - 男声
|
|
41
|
+
DEFAULT_DIALOGUE_B_VOICE: ClassVar[str] = "en-US-JennyNeural" # 对话 B - 女声
|
|
42
|
+
|
|
43
|
+
SUPPORTED_VOICES: ClassVar[Dict[str, str]] = {
|
|
44
|
+
"en-US-JennyNeural": "美式英语女声,友好亲切(推荐)",
|
|
45
|
+
"en-US-AriaNeural": "美式英语女声,自信清晰",
|
|
46
|
+
"en-US-EricNeural": "美式英语男声,专业理性(推荐)",
|
|
47
|
+
"en-US-GuyNeural": "美式英语男声,热情活力",
|
|
48
|
+
"en-US-AnaNeural": "美式英语女声,可爱随和",
|
|
49
|
+
"en-US-ChristopherNeural": "美式英语男声,权威可靠",
|
|
50
|
+
"en-US-MichelleNeural": "美式英语女声,友好舒适",
|
|
51
|
+
"en-US-RogerNeural": "美式英语男声,生动活泼",
|
|
52
|
+
"en-US-AndrewNeural": "美式英语男声,友好积极",
|
|
53
|
+
"en-US-BrianNeural": "美式英语男声,友好积极",
|
|
54
|
+
"en-US-EmmaNeural": "美式英语女声,友好积极",
|
|
55
|
+
"en-US-AvaNeural": "美式英语女声,友好积极",
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
def _validate_credentials(self) -> None:
|
|
59
|
+
"""
|
|
60
|
+
验证认证信息
|
|
61
|
+
|
|
62
|
+
Edge-TTS 不需要认证信息,直接通过。
|
|
63
|
+
"""
|
|
64
|
+
# Edge-TTS 不需要任何认证信息
|
|
65
|
+
pass
|
|
66
|
+
|
|
67
|
+
def synthesize(
|
|
68
|
+
self,
|
|
69
|
+
text: str,
|
|
70
|
+
output_path: Path,
|
|
71
|
+
voice: Optional[str] = None,
|
|
72
|
+
speed: Optional[float] = None
|
|
73
|
+
) -> TTSResult:
|
|
74
|
+
"""
|
|
75
|
+
合成语音
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
text: 要合成的文本
|
|
79
|
+
output_path: 输出文件路径(.mp3)
|
|
80
|
+
voice: 语音 ID(可选,默认使用女声)
|
|
81
|
+
speed: 语速(可选,0.5-2.0,1.0 = 正常)
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
TTSResult: 合成结果
|
|
85
|
+
"""
|
|
86
|
+
voice = voice or self.get_voice("female")
|
|
87
|
+
speed_val = speed or self.config.speed
|
|
88
|
+
|
|
89
|
+
# 将 speed (0.5-2.0) 转换为 edge-tts 的 rate 格式
|
|
90
|
+
# speed=1.0 -> rate="+0%"
|
|
91
|
+
# speed=0.7 -> rate="-30%" (更慢,适合学习)
|
|
92
|
+
# speed=1.5 -> rate="+50%" (更快)
|
|
93
|
+
rate_percent = int((speed_val - 1.0) * 100)
|
|
94
|
+
rate = f"{rate_percent:+d}%"
|
|
95
|
+
|
|
96
|
+
# 确保输出目录存在
|
|
97
|
+
output_path = Path(output_path)
|
|
98
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
99
|
+
|
|
100
|
+
async def _synthesize_async():
|
|
101
|
+
"""异步合成语音"""
|
|
102
|
+
communicate = edge_tts.Communicate(text, voice, rate=rate)
|
|
103
|
+
await communicate.save(str(output_path))
|
|
104
|
+
|
|
105
|
+
try:
|
|
106
|
+
# 在同步上下文中运行异步代码
|
|
107
|
+
asyncio.run(_synthesize_async())
|
|
108
|
+
return TTSResult(success=True, audio_path=output_path)
|
|
109
|
+
|
|
110
|
+
except Exception as e:
|
|
111
|
+
return TTSResult(success=False, error_message=str(e))
|