Compare commits

..

No commits in common. "e4fe4b576ec3c130b3f39caeaffa8defb8936f5a" and "c5f094443a493ac9a20c355ab51953c09373660f" have entirely different histories.

4 changed files with 38 additions and 95 deletions

View File

@ -1,6 +1,5 @@
#!/usr/bin/python3 #!/usr/bin/python3
import os.path
import sqlalchemy import sqlalchemy
from datetime import datetime from datetime import datetime
@ -80,7 +79,7 @@ class Notes(Base):
Update note details. If text=None, don't change text. Update note details. If text=None, don't change text.
""" """
DEBUG(f"Notes.update_note(id={id}, row={row}, text={text})") DEBUG(f"update_note(id={id}, row={row}, text={text})")
note = session.query(cls).filter(cls.id == id).one() note = session.query(cls).filter(cls.id == id).one()
note.row = row note.row = row
@ -501,7 +500,8 @@ class Tracks(Base):
DEBUG(f"Tracks.get_track_from_filename({filename=})") DEBUG(f"Tracks.get_track_from_filename({filename=})")
try: try:
track = session.query(Tracks).filter(Tracks.path.ilike( track = session.query(Tracks).filter(Tracks.path.ilike(
f'%{os.path.sep}{filename}')).one() # TODO: filename separator is hardcoded here
f'%/{filename}')).one()
return track return track
except (NoResultFound, MultipleResultsFound): except (NoResultFound, MultipleResultsFound):
return None return None

View File

@ -132,9 +132,7 @@ class Window(QMainWindow, Ui_MainWindow):
if self.music.playing(): if self.music.playing():
DEBUG("closeEvent() ignored as music is playing") DEBUG("closeEvent() ignored as music is playing")
event.ignore() event.ignore()
helpers.show_warning( # TODO notify user
"Track playing",
"Can't close application while track is playing")
else: else:
DEBUG("closeEvent() accepted") DEBUG("closeEvent() accepted")
@ -155,12 +153,7 @@ class Window(QMainWindow, Ui_MainWindow):
if record.f_int != self.y(): if record.f_int != self.y():
record.update(session, {'f_int': self.y()}) record.update(session, {'f_int': self.y()})
# Find a playlist tab (as opposed to an info tab) and self.visible_playlist_tab().close(session)
# save column widths
if self.current_track_playlist_tab:
self.current_track_playlist_tab.close(session)
elif self.next_track_playlist_tab:
self.next_track_playlist_tab.close(session)
event.accept() event.accept()
@ -518,6 +511,12 @@ class Window(QMainWindow, Ui_MainWindow):
silence_at - self.current_track.fade_at silence_at - self.current_track.fade_at
)) ))
def play_previous(self):
"Resume playing last track"
# TODO
pass
def search_database(self): def search_database(self):
with Session() as session: with Session() as session:
dlg = DbDialog(self, session) dlg = DbDialog(self, session)

View File

