From 20636cd081288355f022791eef514993482e5e21 Mon Sep 17 00:00:00 2001 From: billypom on debian Date: Sun, 25 Feb 2024 18:30:52 -0500 Subject: [PATCH] refactor for proper layout usage --- components/MusicTable.py | 19 +- main.py | 32 ++- ui.py | 140 +++++------ ui.ui | 437 +++++++++++++--------------------- utils/__init__.py | 1 + utils/add_files_to_library.py | 10 +- utils/get_album_art.py | 17 ++ 7 files changed, 296 insertions(+), 360 deletions(-) create mode 100644 utils/get_album_art.py diff --git a/components/MusicTable.py b/components/MusicTable.py index 55580ef..d505c40 100644 --- a/components/MusicTable.py +++ b/components/MusicTable.py @@ -4,6 +4,8 @@ from PyQt5.QtWidgets import QTableView from PyQt5.QtCore import QTimer from tinytag import TinyTag from utils import add_files_to_library +from utils import get_id3_tags +from utils import get_album_art import logging @@ -68,7 +70,7 @@ class MusicTable(QTableView): def get_selected_song_metadata(self): """Returns the current/chosen song's ID3 tags""" - return TinyTag.get(self.selected_song_filepath) + return get_id3_tags(self.selected_song_filepath) def get_current_song_filepath(self): """Returns the current/chosen song filepath""" @@ -76,7 +78,11 @@ class MusicTable(QTableView): def get_current_song_metadata(self): """Returns the current/chosen song's ID3 tags""" - return TinyTag.get(self.current_song_filepath) + return get_id3_tags(self.current_song_filepath) + + def get_current_song_album_art(self): + """Returns the APIC data for the currently playing song""" + return get_album_art(self.current_song_filepath) def fetch_library(self): @@ -92,7 +98,8 @@ class MusicTable(QTableView): self.model.appendRow(items) # Set the model to the tableView (we are the tableview) self.setModel(self.model) - self.update() + # self.update() + self.viewport().update() def add_files(self, files): """When song(s) added to the library, update the tableview model @@ -100,11 +107,9 @@ class MusicTable(QTableView): - File > Open > List of song(s) """ print(f'tableView - adding files: {files}') - response = add_files_to_library(files) - if response: + number_of_files_added = add_files_to_library(files) + if number_of_files_added: self.fetch_library() - else: - logging.warning('MusicTable.add_files | failed to add files to library') def load_qapp(self, qapp): diff --git a/main.py b/main.py index 9846ac7..5eeeaed 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,10 @@ import DBA from ui import Ui_MainWindow -from PyQt5.QtWidgets import QMainWindow, QApplication +from PyQt5.QtWidgets import QMainWindow, QApplication, QGraphicsScene import qdarktheme -from PyQt5.QtCore import QUrl, QTimer, QEvent +from PyQt5.QtCore import QUrl, QTimer, QEvent, Qt, QRect from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QAudioProbe +from PyQt5.QtGui import QPixmap from utils import scan_for_music from utils import initialize_library_database from components import AudioVisualizer @@ -21,6 +22,8 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow): self.selected_song_filepath = None self.current_song_filepath = None self.current_song_metadata = None + self.current_song_album_art = None + self.album_art_scene = QGraphicsScene() self.qapp = qapp print(f'ApplicationWindow self.qapp: {self.qapp}') self.tableView.load_qapp(self.qapp) @@ -116,6 +119,7 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow): def play_audio_file(self): """Start playback of selected track & moves playback slider""" self.current_song_metadata = self.tableView.get_current_song_metadata() # get metadata + self.current_song_album_art = self.tableView.get_current_song_album_art() url = QUrl.fromLocalFile(self.tableView.get_current_song_filepath()) # read the file content = QMediaContent(url) # load the audio content self.player.setMedia(content) # what content to play @@ -124,13 +128,31 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow): # assign metadata # FIXME when i change tinytag to something else - artist = self.current_song_metadata.artist - album = self.current_song_metadata.album - title = self.current_song_metadata.title + artist = self.current_song_metadata["artist"][0] if "artist" in self.current_song_metadata else None + album = self.current_song_metadata["album"][0] if "album" in self.current_song_metadata else None + title = self.current_song_metadata["title"][0] # edit labels self.artistLabel.setText(artist) self.albumLabel.setText(album) self.titleLabel.setText(title) + # set album artwork + self.load_album_art(self.current_song_album_art) + + + def load_album_art(self, album_art_data): + """Sets the album art for the currently playing track""" + if self.current_song_album_art: + # Create pixmap for album art + pixmap = QPixmap() + pixmap.loadFromData(self.current_song_album_art) + self.album_art_scene.addPixmap(pixmap) + # Reset the scene + self.albumGraphicsView.setScene(None) + # Set the scene + self.albumGraphicsView.setScene(self.album_art_scene) + # Put artwork in the scene, fit to graphics view widget + self.albumGraphicsView.fitInView(self.album_art_scene.sceneRect(), Qt.KeepAspectRatio) + def update_audio_visualization(self): """Handles upading points on the pyqtgraph visual""" diff --git a/ui.py b/ui.py index eced834..97bb15a 100644 --- a/ui.py +++ b/ui.py @@ -14,58 +14,47 @@ from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") - MainWindow.resize(1062, 795) + MainWindow.resize(1152, 894) MainWindow.setStatusTip("") self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.centralwidget) self.verticalLayout_3.setObjectName("verticalLayout_3") - self.frame_6 = QtWidgets.QFrame(self.centralwidget) - self.frame_6.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.frame_6.setFrameShadow(QtWidgets.QFrame.Raised) - self.frame_6.setObjectName("frame_6") - self.frame_2 = QtWidgets.QFrame(self.frame_6) - self.frame_2.setGeometry(QtCore.QRect(0, 0, 161, 161)) - self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised) - self.frame_2.setObjectName("frame_2") - self.albumGraphicsView = QtWidgets.QGraphicsView(self.frame_2) - self.albumGraphicsView.setGeometry(QtCore.QRect(0, 0, 161, 161)) + self.hLayoutHead = QtWidgets.QHBoxLayout() + self.hLayoutHead.setObjectName("hLayoutHead") + self.vlayoutAlbumArt = QtWidgets.QVBoxLayout() + self.vlayoutAlbumArt.setObjectName("vlayoutAlbumArt") + self.albumGraphicsView = QtWidgets.QGraphicsView(self.centralwidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.albumGraphicsView.sizePolicy().hasHeightForWidth()) + self.albumGraphicsView.setSizePolicy(sizePolicy) + self.albumGraphicsView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.albumGraphicsView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) + self.albumGraphicsView.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustIgnored) + self.albumGraphicsView.setInteractive(False) + self.albumGraphicsView.setViewportUpdateMode(QtWidgets.QGraphicsView.FullViewportUpdate) self.albumGraphicsView.setObjectName("albumGraphicsView") - self.frame_4 = QtWidgets.QFrame(self.frame_6) - self.frame_4.setGeometry(QtCore.QRect(540, 40, 521, 121)) - self.frame_4.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.frame_4.setFrameShadow(QtWidgets.QFrame.Raised) - self.frame_4.setObjectName("frame_4") - self.PlotWidget = PlotWidget(self.frame_4) - self.PlotWidget.setGeometry(QtCore.QRect(0, 0, 521, 121)) - self.PlotWidget.setObjectName("PlotWidget") - self.frame_5 = QtWidgets.QFrame(self.frame_6) - self.frame_5.setGeometry(QtCore.QRect(160, 0, 381, 161)) - self.frame_5.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.frame_5.setFrameShadow(QtWidgets.QFrame.Raised) - self.frame_5.setObjectName("frame_5") - self.verticalLayoutWidget = QtWidgets.QWidget(self.frame_5) - self.verticalLayoutWidget.setGeometry(QtCore.QRect(0, 0, 381, 161)) - self.verticalLayoutWidget.setObjectName("verticalLayoutWidget") - self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget) - self.verticalLayout.setContentsMargins(0, 0, 0, 0) - self.verticalLayout.setObjectName("verticalLayout") - self.artistLabel = QtWidgets.QLabel(self.verticalLayoutWidget) + self.vlayoutAlbumArt.addWidget(self.albumGraphicsView) + self.hLayoutHead.addLayout(self.vlayoutAlbumArt) + self.vLayoutSongDetails = QtWidgets.QVBoxLayout() + self.vLayoutSongDetails.setObjectName("vLayoutSongDetails") + self.artistLabel = QtWidgets.QLabel(self.centralwidget) font = QtGui.QFont() font.setPointSize(24) font.setBold(True) font.setWeight(75) self.artistLabel.setFont(font) self.artistLabel.setObjectName("artistLabel") - self.verticalLayout.addWidget(self.artistLabel) - self.titleLabel = QtWidgets.QLabel(self.verticalLayoutWidget) + self.vLayoutSongDetails.addWidget(self.artistLabel) + self.titleLabel = QtWidgets.QLabel(self.centralwidget) font = QtGui.QFont() font.setPointSize(18) self.titleLabel.setFont(font) self.titleLabel.setObjectName("titleLabel") - self.verticalLayout.addWidget(self.titleLabel) - self.albumLabel = QtWidgets.QLabel(self.verticalLayoutWidget) + self.vLayoutSongDetails.addWidget(self.titleLabel) + self.albumLabel = QtWidgets.QLabel(self.centralwidget) font = QtGui.QFont() font.setPointSize(16) font.setBold(False) @@ -73,44 +62,37 @@ class Ui_MainWindow(object): font.setWeight(50) self.albumLabel.setFont(font) self.albumLabel.setObjectName("albumLabel") - self.verticalLayout.addWidget(self.albumLabel) - self.horizontalLayoutWidget = QtWidgets.QWidget(self.frame_6) - self.horizontalLayoutWidget.setGeometry(QtCore.QRect(540, 0, 521, 41)) - self.horizontalLayoutWidget.setObjectName("horizontalLayoutWidget") - self.horizontalLayout = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget) - self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.vLayoutSongDetails.addWidget(self.albumLabel) + self.hLayoutHead.addLayout(self.vLayoutSongDetails) + self.vLayoutPlaybackVisuals = QtWidgets.QVBoxLayout() + self.vLayoutPlaybackVisuals.setObjectName("vLayoutPlaybackVisuals") + self.horizontalLayout = QtWidgets.QHBoxLayout() self.horizontalLayout.setObjectName("horizontalLayout") - self.playbackSlider = QtWidgets.QSlider(self.horizontalLayoutWidget) + self.playbackSlider = QtWidgets.QSlider(self.centralwidget) self.playbackSlider.setOrientation(QtCore.Qt.Horizontal) self.playbackSlider.setObjectName("playbackSlider") self.horizontalLayout.addWidget(self.playbackSlider) - self.startTimeLabel = QtWidgets.QLabel(self.horizontalLayoutWidget) + self.startTimeLabel = QtWidgets.QLabel(self.centralwidget) self.startTimeLabel.setObjectName("startTimeLabel") self.horizontalLayout.addWidget(self.startTimeLabel) - self.slashLabel = QtWidgets.QLabel(self.horizontalLayoutWidget) + self.slashLabel = QtWidgets.QLabel(self.centralwidget) self.slashLabel.setObjectName("slashLabel") self.horizontalLayout.addWidget(self.slashLabel) - self.endTimeLabel = QtWidgets.QLabel(self.horizontalLayoutWidget) + self.endTimeLabel = QtWidgets.QLabel(self.centralwidget) self.endTimeLabel.setObjectName("endTimeLabel") self.horizontalLayout.addWidget(self.endTimeLabel) - self.verticalLayout_3.addWidget(self.frame_6) - self.frame = QtWidgets.QFrame(self.centralwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth()) - self.frame.setSizePolicy(sizePolicy) - self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.frame.setFrameShadow(QtWidgets.QFrame.Raised) - self.frame.setObjectName("frame") - self.verticalLayoutWidget_2 = QtWidgets.QWidget(self.frame) - self.verticalLayoutWidget_2.setGeometry(QtCore.QRect(0, 0, 1061, 461)) - self.verticalLayoutWidget_2.setObjectName("verticalLayoutWidget_2") - self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_2) - self.verticalLayout_2.setSizeConstraint(QtWidgets.QLayout.SetNoConstraint) - self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) - self.verticalLayout_2.setObjectName("verticalLayout_2") - self.tableView = MusicTable(self.verticalLayoutWidget_2) + self.vLayoutPlaybackVisuals.addLayout(self.horizontalLayout) + self.PlotWidget = PlotWidget(self.centralwidget) + self.PlotWidget.setObjectName("PlotWidget") + self.vLayoutPlaybackVisuals.addWidget(self.PlotWidget) + self.hLayoutHead.addLayout(self.vLayoutPlaybackVisuals) + self.hLayoutHead.setStretch(0, 2) + self.hLayoutHead.setStretch(1, 4) + self.hLayoutHead.setStretch(2, 6) + self.verticalLayout_3.addLayout(self.hLayoutHead) + self.hLayoutMusicTable = QtWidgets.QHBoxLayout() + self.hLayoutMusicTable.setObjectName("hLayoutMusicTable") + self.tableView = MusicTable(self.centralwidget) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum) sizePolicy.setHorizontalStretch(1) sizePolicy.setVerticalStretch(1) @@ -129,47 +111,45 @@ class Ui_MainWindow(object): self.tableView.horizontalHeader().setCascadingSectionResizes(True) self.tableView.horizontalHeader().setStretchLastSection(True) self.tableView.verticalHeader().setVisible(False) - self.verticalLayout_2.addWidget(self.tableView) - self.verticalLayout_3.addWidget(self.frame) - self.horizontalLayout_2 = QtWidgets.QHBoxLayout() - self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.hLayoutMusicTable.addWidget(self.tableView) + self.verticalLayout_3.addLayout(self.hLayoutMusicTable) + self.hLayoutControls = QtWidgets.QHBoxLayout() + self.hLayoutControls.setObjectName("hLayoutControls") self.previousButton = QtWidgets.QPushButton(self.centralwidget) font = QtGui.QFont() font.setPointSize(28) self.previousButton.setFont(font) self.previousButton.setObjectName("previousButton") - self.horizontalLayout_2.addWidget(self.previousButton) + self.hLayoutControls.addWidget(self.previousButton) self.playButton = QtWidgets.QPushButton(self.centralwidget) font = QtGui.QFont() font.setPointSize(28) self.playButton.setFont(font) self.playButton.setObjectName("playButton") - self.horizontalLayout_2.addWidget(self.playButton) + self.hLayoutControls.addWidget(self.playButton) self.nextButton = QtWidgets.QPushButton(self.centralwidget) font = QtGui.QFont() font.setPointSize(28) self.nextButton.setFont(font) self.nextButton.setObjectName("nextButton") - self.horizontalLayout_2.addWidget(self.nextButton) - self.verticalLayout_3.addLayout(self.horizontalLayout_2) - self.frame_3 = QtWidgets.QFrame(self.centralwidget) - self.frame_3.setFrameShape(QtWidgets.QFrame.StyledPanel) - self.frame_3.setFrameShadow(QtWidgets.QFrame.Raised) - self.frame_3.setObjectName("frame_3") - self.volumeSlider = QtWidgets.QSlider(self.frame_3) - self.volumeSlider.setGeometry(QtCore.QRect(10, 10, 181, 31)) + self.hLayoutControls.addWidget(self.nextButton) + self.verticalLayout_3.addLayout(self.hLayoutControls) + self.hLayoutControls2 = QtWidgets.QHBoxLayout() + self.hLayoutControls2.setObjectName("hLayoutControls2") + self.volumeSlider = QtWidgets.QSlider(self.centralwidget) self.volumeSlider.setMaximum(100) self.volumeSlider.setProperty("value", 50) self.volumeSlider.setOrientation(QtCore.Qt.Horizontal) self.volumeSlider.setObjectName("volumeSlider") - self.verticalLayout_3.addWidget(self.frame_3) + self.hLayoutControls2.addWidget(self.volumeSlider) + self.verticalLayout_3.addLayout(self.hLayoutControls2) self.verticalLayout_3.setStretch(0, 3) self.verticalLayout_3.setStretch(1, 8) self.verticalLayout_3.setStretch(2, 1) self.verticalLayout_3.setStretch(3, 1) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 1062, 24)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1152, 24)) self.menubar.setObjectName("menubar") self.menuFile = QtWidgets.QMenu(self.menubar) self.menuFile.setObjectName("menuFile") diff --git a/ui.ui b/ui.ui index 3b7210a..96146c1 100644 --- a/ui.ui +++ b/ui.ui @@ -6,8 +6,8 @@ 0 0 - 1062 - 795 + 1152 + 894 @@ -19,258 +19,177 @@ - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - 0 - 0 - 161 - 161 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - 0 - 0 - 161 - 161 - - - - - - - - 540 - 40 - 521 - 121 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - 0 - 0 - 521 - 121 - - - - - - - - 160 - 0 - 381 - 161 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - 0 - 0 - 381 - 161 - - - - - - - - 24 - 75 - true - - - - artist - - - - - - - - 18 - - - - song title - - - - - - - - 16 - 50 - true - false - - - - album - - - - - - - - - - 540 - 0 - 521 - 41 - - - + + + - - - Qt::Horizontal - - - - - - - 00:00 - - - - - - - / - - - - - - - 00:00 - - - - - - - - - - - - 0 - 0 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - 0 - 0 - 1061 - 461 - - - - - QLayout::SetNoConstraint - - - + - 1 - 1 + 0 + 0 - - - 32000 - 32000 - - - - true + + Qt::ScrollBarAlwaysOff Qt::ScrollBarAlwaysOff - QAbstractScrollArea::AdjustToContents + QAbstractScrollArea::AdjustIgnored - - QAbstractItemView::AnyKeyPressed|QAbstractItemView::EditKeyPressed - - - true - - - QAbstractItemView::ExtendedSelection - - - QAbstractItemView::SelectRows - - - true - - - true - - - true - - + false - + + + QGraphicsView::FullViewportUpdate + - - + + + + + + + + 24 + 75 + true + + + + artist + + + + + + + + 18 + + + + song title + + + + + + + + 16 + 50 + true + false + + + + album + + + + + + + + + + + + + Qt::Horizontal + + + + + + + 00:00 + + + + + + + / + + + + + + + 00:00 + + + + + + + + + + + - + + + + + + 1 + 1 + + + + + 32000 + 32000 + + + + true + + + Qt::ScrollBarAlwaysOff + + + QAbstractScrollArea::AdjustToContents + + + QAbstractItemView::AnyKeyPressed|QAbstractItemView::EditKeyPressed + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + true + + + true + + + true + + + false + + + + + + + @@ -310,33 +229,21 @@ - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - 10 - 10 - 181 - 31 - - - - 100 - - - 50 - - - Qt::Horizontal - - - + + + + + 100 + + + 50 + + + Qt::Horizontal + + + + @@ -345,7 +252,7 @@ 0 0 - 1062 + 1152 24 diff --git a/utils/__init__.py b/utils/__init__.py index fe8a054..5760804 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -1,4 +1,5 @@ from .safe_get import safe_get +from .get_album_art import get_album_art from .get_id3_tags import get_id3_tags from .initialize_library_database import initialize_library_database from .scan_for_music import scan_for_music diff --git a/utils/add_files_to_library.py b/utils/add_files_to_library.py index 19cd891..a82d09d 100644 --- a/utils/add_files_to_library.py +++ b/utils/add_files_to_library.py @@ -9,9 +9,13 @@ config.read("config.ini") def add_files_to_library(files): """Adds audio file(s) to the sqllite db - `files` | list() | List of fully qualified paths to audio file(s) - Returns a count of records added + + files | list() of fully qualified paths to audio file(s) + + Returns true if any files were added """ + if not files: + return False print(f"utils | adding files to library: {files}") extensions = config.get("settings", "extensions").split(",") insert_data = [] # To store data for batch insert @@ -20,7 +24,7 @@ def add_files_to_library(files): filename = filepath.split("/")[-1] audio = get_id3_tags(filepath) if "title" not in audio: - return + return False # Append data tuple to insert_data list insert_data.append( ( diff --git a/utils/get_album_art.py b/utils/get_album_art.py new file mode 100644 index 0000000..a9c947b --- /dev/null +++ b/utils/get_album_art.py @@ -0,0 +1,17 @@ +from mutagen.id3 import ID3, APIC + +def get_album_art(file): + """Get the album art for an audio file + # Parameters + `file` | str | Fully qualified path to file + + # Returns + dict of all id3 tags + """ + try: + audio = ID3(file) + album_art = audio.get('APIC:').data if 'APIC:' in audio else None + return album_art + except Exception as e: + print(f"Error: {e}") + return {} \ No newline at end of file