layouts, mutagen id3 tags, table drag & drop, double click to play

This commit is contained in:
billypom on debian 2024-01-28 09:59:51 -05:00
parent b85cd47844
commit 76c1aa554b
13 changed files with 552 additions and 353 deletions

View File

@ -1,10 +1,17 @@
from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5 import QtWidgets
import numpy as np
from .fft_analyser import FFTAnalyser
from utils import FFTAnalyser
class AudioVisualizer(QtWidgets.QWidget):
"""_Audio Visualizer component_
Args:
QtWidgets (_type_): _description_
Returns:
_type_: _description_
"""
def __init__(self, media_player):
super().__init__()
self.media_player = media_player

View File

@ -1,16 +1,17 @@
import DBA
from PyQt5.QtGui import QStandardItem, QStandardItemModel
from PyQt5.QtWidgets import QTableView, QApplication
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
import logging
class MusicTable(QTableView):
def __init__(self, parent=None, qapp=None):
QTableView.__init__(self, parent)
self.headers = ['title', 'artist', 'album', 'genre', 'codec', 'year', 'path']
self.model = QStandardItemModel()
self.model.setHorizontalHeaderLabels(self.headers)
self.songChanged = None
self.selected_song_filepath = None
self.current_song_filepath = None
@ -56,6 +57,9 @@ class MusicTable(QTableView):
"""Sets the filepath of the currently selected song"""
self.selected_song_filepath = self.currentIndex().siblingAtColumn(self.headers.index('path')).data()
print(f'Selected song: {self.selected_song_filepath}')
print('TAGS:')
print(get_id3_tags(self.selected_song_filepath))
print('END TAGS')
def set_current_song_filepath(self):
"""Sets the filepath of the currently playing/chosen song"""
@ -80,6 +84,9 @@ class MusicTable(QTableView):
def fetch_library(self):
"""Initialize the tableview model"""
self.model = QStandardItemModel()
self.model.setHorizontalHeaderLabels(self.headers)
# Fetch library data
with DBA.DBAccess() as db:
data = db.query('SELECT title, artist, album, genre, codec, album_date, filepath FROM library;', ())
@ -89,6 +96,20 @@ class MusicTable(QTableView):
self.model.appendRow(items)
# Set the model to the tableView (we are the tableview)
self.setModel(self.model)
self.update()
def add_files(self, files):
"""When song(s) added to the library, update the tableview model
- Drag & Drop song(s) on tableView
- File > Open > List of song(s)
"""
print(f'tableView - adding files: {files}')
response = add_files_to_library(files)
if response:
self.fetch_library()
else:
logging.warning('MusicTable.add_files | failed to add files to library')
def load_qapp(self, qapp):
self.qapp = qapp

View File

@ -1 +1,2 @@
from .MusicTable import MusicTable
from .AudioVisualizer import AudioVisualizer

52
main.py
View File

@ -2,15 +2,16 @@ import DBA
from ui import Ui_MainWindow
from PyQt5.QtWidgets import QMainWindow, QApplication
import qdarktheme
from PyQt5.QtCore import QUrl, QTimer, QFile, QTextStream
from PyQt5.QtCore import QUrl, QTimer, QEvent
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QAudioProbe
from PyQt5.QtGui import QStandardItem, QStandardItemModel
from utils import scan_for_music
from utils import initialize_library_database
from utils import AudioVisualizer
from components import AudioVisualizer
from pyqtgraph import mkBrush
from components import MusicTable
import configparser
# Create ui.py file from Qt Designer
# pyuic5 ui.ui -o ui.py
class ApplicationWindow(QMainWindow, Ui_MainWindow):
def __init__(self, qapp):
@ -23,6 +24,8 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
self.qapp = qapp
print(f'ApplicationWindow self.qapp: {self.qapp}')
self.tableView.load_qapp(self.qapp)
self.config = configparser.ConfigParser()
self.config.read('config.ini')
global stopped
stopped = False
@ -64,12 +67,51 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
# self.pauseButton.clicked.connect(self.on_pause_clicked)
self.previousButton.clicked.connect(self.on_previous_clicked) # Click to previous song
self.nextButton.clicked.connect(self.on_next_clicked) # Click to next song
# self.tableView.clicked.connect(self.set_clicked_cell_filepath)
self.actionPreferences.triggered.connect(self.actionPreferencesClicked) # Open preferences menu
self.actionScanLibraries.triggered.connect(self.scan_libraries) # Scan library
self.actionClearDatabase.triggered.connect(initialize_library_database) # Clear database
## tableView
# self.tableView.clicked.connect(self.set_clicked_cell_filepath)
self.tableView.doubleClicked.connect(self.play_audio_file) # Double click to play song
self.tableView.viewport().installEventFilter(self) # for drag & drop functionality
# self.tableView.model.layoutChanged()
### set column widths
table_view_column_widths = str(self.config['table']['column_widths']).split(',')
for i in range(self.tableView.model.columnCount()):
self.tableView.setColumnWidth(i, int(table_view_column_widths[i]))
def eventFilter(self, source, event):
"""Handles events"""
# tableView (drag & drop)
if (source is self.tableView.viewport() and
(event.type() == QEvent.DragEnter or
event.type() == QEvent.DragMove or
event.type() == QEvent.Drop) and
event.mimeData().hasUrls()):
files = []
if event.type() == QEvent.Drop:
for url in event.mimeData().urls():
if url.isLocalFile():
files.append(url.path())
self.tableView.add_files(files)
event.accept()
return True
return super().eventFilter(source, event)
def closeEvent(self, event):
"""Save settings when closing the application"""
# MusicTable/tableView column widths
list_of_column_widths = []
for i in range(self.tableView.model.columnCount()):
list_of_column_widths.append(str(self.tableView.columnWidth(i)))
column_widths_as_string = ','.join(list_of_column_widths)
self.config['table']['column_widths'] = column_widths_as_string
# Save the config
with open('config.ini', 'w') as configfile:
self.config.write(configfile)
super().closeEvent(event)
def play_audio_file(self):
"""Start playback of selected track & moves playback slider"""

