diff --git a/components/HeaderTags.py b/components/HeaderTags.py
index 891313b..c08898a 100644
--- a/components/HeaderTags.py
+++ b/components/HeaderTags.py
@@ -4,15 +4,31 @@ from appdirs import user_config_dir
from dataclasses import dataclass, asdict
from typing import Optional
+
@dataclass
-class SQLiteMap():
- title: str
- artist: str
+class SQLiteMap:
+ title: Optional[str] = None
+ artist: Optional[str] = None
album: Optional[str] = None
+ album_artist: Optional[str] = None
+ track_number: Optional[str] = None
+ genre: Optional[str] = None
+ length_seconds: Optional[str] = None
+ album_date: Optional[str] = None
+ codec: Optional[str] = None
+ filepath: Optional[str] = None
+"""
+db names are called FIELDS (e.g., title, track_number, length_seconds)
+gui names are called HEADERS (e.g., title, track, length, year)
+id3 names are called TAGS (e.g., TIT2, TPE1, TALB)
-class HeaderTags():
+is dataclasses rly worth it?
+"""
+
+
+class HeaderTags:
"""
Utility class to converting between different "standards" for tags (headers, id3, etc)
@@ -20,8 +36,8 @@ class HeaderTags():
`gui`: dict = "db name": "gui string"
`id3`: dict = "db name": "id3 tag string"
`id3_keys`: dict = "id3 tag string": "db name"
- `editable_db_tags`: list = "list of db names that are user editable"
- `user_headers`: list = "list of db headers that the user has chosen to see in gui"
+ `editable_fields`: list = "list of db names that are user editable"
+ `user_fields`: list = "list of db headers that the user has chosen to see in gui"
"""
def __init__(self):
@@ -31,7 +47,43 @@ class HeaderTags():
)
self.config = ConfigParser()
self.config.read(cfg_file)
- self.user_headers: list = str(self.config["table"]["columns"]).split(",")
+ print("header tag config")
+ print(self.config)
+ self.user_fields: list = str(self.config["table"]["columns"]).split(",")
+ self.editable_fields: list = [
+ "title",
+ "artist",
+ "album_artist",
+ "album",
+ "track_number",
+ "genre",
+ "album_date",
+ ]
+ self.fields = SQLiteMap()
+ self.headers = SQLiteMap(
+ title="title",
+ artist="artist",
+ album="album",
+ album_artist="alb artist",
+ track_number="track",
+ genre="genre",
+ codec="codec",
+ length_seconds="length",
+ album_date="year",
+ filepath="path",
+ )
+ # self.id3 = SQLiteMap(
+ # title = "TIT2",
+ # artist = "TPE1",
+ # album = "TALB",
+ # album_artist = "TPE2",
+ # track_number = "TRCK",
+ # genre = "TCON",
+ # length_seconds = "TLEN",
+ # album_date = "TDRC",
+ # codec = None,
+ # filepath = None
+ # )
self.db: dict = {
"title": "title",
"artist": "artist",
@@ -74,20 +126,10 @@ class HeaderTags():
if v is not None:
self.id3_keys[v] = k
- self.editable_db_tags: list = [
- "title",
- "artist",
- "album_artist",
- "album",
- "track_number",
- "genre",
- "album_date",
- ]
-
def get_user_gui_headers(self) -> list:
"""Returns a list of headers for the GUI"""
gui_headers = []
- for db, gui in self.gui.items():
- if db in self.user_headers:
+ for db, gui in asdict(self.headers).items():
+ if db in self.user_fields:
gui_headers.append(gui)
return gui_headers
diff --git a/components/MetadataWindow.py b/components/MetadataWindow.py
index e5af1a5..09a5569 100644
--- a/components/MetadataWindow.py
+++ b/components/MetadataWindow.py
@@ -56,9 +56,10 @@ class MetadataWindow(QDialog):
layout.addWidget(category_label)
layout.addWidget(h_separator)
- tag_sets: dict = {}
+ tag_sets: dict[str, list] = {}
# Get a dict of all tags for all songs
# e.g., { "TIT2": ["song_title1", "song_title2"], ... }
+ # e.g., { "title": ["song_title1", "song_title2"], ... }
for song in self.songs:
song_data = get_tags(song[0])[0]
if not song_data:
@@ -71,7 +72,7 @@ class MetadataWindow(QDialog):
)
return
for key, tag in self.headers.id3.items():
- if key not in self.headers.editable_db_tags:
+ if key not in self.headers.editable_fields:
continue
if tag is not None:
try:
diff --git a/components/MusicTable.py b/components/MusicTable.py
index 9df428c..10a0a58 100644
--- a/components/MusicTable.py
+++ b/components/MusicTable.py
@@ -1,7 +1,5 @@
from mutagen.id3 import ID3
-from json import load as jsonload
import DBA
-from pprint import pprint
from PyQt5.QtGui import (
QColor,
QDragMoveEvent,
@@ -18,12 +16,10 @@ from PyQt5.QtWidgets import (
QAction,
QHeaderView,
QMenu,
- QPlainTextEdit,
QTableView,
QShortcut,
QMessageBox,
QAbstractItemView,
- QVBoxLayout,
)
from PyQt5.QtCore import (
QItemSelectionModel,
@@ -32,7 +28,6 @@ from PyQt5.QtCore import (
QModelIndex,
QThreadPool,
pyqtSignal,
- QTimer,
)
from components.DebugWindow import DebugWindow
from components.ErrorDialog import ErrorDialog
@@ -104,6 +99,8 @@ class MusicTable(QTableView):
)
self.config = ConfigParser()
self.config.read(cfg_file)
+ print("music table config:")
+ print(self.config)
# Threads
self.threadpool = QThreadPool
@@ -127,8 +124,6 @@ class MusicTable(QTableView):
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
self.setSelectionBehavior(QAbstractItemView.SelectRows)
# header
- # FIXME: table headers being resized and going out window bounds
- # causing some recursion errors...
self.horizontal_header: QHeaderView = self.horizontalHeader()
assert self.horizontal_header is not None # i hate look at linting errors
self.horizontal_header.setStretchLastSection(True)
@@ -342,7 +337,7 @@ class MusicTable(QTableView):
def on_sort(self):
debug("on_sort")
- search_col_num = self.headers.user_headers.index("filepath")
+ search_col_num = self.headers.user_fields.index("filepath")
selected_qmodel_index = self.find_qmodel_index_by_value(
self.proxymodel, search_col_num, self.selected_song_filepath
)
@@ -397,18 +392,14 @@ class MusicTable(QTableView):
if isinstance(self.model2, QStandardItemModel):
debug("on_cell_data_changed")
# get the ID of the row that was edited
- id_index = self.model2.index(topLeft.row(), 0) # ID is column 0, always
+ id_index = self.model2.index(topLeft.row(), 0)
# get the db song_id from the row
song_id = self.model2.data(id_index, Qt.ItemDataRole.UserRole)
- # get the filepath through a series of steps...
- # NOTE: filepath is always the last column
- filepath_column_idx = self.model2.columnCount() - 1
- filepath_index = self.model2.index(topLeft.row(), filepath_column_idx)
- filepath = self.model2.data(filepath_index)
+ user_index = self.headers.user_fields.index("filepath")
+ filepath = self.currentIndex().siblingAtColumn(user_index).data()
# update the ID3 information
user_input_data = topLeft.data()
- # edited_column_name = self.database_columns[topLeft.column()]
- edited_column_name = self.headers.user_headers[topLeft.column()]
+ edited_column_name = self.headers.user_fields[topLeft.column()]
debug(f"on_cell_data_changed | edited column name: {edited_column_name}")
response = set_tag(filepath, edited_column_name, user_input_data)
if response:
@@ -677,7 +668,7 @@ class MusicTable(QTableView):
self.vertical_scroll_position = self.verticalScrollBar().value() # type: ignore
self.model2.clear()
self.model2.setHorizontalHeaderLabels(self.headers.get_user_gui_headers())
- fields = ", ".join(self.headers.user_headers)
+ fields = ", ".join(self.headers.user_fields)
if playlist_id: # Load a playlist
# Fetch playlist data
selected_playlist_id = playlist_id[0]
@@ -730,13 +721,15 @@ class MusicTable(QTableView):
# reloading the model destroys and makes new indexes
# so we look for the new index of the current song on load
current_song_filepath = self.get_current_song_filepath()
- print(f'load music table current filepath: {current_song_filepath}')
+ print(f"load music table current filepath: {current_song_filepath}")
for row in range(self.model2.rowCount()):
- real_index = self.model2.index(row, self.headers.user_headers.index("filepath"))
+ real_index = self.model2.index(
+ row, self.headers.user_fields.index("filepath")
+ )
if real_index.data() == current_song_filepath:
- print('is it true?')
- print(f'{real_index.data()} == {current_song_filepath}')
- print('load music table real index:')
+ print("is it true?")
+ print(f"{real_index.data()} == {current_song_filepath}")
+ print("load music table real index:")
print(real_index)
self.current_song_qmodel_index = real_index
self.model2.layoutChanged.emit() # emits a signal that the view should be updated
@@ -833,9 +826,7 @@ class MusicTable(QTableView):
selected_rows = self.get_selected_rows()
filepaths = []
for row in selected_rows:
- idx = self.proxymodel.index(
- row, self.headers.user_headers.index("filepath")
- )
+ idx = self.proxymodel.index(row, self.headers.user_fields.index("filepath"))
filepaths.append(idx.data())
return filepaths
@@ -850,7 +841,9 @@ class MusicTable(QTableView):
return []
selected_rows = set(index.row() for index in indexes)
id_list = [
- self.proxymodel.data(self.proxymodel.index(row, 0), Qt.ItemDataRole.UserRole)
+ self.proxymodel.data(
+ self.proxymodel.index(row, 0), Qt.ItemDataRole.UserRole
+ )
for row in selected_rows
]
return id_list
@@ -870,14 +863,18 @@ class MusicTable(QTableView):
def set_selected_song_filepath(self) -> None:
"""Sets the filepath of the currently selected song"""
try:
- table_index = self.headers.user_headers.index("filepath")
- filepath = self.currentIndex().siblingAtColumn(table_index).data()
+ user_index = self.headers.user_fields.index("filepath")
+ filepath = self.currentIndex().siblingAtColumn(user_index).data()
except ValueError:
# if the user doesnt have filepath selected as a header, retrieve the file from db
row = self.currentIndex().row()
- id = self.proxymodel.data(self.proxymodel.index(row, 0), Qt.ItemDataRole.UserRole)
+ id = self.proxymodel.data(
+ 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:
@@ -887,7 +884,9 @@ class MusicTable(QTableView):
"""
# update the filepath
if not filepath:
- path = self.current_song_qmodel_index.siblingAtColumn(self.headers.user_headers.index("filepath")).data()
+ path = self.current_song_qmodel_index.siblingAtColumn(
+ self.headers.user_fields.index("filepath")
+ ).data()
self.current_song_filepath: str = path
else:
self.current_song_filepath = filepath
diff --git a/main.py b/main.py
index 973622d..d92e006 100644
--- a/main.py
+++ b/main.py
@@ -41,7 +41,13 @@ from PyQt5.QtCore import (
QThreadPool,
QRunnable,
)
-from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QAudioProbe, QMediaPlaylist, QMultimedia
+from PyQt5.QtMultimedia import (
+ QMediaPlayer,
+ QMediaContent,
+ QAudioProbe,
+ QMediaPlaylist,
+ QMultimedia,
+)
from PyQt5.QtGui import QClipboard, QCloseEvent, QFont, QPixmap, QResizeEvent
from utils import (
delete_album_art,
@@ -50,7 +56,7 @@ from utils import (
initialize_db,
add_files_to_database,
set_album_art,
- id3_remap
+ id3_remap,
)
from components import (
HeaderTags,
@@ -155,6 +161,8 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
/ "config.ini"
)
self.config.read(self.cfg_file)
+ print("main config:")
+ print(self.config)
self.threadpool: QThreadPool = QThreadPool()
# UI
self.setupUi(self)
@@ -328,7 +336,9 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
file_url = media.canonicalUrl().toLocalFile()
metadata = id3_remap(get_tags(file_url)[0])
if metadata is not None:
- self.set_ui_metadata(metadata["title"], metadata["artist"], metadata["album"], file_url)
+ self.set_ui_metadata(
+ metadata["title"], metadata["artist"], metadata["album"], file_url
+ )
def on_volume_changed(self) -> None:
"""Handles volume changes"""
@@ -374,8 +384,12 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
return
row: int = index.row()
prev_row: int = row - 1
- prev_index: QModelIndex = self.tableView.proxymodel.index(prev_row, index.column())
- prev_filepath = prev_index.siblingAtColumn(self.headers.user_headers.index("filepath")).data()
+ prev_index: QModelIndex = self.tableView.proxymodel.index(
+ prev_row, index.column()
+ )
+ prev_filepath = prev_index.siblingAtColumn(
+ self.headers.user_fields.index("filepath")
+ ).data()
if prev_filepath is None:
return
@@ -394,8 +408,12 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
return
row: int = index.row()
next_row: int = row + 1
- next_index: QModelIndex = self.tableView.proxymodel.index(next_row, index.column())
- next_filepath = next_index.siblingAtColumn(self.headers.user_headers.index("filepath")).data()
+ next_index: QModelIndex = self.tableView.proxymodel.index(
+ next_row, index.column()
+ )
+ next_filepath = next_index.siblingAtColumn(
+ self.headers.user_fields.index("filepath")
+ ).data()
if next_filepath is None:
return
self.play_audio_file(next_filepath)
@@ -471,7 +489,7 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
filepath default value = `tableView.current_song_filepath`
"""
- print('play audio file')
+ print("play audio file")
if not filepath:
filepath = self.tableView.get_selected_song_filepath()
metadata = id3_remap(get_tags(filepath)[0])
@@ -487,7 +505,9 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
# assign "now playing" labels & album artwork
if metadata is not None:
- self.set_ui_metadata(metadata["title"], metadata["artist"], metadata["album"], filepath)
+ self.set_ui_metadata(
+ metadata["title"], metadata["artist"], metadata["album"], filepath
+ )
def set_ui_metadata(self, title, artist, album, filepath):
"""
diff --git a/ui.ui b/ui.ui
index 625aec1..3a82b2a 100644
--- a/ui.ui
+++ b/ui.ui
@@ -6,8 +6,8 @@
0
0
- 1152
- 894
+ 953
+ 739
@@ -310,7 +310,7 @@
0
0
- 1152
+ 953
24
diff --git a/utils/set_tag.py b/utils/set_tag.py
index 32bdfe1..067f7b9 100644
--- a/utils/set_tag.py
+++ b/utils/set_tag.py
@@ -107,6 +107,7 @@ def set_tag(filepath: str, tag_name: str, value: str):
# if tdat_tag:
# # update TDAT if we have it
# audio_file.add(tdat_tag)
+
# Lyrics
if tag_name == "lyrics" or tag_name == "USLT":
try: