修复响应指令语音发送格式误判导致ffmpeg解码失败

1. 语音发送逻辑改为优先按文件后缀推断格式,参考message_push.py的稳定实现。

2. 新增语音发送兜底重试机制:首选格式失败后自动尝试mp3/wav/amr。

3. 增加详细日志,便于排查配置格式与文件真实格式不一致问题。
This commit is contained in:
liuwei
2026-04-23 14:12:30 +08:00
parent 37d6c36e2c
commit 76f2124765

View File

@@ -208,6 +208,70 @@ class FunCommandPlayPlugin(MessagePluginInterface):
self._put_media_bytes_to_cache(cache_key, payload)
return payload
@staticmethod
def _infer_voice_format_by_path(voice_path: str, configured_format: str) -> str:
"""根据语音文件路径推断发送格式。
设计说明:
1. 参考 message_push.py 的既有稳定逻辑:优先按文件后缀判断格式。
2. 若后缀可识别wav/mp3/amr直接使用后缀避免“配置格式与真实文件不一致”导致解码失败。
3. 若后缀不可识别,再回退到配置值;配置也无效时最终默认 mp3。
"""
suffix = Path(str(voice_path or "")).suffix.lower().strip()
if suffix == ".wav":
return "wav"
if suffix == ".amr":
return "amr"
if suffix == ".mp3":
return "mp3"
normalized_cfg = str(configured_format or "").strip().lower()
if normalized_cfg in {"wav", "mp3", "amr"}:
return normalized_cfg
return "mp3"
async def _send_voice_with_fallback(
self,
bot: WechatAPIClient,
target_id: str,
voice_bytes: bytes,
voice_path: str,
configured_format: str,
) -> None:
"""发送语音并进行格式兜底重试。
背景:
- 在线上规则维护中,常见问题是“文件实际是 wav但配置里误写 mp3”
这会触发 ffmpeg 解码失败Header missing
策略:
1. 首次发送:使用“后缀优先”推断格式(与 message_push.py 保持一致)。
2. 失败后自动尝试其它格式mp3/wav/amr提高容错性降低人工维护成本。
"""
first_try = self._infer_voice_format_by_path(voice_path=voice_path, configured_format=configured_format)
candidates: List[str] = [first_try]
for fallback in ("mp3", "wav", "amr"):
if fallback not in candidates:
candidates.append(fallback)
last_error: Optional[Exception] = None
for fmt in candidates:
try:
await bot.send_voice_message(target_id, voice_bytes, fmt)
if fmt != first_try:
self.LOG.warning(
f"[{self.name}] 语音发送触发格式兜底重试成功: path={voice_path}, first={first_try}, final={fmt}"
)
return
except Exception as e:
last_error = e
self.LOG.warning(
f"[{self.name}] 语音发送失败,尝试下一个格式: path={voice_path}, format={fmt}, error={e}"
)
if last_error:
raise last_error
def start(self) -> bool:
self.status = PluginStatus.RUNNING
self.LOG.info(f"[{self.name}] 插件已启动")
@@ -425,10 +489,13 @@ class FunCommandPlayPlugin(MessagePluginInterface):
voice_format = str(action.get("format", "") or "").strip().lower()
voice_bytes = self._load_media_bytes(media_kind="voice", media_path=voice_path)
if voice_bytes:
if not voice_format:
suffix = Path(voice_path).suffix.lower()
voice_format = "wav" if suffix == ".wav" else "mp3"
await bot.send_voice_message(target_id, voice_bytes, voice_format)
await self._send_voice_with_fallback(
bot=bot,
target_id=target_id,
voice_bytes=voice_bytes,
voice_path=voice_path,
configured_format=voice_format,
)
return
if action_type == "video":