View File

@ -1,4 +1,4 @@
tinytag
mutagen
matplotlib
pyqt5
pydub

View File

@ -10,3 +10,7 @@ reorganize_destination = /where/to/reorganize/to
[settings]
# Which file types are scanned
extensions = mp3,wav,ogg,flac
[table]
# Music table column widths
column_widths = 181,116,222,76,74,72,287

158
ui.py
View File

@ -18,32 +18,13 @@ class Ui_MainWindow(object):
MainWindow.setStatusTip("")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.frame = QtWidgets.QFrame(self.centralwidget)
self.frame.setGeometry(QtCore.QRect(0, 160, 1061, 461))
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Ignored)
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.tableView = MusicTable(self.frame)
self.tableView.setGeometry(QtCore.QRect(0, 0, 1061, 461))
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.tableView.sizePolicy().hasHeightForWidth())
self.tableView.setSizePolicy(sizePolicy)
self.tableView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.tableView.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
self.tableView.setEditTriggers(QtWidgets.QAbstractItemView.AnyKeyPressed|QtWidgets.QAbstractItemView.EditKeyPressed)
self.tableView.setAlternatingRowColors(True)
self.tableView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.tableView.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.tableView.setObjectName("tableView")
self.tableView.verticalHeader().setVisible(False)
self.frame_2 = QtWidgets.QFrame(self.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)
@ -51,26 +32,7 @@ class Ui_MainWindow(object):
self.albumGraphicsView = QtWidgets.QGraphicsView(self.frame_2)
self.albumGraphicsView.setGeometry(QtCore.QRect(0, 0, 161, 161))
self.albumGraphicsView.setObjectName("albumGraphicsView")
self.horizontalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
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.horizontalLayout.setObjectName("horizontalLayout")
self.playbackSlider = QtWidgets.QSlider(self.horizontalLayoutWidget)
self.playbackSlider.setOrientation(QtCore.Qt.Horizontal)
self.playbackSlider.setObjectName("playbackSlider")
self.horizontalLayout.addWidget(self.playbackSlider)
self.startTimeLabel = QtWidgets.QLabel(self.horizontalLayoutWidget)
self.startTimeLabel.setObjectName("startTimeLabel")
self.horizontalLayout.addWidget(self.startTimeLabel)
self.slashLabel = QtWidgets.QLabel(self.horizontalLayoutWidget)
self.slashLabel.setObjectName("slashLabel")
self.horizontalLayout.addWidget(self.slashLabel)
self.endTimeLabel = QtWidgets.QLabel(self.horizontalLayoutWidget)
self.endTimeLabel.setObjectName("endTimeLabel")
self.horizontalLayout.addWidget(self.endTimeLabel)
self.frame_4 = QtWidgets.QFrame(self.centralwidget)
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)
@ -78,18 +40,7 @@ class Ui_MainWindow(object):
self.PlotWidget = PlotWidget(self.frame_4)
self.PlotWidget.setGeometry(QtCore.QRect(0, 0, 521, 121))
self.PlotWidget.setObjectName("PlotWidget")
self.frame_3 = QtWidgets.QFrame(self.centralwidget)
self.frame_3.setGeometry(QtCore.QRect(0, 680, 1061, 61))
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.volumeSlider.setMaximum(100)
self.volumeSlider.setProperty("value", 50)
self.volumeSlider.setOrientation(QtCore.Qt.Horizontal)
self.volumeSlider.setObjectName("volumeSlider")
self.frame_5 = QtWidgets.QFrame(self.centralwidget)
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)
@ -123,30 +74,99 @@ class Ui_MainWindow(object):
self.albumLabel.setFont(font)
self.albumLabel.setObjectName("albumLabel")
self.verticalLayout.addWidget(self.albumLabel)
self.horizontalLayoutWidget_2 = QtWidgets.QWidget(self.centralwidget)
self.horizontalLayoutWidget_2.setGeometry(QtCore.QRect(0, 620, 1061, 60))
self.horizontalLayoutWidget_2.setObjectName("horizontalLayoutWidget_2")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.horizontalLayoutWidget_2)
self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0)
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.horizontalLayout.setObjectName("horizontalLayout")
self.playbackSlider = QtWidgets.QSlider(self.horizontalLayoutWidget)
self.playbackSlider.setOrientation(QtCore.Qt.Horizontal)
self.playbackSlider.setObjectName("playbackSlider")
self.horizontalLayout.addWidget(self.playbackSlider)
self.startTimeLabel = QtWidgets.QLabel(self.horizontalLayoutWidget)
self.startTimeLabel.setObjectName("startTimeLabel")
self.horizontalLayout.addWidget(self.startTimeLabel)
self.slashLabel = QtWidgets.QLabel(self.horizontalLayoutWidget)
self.slashLabel.setObjectName("slashLabel")
self.horizontalLayout.addWidget(self.slashLabel)
self.endTimeLabel = QtWidgets.QLabel(self.horizontalLayoutWidget)
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)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Maximum)
sizePolicy.setHorizontalStretch(1)
sizePolicy.setVerticalStretch(1)
sizePolicy.setHeightForWidth(self.tableView.sizePolicy().hasHeightForWidth())
self.tableView.setSizePolicy(sizePolicy)
self.tableView.setMaximumSize(QtCore.QSize(32000, 32000))
self.tableView.setAcceptDrops(True)
self.tableView.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.tableView.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
self.tableView.setEditTriggers(QtWidgets.QAbstractItemView.AnyKeyPressed|QtWidgets.QAbstractItemView.EditKeyPressed)
self.tableView.setAlternatingRowColors(True)
self.tableView.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.tableView.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
self.tableView.setSortingEnabled(True)
self.tableView.setObjectName("tableView")
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.previousButton = QtWidgets.QPushButton(self.horizontalLayoutWidget_2)
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.playButton = QtWidgets.QPushButton(self.horizontalLayoutWidget_2)
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.nextButton = QtWidgets.QPushButton(self.horizontalLayoutWidget_2)
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.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.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))
@ -183,12 +203,12 @@ class Ui_MainWindow(object):
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.startTimeLabel.setText(_translate("MainWindow", "00:00"))
self.slashLabel.setText(_translate("MainWindow", "/"))
self.endTimeLabel.setText(_translate("MainWindow", "00:00"))
self.artistLabel.setText(_translate("MainWindow", "artist"))
self.titleLabel.setText(_translate("MainWindow", "song title"))
self.albumLabel.setText(_translate("MainWindow", "album"))
self.startTimeLabel.setText(_translate("MainWindow", "00:00"))
self.slashLabel.setText(_translate("MainWindow", "/"))
self.endTimeLabel.setText(_translate("MainWindow", "00:00"))
self.previousButton.setText(_translate("MainWindow", "⏮️"))
self.playButton.setText(_translate("MainWindow", "▶️"))
self.nextButton.setText(_translate("MainWindow", "⏭️"))

