[Incomplete] Zeroconf feature

This commit is contained in:
kokarare1212 2021-05-19 20:34:24 +09:00
parent 5768f49f4a
commit 5a2f8eed5d
9 changed files with 228 additions and 3 deletions

View File

@ -4,6 +4,8 @@ import concurrent.futures
import random import random
import socket import socket
from zeroconf import ServiceBrowser, ServiceInfo, Zeroconf
from librespot.common import Utils from librespot.common import Utils
from librespot.core import Session from librespot.core import Session
from librespot.crypto import DiffieHellman from librespot.crypto import DiffieHellman
@ -13,6 +15,7 @@ from librespot.standard import Runnable
class ZeroconfServer(Closeable): class ZeroconfServer(Closeable):
SERVICE = "spotify-connect"
__MAX_PORT = 65536 __MAX_PORT = 65536
__MIN_PORT = 1024 __MIN_PORT = 1024
__EOL = "\r\n" __EOL = "\r\n"

View File

View File

@ -35,9 +35,9 @@ class Player(Closeable, PlayerSession.Listener, AudioSink.Listener):
self._events = Player.EventsDispatcher(conf) self._events = Player.EventsDispatcher(conf)
self._sink = AudioSink(conf, self) self._sink = AudioSink(conf, self)
self._init_state() self.__init_state()
def _init_state(self): def __init_state(self):
self._state = StateWrapper.StateWrapper(self._session, self, self._state = StateWrapper.StateWrapper(self._session, self,
self._conf) self._conf)

View File

@ -0,0 +1,59 @@
import struct
class Packet:
__FLAG_RESPONSE: int = 15
__FLAG_AA: int = 10
__questions: list
__answers: list
__authorities: list
__additionals: list
__id: int
__flags: int
__address: str
def __init__(self, _id: int):
self.__id = _id
self.__questions = []
self.__answers = []
self.__authorities = []
self.__additionals = []
def get_address(self) -> str:
return self.__address
def set_address(self, address: str) -> None:
self.__address = address
def get_id(self) -> int:
return self.__id
def is_response(self) -> bool:
return self.__is_flag(self.__FLAG_RESPONSE)
def set_response(self, on: bool) -> None:
self.__set_flag(self.__FLAG_RESPONSE, on)
def is_authoritative(self) -> bool:
return self.__is_flag(self.__FLAG_AA)
def set_authoritative(self, on: bool) -> None:
self.__set_flag(self.__FLAG_AA, on)
def __is_flag(self, flag: int):
return (self.__flags & (1 << flag)) != 0
def __set_flag(self, flag: int, on: bool):
if on:
self.__flags |= (1 << flag)
else:
self.__flags &= ~(1 << flag)
def read(self, inp: bytes, address: str):
self.__address = address
self.__id = struct.unpack("<h", inp[0:2])[0]
self.__flags = struct.unpack("<h", inp[2:4])[0]
num_questions = struct.unpack("<h", inp[4:6])[0]
num_answers = struct.unpack("<h", inp[6:8])[0]
num_authorities = struct.unpack("<h", inp[8:10])[0]
num_additionals = struct.unpack("<h", inp[10:12])[0]

View File

@ -0,0 +1,38 @@
from librespot.zeroconf import Packet
class Record:
TYPE_A: int = 0x01
TYPE_PTR: int = 0x0c
TYPE_CNAME: int = 0x05
TYPE_TXT: int = 0x10
TYPE_AAAA: int = 0x1c
TYPE_SRV: int = 0x21
TYPE_NSEC: int = 0x2f
TYPE_ANY: int = 0xff
__type: int
_ttl: int
__name: str
__clazz: int
__data: bytes
def __init__(self, typ: int):
self.__type = typ
self.__clazz = 1
@staticmethod
def _write_name(self, name: str, packet: Packet):
length = len(name)
out = b""
start = 0
for i in range(length + 1):
c = "." if i == length else name[i]
if c == ".":
out += bytes([i - start])
for j in range(start, i):
out += name.encode()[j]
start = i + 1
out += bytes([0])
return out, len(name) + 2

View File

@ -0,0 +1,60 @@
from __future__ import annotations
from librespot.zeroconf import Packet
class Service:
__alias: str
__service: str
__port: int
__text: dict
__domain: str
__protocol: str
__host: str
def __init__(self, alias: str, service: str, port: int):
self.__alias = alias
for s in alias:
c = ord(s)
if c < 0x20 or c == 0x7f:
raise TypeError()
self.__service = service
self.__port = port
self.__protocol = "tcp"
self.__text = {}
def __esc(self, text: str):
ns = ""
for s in text:
c = ord(s)
if c == 0x2e or c == 0x5c:
ns += "\\"
ns += s
return ns
def set_protocol(self, protocol: str) -> Service:
if protocol == "tcp" or protocol == "udp":
self.__protocol = protocol
else:
raise TypeError()
return self
def get_domain(self) -> str:
return self.__domain
def set_domain(self, domain: str) -> Service:
if domain is None or len(domain) < 2 or domain[0] != ".":
raise TypeError(domain)
self.__domain = domain
return self
def get_host(self) -> str:
return self.__host
def set_host(self, host: str) -> Service:
self.__host = host
return self
def get_packet(self) -> Packet:
packet = Packet()
return packet

View File

@ -0,0 +1,64 @@
from __future__ import annotations
from librespot.standard import Closeable
import base64
import random
import socket
class Zeroconf(Closeable):
__DISCOVERY = "_services._dns-sd._udp.local"
__BROADCAST4: socket.socket
__BROADCAST6: socket.socket
__use_ipv4: bool = True
__use_ipv6: bool = True
__hostname: str
__domain: str
def __init__(self):
try:
self.__BROADCAST4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.__BROADCAST4.connect(("224.0.0.251", 5353))
self.__BROADCAST6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
self.__BROADCAST6.connect(("FF02::FB", 5353))
except Exception as e:
pass
self.set_domain(".local")
self.set_local_host_name(Zeroconf.get_or_create_local_host_name())
@staticmethod
def get_or_create_local_host_name() -> str:
host = socket.gethostname()
if host == "localhost":
host = base64.b64encode(random.randint(-9223372036854775808, 9223372036854775807)).decode() + ".local"
return host
def set_use_ipv4(self, ipv4: bool) -> Zeroconf:
self.__use_ipv4 = ipv4
return self
def set_use_ipv6(self, ipv6: bool) -> Zeroconf:
self.__use_ipv6 = ipv6
return self
def close(self) -> None:
super().close()
def get_domain(self) -> str:
return self.__domain
def set_domain(self, domain: str) -> Zeroconf:
self.__domain = domain
return self
def get_local_host_name(self) -> str:
return self.__hostname
def set_local_host_name(self, name: str) -> Zeroconf:
self.__hostname = name
return self
def handle_packet(self, packet):
pass
def announce(self, service):
pass

View File

@ -0,0 +1 @@
from librespot.zeroconf.Packet import Packet

View File

@ -1,4 +1,4 @@
defusedxml==0.7.1 defusedxml==0.7.1
protobuf==3.15.8 protobuf==3.15.8
pycryptodome==3.10.1 pycryptodome==3.10.1
requests==2.25.1 requests==2.25.1