Revise menu, selected tracks duration summing OK

This commit is contained in:
Keith Edmunds 2022-08-07 16:15:11 +01:00
parent 91841cfc18
commit 89781c0a94
5 changed files with 364 additions and 309 deletions

View File

@ -36,7 +36,7 @@ class Config(object):
DEFAULT_COLUMN_WIDTH = 200 DEFAULT_COLUMN_WIDTH = 200
DEFAULT_IMPORT_DIRECTORY = "/home/kae/Nextcloud/tmp" DEFAULT_IMPORT_DIRECTORY = "/home/kae/Nextcloud/tmp"
DEFAULT_OUTPUT_DIRECTORY = "/home/kae/music/Singles" DEFAULT_OUTPUT_DIRECTORY = "/home/kae/music/Singles"
DISPLAY_SQL = True DISPLAY_SQL = False
ERRORS_TO = ['kae@midnighthax.com'] ERRORS_TO = ['kae@midnighthax.com']
FADE_STEPS = 20 FADE_STEPS = 20
FADE_TIME = 3000 FADE_TIME = 3000

View File

@ -20,7 +20,7 @@ from PyQt5.QtWidgets import (
# QDialog, # QDialog,
# QFileDialog, # QFileDialog,
# QInputDialog, # QInputDialog,
# QLabel, QLabel,
# QLineEdit, # QLineEdit,
# QListWidgetItem, # QListWidgetItem,
QMainWindow, QMainWindow,
@ -84,8 +84,8 @@ class Window(QMainWindow, Ui_MainWindow):
# self.previous_track_position: Optional[int] = None # self.previous_track_position: Optional[int] = None
# #
# self.set_main_window_size() # self.set_main_window_size()
# self.lblSumPlaytime: QLabel = QLabel("") self.lblSumPlaytime = QLabel("")
# self.statusbar.addPermanentWidget(self.lblSumPlaytime) self.statusbar.addPermanentWidget(self.lblSumPlaytime)
# self.txtSearch = QLineEdit() # self.txtSearch = QLineEdit()
# self.statusbar.addWidget(self.txtSearch) # self.statusbar.addWidget(self.txtSearch)
# self.txtSearch.setHidden(True) # self.txtSearch.setHidden(True)

View File

@ -1,7 +1,7 @@
from collections import namedtuple from collections import namedtuple
from typing import List, Optional from typing import List, Optional
# from PyQt5 import QtCore from PyQt5 import QtCore
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtGui import ( from PyQt5.QtGui import (
QColor, QColor,
@ -75,7 +75,6 @@ columns["row_notes"] = Column(idx=8, heading=Config.COLUMN_NAME_NOTES)
# """https://stackoverflow.com/questions/72790705/dont-select-text-in-qtablewidget-cell-when-editing/72792962#72792962""" # """https://stackoverflow.com/questions/72790705/dont-select-text-in-qtablewidget-cell-when-editing/72792962#72792962"""
# #
# def createEditor(self, parent, option, index): # def createEditor(self, parent, option, index):
# import ipdb; ipdb.set_trace()
# editor = super().createEditor(parent, option, index) # editor = super().createEditor(parent, option, index)
# if isinstance(editor, QLineEdit): # if isinstance(editor, QLineEdit):
# def deselect(): # def deselect():
@ -92,9 +91,10 @@ class PlaylistTab(QTableWidget):
# cellEditingEnded = QtCore.pyqtSignal() # cellEditingEnded = QtCore.pyqtSignal()
# Qt.UserRoles # Qt.UserRoles
ROW_METADATA = Qt.UserRole ROW_FLAGS = Qt.UserRole
ROW_TRACK_ID = Qt.UserRole + 1 ROW_TRACK_ID = Qt.UserRole + 1
PLAYLISTROW_ID = Qt.UserRole + 2 ROW_DURATION = Qt.UserRole + 2
PLAYLISTROW_ID = Qt.UserRole + 3
def __init__(self, musicmuster: QMainWindow, session: Session, def __init__(self, musicmuster: QMainWindow, session: Session,
playlist_id: int, *args, **kwargs) -> None: playlist_id: int, *args, **kwargs) -> None:
@ -135,20 +135,20 @@ class PlaylistTab(QTableWidget):
self.setDropIndicatorShown(True) self.setDropIndicatorShown(True)
self.setDragDropMode(QAbstractItemView.InternalMove) self.setDragDropMode(QAbstractItemView.InternalMove)
self.setDragEnabled(False) self.setDragEnabled(False)
#
# # This property defines how the widget shows a context menu # This property defines how the widget shows a context menu
# self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
# # This signal is emitted when the widget's contextMenuPolicy is # This signal is emitted when the widget's contextMenuPolicy is
# # Qt::CustomContextMenu, and the user has requested a context # Qt::CustomContextMenu, and the user has requested a context
# # menu on the widget. # menu on the widget.
# self.customContextMenuRequested.connect(self._context_menu) self.customContextMenuRequested.connect(self._context_menu)
# self.viewport().installEventFilter(self) self.viewport().installEventFilter(self)
#
# self.itemSelectionChanged.connect(self._select_event) self.itemSelectionChanged.connect(self._select_event)
#
self.row_filter: Optional[str] = None self.row_filter: Optional[str] = None
# self.editing_cell: bool = False # self.editing_cell: bool = False
# self.selecting_in_progress = False self.selecting_in_progress = False
# Connect signals # Connect signals
# self.cellChanged.connect(self._cell_changed) # self.cellChanged.connect(self._cell_changed)
# self.cellClicked.connect(self._edit_note_cell) # self.cellClicked.connect(self._edit_note_cell)
@ -156,7 +156,7 @@ class PlaylistTab(QTableWidget):
# self.cellEditingStarted.connect(self._cell_edit_started) # self.cellEditingStarted.connect(self._cell_edit_started)
# self.doubleClicked.connect(self._edit_cell) # self.doubleClicked.connect(self._edit_cell)
self.horizontalHeader().sectionResized.connect(self._column_resize) self.horizontalHeader().sectionResized.connect(self._column_resize)
#
# Now load our tracks and notes # Now load our tracks and notes
self.populate(session, self.playlist_id) self.populate(session, self.playlist_id)
@ -236,58 +236,90 @@ class PlaylistTab(QTableWidget):
with Session() as session: # checked with Session() as session: # checked
self.save_playlist(session) self.save_playlist(session)
self.update_display(session) self.update_display(session)
#
# def edit(self, index, trigger, event): # review # def edit(self, index, trigger, event):
# result = super(PlaylistTab, self).edit(index, trigger, event) # result = super(PlaylistTab, self).edit(index, trigger, event)
# if result: # if result:
# self.cellEditingStarted.emit(index.row(), index.column()) # self.cellEditingStarted.emit(index.row(), index.column())
# return result # return result
#
# def eventFilter(self, source, event): # review def eventFilter(self, source, event):
# """Used to process context (right-click) menu, which is defined here""" """Used to process context (right-click) menu, which is defined here"""
#
# if (event.type() == QtCore.QEvent.MouseButtonPress and # noqa W504 if (event.type() == QtCore.QEvent.MouseButtonPress and # noqa W504
# event.buttons() == QtCore.Qt.RightButton and # noqa W504 event.buttons() == QtCore.Qt.RightButton and # noqa W504
# source is self.viewport()): source is self.viewport()):
# item = self.itemAt(event.pos()) item = self.itemAt(event.pos())
# if item is not None: if item is not None:
# row = item.row() row_number = item.row()
# log.debug(f"playlist.eventFilter(): Right-click on row {row}") track_id = self._get_row_track_id(row_number)
# current = row == self._get_current_track_row() if track_id:
# next_row = row == self._get_next_track_row() current = row_number == self._get_current_track_row()
# self.menu = QMenu(self) next_row = row_number == self._get_next_track_row()
# act_info = self.menu.addAction('Info') else:
# act_info.triggered.connect(lambda: self._info_row(row)) current = next_row = False
# self.menu.addSeparator()
# if row not in self._get_notes_rows(): self.menu = QMenu(self)
# act_mplayer = self.menu.addAction(
# "Play track with mplayer") if track_id:
# act_mplayer.triggered.connect( # Info
# lambda: self._mplayer(row)) act_info = self.menu.addAction('Info')
# self.menu.addSeparator() act_info.triggered.connect(
# if not current and not next_row: lambda: self._info_row(track_id)
# act_setnext = self.menu.addAction("Set next") )
# with Session() as session:
# act_setnext.triggered.connect( self.menu.addSeparator()
# lambda: self._set_next(row, session))
# act_copypath = self.menu.addAction("Copy track path") # Play with mplayer
# act_copypath.triggered.connect( act_mplayer = self.menu.addAction(
# lambda: self._copy_path(row)) "Play with mplayer")
# if not current: act_mplayer.triggered.connect(
# act_rescan = self.menu.addAction("Rescan track") lambda: self._mplayer_play(track_id))
# act_rescan.triggered.connect(lambda: self._rescan(row))
# act_audacity = self.menu.addAction( # Set next
# "Open track in Audacity") if not current and not next_row:
# act_audacity.triggered.connect( act_setnext = self.menu.addAction("Set next")
# lambda: self._audacity(row)) with Session() as session:
# if not current and not next_row: act_setnext.triggered.connect(
# act_move = self.menu.addAction('Move to playlist...') lambda: self._set_next(session, row_number))
# act_move.triggered.connect(self.musicmuster.move_selected)
# self.menu.addSeparator() # Open in Audacity
# act_delete = self.menu.addAction('Delete') if not current:
# act_delete.triggered.connect(self._delete_rows) act_audacity = self.menu.addAction(
# "Open in Audacity")
# return super(PlaylistTab, self).eventFilter(source, event) act_audacity.triggered.connect(
lambda: self._open_in_audacity(track_id))
# Rescan
act_rescan = self.menu.addAction("Rescan")
act_rescan.triggered.connect(
lambda: self._rescan(track_id)
)
self.menu.addSeparator()
# Remove track
act_remove_track = self.menu.addAction('Remove track')
act_remove_track.triggered.connect(
lambda: self._remove_track(row_number)
)
else:
# Add track to section header (ie, make this a track
# row)
act_add_track = self.menu.addAction('Add track')
act_add_track.triggered.connect(self._add_track)
if not current and not next_row:
act_move = self.menu.addAction('Move to playlist...')
act_move.triggered.connect(self.musicmuster.move_selected)
self.menu.addSeparator()
# Remove row
act_delete = self.menu.addAction('Remove row')
act_delete.triggered.connect(self._delete_rows)
return super(PlaylistTab, self).eventFilter(source, event)
def mouseReleaseEvent(self, event): def mouseReleaseEvent(self, event):
""" """
@ -301,7 +333,7 @@ class PlaylistTab(QTableWidget):
super().mouseReleaseEvent(event) super().mouseReleaseEvent(event)
# # ########## Externally called functions ########## # # ########## Externally called functions ##########
#
def clear_selection(self) -> None: def clear_selection(self) -> None:
"""Unselect all tracks and reset drag mode""" """Unselect all tracks and reset drag mode"""
@ -399,8 +431,9 @@ class PlaylistTab(QTableWidget):
# Add row metadata to userdata column # Add row metadata to userdata column
userdata_item = QTableWidgetItem() userdata_item = QTableWidgetItem()
userdata_item.setData(self.ROW_METADATA, 0) userdata_item.setData(self.ROW_FLAGS, 0)
userdata_item.setData(self.PLAYLISTROW_ID, row_data.id) userdata_item.setData(self.PLAYLISTROW_ID, row_data.id)
userdata_item.setData(self.ROW_TRACK_ID, row_data.track_id)
self.setItem(row, columns['userdata'].idx, userdata_item) self.setItem(row, columns['userdata'].idx, userdata_item)
if row_data.track_id: if row_data.track_id:
@ -420,6 +453,7 @@ class PlaylistTab(QTableWidget):
duration_item = QTableWidgetItem( duration_item = QTableWidgetItem(
helpers.ms_to_mmss(row_data.track.duration)) helpers.ms_to_mmss(row_data.track.duration))
self.setItem(row, columns['duration'].idx, duration_item) self.setItem(row, columns['duration'].idx, duration_item)
self._set_row_duration(row, row_data.track.duration)
start_item = QTableWidgetItem() start_item = QTableWidgetItem()
self.setItem(row, columns['start_time'].idx, start_item) self.setItem(row, columns['start_time'].idx, start_item)
@ -441,9 +475,6 @@ class PlaylistTab(QTableWidget):
if not helpers.file_is_readable(row_data.track.path): if not helpers.file_is_readable(row_data.track.path):
self._set_unreadable_row(row) self._set_unreadable_row(row)
# Save track_id
userdata_item.setData(self.ROW_TRACK_ID, row_data.track_id)
else: else:
# This is a section header so make empty items (row # This is a section header so make empty items (row
# background won't be coloured without items present). Any # background won't be coloured without items present). Any
@ -484,7 +515,7 @@ class PlaylistTab(QTableWidget):
# # Put an item in COL_USERDATA for later # # Put an item in COL_USERDATA for later
# item: QTableWidgetItem = QTableWidgetItem() # item: QTableWidgetItem = QTableWidgetItem()
# # Add row metadata # # Add row metadata
# item.setData(self.ROW_METADATA, 0) # item.setData(self.ROW_FLAGS, 0)
# self.setItem(row, self.COL_USERDATA, item) # self.setItem(row, self.COL_USERDATA, item)
# #
# # Add track details to columns # # Add track details to columns
@ -618,7 +649,7 @@ class PlaylistTab(QTableWidget):
# search_from = current_row + 1 # search_from = current_row + 1
# next_row = self._find_next_track_row(search_from) # next_row = self._find_next_track_row(search_from)
# if next_row: # if next_row:
# self._set_next(next_row, session) # self._set_next(session, next_row)
# #
# # Update display # # Update display
# self.update_display(session) # self.update_display(session)
@ -863,7 +894,7 @@ class PlaylistTab(QTableWidget):
# return None # return None
# #
# with Session() as session: # with Session() as session:
# self._set_next(row, session) # self._set_next(session, row)
def update_display(self, session, clear_selection: bool = True) -> None: def update_display(self, session, clear_selection: bool = True) -> None:
""" """
@ -1059,18 +1090,25 @@ class PlaylistTab(QTableWidget):
session, section_start_row, section_time, no_end=True) session, section_start_row, section_time, no_end=True)
# #
# # ########## Internally called functions ########## # # ########## Internally called functions ##########
#
# def _audacity(self, row: int) -> None: def _add_track(self, row: int) -> None:
# """Open track in Audacity. Audacity must be already running""" """Add a track to a section header making it a normal track row"""
#
# log.debug(f"_audacity({row})") print("playlists._add_track() not yet implemented")
#
# if row in self._get_notes_rows(): def _open_in_audacity(self, track_id: int) -> None:
# return None """Open track in Audacity. Audacity must be already running"""
#
# with Session() as session: with Session() as session:
# track: Tracks = self._get_row_track_object(row, session) track = session.get(Tracks, track_id)
# open_in_audacity(track.path) if not track:
log.error(
f"playlists._open_in_audacity({track_id=}): "
"Track not found"
)
return
open_in_audacity(track.path)
def _calculate_end_time(self, start: Optional[datetime], def _calculate_end_time(self, start: Optional[datetime],
duration: int) -> Optional[datetime]: duration: int) -> Optional[datetime]:
@ -1080,11 +1118,11 @@ class PlaylistTab(QTableWidget):
return None return None
return start + timedelta(milliseconds=duration) return start + timedelta(milliseconds=duration)
#
# def _context_menu(self, pos): # review def _context_menu(self, pos):
#
# assert self.menu assert self.menu
# self.menu.exec_(self.mapToGlobal(pos)) self.menu.exec_(self.mapToGlobal(pos))
# #
# def _copy_path(self, row: int) -> None: # def _copy_path(self, row: int) -> None:
# """ # """
@ -1364,14 +1402,16 @@ class PlaylistTab(QTableWidget):
"""Return rows marked as played, or None""" """Return rows marked as played, or None"""
return self._meta_search(RowMeta.PLAYED, one=False) return self._meta_search(RowMeta.PLAYED, one=False)
#
# def _get_row_duration(self, row: int) -> int: def _get_row_duration(self, row: int) -> int:
# """Return duration associated with this row""" """Return duration associated with this row"""
#
# try: duration = (self.item(row, columns['userdata'].idx)
# return self.item(row, self.COL_USERDATA).data(self.ROW_DURATION) .data(self.ROW_DURATION))
# except: if duration:
# return 0 return duration
else:
return 0
# #
# def _get_row_end_time(self, row) -> Optional[datetime]: # def _get_row_end_time(self, row) -> Optional[datetime]:
# """ # """
@ -1434,34 +1474,32 @@ class PlaylistTab(QTableWidget):
"""Return rows marked as unreadable, or None""" """Return rows marked as unreadable, or None"""
return self._meta_search(RowMeta.UNREADABLE, one=False) return self._meta_search(RowMeta.UNREADABLE, one=False)
#
# def _info_row(self, row: int) -> None: def _info_row(self, track_id: int) -> None:
# """Display popup with info re row""" """Display popup with info re row"""
#
# txt: str with Session() as session:
# track = session.get(Tracks, track_id)
# with Session() as session: if track:
# if row in self._get_notes_rows(): txt = (
# note: Notes = self._get_row_notes_object(row, session) f"Title: {track.title}\n"
# txt = note.note f"Artist: {track.artist}\n"
# else: f"Track ID: {track.id}\n"
# track: Tracks = self._get_row_track_object(row, session) f"Track duration: {helpers.ms_to_mmss(track.duration)}\n"
# txt = ( f"Track fade at: {helpers.ms_to_mmss(track.fade_at)}\n"
# f"Title: {track.title}\n" f"Track silence at: {helpers.ms_to_mmss(track.silence_at)}"
# f"Artist: {track.artist}\n" "\n\n"
# f"Track ID: {track.id}\n" f"Path: {track.path}\n"
# f"Track duration: {helpers.ms_to_mmss(track.duration)}\n" )
# f"Track fade at: {helpers.ms_to_mmss(track.fade_at)}\n" else:
# f"Track silence at: {helpers.ms_to_mmss(track.silence_at)}" txt = f"Can't find {track_id=}"
# "\n\n"
# f"Path: {track.path}\n" info: QMessageBox = QMessageBox(self)
# ) info.setIcon(QMessageBox.Information)
# info: QMessageBox = QMessageBox(self) info.setText(txt)
# info.setIcon(QMessageBox.Information) info.setStandardButtons(QMessageBox.Ok)
# info.setText(txt) info.setDefaultButton(QMessageBox.Cancel)
# info.setStandardButtons(QMessageBox.Ok) info.exec()
# info.setDefaultButton(QMessageBox.Cancel)
# info.exec()
# #
# def _insert_note(self, session: Session, note: Notes, # def _insert_note(self, session: Session, note: Notes,
# row: Optional[int] = None, repaint: bool = True) -> None: # row: Optional[int] = None, repaint: bool = True) -> None:
@ -1539,7 +1577,7 @@ class PlaylistTab(QTableWidget):
# #
# new_metadata: int = self._meta_get(row) & ~(1 << attribute) # new_metadata: int = self._meta_get(row) & ~(1 << attribute)
# self.item(row, self.COL_USERDATA).setData( # self.item(row, self.COL_USERDATA).setData(
# self.ROW_METADATA, new_metadata) # self.ROW_FLAGS, new_metadata)
# #
# def _meta_clear_next(self) -> None: # def _meta_clear_next(self) -> None:
# """ # """
@ -1554,7 +1592,7 @@ class PlaylistTab(QTableWidget):
"""Return row metadata""" """Return row metadata"""
return (self.item(row, columns['userdata'].idx) return (self.item(row, columns['userdata'].idx)
.data(self.ROW_METADATA)) .data(self.ROW_FLAGS))
# #
# def _meta_notset(self, metadata: int) -> List[int]: # def _meta_notset(self, metadata: int) -> List[int]:
# """ # """
@ -1612,38 +1650,58 @@ class PlaylistTab(QTableWidget):
# else: # else:
# new_metadata = self._meta_get(row) | (1 << attribute) # new_metadata = self._meta_get(row) | (1 << attribute)
# self.item(row, self.COL_USERDATA).setData( # self.item(row, self.COL_USERDATA).setData(
# self.ROW_METADATA, new_metadata) # self.ROW_FLAGS, new_metadata)
#
# def _mplayer(self, row: int) -> None: def _mplayer_play(self, track_id: int) -> None:
# """Play track with mplayer""" """Play track with mplayer"""
#
# log.debug(f"_mplayer({row})") with Session() as session:
# track = session.get(Tracks, track_id)
# if row in self._get_notes_rows(): if not track:
# return None log.error(
# f"playlists._mplayer_play({track_id=}): "
# with Session() as session: "Track not found"
# track: Tracks = self._get_row_track_object(row, session) )
# cmd_list = ['gmplayer', '-vc', 'null', '-vo', 'null', track.path] return
# thread = threading.Thread(
# target=self._run_subprocess, args=(cmd_list,)) cmd_list = ['gmplayer', '-vc', 'null', '-vo', 'null', track.path]
# thread.start() thread = threading.Thread(
# target=self._run_subprocess, args=(cmd_list,))
# def _rescan(self, row: int) -> None: thread.start()
# """
# If passed row is track row, rescan it. def _remove_track(self, row: int) -> None:
# Otherwise, return None. """Remove track from row, making it a section header"""
# """
# # Update playlist_rows record
# log.debug(f"_rescan({row=})") plr = session.get(PlaylistRows, self._get_playlistrow_id(row))
# plr.track_id = None
# with Session() as session: plr.save()
# if row in self._get_track_rows():
# track: Tracks = self._get_row_track_object(row, session) # Clear track text items
# if track: for i in range(2, len(columns) - 1):
# track.rescan(session) self.item(row, i).setText("")
# self._update_row(session, row, track) # Set note text in correct column for section head
# self.item(row, 1).setText(plr.note)
# Remove row duration
self._set_row_duration(row, 0)
# And refresh display
self.update_display()
def _rescan(self, track_id: int) -> None:
"""Rescan track"""
with Session() as session:
track = session.get(Tracks, track_id)
if not track:
log.error(
f"playlists._open_in_audacity({track_id=}): "
"Track not found"
)
return
track.rescan(session)
self._update_row(session, row, track)
# def _run_subprocess(self, args): # def _run_subprocess(self, args):
# """Run args in subprocess""" # """Run args in subprocess"""
# #
@ -1654,12 +1712,12 @@ class PlaylistTab(QTableWidget):
# #
# self._clear_current_track_row() # self._clear_current_track_row()
# self._meta_set_attribute(row, RowMeta.CURRENT) # self._meta_set_attribute(row, RowMeta.CURRENT)
#
# def _set_next_track_row(self, row: int) -> None: def _set_next_track_row(self, row: int) -> None:
# """Mark this row as next track""" """Mark this row as next track"""
#
# self._meta_clear_next() self._meta_clear_next()
# self._meta_set_attribute(row, RowMeta.NEXT) self._meta_set_attribute(row, RowMeta.NEXT)
# #
# def _set_note_row(self, row: int) -> None: # def _set_note_row(self, row: int) -> None:
# """Mark this row as a note""" # """Mark this row as a note"""
@ -1676,36 +1734,34 @@ class PlaylistTab(QTableWidget):
self._meta_set_attribute(row, RowMeta.UNREADABLE) self._meta_set_attribute(row, RowMeta.UNREADABLE)
# def _select_event(self) -> None: def _select_event(self) -> None:
# """ """
# Called when item selection changes. Called when item selection changes.
# If multiple rows are selected, display sum of durations in status bar. If multiple rows are selected, display sum of durations in status bar.
# """ """
#
# # If we are in the process of selecting multiple tracks, no-op here # If we are in the process of selecting multiple tracks, no-op here
# if self.selecting_in_progress: if self.selecting_in_progress:
# return return
#
# # Get the row number of all selected items and put into a set # Get the row number of all selected items and put into a set
# # to deduplicate # to deduplicate
# sel_rows: Set[int] = set([item.row() for item in self.selectedItems()]) selected_rows = set([item.row() for item in self.selectedItems()])
# # If no rows are selected, we have nothing to do # If no rows are selected, we have nothing to do
# if len(sel_rows) == 0: if len(selected_rows) == 0:
# self.musicmuster.lblSumPlaytime.setText("") self.musicmuster.lblSumPlaytime.setText("")
# return return
#
# notes_rows: Set[int] = set(self._get_notes_rows()) ms = 0
# ms: int = 0 for row in selected_rows:
# with Session() as session: ms += self._get_row_duration(row)
# for row in (sel_rows - notes_rows):
# ms += self._get_row_track_object(row, session).duration or 0 # Only paint message if there are selected track rows
# if ms > 0:
# # Only paint message if there are selected track rows self.musicmuster.lblSumPlaytime.setText(
# if ms > 0: f"Selected duration: {helpers.ms_to_mmss(ms)}")
# self.musicmuster.lblSumPlaytime.setText( else:
# f"Selected duration: {helpers.ms_to_mmss(ms)}") self.musicmuster.lblSumPlaytime.setText("")
# else:
# self.musicmuster.lblSumPlaytime.setText("")
# #
# def _select_tracks(self, played: bool) -> None: # def _select_tracks(self, played: bool) -> None:
# """ # """
@ -1739,41 +1795,42 @@ class PlaylistTab(QTableWidget):
self.setColumnWidth(idx, record.f_int) self.setColumnWidth(idx, record.f_int)
else: else:
self.setColumnWidth(idx, Config.DEFAULT_COLUMN_WIDTH) self.setColumnWidth(idx, Config.DEFAULT_COLUMN_WIDTH)
#
# def _set_next(self, row: int, session: Session) -> None: def _set_next(self, session: Session, row: int) -> None:
# """ """
# Set passed row as next track to play. Set passed row as next track to play.
#
# Actions required: Actions required:
# - Check row is a track row - Check row has a track
# - Check track is readable - Check track is readable
# - Mark as next track - Mark as next track
# - Update display - Update display
# - Notify musicmuster - Notify musicmuster
# """ """
#
# log.debug(f"_set_next({row=})") track_id = self._get_row_track_id(row_number)
# if not track_id:
# # Check row is a track row log.error(f"playlists._set_next({row=}) has no track associated")
# if row in self._get_notes_rows(): return
# return None
# track: Tracks = self._get_row_track_object(row, session) track = session.get(Tracks, track_id)
# if not track: if not track:
# return None log.error(f"playlists._set_next({row=}): Track not found")
# return
# # Check track is readable
# if not self._file_is_readable(track.path): # Check track is readable
# self._set_unreadable_row(row) if not self._file_is_readable(track.path):
# return None self._set_unreadable_row(row)
# return None
# # Mark as next track
# self._set_next_track_row(row) # Mark as next track
# self._set_next_track_row(row)
# # Update display
# self.update_display(session) # Update display
# self.update_display(session)
# # Notify musicmuster
# self.musicmuster.this_is_the_next_track(self, track, session) # Notify musicmuster
self.musicmuster.this_is_the_next_track(self, track, session)
def _set_row_bold(self, row: int, bold: bool = True) -> None: def _set_row_bold(self, row: int, bold: bool = True) -> None:
"""Make row bold (bold=True) or not bold""" """Make row bold (bold=True) or not bold"""
@ -1802,13 +1859,11 @@ class PlaylistTab(QTableWidget):
# #
# self.item(row, self.COL_USERDATA).setData( # self.item(row, self.COL_USERDATA).setData(
# self.CONTENT_OBJECT, object_id) # self.CONTENT_OBJECT, object_id)
#
# def _set_row_duration(self, row: int, ms: int) -> None: def _set_row_duration(self, row: int, ms: int) -> None:
# """Set duration of this row in row metadata""" """Set duration of this row in row metadata"""
#
# assert self.item(row, columns['userdata'].idx) self.item(row, columns['userdata'].idx).setData(self.ROW_DURATION, ms)
#
# self.item(row, columns['userdata'].idx).setData(self.ROW_DURATION, ms)
def _set_row_end_time(self, row: int, time: Optional[datetime]) -> None: def _set_row_end_time(self, row: int, time: Optional[datetime]) -> None:
"""Set passed row end time to passed time""" """Set passed row end time to passed time"""