551
ui.ui
View File

@ -17,309 +17,328 @@
<string/>
</property>
<widget class="QWidget" name="centralwidget">
<widget class="QFrame" name="frame">
<property name="geometry">
<rect>
<x>0</x>
<y>160</y>
<width>1061</width>
<height>461</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Ignored" vsizetype="Ignored">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<widget class="MusicTable" name="tableView">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1061</width>
<height>461</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::AnyKeyPressed|QAbstractItemView::EditKeyPressed</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</widget>
<widget class="QFrame" name="frame_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>161</width>
<height>161</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<widget class="QGraphicsView" name="albumGraphicsView">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>161</width>
<height>161</height>
</rect>
</property>
</widget>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget">
<property name="geometry">
<rect>
<x>540</x>
<y>0</y>
<width>521</width>
<height>41</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSlider" name="playbackSlider">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<layout class="QVBoxLayout" name="verticalLayout_3" stretch="3,8,1,1">
<item>
<widget class="QFrame" name="frame_6">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<widget class="QFrame" name="frame_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>161</width>
<height>161</height>
</rect>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="startTimeLabel">
<property name="text">
<string>00:00</string>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="slashLabel">
<property name="text">
<string>/</string>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<widget class="QGraphicsView" name="albumGraphicsView">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>161</width>
<height>161</height>
</rect>
</property>
</widget>
</widget>
</item>
<item>
<widget class="QLabel" name="endTimeLabel">
<property name="text">
<string>00:00</string>
<widget class="QFrame" name="frame_4">
<property name="geometry">
<rect>
<x>540</x>
<y>40</y>
<width>521</width>
<height>121</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<widget class="PlotWidget" name="PlotWidget" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>521</width>
<height>121</height>
</rect>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QFrame" name="frame_4">
<property name="geometry">
<rect>
<x>540</x>
<y>40</y>
<width>521</width>
<height>121</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<widget class="PlotWidget" name="PlotWidget" native="true">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>521</width>
<height>121</height>
</rect>
</property>
</widget>
</widget>
<widget class="QFrame" name="frame_3">
<property name="geometry">
<rect>
<x>0</x>
<y>680</y>
<width>1061</width>
<height>61</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<widget class="QSlider" name="volumeSlider">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>181</width>
<height>31</height>
</rect>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</widget>
<widget class="QFrame" name="frame_5">
<property name="geometry">
<rect>
<x>160</x>
<y>0</y>
<width>381</width>
<height>161</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>381</width>
<height>161</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<widget class="QFrame" name="frame_5">
<property name="geometry">
<rect>
<x>160</x>
<y>0</y>
<width>381</width>
<height>161</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>381</width>
<height>161</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="artistLabel">
<property name="font">
<font>
<pointsize>24</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>artist</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="titleLabel">
<property name="font">
<font>
<pointsize>18</pointsize>
</font>
</property>
<property name="text">
<string>song title</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="albumLabel">
<property name="font">
<font>
<pointsize>16</pointsize>
<weight>50</weight>
<italic>true</italic>
<bold>false</bold>
</font>
</property>
<property name="text">
<string>album</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget">
<property name="geometry">
<rect>
<x>540</x>
<y>0</y>
<width>521</width>
<height>41</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSlider" name="playbackSlider">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="startTimeLabel">
<property name="text">
<string>00:00</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="slashLabel">
<property name="text">
<string>/</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="endTimeLabel">
<property name="text">
<string>00:00</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<widget class="QWidget" name="verticalLayoutWidget_2">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1061</width>
<height>461</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0">
<property name="sizeConstraint">
<enum>QLayout::SetNoConstraint</enum>
</property>
<item>
<widget class="MusicTable" name="tableView">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>1</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>32000</width>
<height>32000</height>
</size>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="sizeAdjustPolicy">
<enum>QAbstractScrollArea::AdjustToContents</enum>
</property>
<property name="editTriggers">
<set>QAbstractItemView::AnyKeyPressed|QAbstractItemView::EditKeyPressed</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<attribute name="horizontalHeaderCascadingSectionResizes">
<bool>true</bool>
</attribute>
<attribute name="horizontalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="artistLabel">
<widget class="QPushButton" name="previousButton">
<property name="font">
<font>
<pointsize>24</pointsize>
<weight>75</weight>
<bold>true</bold>
<pointsize>28</pointsize>
</font>
</property>
<property name="text">
<string>artist</string>
<string>⏮️</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="titleLabel">
<widget class="QPushButton" name="playButton">
<property name="font">
<font>
<pointsize>18</pointsize>
<pointsize>28</pointsize>
</font>
</property>
<property name="text">
<string>song title</string>
<string>▶️</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="albumLabel">
<widget class="QPushButton" name="nextButton">
<property name="font">
<font>
<pointsize>16</pointsize>
<weight>50</weight>
<italic>true</italic>
<bold>false</bold>
<pointsize>28</pointsize>
</font>
</property>
<property name="text">
<string>album</string>
<string>⏭️</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QWidget" name="horizontalLayoutWidget_2">
<property name="geometry">
<rect>
<x>0</x>
<y>620</y>
<width>1061</width>
<height>60</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QPushButton" name="previousButton">
<property name="font">
<font>
<pointsize>28</pointsize>
</font>
</item>
<item>
<widget class="QFrame" name="frame_3">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<widget class="QSlider" name="volumeSlider">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>181</width>
<height>31</height>
</rect>
</property>
<property name="text">
<string>⏮️</string>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>50</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="playButton">
<property name="font">
<font>
<pointsize>28</pointsize>
</font>
</property>
<property name="text">
<string>▶️</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="nextButton">
<property name="font">
<font>
<pointsize>28</pointsize>
</font>
</property>
<property name="text">
<string>⏭️</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">

