import abc
from dataclasses import dataclass
from datetime import timedelta
from enum import Enum
from typing import Callable, Dict, Optional, Set, Tuple, Type, Union
from ..adapters.api_objects import Song
[docs]@dataclass
class PlayerEvent:
"""
Represents an event triggered by the player. This is a way to signal state changes
to Sublime Music if the player can be controlled outside of Sublime Music (for
example, Chromecast player).
Each player event has a :class:`PlayerEvent.EventType`. Additionally, each event
type has additional information in the form of additional properties on the
:class:`PlayerEvent` object.
* :class:`PlayerEvent.EventType.PLAY_STATE_CHANGE` -- indicates that the play state
of the player has changed. The :class:`PlayerEvent.playing` property is required
for this event type.
* :class:`PlayerEvent.EventType.VOLUME_CHANGE` -- indicates that the player's volume
has changed. The :classs`PlayerEvent.volume` property is required for this event
type and should be in the range [0, 100].
* :class:`PlayerEvent.EventType.STREAM_CACHE_PROGRESS_CHANGE` -- indicates that the
stream cache progress has changed. When streaming a song, this will be used to
show how much of the song has been loaded into the player. The
:class:`PlayerEvent.stream_cache_duration` property is required for this event
type and should be a float represent the number of seconds of the song that have
been cached.
* :class:`PlayerEvent.EventType.CONNECTING` -- indicates that a device is being
connected to. The :class:`PlayerEvent.device_id` property is required for this
event type and indicates the device ID that is being connected to.
* :class:`PlayerEvent.EventType.CONNECTED` -- indicates that a device has been
connected to. The :class:`PlayerEvent.device_id` property is required for this
event type and indicates the device ID that has been connected to.
"""
[docs] class EventType(Enum):
PLAY_STATE_CHANGE = 0
VOLUME_CHANGE = 1
STREAM_CACHE_PROGRESS_CHANGE = 2
CONNECTING = 3
CONNECTED = 4
DISCONNECT = 5
type: EventType
device_id: str
playing: Optional[bool] = None
volume: Optional[float] = None
stream_cache_duration: Optional[float] = None
[docs]@dataclass
class PlayerDeviceEvent:
[docs] class Delta(Enum):
ADD = 0
REMOVE = 1
delta: Delta
player_type: Type
id: str
name: str
[docs]class Player(abc.ABC):
song_loaded = False
@property
@abc.abstractmethod
def enabled(self) -> bool:
return True
@property
@abc.abstractmethod
def name(self) -> str:
"""
:returns: returns the friendly name of the player for display in the UI.
"""
@property
@abc.abstractmethod
def supported_schemes(self) -> Set[str]:
"""
:returns: a set of all the schemes that the player can play.
"""
@property
def can_start_playing_with_no_latency(self) -> bool:
"""
:returns: whether the player can start playing a song with no latency.
"""
return False
@property
def gapless_playback(self) -> bool:
"""
:returns: whether the player supports and is using gapless playback
"""
return False
[docs] @staticmethod
@abc.abstractmethod
def get_configuration_options() -> Dict[str, Union[Type, Tuple[str, ...]]]:
"""
:returns: a dictionary of configuration key -> type of the option or tuple of
options (for a dropdown menu).
"""
[docs] @abc.abstractmethod
def __init__(
self,
on_timepos_change: Callable[[Optional[float]], None],
on_track_end: Callable[[], None],
on_player_event: Callable[[PlayerEvent], None],
player_device_change_callback: Callable[[PlayerDeviceEvent], None],
config: Dict[str, Union[str, int, bool]],
):
"""
Initialize the player.
:param config: A dictionary of configuration key -> configuration value.
"""
[docs] @abc.abstractmethod
def change_settings(self, config: Dict[str, Union[str, int, bool]]):
"""
This function is called when the player settings are changed (normally this
happens when the user changes the settings in the UI).
:param config: A dictionary of configuration key -> configuration value.
"""
[docs] @abc.abstractmethod
def refresh_players(self):
"""
This function is called when the user requests the player list to be refreshed
in the UI.
This function should call the ``player_device_change_callback`` with the delta
events to indicate changes to the UI. If there is no reason to refresh (for
example, the MPV player), then this function can do nothing.
"""
[docs] @abc.abstractmethod
def set_current_device_id(self, device_id: str):
"""
Switch to the given device ID.
"""
[docs] @abc.abstractmethod
def reset(self):
"""
Reset the player.
"""
[docs] @abc.abstractmethod
def shutdown(self):
"""
Do any cleanup of the player.
"""
@property
@abc.abstractmethod
def playing(self) -> bool:
"""
:returns: whether or not the player is currently playing a song.
"""
[docs] @abc.abstractmethod
def get_volume(self) -> float:
"""
:returns: the current volume on a scale of [0, 100]
"""
[docs] @abc.abstractmethod
def set_volume(self, volume: float):
"""
Set the volume of the player to the given value.
:param volume: the value to set the volume to. Will be in the range [0, 100]
"""
[docs] @abc.abstractmethod
def get_is_muted(self) -> bool:
"""
:returns: whether or not the player is muted.
"""
[docs] @abc.abstractmethod
def set_muted(self, muted: bool):
"""
:param muted: set the player's "muted" property to the given value.
"""
[docs] @abc.abstractmethod
def pause(self):
"""
Pause the player.
"""
[docs] @abc.abstractmethod
def play(self):
"""
Play the current media.
"""
[docs] @abc.abstractmethod
def seek(self, position: timedelta):
"""
:param position: seek to the given position in the song.
"""