|
| 1 | +# ================================================== |
| 2 | +# 导入库 |
| 3 | +# ================================================== |
| 4 | +import asyncio |
| 5 | +import edge_tts |
| 6 | +from loguru import logger |
| 7 | +from PySide6.QtCore import QThread, Signal |
| 8 | + |
| 9 | + |
| 10 | +class EdgeTTSWorker(QThread): |
| 11 | + """Edge TTS语音列表获取线程""" |
| 12 | + |
| 13 | + voices_fetched = Signal(list) |
| 14 | + error_occurred = Signal(str) |
| 15 | + |
| 16 | + def run(self): |
| 17 | + """运行线程,获取Edge TTS语音列表""" |
| 18 | + try: |
| 19 | + # 获取语音列表 |
| 20 | + voices = self.get_voices() |
| 21 | + self.voices_fetched.emit(voices) |
| 22 | + except Exception as e: |
| 23 | + logger.error(f"获取Edge TTS语音列表失败: {e}") |
| 24 | + self.error_occurred.emit(str(e)) |
| 25 | + |
| 26 | + def get_voices(self): |
| 27 | + """获取Edge TTS语音列表""" |
| 28 | + try: |
| 29 | + # 同步获取语音列表 |
| 30 | + loop = asyncio.new_event_loop() |
| 31 | + asyncio.set_event_loop(loop) |
| 32 | + voices = loop.run_until_complete(edge_tts.list_voices()) |
| 33 | + loop.close() |
| 34 | + |
| 35 | + logger.debug(f"Edge TTS API返回原始语音数量: {len(voices)}") |
| 36 | + |
| 37 | + # 调试:打印前几个语音的结构 |
| 38 | + if voices: |
| 39 | + logger.debug(f"第一个语音的结构: {voices[0]}") |
| 40 | + logger.debug(f"第一个语音的键: {list(voices[0].keys())}") |
| 41 | + |
| 42 | + # 简化过滤逻辑 |
| 43 | + filtered_voices = [] |
| 44 | + total_processed = 0 |
| 45 | + passed_filter = 0 |
| 46 | + |
| 47 | + for v in voices: |
| 48 | + try: |
| 49 | + total_processed += 1 |
| 50 | + # 检查必要字段 - 注意:API返回的是Name而不是FriendlyName |
| 51 | + if "Name" in v and "ShortName" in v and "Locale" in v: |
| 52 | + passed_filter += 1 |
| 53 | + name = v["Name"] # 使用Name字段 |
| 54 | + display_name = v.get("DisplayName", name) # 优先使用DisplayName |
| 55 | + short_name = v["ShortName"] |
| 56 | + locale = v["Locale"] |
| 57 | + gender = v.get("Gender", "Unknown") |
| 58 | + voice_type = v.get("VoiceType", "Unknown") |
| 59 | + |
| 60 | + # 直接使用short_name作为ID |
| 61 | + voice_id = short_name |
| 62 | + |
| 63 | + filtered_voices.append( |
| 64 | + { |
| 65 | + "name": display_name, # 使用更友好的显示名称 |
| 66 | + "id": voice_id, |
| 67 | + "languages": locale.replace("_", "-"), |
| 68 | + "full_info": f"{gender} | {locale} | Type: {voice_type}", |
| 69 | + } |
| 70 | + ) |
| 71 | + except Exception as e: |
| 72 | + logger.warning(f"处理语音 {v} 时出错: {e}") |
| 73 | + if total_processed <= 5: # 只显示前5个出错的语音 |
| 74 | + logger.debug(f"出错语音结构: {v}") |
| 75 | + continue |
| 76 | + |
| 77 | + logger.debug(f"成功获取Edge TTS语音列表,共{len(filtered_voices)}个语音") |
| 78 | + logger.debug( |
| 79 | + f"处理统计: 总共{total_processed}个, 通过过滤{passed_filter}个, 最终{len(filtered_voices)}个" |
| 80 | + ) |
| 81 | + |
| 82 | + # 如果过滤后列表为空,返回默认语音列表 |
| 83 | + if not filtered_voices: |
| 84 | + logger.warning("过滤后语音列表为空,返回默认语音") |
| 85 | + return self.get_default_voices() |
| 86 | + |
| 87 | + # 按语言排序,优先显示中文语音 |
| 88 | + filtered_voices.sort( |
| 89 | + key=lambda x: ("zh-CN" not in x["languages"], x["name"]) |
| 90 | + ) |
| 91 | + logger.debug( |
| 92 | + f"语音列表已排序,中文语音优先显示,共{len(filtered_voices)}个语音" |
| 93 | + ) |
| 94 | + |
| 95 | + return filtered_voices |
| 96 | + except Exception as e: |
| 97 | + logger.error(f"获取Edge TTS语音列表失败: {e}") |
| 98 | + # 返回默认语音列表 |
| 99 | + return self.get_default_voices() |
| 100 | + |
| 101 | + def get_default_voices(self): |
| 102 | + """获取默认语音列表""" |
| 103 | + default_voices = [ |
| 104 | + { |
| 105 | + "name": "Xiaoxiao (Chinese Female)", |
| 106 | + "id": "zh-CN-XiaoxiaoNeural", |
| 107 | + "languages": "zh-CN", |
| 108 | + "full_info": "Female | zh-CN | Type: Neural", |
| 109 | + }, |
| 110 | + { |
| 111 | + "name": "Yunxi (Chinese Male)", |
| 112 | + "id": "zh-CN-YunxiNeural", |
| 113 | + "languages": "zh-CN", |
| 114 | + "full_info": "Male | zh-CN | Type: Neural", |
| 115 | + }, |
| 116 | + { |
| 117 | + "name": "Xiaoyi (Chinese Female)", |
| 118 | + "id": "zh-CN-XiaoyiNeural", |
| 119 | + "languages": "zh-CN", |
| 120 | + "full_info": "Female | zh-CN | Type: Neural", |
| 121 | + }, |
| 122 | + { |
| 123 | + "name": "Jenny (English Female)", |
| 124 | + "id": "en-US-JennyNeural", |
| 125 | + "languages": "en-US", |
| 126 | + "full_info": "Female | en-US | Type: Neural", |
| 127 | + }, |
| 128 | + { |
| 129 | + "name": "Guy (English Male)", |
| 130 | + "id": "en-US-GuyNeural", |
| 131 | + "languages": "en-US", |
| 132 | + "full_info": "Male | en-US | Type: Neural", |
| 133 | + }, |
| 134 | + ] |
| 135 | + logger.info("使用默认语音列表") |
| 136 | + return default_voices |
0 commit comments