Files
sublime-music/sublime_music/ui/state.py
Benjamin Schaaf 5c4a29e6ae WIP
2022-01-02 20:40:07 +11:00

146 lines
4.5 KiB
Python

from dataclasses import dataclass, field
from datetime import timedelta
from enum import Enum
from typing import Any, Callable, Dict, Optional, Set, Tuple, Type
from ..adapters import AlbumSearchQuery
from ..adapters.api_objects import Genre, Song
from ..util import this_decade
class RepeatType(Enum):
NO_REPEAT = 0
REPEAT_QUEUE = 1
REPEAT_SONG = 2
@property
def icon(self) -> str:
"""
Get the icon for the repeat type.
>>> RepeatType.NO_REPEAT.icon, RepeatType.REPEAT_QUEUE.icon
('media-playlist-repeat-symbolic', 'media-playlist-repeat-symbolic')
>>> RepeatType.REPEAT_SONG.icon
'media-playlist-repeat-song-symbolic'
"""
song_str = "-song" if self == RepeatType.REPEAT_SONG else ""
return f"media-playlist-repeat{song_str}-symbolic"
def as_mpris_loop_status(self) -> str:
return ["None", "Playlist", "Track"][self.value]
@staticmethod
def from_mpris_loop_status(loop_status: str) -> "RepeatType":
return {
"None": RepeatType.NO_REPEAT,
"Track": RepeatType.REPEAT_SONG,
"Playlist": RepeatType.REPEAT_QUEUE,
}[loop_status]
@dataclass
class UIState:
"""Represents the UI state of the application."""
@dataclass(unsafe_hash=True)
class UINotification:
markup: str
actions: Tuple[Tuple[str, Callable[[], None]], ...] = field(
default_factory=tuple
)
icon: Optional[str] = None
version: int = 1
# Play state
playing: bool = False
current_song_index: int = -1
play_queue: Tuple[str, ...] = field(default_factory=tuple)
old_play_queue: Tuple[str, ...] = field(default_factory=tuple)
_volume: Dict[str, float] = field(default_factory=lambda: {"this device": 100.0})
is_muted: bool = False
repeat_type: RepeatType = RepeatType.NO_REPEAT
shuffle_on: bool = False
song_progress: timedelta = timedelta()
song_stream_cache_progress: Optional[timedelta] = timedelta()
current_device: str = "this device"
connecting_to_device: bool = False
connected_device_name: Optional[str] = None
available_players: Dict[Type, Set[Tuple[str, str]]] = field(default_factory=dict)
# UI state
current_tab: str = "albums"
selected_album_id: Optional[str] = None
selected_artist_id: Optional[str] = None
selected_browse_element_id: Optional[str] = None
selected_playlist_id: Optional[str] = None
album_sort_direction: str = "ascending"
album_page_size: int = 30
album_page: int = 0
current_notification: Optional[UINotification] = None
playlist_details_expanded: bool = True
artist_details_expanded: bool = True
loading_play_queue: bool = False
current_album_search_query: AlbumSearchQuery = AlbumSearchQuery(
AlbumSearchQuery.Type.RANDOM,
year_range=this_decade(),
)
active_playlist_id: Optional[str] = None
def __getstate__(self):
state = self.__dict__.copy()
del state["song_stream_cache_progress"]
del state["current_notification"]
del state["playing"]
del state["available_players"]
return state
def __setstate__(self, state: Dict[str, Any]):
self.__dict__.update(state)
self.song_stream_cache_progress = None
self.current_notification = None
self.playing = False
# Ensure a song is selected if the play queue isn't empty
if self.play_queue and self.current_song_index < 0:
self.current_song_index = 0
self.song_progress = timedelta(0)
def __init_available_players__(self):
from sublime_music.players import PlayerManager
self.available_players = {
pt: set() for pt in PlayerManager.available_player_types
}
def migrate(self):
pass
_current_song: Optional[Song] = None
@property
def current_song(self) -> Optional[Song]:
if not self.play_queue or self.current_song_index < 0:
return None
from sublime_music.adapters import AdapterManager
current_song_id = self.play_queue[self.current_song_index]
if not self._current_song or self._current_song.id != current_song_id:
self._current_song = AdapterManager.get_song_details(
current_song_id
).result()
return self._current_song
@property
def volume(self) -> float:
return self._volume.get(self.current_device, 100.0)
@volume.setter
def volume(self, value: float):
self._volume[self.current_device] = value