multithreading working with add files to library
This commit is contained in:
parent
3bd2c539cf
commit
d351f3515e
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@
|
||||
/references
|
||||
/db
|
||||
config.ini
|
||||
log
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
|
||||
@ -31,6 +31,7 @@ from components.AddToPlaylistWindow import AddToPlaylistWindow
|
||||
from components.MetadataWindow import MetadataWindow
|
||||
|
||||
# from main import WorkerThread
|
||||
from main import Worker
|
||||
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.get_reorganize_vars import get_reorganize_vars
|
||||
@ -45,29 +46,6 @@ import os
|
||||
import shutil
|
||||
|
||||
|
||||
class DropAddFilesThread(QThread):
|
||||
signalStarted = pyqtSignal()
|
||||
signalProgress = pyqtSignal(str)
|
||||
signalFinished = pyqtSignal()
|
||||
|
||||
def __init__(self, files, parent=None):
|
||||
QThread.__init__(self, parent)
|
||||
self.files = files
|
||||
|
||||
def run(self) -> None:
|
||||
self.add_files()
|
||||
return
|
||||
|
||||
def add_files(self) -> None:
|
||||
"""When song(s) added to the library, update the tableview model
|
||||
- Drag & Drop song(s) on tableView
|
||||
- File > Open > List of song(s)
|
||||
"""
|
||||
add_files_to_library(self.files)
|
||||
self.signalFinished.emit()
|
||||
return
|
||||
|
||||
|
||||
class MusicTable(QTableView):
|
||||
playPauseSignal = pyqtSignal()
|
||||
enterKey = pyqtSignal()
|
||||
@ -191,7 +169,7 @@ class MusicTable(QTableView):
|
||||
try:
|
||||
model.removeRow(index)
|
||||
except Exception as e:
|
||||
logging.info(f"MusicTable.py delete_songs() failed | {e}")
|
||||
logging.info(f" delete_songs() failed | {e}")
|
||||
self.load_music_table()
|
||||
self.model.dataChanged.connect(self.on_cell_data_changed)
|
||||
|
||||
@ -237,7 +215,7 @@ class MusicTable(QTableView):
|
||||
else:
|
||||
raise RuntimeError("No USLT tags found in song metadata")
|
||||
except Exception as e:
|
||||
print(f"MusicTable.py | show_lyrics_menu | could not retrieve lyrics | {e}")
|
||||
logging.error(f"show_lyrics_menu() | could not retrieve lyrics | {e}")
|
||||
lyrics = ""
|
||||
lyrics_window = LyricsWindow(selected_song_filepath, lyrics)
|
||||
lyrics_window.exec_()
|
||||
@ -261,6 +239,7 @@ class MusicTable(QTableView):
|
||||
e.ignore()
|
||||
|
||||
def dropEvent(self, e: QDropEvent | None):
|
||||
self.model.dataChanged.disconnect(self.on_cell_data_changed)
|
||||
if e is None:
|
||||
return
|
||||
data = e.mimeData()
|
||||
@ -270,14 +249,13 @@ class MusicTable(QTableView):
|
||||
if url.isLocalFile():
|
||||
files.append(url.path())
|
||||
e.accept()
|
||||
self.worker = DropAddFilesThread(files=files)
|
||||
# self.model.dataChanged.disconnect(self.on_cell_data_changed)
|
||||
# self.model.dataChanged.connect(self.on_cell_data_changed)
|
||||
self.worker.signalFinished.connect(self.load_music_table)
|
||||
self.worker.start()
|
||||
# self.add_files(files)
|
||||
worker = Worker(add_files_to_library, files)
|
||||
worker.signals.signal_progress.connect(self.handle_progress)
|
||||
worker.signals.signal_finished.connect(self.load_music_table)
|
||||
self.threadpool.start(worker)
|
||||
else:
|
||||
e.ignore()
|
||||
self.model.dataChanged.connect(self.on_cell_data_changed)
|
||||
|
||||
def keyPressEvent(self, e):
|
||||
"""Press a key. Do a thing"""
|
||||
@ -315,7 +293,7 @@ class MusicTable(QTableView):
|
||||
|
||||
def on_cell_data_changed(self, topLeft: QModelIndex, bottomRight: QModelIndex):
|
||||
"""Handles updating ID3 tags when data changes in a cell"""
|
||||
print("on_cell_data_changed")
|
||||
logging.info("on_cell_data_changed")
|
||||
id_index = self.model.index(topLeft.row(), 0) # ID is column 0, always
|
||||
song_id = self.model.data(id_index, Qt.UserRole)
|
||||
# filepath is always the last column
|
||||
@ -326,7 +304,7 @@ class MusicTable(QTableView):
|
||||
# update the ID3 information
|
||||
user_input_data = topLeft.data()
|
||||
edited_column_name = self.database_columns[topLeft.column()]
|
||||
print(f"edited column name: {edited_column_name}")
|
||||
logging.info(f"edited column name: {edited_column_name}")
|
||||
response = set_id3_tag(filepath, edited_column_name, user_input_data)
|
||||
if response:
|
||||
# Update the library with new metadata
|
||||
@ -366,13 +344,12 @@ class MusicTable(QTableView):
|
||||
"UPDATE song SET filepath = ? WHERE filepath = ?",
|
||||
(new_path, filepath),
|
||||
)
|
||||
print(f"Moved: {filepath} -> {new_path}")
|
||||
logging.info(
|
||||
f"reorganize_selected_files() | Moved: {filepath} -> {new_path}"
|
||||
)
|
||||
except Exception as e:
|
||||
logging.warning(
|
||||
f"MusicTable.py reorganize_selected_files() | Error moving file: {filepath} | {e}"
|
||||
)
|
||||
print(
|
||||
f"MusicTable.py reorganize_selected_files() | Error moving file: {filepath} | {e}"
|
||||
f"reorganize_selected_files() | Error moving file: {filepath} | {e}"
|
||||
)
|
||||
# Draw the rest of the owl
|
||||
# self.model.dataChanged.disconnect(self.on_cell_data_changed)
|
||||
@ -388,6 +365,14 @@ class MusicTable(QTableView):
|
||||
self.set_current_song_filepath()
|
||||
self.playPauseSignal.emit()
|
||||
|
||||
def add_files(self, files, progress_callback) -> None:
|
||||
"""When song(s) added to the library, update the tableview model
|
||||
- Drag & Drop song(s) on tableView
|
||||
- File > Open > List of song(s)
|
||||
"""
|
||||
add_files_to_library(files, progress_callback)
|
||||
return
|
||||
|
||||
def load_music_table(self, *playlist_id):
|
||||
"""
|
||||
Loads data into self (QTableView)
|
||||
@ -401,8 +386,8 @@ class MusicTable(QTableView):
|
||||
# then re-enable after we are done loading
|
||||
self.model.dataChanged.disconnect(self.on_cell_data_changed)
|
||||
except Exception as e:
|
||||
print(
|
||||
f"MusicTable.py load_music_table() | could not disconnect on_cell_data_changed trigger: {e}"
|
||||
logging.info(
|
||||
f"load_music_table() | could not disconnect on_cell_data_changed trigger: {e}"
|
||||
)
|
||||
pass
|
||||
self.vertical_scroll_position = (
|
||||
@ -413,7 +398,9 @@ class MusicTable(QTableView):
|
||||
self.model.setHorizontalHeaderLabels(self.table_headers)
|
||||
if playlist_id:
|
||||
selected_playlist_id = playlist_id[0]
|
||||
print(f"selected_playlist_id: {selected_playlist_id}")
|
||||
logging.info(
|
||||
f"load_music_table() | selected_playlist_id: {selected_playlist_id}"
|
||||
)
|
||||
# Fetch playlist data
|
||||
try:
|
||||
with DBA.DBAccess() as db:
|
||||
@ -422,10 +409,7 @@ class MusicTable(QTableView):
|
||||
(selected_playlist_id,),
|
||||
)
|
||||
except Exception as e:
|
||||
logging.warning(
|
||||
f"MusicTable.py | load_music_table | Unhandled exception: {e}"
|
||||
)
|
||||
print(f"MusicTable.py | load_music_table | Unhandled exception: {e}")
|
||||
logging.warning(f"load_music_table() | Unhandled exception: {e}")
|
||||
return
|
||||
else:
|
||||
# Fetch library data
|
||||
@ -436,9 +420,7 @@ class MusicTable(QTableView):
|
||||
(),
|
||||
)
|
||||
except Exception as e:
|
||||
logging.warning(
|
||||
f"MusicTable.py | load_music_table | Unhandled exception: {e}"
|
||||
)
|
||||
logging.warning(f"load_music_table() | Unhandled exception: {e}")
|
||||
return
|
||||
# Populate the model
|
||||
for row_data in data:
|
||||
@ -458,7 +440,7 @@ class MusicTable(QTableView):
|
||||
|
||||
def restore_scroll_position(self) -> None:
|
||||
"""Restores the scroll position"""
|
||||
print("restore_scroll_position")
|
||||
logging.info("restore_scroll_position")
|
||||
QTimer.singleShot(
|
||||
100,
|
||||
lambda: self.verticalScrollBar().setValue(self.vertical_scroll_position),
|
||||
@ -520,7 +502,7 @@ class MusicTable(QTableView):
|
||||
self.selected_song_filepath = (
|
||||
self.currentIndex().siblingAtColumn(self.table_headers.index("path")).data()
|
||||
)
|
||||
print(self.selected_song_filepath)
|
||||
logging.info(self.selected_song_filepath)
|
||||
|
||||
def set_current_song_filepath(self) -> None:
|
||||
"""Sets the filepath of the currently playing song"""
|
||||
|
||||
140
main.py
140
main.py
@ -9,6 +9,7 @@ from pyqtgraph import mkBrush
|
||||
from mutagen.id3 import ID3
|
||||
from mutagen.id3._frames import APIC
|
||||
from configparser import ConfigParser
|
||||
import traceback
|
||||
import DBA
|
||||
from ui import Ui_MainWindow
|
||||
from PyQt5.QtWidgets import (
|
||||
@ -28,6 +29,7 @@ from PyQt5.QtCore import (
|
||||
Qt,
|
||||
pyqtSignal,
|
||||
QObject,
|
||||
QThread,
|
||||
pyqtSlot,
|
||||
QThreadPool,
|
||||
QRunnable,
|
||||
@ -35,7 +37,12 @@ from PyQt5.QtCore import (
|
||||
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QAudioProbe
|
||||
from PyQt5.QtGui import QCloseEvent, QPixmap
|
||||
from uuid import uuid4, UUID
|
||||
from utils import scan_for_music, delete_and_create_library_database, initialize_db
|
||||
from utils import (
|
||||
scan_for_music,
|
||||
delete_and_create_library_database,
|
||||
initialize_db,
|
||||
add_files_to_library,
|
||||
)
|
||||
from components import (
|
||||
PreferencesWindow,
|
||||
AudioVisualizer,
|
||||
@ -49,6 +56,11 @@ from components import (
|
||||
|
||||
# good help with signals slots in threads
|
||||
# https://stackoverflow.com/questions/52993677/how-do-i-setup-signals-and-slots-in-pyqt-with-qthreads-in-both-directions
|
||||
|
||||
# GOOD
|
||||
# https://www.pythonguis.com/tutorials/multithreading-pyqt-applications-qthreadpool/
|
||||
|
||||
|
||||
class WorkerSignals(QObject):
|
||||
"""
|
||||
How to use signals for a QRunnable class; unlike most cases where signals
|
||||
@ -67,31 +79,52 @@ class WorkerSignals(QObject):
|
||||
# pandas DataFrames or numpy arrays. Instead, pass the minimum amount of information needed
|
||||
# (i.e. lists of filepaths)
|
||||
|
||||
signal_started = pyqtSignal(list)
|
||||
signal_finished = pyqtSignal(UUID)
|
||||
signal_started = pyqtSignal()
|
||||
signal_finished = pyqtSignal()
|
||||
signal_progress = pyqtSignal(str)
|
||||
|
||||
|
||||
class WorkerThread(QRunnable):
|
||||
class Worker(QRunnable):
|
||||
"""
|
||||
This is the thread that is going to do the work so that the
|
||||
application doesn't freeze
|
||||
|
||||
Inherits from QRunnable to handle worker thread setup, signals, and tear down
|
||||
:param callback: the function callback to run on this worker thread. Supplied
|
||||
arg and kwargs will be passed through to the runner
|
||||
:type callback: function
|
||||
:param args: Arguments to pass to the callback function
|
||||
:param kwargs: Keywords to pass to the callback function
|
||||
"""
|
||||
|
||||
def __init__(self, worker_id: UUID) -> None:
|
||||
super().__init__()
|
||||
# allow for signals to be used
|
||||
self.worker_id = worker_id
|
||||
def __init__(self, fn, *args, **kwargs):
|
||||
super(Worker, self).__init__()
|
||||
# Store constructor arguments (re-used for processing)
|
||||
self.fn = fn
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
self.signals: WorkerSignals = WorkerSignals()
|
||||
|
||||
# Add a callback to our kwargs
|
||||
self.kwargs["progress_callback"] = self.signals.signal_progress
|
||||
|
||||
@pyqtSlot()
|
||||
def run(self):
|
||||
"""
|
||||
This is where the work is done.
|
||||
MUST be called run() in order for QRunnable to work
|
||||
|
||||
Initialize the runner function with passed args & kwargs
|
||||
"""
|
||||
self.signals.signal_started.emit("start worker")
|
||||
self.signals.signal_progress.emit("worker progress")
|
||||
self.signals.signal_finished.emit(self.worker_id)
|
||||
self.signals.signal_started.emit()
|
||||
try:
|
||||
self.fn(*self.args, **self.kwargs)
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
exctype, value = sys.exc_info()[:2]
|
||||
self.signals.signal_finished.emit((exctype, value, traceback.format_exc()))
|
||||
finally:
|
||||
self.signals.signal_finished.emit()
|
||||
|
||||
|
||||
class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
@ -122,7 +155,6 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
self.audio_visualizer: AudioVisualizer = AudioVisualizer(self.player)
|
||||
self.current_volume: int = 50
|
||||
self.qapp = qapp
|
||||
# print(f'ApplicationWindow self.qapp: {self.qapp}')
|
||||
self.tableView.load_qapp(self.qapp)
|
||||
self.albumGraphicsView.load_qapp(self.qapp)
|
||||
self.config.read("config.ini")
|
||||
@ -154,7 +186,6 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
self.playbackSlider.sliderReleased.connect(
|
||||
lambda: self.player.setPosition(self.playbackSlider.value())
|
||||
) # maybe sliderReleased works better than sliderMoved
|
||||
self
|
||||
self.volumeSlider.sliderMoved[int].connect(
|
||||
lambda: self.volume_changed()
|
||||
) # Move slider to adjust volume
|
||||
@ -213,45 +244,6 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
||||
self.tableView.horizontalHeader().setStretchLastSection(False)
|
||||
|
||||
def start_worker(self):
|
||||
worker_id = uuid4() # generate an unique id for the worker
|
||||
worker = WorkerThread(worker_id=worker_id)
|
||||
|
||||
# Connect the signals to the slots
|
||||
worker.signals.signal_started.connect(self.handle_started)
|
||||
worker.signals.signal_progress.connect(self.handle_progress)
|
||||
worker.signals.signal_finished.connect(self.handle_finished)
|
||||
|
||||
# Store the worker in a dict so we can keep track of it
|
||||
self.workers[worker_id] = worker
|
||||
|
||||
# Start the worker
|
||||
self.threadpool.start(worker)
|
||||
|
||||
# Slots should have decorators whose arguments match the signals they are connected to
|
||||
@pyqtSlot(list)
|
||||
def handle_files_from_child_window(self, files: list[str]):
|
||||
self.files_to_process = files
|
||||
print(f"Files to process: {self.files_to_process}")
|
||||
self.show_status_bar_message(f"Files to process: {self.files_to_process}")
|
||||
|
||||
@pyqtSlot(int)
|
||||
def handle_progress(self, n):
|
||||
print(f"progress: {n}%")
|
||||
self.show_status_bar_message(f"progress: {n}%")
|
||||
|
||||
@pyqtSlot(UUID)
|
||||
def handle_finished(self, worker_id):
|
||||
print(f"worker {worker_id} finished")
|
||||
self.show_status_bar_message(f"worker {worker_id} finished")
|
||||
# remove the worker from the dict
|
||||
self.workers.pop(worker_id)
|
||||
assert worker_id not in self.workers
|
||||
|
||||
@pyqtSlot(list)
|
||||
def handle_started(self, files: list[str]):
|
||||
print(f"Started processing files: {files}")
|
||||
|
||||
def closeEvent(self, a0: QCloseEvent | None) -> None:
|
||||
"""Save settings when closing the application"""
|
||||
# MusicTable/tableView column widths
|
||||
@ -355,7 +347,9 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
"""Sets the ID3 tag APIC (album art) for all selected song filepaths"""
|
||||
selected_songs = self.tableView.get_selected_songs_filepaths()
|
||||
for song in selected_songs:
|
||||
print(f"updating album art for {song}")
|
||||
logging.info(
|
||||
f"main.py set_album_art_for_selected_songs() | updating album art for {song}"
|
||||
)
|
||||
self.update_album_art_for_song(song, album_art_path)
|
||||
|
||||
def update_album_art_for_song(
|
||||
@ -401,7 +395,9 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
del audio["APIC"]
|
||||
audio.save()
|
||||
except Exception as e:
|
||||
print(f"Error processing {file}: {e}")
|
||||
logging.error(
|
||||
f"main.py delete_album_art_for_selected_songs() | Error processing {file}: {e}"
|
||||
)
|
||||
|
||||
def update_audio_visualization(self) -> None:
|
||||
"""Handles upading points on the pyqtgraph visual"""
|
||||
@ -445,7 +441,7 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
self.current_volume = self.volumeSlider.value()
|
||||
self.player.setVolume(self.current_volume)
|
||||
except Exception as e:
|
||||
print(f"Changing volume error: {e}")
|
||||
logging.error(f"main.py volume_changed() | Changing volume error: {e}")
|
||||
|
||||
def on_play_clicked(self) -> None:
|
||||
"""Updates the Play & Pause buttons when clicked"""
|
||||
@ -462,17 +458,20 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
|
||||
def on_previous_clicked(self) -> None:
|
||||
""""""
|
||||
print("previous")
|
||||
logging.info("main.py on_previous_clicked()")
|
||||
|
||||
def on_next_clicked(self) -> None:
|
||||
print("next")
|
||||
logging.info("main.py on_next_clicked()")
|
||||
|
||||
def add_latest_playlist_to_tree(self) -> None:
|
||||
"""Refreshes the playlist tree"""
|
||||
self.playlistTreeView.add_latest_playlist_to_tree()
|
||||
|
||||
def open_files(self) -> None:
|
||||
"""Opens the open files window"""
|
||||
"""
|
||||
Opens the open files window
|
||||
- File > Open > List of song(s)
|
||||
"""
|
||||
open_files_window = QFileDialog(
|
||||
self, "Open file(s)", ".", "Audio files (*.mp3)"
|
||||
)
|
||||
@ -480,7 +479,17 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
open_files_window.setFileMode(QFileDialog.ExistingFiles)
|
||||
open_files_window.exec_()
|
||||
filenames = open_files_window.selectedFiles()
|
||||
self.tableView.add_files(filenames)
|
||||
# Adds files to the library in a new thread
|
||||
worker = Worker(add_files_to_library, filenames)
|
||||
worker.signals.signal_progress.connect(self.handle_progress)
|
||||
worker.signals.signal_finished.connect(self.tableView.load_music_table)
|
||||
self.threadpool.start(worker)
|
||||
|
||||
def handle_progress(self, data):
|
||||
"""
|
||||
updates the status bar when progress is emitted
|
||||
"""
|
||||
self.show_status_bar_message(data)
|
||||
|
||||
# File
|
||||
|
||||
@ -584,16 +593,23 @@ if __name__ == "__main__":
|
||||
with open("utils/init.sql", "r") as file:
|
||||
lines = file.read()
|
||||
for statement in lines.split(";"):
|
||||
print(f"executing [{statement}]")
|
||||
logging.info(f"executing [{statement}]")
|
||||
db.execute(statement, ())
|
||||
# logging setup
|
||||
logging.basicConfig(filename="musicpom.log", encoding="utf-8", level=logging.DEBUG)
|
||||
file_handler = logging.FileHandler(filename="log", encoding="utf-8")
|
||||
stdout_handler = logging.StreamHandler(stream=sys.stdout)
|
||||
handlers = [file_handler, stdout_handler]
|
||||
# logging.basicConfig(filename="log", encoding="utf-8", level=logging.DEBUG)
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG,
|
||||
format="[%(asctime)s] {%(filename)s:%(lineno)d} %(levelname)s - %(message)s",
|
||||
handlers=handlers,
|
||||
)
|
||||
# Allow for dynamic imports of my custom classes and utilities
|
||||
project_root = os.path.abspath(os.path.dirname(__file__))
|
||||
sys.path.append(project_root)
|
||||
# Start the app
|
||||
app = QApplication(sys.argv)
|
||||
# print(f"main.py app: {app}")
|
||||
# Dark theme >:3
|
||||
qdarktheme.setup_theme()
|
||||
# Show the UI
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import sys
|
||||
from mutagen.id3 import ID3, APIC
|
||||
import os
|
||||
import logging
|
||||
|
||||
|
||||
def print_id3_tags(file_path):
|
||||
|
||||
@ -8,19 +8,20 @@ config = ConfigParser()
|
||||
config.read("config.ini")
|
||||
|
||||
|
||||
def add_files_to_library(files):
|
||||
def add_files_to_library(files, progress_callback):
|
||||
"""Adds audio file(s) to the sqllite db
|
||||
files = list() of fully qualified paths to audio file(s)
|
||||
Returns a list of dictionaries of metadata
|
||||
"""
|
||||
|
||||
# print("Running add_files_to_library.py")
|
||||
logging.info("started function")
|
||||
if not files:
|
||||
return []
|
||||
extensions = config.get("settings", "extensions").split(",")
|
||||
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)
|
||||
|
||||
|
||||
@ -1,40 +1,11 @@
|
||||
# from mutagen.id3 import ID3
|
||||
# from mutagen.id3._frames import TIT2
|
||||
# import os
|
||||
#
|
||||
#
|
||||
# def get_id3_tags(filename):
|
||||
# """Get the ID3 tags for an audio file
|
||||
#
|
||||
# # Parameters
|
||||
# `file` | str | Fully qualified path to file
|
||||
#
|
||||
# # Returns
|
||||
# dict of all id3 tags
|
||||
# at minimum we will get the filename as a title.[ID3:TIT2]
|
||||
# """
|
||||
#
|
||||
# # Check if all tags are empty
|
||||
# # tags_are_empty = all(not values for values in audio.values())
|
||||
# audio = ID3()
|
||||
# try:
|
||||
# if not audio["TIT2"] or audio["TIT2"].text[0] == "":
|
||||
# title = os.path.splitext(os.path.basename(filename))[0]
|
||||
# frame = TIT2(encoding=3, text=[title])
|
||||
# audio["TIT2"] = frame
|
||||
# audio.save() # type: ignore
|
||||
# except Exception as e:
|
||||
# print(f"get_id3_tags.py | Could not assign file ID3 tag: {e}")
|
||||
# return audio
|
||||
|
||||
|
||||
import os
|
||||
import logging
|
||||
from mutagen.id3 import ID3, TIT2, ID3NoHeaderError
|
||||
|
||||
|
||||
def get_id3_tags(filename):
|
||||
"""Get the ID3 tags for an audio file"""
|
||||
print(f"get id3 tags filename: {filename}")
|
||||
logging.info(f"filename: {filename}")
|
||||
|
||||
try:
|
||||
# Open the MP3 file and read its content
|
||||
@ -59,6 +30,6 @@ def get_id3_tags(filename):
|
||||
audio.save()
|
||||
|
||||
except Exception as e:
|
||||
print(f"get_id3_tags.py | Could not assign file ID3 tag: {e}")
|
||||
logging.error(f"Could not assign file ID3 tag: {e}")
|
||||
|
||||
return audio
|
||||
|
||||
@ -92,9 +92,7 @@ def set_id3_tag(filepath: str, tag_name: str, value: str):
|
||||
|
||||
Returns:
|
||||
True / False"""
|
||||
print(
|
||||
f"set_id3_tag.py | filepath: {filepath} | tag_name: {tag_name} | value: {value}"
|
||||
)
|
||||
logging.info(f"filepath: {filepath} | tag_name: {tag_name} | value: {value}")
|
||||
|
||||
try:
|
||||
try: # Load existing tags
|
||||
@ -114,9 +112,7 @@ def set_id3_tag(filepath: str, tag_name: str, value: str):
|
||||
try:
|
||||
audio = ID3(filepath)
|
||||
except Exception as e:
|
||||
logging.debug(
|
||||
f"set_id3_tag.py set_id3_tag() ran into an exception: {e}"
|
||||
)
|
||||
logging.error(f"ran into an exception: {e}")
|
||||
audio = ID3()
|
||||
audio.delall("USLT")
|
||||
frame = USLT(encoding=3, text=value)
|
||||
@ -128,7 +124,7 @@ def set_id3_tag(filepath: str, tag_name: str, value: str):
|
||||
tag_name = id3_tag_mapping[tag_name]
|
||||
# Other
|
||||
if tag_name in mutagen_id3_tag_mapping: # Tag accounted for
|
||||
print(f"set_id3_tag.py | tag_name = {tag_name}")
|
||||
logging.info(f"tag_name = {tag_name}")
|
||||
tag_class = mutagen_id3_tag_mapping[tag_name]
|
||||
if issubclass(tag_class, Frame):
|
||||
frame = tag_class(encoding=3, text=[value])
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user