From c7f7f25af064f388d8385316518c888612c15947 Mon Sep 17 00:00:00 2001 From: Keith Edmunds Date: Tue, 19 Apr 2022 15:25:15 +0100 Subject: [PATCH] Run file import in separate thread --- app/config.py | 1 + app/musicmuster.py | 49 +++++++++++++++++++++++++++++++++++++++------- app/utilities.py | 44 ++++++++--------------------------------- 3 files changed, 51 insertions(+), 43 deletions(-) diff --git a/app/config.py b/app/config.py index a25f66d..f52ec2f 100644 --- a/app/config.py +++ b/app/config.py @@ -54,6 +54,7 @@ class Config(object): NORMALISE_ON_IMPORT = True NOTE_TIME_FORMAT = "%H:%M:%S" ROOT = os.environ.get('ROOT') or "/home/kae/music" + IMPORT_DESTINATION = os.path.join(ROOT, "Singles") SCROLL_TOP_MARGIN = 3 TESTMODE = True TOD_TIME_FORMAT = "%H:%M:%S" diff --git a/app/musicmuster.py b/app/musicmuster.py index d94fa92..d84bfc4 100755 --- a/app/musicmuster.py +++ b/app/musicmuster.py @@ -4,6 +4,7 @@ import argparse import os.path import psutil import sys +import threading import urllib.parse import webbrowser @@ -24,6 +25,7 @@ from PyQt5.QtWidgets import ( QLineEdit, QListWidgetItem, QMainWindow, + QMessageBox, ) import dbconfig @@ -420,18 +422,51 @@ class Window(QMainWindow, Ui_MainWindow): dlg = QFileDialog() dlg.setFileMode(QFileDialog.ExistingFiles) dlg.setViewMode(QFileDialog.Detail) - # TODO: remove hardcoded directory - dlg.setDirectory(os.path.join(Config.ROOT, "Singles")) + dlg.setDirectory(Config.IMPORT_DESTINATION) dlg.setNameFilter("Music files (*.flac *.mp3)") if dlg.exec_(): with Session() as session: + txt: str = "" + new_tracks = [] for fname in dlg.selectedFiles(): - track = create_track_from_file(session, fname) - # Add to playlist on screen - # If we don't specify "repaint=False", playlist will - # also be saved to database - self.visible_playlist_tab().insert_track(session, track) + tags = helpers.get_tags(fname) + new_tracks.append((fname, tags)) + title = tags['title'] + artist = tags['artist'] + possible_matches = Tracks.search_titles(session, title) + if possible_matches: + txt += 'Similar to new track ' + txt += f'"{title}" by "{artist}":\n' + for track in possible_matches: + txt += f' "{track.title}" by {track.artist}\n' + txt += "\n" + # Check whether to proceed if there were potential matches + if txt: + txt += "Proceed with import?" + result = QMessageBox.question(self, + "Possible duplicates", + txt, + QMessageBox.Ok, + QMessageBox.Cancel + ) + if result == QMessageBox.Cancel: + return + + # Import in separate thread + thread = threading.Thread(target=self._import_tracks, + args=(session, new_tracks)) + thread.start() + + def _import_tracks(self, session: Session, tracks: list): + """Import passed files""" + + for (fname, tags) in tracks: + track = create_track_from_file(session, fname, tags=tags) + # Add to playlist on screen + # If we don't specify "repaint=False", playlist will + # also be saved to database + self.visible_playlist_tab().insert_track(session, track) def load_last_playlists(self): """Load the playlists that we loaded at end of last session""" diff --git a/app/utilities.py b/app/utilities.py index aa07602..a6ea5ff 100755 --- a/app/utilities.py +++ b/app/utilities.py @@ -38,7 +38,6 @@ def main(): group.add_argument('-f', '--full-update', action="store_true", dest="full_update", default=False, help="Update database") - group.add_argument('-i', '--import', dest="fname", help="Input file") args = p.parse_args() # Run as required @@ -50,18 +49,13 @@ def main(): DEBUG("Full update of database") with Session() as session: full_update_db(session) - elif args.fname: - fname = os.path.realpath(args.fname) - with Session() as session: - create_track_from_file(session, fname, interactive=True) - else: INFO("No action specified") DEBUG("Finished") -def create_track_from_file(session, path, normalise=None, interactive=False): +def create_track_from_file(session, path, normalise=None, tags=None): """ Create track in database from passed path, or update database entry if path already in database. @@ -69,34 +63,14 @@ def create_track_from_file(session, path, normalise=None, interactive=False): Return track. """ - if interactive: - msg = f"Importing {path}" - INFO(msg) - INFO("-" * len(msg)) - INFO("Get track info...") - t = get_tags(path) - title = t['title'] - artist = t['artist'] - if interactive: - INFO(f" Title: \"{title}\"") - INFO(f" Artist: \"{artist}\"") - # Check for duplicate - if interactive: - tracks = Tracks.search_titles(session, title) - if tracks: - print("Found the following possible matches:") - for track in tracks: - print(f'"{track.title}" by {track.artist}') - response = input("Continue [c] or abort [a]?") - if not response: - return - if response[0].lower() not in ['c', 'y']: - return + if not tags: + t = get_tags(path) + else: + t = tags + track = Tracks.get_or_create(session, path) - track.title = title - track.artist = artist - if interactive: - INFO("Parse for start, fade and silence...") + track.title = t['title'] + track.artist = t['artist'] audio = get_audio_segment(path) track.duration = len(audio) track.start_gap = leading_silence(audio) @@ -108,8 +82,6 @@ def create_track_from_file(session, path, normalise=None, interactive=False): session.commit() if normalise or normalise is None and Config.NORMALISE_ON_IMPORT: - if interactive: - INFO("Normalise...") # Check type ftype = os.path.splitext(path)[1][1:] if ftype not in ['mp3', 'flac']: