librespot-python/librespot/player/state/DeviceStateHandler.py

116 lines
3.4 KiB
Python

from __future__ import annotations
import base64
import concurrent.futures
import enum
import logging
import time
import typing
import urllib.parse
from librespot.common import Utils
from librespot.core import Session
from librespot.player import PlayerConfiguration
from librespot.proto import Connect_pb2 as Connect
from librespot.proto import Player_pb2 as Player
class DeviceStateHandler:
_LOGGER: logging = logging.getLogger(__name__)
_session: Session
_deviceInfo: Connect.DeviceInfo
_listeners: typing.List[DeviceStateHandler.Listener] = []
_putState: Connect.PutStateRequest
_putStateWorker: concurrent.futures.ThreadPoolExecutor = (
concurrent.futures.ThreadPoolExecutor())
_connectionId: str
def __init__(self, session: Session, player, conf: PlayerConfiguration):
self._session = session
self._deviceInfo = None
self._putState = Connect.PutStateRequest()
def _update_connection_id(self, newer: str) -> None:
newer = urllib.parse.unquote(newer, "UTF-8")
if self._connectionId is None or self._connectionId != newer:
self._connectionId = newer
self._LOGGER.debug("Updated Spotify-Connection-Id: {}".format(
self._connectionId))
self._notify_ready()
def add_listener(self, listener: DeviceStateHandler.Listener):
self._listeners.append(listener)
def _notify_ready(self) -> None:
for listener in self._listeners:
listener.ready()
def update_state(
self,
reason: Connect.PutStateReason,
player_time: int,
state: Player.PlayerState,
):
if self._connectionId is None:
raise TypeError()
if player_time == -1:
pass
else:
pass
self._putState.put_state_reason = reason
self._putState.client_side_timestamp = int(time.time() * 1000)
self._putState.device.device_info = self._deviceInfo
self._putState.device.player_state = state
self._putStateWorker.submit(self._put_connect_state, self._putState)
def _put_connect_state(self, req: Connect.PutStateRequest):
self._session.api().put_connect_state(self._connectionId, req)
self._LOGGER.info("Put state. ts: {}, connId: {}, reason: {}".format(
req.client_side_timestamp,
Utils.truncate_middle(self._connectionId, 10),
req.put_state_reason,
))
class Endpoint(enum.Enum):
Play: str = "play"
Pause: str = "pause"
Resume: str = "resume"
SeekTo: str = "seek_to"
SkipNext: str = "skip_next"
SkipPrev: str = "skip_prev"
class Listener:
def ready(self) -> None:
pass
def command(
self,
endpoint: DeviceStateHandler.Endpoint,
data: DeviceStateHandler.CommandBody,
) -> None:
pass
def volume_changed(self) -> None:
pass
def not_active(self) -> None:
pass
class CommandBody:
_obj: typing.Any
_data: bytes
_value: str
def __init__(self, obj: typing.Any):
self._obj = obj
if obj.get("data") is not None:
self._data = base64.b64decode(obj.get("data"))
if obj.get("value") is not None:
self._value = obj.get("value")