Merge branch 'dev'

This commit is contained in:
Keith Edmunds 2024-05-20 17:55:49 +01:00
commit 1421934415
5 changed files with 69 additions and 33 deletions

View File

@ -209,9 +209,10 @@ class TrackFileData:
Simple class to track details changes to a track file Simple class to track details changes to a track file
""" """
source_file_path: str new_file_path: str
track_id: int = 0 track_id: int = 0
track_path: Optional[str] = None track_path: Optional[str] = None
obsolete_path: Optional[str] = None
tags: dict[str, Any] = field(default_factory=dict) tags: dict[str, Any] = field(default_factory=dict)
audio_metadata: dict[str, str | int | float] = field(default_factory=dict) audio_metadata: dict[str, str | int | float] = field(default_factory=dict)

View File

@ -78,6 +78,7 @@ class Config(object):
OBS_HOST = "localhost" OBS_HOST = "localhost"
OBS_PASSWORD = "auster" OBS_PASSWORD = "auster"
OBS_PORT = 4455 OBS_PORT = 4455
PLAY_NEXT_GUARD_MS = 10000
PLAY_SETTLE = 500000 PLAY_SETTLE = 500000
REPLACE_FILES_DEFAULT_SOURCE = "/home/kae/music/Singles/tmp" REPLACE_FILES_DEFAULT_SOURCE = "/home/kae/music/Singles/tmp"
RETURN_KEY_DEBOUNCE_MS = 500 RETURN_KEY_DEBOUNCE_MS = 500

View File