View File

@ -734,57 +734,48 @@ border: 1px solid rgb(85, 87, 83);</string>
</property> </property>
<widget class="QMenu" name="menuFile"> <widget class="QMenu" name="menuFile">
<property name="title"> <property name="title">
<string>Fi&amp;le</string> <string>P&amp;laylists</string>
</property> </property>
<addaction name="actionNewPlaylist"/> <addaction name="actionNewPlaylist"/>
<addaction name="actionOpenPlaylist"/> <addaction name="actionOpenPlaylist"/>
<addaction name="actionClosePlaylist"/> <addaction name="actionClosePlaylist"/>
<addaction name="actionRenamePlaylist"/> <addaction name="actionRenamePlaylist"/>
<addaction name="actionExport_playlist"/>
<addaction name="actionDeletePlaylist"/> <addaction name="actionDeletePlaylist"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionMoveSelected"/>
<addaction name="actionMove_unplayed"/>
<addaction name="actionDownload_CSV_of_played_tracks"/> <addaction name="actionDownload_CSV_of_played_tracks"/>
<addaction name="actionExport_playlist"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionE_xit"/> <addaction name="actionE_xit"/>
<addaction name="separator"/> <addaction name="separator"/>
</widget> </widget>
<widget class="QMenu" name="menuPlaylist"> <widget class="QMenu" name="menuPlaylist">
<property name="title"> <property name="title">
<string>&amp;Tracks</string> <string>Sho&amp;wtime</string>
</property> </property>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionSearch_database"/>
<addaction name="actionAdd_note"/>
<addaction name="actionImport"/>
<addaction name="action_Clear_selection"/>
<addaction name="separator"/>
<addaction name="actionSetNext"/>
<addaction name="separator"/>
<addaction name="actionSelect_unplayed_tracks"/>
<addaction name="actionSelect_played_tracks"/>
<addaction name="actionMoveSelected"/>
<addaction name="separator"/>
<addaction name="separator"/>
<addaction name="actionSelect_next_track"/>
<addaction name="actionSelect_previous_track"/>
<addaction name="separator"/>
<addaction name="actionSearch"/>
</widget>
<widget class="QMenu" name="menu_Music">
<property name="title">
<string>&amp;Music</string>
</property>
<addaction name="actionPlay_next"/> <addaction name="actionPlay_next"/>
<addaction name="actionSkip_next"/>
<addaction name="actionFade"/> <addaction name="actionFade"/>
<addaction name="actionStop"/> <addaction name="actionStop"/>
<addaction name="action_Resume_previous"/> <addaction name="separator"/>
<addaction name="actionSkip_next"/>
<addaction name="separator"/>
<addaction name="actionInsert"/>
<addaction name="actionRemove"/>
<addaction name="actionImport"/>
<addaction name="actionSetNext"/>
<addaction name="action_Clear_selection"/>
<addaction name="actionInsert_section_header"/>
<addaction name="separator"/>
<addaction name="actionSearch"/>
<addaction name="actionSelect_next_track"/>
<addaction name="actionSelect_previous_track"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionEnable_controls"/> <addaction name="actionEnable_controls"/>
</widget> </widget>
<addaction name="menuFile"/> <addaction name="menuFile"/>
<addaction name="menuPlaylist"/> <addaction name="menuPlaylist"/>
<addaction name="menu_Music"/>
</widget> </widget>
<widget class="QStatusBar" name="statusbar"> <widget class="QStatusBar" name="statusbar">
<property name="enabled"> <property name="enabled">
@ -818,13 +809,13 @@ border: 1px solid rgb(85, 87, 83);</string>
<string>Ctrl+Alt+Return</string> <string>Ctrl+Alt+Return</string>
</property> </property>
</action> </action>
<action name="actionSearch_database"> <action name="actionInsert">
<property name="icon"> <property name="icon">
<iconset> <iconset>
<normaloff>../../../../.designer/backup/icon_search_database.png</normaloff>../../../../.designer/backup/icon_search_database.png</iconset> <normaloff>../../../../.designer/backup/icon_search_database.png</normaloff>../../../../.designer/backup/icon_search_database.png</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>Search &amp;database</string> <string>Insert &amp;track...</string>
</property> </property>
<property name="shortcut"> <property name="shortcut">
<string>Ctrl+D</string> <string>Ctrl+D</string>
@ -949,7 +940,7 @@ border: 1px solid rgb(85, 87, 83);</string>
</action> </action>
<action name="actionExport_playlist"> <action name="actionExport_playlist">
<property name="text"> <property name="text">
<string>E&amp;xport playlist...</string> <string>E&amp;xport...</string>
</property> </property>
</action> </action>
<action name="actionSetNext"> <action name="actionSetNext">
@ -981,9 +972,9 @@ border: 1px solid rgb(85, 87, 83);</string>
<string>Select played tracks</string> <string>Select played tracks</string>
</property> </property>
</action> </action>
<action name="actionSelect_unplayed_tracks"> <action name="actionMove_unplayed">
<property name="text"> <property name="text">
<string>Select unplayed tracks</string> <string>Move &amp;unplayed tracks to...</string>
</property> </property>
</action> </action>
<action name="actionAdd_note"> <action name="actionAdd_note">
@ -1001,7 +992,7 @@ border: 1px solid rgb(85, 87, 83);</string>
</action> </action>
<action name="actionImport"> <action name="actionImport">
<property name="text"> <property name="text">
<string>Import...</string> <string>Import track...</string>
</property> </property>
<property name="shortcut"> <property name="shortcut">
<string>Ctrl+Shift+I</string> <string>Ctrl+Shift+I</string>
@ -1020,6 +1011,16 @@ border: 1px solid rgb(85, 87, 83);</string>
<string>/</string> <string>/</string>
</property> </property>
</action> </action>
<action name="actionInsert_section_header">
<property name="text">
<string>Insert &amp;section header...</string>
</property>
</action>
<action name="actionRemove">
<property name="text">
<string>&amp;Remove track</string>
</property>
</action>
</widget> </widget>
<resources> <resources>
<include location="icons.qrc"/> <include location="icons.qrc"/>

