aboutsummaryrefslogtreecommitdiffstats
path: root/dialogs/album_info_dialog.py
diff options
context:
space:
mode:
Diffstat (limited to 'dialogs/album_info_dialog.py')
-rw-r--r--dialogs/album_info_dialog.py204
1 files changed, 204 insertions, 0 deletions
diff --git a/dialogs/album_info_dialog.py b/dialogs/album_info_dialog.py
new file mode 100644
index 0000000..7ed5833
--- /dev/null
+++ b/dialogs/album_info_dialog.py
@@ -0,0 +1,204 @@
+from PyQt6.QtCore import Qt
+from PyQt6.QtWidgets import (
+ QAbstractItemView,
+ QCheckBox,
+ QDialog,
+ QHBoxLayout,
+ QHeaderView,
+ QLabel,
+ QLineEdit,
+ QListWidget,
+ QListWidgetItem,
+ QPushButton,
+ QSplitter,
+ QTableWidget,
+ QTableWidgetItem,
+ QVBoxLayout,
+ QWidget,
+)
+
+import covers
+from ui_common import ProgressStrip, Task, section_label
+
+
+class AlbumInfoDialog(QDialog):
+ def __init__(self, album: str, artist: str, count: int, parent=None):
+ super().__init__(parent)
+ self.setWindowTitle(f"Album Info · {count} track(s)")
+ self.resize(900, 640)
+ self.result_info: dict | None = None
+ self._task: Task | None = None
+ self._releases: list[dict] = []
+
+ self.album = QLineEdit(album, placeholderText="Album")
+ self.artist = QLineEdit(artist, placeholderText="Artist (optional)")
+ for edit in (self.album, self.artist):
+ edit.returnPressed.connect(self.search)
+ search = QPushButton("Search")
+ search.clicked.connect(self.search)
+ top = QHBoxLayout()
+ top.addWidget(self.album, 3)
+ top.addWidget(self.artist, 2)
+ top.addWidget(search)
+
+ self.results = QListWidget()
+ self.results.setAlternatingRowColors(True)
+ self.results.itemDoubleClicked.connect(self._apply)
+ self.results.currentItemChanged.connect(self._on_release_changed)
+
+ self.tracks = QTableWidget(0, 3)
+ self.tracks.setHorizontalHeaderLabels(["#", "Title", "Artist"])
+ self.tracks.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
+ self.tracks.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
+ self.tracks.verticalHeader().setVisible(False)
+ self.tracks.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
+ self.tracks.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
+
+ lists = QSplitter(Qt.Orientation.Horizontal)
+ releases_box = QWidget()
+ releases_layout = QVBoxLayout(releases_box)
+ releases_layout.setContentsMargins(0, 0, 0, 0)
+ releases_layout.addWidget(section_label("Releases"))
+ releases_layout.addWidget(self.results)
+ tracks_box = QWidget()
+ tracks_layout = QVBoxLayout(tracks_box)
+ tracks_layout.setContentsMargins(0, 0, 0, 0)
+ tracks_layout.addWidget(section_label("Tracklist"))
+ tracks_layout.addWidget(self.tracks)
+ lists.addWidget(releases_box)
+ lists.addWidget(tracks_box)
+ lists.setSizes([380, 520])
+
+ self.tag_titles = QCheckBox("Tag titles / artists by track number")
+ self.tag_titles.setChecked(True)
+ self.tag_titles.setToolTip("Match each row's track number against the MusicBrainz tracklist\n"
+ "and fill in TITLE and ARTIST")
+ self.status = QLabel()
+ self.apply_btn = QPushButton(f"Apply to {count} Track(s)")
+ self.apply_btn.setDefault(True)
+ self.apply_btn.setEnabled(False)
+ self.apply_btn.clicked.connect(self._apply)
+ cancel = QPushButton("Cancel")
+ cancel.clicked.connect(self.reject)
+ bottom = QHBoxLayout()
+ bottom.addWidget(self.tag_titles)
+ bottom.addWidget(self.status, 1)
+ bottom.addWidget(self.apply_btn)
+ bottom.addWidget(cancel)
+
+ self.progress = ProgressStrip()
+
+ layout = QVBoxLayout(self)
+ layout.addLayout(top)
+ layout.addWidget(self.progress)
+ layout.addWidget(lists, 1)
+ layout.addLayout(bottom)
+
+ if album.strip():
+ self.search()
+
+ def search(self):
+ album, artist = self.album.text().strip(), self.artist.text().strip()
+ if not album:
+ self.status.setText("Enter an album name to search.")
+ return
+ self.results.clear()
+ self.tracks.setRowCount(0)
+ self._releases = []
+ self.status.clear()
+ self.apply_btn.setEnabled(False)
+ self.progress.start("Searching MusicBrainz")
+ self.progress.set_detail(f"{album} - {artist}" if artist else album)
+
+ def run(_task):
+ return covers.search_releases(album, artist)
+
+ self._task = Task(run)
+ self._task.done.connect(self._on_results)
+ self._task.failed.connect(self._on_failed)
+ self._task.start()
+
+ def _stale(self) -> bool:
+ return self.sender() is not self._task
+
+ def _on_results(self, releases):
+ if self._stale():
+ return
+ self.progress.finish()
+ self._releases = list(releases)
+ for rel in self._releases:
+ label = f"{rel['title']} — {rel['artist']}"
+ item = QListWidgetItem(f"{label}\n{rel['details']}" if rel["details"] else label)
+ item.setToolTip(f"{label}\n{rel['details']}\nMBID: {rel['id']}")
+ self.results.addItem(item)
+ if self._releases:
+ self.status.setText(f"{len(self._releases)} release(s) found.")
+ self.results.setCurrentRow(0)
+ else:
+ self.status.setText("No releases found.")
+
+ def _on_failed(self, message):
+ if not self._stale():
+ self.progress.finish()
+ self.status.setText(f"Search failed: {message}")
+
+ def _current_release(self) -> dict | None:
+ row = self.results.currentRow()
+ return self._releases[row] if 0 <= row < len(self._releases) else None
+
+ def _on_release_changed(self, *_):
+ release = self._current_release()
+ self.tracks.setRowCount(0)
+ self.apply_btn.setEnabled(bool(release))
+ if not release:
+ return
+ self.progress.start("Loading tracklist")
+ self.progress.set_detail(release["title"])
+ mbid = release["id"]
+
+ def run(_task):
+ return mbid, covers.fetch_release_tracks(mbid)
+
+ self._task = Task(run)
+ self._task.done.connect(self._on_tracks)
+ self._task.failed.connect(self._on_failed)
+ self._task.start()
+
+ def _on_tracks(self, payload):
+ if self._stale():
+ return
+ self.progress.finish()
+ mbid, tracks = payload
+ release = self._current_release()
+ if not release or release["id"] != mbid:
+ return
+ self.tracks.setRowCount(len(tracks))
+ for row, track in enumerate(tracks):
+ number = f"{track['disc']}-{track['number']}" if track["disc"] > 1 else str(track["number"])
+ for col, text in enumerate((number, track["title"], track["artist"])):
+ self.tracks.setItem(row, col, QTableWidgetItem(text))
+ self.status.setText(f"{len(tracks)} track(s) in {release['title']}.")
+
+ def _apply(self, *_):
+ release = self._current_release()
+ if not release:
+ return
+ by_number: dict[int, tuple[str, str]] = {}
+ for row in range(self.tracks.rowCount()):
+ number_item = self.tracks.item(row, 0)
+ title_item = self.tracks.item(row, 1)
+ artist_item = self.tracks.item(row, 2)
+ if not number_item:
+ continue
+ number = number_item.text().split("-")[-1]
+ if number.isdigit():
+ by_number[int(number)] = (title_item.text() if title_item else "",
+ artist_item.text() if artist_item else "")
+ self.result_info = {
+ "album": release["title"],
+ "album_artist": release["artist"],
+ "mbid": release["id"],
+ "tag_tracks": self.tag_titles.isChecked(),
+ "by_number": by_number,
+ }
+ self.accept()
send patches to the email below
yukais@pinapelz.com
include the subject [PATCH repo_name]
pinapelz.com
homepage