mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-30 06:41:51 +00:00
fix(discord): recover Windows voice opus decoding
This commit is contained in:
parent
bb65bebed7
commit
ea34925002
2 changed files with 60 additions and 3 deletions
|
|
@ -68,6 +68,26 @@ from gateway.platforms.base import (
|
|||
from tools.url_safety import is_safe_url
|
||||
|
||||
|
||||
def _find_discord_windows_bundled_opus(discord_module: Any = None) -> Optional[str]:
|
||||
"""Return discord.py's bundled Windows opus DLL path when present."""
|
||||
if sys.platform != "win32":
|
||||
return None
|
||||
discord_module = discord if discord_module is None else discord_module
|
||||
if discord_module is None:
|
||||
return None
|
||||
|
||||
opus_module = getattr(discord_module, "opus", None)
|
||||
opus_file = getattr(opus_module, "__file__", None)
|
||||
if not opus_file:
|
||||
return None
|
||||
|
||||
target = "x64" if struct.calcsize("P") * 8 > 32 else "x86"
|
||||
bundled = _Path(opus_file).resolve().parent / "bin" / f"libopus-0.{target}.dll"
|
||||
if bundled.is_file():
|
||||
return str(bundled)
|
||||
return None
|
||||
|
||||
|
||||
def _clean_discord_id(entry: str) -> str:
|
||||
"""Strip common prefixes from a Discord user ID or username entry.
|
||||
|
||||
|
|
@ -403,7 +423,13 @@ class VoiceReceiver:
|
|||
self._buffers[ssrc].extend(pcm)
|
||||
self._last_packet_time[ssrc] = time.monotonic()
|
||||
except Exception as e:
|
||||
logger.debug("Opus decode error for SSRC %s: %s", ssrc, e)
|
||||
with self._lock:
|
||||
self._decoders.pop(ssrc, None)
|
||||
logger.debug(
|
||||
"Opus decode error for SSRC %s; reset decoder: %s",
|
||||
ssrc,
|
||||
e,
|
||||
)
|
||||
return
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
|
@ -604,7 +630,13 @@ class DiscordAdapter(BasePlatformAdapter):
|
|||
# Load opus codec for voice channel support
|
||||
if not discord.opus.is_loaded():
|
||||
import ctypes.util
|
||||
opus_candidates = []
|
||||
bundled_opus = _find_discord_windows_bundled_opus(discord)
|
||||
if bundled_opus:
|
||||
opus_candidates.append(bundled_opus)
|
||||
opus_path = ctypes.util.find_library("opus")
|
||||
if opus_path:
|
||||
opus_candidates.append(opus_path)
|
||||
# ctypes.util.find_library fails on macOS with Homebrew-installed libs,
|
||||
# so fall back to known Homebrew paths if needed.
|
||||
if not opus_path:
|
||||
|
|
@ -615,11 +647,13 @@ class DiscordAdapter(BasePlatformAdapter):
|
|||
if sys.platform == "darwin":
|
||||
for _hp in _homebrew_paths:
|
||||
if os.path.isfile(_hp):
|
||||
opus_path = _hp
|
||||
opus_candidates.append(_hp)
|
||||
break
|
||||
if opus_path:
|
||||
for opus_path in opus_candidates:
|
||||
try:
|
||||
discord.opus.load_opus(opus_path)
|
||||
if discord.opus.is_loaded():
|
||||
break
|
||||
except Exception:
|
||||
logger.warning("Opus codec found at %s but failed to load", opus_path)
|
||||
if not discord.opus.is_loaded():
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"""Tests for Discord Opus codec loading — must use ctypes.util.find_library."""
|
||||
|
||||
import inspect
|
||||
import types
|
||||
|
||||
|
||||
class TestOpusFindLibrary:
|
||||
|
|
@ -29,12 +30,34 @@ class TestOpusFindLibrary:
|
|||
assert "sys.platform" in source or "darwin" in source, \
|
||||
"Homebrew fallback must be guarded by macOS platform check"
|
||||
|
||||
def test_windows_bundled_discord_opus_dll_is_discovered(self, monkeypatch, tmp_path):
|
||||
"""Native Windows installs should try discord.py's bundled opus DLL."""
|
||||
import plugins.platforms.discord.adapter as adapter
|
||||
|
||||
opus_py = tmp_path / "discord" / "opus.py"
|
||||
bundled = opus_py.parent / "bin" / "libopus-0.x64.dll"
|
||||
bundled.parent.mkdir(parents=True)
|
||||
opus_py.write_text("# fake discord.opus module\n")
|
||||
bundled.write_bytes(b"fake dll")
|
||||
|
||||
discord_stub = types.SimpleNamespace(
|
||||
opus=types.SimpleNamespace(__file__=str(opus_py))
|
||||
)
|
||||
monkeypatch.setattr(adapter.sys, "platform", "win32")
|
||||
monkeypatch.setattr(adapter.struct, "calcsize", lambda _fmt: 8)
|
||||
|
||||
assert adapter._find_discord_windows_bundled_opus(discord_stub) == str(
|
||||
bundled.resolve()
|
||||
)
|
||||
|
||||
def test_opus_decode_error_logged(self):
|
||||
"""Opus decode failure must log the error, not silently return."""
|
||||
from plugins.platforms.discord.adapter import VoiceReceiver
|
||||
source = inspect.getsource(VoiceReceiver._on_packet)
|
||||
assert "logger" in source, \
|
||||
"_on_packet must log Opus decode errors"
|
||||
assert "self._decoders.pop" in source, \
|
||||
"_on_packet must reset the Opus decoder after decode failures"
|
||||
# Must not have bare `except Exception:\n return`
|
||||
lines = source.split("\n")
|
||||
for i, line in enumerate(lines):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue