From 40d78066c6d2824772193ec883fa79e909831e07 Mon Sep 17 00:00:00 2001 From: billy Date: Tue, 8 Apr 2025 15:57:34 -0400 Subject: [PATCH] add files to lib return value debug --- components/DebugWindow.py | 3 +- components/MusicTable.py | 110 ++++++++++++++++++--------------- utils/add_files_to_database.py | 21 +++---- utils/get_id3_tags.py | 53 ++++++++-------- 4 files changed, 98 insertions(+), 89 deletions(-) diff --git a/components/DebugWindow.py b/components/DebugWindow.py index ca7ce6a..a47112f 100644 --- a/components/DebugWindow.py +++ b/components/DebugWindow.py @@ -8,12 +8,11 @@ from logging import debug class DebugWindow(QDialog): - def __init__(self, song_filepath: str, text: str): + def __init__(self, text: str): super(DebugWindow, self).__init__() self.setWindowTitle("debug") self.setMinimumSize(400, 400) self.text: str = text - self.song_filepath: str = song_filepath layout = QVBoxLayout() # Labels & input fields diff --git a/components/MusicTable.py b/components/MusicTable.py index ec2eb95..6756ddd 100644 --- a/components/MusicTable.py +++ b/components/MusicTable.py @@ -124,7 +124,7 @@ class MusicTable(QTableView): self.selected_song_filepath = "" self.current_song_filepath = "" self.current_song_db_id = None - self.current_song_qmodelindex = None + self.current_song_qmodelindex: QModelIndex # Properties self.setAcceptDrops(True) @@ -265,7 +265,7 @@ class MusicTable(QTableView): if directories: worker = Worker(self.get_audio_files_recursively, directories) worker.signals.signal_progress.connect(self.handle_progress) - worker.signals.signal_result.connect(self.on_recursive_search_finished) + worker.signals.signal_result.connect(self.on_get_audio_files_recursively_finished) worker.signals.signal_finished.connect(self.load_music_table) if self.qapp: threadpool = self.qapp.threadpool @@ -399,49 +399,29 @@ class MusicTable(QTableView): update_song_in_database(song_id, edited_column_name, user_input_data) return - def on_recursive_search_finished(self, result): - """file search completion handler""" - if result: - self.add_files_to_library(result) - def handle_progress(self, data): """Emits data to main""" self.handleProgressSignal.emit(data) - # ____________________ - # | | - # | | - # | Connection Mgmt | - # | | - # |____________________| + def on_get_audio_files_recursively_finished(self, result): + """file search completion handler""" + if result: + self.add_files_to_library(result) - def disconnect_data_changed(self): - """Disconnects the dataChanged signal from QTableView.model""" - try: - self.model2.dataChanged.disconnect() - except Exception: - pass + def on_add_files_to_database_finished(self, *args): + """ + Shows failed to import files and reasons + Runs after worker process signal_finished for add_files_to_database() - def connect_data_changed(self): - """Connects the dataChanged signal from QTableView.model""" - try: - self.model2.dataChanged.connect(self.on_cell_data_changed) - except Exception: - pass - - def disconnect_layout_changed(self): - """Disconnects the layoutChanged signal from QTableView.model""" - try: - self.model2.layoutChanged.disconnect() - except Exception: - pass - - def connect_layout_changed(self): - """Connects the layoutChanged signal from QTableView.model""" - try: - self.model2.layoutChanged.connect(self.restore_scroll_position) - except Exception: - pass + Args: + - args: ((return_data),) + - data returned from the original worker process function are returned here + as the first item in a tuple + """ + # TODO: make this prettier, show a table in a window instead of raw text probably + _, details = args[0][:2] + window = DebugWindow(details) + window.exec_() # ____________________ # | | @@ -458,6 +438,7 @@ class MusicTable(QTableView): """ worker = Worker(add_files_to_database, files) worker.signals.signal_progress.connect(self.qapp.handle_progress) + worker.signals.signal_result.connect(self.on_add_files_to_database_finished) worker.signals.signal_finished.connect(self.load_music_table) if self.qapp: threadpool = self.qapp.threadpool @@ -539,12 +520,9 @@ class MusicTable(QTableView): def show_id3_tags_debug_menu(self): """Shows ID3 tags for a specific .mp3 file""" - selected_song_filepath = self.get_selected_song_filepath() - if selected_song_filepath is None: - return - current_song = self.get_selected_song_metadata() - lyrics_window = DebugWindow(selected_song_filepath, str(current_song)) - lyrics_window.exec_() + if self.get_selected_song_filepath() is not None: + window = DebugWindow(str(self.get_selected_song_metadata())) + window.exec_() def show_lyrics_menu(self): """Shows the lyrics for the currently selected song""" @@ -601,6 +579,8 @@ class MusicTable(QTableView): debug("reorganizing files") # FIXME: batch update, instead of doing 1 file at a time # DBAccess is being instantiated for every file, boo + # NOTE: + # is that even possible with move file function? # Get target directory target_dir = str(self.config["directories"]["reorganize_destination"]) @@ -618,10 +598,8 @@ class MusicTable(QTableView): if progress_callback: progress_callback.emit(f"Organizing: {filepath}") # Create the directories if they dont exist - debug("make dirs") os.makedirs(os.path.dirname(new_path), exist_ok=True) # Move the file to the new directory - debug(f"{filepath} > {new_path}") shutil.move(filepath, new_path) # Update the db with DBA.DBAccess() as db: @@ -629,7 +607,7 @@ class MusicTable(QTableView): "UPDATE song SET filepath = ? WHERE filepath = ?", (new_path, filepath), ) - debug(f"reorganize_files() | Moved: {filepath} -> {new_path}") + # debug(f"reorganize_files() | Moved: {filepath} -> {new_path}") except Exception as e: error(f"reorganize_files() | Error moving file: {filepath} | {e}") # Draw the rest of the owl @@ -757,7 +735,7 @@ class MusicTable(QTableView): def restore_scroll_position(self) -> None: """Restores the scroll position""" - debug("restore_scroll_position") + debug("restore_scroll_position (inactive)") # QTimer.singleShot( # 100, # lambda: self.verticalScrollBar().setValue(self.vertical_scroll_position), @@ -845,6 +823,40 @@ class MusicTable(QTableView): """Necessary for using members and methods of main application window""" self.qapp = qapp + # ____________________ + # | | + # | | + # | Connection Mgmt | + # | | + # |____________________| + + def disconnect_data_changed(self): + """Disconnects the dataChanged signal from QTableView.model""" + try: + self.model2.dataChanged.disconnect() + except Exception: + pass + + def connect_data_changed(self): + """Connects the dataChanged signal from QTableView.model""" + try: + self.model2.dataChanged.connect(self.on_cell_data_changed) + except Exception: + pass + + def disconnect_layout_changed(self): + """Disconnects the layoutChanged signal from QTableView.model""" + try: + self.model2.layoutChanged.disconnect() + except Exception: + pass + + def connect_layout_changed(self): + """Connects the layoutChanged signal from QTableView.model""" + try: + self.model2.layoutChanged.connect(self.restore_scroll_position) + except Exception: + pass # QT Roles diff --git a/utils/add_files_to_database.py b/utils/add_files_to_database.py index 451a219..1fe6b82 100644 --- a/utils/add_files_to_database.py +++ b/utils/add_files_to_database.py @@ -1,4 +1,5 @@ from PyQt5.QtWidgets import QMessageBox +from mutagen.id3 import ID3 import DBA from logging import debug from utils import get_id3_tags, convert_id3_timestamp_to_datetime @@ -21,25 +22,21 @@ def add_files_to_database(files, progress_callback=None): Path(user_config_dir(appname="musicpom", appauthor="billypom")) / "config.ini" ) config.read(cfg_file) - if not files: - return False + return False, {'Failure': 'All operations failed in add_files_to_database()'} extensions = config.get("settings", "extensions").split(",") + failed_dict = {} insert_data = [] # To store data for batch insert for filepath in files: if any(filepath.lower().endswith(ext) for ext in extensions): if progress_callback: progress_callback.emit(filepath) filename = filepath.split("/")[-1] - audio = get_id3_tags(filepath) - if not audio: - QMessageBox.error( - "Error", - f"Could not retrieve ID3 tags for {filepath}", - QMessageBox.Ok, - QMessageBox.Ok, - ) - return + + audio, details = get_id3_tags(filepath) + if not isinstance(audio, ID3): + failed_dict[filepath] = details + continue try: title = audio["TIT2"].text[0] @@ -105,7 +102,7 @@ def add_files_to_database(files, progress_callback=None): "INSERT OR IGNORE INTO song (filepath, title, album, artist, track_number, genre, codec, album_date, bitrate) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", insert_data, ) - return True + return True, failed_dict # id int unsigned auto_increment, diff --git a/utils/get_id3_tags.py b/utils/get_id3_tags.py index eece6c7..6507069 100644 --- a/utils/get_id3_tags.py +++ b/utils/get_id3_tags.py @@ -5,38 +5,39 @@ from mutagen.id3._frames import TIT2 from mutagen.id3._util import ID3NoHeaderError -def get_id3_tags(filename: str): +def get_id3_tags(filename: str) -> tuple[object, str]: """Get the ID3 tags for an audio file""" # debug(filename) - if filename.endswith(".flac"): - return + if filename.endswith(".mp3"): + try: + # Open the MP3 file and read its content + audio = ID3(filename) + except ID3NoHeaderError: + audio = ID3() - try: - # Open the MP3 file and read its content - audio = ID3(filename) - except ID3NoHeaderError: - audio = ID3() + try: + if os.path.exists(filename): + audio.save(os.path.abspath(filename)) - try: - if os.path.exists(filename): - audio.save(os.path.abspath(filename)) + # NOTE: If 'TIT2' tag is not set, we add it with a default value + # title = filename without extension - # NOTE: If 'TIT2' tag is not set, we add it with a default value - # title = filename without extension - - title = os.path.splitext(os.path.basename(filename))[0] - list_of_id3_tags = list(audio.keys()) - if "TIT2" in list_of_id3_tags: + title = os.path.splitext(os.path.basename(filename))[0] + list_of_id3_tags = list(audio.keys()) + if "TIT2" in list_of_id3_tags: + audio.save() + return audio, "" + else: + tit2_tag = TIT2(encoding=3, text=[title]) + audio["TIT2"] = tit2_tag + # Save the updated tags audio.save() - return audio - else: - tit2_tag = TIT2(encoding=3, text=[title]) - audio["TIT2"] = tit2_tag - # Save the updated tags - audio.save() + return audio, "" + + except Exception as e: + error(f"Could not assign file ID3 tag: {e}") + return None, f"Could not assign ID3 tag to file: {e}" + return None, "non mp3 file" - except Exception as e: - error(f"Could not assign file ID3 tag: {e}") - return audio