[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 socket
from zeroconf import ServiceBrowser, ServiceInfo, Zeroconf
from librespot.common import Utils
from librespot.core import Session
from librespot.crypto import DiffieHellman
@ -13,6 +15,7 @@ from librespot.standard import Runnable
class ZeroconfServer(Closeable):
SERVICE = "spotify-connect"
__MAX_PORT = 65536
__MIN_PORT = 1024
__EOL = "\r\n"

View File

View File

@ -35,9 +35,9 @@ class Player(Closeable, PlayerSession.Listener, AudioSink.Listener):
self._events = Player.EventsDispatcher(conf)
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._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
protobuf==3.15.8
pycryptodome==3.10.1
requests==2.25.1
requests==2.25.1