Compare commits
6 Commits
c5f094443a
...
e4fe4b576e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4fe4b576e | ||
|
|
54cfb1191a | ||
|
|
d8072ae73f | ||
|
|
d2e2144148 | ||
|
|
9dfc5e50cc | ||
|
|
4267901630 |
@ -1,5 +1,6 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import os.path
|
||||
import sqlalchemy
|
||||
|
||||
from datetime import datetime
|
||||
@ -79,7 +80,7 @@ class Notes(Base):
|
||||
Update note details. If text=None, don't change text.
|
||||
"""
|
||||
|
||||
DEBUG(f"update_note(id={id}, row={row}, text={text})")
|
||||
DEBUG(f"Notes.update_note(id={id}, row={row}, text={text})")
|
||||
|
||||
note = session.query(cls).filter(cls.id == id).one()
|
||||
note.row = row
|
||||
@ -500,8 +501,7 @@ class Tracks(Base):
|
||||
DEBUG(f"Tracks.get_track_from_filename({filename=})")
|
||||
try:
|
||||
track = session.query(Tracks).filter(Tracks.path.ilike(
|
||||
# TODO: filename separator is hardcoded here
|
||||
f'%/{filename}')).one()
|
||||
f'%{os.path.sep}{filename}')).one()
|
||||
return track
|
||||
except (NoResultFound, MultipleResultsFound):
|
||||
return None
|
||||
|
||||
@ -132,7 +132,9 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
if self.music.playing():
|
||||
DEBUG("closeEvent() ignored as music is playing")
|
||||
event.ignore()
|
||||
# TODO notify user
|
||||
helpers.show_warning(
|
||||
"Track playing",
|
||||
"Can't close application while track is playing")
|
||||
else:
|
||||
DEBUG("closeEvent() accepted")
|
||||
|
||||
@ -153,7 +155,12 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
if record.f_int != self.y():
|
||||
record.update(session, {'f_int': self.y()})
|
||||
|
||||
self.visible_playlist_tab().close(session)
|
||||
# Find a playlist tab (as opposed to an info tab) and
|
||||
# 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()
|
||||
|
||||
@ -511,12 +518,6 @@ class Window(QMainWindow, Ui_MainWindow):
|
||||
silence_at - self.current_track.fade_at
|
||||
))
|
||||
|
||||
def play_previous(self):
|
||||
"Resume playing last track"
|
||||
|
||||
# TODO
|
||||
pass
|
||||
|
||||
def search_database(self):
|
||||
with Session() as session:
|
||||
dlg = DbDialog(self, session)
|
||||
|
||||
@ -17,7 +17,7 @@ import os
|
||||
|
||||
from config import Config
|
||||
from datetime import datetime, timedelta
|
||||
from helpers import get_relative_date, open_in_audacity, show_warning
|
||||
from helpers import get_relative_date, open_in_audacity
|
||||
from log import DEBUG, ERROR
|
||||
from model import (
|
||||
Notes, Playdates, Playlists, PlaylistTracks, Session, Settings, Tracks
|
||||
@ -172,23 +172,31 @@ class PlaylistTab(QTableWidget):
|
||||
if item is not None:
|
||||
row = item.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)
|
||||
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():
|
||||
act_setnext = self.menu.addAction("Set next")
|
||||
act_setnext.triggered.connect(lambda: self._set_next(row))
|
||||
if not current and not next:
|
||||
act_setnext = self.menu.addAction("Set next")
|
||||
act_setnext.triggered.connect(
|
||||
lambda: self._set_next(row))
|
||||
act_copypath = self.menu.addAction("Copy track path")
|
||||
act_copypath.triggered.connect(
|
||||
lambda: self._copy_path(row))
|
||||
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:
|
||||
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:
|
||||
self.menu.addSeparator()
|
||||
act_delete = self.menu.addAction('Delete')
|
||||
act_delete.triggered.connect(self._delete_rows)
|
||||
act_info = self.menu.addAction('Info')
|
||||
act_info.triggered.connect(lambda: self._info_row(row))
|
||||
act_delete = self.menu.addAction('Delete')
|
||||
act_delete.triggered.connect(self._delete_rows)
|
||||
|
||||
return super(PlaylistTab, self).eventFilter(source, event)
|
||||
|
||||
@ -224,10 +232,12 @@ class PlaylistTab(QTableWidget):
|
||||
start_time = None
|
||||
try:
|
||||
start_time = datetime.strptime(note.note[-9:], " %H:%M:%S").time()
|
||||
DEBUG(f"Note contains valid time={start_time}")
|
||||
DEBUG(
|
||||
f"playlist.inset_note(): Note contains valid time={start_time}"
|
||||
)
|
||||
except ValueError:
|
||||
DEBUG(
|
||||
f"Note on row {row} ('{note.note}') "
|
||||
f"playlist.inset_note(): Note on row {row} ('{note.note}') "
|
||||
"does not contain valid time"
|
||||
)
|
||||
|
||||
@ -613,6 +623,9 @@ class PlaylistTab(QTableWidget):
|
||||
|
||||
if not self.editing_cell:
|
||||
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()
|
||||
|
||||
@ -621,7 +634,28 @@ class PlaylistTab(QTableWidget):
|
||||
row_id = self._get_row_id(row)
|
||||
with Session() as session:
|
||||
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)
|
||||
# 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:
|
||||
track = Tracks.get_track(session, row_id)
|
||||
if column == self.COL_ARTIST:
|
||||
@ -639,6 +673,11 @@ class PlaylistTab(QTableWidget):
|
||||
def _cell_edit_ended(self):
|
||||
DEBUG("_cell_edit_ended()")
|
||||
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()
|
||||
|
||||
def _delete_rows(self):
|
||||
@ -652,22 +691,14 @@ class PlaylistTab(QTableWidget):
|
||||
|
||||
with Session() as session:
|
||||
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()
|
||||
|
||||
msg = QMessageBox(self)
|
||||
msg.setIcon(QMessageBox.Warning)
|
||||
msg.setText(f"Delete '{title}'?")
|
||||
msg.setStandardButtons(QMessageBox.Yes | QMessageBox.Cancel)
|
||||
msg.setDefaultButton(QMessageBox.Cancel)
|
||||
msg.setWindowTitle("Delete row")
|
||||
# Store list of notes
|
||||
# Store list of rows to delete
|
||||
if msg.exec() == QMessageBox.Yes:
|
||||
rows_to_delete.append(row)
|
||||
|
||||
@ -963,6 +994,18 @@ class PlaylistTab(QTableWidget):
|
||||
# Set colours and start times
|
||||
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
|
||||
for row in range(self.rowCount()):
|
||||
# We can't calculate start times until next_start_time is
|
||||
@ -1043,12 +1086,16 @@ class PlaylistTab(QTableWidget):
|
||||
self._set_row_not_bold(row)
|
||||
else:
|
||||
# Set start/end times only if we haven't played it yet
|
||||
if next_start_time:
|
||||
if next_start_time and row >= start_times_row:
|
||||
self._set_row_start_time(row, next_start_time)
|
||||
next_start_time = self._calculate_next_start_time(
|
||||
session, row, next_start_time)
|
||||
# Set end 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
|
||||
self._set_row_bold(row)
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import shutil
|
||||
import tempfile
|
||||
|
||||
from config import Config
|
||||
from helpers import show_warning
|
||||
from log import DEBUG, INFO
|
||||
from model import Notes, Playdates, PlaylistTracks, Session, Tracks
|
||||
from mutagen.flac import FLAC
|
||||
@ -369,15 +370,23 @@ def update_meta(session, track, artist=None, title=None):
|
||||
INFO(f"File type {ftype} not implemented")
|
||||
return
|
||||
|
||||
# Update tags
|
||||
f = tag_handler(track.path)
|
||||
with Session() as session:
|
||||
try:
|
||||
if artist:
|
||||
f["artist"] = artist
|
||||
Tracks.update_artist(session, track.id, 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:
|
||||
if artist:
|
||||
Tracks.update_artist(session, track.id, artist)
|
||||
if title:
|
||||
Tracks.update_title(session, track.id, title)
|
||||
f.save()
|
||||
|
||||
|
||||
if __name__ == '__main__' and '__file__' in globals():
|
||||
|
||||
Loading…
Reference in New Issue
Block a user