From 78b60dba026e5811d8dc128f8f1dd125e8536d45 Mon Sep 17 00:00:00 2001 From: The Wobbler Date: Sun, 23 Feb 2025 18:05:28 +0100 Subject: [PATCH] Implemented saving of the sortorder to the .wbz.m3u --- wobuzz/player/playlist.py | 63 +++++++++++++++++++++++++++++++++++++-- wobuzz/player/track.py | 2 +- wobuzz/ui/playlist.py | 7 ++++- 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/wobuzz/player/playlist.py b/wobuzz/player/playlist.py index 11f087c..d1d56bd 100644 --- a/wobuzz/player/playlist.py +++ b/wobuzz/player/playlist.py @@ -134,7 +134,63 @@ class Playlist: self.app.gui.on_background_job_stop(process_title) def load_from_wbz(self, path): - self.load_from_m3u(path) # placeholder + file = open(path, "r") + m3u = file.read() + file.close() + + lines = m3u.split("\n") # m3u entries are separated by newlines + lines = lines[:-1] # remove last entry because it is just an empty string + + num_lines = len(lines) + + i = 0 + + process_title = f'Loading Playlist "{self.title}"' + + self.app.gui.on_background_job_start( + process_title, + f'Loading the tracks of "{self.title}".', + num_lines, + lambda: i + ) + + while i < num_lines: + line = lines[i] + + if line.startswith("#"): # comments and EXTM3U/WOBUZZM3U + if line.startswith("#SORT: "): # sort + sort_line = line[6:] # delete "#SORT: " from the line + + sorting = sort_line.split(", ") # split into the sort column specifier and the sort order + # e.g. ["0", "True"] + + del self.sorting[0] # delete first sort so the length stays at 6 + + # convert these from strings back to int and bool and append them to the sorting + self.sorting.append((int(sorting[0]), sorting[1] == "True")) + + i += 1 + + continue + + elif line.startswith("http"): # filter out urls + i += 1 + + continue + + self.append_track(Track(self.app, line, cache=i == 0)) # first track is cached + + i += 1 + + # set current track to the first track if there is no currently playing track + if self.current_track is None and self.has_tracks(): + self.current_track = self.tracks[0] + + list(self.views.values())[0].sort() # execute sort() on the first view + + self.loaded = True + + self.app.gui.on_background_job_stop(process_title) def sync(self, view, user_sort: bool=False): num_tracks = view.topLevelItemCount() @@ -206,7 +262,10 @@ class Playlist: first_view.sortItems(4, Qt.SortOrder.AscendingOrder) self.sync(first_view) - wbz_data = "#WOBUZZM3U\n" + wbz_data = "#WOBUZZM3U\n" # header + + for sort_column, order in self.sorting: + wbz_data += f"#SORT: {sort_column}, {order}\n" for track in self.tracks: wbz_data += f"{track.path}\n" diff --git a/wobuzz/player/track.py b/wobuzz/player/track.py index 77d1d31..89bcf7e 100644 --- a/wobuzz/player/track.py +++ b/wobuzz/player/track.py @@ -67,7 +67,7 @@ class Track: self.duration = len(self.audio) # track duration in milliseconds - self.tags = TinyTag.get(self.path, ignore_errors=True, duration=False, image=True) # metadata with images + self.tags = TinyTag.get(self.path, ignore_errors=True, duration=False, image=True) # metadata with images self.cached = True diff --git a/wobuzz/ui/playlist.py b/wobuzz/ui/playlist.py index 16a1e10..f37df00 100644 --- a/wobuzz/ui/playlist.py +++ b/wobuzz/ui/playlist.py @@ -9,6 +9,7 @@ from .track import TrackItem class PlaylistView(QTreeWidget): itemDropped = pyqtSignal(QTreeWidget, list) + sort_signal = pyqtSignal(int, Qt.SortOrder) playing_mark = QIcon.fromTheme(QIcon.ThemeIcon.MediaPlaybackStart) @@ -42,6 +43,7 @@ class PlaylistView(QTreeWidget): self.itemActivated.connect(self.on_track_activation) self.header.sectionClicked.connect(self.on_header_click) + self.sort_signal.connect(self.sortItems) def on_header_click(self, section_index: int): if section_index == 0: # this would just invert the current sorting @@ -73,7 +75,10 @@ class PlaylistView(QTreeWidget): # convert True/False to Qt.SortOrder.AscendingOrder/Qt.SortOrder.DescendingOrder qorder = Qt.SortOrder.AscendingOrder if order else Qt.SortOrder.DescendingOrder - self.sortItems(index, qorder) + # somehow, QTreeWidget.sortItems() cant be called from a thread, so we have to use a signal to execute it + # in the main thread + self.sort_signal.emit(index, qorder) + # self.sortItems(index, qorder) self.on_sort()