diff --git a/setup.py b/setup.py index 6a362d3..3edf7c8 100644 --- a/setup.py +++ b/setup.py @@ -56,7 +56,7 @@ setup( }, install_requires=[ 'bottle', - 'dataclasses-json @ git+https://github.com/sumnerevans/dataclasses-json@cc2eaeb#egg=dataclasses-json', + 'dataclasses-json @ git+https://github.com/sumnerevans/dataclasses-json@cc2eaeb#egg=dataclasses-json', # noqa: E501 'deepdiff', 'Deprecated', 'fuzzywuzzy', diff --git a/sublime/adapters/adapter_base.py b/sublime/adapters/adapter_base.py index 7d670d1..7830e92 100644 --- a/sublime/adapters/adapter_base.py +++ b/sublime/adapters/adapter_base.py @@ -199,6 +199,9 @@ class Adapter(abc.ABC): """ return False + # TODO some way of specifying what types of schemas can be provided (for + # example, http, https, file) + # Data Retrieval Methods # These properties determine if what things the adapter can be used to do # at the current moment. diff --git a/sublime/adapters/filesystem/sqlite_extensions.py b/sublime/adapters/filesystem/sqlite_extensions.py index 156d733..074b586 100644 --- a/sublime/adapters/filesystem/sqlite_extensions.py +++ b/sublime/adapters/filesystem/sqlite_extensions.py @@ -138,7 +138,7 @@ class SortedManyToManyField(ManyToManyField): database = self.model._meta.database schema = self.model._meta.schema table_name = '{}_{}_through'.format(*tables) - indexes = (((lhs._meta.name, rhs._meta.name), True), ) + indexes = (((lhs._meta.name, rhs._meta.name, 'position'), True), ) params = {'on_delete': self._on_delete, 'on_update': self._on_update} attrs = { diff --git a/sublime/app.py b/sublime/app.py index b114ad0..3ae5ba7 100644 --- a/sublime/app.py +++ b/sublime/app.py @@ -28,11 +28,12 @@ except Exception: glib_notify_exists = False from .adapters import AdapterManager +from .adapters.api_objects import Playlist from .cache_manager import CacheManager from .config import AppConfiguration, ReplayGainType from .dbus_manager import dbus_propagate, DBusManager from .players import ChromecastPlayer, MPVPlayer, PlayerEvent -from .server.api_objects import Child, Directory, Playlist +from .server.api_objects import Child, Directory from .ui.configure_servers import ConfigureServersDialog from .ui.main import MainWindow from .ui.settings import SettingsDialog @@ -288,7 +289,7 @@ class SublimeMusicApp(Gtk.Application): if not self.dbus_manager: return - if len(track_ids): + if len(track_ids) == 0: # We are lucky, just return an empty list. return GLib.Variant('(aa{sv})', ([], )) @@ -302,8 +303,10 @@ class SublimeMusicApp(Gtk.Application): ] # Get rid of all of the tracks that were not requested. - metadatas = filter( - lambda m: m['mpris:trackid'] in track_ids, metadatas) + metadatas = list( + filter(lambda m: m['mpris:trackid'] in track_ids, metadatas)) + + assert len(metadatas) == len(track_ids) # Sort them so they get returned in the same order as they were # requested. @@ -322,17 +325,18 @@ class SublimeMusicApp(Gtk.Application): def activate_playlist(playlist_id: str): playlist_id = playlist_id.split('/')[-1] - playlist = CacheManager.get_playlist(playlist_id).result() + playlist = AdapterManager.get_playlist_details( + playlist_id).result() # Calculate the song id to play. song_idx = 0 if self.app_config.state.shuffle_on: - song_idx = random.randint(0, len(playlist.entry) - 1) + song_idx = random.randint(0, len(playlist.songs) - 1) self.on_song_clicked( None, song_idx, - [s.id for s in playlist.entry], + [s.id for s in playlist.songs], {'active_playlist_id': playlist_id}, ) @@ -342,13 +346,13 @@ class SublimeMusicApp(Gtk.Application): order: str, reverse_order: bool, ) -> GLib.Variant: - playlists_result = CacheManager.get_playlists() - if playlists_result.is_future: + playlists_result = AdapterManager.get_playlists() + if not playlists_result.data_is_available: # We don't want to wait for the response in this case, so just # return an empty array. return GLib.Variant('(a(oss))', ([], )) - playlists = playlists_result.result() + playlists = list(playlists_result.result()) sorters = { 'Alphabetical': lambda p: p.name, @@ -362,7 +366,7 @@ class SublimeMusicApp(Gtk.Application): def make_playlist_tuple(p: Playlist) -> GLib.Variant: cover_art_filename = CacheManager.get_cover_art_filename( - p.coverArt, + p.cover_art, allow_download=False, ).result() return (f'/playlist/{p.id}', p.name, cover_art_filename or '') @@ -400,6 +404,7 @@ class SublimeMusicApp(Gtk.Application): 'org.mpris.MediaPlayer2.TrackList': { 'GoTo': set_pos_fn, 'GetTracksMetadata': get_tracks_metadata, + # 'RemoveTrack': remove_track, }, 'org.mpris.MediaPlayer2.Playlists': { 'ActivatePlaylist': activate_playlist, diff --git a/tests/adapter_tests/filesystem_adapter_tests.py b/tests/adapter_tests/filesystem_adapter_tests.py index 5c553da..5bf1f3e 100644 --- a/tests/adapter_tests/filesystem_adapter_tests.py +++ b/tests/adapter_tests/filesystem_adapter_tests.py @@ -140,6 +140,15 @@ def test_caching_get_playlist_details(cache_adapter: FilesystemAdapter): duration=timedelta(seconds=21.8), path='/foo/song1.mp3', ), + SubsonicAPI.Song( + '1', + 'Song 1', + parent='foo', + album='foo', + artist='foo', + duration=timedelta(seconds=21.8), + path='/foo/song1.mp3', + ), ] cache_adapter.ingest_new_data( FilesystemAdapter.FunctionNames.GET_PLAYLIST_DETAILS, @@ -150,8 +159,8 @@ def test_caching_get_playlist_details(cache_adapter: FilesystemAdapter): playlist = cache_adapter.get_playlist_details('1') assert playlist.id == '1' assert playlist.name == 'foo' - assert playlist.song_count == 2 - assert playlist.duration == timedelta(seconds=32) + assert playlist.song_count == 3 + assert playlist.duration == timedelta(seconds=53.8) for actual, song in zip(playlist.songs, songs): for k, v in asdict(song).items(): assert getattr(actual, k, None) == v