View File

@ -1,4 +1,5 @@
from .get_id3_tags import get_id3_tags
from .initialize_library_database import initialize_library_database
from .scan_for_music import scan_for_music
from .audio_visualizer import AudioVisualizer
from.fft_analyser import FFTAnalyser
from .fft_analyser import FFTAnalyser
from .add_files_to_library import add_files_to_library

View File

@ -0,0 +1,58 @@
import DBA
from configparser import ConfigParser
from utils import get_id3_tags
config = ConfigParser()
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
"""
print(f'utils | adding files to library: {files}')
extensions = config.get('settings', 'extensions').split(',')
insert_data = [] # To store data for batch insert
for file in files:
if any(file.lower().endswith(ext) for ext in extensions):
filename = file.split("/")[-1]
audio = get_id3_tags(file)
# Append data tuple to insert_data list
insert_data.append(
(
file,
audio.title,
audio.album,
audio.artist,
audio.genre,
filename.split(".")[-1],
audio.year,
audio.bitrate,
)
)
# Check if batch size is reached
if len(insert_data) >= 1000:
with DBA.DBAccess() as db:
db.executemany('INSERT OR IGNORE INTO library (filepath, title, album, artist, genre, codec, album_date, bitrate) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', insert_data)
insert_data = [] # Reset the insert_data list
# Insert any remaining data
if insert_data:
with DBA.DBAccess() as db:
db.executemany('INSERT OR IGNORE INTO library (filepath, title, album, artist, genre, codec, album_date, bitrate) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', insert_data)
return True
# id int unsigned auto_increment,
# title varchar(255),
# album varchar(255),
# artist varchar(255),
# genre varchar(255),
# codec varchar(15),
# album_date date,
# bitrate int unsigned,
# date_added TIMESTAMP default CURRENT_TIMESTAMP,
# scan_for_music(config.get('directories', 'library1'))

View File

@ -5,6 +5,11 @@ from pydub import AudioSegment
def create_waveform_from_file(file):
"""_summary_
Args:
file (_type_): _description_
"""
# Read the MP3 file
audio = AudioSegment.from_file(file)
# Convert to mono and get frame rate and number of channels
@ -43,7 +48,7 @@ def create_waveform_from_file(file):
# Graph goes to ends of pic
plt.xlim(0, len(waveform))
# Save pic
plt.savefig('now_playing_waveform.png', dpi=64, bbox_inches='tight', pad_inches=0)
plt.savefig('assets/now_playing_waveform.png', dpi=64, bbox_inches='tight', pad_inches=0)
# Show me tho
# plt.show()

21
utils/get_id3_tags.py Normal file
View File

@ -0,0 +1,21 @@
from mutagen.easyid3 import EasyID3
from mutagen import File
def get_id3_tags(file):
"""Get the ID3 tags for an audio file
# Parameters
`file` | str | Fully qualified path to file
# Returns
dict of all id3 tags
"""
try:
audio = EasyID3(file)
return audio
except Exception as e:
print(f"Error: {e}")
return {}
filepath = '/home/billy/Music/songs/meanings/blah99/H.mp3'
id3_tags = get_id3_tags(filepath)
print(id3_tags)

View File

@ -1,7 +1,7 @@
import os
import DBA
from configparser import ConfigParser
from tinytag import TinyTag
from utils import get_id3_tags
config = ConfigParser()
config.read('config.ini')
@ -16,7 +16,7 @@ def scan_for_music():
for filename in filenames:
if any(filename.lower().endswith(ext) for ext in extensions):
filepath = os.path.join(dirpath, filename)
audio = TinyTag.get(filepath)
audio = get_id3_tags(filepath)
# Append data tuple to insert_data list
insert_data.append((