Skip to content

Commit 3c4992c

Browse files
committed
Add VBAN module
1 parent c438a79 commit 3c4992c

File tree

1 file changed

+86
-0
lines changed
  • music_assistant/providers/vban_receiver

1 file changed

+86
-0
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
"""VBAN subclasses to workaround issues in aiovban 0.6.3."""
2+
3+
from __future__ import annotations
4+
5+
import asyncio
6+
import logging
7+
from dataclasses import dataclass
8+
from typing import Any
9+
10+
from aiovban.asyncio import AsyncVBANClient
11+
from aiovban.packet import VBANPacket
12+
from aiovban.packet.headers import VBANHeaderException
13+
14+
logger = logging.getLogger(__name__)
15+
16+
17+
@dataclass
18+
class VBANBaseProtocolMod(asyncio.DatagramProtocol):
19+
"""VBANBaseProtocol workaround."""
20+
21+
client: AsyncVBANClientMod
22+
23+
def __post_init__(self) -> None:
24+
"""Initialize."""
25+
# WORKAROUND: each instance gets it's own Future.
26+
self.done: asyncio.Future[Any] = asyncio.get_event_loop().create_future()
27+
# self.done = asyncio.get_event_loop().create_future()
28+
self.background_tasks: set[asyncio.Task[Any]] = set()
29+
30+
def error_received(self, exc: Exception) -> None:
31+
"""Handle error."""
32+
self.done.set_exception(exc)
33+
34+
def connection_lost(self, exc: Exception | None) -> None:
35+
"""Handle lost connection."""
36+
if self.done.done():
37+
return
38+
# WORKAROUND: handle exc properly.
39+
if exc:
40+
self.done.set_exception(exc)
41+
else:
42+
self.done.set_result(None)
43+
44+
45+
@dataclass
46+
class VBANListenerProtocolMod(VBANBaseProtocolMod):
47+
"""VBANListenerProcotol workaround."""
48+
49+
def connection_made(self, transport) -> None: # type: ignore[no-untyped-def]
50+
"""Handle connection made."""
51+
logger.debug(f"Connection made to {transport}")
52+
53+
def datagram_received(self, data: bytes, addr: tuple[str, int]) -> None:
54+
"""Handle received datagram."""
55+
try:
56+
if self.client.quick_reject(addr[0]):
57+
return
58+
packet = VBANPacket.unpack(data)
59+
task = asyncio.create_task(self.client.process_packet(addr[0], addr[1], packet))
60+
self.background_tasks.add(task)
61+
task.add_done_callback(self.background_tasks.discard)
62+
except VBANHeaderException as e:
63+
logger.error(f"Error unpacking packet: {e}")
64+
65+
66+
class AsyncVBANClientMod(AsyncVBANClient): # type: ignore[misc]
67+
"""AsyncVBANClient workaround."""
68+
69+
async def listen(
70+
self,
71+
address: str = "0.0.0.0",
72+
port: int = 6980,
73+
loop: asyncio.AbstractEventLoop | None = None,
74+
) -> None:
75+
"""Create UDP listener."""
76+
loop = loop or asyncio.get_running_loop()
77+
78+
# Create a socket and set the options
79+
self._transport, proto = await loop.create_datagram_endpoint(
80+
lambda: VBANListenerProtocolMod(self),
81+
local_addr=(address, port),
82+
allow_broadcast=not self.ignore_audio_streams,
83+
)
84+
85+
# WORKAROUND: await, not return.
86+
await proto.done

0 commit comments

Comments
 (0)