working on stuff and fixing nvim config testing stuff
This commit is contained in:
parent
d01a743c45
commit
dbfab01b53
@ -1,4 +1,3 @@
|
||||
from mutagen.id3 import ID3
|
||||
import DBA
|
||||
from PyQt5.QtGui import (
|
||||
QColor,
|
||||
@ -14,6 +13,7 @@ from PyQt5.QtGui import (
|
||||
)
|
||||
from PyQt5.QtWidgets import (
|
||||
QAction,
|
||||
QApplication,
|
||||
QHeaderView,
|
||||
QMenu,
|
||||
QTableView,
|
||||
@ -26,11 +26,9 @@ from PyQt5.QtCore import (
|
||||
QSortFilterProxyModel,
|
||||
Qt,
|
||||
QModelIndex,
|
||||
QThreadPool,
|
||||
pyqtSignal,
|
||||
)
|
||||
from components.DebugWindow import DebugWindow
|
||||
from components.ErrorDialog import ErrorDialog
|
||||
from components.LyricsWindow import LyricsWindow
|
||||
from components.AddToPlaylistWindow import AddToPlaylistWindow
|
||||
from components.MetadataWindow import MetadataWindow
|
||||
@ -40,11 +38,9 @@ from components.HeaderTags import HeaderTags
|
||||
from utils import (
|
||||
batch_delete_filepaths_from_database,
|
||||
batch_delete_filepaths_from_playlist,
|
||||
delete_song_id_from_database,
|
||||
add_files_to_database,
|
||||
get_reorganize_vars,
|
||||
update_song_in_database,
|
||||
get_album_art,
|
||||
id3_remap,
|
||||
get_tags,
|
||||
set_tag,
|
||||
@ -59,21 +55,20 @@ from pathlib import Path
|
||||
from appdirs import user_config_dir
|
||||
from configparser import ConfigParser
|
||||
|
||||
|
||||
class MusicTable(QTableView):
|
||||
playlistStatsSignal = pyqtSignal(str)
|
||||
loadMusicTableSignal = pyqtSignal()
|
||||
sortSignal = pyqtSignal()
|
||||
playPauseSignal = pyqtSignal()
|
||||
playSignal = pyqtSignal(str)
|
||||
enterKey = pyqtSignal()
|
||||
deleteKey = pyqtSignal()
|
||||
refreshMusicTableSignal = pyqtSignal()
|
||||
handleProgressSignal = pyqtSignal(str)
|
||||
getThreadPoolSignal = pyqtSignal()
|
||||
searchBoxSignal = pyqtSignal()
|
||||
focusEnterSignal = pyqtSignal()
|
||||
focusLeaveSignal = pyqtSignal()
|
||||
playlistStatsSignal: pyqtSignal = pyqtSignal(str)
|
||||
loadMusicTableSignal: pyqtSignal = pyqtSignal()
|
||||
sortSignal: pyqtSignal = pyqtSignal()
|
||||
playPauseSignal: pyqtSignal = pyqtSignal()
|
||||
playSignal: pyqtSignal = pyqtSignal(str)
|
||||
enterKey: pyqtSignal = pyqtSignal()
|
||||
deleteKey: pyqtSignal = pyqtSignal()
|
||||
refreshMusicTableSignal: pyqtSignal = pyqtSignal()
|
||||
handleProgressSignal: pyqtSignal = pyqtSignal(str)
|
||||
getThreadPoolSignal: pyqtSignal = pyqtSignal()
|
||||
searchBoxSignal: pyqtSignal = pyqtSignal()
|
||||
focusEnterSignal: pyqtSignal = pyqtSignal()
|
||||
focusLeaveSignal: pyqtSignal = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None, application_window=None):
|
||||
super().__init__(parent)
|
||||
@ -85,7 +80,7 @@ class MusicTable(QTableView):
|
||||
Path(user_config_dir(appname="musicpom", appauthor="billypom"))
|
||||
/ "config.ini"
|
||||
)
|
||||
self.config.read(cfg_file)
|
||||
_ = self.config.read(cfg_file)
|
||||
debug(f"music table config: {self.config}")
|
||||
|
||||
# NOTE:
|
||||
@ -96,13 +91,12 @@ class MusicTable(QTableView):
|
||||
# Create QSortFilterProxyModel
|
||||
# Set QSortFilterProxyModel source to QStandardItemModel
|
||||
# Set QTableView model to the Proxy model
|
||||
# so it looks like that, i guess
|
||||
# so it looks like the above note, i guess
|
||||
|
||||
# need a QStandardItemModel to do actions on cells
|
||||
self.model2: QStandardItemModel = QStandardItemModel()
|
||||
self.proxymodel = QSortFilterProxyModel()
|
||||
self.search_string = None
|
||||
self.threadpool = QThreadPool
|
||||
self.proxymodel: QSortFilterProxyModel = QSortFilterProxyModel()
|
||||
self.search_string: str | None = None
|
||||
self.headers = HeaderTags()
|
||||
# db names of headers
|
||||
self.database_columns: list[str] = str(
|
||||
@ -144,8 +138,7 @@ class MusicTable(QTableView):
|
||||
self.deleteKey.connect(self.delete_songs)
|
||||
self.doubleClicked.connect(self.play_selected_audio_file)
|
||||
self.enterKey.connect(self.play_selected_audio_file)
|
||||
self.model2.dataChanged.connect(
|
||||
self.on_cell_data_changed) # editing cells
|
||||
self.model2.dataChanged.connect(self.on_cell_data_changed) # editing cells
|
||||
# self.model2.layoutChanged.connect(self.restore_scroll_position)
|
||||
self.horizontal_header.sectionResized.connect(self.on_header_resized)
|
||||
# Final actions
|
||||
@ -202,35 +195,32 @@ class MusicTable(QTableView):
|
||||
"""Right-click context menu"""
|
||||
menu = QMenu(self)
|
||||
add_to_playlist_action = QAction("Add to playlist", self)
|
||||
add_to_playlist_action.triggered.connect(
|
||||
self.add_selected_files_to_playlist)
|
||||
_ = add_to_playlist_action.triggered.connect(self.add_selected_files_to_playlist)
|
||||
menu.addAction(add_to_playlist_action)
|
||||
# edit metadata
|
||||
edit_metadata_action = QAction("Edit metadata", self)
|
||||
edit_metadata_action.triggered.connect(
|
||||
self.edit_selected_files_metadata)
|
||||
_ = edit_metadata_action.triggered.connect(self.edit_selected_files_metadata)
|
||||
menu.addAction(edit_metadata_action)
|
||||
# edit lyrics
|
||||
edit_lyrics_action = QAction("Lyrics (View/Edit)", self)
|
||||
edit_lyrics_action.triggered.connect(self.show_lyrics_menu)
|
||||
_ = edit_lyrics_action.triggered.connect(self.show_lyrics_menu)
|
||||
menu.addAction(edit_lyrics_action)
|
||||
# jump to current song in table
|
||||
jump_to_current_song_action = QAction("Jump to current song", self)
|
||||
jump_to_current_song_action.triggered.connect(
|
||||
self.jump_to_current_song)
|
||||
_ = jump_to_current_song_action.triggered.connect(self.jump_to_current_song)
|
||||
menu.addAction(jump_to_current_song_action)
|
||||
# open in file explorer
|
||||
open_containing_folder_action = QAction(
|
||||
"Open in system file manager", self)
|
||||
open_containing_folder_action.triggered.connect(self.open_directory)
|
||||
_ = open_containing_folder_action.triggered.connect(self.open_directory)
|
||||
menu.addAction(open_containing_folder_action)
|
||||
# view id3 tags (debug)
|
||||
view_id3_tags_debug = QAction("View ID3 tags (debug)", self)
|
||||
view_id3_tags_debug.triggered.connect(self.view_id3_tags_debug_menu)
|
||||
_ = view_id3_tags_debug.triggered.connect(self.view_id3_tags_debug_menu)
|
||||
menu.addAction(view_id3_tags_debug)
|
||||
# delete song
|
||||
delete_action = QAction("Delete", self)
|
||||
delete_action.triggered.connect(self.delete_songs)
|
||||
_ = delete_action.triggered.connect(self.delete_songs)
|
||||
menu.addAction(delete_action)
|
||||
# show
|
||||
self.set_selected_song_filepath()
|
||||
@ -261,8 +251,8 @@ class MusicTable(QTableView):
|
||||
data = e.mimeData()
|
||||
debug("dropEvent")
|
||||
if data and data.hasUrls():
|
||||
directories = []
|
||||
files = []
|
||||
directories: list[str] = []
|
||||
files: list[str] = []
|
||||
for url in data.urls():
|
||||
if url.isLocalFile():
|
||||
path = url.toLocalFile()
|
||||
@ -275,11 +265,9 @@ class MusicTable(QTableView):
|
||||
e.accept()
|
||||
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_get_audio_files_recursively_finished
|
||||
)
|
||||
worker.signals.signal_finished.connect(self.load_music_table)
|
||||
_ = worker.signals.signal_progress.connect(self.handle_progress)
|
||||
_ = 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
|
||||
threadpool.start(worker)
|
||||
@ -301,9 +289,8 @@ class MusicTable(QTableView):
|
||||
index = self.currentIndex()
|
||||
new_index = self.model2.index(index.row(), index.column() + 1)
|
||||
if new_index.isValid():
|
||||
# print(f"right -> ({new_index.row()},{new_index.column()})")
|
||||
self.setCurrentIndex(new_index)
|
||||
self.viewport().update() # type: ignore
|
||||
self.viewport().update()
|
||||
super().keyPressEvent(e)
|
||||
return
|
||||
|
||||
@ -311,9 +298,8 @@ class MusicTable(QTableView):
|
||||
index = self.currentIndex()
|
||||
new_index = self.model2.index(index.row(), index.column() - 1)
|
||||
if new_index.isValid():
|
||||
# print(f"left -> ({new_index.row()},{new_index.column()})")
|
||||
self.setCurrentIndex(new_index)
|
||||
self.viewport().update() # type: ignore
|
||||
self.viewport().update()
|
||||
super().keyPressEvent(e)
|
||||
return
|
||||
|
||||
@ -321,9 +307,8 @@ class MusicTable(QTableView):
|
||||
index = self.currentIndex()
|
||||
new_index = self.model2.index(index.row() - 1, index.column())
|
||||
if new_index.isValid():
|
||||
# print(f"up -> ({new_index.row()},{new_index.column()})")
|
||||
self.setCurrentIndex(new_index)
|
||||
self.viewport().update() # type: ignore
|
||||
self.viewport().update()
|
||||
super().keyPressEvent(e)
|
||||
return
|
||||
|
||||
@ -331,15 +316,14 @@ class MusicTable(QTableView):
|
||||
index = self.currentIndex()
|
||||
new_index = self.model2.index(index.row() + 1, index.column())
|
||||
if new_index.isValid():
|
||||
# print(f"down -> ({new_index.row()},{new_index.column()})")
|
||||
self.setCurrentIndex(new_index)
|
||||
self.viewport().update() # type: ignore
|
||||
self.viewport().update()
|
||||
super().keyPressEvent(e)
|
||||
return
|
||||
|
||||
elif key in (Qt.Key.Key_Return, Qt.Key.Key_Enter):
|
||||
if self.state() != QAbstractItemView.EditingState:
|
||||
self.enterKey.emit() # Enter key detected
|
||||
self.enterKey.emit()
|
||||
else:
|
||||
super().keyPressEvent(e)
|
||||
else: # Default behavior
|
||||
@ -450,8 +434,10 @@ class MusicTable(QTableView):
|
||||
- data returned from the original worker process function are returned here
|
||||
as the first item in a tuple
|
||||
"""
|
||||
_, details = args[0][:2]
|
||||
print('hello?')
|
||||
print(args)
|
||||
try:
|
||||
_, details = args[0][:2]
|
||||
details = dict(tuple(details)[0])
|
||||
if details:
|
||||
window = DebugWindow(details)
|
||||
@ -484,14 +470,15 @@ class MusicTable(QTableView):
|
||||
def add_files_to_library(self, files: list[str]) -> None:
|
||||
"""
|
||||
Spawns a worker thread - adds a list of filepaths to the library
|
||||
|
||||
- Drag & Drop song(s) on tableView
|
||||
- File > Open > List of song(s)
|
||||
"""
|
||||
worker = Worker(add_files_to_database, files)
|
||||
debug('add_files_to_library()')
|
||||
worker = Worker(add_files_to_database, files, None)
|
||||
_ = 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_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
|
||||
threadpool.start(worker)
|
||||
@ -711,7 +698,7 @@ class MusicTable(QTableView):
|
||||
self.set_current_song_filepath()
|
||||
self.playPauseSignal.emit()
|
||||
|
||||
def load_music_table(self, *playlist_id):
|
||||
def load_music_table(self, *playlist_id: int):
|
||||
"""
|
||||
Loads data into self (QTableView)
|
||||
Loads all songs in library, by default
|
||||
@ -721,7 +708,7 @@ class MusicTable(QTableView):
|
||||
"""
|
||||
self.disconnect_data_changed()
|
||||
self.disconnect_layout_changed()
|
||||
self.vertical_scroll_position = self.verticalScrollBar().value() # type: ignore
|
||||
self.vertical_scroll_position = self.verticalScrollBar().value()
|
||||
self.model2.clear()
|
||||
self.model2.setHorizontalHeaderLabels(
|
||||
self.headers.get_user_gui_headers())
|
||||
@ -845,10 +832,8 @@ class MusicTable(QTableView):
|
||||
Sorts the data in QTableView (self) by multiple columns
|
||||
as defined in config.ini
|
||||
"""
|
||||
# TODO: Rewrite this function to use self.load_music_table() with dynamic SQL queries
|
||||
# in order to sort the data more effectively & have more control over UI refreshes.
|
||||
|
||||
# Disconnect these signals to prevent unnecessary loads
|
||||
# Disconnect these signals to prevent unnecessary reloads
|
||||
debug("sort_table_by_multiple_columns()")
|
||||
self.disconnect_data_changed()
|
||||
self.disconnect_layout_changed()
|
||||
@ -879,6 +864,8 @@ class MusicTable(QTableView):
|
||||
self.connect_data_changed()
|
||||
self.connect_layout_changed()
|
||||
# self.model2.layoutChanged.emit()
|
||||
# TODO: Rewrite this function to use self.load_music_table() with dynamic SQL queries
|
||||
# in order to sort the data more effectively & have more control over UI refreshes.
|
||||
|
||||
def restore_scroll_position(self) -> None:
|
||||
"""Restores the scroll position"""
|
||||
@ -888,10 +875,10 @@ class MusicTable(QTableView):
|
||||
# lambda: self.verticalScrollBar().setValue(self.vertical_scroll_position),
|
||||
# )
|
||||
|
||||
def get_audio_files_recursively(self, directories, progress_callback=None):
|
||||
def get_audio_files_recursively(self, directories: list[str], progress_callback=None) -> list[str]:
|
||||
"""Scans a directories for files"""
|
||||
extensions = self.config.get("settings", "extensions").split(",")
|
||||
audio_files = []
|
||||
audio_files: list[str] = []
|
||||
for directory in directories:
|
||||
for root, _, files in os.walk(directory):
|
||||
for file in files:
|
||||
@ -965,9 +952,7 @@ class MusicTable(QTableView):
|
||||
self.proxymodel.index(row, 0), Qt.ItemDataRole.UserRole
|
||||
)
|
||||
with DBA.DBAccess() as db:
|
||||
filepath = db.query("SELECT filepath FROM song WHERE id = ?", (id,))[0][
|
||||
0
|
||||
]
|
||||
filepath = db.query("SELECT filepath FROM song WHERE id = ?", (id,))[0][0]
|
||||
self.selected_song_filepath = filepath
|
||||
|
||||
def set_current_song_filepath(self, filepath=None) -> None:
|
||||
@ -984,7 +969,7 @@ class MusicTable(QTableView):
|
||||
else:
|
||||
self.current_song_filepath = filepath
|
||||
|
||||
def set_current_song_qmodel_index(self, index=None):
|
||||
def set_current_song_qmodel_index(self, index: QModelIndex | None = None):
|
||||
"""
|
||||
Takes in the proxy model index for current song - QModelIndex
|
||||
converts to model2 index
|
||||
@ -994,9 +979,9 @@ class MusicTable(QTableView):
|
||||
index = self.currentIndex()
|
||||
# map proxy (sortable) model to the original model (used for interactions)
|
||||
real_index: QModelIndex = self.proxymodel.mapToSource(index)
|
||||
self.current_song_qmodel_index: QModelIndex = real_index
|
||||
self.current_song_qmodel_index = real_index
|
||||
|
||||
def set_selected_song_qmodel_index(self, index=None):
|
||||
def set_selected_song_qmodel_index(self, index: QModelIndex | None = None):
|
||||
"""
|
||||
Takes in the proxy model index for current song - QModelIndex
|
||||
converts to model2 index
|
||||
@ -1006,15 +991,15 @@ class MusicTable(QTableView):
|
||||
index = self.currentIndex()
|
||||
# map proxy (sortable) model to the original model (used for interactions)
|
||||
real_index: QModelIndex = self.proxymodel.mapToSource(index)
|
||||
self.selected_song_qmodel_index: QModelIndex = real_index
|
||||
self.selected_song_qmodel_index = real_index
|
||||
|
||||
def set_search_string(self, text: str):
|
||||
"""set the search string"""
|
||||
self.search_string = text
|
||||
|
||||
def load_qapp(self, qapp) -> None:
|
||||
def load_qapp(self, qapp: QApplication) -> None:
|
||||
"""Necessary for using members and methods of main application window"""
|
||||
self.qapp = qapp
|
||||
self.qapp: QApplication = qapp
|
||||
|
||||
# ____________________
|
||||
# | |
|
||||
@ -1033,7 +1018,7 @@ class MusicTable(QTableView):
|
||||
def connect_data_changed(self):
|
||||
"""Connects the dataChanged signal from QTableView.model"""
|
||||
try:
|
||||
self.model2.dataChanged.connect(self.on_cell_data_changed)
|
||||
_ = self.model2.dataChanged.connect(self.on_cell_data_changed)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@ -1047,7 +1032,7 @@ class MusicTable(QTableView):
|
||||
def connect_layout_changed(self):
|
||||
"""Connects the layoutChanged signal from QTableView.model"""
|
||||
try:
|
||||
self.model2.layoutChanged.connect(self.restore_scroll_position)
|
||||
_ = self.model2.layoutChanged.connect(self.restore_scroll_position)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
52
main.py
52
main.py
@ -12,7 +12,7 @@ from mutagen.id3 import ID3
|
||||
from configparser import ConfigParser
|
||||
from pathlib import Path
|
||||
from appdirs import user_config_dir
|
||||
from logging import debug, error, warning, basicConfig, INFO, DEBUG
|
||||
from logging import debug, error, basicConfig, DEBUG
|
||||
from ui import Ui_MainWindow
|
||||
from PyQt5.QtWidgets import (
|
||||
QFileDialog,
|
||||
@ -21,34 +21,25 @@ from PyQt5.QtWidgets import (
|
||||
QMainWindow,
|
||||
QApplication,
|
||||
QGraphicsScene,
|
||||
QGraphicsPixmapItem,
|
||||
QMessageBox,
|
||||
QPushButton,
|
||||
QStatusBar,
|
||||
QStyle,
|
||||
QTableView,
|
||||
)
|
||||
from PyQt5.QtCore import (
|
||||
QModelIndex,
|
||||
QSize,
|
||||
QThread,
|
||||
QUrl,
|
||||
QTimer,
|
||||
Qt,
|
||||
pyqtSignal,
|
||||
QObject,
|
||||
pyqtSlot,
|
||||
QThreadPool,
|
||||
QRunnable,
|
||||
)
|
||||
from PyQt5.QtMultimedia import (
|
||||
QMediaPlayer,
|
||||
QMediaContent,
|
||||
QAudioProbe,
|
||||
QMediaPlaylist,
|
||||
QMultimedia,
|
||||
)
|
||||
from PyQt5.QtGui import QClipboard, QCloseEvent, QFont, QPixmap, QResizeEvent
|
||||
from PyQt5.QtGui import QCloseEvent, QFont, QResizeEvent
|
||||
from utils import (
|
||||
delete_album_art,
|
||||
get_tags,
|
||||
@ -68,7 +59,6 @@ from components import (
|
||||
AudioVisualizer,
|
||||
CreatePlaylistWindow,
|
||||
ExportPlaylistWindow,
|
||||
SearchLineEdit,
|
||||
)
|
||||
|
||||
# good help with signals slots in threads
|
||||
@ -79,14 +69,14 @@ from components import (
|
||||
|
||||
|
||||
class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
reloadConfigSignal = pyqtSignal()
|
||||
reloadDatabaseSignal = pyqtSignal()
|
||||
reloadConfigSignal: pyqtSignal = pyqtSignal()
|
||||
reloadDatabaseSignal: pyqtSignal = pyqtSignal()
|
||||
|
||||
def __init__(self, clipboard):
|
||||
super(ApplicationWindow, self).__init__()
|
||||
self.clipboard = clipboard
|
||||
self.config: ConfigParser = ConfigParser()
|
||||
self.cfg_file = (
|
||||
self.cfg_file: Path = (
|
||||
Path(user_config_dir(appname="musicpom", appauthor="billypom"))
|
||||
/ "config.ini"
|
||||
)
|
||||
@ -119,14 +109,15 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
self.audio_visualizer: AudioVisualizer = AudioVisualizer(
|
||||
self.player, self.probe, self.PlotWidget
|
||||
)
|
||||
self.timer = QTimer(self) # for playback slider and such
|
||||
self.timer: QTimer = QTimer(parent=self) # for playback slider and such
|
||||
|
||||
# Button styles
|
||||
style: QStyle | None
|
||||
if not self.style():
|
||||
style = QStyle()
|
||||
else:
|
||||
style = self.style()
|
||||
assert style is not None # i hate linting errors
|
||||
assert style is not None
|
||||
pixmapi = QStyle.StandardPixmap.SP_MediaSkipForward
|
||||
icon = style.standardIcon(pixmapi)
|
||||
self.nextButton.setIcon(icon)
|
||||
@ -380,7 +371,6 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
QtCore.Qt.TextInteractionFlag.TextSelectableByMouse
|
||||
)
|
||||
|
||||
font: QFont = QFont()
|
||||
font.setPointSize(12)
|
||||
font.setBold(False)
|
||||
self.titleLabel.setFont(font)
|
||||
@ -388,7 +378,6 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
QtCore.Qt.TextInteractionFlag.TextSelectableByMouse
|
||||
)
|
||||
|
||||
font: QFont = QFont()
|
||||
font.setPointSize(12)
|
||||
font.setItalic(True)
|
||||
self.albumLabel.setFont(font)
|
||||
@ -585,10 +574,8 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
preferences_window = PreferencesWindow(
|
||||
self.reloadConfigSignal, self.reloadDatabaseSignal
|
||||
)
|
||||
preferences_window.reloadConfigSignal.connect(
|
||||
self.load_config) # type: ignore
|
||||
preferences_window.reloadDatabaseSignal.connect(
|
||||
self.tableView.load_music_table) # type: ignore
|
||||
preferences_window.reloadConfigSignal.connect(self.load_config)
|
||||
preferences_window.reloadDatabaseSignal.connect(self.tableView.load_music_table)
|
||||
preferences_window.exec_() # Display the preferences window modally
|
||||
|
||||
# Quick Actions
|
||||
@ -606,11 +593,11 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
||||
def delete_database(self) -> None:
|
||||
"""Deletes the entire database"""
|
||||
reply = QMessageBox.question(
|
||||
self,
|
||||
"Confirmation",
|
||||
"Delete database?",
|
||||
QMessageBox.Yes | QMessageBox.No,
|
||||
QMessageBox.Yes,
|
||||
parent=self,
|
||||
title="Confirmation",
|
||||
text="Delete database?",
|
||||
buttons=QMessageBox.Yes | QMessageBox.No,
|
||||
defaultButton=QMessageBox.Yes,
|
||||
)
|
||||
if reply == QMessageBox.Yes:
|
||||
initialize_db()
|
||||
@ -629,7 +616,8 @@ def update_database_file() -> bool:
|
||||
appname="musicpom", appauthor="billypom")))
|
||||
config = ConfigParser()
|
||||
config.read(cfg_file)
|
||||
db_filepath: str = config.get("settings", "db")
|
||||
db_filepath: str
|
||||
db_filepath= config.get("settings", "db")
|
||||
|
||||
# If the database location isnt set at the config location, move it
|
||||
if not db_filepath.startswith(cfg_path):
|
||||
@ -643,7 +631,7 @@ def update_database_file() -> bool:
|
||||
with open(cfg_file, "w") as configfile:
|
||||
config.write(configfile)
|
||||
config.read(cfg_file)
|
||||
db_filepath: str = config.get("settings", "db")
|
||||
db_filepath = config.get("settings", "db")
|
||||
|
||||
db_path = db_filepath.split("/")
|
||||
db_path.pop()
|
||||
@ -722,8 +710,8 @@ if __name__ == "__main__":
|
||||
format="{%(filename)s:%(lineno)d} %(levelname)s - %(message)s",
|
||||
handlers=handlers,
|
||||
)
|
||||
debug(f'--------- musicpom debug started')
|
||||
debug(f'---------------------| ')
|
||||
debug('--------- musicpom debug started')
|
||||
debug('---------------------| ')
|
||||
debug(f'----------------------> {handlers} ')
|
||||
# Initialization
|
||||
config: ConfigParser = update_config_file()
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
from PyQt5.QtWidgets import QMessageBox
|
||||
from mutagen.id3 import ID3
|
||||
import DBA
|
||||
from logging import debug
|
||||
from utils import get_tags, convert_id3_timestamp_to_datetime, id3_remap
|
||||
@ -8,7 +6,7 @@ from pathlib import Path
|
||||
from appdirs import user_config_dir
|
||||
|
||||
|
||||
def add_files_to_database(files, progress_callback=None):
|
||||
def add_files_to_database(files: list[str], playlist_id: int | None = None, progress_callback=None) -> tuple[bool, dict[str, str]]:
|
||||
"""
|
||||
Adds audio file(s) to the sqllite db "song" table
|
||||
Args:
|
||||
@ -21,26 +19,40 @@ def add_files_to_database(files, progress_callback=None):
|
||||
(True, {"filename.mp3":"failed because i said so"})
|
||||
```
|
||||
"""
|
||||
# yea
|
||||
if playlist_id:
|
||||
pass
|
||||
|
||||
config = ConfigParser()
|
||||
cfg_file = (
|
||||
Path(user_config_dir(appname="musicpom", appauthor="billypom")) / "config.ini"
|
||||
)
|
||||
config.read(cfg_file)
|
||||
_ = config.read(cfg_file)
|
||||
if not files:
|
||||
return False, {"Failure": "All operations failed in add_files_to_database()"}
|
||||
failed_dict = {}
|
||||
insert_data = [] # To store data for batch insert
|
||||
failed_dict: dict[str, str] = {}
|
||||
insert_data: list[tuple[
|
||||
str,
|
||||
str | int | None,
|
||||
str | int | None,
|
||||
str | int | None,
|
||||
str | int | None,
|
||||
str | int | None,
|
||||
str,
|
||||
str | int | None,
|
||||
str | int | None,
|
||||
str | int | None]] = [] # To store data for batch insert
|
||||
for filepath in files:
|
||||
if progress_callback:
|
||||
progress_callback.emit(filepath)
|
||||
filename = filepath.split("/")[-1]
|
||||
|
||||
tags, details = get_tags(filepath)
|
||||
if details:
|
||||
failed_dict[filepath] = details
|
||||
tags, fail_reason = get_tags(filepath)
|
||||
if fail_reason:
|
||||
# if we fail to get audio tags, skip to next song
|
||||
failed_dict[filepath] = fail_reason
|
||||
continue
|
||||
audio = id3_remap(tags)
|
||||
|
||||
# remap tags from ID3 to database tags
|
||||
audio: dict[str, str | int | None] = id3_remap(tags)
|
||||
# Append data tuple to insert_data list
|
||||
insert_data.append(
|
||||
(
|
||||
@ -60,21 +72,22 @@ def add_files_to_database(files, progress_callback=None):
|
||||
if len(insert_data) >= 1000:
|
||||
debug(f"inserting a LOT of songs: {len(insert_data)}")
|
||||
with DBA.DBAccess() as db:
|
||||
db.executemany(
|
||||
"INSERT OR IGNORE INTO song (filepath, title, album, artist, track_number, genre, codec, album_date, bitrate, length_seconds) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
result = db.executemany(
|
||||
"INSERT OR IGNORE INTO song (filepath, title, album, artist, track_number, genre, codec, album_date, bitrate, length_seconds) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING id",
|
||||
insert_data,
|
||||
)
|
||||
debug('IMPORTANT')
|
||||
debug(f'batch insert result: {result}')
|
||||
debug('IMPORTANT')
|
||||
insert_data = [] # Reset the insert_data list
|
||||
else:
|
||||
# continue adding files if we havent reached big length
|
||||
continue
|
||||
# Insert any remaining data
|
||||
debug("i check for insert data")
|
||||
# Insert any remaining data after reading every file
|
||||
if insert_data:
|
||||
debug(f"inserting some songs: {len(insert_data)}")
|
||||
with DBA.DBAccess() as db:
|
||||
db.executemany(
|
||||
"INSERT OR IGNORE INTO song (filepath, title, album, artist, track_number, genre, codec, album_date, bitrate, length_seconds) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
result = db.executemany(
|
||||
"INSERT OR IGNORE INTO song (filepath, title, album, artist, track_number, genre, codec, album_date, bitrate, length_seconds) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING id",
|
||||
insert_data,
|
||||
)
|
||||
debug('IMPORTANT')
|
||||
debug(f'batch insert result: {result}')
|
||||
debug('IMPORTANT')
|
||||
return True, failed_dict
|
||||
|
||||
@ -34,7 +34,7 @@ def get_mp3_tags(filename: str) -> tuple[MP3 | ID3 | FLAC, str]:
|
||||
return MP3(), f"Could not assign ID3 tag to file: {e}"
|
||||
|
||||
|
||||
def id3_remap(audio: MP3 | ID3 | FLAC) -> dict:
|
||||
def id3_remap(audio: MP3 | ID3 | FLAC) -> dict[str, str | int | None]:
|
||||
"""
|
||||
Turns the ID3 dict of an audio file into a normal dict that I, the human, can use.
|
||||
Add extra fields too :D yahooo
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user