Implement the Playlists interface

This commit is contained in:
Sumner Evans
2019-10-12 23:32:01 -06:00
parent 978f61d97a
commit d66da573b4
5 changed files with 98 additions and 6 deletions

View File

@@ -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)

View File

@@ -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)

View File

@@ -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),
},
}

View File

@@ -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)

View 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):