batch delete files from table and db

This commit is contained in:
tsi-billypom 2024-09-16 16:36:19 -04:00
parent 00d1c01e9f
commit 705407f3a3
4 changed files with 84 additions and 30 deletions

View File

@ -33,6 +33,9 @@ from components.AddToPlaylistWindow import AddToPlaylistWindow
from components.MetadataWindow import MetadataWindow
from main import Worker
from utils.batch_delete_filepaths_from_database import (
batch_delete_filepaths_from_database,
)
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
@ -163,32 +166,31 @@ class MusicTable(QTableView):
QMessageBox.Yes,
)
if reply:
try:
self.model.dataChanged.disconnect(self.on_cell_data_changed)
except Exception:
pass
selected_filepaths = self.get_selected_songs_filepaths()
selected_indices = self.get_selected_rows()
# FIXME: this should be batch delete with a worker thread
# probably pass selected_filepaths to a worker thread
worker = Worker(batch_delete_filepaths_from_database, selected_filepaths)
worker.signals.signal_progress.connect(self.qapp.handle_progress)
worker.signals.signal_finished.connect(self.remove_selected_row_indices)
worker.signals.signal_finished.connect(self.load_music_table)
if self.qapp:
threadpool = self.qapp.threadpool
threadpool.start(worker)
for file in selected_filepaths:
with DBA.DBAccess() as db:
song_id = db.query(
"SELECT id FROM song WHERE filepath = ?", (file,)
)[0][0]
delete_song_id_from_database(song_id)
# This part cannot be thread...i think? bcus its Q stuff happening
for index in selected_indices:
try:
self.model.removeRow(index)
except Exception as e:
logging.info(f" delete_songs() failed | {e}")
def remove_selected_row_indices(self):
"""Removes rows from the QTableView based on a list of indices"""
selected_indices = self.get_selected_rows()
try:
self.model.dataChanged.disconnect(self.on_cell_data_changed)
except Exception:
pass
for index in selected_indices:
try:
self.model.dataChanged.connect(self.on_cell_data_changed)
except Exception:
pass
self.load_music_table()
self.model.removeRow(index)
except Exception as e:
logging.info(f" delete_songs() failed | {e}")
try:
self.model.dataChanged.connect(self.on_cell_data_changed)
except Exception:
pass
def open_directory(self):
"""Opens the currently selected song in the system file manager"""
@ -358,7 +360,7 @@ class MusicTable(QTableView):
worker.signals.signal_progress.connect(self.qapp.handle_progress)
self.qapp.threadpool.start(worker)
def reorganize_selected_files(self, progress_callback):
def reorganize_selected_files(self, progress_callback=None):
"""Ctrl+Shift+R = Reorganize"""
filepaths = self.get_selected_songs_filepaths()
# Confirmation screen (yes, no)
@ -376,7 +378,8 @@ class MusicTable(QTableView):
if str(filepath).startswith((target_dir)):
continue
try:
progress_callback.emit(filepath)
if progress_callback:
progress_callback.emit(f"Organizing: {filepath}")
# Read file metadata
artist, album = get_reorganize_vars(filepath)
# Determine the new path that needs to be made

10
main.py
View File

@ -61,11 +61,11 @@ from components import (
class WorkerSignals(QObject):
"""
How to use signals for a QRunnable class; unlike most cases where signals
are defined as class attributes directly in the class, here we define a
class that inherits from QObject and define the signals as class
attributes in that class. Then we can instantiate that class and use it
as a signal object.
How to use signals for a QRunnable class;
Unlike most cases where signals are defined as class attributes directly in the class,
here we define a class that inherits from QObject
and define the signals as class attributes in that class.
Then we can instantiate that class and use it as a signal object.
"""
# 1)

View File

@ -7,6 +7,7 @@ from .get_id3_tags import get_id3_tags
from .get_reorganize_vars import get_reorganize_vars
from .set_id3_tag import set_id3_tag
from .delete_song_id_from_database import delete_song_id_from_database
from .batch_delete_filepaths_from_database import batch_delete_filepaths_from_database
from .delete_and_create_library_database import delete_and_create_library_database
from .update_song_in_database import update_song_in_database
from .scan_for_music import scan_for_music

View File

@ -0,0 +1,50 @@
import DBA
from components.ErrorDialog import ErrorDialog
def batch_delete_filepaths_from_database(
files: list[str], chunk_size=1000, progress_callback=None
) -> bool:
"""
Handles deleting many songs from the database by filepath
Accounts for playlists and other song-linked tables
Returns True on success
False on failure/error
"""
# Get song IDs from filepaths
try:
with DBA.DBAccess() as db:
placeholders = ", ".join("?" for _ in files)
query = f"SELECT id FROM song WHERE filepath in ({placeholders});"
result = db.query(query, files)
song_ids = [item[0] for item in result]
except Exception as e:
dialog = ErrorDialog(
f"batch_delete_filepaths_from_database.py | An error occurred during retrieval of song_ids: {e}"
)
dialog.exec_()
return False
try:
with DBA.DBAccess() as db:
# Batch delete in chunks
for i in range(0, len(song_ids), chunk_size):
chunk = song_ids[i : i + chunk_size]
placeholders = ", ".join("?" for _ in chunk)
# Delete from playlists
query = f"DELETE FROM song_playlist WHERE song_id IN ({placeholders});"
db.execute(query, chunk)
# Delete from library
query = f"DELETE FROM song WHERE id in ({placeholders});"
db.execute(query, chunk)
if progress_callback:
progress_callback.emit(f"Deleting songs: {i}")
except Exception as e:
dialog = ErrorDialog(
f"batch_delete_filepaths_from_database.py | An error occurred during batch processing: {e}"
)
dialog.exec_()
return False
return True