WIP: implement-discord-module #4
@@ -64,7 +64,11 @@ class UDPAudioSource(discord.AudioSource):
|
|||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
if self.ffmpeg_process:
|
if self.ffmpeg_process:
|
||||||
self.ffmpeg_process.kill()
|
try:
|
||||||
|
self.ffmpeg_process.stdin.close()
|
||||||
|
self.ffmpeg_process.terminate()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
class DiscordRadioBot(discord.Client):
|
class DiscordRadioBot(discord.Client):
|
||||||
"""
|
"""
|
||||||
@@ -103,17 +107,18 @@ class DiscordRadioBot(discord.Client):
|
|||||||
Runs in a background thread. Listens for UDP packets from OP25.
|
Runs in a background thread. Listens for UDP packets from OP25.
|
||||||
"""
|
"""
|
||||||
self.udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
self.udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
# Bind to 0.0.0.0 to ensure we catch traffic from any interface (Docker/Localhost)
|
# Bind to all interfaces to ensure we catch the OP25 stream
|
||||||
self.udp_sock.bind(('0.0.0.0', self.listen_port))
|
self.udp_sock.bind(('0.0.0.0', self.listen_port))
|
||||||
self.udp_sock.settimeout(1.0)
|
self.udp_sock.settimeout(1.0)
|
||||||
|
|
||||||
LOGGER.info(f"UDP Audio Bridge listening on 0.0.0.0:{self.listen_port}")
|
LOGGER.info(f"UDP Audio Bridge listening on 0.0.0.0:{self.listen_port}")
|
||||||
LOGGER.info(f"Forwarding audio to: {self.forward_ports}")
|
LOGGER.info(f"Forwarding audio to: {self.forward_ports}")
|
||||||
|
|
||||||
forward_socks = []
|
# Create sockets for each forward port
|
||||||
|
forward_targets = []
|
||||||
for port in self.forward_ports:
|
for port in self.forward_ports:
|
||||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
forward_socks.append((s, port))
|
forward_targets.append((s, port))
|
||||||
|
|
||||||
self.running = True
|
self.running = True
|
||||||
while self.running:
|
while self.running:
|
||||||
@@ -121,21 +126,23 @@ class DiscordRadioBot(discord.Client):
|
|||||||
data, addr = self.udp_sock.recvfrom(4096)
|
data, addr = self.udp_sock.recvfrom(4096)
|
||||||
self.packets_received += 1
|
self.packets_received += 1
|
||||||
|
|
||||||
# 1. Forward to Liquidsoap/Other tools
|
# 1. Forward to Liquidsoap/audio.py
|
||||||
for sock, port in forward_socks:
|
for sock, port in forward_targets:
|
||||||
# Send to localhost (where Liquidsoap should be listening)
|
try:
|
||||||
sock.sendto(data, ('127.0.0.1', port))
|
sock.sendto(data, ('127.0.0.1', port))
|
||||||
self.packets_forwarded += 1
|
self.packets_forwarded += 1
|
||||||
|
except Exception as fe:
|
||||||
|
# Log forwarding errors only once per 5 seconds to avoid spam
|
||||||
|
if self.packets_forwarded % 100 == 0:
|
||||||
|
LOGGER.error(f"Forward error to port {port}: {fe}")
|
||||||
|
|
||||||
# 2. Add to Discord Buffer
|
# 2. Add to Discord Buffer
|
||||||
self.audio_buffer.extend(data)
|
self.audio_buffer.extend(data)
|
||||||
|
|
||||||
# Periodic Debug Logging (Every 5 seconds, only if active)
|
# Periodic Stats Logging
|
||||||
if time.time() - self.last_log_time > 5:
|
if time.time() - self.last_log_time > 10:
|
||||||
if self.packets_received > 0:
|
if self.packets_received > 0:
|
||||||
LOGGER.info(f"UDP Stats [5s]: Rx {self.packets_received} pkts | Tx {self.packets_forwarded} pkts | Buffer: {len(self.audio_buffer)} bytes")
|
LOGGER.info(f"Discord Audio Bridge Stats: Received {self.packets_received} packets, Forwarded {self.packets_forwarded}")
|
||||||
|
|
||||||
# Reset counters
|
|
||||||
self.packets_received = 0
|
self.packets_received = 0
|
||||||
self.packets_forwarded = 0
|
self.packets_forwarded = 0
|
||||||
self.last_log_time = time.time()
|
self.last_log_time = time.time()
|
||||||
@@ -208,10 +215,8 @@ class DiscordRadioBot(discord.Client):
|
|||||||
This turns the green ring ON.
|
This turns the green ring ON.
|
||||||
"""
|
"""
|
||||||
if self.voice_client and not self.voice_client.is_playing():
|
if self.voice_client and not self.voice_client.is_playing():
|
||||||
# clear buffer slightly to ensure we aren't playing old data
|
# Clear old audio so we don't start with a delay
|
||||||
# but keep a little to prevent jitter
|
self.audio_buffer.clear()
|
||||||
# self.audio_buffer.clear()
|
|
||||||
|
|
||||||
source = UDPAudioSource(self.audio_buffer)
|
source = UDPAudioSource(self.audio_buffer)
|
||||||
self.voice_client.play(source)
|
self.voice_client.play(source)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user