diff --git a/app/musicmuster.py b/app/musicmuster.py
index 19bfabd..49bee91 100755
--- a/app/musicmuster.py
+++ b/app/musicmuster.py
@@ -22,6 +22,8 @@ from typing import (
Sequence,
)
+from sqlalchemy import text
+
from PyQt6.QtCore import (
pyqtSignal,
QDate,
@@ -668,6 +670,7 @@ class Window(QMainWindow, Ui_MainWindow):
lambda: self.tabPlaylist.currentWidget().lookup_row_in_wikipedia()
)
self.actionSearch.triggered.connect(self.search_playlist)
+ self.actionSelect_duplicate_rows.triggered.connect(self.select_duplicate_rows)
self.actionSelect_next_track.triggered.connect(self.select_next_row)
self.actionSelect_previous_track.triggered.connect(self.select_previous_row)
self.actionMoveUnplayed.triggered.connect(self.move_unplayed)
@@ -1487,6 +1490,31 @@ class Window(QMainWindow, Ui_MainWindow):
self.visible_playlist_tab().set_search(self.txtSearch.text())
self.enable_play_next_controls()
+ def select_duplicate_rows(self) -> None:
+ """
+ Select the last of any rows with duplicate tracks in current playlist.
+ This allows the selection to typically come towards the end of the playlist away
+ from any show specific sections.
+ If there a track is selected on three or more rows, only the last one is selected.
+ """
+
+ visible_playlist_id = self.visible_playlist_tab().playlist_id
+ # Get row number of duplicate rows
+ sql = text(f"""
+ SELECT max(plr_rownum)
+ FROM playlist_rows
+ WHERE playlist_id = {visible_playlist_id}
+ AND track_id != 0
+ GROUP BY track_id
+ HAVING count(id) > 1
+ """)
+
+ with Session() as session:
+ row_numbers = [int(a) for a in session.execute(sql).scalars().all()]
+ if row_numbers:
+ self.visible_playlist_tab().select_rows(row_numbers)
+ self.statusbar.showMessage(f"{len(row_numbers)} duplicate rows selected", 10000)
+
def select_next_row(self) -> None:
"""Select next or first row in playlist"""
diff --git a/app/playlists.py b/app/playlists.py
index c75039f..a3aff53 100644
--- a/app/playlists.py
+++ b/app/playlists.py
@@ -979,6 +979,21 @@ class PlaylistTab(QTableWidget):
self.selectRow(row_number)
+ def select_rows(self, rows: List[int]) -> None:
+ """
+ Select rows that are passed
+ """
+
+ # Clear any selected rows to avoid confustion
+ self.clear_selection()
+ # We need to be in MultiSelection mode
+ self.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection)
+ # Select the rows
+ for row in rows:
+ self.selectRow(row)
+ # Reset selection mode
+ self.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
+
def tab_visible(self) -> None:
"""Called when tab becomes visible"""
diff --git a/app/ui/main_window.ui b/app/ui/main_window.ui
index 538a6d6..14a028a 100644
--- a/app/ui/main_window.ui
+++ b/app/ui/main_window.ui
@@ -746,6 +746,8 @@ padding-left: 8px;
+
+
@@ -1132,6 +1134,11 @@ padding-left: 8px;
Ctrl+S
+
+
+ Select duplicate rows...
+
+
diff --git a/app/ui/main_window_ui.py b/app/ui/main_window_ui.py
index 40adf63..4488a3e 100644
--- a/app/ui/main_window_ui.py
+++ b/app/ui/main_window_ui.py
@@ -1,6 +1,6 @@
# Form implementation generated from reading ui file 'app/ui/main_window.ui'
#
-# Created by: PyQt6 UI code generator 6.5.2
+# Created by: PyQt6 UI code generator 6.5.3
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
@@ -453,6 +453,8 @@ class Ui_MainWindow(object):
self.actionSearch_title_in_Wikipedia.setObjectName("actionSearch_title_in_Wikipedia")
self.actionSearch_title_in_Songfacts = QtGui.QAction(parent=MainWindow)
self.actionSearch_title_in_Songfacts.setObjectName("actionSearch_title_in_Songfacts")
+ self.actionSelect_duplicate_rows = QtGui.QAction(parent=MainWindow)
+ self.actionSelect_duplicate_rows.setObjectName("actionSelect_duplicate_rows")
self.menuFile.addAction(self.actionNewPlaylist)
self.menuFile.addAction(self.actionNew_from_template)
self.menuFile.addAction(self.actionOpenPlaylist)
@@ -461,6 +463,8 @@ class Ui_MainWindow(object):
self.menuFile.addAction(self.actionDeletePlaylist)
self.menuFile.addAction(self.actionExport_playlist)
self.menuFile.addSeparator()
+ self.menuFile.addAction(self.actionSelect_duplicate_rows)
+ self.menuFile.addSeparator()
self.menuFile.addAction(self.actionMoveSelected)
self.menuFile.addAction(self.actionMoveUnplayed)
self.menuFile.addAction(self.actionDownload_CSV_of_played_tracks)
@@ -595,5 +599,6 @@ class Ui_MainWindow(object):
self.actionSearch_title_in_Wikipedia.setShortcut(_translate("MainWindow", "Ctrl+W"))
self.actionSearch_title_in_Songfacts.setText(_translate("MainWindow", "Search title in Songfacts"))
self.actionSearch_title_in_Songfacts.setShortcut(_translate("MainWindow", "Ctrl+S"))
+ self.actionSelect_duplicate_rows.setText(_translate("MainWindow", "Select duplicate rows..."))
from infotabs import InfoTabs
from pyqtgraph import PlotWidget