Skip to content

Commit ee78ac7

Browse files
committed
修复
- 修复 学生名单导出,修复导出失败时**AttributeError**报错 - 修复 TTS语音引擎,优化初始化失败的**异常处理**逻辑 - 修复 音乐播放功能,修复非随机播放时**UnboundLocalError**报错 - 修复 语音播放队列,优化队列已满时的**日志级别** - 修复 语音生成功能,优化网络错误重试时的**日志级别** - 修复 设置文件读取,修复空文件**JSON解析**错误 - 修复 图标加载功能,修复空文件**图标映射**错误
1 parent dbca363 commit ee78ac7

11 files changed

Lines changed: 102 additions & 49 deletions

File tree

CHANGELOG/v2.2.1/CHANGELOG.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,17 @@ v2.0 - Koharu(小鸟游星野) release 4
77

88
## 💡 功能优化
99

10-
-
10+
- 优化 Sentry错误上报,过滤**第三方库**无效错误
1111

1212
## 🐛 修复问题
1313

14-
- 修复 **日志等级** 问题,防止 Sentry 爆炸。
14+
- 修复 学生名单导出,修复导出失败时**AttributeError**报错
15+
- 修复 TTS语音引擎,优化初始化失败的**异常处理**逻辑
16+
- 修复 音乐播放功能,修复非随机播放时**UnboundLocalError**报错
17+
- 修复 语音播放队列,优化队列已满时的**日志级别**
18+
- 修复 语音生成功能,优化网络错误重试时的**日志级别**
19+
- 修复 设置文件读取,修复空文件**JSON解析**错误
20+
- 修复 图标加载功能,修复空文件**图标映射**错误
1521

1622
## 🔧 其它变更
1723

