Implement the Playlists interface
This commit is contained in:
@@ -266,6 +266,7 @@ class LibremsonicApp(Gtk.Application):
|
||||
):
|
||||
second_microsecond_conversion = 1000000
|
||||
track_id_re = re.compile(r'/song/(.*)')
|
||||
playlist_id_re = re.compile(r'/playlist/(.*)')
|
||||
|
||||
def seek_fn(offset):
|
||||
offset_seconds = offset / second_microsecond_conversion
|
||||
@@ -297,6 +298,50 @@ class LibremsonicApp(Gtk.Application):
|
||||
|
||||
return GLib.Variant('(aa{sv})', (metadatas, ))
|
||||
|
||||
def activate_playlist(playlist_id):
|
||||
playlist_id = playlist_id_re.match(playlist_id).group(1)
|
||||
playlist = CacheManager.get_playlist(playlist_id).result()
|
||||
|
||||
# Calculate the song id to play.
|
||||
song_id = playlist.entry[0].id
|
||||
if self.state.shuffle_on:
|
||||
rand_idx = random.randint(0, len(playlist.entry) - 1)
|
||||
song_id = playlist.entry[rand_idx].id
|
||||
|
||||
self.on_song_clicked(
|
||||
None,
|
||||
song_id,
|
||||
[s.id for s in playlist.entry],
|
||||
{'active_playlist_id': playlist_id},
|
||||
)
|
||||
|
||||
def get_playlists(index, max_count, order, reverse_order):
|
||||
playlists = CacheManager.get_playlists().result()
|
||||
sorters = {
|
||||
'Alphabetical': lambda p: p.name,
|
||||
'Created': lambda p: p.created,
|
||||
'Modified': lambda p: p.changed,
|
||||
}
|
||||
playlists.sort(
|
||||
key=sorters.get(order, lambda p: p),
|
||||
reverse=reverse_order,
|
||||
)
|
||||
|
||||
return GLib.Variant(
|
||||
'(a(oss))', (
|
||||
[
|
||||
(
|
||||
'/playlist/' + p.id,
|
||||
p.name,
|
||||
CacheManager.get_cover_art_filename(
|
||||
p.coverArt,
|
||||
allow_download=False,
|
||||
).result() or '',
|
||||
)
|
||||
for p in playlists[index:(index + max_count)]
|
||||
if p.songCount > 0
|
||||
], ))
|
||||
|
||||
method_call_map = {
|
||||
'org.mpris.MediaPlayer2': {
|
||||
'Raise': self.window.present,
|
||||
@@ -316,6 +361,10 @@ class LibremsonicApp(Gtk.Application):
|
||||
'GoTo': set_pos_fn,
|
||||
'GetTracksMetadata': get_track_metadata,
|
||||
},
|
||||
'org.mpris.MediaPlayer2.Playlists': {
|
||||
'ActivatePlaylist': activate_playlist,
|
||||
'GetPlaylists': get_playlists,
|
||||
},
|
||||
}
|
||||
method = method_call_map.get(interface, {}).get(method)
|
||||
if method is None:
|
||||
@@ -533,6 +582,9 @@ class LibremsonicApp(Gtk.Application):
|
||||
if metadata.get('force_shuffle_state') is not None:
|
||||
self.state.shuffle_on = metadata['force_shuffle_state']
|
||||
|
||||
if metadata.get('active_playlist_id') is not None:
|
||||
self.state.active_playlist_id = metadata.get('active_playlist_id')
|
||||
|
||||
# If shuffle is enabled, then shuffle the playlist.
|
||||
if self.state.shuffle_on:
|
||||
song_queue.remove(song_id)
|
||||
|
@@ -210,10 +210,14 @@ class CacheManager(metaclass=Singleton):
|
||||
download_fn: Callable[[], bytes],
|
||||
before_download: Callable[[], None] = lambda: None,
|
||||
force: bool = False,
|
||||
allow_download: bool = True,
|
||||
):
|
||||
abs_path = self.calculate_abs_path(relative_path)
|
||||
download_path = self.calculate_download_path(relative_path)
|
||||
if not abs_path.exists() or force:
|
||||
if not allow_download:
|
||||
return None
|
||||
|
||||
before_download()
|
||||
resource_downloading = False
|
||||
with self.download_set_lock:
|
||||
@@ -556,6 +560,7 @@ class CacheManager(metaclass=Singleton):
|
||||
before_download: Callable[[], None] = lambda: None,
|
||||
size: Union[str, int] = 200,
|
||||
force: bool = False,
|
||||
allow_download: bool = True,
|
||||
) -> Future:
|
||||
def do_get_cover_art_filename() -> str:
|
||||
tag = 'tag_' if self.browse_by_tags else ''
|
||||
@@ -564,6 +569,7 @@ class CacheManager(metaclass=Singleton):
|
||||
lambda: self.server.get_cover_art(id, str(size)),
|
||||
before_download=before_download,
|
||||
force=force,
|
||||
allow_download=allow_download,
|
||||
)
|
||||
|
||||
return CacheManager.executor.submit(do_get_cover_art_filename)
|
||||
|
@@ -157,6 +157,26 @@ class DBusManager:
|
||||
current = state.play_queue.index(state.current_song.id)
|
||||
has_next_song = current < len(state.play_queue) - 1
|
||||
|
||||
if state.active_playlist_id is None:
|
||||
active_playlist = (False, GLib.Variant('(oss)', ('/', '', '')))
|
||||
else:
|
||||
playlist = CacheManager.get_playlist(
|
||||
state.active_playlist_id).result()
|
||||
active_playlist = (
|
||||
True,
|
||||
GLib.Variant(
|
||||
'(oss)',
|
||||
(
|
||||
'/playlist/' + playlist.id,
|
||||
playlist.name,
|
||||
CacheManager.get_cover_art_filename(
|
||||
playlist.coverArt,
|
||||
allow_download=False,
|
||||
).result() or '',
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
return {
|
||||
'org.mpris.MediaPlayer2': {
|
||||
'CanQuit': True,
|
||||
@@ -217,8 +237,8 @@ class DBusManager:
|
||||
# TODO this may do a network request. This really is a case for
|
||||
# doing the whole thing with caching some data beforehand.
|
||||
'PlaylistCount': len(CacheManager.get_playlists().result()),
|
||||
'Orderings': ['Alphabetical'],
|
||||
'ActivePlaylist': None,
|
||||
'Orderings': ['Alphabetical', 'Created', 'Modified'],
|
||||
'ActivePlaylist': ('(b(oss))', active_playlist),
|
||||
},
|
||||
}
|
||||
|
||||
|
@@ -66,6 +66,7 @@ class ApplicationState:
|
||||
selected_artist_id: str = None
|
||||
selected_playlist_id: str = None
|
||||
current_album_sort: str = 'random'
|
||||
active_playlist_id: str = None
|
||||
|
||||
def to_json(self):
|
||||
current_song = (
|
||||
@@ -89,6 +90,7 @@ class ApplicationState:
|
||||
'selected_playlist_id':
|
||||
getattr(self, 'selected_playlist_id', None),
|
||||
'current_album_sort': getattr(self, 'current_album_sort', None),
|
||||
'active_playlist_id': getattr(self, 'active_playlist_id', None),
|
||||
}
|
||||
|
||||
def load_from_json(self, json_object):
|
||||
@@ -115,6 +117,7 @@ class ApplicationState:
|
||||
'selected_playlist_id', None)
|
||||
self.current_album_sort = json_object.get(
|
||||
'current_album_sort', 'random')
|
||||
self.active_playlist_id = json_object.get('active_playlist_id', None)
|
||||
|
||||
def load(self):
|
||||
self.config = self.get_config(self.config_file)
|
||||
|
@@ -568,8 +568,14 @@ class PlaylistDetailPanel(Gtk.Overlay):
|
||||
def on_play_all_clicked(self, btn):
|
||||
song_id = self.playlist_song_store[0][-1]
|
||||
self.emit(
|
||||
'song-clicked', song_id, [m[-1] for m in self.playlist_song_store],
|
||||
{'force_shuffle_state': False})
|
||||
'song-clicked',
|
||||
song_id,
|
||||
[m[-1] for m in self.playlist_song_store],
|
||||
{
|
||||
'force_shuffle_state': False,
|
||||
'active_playlist_id': self.playlist_id,
|
||||
},
|
||||
)
|
||||
|
||||
def on_shuffle_all_button(self, btn):
|
||||
rand_idx = randint(0, len(self.playlist_song_store) - 1)
|
||||
@@ -577,7 +583,10 @@ class PlaylistDetailPanel(Gtk.Overlay):
|
||||
'song-clicked',
|
||||
self.playlist_song_store[rand_idx][-1],
|
||||
[m[-1] for m in self.playlist_song_store],
|
||||
{'force_shuffle_state': True},
|
||||
{
|
||||
'force_shuffle_state': True,
|
||||
'active_playlist_id': self.playlist_id,
|
||||
},
|
||||
)
|
||||
|
||||
def on_song_activated(self, treeview, idx, column):
|
||||
@@ -586,7 +595,9 @@ class PlaylistDetailPanel(Gtk.Overlay):
|
||||
'song-clicked',
|
||||
self.playlist_song_store[idx][-1],
|
||||
[m[-1] for m in self.playlist_song_store],
|
||||
{},
|
||||
{
|
||||
'active_playlist_id': self.playlist_id,
|
||||
},
|
||||
)
|
||||
|
||||
def on_song_button_press(self, tree, event):
|
||||
|
Reference in New Issue
Block a user