fix(discord): recover Windows voice opus decoding

This commit is contained in:
helix4u 2026-05-26 22:11:50 -06:00 committed by Teknium
parent bb65bebed7
commit ea34925002
2 changed files with 60 additions and 3 deletions

View file

@ -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():

View file

@ -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):