Compare commits
2 Commits
4b440378a5
...
3d16882185
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d16882185 | ||
|
|
18d248386c |
@ -4,15 +4,31 @@ from appdirs import user_config_dir
|
|||||||
from dataclasses import dataclass, asdict
|
from dataclasses import dataclass, asdict
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class SQLiteMap():
|
class SQLiteMap:
|
||||||
title: str
|
title: Optional[str] = None
|
||||||
artist: str
|
artist: Optional[str] = None
|
||||||
album: 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)
|
Utility class to converting between different "standards" for tags (headers, id3, etc)
|
||||||
|
|
||||||
@ -20,8 +36,8 @@ class HeaderTags():
|
|||||||
`gui`: dict = "db name": "gui string"
|
`gui`: dict = "db name": "gui string"
|
||||||
`id3`: dict = "db name": "id3 tag string"
|
`id3`: dict = "db name": "id3 tag string"
|
||||||
`id3_keys`: dict = "id3 tag string": "db name"
|
`id3_keys`: dict = "id3 tag string": "db name"
|
||||||
`editable_db_tags`: list = "list of db names that are user editable"
|
`editable_fields`: 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"
|
`user_fields`: list = "list of db headers that the user has chosen to see in gui"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@ -31,7 +47,43 @@ class HeaderTags():
|
|||||||
)
|
)
|
||||||
self.config = ConfigParser()
|
self.config = ConfigParser()
|
||||||
self.config.read(cfg_file)
|
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 = {
|
self.db: dict = {
|
||||||
"title": "title",
|
"title": "title",
|
||||||
"artist": "artist",
|
"artist": "artist",
|
||||||
@ -74,20 +126,10 @@ class HeaderTags():
|
|||||||
if v is not None:
|
if v is not None:
|
||||||
self.id3_keys[v] = k
|
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:
|
def get_user_gui_headers(self) -> list:
|
||||||
"""Returns a list of headers for the GUI"""
|
"""Returns a list of headers for the GUI"""
|
||||||
gui_headers = []
|
gui_headers = []
|
||||||
for db, gui in self.gui.items():
|
for db, gui in asdict(self.headers).items():
|
||||||
if db in self.user_headers:
|
if db in self.user_fields:
|
||||||
gui_headers.append(gui)
|
gui_headers.append(gui)
|
||||||
return gui_headers
|
return gui_headers
|
||||||
|
|||||||
@ -56,9 +56,10 @@ class MetadataWindow(QDialog):
|
|||||||
layout.addWidget(category_label)
|
layout.addWidget(category_label)
|
||||||
layout.addWidget(h_separator)
|
layout.addWidget(h_separator)
|
||||||
|
|
||||||
tag_sets: dict = {}
|
tag_sets: dict[str, list] = {}
|
||||||
# Get a dict of all tags for all songs
|
# Get a dict of all tags for all songs
|
||||||
# e.g., { "TIT2": ["song_title1", "song_title2"], ... }
|
# e.g., { "TIT2": ["song_title1", "song_title2"], ... }
|
||||||
|
# e.g., { "title": ["song_title1", "song_title2"], ... }
|
||||||
for song in self.songs:
|
for song in self.songs:
|
||||||
song_data = get_tags(song[0])[0]
|
song_data = get_tags(song[0])[0]
|
||||||
if not song_data:
|
if not song_data:
|
||||||
@ -71,7 +72,7 @@ class MetadataWindow(QDialog):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
for key, tag in self.headers.id3.items():
|
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
|
continue
|
||||||
if tag is not None:
|
if tag is not None:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
from mutagen.id3 import ID3
|
from mutagen.id3 import ID3
|
||||||
from json import load as jsonload
|
|
||||||
import DBA
|
import DBA
|
||||||
from pprint import pprint
|
|
||||||
from PyQt5.QtGui import (
|
from PyQt5.QtGui import (
|
||||||
QColor,
|
QColor,
|
||||||
QDragMoveEvent,
|
QDragMoveEvent,
|
||||||
@ -18,12 +16,10 @@ from PyQt5.QtWidgets import (
|
|||||||
QAction,
|
QAction,
|
||||||
QHeaderView,
|
QHeaderView,
|
||||||
QMenu,
|
QMenu,
|
||||||
QPlainTextEdit,
|
|
||||||
QTableView,
|
QTableView,
|
||||||
QShortcut,
|
QShortcut,
|
||||||
QMessageBox,
|
QMessageBox,
|
||||||
QAbstractItemView,
|
QAbstractItemView,
|
||||||
QVBoxLayout,
|
|
||||||
)
|
)
|
||||||
from PyQt5.QtCore import (
|
from PyQt5.QtCore import (
|
||||||
QItemSelectionModel,
|
QItemSelectionModel,
|
||||||
@ -32,7 +28,6 @@ from PyQt5.QtCore import (
|
|||||||
QModelIndex,
|
QModelIndex,
|
||||||
QThreadPool,
|
QThreadPool,
|
||||||
pyqtSignal,
|
pyqtSignal,
|
||||||
QTimer,
|
|
||||||
)
|
)
|
||||||
from components.DebugWindow import DebugWindow
|
from components.DebugWindow import DebugWindow
|
||||||
from components.ErrorDialog import ErrorDialog
|
from components.ErrorDialog import ErrorDialog
|
||||||
@ -106,6 +101,8 @@ class MusicTable(QTableView):
|
|||||||
)
|
)
|
||||||
self.config = ConfigParser()
|
self.config = ConfigParser()
|
||||||
self.config.read(cfg_file)
|
self.config.read(cfg_file)
|
||||||
|
print("music table config:")
|
||||||
|
print(self.config)
|
||||||
|
|
||||||
# Threads
|
# Threads
|
||||||
self.threadpool = QThreadPool
|
self.threadpool = QThreadPool
|
||||||
@ -129,8 +126,6 @@ class MusicTable(QTableView):
|
|||||||
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
||||||
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||||
# header
|
# header
|
||||||
# FIXME: table headers being resized and going out window bounds
|
|
||||||
# causing some recursion errors...
|
|
||||||
self.horizontal_header: QHeaderView = self.horizontalHeader()
|
self.horizontal_header: QHeaderView = self.horizontalHeader()
|
||||||
assert self.horizontal_header is not None # i hate look at linting errors
|
assert self.horizontal_header is not None # i hate look at linting errors
|
||||||
self.horizontal_header.setStretchLastSection(True)
|
self.horizontal_header.setStretchLastSection(True)
|
||||||
@ -344,7 +339,7 @@ class MusicTable(QTableView):
|
|||||||
|
|
||||||
def on_sort(self):
|
def on_sort(self):
|
||||||
debug("on_sort")
|
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(
|
selected_qmodel_index = self.find_qmodel_index_by_value(
|
||||||
self.proxymodel, search_col_num, self.selected_song_filepath
|
self.proxymodel, search_col_num, self.selected_song_filepath
|
||||||
)
|
)
|
||||||
@ -399,18 +394,14 @@ class MusicTable(QTableView):
|
|||||||
if isinstance(self.model2, QStandardItemModel):
|
if isinstance(self.model2, QStandardItemModel):
|
||||||
debug("on_cell_data_changed")
|
debug("on_cell_data_changed")
|
||||||
# get the ID of the row that was edited
|
# 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
|
# get the db song_id from the row
|
||||||
song_id = self.model2.data(id_index, Qt.ItemDataRole.UserRole)
|
song_id = self.model2.data(id_index, Qt.ItemDataRole.UserRole)
|
||||||
# get the filepath through a series of steps...
|
user_index = self.headers.user_fields.index("filepath")
|
||||||
# NOTE: filepath is always the last column
|
filepath = self.currentIndex().siblingAtColumn(user_index).data()
|
||||||
filepath_column_idx = self.model2.columnCount() - 1
|
|
||||||
filepath_index = self.model2.index(topLeft.row(), filepath_column_idx)
|
|
||||||
filepath = self.model2.data(filepath_index)
|
|
||||||
# update the ID3 information
|
# update the ID3 information
|
||||||
user_input_data = topLeft.data()
|
user_input_data = topLeft.data()
|
||||||
# edited_column_name = self.database_columns[topLeft.column()]
|
edited_column_name = self.headers.user_fields[topLeft.column()]
|
||||||
edited_column_name = self.headers.user_headers[topLeft.column()]
|
|
||||||
debug(f"on_cell_data_changed | edited column name: {edited_column_name}")
|
debug(f"on_cell_data_changed | edited column name: {edited_column_name}")
|
||||||
response = set_tag(filepath, edited_column_name, user_input_data)
|
response = set_tag(filepath, edited_column_name, user_input_data)
|
||||||
if response:
|
if response:
|
||||||
@ -685,12 +676,12 @@ class MusicTable(QTableView):
|
|||||||
self.vertical_scroll_position = self.verticalScrollBar().value() # type: ignore
|
self.vertical_scroll_position = self.verticalScrollBar().value() # type: ignore
|
||||||
self.model2.clear()
|
self.model2.clear()
|
||||||
self.model2.setHorizontalHeaderLabels(self.headers.get_user_gui_headers())
|
self.model2.setHorizontalHeaderLabels(self.headers.get_user_gui_headers())
|
||||||
|
fields = ", ".join(self.headers.user_fields)
|
||||||
search_clause = (
|
search_clause = (
|
||||||
"title LIKE %?% AND artist LIKE %?% and album LIKE %?%"
|
"title LIKE %?% AND artist LIKE %?% and album LIKE %?%"
|
||||||
if self.search_string
|
if self.search_string
|
||||||
else ""
|
else ""
|
||||||
)
|
)
|
||||||
fields = ", ".join(self.headers.user_headers)
|
|
||||||
params = ""
|
params = ""
|
||||||
if playlist_id: # Load a playlist
|
if playlist_id: # Load a playlist
|
||||||
selected_playlist_id = playlist_id[0]
|
selected_playlist_id = playlist_id[0]
|
||||||
@ -762,7 +753,7 @@ class MusicTable(QTableView):
|
|||||||
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()):
|
for row in range(self.model2.rowCount()):
|
||||||
real_index = self.model2.index(
|
real_index = self.model2.index(
|
||||||
row, self.headers.user_headers.index("filepath")
|
row, self.headers.user_fields.index("filepath")
|
||||||
)
|
)
|
||||||
if real_index.data() == current_song_filepath:
|
if real_index.data() == current_song_filepath:
|
||||||
print("is it true?")
|
print("is it true?")
|
||||||
@ -864,9 +855,7 @@ class MusicTable(QTableView):
|
|||||||
selected_rows = self.get_selected_rows()
|
selected_rows = self.get_selected_rows()
|
||||||
filepaths = []
|
filepaths = []
|
||||||
for row in selected_rows:
|
for row in selected_rows:
|
||||||
idx = self.proxymodel.index(
|
idx = self.proxymodel.index(row, self.headers.user_fields.index("filepath"))
|
||||||
row, self.headers.user_headers.index("filepath")
|
|
||||||
)
|
|
||||||
filepaths.append(idx.data())
|
filepaths.append(idx.data())
|
||||||
return filepaths
|
return filepaths
|
||||||
|
|
||||||
@ -903,8 +892,8 @@ class MusicTable(QTableView):
|
|||||||
def set_selected_song_filepath(self) -> None:
|
def set_selected_song_filepath(self) -> None:
|
||||||
"""Sets the filepath of the currently selected song"""
|
"""Sets the filepath of the currently selected song"""
|
||||||
try:
|
try:
|
||||||
table_index = self.headers.user_headers.index("filepath")
|
user_index = self.headers.user_fields.index("filepath")
|
||||||
filepath = self.currentIndex().siblingAtColumn(table_index).data()
|
filepath = self.currentIndex().siblingAtColumn(user_index).data()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# if the user doesnt have filepath selected as a header, retrieve the file from db
|
# if the user doesnt have filepath selected as a header, retrieve the file from db
|
||||||
row = self.currentIndex().row()
|
row = self.currentIndex().row()
|
||||||
@ -925,7 +914,7 @@ class MusicTable(QTableView):
|
|||||||
# update the filepath
|
# update the filepath
|
||||||
if not filepath:
|
if not filepath:
|
||||||
path = self.current_song_qmodel_index.siblingAtColumn(
|
path = self.current_song_qmodel_index.siblingAtColumn(
|
||||||
self.headers.user_headers.index("filepath")
|
self.headers.user_fields.index("filepath")
|
||||||
).data()
|
).data()
|
||||||
self.current_song_filepath: str = path
|
self.current_song_filepath: str = path
|
||||||
else:
|
else:
|
||||||
|
|||||||
6
main.py
6
main.py
@ -162,6 +162,8 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
/ "config.ini"
|
/ "config.ini"
|
||||||
)
|
)
|
||||||
self.config.read(self.cfg_file)
|
self.config.read(self.cfg_file)
|
||||||
|
print("main config:")
|
||||||
|
print(self.config)
|
||||||
self.threadpool: QThreadPool = QThreadPool()
|
self.threadpool: QThreadPool = QThreadPool()
|
||||||
# UI
|
# UI
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
@ -392,7 +394,7 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
prev_row, index.column()
|
prev_row, index.column()
|
||||||
)
|
)
|
||||||
prev_filepath = prev_index.siblingAtColumn(
|
prev_filepath = prev_index.siblingAtColumn(
|
||||||
self.headers.user_headers.index("filepath")
|
self.headers.user_fields.index("filepath")
|
||||||
).data()
|
).data()
|
||||||
if prev_filepath is None:
|
if prev_filepath is None:
|
||||||
return
|
return
|
||||||
@ -416,7 +418,7 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
next_row, index.column()
|
next_row, index.column()
|
||||||
)
|
)
|
||||||
next_filepath = next_index.siblingAtColumn(
|
next_filepath = next_index.siblingAtColumn(
|
||||||
self.headers.user_headers.index("filepath")
|
self.headers.user_fields.index("filepath")
|
||||||
).data()
|
).data()
|
||||||
if next_filepath is None:
|
if next_filepath is None:
|
||||||
return
|
return
|
||||||
|
|||||||
432
ui.ui
432
ui.ui
@ -1,432 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>MainWindow</class>
|
|
||||||
<widget class="QMainWindow" name="MainWindow">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>1152</width>
|
|
||||||
<height>894</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>MainWindow</string>
|
|
||||||
</property>
|
|
||||||
<property name="statusTip">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="centralwidget">
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="20">
|
|
||||||
<item>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="SearchLineEdit" name="lineEditSearch"/>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="hLayoutHead" stretch="1,0,6">
|
|
||||||
<property name="sizeConstraint">
|
|
||||||
<enum>QLayout::SetFixedSize</enum>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<layout class="QVBoxLayout" name="vlayoutAlbumArt">
|
|
||||||
<property name="sizeConstraint">
|
|
||||||
<enum>QLayout::SetFixedSize</enum>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="AlbumArtGraphicsView" name="albumGraphicsView">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>200</width>
|
|
||||||
<height>200</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>200</width>
|
|
||||||
<height>200</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QVBoxLayout" name="vLayoutSongDetails"/>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QVBoxLayout" name="vLayoutPlaybackVisuals">
|
|
||||||
<item>
|
|
||||||
<widget class="PlotWidget" name="PlotWidget" native="true"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="hLayoutMusicTable" stretch="0,10">
|
|
||||||
<property name="sizeConstraint">
|
|
||||||
<enum>QLayout::SetMaximumSize</enum>
|
|
||||||
</property>
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
|
||||||
<item>
|
|
||||||
<widget class="PlaylistsPane" name="playlistTreeView"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="MusicTable" name="tableView"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="hLayoutCurrentSongDetails" stretch="0">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="hLayoutSongDetails" stretch="0,0,0">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="artistLabel"/>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="titleLabel"/>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="albumLabel"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="hLayoutPlayback" stretch="4,0">
|
|
||||||
<property name="sizeConstraint">
|
|
||||||
<enum>QLayout::SetMaximumSize</enum>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QSlider" name="playbackSlider">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="timeHorizontalLayout2">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="startTimeLabel">
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<family>Monospace</family>
|
|
||||||
<italic>false</italic>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>00:00</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="slashLabel">
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<family>Monospace</family>
|
|
||||||
<italic>false</italic>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>/</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="endTimeLabel">
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<family>Monospace</family>
|
|
||||||
<weight>75</weight>
|
|
||||||
<italic>false</italic>
|
|
||||||
<bold>true</bold>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>00:00</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="hLayoutControls" stretch="1,0,1,1,1,0,1">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="hLayoutVolume">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="volumeLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>50</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QSlider" name="volumeSlider">
|
|
||||||
<property name="minimum">
|
|
||||||
<number>-1</number>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<number>101</number>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<number>50</number>
|
|
||||||
</property>
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="tickPosition">
|
|
||||||
<enum>QSlider::TicksAbove</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer_4">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="previousButton">
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>28</pointsize>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="playButton">
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>28</pointsize>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="nextButton">
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<pointsize>28</pointsize>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
|
||||||
<item>
|
|
||||||
<widget class="QSlider" name="speedSlider">
|
|
||||||
<property name="minimum">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="maximum">
|
|
||||||
<number>100</number>
|
|
||||||
</property>
|
|
||||||
<property name="singleStep">
|
|
||||||
<number>1</number>
|
|
||||||
</property>
|
|
||||||
<property name="value">
|
|
||||||
<number>50</number>
|
|
||||||
</property>
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="invertedAppearance">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="tickPosition">
|
|
||||||
<enum>QSlider::TicksAbove</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="speedLabel">
|
|
||||||
<property name="font">
|
|
||||||
<font>
|
|
||||||
<family>Monospace</family>
|
|
||||||
</font>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>1.00</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QMenuBar" name="menubar">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>1152</width>
|
|
||||||
<height>21</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<widget class="QMenu" name="menuFile">
|
|
||||||
<property name="title">
|
|
||||||
<string>File</string>
|
|
||||||
</property>
|
|
||||||
<addaction name="actionOpenFiles"/>
|
|
||||||
<addaction name="actionNewPlaylist"/>
|
|
||||||
<addaction name="actionExportPlaylist"/>
|
|
||||||
</widget>
|
|
||||||
<widget class="QMenu" name="menuEdit">
|
|
||||||
<property name="title">
|
|
||||||
<string>Edit</string>
|
|
||||||
</property>
|
|
||||||
<addaction name="actionPreferences"/>
|
|
||||||
</widget>
|
|
||||||
<widget class="QMenu" name="menuView">
|
|
||||||
<property name="title">
|
|
||||||
<string>View</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
<widget class="QMenu" name="menuQuick_Actions">
|
|
||||||
<property name="title">
|
|
||||||
<string>Quick-Actions</string>
|
|
||||||
</property>
|
|
||||||
<addaction name="actionScanLibraries"/>
|
|
||||||
<addaction name="actionDeleteLibrary"/>
|
|
||||||
<addaction name="actionDeleteDatabase"/>
|
|
||||||
<addaction name="actionSortColumns"/>
|
|
||||||
</widget>
|
|
||||||
<addaction name="menuFile"/>
|
|
||||||
<addaction name="menuEdit"/>
|
|
||||||
<addaction name="menuView"/>
|
|
||||||
<addaction name="menuQuick_Actions"/>
|
|
||||||
</widget>
|
|
||||||
<widget class="QStatusBar" name="statusbar"/>
|
|
||||||
<action name="actionPreferences">
|
|
||||||
<property name="text">
|
|
||||||
<string>Preferences</string>
|
|
||||||
</property>
|
|
||||||
<property name="statusTip">
|
|
||||||
<string>Open preferences</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionScanLibraries">
|
|
||||||
<property name="text">
|
|
||||||
<string>Scan libraries</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionDeleteLibrary">
|
|
||||||
<property name="text">
|
|
||||||
<string>Delete Library</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionSortColumns">
|
|
||||||
<property name="text">
|
|
||||||
<string>Sort Columns</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionOpenFiles">
|
|
||||||
<property name="text">
|
|
||||||
<string>Open file(s)</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionDeleteDatabase">
|
|
||||||
<property name="text">
|
|
||||||
<string>Delete Database</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionNewPlaylist">
|
|
||||||
<property name="text">
|
|
||||||
<string>New playlist</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
<action name="actionExportPlaylist">
|
|
||||||
<property name="text">
|
|
||||||
<string>Export playlist</string>
|
|
||||||
</property>
|
|
||||||
</action>
|
|
||||||
</widget>
|
|
||||||
<customwidgets>
|
|
||||||
<customwidget>
|
|
||||||
<class>PlotWidget</class>
|
|
||||||
<extends>QWidget</extends>
|
|
||||||
<header>pyqtgraph</header>
|
|
||||||
<container>1</container>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
|
||||||
<class>MusicTable</class>
|
|
||||||
<extends>QTableView</extends>
|
|
||||||
<header>components</header>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
|
||||||
<class>AlbumArtGraphicsView</class>
|
|
||||||
<extends>QGraphicsView</extends>
|
|
||||||
<header>components</header>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
|
||||||
<class>PlaylistsPane</class>
|
|
||||||
<extends>QTreeView</extends>
|
|
||||||
<header>components</header>
|
|
||||||
</customwidget>
|
|
||||||
<customwidget>
|
|
||||||
<class>SearchLineEdit</class>
|
|
||||||
<extends>QLineEdit</extends>
|
|
||||||
<header>components</header>
|
|
||||||
</customwidget>
|
|
||||||
</customwidgets>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
||||||
@ -107,6 +107,7 @@ def set_tag(filepath: str, tag_name: str, value: str):
|
|||||||
# if tdat_tag:
|
# if tdat_tag:
|
||||||
# # update TDAT if we have it
|
# # update TDAT if we have it
|
||||||
# audio_file.add(tdat_tag)
|
# audio_file.add(tdat_tag)
|
||||||
|
|
||||||
# Lyrics
|
# Lyrics
|
||||||
if tag_name == "lyrics" or tag_name == "USLT":
|
if tag_name == "lyrics" or tag_name == "USLT":
|
||||||
try:
|
try:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user