add files to lib return value debug

This commit is contained in:
billy 2025-04-08 15:57:34 -04:00
parent 47ec08ec15
commit 40d78066c6
4 changed files with 98 additions and 89 deletions

View File

@ -8,12 +8,11 @@ from logging import debug
class DebugWindow(QDialog): class DebugWindow(QDialog):
def __init__(self, song_filepath: str, text: str): def __init__(self, text: str):
super(DebugWindow, self).__init__() super(DebugWindow, self).__init__()
self.setWindowTitle("debug") self.setWindowTitle("debug")
self.setMinimumSize(400, 400) self.setMinimumSize(400, 400)
self.text: str = text self.text: str = text
self.song_filepath: str = song_filepath
layout = QVBoxLayout() layout = QVBoxLayout()
# Labels & input fields # Labels & input fields

View File

@ -124,7 +124,7 @@ class MusicTable(QTableView):
self.selected_song_filepath = "" self.selected_song_filepath = ""
self.current_song_filepath = "" self.current_song_filepath = ""
self.current_song_db_id = None self.current_song_db_id = None
self.current_song_qmodelindex = None self.current_song_qmodelindex: QModelIndex
# Properties # Properties
self.setAcceptDrops(True) self.setAcceptDrops(True)
@ -265,7 +265,7 @@ class MusicTable(QTableView):
if directories: if directories:
worker = Worker(self.get_audio_files_recursively, directories) worker = Worker(self.get_audio_files_recursively, directories)
worker.signals.signal_progress.connect(self.handle_progress) 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) worker.signals.signal_finished.connect(self.load_music_table)
if self.qapp: if self.qapp:
threadpool = self.qapp.threadpool threadpool = self.qapp.threadpool
@ -399,49 +399,29 @@ class MusicTable(QTableView):
update_song_in_database(song_id, edited_column_name, user_input_data) update_song_in_database(song_id, edited_column_name, user_input_data)
return 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): def handle_progress(self, data):
"""Emits data to main""" """Emits data to main"""
self.handleProgressSignal.emit(data) self.handleProgressSignal.emit(data)
# ____________________ def on_get_audio_files_recursively_finished(self, result):
# | | """file search completion handler"""
# | | if result:
# | Connection Mgmt | self.add_files_to_library(result)
# | |
# |____________________|
def disconnect_data_changed(self): def on_add_files_to_database_finished(self, *args):
"""Disconnects the dataChanged signal from QTableView.model""" """
try: Shows failed to import files and reasons
self.model2.dataChanged.disconnect() Runs after worker process signal_finished for add_files_to_database()
except Exception:
pass
def connect_data_changed(self): Args:
"""Connects the dataChanged signal from QTableView.model""" - args: ((return_data),)
try: - data returned from the original worker process function are returned here
self.model2.dataChanged.connect(self.on_cell_data_changed) as the first item in a tuple
except Exception: """
pass # TODO: make this prettier, show a table in a window instead of raw text probably
_, details = args[0][:2]
def disconnect_layout_changed(self): window = DebugWindow(details)
"""Disconnects the layoutChanged signal from QTableView.model""" window.exec_()
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
# ____________________ # ____________________
# | | # | |
@ -458,6 +438,7 @@ class MusicTable(QTableView):
""" """
worker = Worker(add_files_to_database, files) worker = Worker(add_files_to_database, files)
worker.signals.signal_progress.connect(self.qapp.handle_progress) 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) worker.signals.signal_finished.connect(self.load_music_table)
if self.qapp: if self.qapp:
threadpool = self.qapp.threadpool threadpool = self.qapp.threadpool
@ -539,12 +520,9 @@ class MusicTable(QTableView):
def show_id3_tags_debug_menu(self): def show_id3_tags_debug_menu(self):
"""Shows ID3 tags for a specific .mp3 file""" """Shows ID3 tags for a specific .mp3 file"""
selected_song_filepath = self.get_selected_song_filepath() if self.get_selected_song_filepath() is not None:
if selected_song_filepath is None: window = DebugWindow(str(self.get_selected_song_metadata()))
return window.exec_()
current_song = self.get_selected_song_metadata()
lyrics_window = DebugWindow(selected_song_filepath, str(current_song))
lyrics_window.exec_()
def show_lyrics_menu(self): def show_lyrics_menu(self):
"""Shows the lyrics for the currently selected song""" """Shows the lyrics for the currently selected song"""
@ -601,6 +579,8 @@ class MusicTable(QTableView):
debug("reorganizing files") debug("reorganizing files")
# FIXME: batch update, instead of doing 1 file at a time # FIXME: batch update, instead of doing 1 file at a time
# DBAccess is being instantiated for every file, boo # DBAccess is being instantiated for every file, boo
# NOTE:
# is that even possible with move file function?
# Get target directory # Get target directory
target_dir = str(self.config["directories"]["reorganize_destination"]) target_dir = str(self.config["directories"]["reorganize_destination"])
@ -618,10 +598,8 @@ class MusicTable(QTableView):
if progress_callback: if progress_callback:
progress_callback.emit(f"Organizing: {filepath}") progress_callback.emit(f"Organizing: {filepath}")
# Create the directories if they dont exist # Create the directories if they dont exist
debug("make dirs")
os.makedirs(os.path.dirname(new_path), exist_ok=True) os.makedirs(os.path.dirname(new_path), exist_ok=True)
# Move the file to the new directory # Move the file to the new directory
debug(f"{filepath} > {new_path}")
shutil.move(filepath, new_path) shutil.move(filepath, new_path)
# Update the db # Update the db
with DBA.DBAccess() as db: with DBA.DBAccess() as db:
@ -629,7 +607,7 @@ class MusicTable(QTableView):
"UPDATE song SET filepath = ? WHERE filepath = ?", "UPDATE song SET filepath = ? WHERE filepath = ?",
(new_path, filepath), (new_path, filepath),
) )
debug(f"reorganize_files() | Moved: {filepath} -> {new_path}") # debug(f"reorganize_files() | Moved: {filepath} -> {new_path}")
except Exception as e: except Exception as e:
error(f"reorganize_files() | Error moving file: {filepath} | {e}") error(f"reorganize_files() | Error moving file: {filepath} | {e}")
# Draw the rest of the owl # Draw the rest of the owl
@ -757,7 +735,7 @@ class MusicTable(QTableView):
def restore_scroll_position(self) -> None: def restore_scroll_position(self) -> None:
"""Restores the scroll position""" """Restores the scroll position"""
debug("restore_scroll_position") debug("restore_scroll_position (inactive)")
# QTimer.singleShot( # QTimer.singleShot(
# 100, # 100,
# lambda: self.verticalScrollBar().setValue(self.vertical_scroll_position), # 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""" """Necessary for using members and methods of main application window"""
self.qapp = qapp 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 # QT Roles