View File

@ -342,8 +342,6 @@ class Ui_MainWindow(object):
self.menuFile.setObjectName("menuFile") self.menuFile.setObjectName("menuFile")
self.menuPlaylist = QtWidgets.QMenu(self.menubar) self.menuPlaylist = QtWidgets.QMenu(self.menubar)
self.menuPlaylist.setObjectName("menuPlaylist") self.menuPlaylist.setObjectName("menuPlaylist")
self.menu_Music = QtWidgets.QMenu(self.menubar)
self.menu_Music.setObjectName("menu_Music")
MainWindow.setMenuBar(self.menubar) MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setEnabled(True) self.statusbar.setEnabled(True)
@ -360,11 +358,11 @@ class Ui_MainWindow(object):
icon4.addPixmap(QtGui.QPixmap(":/icons/next"), QtGui.QIcon.Normal, QtGui.QIcon.Off) icon4.addPixmap(QtGui.QPixmap(":/icons/next"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.actionSkip_next.setIcon(icon4) self.actionSkip_next.setIcon(icon4)
self.actionSkip_next.setObjectName("actionSkip_next") self.actionSkip_next.setObjectName("actionSkip_next")
self.actionSearch_database = QtWidgets.QAction(MainWindow) self.actionInsert = QtWidgets.QAction(MainWindow)
icon5 = QtGui.QIcon() icon5 = QtGui.QIcon()
icon5.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon_search_database.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) icon5.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon_search_database.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.actionSearch_database.setIcon(icon5) self.actionInsert.setIcon(icon5)
self.actionSearch_database.setObjectName("actionSearch_database") self.actionInsert.setObjectName("actionInsert")
self.actionAdd_file = QtWidgets.QAction(MainWindow) self.actionAdd_file = QtWidgets.QAction(MainWindow)
icon6 = QtGui.QIcon() icon6 = QtGui.QIcon()
icon6.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon_open_file.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) icon6.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon_open_file.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
@ -422,8 +420,8 @@ class Ui_MainWindow(object):
self.actionSelect_previous_track.setObjectName("actionSelect_previous_track") self.actionSelect_previous_track.setObjectName("actionSelect_previous_track")
self.actionSelect_played_tracks = QtWidgets.QAction(MainWindow) self.actionSelect_played_tracks = QtWidgets.QAction(MainWindow)
self.actionSelect_played_tracks.setObjectName("actionSelect_played_tracks") self.actionSelect_played_tracks.setObjectName("actionSelect_played_tracks")
self.actionSelect_unplayed_tracks = QtWidgets.QAction(MainWindow) self.actionMove_unplayed = QtWidgets.QAction(MainWindow)
self.actionSelect_unplayed_tracks.setObjectName("actionSelect_unplayed_tracks") self.actionMove_unplayed.setObjectName("actionMove_unplayed")
self.actionAdd_note = QtWidgets.QAction(MainWindow) self.actionAdd_note = QtWidgets.QAction(MainWindow)
self.actionAdd_note.setObjectName("actionAdd_note") self.actionAdd_note.setObjectName("actionAdd_note")
self.actionEnable_controls = QtWidgets.QAction(MainWindow) self.actionEnable_controls = QtWidgets.QAction(MainWindow)
@ -434,44 +432,44 @@ class Ui_MainWindow(object):
self.actionDownload_CSV_of_played_tracks.setObjectName("actionDownload_CSV_of_played_tracks") self.actionDownload_CSV_of_played_tracks.setObjectName("actionDownload_CSV_of_played_tracks")
self.actionSearch = QtWidgets.QAction(MainWindow) self.actionSearch = QtWidgets.QAction(MainWindow)
self.actionSearch.setObjectName("actionSearch") self.actionSearch.setObjectName("actionSearch")
self.actionInsert_section_header = QtWidgets.QAction(MainWindow)
self.actionInsert_section_header.setObjectName("actionInsert_section_header")
self.actionRemove = QtWidgets.QAction(MainWindow)
self.actionRemove.setObjectName("actionRemove")
self.menuFile.addAction(self.actionNewPlaylist) self.menuFile.addAction(self.actionNewPlaylist)
self.menuFile.addAction(self.actionOpenPlaylist) self.menuFile.addAction(self.actionOpenPlaylist)
self.menuFile.addAction(self.actionClosePlaylist) self.menuFile.addAction(self.actionClosePlaylist)
self.menuFile.addAction(self.actionRenamePlaylist) self.menuFile.addAction(self.actionRenamePlaylist)
self.menuFile.addAction(self.actionExport_playlist)
self.menuFile.addAction(self.actionDeletePlaylist) self.menuFile.addAction(self.actionDeletePlaylist)
self.menuFile.addSeparator() self.menuFile.addSeparator()
self.menuFile.addAction(self.actionMoveSelected)
self.menuFile.addAction(self.actionMove_unplayed)
self.menuFile.addAction(self.actionDownload_CSV_of_played_tracks) self.menuFile.addAction(self.actionDownload_CSV_of_played_tracks)
self.menuFile.addAction(self.actionExport_playlist)
self.menuFile.addSeparator() self.menuFile.addSeparator()
self.menuFile.addAction(self.actionE_xit) self.menuFile.addAction(self.actionE_xit)
self.menuFile.addSeparator() self.menuFile.addSeparator()
self.menuPlaylist.addSeparator() self.menuPlaylist.addSeparator()
self.menuPlaylist.addAction(self.actionSearch_database) self.menuPlaylist.addAction(self.actionPlay_next)
self.menuPlaylist.addAction(self.actionAdd_note) self.menuPlaylist.addAction(self.actionFade)
self.menuPlaylist.addAction(self.actionStop)
self.menuPlaylist.addSeparator()
self.menuPlaylist.addAction(self.actionSkip_next)
self.menuPlaylist.addSeparator()
self.menuPlaylist.addAction(self.actionInsert)
self.menuPlaylist.addAction(self.actionRemove)
self.menuPlaylist.addAction(self.actionImport) self.menuPlaylist.addAction(self.actionImport)
self.menuPlaylist.addAction(self.action_Clear_selection)
self.menuPlaylist.addSeparator()
self.menuPlaylist.addAction(self.actionSetNext) self.menuPlaylist.addAction(self.actionSetNext)
self.menuPlaylist.addAction(self.action_Clear_selection)
self.menuPlaylist.addAction(self.actionInsert_section_header)
self.menuPlaylist.addSeparator() self.menuPlaylist.addSeparator()
self.menuPlaylist.addAction(self.actionSelect_unplayed_tracks) self.menuPlaylist.addAction(self.actionSearch)
self.menuPlaylist.addAction(self.actionSelect_played_tracks)
self.menuPlaylist.addAction(self.actionMoveSelected)
self.menuPlaylist.addSeparator()
self.menuPlaylist.addSeparator()
self.menuPlaylist.addAction(self.actionSelect_next_track) self.menuPlaylist.addAction(self.actionSelect_next_track)
self.menuPlaylist.addAction(self.actionSelect_previous_track) self.menuPlaylist.addAction(self.actionSelect_previous_track)
self.menuPlaylist.addSeparator() self.menuPlaylist.addSeparator()
self.menuPlaylist.addAction(self.actionSearch) self.menuPlaylist.addAction(self.actionEnable_controls)
self.menu_Music.addAction(self.actionPlay_next)
self.menu_Music.addAction(self.actionSkip_next)
self.menu_Music.addAction(self.actionFade)
self.menu_Music.addAction(self.actionStop)
self.menu_Music.addAction(self.action_Resume_previous)
self.menu_Music.addSeparator()
self.menu_Music.addAction(self.actionEnable_controls)
self.menubar.addAction(self.menuFile.menuAction()) self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menuPlaylist.menuAction()) self.menubar.addAction(self.menuPlaylist.menuAction())
self.menubar.addAction(self.menu_Music.menuAction())
self.retranslateUi(MainWindow) self.retranslateUi(MainWindow)
self.tabPlaylist.setCurrentIndex(-1) self.tabPlaylist.setCurrentIndex(-1)
@ -505,15 +503,14 @@ class Ui_MainWindow(object):
self.label_end_timer.setText(_translate("MainWindow", "00:00")) self.label_end_timer.setText(_translate("MainWindow", "00:00"))
self.btnDrop3db.setText(_translate("MainWindow", "-3dB to talk")) self.btnDrop3db.setText(_translate("MainWindow", "-3dB to talk"))
self.btnHidePlayed.setText(_translate("MainWindow", "Hide played")) self.btnHidePlayed.setText(_translate("MainWindow", "Hide played"))
self.menuFile.setTitle(_translate("MainWindow", "Fi&le")) self.menuFile.setTitle(_translate("MainWindow", "P&laylists"))
self.menuPlaylist.setTitle(_translate("MainWindow", "&Tracks")) self.menuPlaylist.setTitle(_translate("MainWindow", "Sho&wtime"))
self.menu_Music.setTitle(_translate("MainWindow", "&Music"))
self.actionPlay_next.setText(_translate("MainWindow", "&Play next")) self.actionPlay_next.setText(_translate("MainWindow", "&Play next"))
self.actionPlay_next.setShortcut(_translate("MainWindow", "Return")) self.actionPlay_next.setShortcut(_translate("MainWindow", "Return"))
self.actionSkip_next.setText(_translate("MainWindow", "Skip to &next")) self.actionSkip_next.setText(_translate("MainWindow", "Skip to &next"))
self.actionSkip_next.setShortcut(_translate("MainWindow", "Ctrl+Alt+Return")) self.actionSkip_next.setShortcut(_translate("MainWindow", "Ctrl+Alt+Return"))
self.actionSearch_database.setText(_translate("MainWindow", "Search &database")) self.actionInsert.setText(_translate("MainWindow", "Insert &track..."))
self.actionSearch_database.setShortcut(_translate("MainWindow", "Ctrl+D")) self.actionInsert.setShortcut(_translate("MainWindow", "Ctrl+D"))
self.actionAdd_file.setText(_translate("MainWindow", "Add &file")) self.actionAdd_file.setText(_translate("MainWindow", "Add &file"))
self.actionAdd_file.setShortcut(_translate("MainWindow", "Ctrl+F")) self.actionAdd_file.setShortcut(_translate("MainWindow", "Ctrl+F"))
self.actionFade.setText(_translate("MainWindow", "F&ade")) self.actionFade.setText(_translate("MainWindow", "F&ade"))
@ -534,7 +531,7 @@ class Ui_MainWindow(object):
self.actionRenamePlaylist.setText(_translate("MainWindow", "&Rename...")) self.actionRenamePlaylist.setText(_translate("MainWindow", "&Rename..."))
self.actionDeletePlaylist.setText(_translate("MainWindow", "Dele&te...")) self.actionDeletePlaylist.setText(_translate("MainWindow", "Dele&te..."))
self.actionMoveSelected.setText(_translate("MainWindow", "Mo&ve selected tracks to...")) self.actionMoveSelected.setText(_translate("MainWindow", "Mo&ve selected tracks to..."))
self.actionExport_playlist.setText(_translate("MainWindow", "E&xport playlist...")) self.actionExport_playlist.setText(_translate("MainWindow", "E&xport..."))
self.actionSetNext.setText(_translate("MainWindow", "Set &next")) self.actionSetNext.setText(_translate("MainWindow", "Set &next"))
self.actionSetNext.setShortcut(_translate("MainWindow", "Ctrl+N")) self.actionSetNext.setShortcut(_translate("MainWindow", "Ctrl+N"))
self.actionSelect_next_track.setText(_translate("MainWindow", "Select next track")) self.actionSelect_next_track.setText(_translate("MainWindow", "Select next track"))
@ -542,13 +539,15 @@ class Ui_MainWindow(object):
self.actionSelect_previous_track.setText(_translate("MainWindow", "Select previous track")) self.actionSelect_previous_track.setText(_translate("MainWindow", "Select previous track"))
self.actionSelect_previous_track.setShortcut(_translate("MainWindow", "K")) self.actionSelect_previous_track.setShortcut(_translate("MainWindow", "K"))
self.actionSelect_played_tracks.setText(_translate("MainWindow", "Select played tracks")) self.actionSelect_played_tracks.setText(_translate("MainWindow", "Select played tracks"))
self.actionSelect_unplayed_tracks.setText(_translate("MainWindow", "Select unplayed tracks")) self.actionMove_unplayed.setText(_translate("MainWindow", "Move &unplayed tracks to..."))
self.actionAdd_note.setText(_translate("MainWindow", "Add note...")) self.actionAdd_note.setText(_translate("MainWindow", "Add note..."))
self.actionAdd_note.setShortcut(_translate("MainWindow", "Ctrl+T")) self.actionAdd_note.setShortcut(_translate("MainWindow", "Ctrl+T"))
self.actionEnable_controls.setText(_translate("MainWindow", "Enable controls")) self.actionEnable_controls.setText(_translate("MainWindow", "Enable controls"))
self.actionImport.setText(_translate("MainWindow", "Import...")) self.actionImport.setText(_translate("MainWindow", "Import track..."))
self.actionImport.setShortcut(_translate("MainWindow", "Ctrl+Shift+I")) self.actionImport.setShortcut(_translate("MainWindow", "Ctrl+Shift+I"))
self.actionDownload_CSV_of_played_tracks.setText(_translate("MainWindow", "Download CSV of played tracks...")) self.actionDownload_CSV_of_played_tracks.setText(_translate("MainWindow", "Download CSV of played tracks..."))
self.actionSearch.setText(_translate("MainWindow", "Search...")) self.actionSearch.setText(_translate("MainWindow", "Search..."))
self.actionSearch.setShortcut(_translate("MainWindow", "/")) self.actionSearch.setShortcut(_translate("MainWindow", "/"))
self.actionInsert_section_header.setText(_translate("MainWindow", "Insert &section header..."))
self.actionRemove.setText(_translate("MainWindow", "&Remove track"))
import icons_rc import icons_rc