MetadataWindow update to DB and id3 tags complete

This commit is contained in:
tsi-billypom 2024-08-13 14:18:03 -04:00
parent 3f8d632de8
commit 63c54f9de7
7 changed files with 66 additions and 33 deletions

View File

@ -8,13 +8,19 @@ from PyQt5.QtWidgets import (
QPushButton,
)
from PyQt5.QtGui import QFont
from PyQt5.QtCore import pyqtSignal
from mutagen.id3 import ID3
from components.ErrorDialog import ErrorDialog
from utils.get_id3_tags import get_id3_tags
from utils.set_id3_tag import set_id3_tag
from utils.update_song_in_database import update_song_in_database
from utils.id3_tag_mapping import id3_tag_mapping
class MetadataWindow(QDialog):
def __init__(self, songs: list):
refreshMusicTableSignal = pyqtSignal()
def __init__(self, refreshMusicTableSignal, songs: list, ids: list):
"""
Window that allows batch editing of metadata for multiple files
@ -22,17 +28,9 @@ class MetadataWindow(QDialog):
- list of strings, absolute paths to mp3 files
"""
super(MetadataWindow, self).__init__()
self.songs = songs
self.id3_tag_mapping = {
"TIT2": "title",
"TPE1": "artist",
"TALB": "album",
"TPE2": "album_artist",
"TCON": "genre",
"TRCK": "track_number",
"APIC": "album_cover",
"TCOP": "copyright",
}
self.refreshMusicTableSignal = refreshMusicTableSignal
self.songs = list(zip(songs, ids))
self.id3_tag_mapping = id3_tag_mapping
# Keep a dictionary of the input fields for save function
self.input_fields = {}
self.setWindowTitle("Edit metadata")
@ -52,7 +50,7 @@ class MetadataWindow(QDialog):
# Get a dict of all tags for all songs
# e.g., { "TIT2": ["song_title1", "song_title2"], ... }
for song in self.songs:
song_data = get_id3_tags(song)
song_data = get_id3_tags(song[0])
for tag in self.id3_tag_mapping:
try:
_ = tag_sets[tag]
@ -107,5 +105,22 @@ class MetadataWindow(QDialog):
"""Save changes made to metadata for each song in dict"""
for song in self.songs:
for tag, field in self.input_fields.items():
set_id3_tag(filepath=song, tag_name=tag, value=field)
if field.text() is not None and field.text() != "":
# Update the ID3 tag if not blank
success = set_id3_tag(
filepath=song[0], tag_name=tag, value=field.text()
)
if success:
update_song_in_database(
song[1],
edited_column_name=self.id3_tag_mapping[tag],
user_input_data=field.text(),
)
else:
error_dialog = ErrorDialog(
f"Could not update metadata for {song}. Exiting early"
)
error_dialog.exec()
self.close()
self.refreshMusicTableSignal.emit()
self.close()

View File

@ -23,7 +23,7 @@ from components.AddToPlaylistWindow import AddToPlaylistWindow
from components.MetadataWindow import MetadataWindow
from utils.delete_song_id_from_database import delete_song_id_from_database
from utils.add_files_to_library import add_files_to_library
from utils.update_song_in_library import update_song_in_library
from utils.update_song_in_database import update_song_in_database
from utils.get_id3_tags import get_id3_tags
from utils.get_album_art import get_album_art
from utils import set_id3_tag
@ -38,12 +38,15 @@ class MusicTable(QTableView):
playPauseSignal = pyqtSignal()
enterKey = pyqtSignal()
deleteKey = pyqtSignal()
refreshMusicTable = pyqtSignal()
def __init__(self: QTableView, parent=None):
# QTableView.__init__(self, parent)
super().__init__(parent)
# Necessary for actions related to cell values
# FIXME: why do these give me pyright errors
self.model = QStandardItemModel(self)
# self.model = QAbstractItemModel(self)
self.setModel(self.model)
# Config
@ -165,7 +168,9 @@ class MusicTable(QTableView):
# FIXME:
"""Opens a form with metadata from the selected audio files"""
files = self.get_selected_songs_filepaths()
window = MetadataWindow(files)
song_ids = self.get_selected_songs_db_ids()
window = MetadataWindow(self.refreshMusicTable, files, song_ids)
window.refreshMusicTableSignal.connect(self.load_music_table)
window.exec_() # Display the preferences window modally
def add_selected_files_to_playlist(self):
@ -231,9 +236,11 @@ class MusicTable(QTableView):
else:
e.ignore()
def keyPressEvent(self, event):
def keyPressEvent(self, e):
"""Press a key. Do a thing"""
key = event.key()
if not e:
return
key = e.key()
if key == Qt.Key_Space: # Spacebar to play/pause
self.toggle_play_pause()
elif key == Qt.Key_Up: # Arrow key navigation
@ -254,9 +261,9 @@ class MusicTable(QTableView):
if self.state() != QAbstractItemView.EditingState:
self.enterKey.emit() # Enter key detected
else:
super().keyPressEvent(event)
super().keyPressEvent(e)
else: # Default behavior
super().keyPressEvent(event)
super().keyPressEvent(e)
def setup_keyboard_shortcuts(self):
"""Setup shortcuts here"""
@ -267,7 +274,7 @@ class MusicTable(QTableView):
"""Handles updating ID3 tags when data changes in a cell"""
print("on_cell_data_changed")
id_index = self.model.index(topLeft.row(), 0) # ID is column 0, always
library_id = self.model.data(id_index, Qt.UserRole)
song_id = self.model.data(id_index, Qt.UserRole)
# filepath is always the last column
filepath_column_idx = self.model.columnCount() - 1
filepath_index = self.model.index(topLeft.row(), filepath_column_idx)
@ -280,7 +287,7 @@ class MusicTable(QTableView):
response = set_id3_tag(filepath, edited_column_name, user_input_data)
if response:
# Update the library with new metadata
update_song_in_library(library_id, edited_column_name, user_input_data)
update_song_in_database(song_id, edited_column_name, user_input_data)
def reorganize_selected_files(self):
"""Ctrl+Shift+R = Reorganize"""
@ -411,6 +418,7 @@ class MusicTable(QTableView):
self.model.layoutChanged.emit() # emits a signal that the view should be updated
try:
self.model.dataChanged.connect(self.on_cell_data_changed)
self.restore_scroll_position()
except Exception:
pass

View File

@ -440,7 +440,7 @@ if __name__ == "__main__":
sys.path.append(project_root)
# Start the app
app = QApplication(sys.argv)
print(f"main.py app: {app}")
# print(f"main.py app: {app}")
# Dark theme >:3
qdarktheme.setup_theme()
# Show the UI

View File

@ -7,7 +7,7 @@ from .get_id3_tags import get_id3_tags
from .set_id3_tag import set_id3_tag
from .delete_song_id_from_database import delete_song_id_from_database
from .delete_and_create_library_database import delete_and_create_library_database
from .update_song_in_library import update_song_in_library
from .update_song_in_database import update_song_in_database
from .scan_for_music import scan_for_music
from .add_files_to_library import add_files_to_library
from .handle_year_and_date_id3_tag import handle_year_and_date_id3_tag

10
utils/id3_tag_mapping.py Normal file
View File

@ -0,0 +1,10 @@
id3_tag_mapping = {
"TIT2": "title",
"TPE1": "artist",
"TALB": "album",
"TPE2": "album_artist",
"TCON": "genre",
"TRCK": "track_number",
# "APIC": "album_cover",
"TCOP": "copyright",
}

View File

@ -40,7 +40,7 @@ from mutagen.id3._frames import (
WPUB,
)
id3_tag_mapping = {
mutagen_id3_tag_mapping = {
"title": TIT2, # Title/song name/content description
"artist": TPE1, # Lead performer(s)/Soloist(s)
"album": TALB, # Album/Movie/Show title
@ -119,8 +119,8 @@ def set_id3_tag(filepath: str, tag_name: str, value: str):
audio.save()
return True
# Other
elif tag_name in id3_tag_mapping: # Tag accounted for
tag_class = id3_tag_mapping[tag_name]
elif tag_name in mutagen_id3_tag_mapping: # Tag accounted for
tag_class = mutagen_id3_tag_mapping[tag_name]
if issubclass(tag_class, Frame):
frame = tag_class(encoding=3, text=[value])
audio_file.add(frame) # Add the tag

View File

@ -2,13 +2,13 @@ import DBA
from components.ErrorDialog import ErrorDialog
def update_song_in_library(
library_id: int, edited_column_name: str, user_input_data: str
def update_song_in_database(
song_id: int, edited_column_name: str, user_input_data: str
):
"""Updates a field in the library database based on an ID
"""Updates a field in the database based on an ID
Args:
library_id: the database ID of the song
song_id: the database ID of the song
edited_column_name: the name of the database column
user_input_data: the data to input
@ -19,11 +19,11 @@ def update_song_in_library(
# yeah yeah this is bad... the column names are defined in the program by me so im ok with it because it works
db.execute(
f"UPDATE song SET {edited_column_name} = ? WHERE id = ?",
(user_input_data, library_id),
(user_input_data, song_id),
)
except Exception as e:
dialog = ErrorDialog(
f"Unable to update [{edited_column_name}] to [{user_input_data}]. ID: {library_id} | {e}"
f"Unable to update [{edited_column_name}] to [{user_input_data}]. ID: {song_id} | {e}"
)
dialog.exec_()
return False