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); - Fi&le + P&laylists + + + - - &Tracks + Sho&wtime - - - - - - - - - - - - - - - - - - - - &Music - - - + + + + + + + + + + + + + - @@ -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 &section 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