layouts, mutagen id3 tags, table drag & drop, double click to play
This commit is contained in:
parent
b85cd47844
commit
76c1aa554b
@ -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
|
||||
@ -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
|
||||
|
||||
@ -1 +1,2 @@
|
||||
from .MusicTable import MusicTable
|
||||
from .AudioVisualizer import AudioVisualizer
|
||||
52
main.py
52
main.py
@ -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"""
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
tinytag
|
||||
mutagen
|
||||
matplotlib
|
||||
pyqt5
|
||||
pydub
|
||||
|
||||
@ -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
158
ui.py
@ -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", "⏭️"))
|
||||
|
||||
281
ui.ui
281
ui.ui
@ -17,65 +17,15 @@
|
||||
<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>
|
||||
<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="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>
|
||||
@ -102,46 +52,6 @@
|
||||
</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>
|
||||
</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 class="QFrame" name="frame_4">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
@ -168,41 +78,6 @@
|
||||
</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>
|
||||
@ -272,15 +147,129 @@
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<widget class="QWidget" name="horizontalLayoutWidget_2">
|
||||
<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>620</y>
|
||||
<y>0</y>
|
||||
<width>1061</width>
|
||||
<height>60</height>
|
||||
<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="QPushButton" name="previousButton">
|
||||
@ -319,8 +308,38 @@
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</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="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>50</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
|
||||
@ -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 .add_files_to_library import add_files_to_library
|
||||
58
utils/add_files_to_library.py
Normal file
58
utils/add_files_to_library.py
Normal 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'))
|
||||
@ -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
21
utils/get_id3_tags.py
Normal 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)
|
||||
@ -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((
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user