diff --git a/app/config.py b/app/config.py
index ab31ec5..e5b8b5f 100644
--- a/app/config.py
+++ b/app/config.py
@@ -36,7 +36,7 @@ class Config(object):
DEFAULT_COLUMN_WIDTH = 200
DEFAULT_IMPORT_DIRECTORY = "/home/kae/Nextcloud/tmp"
DEFAULT_OUTPUT_DIRECTORY = "/home/kae/music/Singles"
- DISPLAY_SQL = True
+ DISPLAY_SQL = False
ERRORS_TO = ['kae@midnighthax.com']
FADE_STEPS = 20
FADE_TIME = 3000
diff --git a/app/musicmuster.py b/app/musicmuster.py
index baca8f0..362d045 100755
--- a/app/musicmuster.py
+++ b/app/musicmuster.py
@@ -20,7 +20,7 @@ from PyQt5.QtWidgets import (
# QDialog,
# QFileDialog,
# QInputDialog,
- # QLabel,
+ QLabel,
# QLineEdit,
# QListWidgetItem,
QMainWindow,
@@ -84,8 +84,8 @@ class Window(QMainWindow, Ui_MainWindow):
# self.previous_track_position: Optional[int] = None
#
# self.set_main_window_size()
-# self.lblSumPlaytime: QLabel = QLabel("")
-# self.statusbar.addPermanentWidget(self.lblSumPlaytime)
+ self.lblSumPlaytime = QLabel("")
+ self.statusbar.addPermanentWidget(self.lblSumPlaytime)
# self.txtSearch = QLineEdit()
# self.statusbar.addWidget(self.txtSearch)
# self.txtSearch.setHidden(True)
diff --git a/app/playlists.py b/app/playlists.py
index fe5211c..d2d2141 100644
--- a/app/playlists.py
+++ b/app/playlists.py
@@ -1,7 +1,7 @@
from collections import namedtuple
from typing import List, Optional
-# from PyQt5 import QtCore
+from PyQt5 import QtCore
from PyQt5.QtCore import Qt
from PyQt5.QtGui import (
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"""
#
# def createEditor(self, parent, option, index):
-# import ipdb; ipdb.set_trace()
# editor = super().createEditor(parent, option, index)
# if isinstance(editor, QLineEdit):
# def deselect():
@@ -92,9 +91,10 @@ class PlaylistTab(QTableWidget):
# cellEditingEnded = QtCore.pyqtSignal()
# Qt.UserRoles
- ROW_METADATA = Qt.UserRole
+ ROW_FLAGS = Qt.UserRole
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,
playlist_id: int, *args, **kwargs) -> None:
@@ -135,20 +135,20 @@ class PlaylistTab(QTableWidget):
self.setDropIndicatorShown(True)
self.setDragDropMode(QAbstractItemView.InternalMove)
self.setDragEnabled(False)
-#
-# # This property defines how the widget shows a context menu
-# self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
-# # This signal is emitted when the widget's contextMenuPolicy is
-# # Qt::CustomContextMenu, and the user has requested a context
-# # menu on the widget.
-# self.customContextMenuRequested.connect(self._context_menu)
-# self.viewport().installEventFilter(self)
-#
-# self.itemSelectionChanged.connect(self._select_event)
-#
+
+ # This property defines how the widget shows a context menu
+ self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
+ # This signal is emitted when the widget's contextMenuPolicy is
+ # Qt::CustomContextMenu, and the user has requested a context
+ # menu on the widget.
+ self.customContextMenuRequested.connect(self._context_menu)
+ self.viewport().installEventFilter(self)
+
+ self.itemSelectionChanged.connect(self._select_event)
+
self.row_filter: Optional[str] = None
# self.editing_cell: bool = False
-# self.selecting_in_progress = False
+ self.selecting_in_progress = False
# Connect signals
# self.cellChanged.connect(self._cell_changed)
# self.cellClicked.connect(self._edit_note_cell)
@@ -156,7 +156,7 @@ class PlaylistTab(QTableWidget):
# self.cellEditingStarted.connect(self._cell_edit_started)
# self.doubleClicked.connect(self._edit_cell)
self.horizontalHeader().sectionResized.connect(self._column_resize)
-#
+
# Now load our tracks and notes
self.populate(session, self.playlist_id)
@@ -236,58 +236,90 @@ class PlaylistTab(QTableWidget):
with Session() as session: # checked
self.save_playlist(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)
# if result:
# self.cellEditingStarted.emit(index.row(), index.column())
# return result
-#
-# def eventFilter(self, source, event): # review
-# """Used to process context (right-click) menu, which is defined here"""
-#
-# if (event.type() == QtCore.QEvent.MouseButtonPress and # noqa W504
-# event.buttons() == QtCore.Qt.RightButton and # noqa W504
-# source is self.viewport()):
-# item = self.itemAt(event.pos())
-# if item is not None:
-# row = item.row()
-# log.debug(f"playlist.eventFilter(): Right-click on row {row}")
-# current = row == self._get_current_track_row()
-# next_row = row == self._get_next_track_row()
-# self.menu = QMenu(self)
-# act_info = self.menu.addAction('Info')
-# act_info.triggered.connect(lambda: self._info_row(row))
-# self.menu.addSeparator()
-# if row not in self._get_notes_rows():
-# act_mplayer = self.menu.addAction(
-# "Play track with mplayer")
-# act_mplayer.triggered.connect(
-# lambda: self._mplayer(row))
-# self.menu.addSeparator()
-# if not current and not next_row:
-# act_setnext = self.menu.addAction("Set next")
-# with Session() as session:
-# act_setnext.triggered.connect(
-# lambda: self._set_next(row, session))
-# act_copypath = self.menu.addAction("Copy track path")
-# act_copypath.triggered.connect(
-# lambda: self._copy_path(row))
-# if not current:
-# act_rescan = self.menu.addAction("Rescan track")
-# act_rescan.triggered.connect(lambda: self._rescan(row))
-# act_audacity = self.menu.addAction(
-# "Open track in Audacity")
-# act_audacity.triggered.connect(
-# lambda: self._audacity(row))
-# 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()
-# act_delete = self.menu.addAction('Delete')
-# act_delete.triggered.connect(self._delete_rows)
-#
-# return super(PlaylistTab, self).eventFilter(source, event)
+
+ def eventFilter(self, source, event):
+ """Used to process context (right-click) menu, which is defined here"""
+
+ if (event.type() == QtCore.QEvent.MouseButtonPress and # noqa W504
+ event.buttons() == QtCore.Qt.RightButton and # noqa W504
+ source is self.viewport()):
+ item = self.itemAt(event.pos())
+ if item is not None:
+ row_number = item.row()
+ track_id = self._get_row_track_id(row_number)
+ if track_id:
+ current = row_number == self._get_current_track_row()
+ next_row = row_number == self._get_next_track_row()
+ else:
+ current = next_row = False
+
+ self.menu = QMenu(self)
+
+ if track_id:
+ # Info
+ act_info = self.menu.addAction('Info')
+ act_info.triggered.connect(
+ lambda: self._info_row(track_id)
+ )
+
+ self.menu.addSeparator()
+
+ # Play with mplayer
+ act_mplayer = self.menu.addAction(
+ "Play with mplayer")
+ act_mplayer.triggered.connect(
+ lambda: self._mplayer_play(track_id))
+
+ # Set next
+ if not current and not next_row:
+ act_setnext = self.menu.addAction("Set next")
+ with Session() as session:
+ act_setnext.triggered.connect(
+ lambda: self._set_next(session, row_number))
+
+ # Open in Audacity
+ if not current:
+ act_audacity = self.menu.addAction(
+ "Open in Audacity")
+ 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):
"""
@@ -301,7 +333,7 @@ class PlaylistTab(QTableWidget):
super().mouseReleaseEvent(event)
# # ########## Externally called functions ##########
-#
+
def clear_selection(self) -> None:
"""Unselect all tracks and reset drag mode"""
@@ -399,8 +431,9 @@ class PlaylistTab(QTableWidget):
# Add row metadata to userdata column
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.ROW_TRACK_ID, row_data.track_id)
self.setItem(row, columns['userdata'].idx, userdata_item)
if row_data.track_id:
@@ -420,6 +453,7 @@ class PlaylistTab(QTableWidget):
duration_item = QTableWidgetItem(
helpers.ms_to_mmss(row_data.track.duration))
self.setItem(row, columns['duration'].idx, duration_item)
+ self._set_row_duration(row, row_data.track.duration)
start_item = QTableWidgetItem()
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):
self._set_unreadable_row(row)
- # Save track_id
- userdata_item.setData(self.ROW_TRACK_ID, row_data.track_id)
-
else:
# This is a section header so make empty items (row
# background won't be coloured without items present). Any
@@ -484,7 +515,7 @@ class PlaylistTab(QTableWidget):
# # Put an item in COL_USERDATA for later
# item: QTableWidgetItem = QTableWidgetItem()
# # Add row metadata
-# item.setData(self.ROW_METADATA, 0)
+# item.setData(self.ROW_FLAGS, 0)
# self.setItem(row, self.COL_USERDATA, item)
#
# # Add track details to columns
@@ -618,7 +649,7 @@ class PlaylistTab(QTableWidget):
# search_from = current_row + 1
# next_row = self._find_next_track_row(search_from)
# if next_row:
-# self._set_next(next_row, session)
+# self._set_next(session, next_row)
#
# # Update display
# self.update_display(session)
@@ -863,7 +894,7 @@ class PlaylistTab(QTableWidget):
# return None
#
# with Session() as session:
-# self._set_next(row, session)
+# self._set_next(session, row)
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)
#
# # ########## Internally called functions ##########
-#
-# def _audacity(self, row: int) -> None:
-# """Open track in Audacity. Audacity must be already running"""
-#
-# log.debug(f"_audacity({row})")
-#
-# if row in self._get_notes_rows():
-# return None
-#
-# with Session() as session:
-# track: Tracks = self._get_row_track_object(row, session)
-# open_in_audacity(track.path)
+
+ def _add_track(self, row: int) -> None:
+ """Add a track to a section header making it a normal track row"""
+
+ print("playlists._add_track() not yet implemented")
+
+ def _open_in_audacity(self, track_id: int) -> None:
+ """Open track in Audacity. Audacity must be already running"""
+
+ 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
+
+ open_in_audacity(track.path)
def _calculate_end_time(self, start: Optional[datetime],
duration: int) -> Optional[datetime]:
@@ -1080,11 +1118,11 @@ class PlaylistTab(QTableWidget):
return None
return start + timedelta(milliseconds=duration)
-#
-# def _context_menu(self, pos): # review
-#
-# assert self.menu
-# self.menu.exec_(self.mapToGlobal(pos))
+
+ def _context_menu(self, pos):
+
+ assert self.menu
+ self.menu.exec_(self.mapToGlobal(pos))
#
# def _copy_path(self, row: int) -> None:
# """
@@ -1364,14 +1402,16 @@ class PlaylistTab(QTableWidget):
"""Return rows marked as played, or None"""
return self._meta_search(RowMeta.PLAYED, one=False)
-#
-# def _get_row_duration(self, row: int) -> int:
-# """Return duration associated with this row"""
-#
-# try:
-# return self.item(row, self.COL_USERDATA).data(self.ROW_DURATION)
-# except:
-# return 0
+
+ def _get_row_duration(self, row: int) -> int:
+ """Return duration associated with this row"""
+
+ duration = (self.item(row, columns['userdata'].idx)
+ .data(self.ROW_DURATION))
+ if duration:
+ return duration
+ else:
+ return 0
#
# def _get_row_end_time(self, row) -> Optional[datetime]:
# """
@@ -1434,34 +1474,32 @@ class PlaylistTab(QTableWidget):
"""Return rows marked as unreadable, or None"""
return self._meta_search(RowMeta.UNREADABLE, one=False)
-#
-# def _info_row(self, row: int) -> None:
-# """Display popup with info re row"""
-#
-# txt: str
-#
-# with Session() as session:
-# if row in self._get_notes_rows():
-# note: Notes = self._get_row_notes_object(row, session)
-# txt = note.note
-# else:
-# track: Tracks = self._get_row_track_object(row, session)
-# txt = (
-# f"Title: {track.title}\n"
-# f"Artist: {track.artist}\n"
-# f"Track ID: {track.id}\n"
-# f"Track duration: {helpers.ms_to_mmss(track.duration)}\n"
-# f"Track fade at: {helpers.ms_to_mmss(track.fade_at)}\n"
-# f"Track silence at: {helpers.ms_to_mmss(track.silence_at)}"
-# "\n\n"
-# f"Path: {track.path}\n"
-# )
-# info: QMessageBox = QMessageBox(self)
-# info.setIcon(QMessageBox.Information)
-# info.setText(txt)
-# info.setStandardButtons(QMessageBox.Ok)
-# info.setDefaultButton(QMessageBox.Cancel)
-# info.exec()
+
+ def _info_row(self, track_id: int) -> None:
+ """Display popup with info re row"""
+
+ with Session() as session:
+ track = session.get(Tracks, track_id)
+ if track:
+ txt = (
+ f"Title: {track.title}\n"
+ f"Artist: {track.artist}\n"
+ f"Track ID: {track.id}\n"
+ f"Track duration: {helpers.ms_to_mmss(track.duration)}\n"
+ f"Track fade at: {helpers.ms_to_mmss(track.fade_at)}\n"
+ f"Track silence at: {helpers.ms_to_mmss(track.silence_at)}"
+ "\n\n"
+ f"Path: {track.path}\n"
+ )
+ else:
+ txt = f"Can't find {track_id=}"
+
+ info: QMessageBox = QMessageBox(self)
+ info.setIcon(QMessageBox.Information)
+ info.setText(txt)
+ info.setStandardButtons(QMessageBox.Ok)
+ info.setDefaultButton(QMessageBox.Cancel)
+ info.exec()
#
# def _insert_note(self, session: Session, note: Notes,
# row: Optional[int] = None, repaint: bool = True) -> None:
@@ -1539,7 +1577,7 @@ class PlaylistTab(QTableWidget):
#
# new_metadata: int = self._meta_get(row) & ~(1 << attribute)
# self.item(row, self.COL_USERDATA).setData(
-# self.ROW_METADATA, new_metadata)
+# self.ROW_FLAGS, new_metadata)
#
# def _meta_clear_next(self) -> None:
# """
@@ -1554,7 +1592,7 @@ class PlaylistTab(QTableWidget):
"""Return row metadata"""
return (self.item(row, columns['userdata'].idx)
- .data(self.ROW_METADATA))
+ .data(self.ROW_FLAGS))
#
# def _meta_notset(self, metadata: int) -> List[int]:
# """
@@ -1612,38 +1650,58 @@ class PlaylistTab(QTableWidget):
# else:
# new_metadata = self._meta_get(row) | (1 << attribute)
# self.item(row, self.COL_USERDATA).setData(
-# self.ROW_METADATA, new_metadata)
-#
-# def _mplayer(self, row: int) -> None:
-# """Play track with mplayer"""
-#
-# log.debug(f"_mplayer({row})")
-#
-# if row in self._get_notes_rows():
-# return None
-#
-# with Session() as session:
-# track: Tracks = self._get_row_track_object(row, session)
-# cmd_list = ['gmplayer', '-vc', 'null', '-vo', 'null', track.path]
-# thread = threading.Thread(
-# target=self._run_subprocess, args=(cmd_list,))
-# thread.start()
-#
-# def _rescan(self, row: int) -> None:
-# """
-# If passed row is track row, rescan it.
-# Otherwise, return None.
-# """
-#
-# log.debug(f"_rescan({row=})")
-#
-# with Session() as session:
-# if row in self._get_track_rows():
-# track: Tracks = self._get_row_track_object(row, session)
-# if track:
-# track.rescan(session)
-# self._update_row(session, row, track)
-#
+# self.ROW_FLAGS, new_metadata)
+
+ def _mplayer_play(self, track_id: int) -> None:
+ """Play track with mplayer"""
+
+ with Session() as session:
+ track = session.get(Tracks, track_id)
+ if not track:
+ log.error(
+ f"playlists._mplayer_play({track_id=}): "
+ "Track not found"
+ )
+ return
+
+ cmd_list = ['gmplayer', '-vc', 'null', '-vo', 'null', track.path]
+ thread = threading.Thread(
+ target=self._run_subprocess, args=(cmd_list,))
+ thread.start()
+
+ def _remove_track(self, row: int) -> None:
+ """Remove track from row, making it a section header"""
+
+ # Update playlist_rows record
+ plr = session.get(PlaylistRows, self._get_playlistrow_id(row))
+ plr.track_id = None
+ plr.save()
+
+ # Clear track text items
+ for i in range(2, len(columns) - 1):
+ self.item(row, i).setText("")
+ # 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):
# """Run args in subprocess"""
#
@@ -1654,12 +1712,12 @@ class PlaylistTab(QTableWidget):
#
# self._clear_current_track_row()
# self._meta_set_attribute(row, RowMeta.CURRENT)
-#
-# def _set_next_track_row(self, row: int) -> None:
-# """Mark this row as next track"""
-#
-# self._meta_clear_next()
-# self._meta_set_attribute(row, RowMeta.NEXT)
+
+ def _set_next_track_row(self, row: int) -> None:
+ """Mark this row as next track"""
+
+ self._meta_clear_next()
+ self._meta_set_attribute(row, RowMeta.NEXT)
#
# def _set_note_row(self, row: int) -> None:
# """Mark this row as a note"""
@@ -1676,36 +1734,34 @@ class PlaylistTab(QTableWidget):
self._meta_set_attribute(row, RowMeta.UNREADABLE)
-# def _select_event(self) -> None:
-# """
-# Called when item selection changes.
-# 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 self.selecting_in_progress:
-# return
-#
-# # Get the row number of all selected items and put into a set
-# # to deduplicate
-# sel_rows: Set[int] = set([item.row() for item in self.selectedItems()])
-# # If no rows are selected, we have nothing to do
-# if len(sel_rows) == 0:
-# self.musicmuster.lblSumPlaytime.setText("")
-# return
-#
-# notes_rows: Set[int] = set(self._get_notes_rows())
-# ms: int = 0
-# with Session() as session:
-# 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:
-# self.musicmuster.lblSumPlaytime.setText(
-# f"Selected duration: {helpers.ms_to_mmss(ms)}")
-# else:
-# self.musicmuster.lblSumPlaytime.setText("")
+ def _select_event(self) -> None:
+ """
+ Called when item selection changes.
+ 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 self.selecting_in_progress:
+ return
+
+ # Get the row number of all selected items and put into a set
+ # to deduplicate
+ selected_rows = set([item.row() for item in self.selectedItems()])
+ # If no rows are selected, we have nothing to do
+ if len(selected_rows) == 0:
+ self.musicmuster.lblSumPlaytime.setText("")
+ return
+
+ ms = 0
+ for row in selected_rows:
+ ms += self._get_row_duration(row)
+
+ # Only paint message if there are selected track rows
+ if ms > 0:
+ self.musicmuster.lblSumPlaytime.setText(
+ f"Selected duration: {helpers.ms_to_mmss(ms)}")
+ else:
+ self.musicmuster.lblSumPlaytime.setText("")
#
# def _select_tracks(self, played: bool) -> None:
# """
@@ -1739,41 +1795,42 @@ class PlaylistTab(QTableWidget):
self.setColumnWidth(idx, record.f_int)
else:
self.setColumnWidth(idx, Config.DEFAULT_COLUMN_WIDTH)
-#
-# def _set_next(self, row: int, session: Session) -> None:
-# """
-# Set passed row as next track to play.
-#
-# Actions required:
-# - Check row is a track row
-# - Check track is readable
-# - Mark as next track
-# - Update display
-# - Notify musicmuster
-# """
-#
-# log.debug(f"_set_next({row=})")
-#
-# # Check row is a track row
-# if row in self._get_notes_rows():
-# return None
-# track: Tracks = self._get_row_track_object(row, session)
-# if not track:
-# return None
-#
-# # Check track is readable
-# if not self._file_is_readable(track.path):
-# self._set_unreadable_row(row)
-# return None
-#
-# # Mark as next track
-# self._set_next_track_row(row)
-#
-# # Update display
-# self.update_display(session)
-#
-# # Notify musicmuster
-# self.musicmuster.this_is_the_next_track(self, track, session)
+
+ def _set_next(self, session: Session, row: int) -> None:
+ """
+ Set passed row as next track to play.
+
+ Actions required:
+ - Check row has a track
+ - Check track is readable
+ - Mark as next track
+ - Update display
+ - Notify musicmuster
+ """
+
+ track_id = self._get_row_track_id(row_number)
+ if not track_id:
+ log.error(f"playlists._set_next({row=}) has no track associated")
+ return
+
+ track = session.get(Tracks, track_id)
+ if not track:
+ log.error(f"playlists._set_next({row=}): Track not found")
+ return
+
+ # Check track is readable
+ if not self._file_is_readable(track.path):
+ self._set_unreadable_row(row)
+ return None
+
+ # Mark as next track
+ self._set_next_track_row(row)
+
+ # Update display
+ self.update_display(session)
+
+ # Notify musicmuster
+ self.musicmuster.this_is_the_next_track(self, track, session)
def _set_row_bold(self, row: int, bold: bool = True) -> None:
"""Make row bold (bold=True) or not bold"""
@@ -1802,13 +1859,11 @@ class PlaylistTab(QTableWidget):
#
# self.item(row, self.COL_USERDATA).setData(
# self.CONTENT_OBJECT, object_id)
-#
-# def _set_row_duration(self, row: int, ms: int) -> None:
-# """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)
+
+ def _set_row_duration(self, row: int, ms: int) -> None:
+ """Set duration of this row in row metadata"""
+
+ self.item(row, columns['userdata'].idx).setData(self.ROW_DURATION, ms)
def _set_row_end_time(self, row: int, time: Optional[datetime]) -> None:
"""Set passed row end time to passed time"""
diff --git a/app/ui/main_window.ui b/app/ui/main_window.ui
index 590dc88..97bc222 100644
--- a/app/ui/main_window.ui
+++ b/app/ui/main_window.ui
@@ -734,57 +734,48 @@ border: 1px solid rgb(85, 87, 83);
-
-
@@ -818,13 +809,13 @@ border: 1px solid rgb(85, 87, 83);
Ctrl+Alt+Return
-
+
../../../../.designer/backup/icon_search_database.png../../../../.designer/backup/icon_search_database.png
- Search &database
+ Insert &track...
Ctrl+D
@@ -949,7 +940,7 @@ border: 1px solid rgb(85, 87, 83);
- E&xport playlist...
+ E&xport...
@@ -981,9 +972,9 @@ border: 1px solid rgb(85, 87, 83);
Select played tracks
-
+
- Select unplayed tracks
+ Move &unplayed tracks to...
@@ -1001,7 +992,7 @@ border: 1px solid rgb(85, 87, 83);
- Import...
+ Import track...
Ctrl+Shift+I
@@ -1020,6 +1011,16 @@ border: 1px solid rgb(85, 87, 83);
/
+
+
+ Insert §ion header...
+
+
+
+
+ &Remove track
+
+
diff --git a/app/ui/main_window_ui.py b/app/ui/main_window_ui.py
index 6f12b27..8424f10 100644
--- a/app/ui/main_window_ui.py
+++ b/app/ui/main_window_ui.py
@@ -342,8 +342,6 @@ class Ui_MainWindow(object):
self.menuFile.setObjectName("menuFile")
self.menuPlaylist = QtWidgets.QMenu(self.menubar)
self.menuPlaylist.setObjectName("menuPlaylist")
- self.menu_Music = QtWidgets.QMenu(self.menubar)
- self.menu_Music.setObjectName("menu_Music")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setEnabled(True)
@@ -360,11 +358,11 @@ class Ui_MainWindow(object):
icon4.addPixmap(QtGui.QPixmap(":/icons/next"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
self.actionSkip_next.setIcon(icon4)
self.actionSkip_next.setObjectName("actionSkip_next")
- self.actionSearch_database = QtWidgets.QAction(MainWindow)
+ self.actionInsert = QtWidgets.QAction(MainWindow)
icon5 = QtGui.QIcon()
icon5.addPixmap(QtGui.QPixmap("app/ui/../../../../.designer/backup/icon_search_database.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
- self.actionSearch_database.setIcon(icon5)
- self.actionSearch_database.setObjectName("actionSearch_database")
+ self.actionInsert.setIcon(icon5)
+ self.actionInsert.setObjectName("actionInsert")
self.actionAdd_file = QtWidgets.QAction(MainWindow)
icon6 = QtGui.QIcon()
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_played_tracks = QtWidgets.QAction(MainWindow)
self.actionSelect_played_tracks.setObjectName("actionSelect_played_tracks")
- self.actionSelect_unplayed_tracks = QtWidgets.QAction(MainWindow)
- self.actionSelect_unplayed_tracks.setObjectName("actionSelect_unplayed_tracks")
+ self.actionMove_unplayed = QtWidgets.QAction(MainWindow)
+ self.actionMove_unplayed.setObjectName("actionMove_unplayed")
self.actionAdd_note = QtWidgets.QAction(MainWindow)
self.actionAdd_note.setObjectName("actionAdd_note")
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.actionSearch = QtWidgets.QAction(MainWindow)
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.actionOpenPlaylist)
self.menuFile.addAction(self.actionClosePlaylist)
self.menuFile.addAction(self.actionRenamePlaylist)
+ self.menuFile.addAction(self.actionExport_playlist)
self.menuFile.addAction(self.actionDeletePlaylist)
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.actionExport_playlist)
self.menuFile.addSeparator()
self.menuFile.addAction(self.actionE_xit)
self.menuFile.addSeparator()
self.menuPlaylist.addSeparator()
- self.menuPlaylist.addAction(self.actionSearch_database)
- self.menuPlaylist.addAction(self.actionAdd_note)
+ self.menuPlaylist.addAction(self.actionPlay_next)
+ 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.action_Clear_selection)
- self.menuPlaylist.addSeparator()
self.menuPlaylist.addAction(self.actionSetNext)
+ self.menuPlaylist.addAction(self.action_Clear_selection)
+ self.menuPlaylist.addAction(self.actionInsert_section_header)
self.menuPlaylist.addSeparator()
- self.menuPlaylist.addAction(self.actionSelect_unplayed_tracks)
- self.menuPlaylist.addAction(self.actionSelect_played_tracks)
- self.menuPlaylist.addAction(self.actionMoveSelected)
- self.menuPlaylist.addSeparator()
- self.menuPlaylist.addSeparator()
+ self.menuPlaylist.addAction(self.actionSearch)
self.menuPlaylist.addAction(self.actionSelect_next_track)
self.menuPlaylist.addAction(self.actionSelect_previous_track)
self.menuPlaylist.addSeparator()
- self.menuPlaylist.addAction(self.actionSearch)
- 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.menuPlaylist.addAction(self.actionEnable_controls)
self.menubar.addAction(self.menuFile.menuAction())
self.menubar.addAction(self.menuPlaylist.menuAction())
- self.menubar.addAction(self.menu_Music.menuAction())
self.retranslateUi(MainWindow)
self.tabPlaylist.setCurrentIndex(-1)
@@ -505,15 +503,14 @@ class Ui_MainWindow(object):
self.label_end_timer.setText(_translate("MainWindow", "00:00"))
self.btnDrop3db.setText(_translate("MainWindow", "-3dB to talk"))
self.btnHidePlayed.setText(_translate("MainWindow", "Hide played"))
- self.menuFile.setTitle(_translate("MainWindow", "Fi&le"))
- self.menuPlaylist.setTitle(_translate("MainWindow", "&Tracks"))
- self.menu_Music.setTitle(_translate("MainWindow", "&Music"))
+ self.menuFile.setTitle(_translate("MainWindow", "P&laylists"))
+ self.menuPlaylist.setTitle(_translate("MainWindow", "Sho&wtime"))
self.actionPlay_next.setText(_translate("MainWindow", "&Play next"))
self.actionPlay_next.setShortcut(_translate("MainWindow", "Return"))
self.actionSkip_next.setText(_translate("MainWindow", "Skip to &next"))
self.actionSkip_next.setShortcut(_translate("MainWindow", "Ctrl+Alt+Return"))
- self.actionSearch_database.setText(_translate("MainWindow", "Search &database"))
- self.actionSearch_database.setShortcut(_translate("MainWindow", "Ctrl+D"))
+ self.actionInsert.setText(_translate("MainWindow", "Insert &track..."))
+ self.actionInsert.setShortcut(_translate("MainWindow", "Ctrl+D"))
self.actionAdd_file.setText(_translate("MainWindow", "Add &file"))
self.actionAdd_file.setShortcut(_translate("MainWindow", "Ctrl+F"))
self.actionFade.setText(_translate("MainWindow", "F&ade"))
@@ -534,7 +531,7 @@ class Ui_MainWindow(object):
self.actionRenamePlaylist.setText(_translate("MainWindow", "&Rename..."))
self.actionDeletePlaylist.setText(_translate("MainWindow", "Dele&te..."))
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.setShortcut(_translate("MainWindow", "Ctrl+N"))
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.setShortcut(_translate("MainWindow", "K"))
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.setShortcut(_translate("MainWindow", "Ctrl+T"))
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.actionDownload_CSV_of_played_tracks.setText(_translate("MainWindow", "Download CSV of played tracks..."))
self.actionSearch.setText(_translate("MainWindow", "Search..."))
self.actionSearch.setShortcut(_translate("MainWindow", "/"))
+ self.actionInsert_section_header.setText(_translate("MainWindow", "Insert §ion header..."))
+ self.actionRemove.setText(_translate("MainWindow", "&Remove track"))
import icons_rc