app/common/display/result_display.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,9 @@ def create_student_label(
460460
"""
461461
# 检查 selected_students 是否为 None,避免后续的 len() 调用和迭代操作失败
462462
if selected_students is None:
463-
logger.warning("create_student_label: selected_students 为 None,可能是未设置默认班级或抽取名单")
463+
logger.warning(
464+
"create_student_label: selected_students 为 None,可能是未设置默认班级或抽取名单"
465+
)
464466
return []
465467

466468
student_labels = []

app/common/music/music_player.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ def play_music(
7070

7171
# 获取音乐文件路径
7272
try:
73+
from app.tools.path_utils import get_audio_path
74+
7375
if is_random_play:
7476
# 随机播放:从音乐文件列表中随机选择一个
75-
from app.tools.path_utils import get_audio_path
76-
7777
audio_dir = get_audio_path("music")
7878
if audio_dir.exists():
7979
supported_formats = [".mp3", ".flac", ".wav", ".ogg"]

app/common/voice/voice.py

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def wrapper(self, *args, **kwargs):
4848
# 示例:检查当前用户是否有使用TTS的权限
4949
has_perm = True # 实际实现时替换为真实权限检查
5050
if not has_perm:
51-
logger.exception(f"权限不足,无法执行 {func.__name__}")
51+
logger.warning(f"权限不足,无法执行 {func.__name__}")
5252
return
5353
return func(self, *args, **kwargs)
5454

@@ -78,15 +78,15 @@ def set_volume(self, volume: float) -> None:
7878
"""设置播放音量,范围0.0-1.0"""
7979
# 输入验证
8080
if not isinstance(volume, (int, float)):
81-
logger.exception(f"无效的音量值: {volume}")
81+
logger.warning(f"无效的音量值: {volume}")
8282
return
8383
self._volume = max(0.0, min(1.0, float(volume)))
8484

8585
def set_speed(self, speed: int) -> None:
8686
"""设置播放语速,范围0-200"""
8787
# 输入验证
8888
if not isinstance(speed, int):
89-
logger.exception(f"无效的语速值: {speed}")
89+
logger.warning(f"无效的语速值: {speed}")
9090
return
9191
self._speed = max(0, min(200, speed))
9292

@@ -240,21 +240,21 @@ def add_task(self, task: Union[Tuple[np.ndarray, int], str]) -> bool:
240240
# 输入验证
241241
if isinstance(task, tuple): # 内存数据
242242
if len(task) != 2:
243-
logger.exception(f"无效的任务格式: {task}")
243+
logger.warning(f"无效的任务格式: {task}")
244244
return False
245245
data, fs = task
246246
if not isinstance(data, np.ndarray) or not isinstance(fs, int):
247-
logger.exception("无效的内存数据格式")
247+
logger.warning("无效的内存数据格式")
248248
return False
249249
else: # 文件路径
250250
if not isinstance(task, str) or not task:
251-
logger.exception("无效的文件路径")
251+
logger.warning("无效的文件路径")
252252
return False
253253

254254
self.play_queue.put_nowait(task)
255255
return True
256256
except queue.Full:
257-
logger.exception("播放队列已满,丢弃新任务")
257+
logger.warning("播放队列已满,丢弃新任务")
258258
return False
259259
except Exception as e:
260260
logger.exception(f"添加播放任务失败: {e}")
@@ -314,10 +314,10 @@ def get_voice(self, text: str, voice: str) -> Tuple[np.ndarray, int]:
314314
"""获取语音数据(自动缓存)"""
315315
# 输入验证
316316
if not isinstance(text, str) or not text:
317-
logger.exception(f"无效的文本: {text}")
317+
logger.warning(f"无效的文本: {text}")
318318
raise ValueError("文本不能为空")
319319
if not isinstance(voice, str) or not voice:
320-
logger.exception(f"无效的语音名称: {voice}")
320+
logger.warning(f"无效的语音名称: {voice}")
321321
raise ValueError("语音名称不能为空")
322322

323323
logger.debug(f"获取语音: text='{text}', voice='{voice}'")
@@ -396,28 +396,28 @@ async def _generate_voice(self, text: str, voice: str) -> Tuple[np.ndarray, int]
396396
return data, fs
397397
except NoAudioReceived as e:
398398
retry_count += 1
399-
logger.exception(
399+
logger.warning(
400400
f"生成语音失败,未接收到音频数据,重试{retry_count}/{max_retries}: {type(e).__name__} {e}"
401401
)
402402
if retry_count < max_retries:
403403
await asyncio.sleep(1)
404404
except WebSocketError as e:
405405
retry_count += 1
406-
logger.exception(
406+
logger.warning(
407407
f"生成语音失败,WebSocket通信错误,重试{retry_count}/{max_retries}: {type(e).__name__} {e}"
408408
)
409409
if retry_count < max_retries:
410410
await asyncio.sleep(1)
411411
except Exception as e:
412412
retry_count += 1
413-
logger.exception(
413+
logger.warning(
414414
f"生成语音失败,重试{retry_count}/{max_retries}: {type(e).__name__} {e}"
415415
)
416416
if retry_count < max_retries:
417417
await asyncio.sleep(1)
418418

419419
# 最终失败时的降级处理
420-
logger.exception("生成语音失败,已达到最大重试次数")
420+
logger.warning("生成语音失败,已达到最大重试次数")
421421
raise RuntimeError("生成语音失败")
422422

423423
def _generate_cache_key(self, text: str, voice: str) -> str:
@@ -644,23 +644,30 @@ def _init_tts_engine(self) -> None:
644644
and sys.getwindowsversion().major >= 10
645645
and platform.machine() != "x86"
646646
):
647-
if not hasattr(
648-
QApplication.instance(), "pumping_reward_voice_engine"
649-
):
650-
QApplication.instance().pumping_reward_voice_engine = (
651-
pyttsx3.init()
647+
try:
648+
if not hasattr(
649+
QApplication.instance(), "pumping_reward_voice_engine"
650+
):
651+
QApplication.instance().pumping_reward_voice_engine = (
652+
pyttsx3.init()
653+
)
654+
QApplication.instance().pumping_reward_voice_engine.startLoop(
655+
False
656+
)
657+
self.voice_engine = (
658+
QApplication.instance().pumping_reward_voice_engine
652659
)
653-
QApplication.instance().pumping_reward_voice_engine.startLoop(
654-
False
660+
logger.info("Windows系统TTS引擎初始化成功")
661+
except Exception as e:
662+
logger.warning(
663+
f"Windows系统TTS引擎初始化失败: {e},语音功能将不可用"
655664
)
656-
self.voice_engine = (
657-
QApplication.instance().pumping_reward_voice_engine
658-
)
659-
logger.info("Windows系统TTS引擎初始化成功")
665+
self.voice_engine = None
660666
else:
661-
logger.exception(
667+
logger.warning(
662668
"Windows系统TTS引擎需要Windows 10及以上系统且非x86架构"
663669
)
670+
self.voice_engine = None
664671

665672
elif system == "Linux":
666673
# Linux平台TTS引擎初始化
@@ -686,17 +693,20 @@ def _init_tts_engine(self) -> None:
686693
)
687694
logger.info("Linux系统TTS引擎初始化成功 (使用espeak)")
688695
else:
689-
logger.exception(
696+
logger.warning(
690697
"Linux系统TTS引擎需要安装espeak: sudo apt-get install espeak"
691698
)
699+
self.voice_engine = None
692700
except Exception as e:
693-
logger.exception(f"Linux系统TTS引擎初始化失败: {e}")
701+
logger.warning(f"Linux系统TTS引擎初始化失败: {e},语音功能将不可用")
702+
self.voice_engine = None
694703

695704
else:
696-
logger.exception(f"不支持的操作系统: {system},系统TTS功能不可用")
705+
logger.warning(f"不支持的操作系统: {system},系统TTS功能不可用")
706+
self.voice_engine = None
697707

698708
except Exception as e:
699-
logger.exception(f"TTS引擎初始化失败: {e}")
709+
logger.warning(f"TTS引擎初始化失败: {e},语音功能将不可用")
700710
self.voice_engine = None
701711

702712
@require_permission("tts.use")

app/tools/personalised.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,12 @@ def _get_icon_map():
183183
try:
184184
map_path = get_data_path("assets", "FluentSystemIcons-Filled.json")
185185
with open(map_path, "r", encoding="utf-8") as f:
186-
_icon_map_cache = json.load(f)
186+
content = f.read()
187+
if not content or not content.strip():
188+
logger.warning(f"图标映射表文件为空: {map_path}")
189+
_icon_map_cache = {}
190+
else:
191+
_icon_map_cache = json.loads(content)
187192
except Exception as e:
188193
logger.exception(f"加载图标映射表失败: {e}")
189194
_icon_map_cache = {}

app/tools/settings_access.py

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -145,14 +145,18 @@ def readme_settings(first_level_key: str, second_level_key: str):
145145
settings_path = get_settings_path()
146146
if file_exists(settings_path):
147147
with open_file(settings_path, "r", encoding="utf-8") as f:
148-
settings_data = json.load(f)
149-
if (
150-
first_level_key in settings_data
151-
and second_level_key in settings_data[first_level_key]
152-
):
153-
value = settings_data[first_level_key][second_level_key]
154-
# logger.debug(f"从设置文件读取: {first_level_key}.{second_level_key} = {value}")
155-
return value
148+
content = f.read()
149+
if not content or not content.strip():
150+
logger.warning(f"设置文件为空: {settings_path}")
151+
else:
152+
settings_data = json.loads(content)
153+
if (
154+
first_level_key in settings_data
155+
and second_level_key in settings_data[first_level_key]
156+
):
157+
value = settings_data[first_level_key][second_level_key]
158+
# logger.debug(f"从设置文件读取: {first_level_key}.{second_level_key} = {value}")
159+
return value
156160

157161
default_setting = _get_default_setting(first_level_key, second_level_key)
158162
if isinstance(default_setting, dict) and "default_value" in default_setting:

app/tools/variable.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
# -------------------- 软件基本信息 --------------------
1010
APPLY_NAME = "SecRandom" # 软件名称
1111
VERSION = "v0.0.0" # 软件当前版本
12-
NEXT_VERSION = "v2.2.0" # 软件下一个版本
12+
NEXT_VERSION = "v2.2.1" # 软件下一个版本
1313
CODENAME = "Koharu" # 软件代号
1414
SPECIAL_VERSION = VERSION if VERSION != "v0.0.0" else NEXT_VERSION
1515
SYSTEM = "windows" if os.name == "nt" else "linux" # 软件系统

app/view/main/quick_draw_animation.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,9 @@ def draw_random_students(self):
172172
class_name = readme_settings_async("quick_draw_settings", "default_class")
173173
if not class_name:
174174
# 未设置默认班级,初始化为空结果并停止动画
175-
logger.warning("draw_random_students: 未设置默认抽取名单,请在设置中配置默认班级")
175+
logger.warning(
176+
"draw_random_students: 未设置默认抽取名单,请在设置中配置默认班级"
177+
)
176178
self.final_selected_students = []
177179
self.final_class_name = None
178180
# 停止动画计时器

app/view/settings/list_management/list_management.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,13 +340,15 @@ def export_student_list(self):
340340
"export",
341341
"title",
342342
"success",
343+
"name",
343344
),
344345
content=get_any_position_value_async(
345346
"notification",
346347
"roll_call",
347348
"export",
348349
"content",
349350
"success",
351+
"name",
350352
).format(path=file_path),
351353
duration=3000,
352354
)
@@ -360,13 +362,15 @@ def export_student_list(self):
360362
"export",
361363
"title",
362364
"failure",
365+
"name",
363366
),
364367
content=get_any_position_value_async(
365368
"notification",
366369
"roll_call",
367370
"export",
368371
"content",
369372
"error",
373+
"name",
370374
).format(message=message),
371375
duration=3000,
372376
)

app/view/settings/update.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -998,8 +998,6 @@ def set_download_cancelled(self):
998998
Q_ARG(str, get_content_name_async("update", "update_cancelled")),
999999
)
10001000

1001-
1002-
10031001
def _restore_from_global_status(self):
10041002
"""从全局状态管理器恢复状态"""
10051003
try:

0 commit comments

Comments
 (0)