refactor(voice): Strict type checking in voice internals & DAVE Support (rec)#3159
refactor(voice): Strict type checking in voice internals & DAVE Support (rec)#3159Paillat-dev wants to merge 26 commits intomasterfrom
Conversation
Discord enforces DAVE (E2E encryption) on all voice channels. py-cord currently decodes Opus packets before stripping the DAVE application-layer encryption, causing OpusError: corrupted stream and making voice reception completely non-functional. Changes: - reader.py: Rewrite decrypt_rtp() to call dave.decrypt() BEFORE Opus decoding. Handles SSRC-to-user_id race condition by trying all known DAVE user IDs when the mapping is not yet populated, then caching the result. Falls back to OPUS_SILENCE on decrypt failure. - opus.py: Remove erroneous dave.decrypt() call in _decode_packet() that was applied to already-decoded PCM data, corrupting the audio stream. - router.py: Catch OpusError and AssertionError in _do_run() gracefully, emitting a single warning instead of crashing the router thread. Fixes: #3139
|
Thanks for opening this pull request! This pull request can be checked-out with: git fetch origin pull/3159/head:pr-3159
git checkout pr-3159This pull request can be installed with: pip install git+https://github.com/Pycord-Development/pycord@refs/pull/3159/head |
3890460 to
fee38b7
Compare
|
|
||
| data.seek(0) | ||
| output = BytesIO() | ||
| with wave.open(output, "wb") as f: |
There was a problem hiding this comment.
See if similar is required by other sinks or if it's even needed here idk if we might actually be better of undoing this. Test the other sinks
| finally: | ||
| self.reader.client.stop_recording() | ||
| try: | ||
| self.reader.client.stop_recording() |
Signed-off-by: Paillat <jeremiecotti@ik.me>
|
Hi, I can't seem to get the recording properly stop and save the audio files. It just seems to be stuck somewhere never finishing the recording. Logging in and connecting works. Recording starts, when typed !stop, Stopping recording for ... is outputed, but audio is not processed properly and seems to be hanging. import discord
from dotenv import load_dotenv
import os
import json
import time
from discord.ext import commands
# import asyncio
load_dotenv()
RECORDINGS_DIR = "recordings"
os.makedirs(RECORDINGS_DIR, exist_ok=True)
recording_start = {}
connections = {}
intents = discord.Intents.default()
intents.message_content = True
intents.voice_states = True
bot = commands.Bot(command_prefix="!", intents=intents)
async def finished_callback(sink: discord.sinks.WaveSink, ctx: commands.Context):
"""Called automatically when stop_recording() is called."""
for user_id, audio in sink.audio_data.items():
print(f"Processing audio for user {user_id} with attributes: {dir(audio)}")
filename = f"{RECORDINGS_DIR}/{user_id}.wav"
with open(filename, "wb") as f:
f.write(audio.file.read())
print(f"Saved {filename}")
await ctx.send(f"Saved {len(sink.audio_data)} audio track(s).")
@bot.event
async def on_ready():
print(f'We have logged in as {bot.user}')
@bot.command()
async def record(ctx):
if not ctx.author.voice:
await ctx.send("You are not connected to a voice channel.")
return
if ctx.guild.voice_client:
await ctx.guild.voice_client.disconnect()
print(f"Connecting to {ctx.author.voice.channel}")
vc = await ctx.author.voice.channel.connect()
print(f"Is connected: {vc.is_connected()}")
connections[ctx.guild.id] = (vc, sink := discord.sinks.WaveSink())
recording_start[ctx.guild.id] = time.time()
print(f"Connected: {vc}")
vc.start_recording(
sink,
finished_callback
)
print("Recording started")
await ctx.send("Recording started! Type `!stop` to stop.")
@bot.command()
async def stop(ctx: commands.Context):
if ctx.guild.id not in connections:
await ctx.send("Not recording.")
return
vc, sink = connections.pop(ctx.guild.id)
print(f"Stopping recording for {vc}")
vc.stop_recording()
await ctx.send("Stopping recording")
@bot.command()
async def check(ctx):
if ctx.guild.id not in connections:
await ctx.send("Not recording")
return
vc, sink = connections[ctx.guild.id]
print(f"audio_data: {sink.audio_data}")
print(f"is_recording: {vc.is_recording()}")
await ctx.send(f"audio_data keys: {list(sink.audio_data.keys())}")
@bot.command()
async def disconnect(ctx: commands.Context):
vc = ctx.guild.voice_client
if vc:
await vc.disconnect(force=True)
await ctx.send("Disconnected.")
else:
await ctx.send("No voice client.")
bot.run(os.getenv('DISCORD_TOKEN'))
|
25347b5 to
cc6ebbb
Compare
45025bd to
01e9fa8
Compare
|
thank you for this change <3 can't wait for it to get in |

Summary
Information
examples, ...).
Checklist
type: ignorecomments were used, a comment is also left explaining why.