@ -78,18 +78,18 @@ class ReplaceFilesDialog(QDialog):
# Work through new files # Work through new files
source_dir = self.ui.lblSourceDirectory.text() source_dir = self.ui.lblSourceDirectory.text()
with db.Session() as session: with db.Session() as session:
for new_basename in os.listdir(source_dir): for new_file_basename in os.listdir(source_dir):
new_path = os.path.join(source_dir, new_basename) new_file_path = os.path.join(source_dir, new_file_basename)
if not os.path.isfile(new_path): if not os.path.isfile(new_file_path):
continue continue
rf = TrackFileData(source_file_path=new_path) rf = TrackFileData(new_file_path=new_file_path)
rf.tags = get_tags(new_path) rf.tags = get_tags(new_file_path)
if not rf.tags['title'] or not rf.tags['artist']: if not rf.tags['title'] or not rf.tags['artist']:
show_warning( show_warning(
parent=self.main_window, parent=self.main_window,
title="Error", title="Error",
msg=( msg=(
f"File {new_path} missing tags\n\n:" f"File {new_file_path} missing tags\n\n:"
f"Title={rf.tags['title']}\n" f"Title={rf.tags['title']}\n"
f"Artist={rf.tags['artist']}\n" f"Artist={rf.tags['artist']}\n"
), ),
@ -98,41 +98,52 @@ class ReplaceFilesDialog(QDialog):
# Check for same filename # Check for same filename
match_track = self.check_by_basename( match_track = self.check_by_basename(
session, new_path, rf.tags['artist'], rf.tags['title'] session, new_file_path, rf.tags['artist'], rf.tags['title']
) )
if not match_track: if not match_track:
match_track = self.check_by_title( match_track = self.check_by_title(
session, new_path, rf.tags['artist'], rf.tags['title'] session, new_file_path, rf.tags['artist'], rf.tags['title']
) )
if not match_track: if not match_track:
match_track = self.get_fuzzy_match(session, new_basename) match_track = self.get_fuzzy_match(session, new_file_basename)
# Build summary # Build summary
rf.track_path = os.path.join(Config.REPLACE_FILES_DEFAULT_DESTINATION,
new_basename)
if match_track: if match_track:
# We will store new file in the same directory as the
# existing file but with the new file name
rf.track_path = os.path.join(
os.path.dirname(match_track.path),
new_file_basename
)
# We will remove existing track file
rf.obsolete_path = match_track.path
rf.track_id = match_track.id rf.track_id = match_track.id
match_basename = os.path.basename(match_track.path) match_basename = os.path.basename(match_track.path)
if match_basename == new_basename: if match_basename == new_file_basename:
path_text = " " + new_basename + " (no change)" path_text = " " + new_file_basename + " (no change)"
else: else:
path_text = f" {match_basename}\n {new_basename}" path_text = f" {match_basename}\n {new_file_basename} (replace)"
filename_item = QTableWidgetItem(path_text) filename_item = QTableWidgetItem(path_text)
if match_track.title == rf.tags['title']: if match_track.title == rf.tags['title']:
title_text = " " + rf.tags['title'] + " (no change)" title_text = " " + rf.tags['title'] + " (no change)"
else: else:
title_text = f" {match_track.title}\n {rf.tags['title']}" title_text = f" {match_track.title}\n {rf.tags['title']} (update)"
title_item = QTableWidgetItem(title_text) title_item = QTableWidgetItem(title_text)
if match_track.artist == rf.tags['artist']: if match_track.artist == rf.tags['artist']:
artist_text = " " + rf.tags['artist'] + " (no change)" artist_text = " " + rf.tags['artist'] + " (no change)"
else: else:
artist_text = f" {match_track.artist}\n {rf.tags['artist']}" artist_text = f" {match_track.artist}\n {rf.tags['artist']} (update)"
artist_item = QTableWidgetItem(artist_text) artist_item = QTableWidgetItem(artist_text)
else: else:
filename_item = QTableWidgetItem(" " + new_basename + " (new)") rf.track_path = os.path.join(Config.REPLACE_FILES_DEFAULT_DESTINATION,
new_file_basename)
filename_item = QTableWidgetItem(" " + new_file_basename + " (new)")
title_item = QTableWidgetItem(" " + rf.tags['title']) title_item = QTableWidgetItem(" " + rf.tags['title'])
artist_item = QTableWidgetItem(" " + rf.tags['artist']) artist_item = QTableWidgetItem(" " + rf.tags['artist'])

View File

@ -165,13 +165,13 @@ class ImportTrack(QObject):
# Sanity check # Sanity check
for tf in track_files: for tf in track_files:
if not tf.tags: if not tf.tags:
raise Exception(f"ImportTrack: no tags for {tf.source_file_path}") raise Exception(f"ImportTrack: no tags for {tf.new_file_path}")
if not tf.audio_metadata: if not tf.audio_metadata:
raise Exception( raise Exception(
f"ImportTrack: no audio_metadata for {tf.source_file_path}" f"ImportTrack: no audio_metadata for {tf.new_file_path}"
) )
if tf.track_path is None: if tf.track_path is None:
raise Exception(f"ImportTrack: no track_path for {tf.source_file_path}") raise Exception(f"ImportTrack: no track_path for {tf.new_file_path}")
def run(self): def run(self):
""" """
@ -181,19 +181,19 @@ class ImportTrack(QObject):
with db.Session() as session: with db.Session() as session:
for tf in self.track_files: for tf in self.track_files:
self.signals.status_message_signal.emit( self.signals.status_message_signal.emit(
f"Importing {basename(tf.source_file_path)}", 5000 f"Importing {basename(tf.new_file_path)}", 5000
) )
# Sanity check # Sanity check
if not os.path.exists(tf.source_file_path): if not os.path.exists(tf.new_file_path):
log.error(f"ImportTrack: file not found: {tf.source_file_path=}") log.error(f"ImportTrack: file not found: {tf.new_file_path=}")
continue continue
# Move the track file. Check that we're not importing a # Move the track file. Check that we're not importing a
# file that's already in its final destination. # file that's already in its final destination.
if os.path.exists(tf.track_path) and tf.track_path != tf.source_file_path: if os.path.exists(tf.track_path) and tf.track_path != tf.new_file_path:
os.unlink(tf.track_path) os.unlink(tf.track_path)
shutil.move(tf.source_file_path, tf.track_path) shutil.move(tf.new_file_path, tf.track_path)
# Import track # Import track
try: try:
@ -1148,10 +1148,21 @@ class Window(QMainWindow, Ui_MainWindow):
> dt.datetime.now() > dt.datetime.now()
): ):
return return
# If return is pressed during first PLAY_NEXT_GUARD_MS then
# default to NOT playing the next track, else default to
# playing it.
default_yes: bool = (
track_sequence.now.start_time is not None
and (
(dt.datetime.now() - track_sequence.now.start_time).total_seconds()
* 1000
> Config.PLAY_NEXT_GUARD_MS
)
)
if not helpers.ask_yes_no( if not helpers.ask_yes_no(
"Track playing", "Track playing",
"Really play next track now?", "Really play next track now?",
default_yes=True, default_yes=default_yes,
parent=self, parent=self,
): ):
return return
@ -1275,10 +1286,20 @@ class Window(QMainWindow, Ui_MainWindow):
for rf in dlg.replacement_files: for rf in dlg.replacement_files:
if rf.track_id: if rf.track_id:
# We're updating an existing track # We're updating an existing track
# If the filename has changed, remove the
# existing file
if rf.obsolete_path is not None:
if os.path.exists(rf.obsolete_path):
os.unlink(rf.obsolete_path)
else:
log.error(
f"replace_files: could not unlink {rf.obsolete_path=}"
)
continue
if rf.track_path: if rf.track_path:
if os.path.exists(rf.track_path): if os.path.exists(rf.track_path):
os.unlink(rf.track_path) os.unlink(rf.track_path)
shutil.move(rf.source_file_path, rf.track_path) shutil.move(rf.new_file_path, rf.track_path)
track = session.get(Tracks, rf.track_id) track = session.get(Tracks, rf.track_id)
if not track: if not track:
raise Exception( raise Exception(
@ -1297,16 +1318,17 @@ class Window(QMainWindow, Ui_MainWindow):
track.path = "DUMMY" track.path = "DUMMY"
session.commit() session.commit()
track.path = rf.track_path track.path = rf.track_path
session.commit() else:
session.commit()
else: else:
# We're importing a new track # We're importing a new track
do_import = self.ok_to_import( do_import = self.ok_to_import(
session, session, os.path.basename(rf.new_file_path), rf.tags
os.path.basename(rf.source_file_path),
rf.tags
) )
if do_import: if do_import:
rf.audio_metadata = helpers.get_audio_metadata(rf.source_file_path) rf.audio_metadata = helpers.get_audio_metadata(
rf.new_file_path
)
import_files.append(rf) import_files.append(rf)
# self.import_filenames(dlg.replacement_files) # self.import_filenames(dlg.replacement_files)

View File

@ -1122,6 +1122,7 @@ class PlaylistModel(QAbstractTableModel):
track = session.get(Tracks, track_id) track = session.get(Tracks, track_id)
set_track_metadata(track) set_track_metadata(track)
self.refresh_row(session, row_number) self.refresh_row(session, row_number)
self.update_track_times()
self.invalidate_row(row_number) self.invalidate_row(row_number)
self.signals.resize_rows_signal.emit(self.playlist_id) self.signals.resize_rows_signal.emit(self.playlist_id)
session.commit() session.commit()