diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9b8fc20..4605431 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -31,34 +31,41 @@ Features @samsartor for contributing the SVGs! * A new icon for indicating the connection state to the Subsonic server. Contributed by @samsartor. +* A new icon for that data wasn't able to be loaded due to being offline. + Contributed by @samsartor. -**Settings** +**Application Menus** * Settings are now in the popup under the gear icon rather than in a separate popup window. -* The music provider configuration has gotten a major revamp. * You can now clear the cache via an option in the Downloads popup. There are options for removing the entire cache and removing just the song file cache. +.. * The music provider configuration has gotten a major revamp. +.. * The Downloads popup shows the songs that are currently being downloaded. +.. * + **Offline Mode** * You can enable *Offline Mode* from the server menu. * Features that require network access are disabled in offline mode. * You can still browse anything that is already cached offline. -.. MENTION man page +**Other Features** + +.. * A man page has been added. Contributed by @baldurmen. Under The Hood -------------- - This release has a ton of under-the-hood changes to make things more robust - and performant. +This release has a ton of under-the-hood changes to make things more robust +and performant. - * The cache is now stored in a SQLite database. - * The cache no longer gets corrupted when Sublime Music fails to write to - disk. - * A generic `Adapter API`_ has been created which means that Sublime Music is - no longer reliant on Subsonic and in the future, more backends can be added. +* The cache is now stored in a SQLite database. +* The cache no longer gets corrupted when Sublime Music fails to write to disk. +* A generic `Adapter API`_ has been created which means that Sublime Music is no + longer reliant on Subsonic. This means that in the future, more backends can + be added. .. _Adapter API: https://sumner.gitlab.io/sublime-music/adapter-api.html diff --git a/sublime/adapters/adapter_base.py b/sublime/adapters/adapter_base.py index 6dda37d..1d58233 100644 --- a/sublime/adapters/adapter_base.py +++ b/sublime/adapters/adapter_base.py @@ -114,15 +114,15 @@ class AlbumSearchQuery: ... AlbumSearchQuery.Type.YEAR_RANGE, year_range=(2018, 2019) ... ) >>> query.strhash() - 'a6571bb7be65984c6627f545cab9fc767fce6d07' + '5b0724ae23acd58bc2f9187617712775670e0b98' """ if not self._strhash: - self._strhash = hashlib.sha1( - bytes( - json.dumps((self.type.value, self.year_range, self.genre.name)), - "utf8", - ) - ).hexdigest() + hash_tuple: Tuple[Any, ...] = (self.type.value,) + if self.type.value == AlbumSearchQuery.Type.YEAR_RANGE: + hash_tuple += (self.year_range,) + elif self.type.value == AlbumSearchQuery.Type.GENRE: + hash_tuple += (self.genre.name,) + self._strhash = hashlib.sha1(bytes(str(hash_tuple), "utf8")).hexdigest() return self._strhash diff --git a/sublime/app.py b/sublime/app.py index a75497e..de02544 100644 --- a/sublime/app.py +++ b/sublime/app.py @@ -653,7 +653,7 @@ class SublimeMusicApp(Gtk.Application): self.app_config.state.current_tab = "albums" self.app_config.state.selected_album_id = album_id.get_string() - self.update_window(force=True) + self.update_window() def on_go_to_artist(self, action: Any, artist_id: GLib.Variant): self.app_config.state.current_tab = "artists" diff --git a/sublime/ui/albums.py b/sublime/ui/albums.py index a2f1842..05107ca 100644 --- a/sublime/ui/albums.py +++ b/sublime/ui/albums.py @@ -554,7 +554,7 @@ class AlbumsGrid(Gtk.Overlay): halign=Gtk.Align.CENTER, selection_mode=Gtk.SelectionMode.SINGLE, ) - flowbox.set_max_children_per_line(10) + flowbox.set_max_children_per_line(7) return flowbox self.grid_top = create_flowbox() @@ -658,11 +658,9 @@ class AlbumsGrid(Gtk.Overlay): if self.sort_dir == "descending" and selected_index: selected_index = len(self.current_models) - selected_index - 1 - selection_changed = selected_index != self.currently_selected_index - self.currently_selected_index = selected_index self.reflow_grids( force_reload_from_master=force_grid_reload_from_master, - selection_changed=selection_changed, + selected_index=selected_index, models=self.current_models, ) self.spinner.hide() @@ -703,7 +701,10 @@ class AlbumsGrid(Gtk.Overlay): for c in self.error_container.get_children(): self.error_container.remove(c) - if is_partial and self.current_query.type != AlbumSearchQuery.Type.RANDOM: + if is_partial and ( + len(albums) == 0 + or self.current_query.type != AlbumSearchQuery.Type.RANDOM + ): load_error = LoadError( "Album list", "load albums", @@ -779,14 +780,17 @@ class AlbumsGrid(Gtk.Overlay): # add extra padding. # 200 + (10 * 2) + (5 * 2) = 230 # picture + (padding * 2) + (margin * 2) - new_items_per_row = min((rect.width // 230), 10) + new_items_per_row = min((rect.width // 230), 7) if new_items_per_row != self.items_per_row: self.items_per_row = new_items_per_row self.detail_box_inner.set_size_request( self.items_per_row * 230 - 10, -1, ) - self.reflow_grids(force_reload_from_master=True) + self.reflow_grids( + force_reload_from_master=True, + selected_index=self.currently_selected_index, + ) # Helper Methods # ========================================================================= @@ -846,17 +850,18 @@ class AlbumsGrid(Gtk.Overlay): def reflow_grids( self, force_reload_from_master: bool = False, - selection_changed: bool = False, + selected_index: int = None, models: List[_AlbumModel] = None, ): # Calculate the page that the currently_selected_index is in. If it's a # different page, then update the window. - page_changed = False - if self.currently_selected_index is not None: - page_of_selected_index = self.currently_selected_index // self.page_size + if selected_index is not None: + page_of_selected_index = selected_index // self.page_size if page_of_selected_index != self.page: - page_changed = True - self.page = page_of_selected_index + self.emit( + "refresh-window", {"album_page": page_of_selected_index}, False + ) + return page_offset = self.page_size * self.page # Calculate the look-at window. @@ -874,14 +879,14 @@ class AlbumsGrid(Gtk.Overlay): # Determine where the cuttoff is between the top and bottom grids. entries_before_fold = self.page_size - if self.currently_selected_index is not None and self.items_per_row: - relative_selected_index = self.currently_selected_index - page_offset + if selected_index is not None and self.items_per_row: + relative_selected_index = selected_index - page_offset entries_before_fold = ( (relative_selected_index // self.items_per_row) + 1 ) * self.items_per_row # Unreveal the current album details first - if self.currently_selected_index is None: + if selected_index is None: self.detail_box_revealer.set_reveal_child(False) if force_reload_from_master: @@ -893,7 +898,7 @@ class AlbumsGrid(Gtk.Overlay): self.list_store_bottom.splice( 0, len(self.list_store_bottom), window[entries_before_fold:], ) - elif self.currently_selected_index or entries_before_fold != self.page_size: + elif selected_index or entries_before_fold != self.page_size: # This case handles when the selection changes and the entries need to be # re-allocated to the top and bottom grids # Move entries between the two stores. @@ -913,14 +918,14 @@ class AlbumsGrid(Gtk.Overlay): self.list_store_bottom.splice(0, 0, self.list_store_top[-diff:]) self.list_store_top.splice(top_store_len - diff, diff, []) - if self.currently_selected_index is not None: - relative_selected_index = self.currently_selected_index - page_offset + if selected_index is not None: + relative_selected_index = selected_index - page_offset to_select = self.grid_top.get_child_at_index(relative_selected_index) if not to_select: return self.grid_top.select_child(to_select) - if not selection_changed: + if self.currently_selected_index == selected_index: return for c in self.detail_box_inner.get_children(): @@ -944,9 +949,4 @@ class AlbumsGrid(Gtk.Overlay): self.grid_top.unselect_all() self.grid_bottom.unselect_all() - # If we had to change the page to select the index, then update the window. It - # should basically be a no-op. - if page_changed: - self.emit( - "refresh-window", {"album_page": self.page}, False, - ) + self.currently_selected_index = selected_index diff --git a/sublime/ui/playlists.py b/sublime/ui/playlists.py index 2cdceaa..90134a8 100644 --- a/sublime/ui/playlists.py +++ b/sublime/ui/playlists.py @@ -556,6 +556,9 @@ class PlaylistDetailPanel(Gtk.Overlay): # and the expensive parts of the second loop are avoided if the IDs haven't # changed. song_ids, songs = [], [] + if len(self._current_song_ids) != len(playlist.songs): + force = True + for i, c in enumerate(playlist.songs): if i >= len(self._current_song_ids) or c.id != self._current_song_ids[i]: force = True