Merge branch 'dev'
This commit is contained in:
commit
1421934415
@ -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)
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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'])
|
||||||
|
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user