@ -17,7 +17,7 @@ import os
from config import Config from config import Config
from datetime import datetime, timedelta from datetime import datetime, timedelta
from helpers import get_relative_date, open_in_audacity from helpers import get_relative_date, open_in_audacity, show_warning
from log import DEBUG, ERROR from log import DEBUG, ERROR
from model import ( from model import (
Notes, Playdates, Playlists, PlaylistTracks, Session, Settings, Tracks Notes, Playdates, Playlists, PlaylistTracks, Session, Settings, Tracks
@ -172,31 +172,23 @@ class PlaylistTab(QTableWidget):
if item is not None: if item is not None:
row = item.row() row = item.row()
DEBUG(f"playlist.eventFilter(): Right-click on row {row}") DEBUG(f"playlist.eventFilter(): Right-click on row {row}")
current = row == self._meta_get_current()
next = row == self._meta_get_next()
self.menu = QMenu(self) 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._meta_get_notes(): if row not in self._meta_get_notes():
if not current and not next:
act_setnext = self.menu.addAction("Set next") act_setnext = self.menu.addAction("Set next")
act_setnext.triggered.connect( act_setnext.triggered.connect(lambda: self._set_next(row))
lambda: self._set_next(row))
act_copypath = self.menu.addAction("Copy track path") act_copypath = self.menu.addAction("Copy track path")
act_copypath.triggered.connect( act_copypath.triggered.connect(
lambda: self._copy_path(row)) lambda: self._copy_path(row))
if not current:
act_rescan = self.menu.addAction("Rescan track") act_rescan = self.menu.addAction("Rescan track")
act_rescan.triggered.connect(lambda: self._rescan(row)) act_rescan.triggered.connect(lambda: self._rescan(row))
act_audacity = self.menu.addAction( act_audacity = self.menu.addAction(
"Open track in Audacity") "Open track in Audacity")
act_audacity.triggered.connect( act_audacity.triggered.connect(lambda: self._audacity(row))
lambda: self._audacity(row))
if not current and not next:
self.menu.addSeparator() self.menu.addSeparator()
act_delete = self.menu.addAction('Delete') act_delete = self.menu.addAction('Delete')
act_delete.triggered.connect(self._delete_rows) act_delete.triggered.connect(self._delete_rows)
act_info = self.menu.addAction('Info')
act_info.triggered.connect(lambda: self._info_row(row))
return super(PlaylistTab, self).eventFilter(source, event) return super(PlaylistTab, self).eventFilter(source, event)
@ -232,12 +224,10 @@ class PlaylistTab(QTableWidget):
start_time = None start_time = None
try: try:
start_time = datetime.strptime(note.note[-9:], " %H:%M:%S").time() start_time = datetime.strptime(note.note[-9:], " %H:%M:%S").time()
DEBUG( DEBUG(f"Note contains valid time={start_time}")
f"playlist.inset_note(): Note contains valid time={start_time}"
)
except ValueError: except ValueError:
DEBUG( DEBUG(
f"playlist.inset_note(): Note on row {row} ('{note.note}') " f"Note on row {row} ('{note.note}') "
"does not contain valid time" "does not contain valid time"
) )
@ -623,9 +613,6 @@ class PlaylistTab(QTableWidget):
if not self.editing_cell: if not self.editing_cell:
return return
# If we update start time, _cell_changed will be called
if column not in [self.COL_TITLE, self.COL_ARTIST]:
return
new = self.item(row, column).text() new = self.item(row, column).text()
@ -634,28 +621,7 @@ class PlaylistTab(QTableWidget):
row_id = self._get_row_id(row) row_id = self._get_row_id(row)
with Session() as session: with Session() as session:
if row in self._meta_get_notes(): if row in self._meta_get_notes():
# Save change to database
DEBUG(
f"Notes.update_note: saving new note text '{new=}'",
True
)
Notes.update_note(session, row_id, row, new) Notes.update_note(session, row_id, row, new)
# Set/clear row start time accordingly
try:
start_dt = datetime.strptime(new[-9:], " %H:%M:%S")
start_time = start_dt.time()
self._set_row_start_time(row, start_time)
DEBUG(
f"_cell_changed:Note {new} contains valid "
f"time={start_time}"
)
except ValueError:
# Reset row start time in case it used to have one
self._set_row_start_time(row, None)
DEBUG(
f"_cell_changed:Note {new} does not contain "
"start time"
)
else: else:
track = Tracks.get_track(session, row_id) track = Tracks.get_track(session, row_id)
if column == self.COL_ARTIST: if column == self.COL_ARTIST:
@ -673,11 +639,6 @@ class PlaylistTab(QTableWidget):
def _cell_edit_ended(self): def _cell_edit_ended(self):
DEBUG("_cell_edit_ended()") DEBUG("_cell_edit_ended()")
self.editing_cell = False self.editing_cell = False
# Call repaint to update start times, such as when a note has
# been edited
self._repaint()
self.master_process.enable_play_next_controls() self.master_process.enable_play_next_controls()
def _delete_rows(self): def _delete_rows(self):
@ -691,14 +652,22 @@ class PlaylistTab(QTableWidget):
with Session() as session: with Session() as session:
for row in rows: for row in rows:
if row == self._meta_get_current():
show_warning("Silly", "Can't delete playing track")
return
elif row == self._meta_get_next():
show_warning("Safety", "Can't delete next track")
return
title = self.item(row, self.COL_TITLE).text() title = self.item(row, self.COL_TITLE).text()
msg = QMessageBox(self) msg = QMessageBox(self)
msg.setIcon(QMessageBox.Warning) msg.setIcon(QMessageBox.Warning)
msg.setText(f"Delete '{title}'?") msg.setText(f"Delete '{title}'?")
msg.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel) msg.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel)
msg.setDefaultButton(QMessageBox.Cancel) msg.setDefaultButton(QMessageBox.Cancel)
msg.setWindowTitle("Delete row") msg.setWindowTitle("Delete row")
# Store list of rows to delete # Store list of notes
if msg.exec() == QMessageBox.Yes: if msg.exec() == QMessageBox.Yes:
rows_to_delete.append(row) rows_to_delete.append(row)
@ -994,18 +963,6 @@ class PlaylistTab(QTableWidget):
# Set colours and start times # Set colours and start times
next_start_time = None next_start_time = None
# Don't change start times for tracks that have been played.
# For unplayed tracks, if there's a 'current' or 'next'
# track marked, populate start times from then onwards. If
# neither, populate start times from first note with a start
# time.
if current and next:
start_times_row = min(current, next)
else:
start_times_row = current or next
if not start_times_row:
start_times_row = 0
# Cycle through all rows # Cycle through all rows
for row in range(self.rowCount()): for row in range(self.rowCount()):
# We can't calculate start times until next_start_time is # We can't calculate start times until next_start_time is
@ -1086,16 +1043,12 @@ class PlaylistTab(QTableWidget):
self._set_row_not_bold(row) self._set_row_not_bold(row)
else: else:
# Set start/end times only if we haven't played it yet # Set start/end times only if we haven't played it yet
if next_start_time and row >= start_times_row: if next_start_time:
self._set_row_start_time(row, next_start_time) self._set_row_start_time(row, next_start_time)
next_start_time = self._calculate_next_start_time( next_start_time = self._calculate_next_start_time(
session, row, next_start_time) session, row, next_start_time)
# Set end time # Set end time
self._set_row_end_time(row, next_start_time) self._set_row_end_time(row, next_start_time)
else:
# Clear start and end time
self._set_row_start_time(row, None)
self._set_row_end_time(row, None)
# Don't dim unplayed tracks # Don't dim unplayed tracks
self._set_row_bold(row) self._set_row_bold(row)