View File

@ -1,4 +1,5 @@
from PyQt5.QtWidgets import QMessageBox from PyQt5.QtWidgets import QMessageBox
from mutagen.id3 import ID3
import DBA import DBA
from logging import debug from logging import debug
from utils import get_id3_tags, convert_id3_timestamp_to_datetime 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" Path(user_config_dir(appname="musicpom", appauthor="billypom")) / "config.ini"
) )
config.read(cfg_file) config.read(cfg_file)
if not files: if not files:
return False return False, {'Failure': 'All operations failed in add_files_to_database()'}
extensions = config.get("settings", "extensions").split(",") extensions = config.get("settings", "extensions").split(",")
failed_dict = {}
insert_data = [] # To store data for batch insert insert_data = [] # To store data for batch insert
for filepath in files: for filepath in files:
if any(filepath.lower().endswith(ext) for ext in extensions): if any(filepath.lower().endswith(ext) for ext in extensions):
if progress_callback: if progress_callback:
progress_callback.emit(filepath) progress_callback.emit(filepath)
filename = filepath.split("/")[-1] filename = filepath.split("/")[-1]
audio = get_id3_tags(filepath)
if not audio: audio, details = get_id3_tags(filepath)
QMessageBox.error( if not isinstance(audio, ID3):
"Error", failed_dict[filepath] = details
f"Could not retrieve ID3 tags for {filepath}", continue
QMessageBox.Ok,
QMessageBox.Ok,
)
return
try: try:
title = audio["TIT2"].text[0] 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 OR IGNORE INTO song (filepath, title, album, artist, track_number, genre, codec, album_date, bitrate) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
insert_data, insert_data,
) )
return True return True, failed_dict
# id int unsigned auto_increment, # id int unsigned auto_increment,

View File

@ -5,38 +5,39 @@ from mutagen.id3._frames import TIT2
from mutagen.id3._util import ID3NoHeaderError 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""" """Get the ID3 tags for an audio file"""
# debug(filename) # debug(filename)
if filename.endswith(".flac"): if filename.endswith(".mp3"):
return try:
# Open the MP3 file and read its content
audio = ID3(filename)
except ID3NoHeaderError:
audio = ID3()
try: try:
# Open the MP3 file and read its content if os.path.exists(filename):
audio = ID3(filename) audio.save(os.path.abspath(filename))
except ID3NoHeaderError:
audio = ID3()
try: # NOTE: If 'TIT2' tag is not set, we add it with a default value
if os.path.exists(filename): # title = filename without extension
audio.save(os.path.abspath(filename))
# NOTE: If 'TIT2' tag is not set, we add it with a default value title = os.path.splitext(os.path.basename(filename))[0]
# title = filename without extension list_of_id3_tags = list(audio.keys())
if "TIT2" in list_of_id3_tags:
title = os.path.splitext(os.path.basename(filename))[0] audio.save()
list_of_id3_tags = list(audio.keys()) return audio, ""
if "TIT2" in list_of_id3_tags: else:
tit2_tag = TIT2(encoding=3, text=[title])
audio["TIT2"] = tit2_tag
# Save the updated tags
audio.save() audio.save()
return audio return audio, ""
else:
tit2_tag = TIT2(encoding=3, text=[title]) except Exception as e:
audio["TIT2"] = tit2_tag error(f"Could not assign file ID3 tag: {e}")
# Save the updated tags return None, f"Could not assign ID3 tag to file: {e}"
audio.save() return None, "non mp3 file"
except Exception as e:
error(f"Could not assign file ID3 tag: {e}")
return audio