75 lines
2.8 KiB
Python

# LIB · TTS 配音模块
# D180 · 2026-07-10
"""Edge-TTS 配音引擎 · 多角色音色"""
import asyncio, edge_tts, os
# 角色音色表
VOICES = {
"林昊": "zh-CN-YunxiNeural", # 青年男声·迷茫虚弱
"旁白": "zh-CN-YunyangNeural", # 新闻男声
"默认女": "zh-CN-XiaoxiaoNeural", # 女声
"默认男": "zh-CN-YunxiNeural",
}
FFMPEG = os.path.expanduser("~/.workbuddy/binaries/ffmpeg.exe")
def tts(text, output_path, voice="zh-CN-YunxiNeural", rate="+0%"):
"""生成语音 mp3"""
os.makedirs(os.path.dirname(output_path) or ".", exist_ok=True)
async def _run():
await edge_tts.Communicate(text, voice, rate=rate).save(output_path)
asyncio.run(_run())
return output_path if os.path.exists(output_path) else None
def tts_for_character(text, character, output_path, rate="+0%"):
"""按角色名查音色生成"""
voice = VOICES.get(character, "zh-CN-YunxiNeural")
return tts(text, output_path, voice, rate)
def mix_audio_video(video_path, audio_path, output_path, video_vol=0.3):
"""FFmpeg 合成视频+音频"""
if not os.path.exists(FFMPEG):
return f"ffmpeg not found at {FFMPEG}"
os.makedirs(os.path.dirname(output_path) or ".", exist_ok=True)
import subprocess
r = subprocess.run([
FFMPEG, "-y",
"-i", video_path,
"-i", audio_path,
"-filter_complex", f"[0:a]volume={video_vol}[va];[1:a]volume=1.0[aa];[va][aa]amix=inputs=2:duration=first",
"-c:v", "copy",
output_path
], capture_output=True, text=True)
return output_path if r.returncode == 0 else r.stderr
def add_subtitle(video_path, subtitle_text, output_path):
"""FFmpeg 添加字幕"""
if not os.path.exists(FFMPEG):
return f"ffmpeg not found at {FFMPEG}"
import subprocess
r = subprocess.run([
FFMPEG, "-y",
"-i", video_path,
"-vf", f"drawtext=text='{subtitle_text}':fontsize=28:fontcolor=white:borderw=2:bordercolor=black:x=(w-text_w)/2:y=h-120",
"-c:a", "copy",
output_path
], capture_output=True, text=True)
return output_path if r.returncode == 0 else r.stderr
if __name__ == "__main__":
import sys
if len(sys.argv) < 2:
print("Usage: python tts.py tts <text> <output> [voice]")
print(" python tts.py char <text> <character> <output>")
print(" python tts.py mix <video> <audio> <output>")
print(f"\nVoices: {list(VOICES.keys())}")
sys.exit(1)
cmd = sys.argv[1]
if cmd == "tts":
voice = sys.argv[4] if len(sys.argv) > 4 else "zh-CN-YunxiNeural"
print(tts(sys.argv[2], sys.argv[3], voice) or "FAILED")
elif cmd == "char":
print(tts_for_character(sys.argv[2], sys.argv[3], sys.argv[4]) or "FAILED")
elif cmd == "mix":
print(mix_audio_video(sys.argv[2], sys.argv[3], sys.argv[4]) or "FAILED")