implement player *partial
This commit is contained in:
parent
1d390c72a3
commit
6382c13fd0
|
@ -834,6 +834,12 @@ class Session(Closeable, MessageListener, SubListener):
|
||||||
def device_id(self) -> str:
|
def device_id(self) -> str:
|
||||||
return self.__inner.device_id
|
return self.__inner.device_id
|
||||||
|
|
||||||
|
def device_name(self) -> str:
|
||||||
|
return self.__inner.device_name
|
||||||
|
|
||||||
|
def device_type(self) -> Connect.DeviceType:
|
||||||
|
return self.__inner.device_type
|
||||||
|
|
||||||
def event(self, resp: MercuryClient.Response) -> None:
|
def event(self, resp: MercuryClient.Response) -> None:
|
||||||
if resp.uri == "spotify:user:attributes:update":
|
if resp.uri == "spotify:user:attributes:update":
|
||||||
attributes_update = UserAttributesUpdate()
|
attributes_update = UserAttributesUpdate()
|
||||||
|
|
|
@ -1,5 +1,95 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
from librespot import Version
|
||||||
from librespot.audio.decoders import AudioQuality
|
from librespot.audio.decoders import AudioQuality
|
||||||
|
from librespot.core import Session
|
||||||
|
from librespot.mercury import MercuryRequests
|
||||||
|
from librespot.proto import Connect_pb2 as Connect
|
||||||
|
from librespot.structure import Closeable, MessageListener, RequestListener
|
||||||
|
import concurrent.futures
|
||||||
|
import logging
|
||||||
|
import typing
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceStateHandler(Closeable, MessageListener, RequestListener):
|
||||||
|
logger = logging.getLogger("Librespot:DeviceStateHandler")
|
||||||
|
__closing = False
|
||||||
|
__connection_id: str = None
|
||||||
|
__device_info: Connect.DeviceInfo
|
||||||
|
__put_state: Connect.PutStateRequest
|
||||||
|
__put_state_worker = concurrent.futures.ThreadPoolExecutor()
|
||||||
|
__session: Session
|
||||||
|
|
||||||
|
def __init__(self, session: Session, conf: PlayerConfiguration):
|
||||||
|
self.__session = session
|
||||||
|
self.__device_info = self.initialize_device_info(session, conf)
|
||||||
|
self.__put_state = Connect.PutStateRequest(
|
||||||
|
device=Connect.Device(
|
||||||
|
device_info=self.__device_info,
|
||||||
|
),
|
||||||
|
member_type=Connect.MemberType.CONNECT_STATE,
|
||||||
|
)
|
||||||
|
self.__session.dealer().add_message_listener(self, ["hm://pusher/v1/connections/", "hm://connect-state/v1/connect/volume", "hm://connect-state/v1/cluster"])
|
||||||
|
self.__session.dealer().add_request_listener(self, "hm://connect-state/v1/")
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
self.__closing = True
|
||||||
|
self.__session.dealer().remove_message_listener(self)
|
||||||
|
self.__session.dealer().remove_request_listener(self)
|
||||||
|
self.__put_state_worker.shutdown()
|
||||||
|
|
||||||
|
def initialize_device_info(self, session: Session, conf: PlayerConfiguration) -> Connect.Device:
|
||||||
|
return Connect.DeviceInfo(
|
||||||
|
can_play=True,
|
||||||
|
capabilities=Connect.Capabilities(
|
||||||
|
can_be_player=True,
|
||||||
|
command_acks=True,
|
||||||
|
is_controllable=True,
|
||||||
|
is_observable=True,
|
||||||
|
gaia_eq_connect_id=True,
|
||||||
|
needs_full_player_state=False,
|
||||||
|
supported_types=["audio/episode", "audio/track"],
|
||||||
|
supports_command_request=True,
|
||||||
|
supports_gzip_pushes=True,
|
||||||
|
supports_logout=True,
|
||||||
|
supports_playlist_v2=True,
|
||||||
|
supports_rename=False,
|
||||||
|
supports_transfer_command=True,
|
||||||
|
volume_steps=conf.volume_steps,
|
||||||
|
),
|
||||||
|
client_id=MercuryRequests.keymaster_client_id,
|
||||||
|
device_id=session.device_id(),
|
||||||
|
device_software_version=Version.version_string(),
|
||||||
|
device_type=session.device_type(),
|
||||||
|
name=session.device_name(),
|
||||||
|
spirc_version="3.2.6",
|
||||||
|
volume=conf.initial_volume,
|
||||||
|
)
|
||||||
|
|
||||||
|
def put_connect_state(self, request: Connect.PutStateRequest):
|
||||||
|
self.__session.api().put_connect_state(self.__connection_id, request)
|
||||||
|
self.logger.info("Put state. [ts: {}, connId: {}, reason: {}]".format(request.client_side_timestamp, self.__connection_id, request.put_state_reason))
|
||||||
|
|
||||||
|
def update_connection_id(self, newer: str) -> None:
|
||||||
|
newer = urllib.parse.unquote(newer)
|
||||||
|
if self.__connection_id is None or self.__connection_id != newer:
|
||||||
|
self.__connection_id = newer
|
||||||
|
self.logger.debug("Updated Spotify-Connection-Id: {}".format(newer))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Player:
|
||||||
|
volume_max = 65536
|
||||||
|
__conf: PlayerConfiguration
|
||||||
|
__session: Session
|
||||||
|
__state: StateWrapper
|
||||||
|
|
||||||
|
def __init__(self, conf: PlayerConfiguration, session: Session):
|
||||||
|
self.__conf = conf
|
||||||
|
self.__session = session
|
||||||
|
|
||||||
|
def init_state(self) -> None:
|
||||||
|
self.__state = StateWrapper(self.__session, self, self.__conf)
|
||||||
|
|
||||||
|
|
||||||
class PlayerConfiguration:
|
class PlayerConfiguration:
|
||||||
|
@ -85,3 +175,20 @@ class PlayerConfiguration:
|
||||||
self.initial_volume,
|
self.initial_volume,
|
||||||
self.volume_steps,
|
self.volume_steps,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class StateWrapper(MessageListener):
|
||||||
|
__conf: PlayerConfiguration
|
||||||
|
__device: DeviceStateHandler
|
||||||
|
__player: Player
|
||||||
|
__session: Session
|
||||||
|
|
||||||
|
def __init__(self, session: Session, player: Player, conf: PlayerConfiguration):
|
||||||
|
self.__session = session
|
||||||
|
self.__player = player
|
||||||
|
self.__device = DeviceStateHandler(session, conf)
|
||||||
|
self.__conf = conf
|
||||||
|
session.dealer().add_message_listener(self, ["spotify:user:attributes:update", "hm://playlist/", "hm://collection/collection/" + session.username() + "/json"])
|
||||||
|
|
||||||
|
def on_message(self, uri: str, headers: typing.Dict[str, str], payload: bytes):
|
||||||
|
pass
|
||||||
|
|
Loading…
Reference in New Issue