from typing import Optional from PyQt6.QtCore import QEvent, Qt from PyQt6.QtWidgets import QDialog, QListWidgetItem from classes import MusicMusterSignals from dbconfig import scoped_session from helpers import ( ask_yes_no, get_relative_date, ms_to_mmss, ) from models import Settings, Tracks from playlistmodel import PlaylistModel from ui.dlg_TrackSelect_ui import Ui_Dialog # type: ignore class TrackSelectDialog(QDialog): """Select track from database""" def __init__( self, session: scoped_session, new_row_number: int, model: PlaylistModel, add_to_header: Optional[bool] = False, *args, **kwargs, ) -> None: """ Subclassed QDialog to manage track selection """ super().__init__(*args, **kwargs) self.session = session self.new_row_number = new_row_number self.model = model self.add_to_header = add_to_header self.ui = Ui_Dialog() self.ui.setupUi(self) self.ui.btnAdd.clicked.connect(self.add_selected) self.ui.btnAddClose.clicked.connect(self.add_selected_and_close) self.ui.btnClose.clicked.connect(self.close) self.ui.matchList.itemDoubleClicked.connect(self.add_selected) self.ui.matchList.itemSelectionChanged.connect(self.selection_changed) self.ui.radioTitle.toggled.connect(self.title_artist_toggle) self.ui.searchString.textEdited.connect(self.chars_typed) self.track: Optional[Tracks] = None self.signals = MusicMusterSignals() record = Settings.get_int_settings(self.session, "dbdialog_width") width = record.f_int or 800 record = Settings.get_int_settings(self.session, "dbdialog_height") height = record.f_int or 600 self.resize(width, height) def add_selected(self) -> None: """Handle Add button""" track = None if self.ui.matchList.selectedItems(): item = self.ui.matchList.currentItem() if item: track = item.data(Qt.ItemDataRole.UserRole) note = self.ui.txtNote.text() if not note and not track: return self.ui.txtNote.clear() self.select_searchtext() track_id = None if track: track_id = track.id else: return # Check whether track is already in playlist move_existing = False existing_prd = self.model.is_track_in_playlist(track_id) if existing_prd is not None: if ask_yes_no( "Duplicate row", "Track already in playlist. " "Move to new location?", default_yes=True, ): move_existing = True if self.add_to_header and existing_prd: # "and existing_prd" for mypy's benefit if move_existing: self.model.move_track_to_header(self.new_row_number, existing_prd, note) else: self.model.add_track_to_header(self.new_row_number, track_id) # Close dialog - we can only add one track to a header self.accept() else: if move_existing and existing_prd: # "and existing_prd" for mypy's benefit self.model.move_track_add_note(self.new_row_number, existing_prd, note) else: self.model.insert_row(self.new_row_number, track_id, note) def add_selected_and_close(self) -> None: """Handle Add and Close button""" self.add_selected() self.accept() def chars_typed(self, s: str) -> None: """Handle text typed in search box""" self.ui.matchList.clear() if len(s) > 0: if self.ui.radioTitle.isChecked(): matches = Tracks.search_titles(self.session, "%" + s) else: matches = Tracks.search_artists(self.session, "%" + s) if matches: for track in matches: last_played = None last_playdate = max( track.playdates, key=lambda p: p.lastplayed, default=None ) if last_playdate: last_played = last_playdate.lastplayed t = QListWidgetItem() track_text = ( f"{track.title} - {track.artist} " f"[{ms_to_mmss(track.duration)}] " f"({get_relative_date(last_played)})" ) t.setText(track_text) t.setData(Qt.ItemDataRole.UserRole, track) self.ui.matchList.addItem(t) def closeEvent(self, event: Optional[QEvent]) -> None: """ Override close and save dialog coordinates """ if not event: return record = Settings.get_int_settings(self.session, "dbdialog_height") if record.f_int != self.height(): record.update(self.session, {"f_int": self.height()}) record = Settings.get_int_settings(self.session, "dbdialog_width") if record.f_int != self.width(): record.update(self.session, {"f_int": self.width()}) event.accept() def keyPressEvent(self, event): """ Clear selection on ESC if there is one """ if event.key() == Qt.Key.Key_Escape: if self.ui.matchList.selectedItems(): self.ui.matchList.clearSelection() return super(TrackSelectDialog, self).keyPressEvent(event) def select_searchtext(self) -> None: """Select the searchbox""" self.ui.searchString.selectAll() self.ui.searchString.setFocus() def selection_changed(self) -> None: """Display selected track path in dialog box""" if not self.ui.matchList.selectedItems(): return item = self.ui.matchList.currentItem() track = item.data(Qt.ItemDataRole.UserRole) last_playdate = max(track.playdates, key=lambda p: p.lastplayed, default=None) if last_playdate: last_played = last_playdate.lastplayed else: last_played = None path_text = f"{track.path} ({get_relative_date(last_played)})" self.ui.dbPath.setText(path_text) def title_artist_toggle(self) -> None: """ Handle switching between searching for artists and searching for titles """ # Logic is handled already in chars_typed(), so just call that. self.chars_typed(self.ui.searchString.text())