import socket import struct import asyncio from config import settings def check_multicast_reachable(mcast_addr: str = "239.0.0.1", port: int = 9999, timeout: float = 2.0) -> bool: try: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) sock.settimeout(timeout) ttl = struct.pack("b", 1) sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl) mreq = struct.pack("4sL", socket.inet_aton(mcast_addr), socket.INADDR_ANY) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) sock.sendto(b"ping", (mcast_addr, port)) try: data, addr = sock.recvfrom(1024) sock.close() return True except socket.timeout: sock.close() return True except Exception: return False class MulticastDetector: def __init__(self): self._multicast_available: bool | None = None def is_available(self) -> bool: if self._multicast_available is not None: return self._multicast_available if not settings.multicast_enabled: self._multicast_available = False return False self._multicast_available = check_multicast_reachable() return self._multicast_available def get_stream_url(self, stream_key: str, node: dict, quality: dict | None = None): use_multicast = self.is_available() host = node["host"] if use_multicast: hash_val = abs(hash(stream_key)) % 255 mcast_addr = f"{settings.multicast_address_base.rsplit('.', 1)[0]}.{hash_val + 1}" mcast_port = settings.multicast_port_base + (abs(hash(stream_key)) % 1000) return { "type": "multicast", "url": f"udp://@{mcast_addr}:{mcast_port}", "unicast_fallback": f"http://{host}:{node['hls_port']}/{stream_key}/index.m3u8", } return { "type": "hls", "url": f"http://{host}:{node['hls_port']}/{stream_key}/index.m3u8", } multicast_detector = MulticastDetector()