View File

@ -6,7 +6,6 @@ import shutil
import tempfile import tempfile
from config import Config from config import Config
from helpers import show_warning
from log import DEBUG, INFO from log import DEBUG, INFO
from model import Notes, Playdates, PlaylistTracks, Session, Tracks from model import Notes, Playdates, PlaylistTracks, Session, Tracks
from mutagen.flac import FLAC from mutagen.flac import FLAC
@ -370,23 +369,15 @@ def update_meta(session, track, artist=None, title=None):
INFO(f"File type {ftype} not implemented") INFO(f"File type {ftype} not implemented")
return return
# Update tags
f = tag_handler(track.path) f = tag_handler(track.path)
try:
if artist:
f["artist"] = artist
if title:
f["title"] = title
f.save()
except TypeError:
show_warning("TAG error", "Can't update tag. Try editing in Audacity")
# Update database
with Session() as session: with Session() as session:
if artist: if artist:
f["artist"] = artist
Tracks.update_artist(session, track.id, artist) Tracks.update_artist(session, track.id, artist)
if title: if title:
f["title"] = title
Tracks.update_title(session, track.id, title) Tracks.update_title(session, track.id, title)
f.save()
if __name__ == '__main__' and '__file__' in globals(): if __name__ == '__main__' and '__file__' in globals():