preparation for reorganization function

This commit is contained in:
billypom on debian 2024-03-19 20:57:23 -04:00
parent ca5b9ad849
commit 6daadb09aa
2 changed files with 72 additions and 47 deletions

View File

@ -1,12 +1,12 @@
import DBA import DBA
from PyQt5.QtGui import QStandardItem, QStandardItemModel from PyQt5.QtGui import QStandardItem, QStandardItemModel, QKeySequence
from PyQt5.QtWidgets import QTableView from PyQt5.QtWidgets import QTableView, QShortcut
from PyQt5.QtCore import QTimer, Qt, pyqtSignal from PyQt5.QtCore import Qt, pyqtSignal
from tinytag import TinyTag
from utils import add_files_to_library from utils import add_files_to_library
from utils import get_id3_tags from utils import get_id3_tags
from utils import get_album_art from utils import get_album_art
import logging import logging
import configparser
class MusicTable(QTableView): class MusicTable(QTableView):
@ -19,12 +19,32 @@ class MusicTable(QTableView):
self.selected_song_filepath = None self.selected_song_filepath = None
self.current_song_filepath = None self.current_song_filepath = None
self.qapp = None self.qapp = None
self.config = configparser.ConfigParser()
self.config.read('config.ini')
# self.tableView.resizeColumnsToContents() # self.tableView.resizeColumnsToContents()
self.clicked.connect(self.set_selected_song_filepath) self.clicked.connect(self.set_selected_song_filepath)
# doubleClicked is a built in event for QTableView - we listen for this event and run set_current_song_filepath # doubleClicked is a built in event for QTableView - we listen for this event and run set_current_song_filepath
self.doubleClicked.connect(self.set_current_song_filepath) self.doubleClicked.connect(self.set_current_song_filepath)
self.enterKey.connect(self.set_current_song_filepath) self.enterKey.connect(self.set_current_song_filepath)
self.fetch_library() self.fetch_library()
self.setup_keyboard_shortcuts()
def setup_keyboard_shortcuts(self):
"""Setup shortcuts here"""
shortcut = QShortcut(QKeySequence("Ctrl+Shift+R"), self)
shortcut.activated.connect(self.reorganize_selected_files)
def reorganize_selected_files(self):
"""Ctrl+Shift+R = Reorganize"""
filepaths = self.get_selected_songs_filepaths()
# right now this just prints the filepaths to the songs obv.
print(f'yay: {filepaths}')
# TODO
# Get user confirmation screen (default yes, so i can press enter and go)
# Get target directory
# Copy files to new dir
# delete files from current dir
# add new files to library
def keyPressEvent(self, event): def keyPressEvent(self, event):
"""Press a key. Do a thing""" """Press a key. Do a thing"""
@ -51,34 +71,45 @@ class MusicTable(QTableView):
if not self.current_song_filepath: if not self.current_song_filepath:
self.set_current_song_filepath() self.set_current_song_filepath()
self.playPauseSignal.emit() self.playPauseSignal.emit()
def get_selected_rows(self):
"""Returns a list of indexes for every selected row"""
rows = []
for idx in self.selectionModel().siblingAtColumn():
rows.append(idx.row())
return rows
def set_selected_song_filepath(self): def set_selected_song_filepath(self):
"""Sets the filepath of the currently selected song""" """Sets the filepath of the currently selected song"""
self.selected_song_filepath = self.currentIndex().siblingAtColumn(self.headers.index('path')).data() self.selected_song_filepath = self.currentIndex().siblingAtColumn(self.headers.index('path')).data()
print(f'Selected song: {self.selected_song_filepath}') print(f'Selected song: {self.selected_song_filepath}')
def set_current_song_filepath(self): def set_current_song_filepath(self):
"""Sets the filepath of the currently playing song""" """Sets the filepath of the currently playing song"""
# Setting the current song filepath automatically plays that song # Setting the current song filepath automatically plays that song
# self.tableView listens to this function and plays the audio file located at self.current_song_filepath # self.tableView listens to this function and plays the audio file located at self.current_song_filepath
self.current_song_filepath = self.currentIndex().siblingAtColumn(self.headers.index('path')).data() self.current_song_filepath = self.currentIndex().siblingAtColumn(self.headers.index('path')).data()
print(f'Current song: {self.current_song_filepath}') print(f'Current song: {self.current_song_filepath}')
def get_selected_rows(self):
"""Returns a list of indexes for every selected row"""
selection_model = self.selectionModel()
return [index.row() for index in selection_model.selectedRows()]
# rows = []
# for idx in self.selectionModel().siblingAtColumn():
# rows.append(idx.row())
# return rows
def get_selected_songs_filepaths(self):
"""Returns a list of the filepaths for the currently selected songs"""
selected_rows = self.get_selected_rows()
filepaths = []
for row in selected_rows:
idx = self.model.index(row, self.headers.index('path'))
filepaths.append(idx.data())
return filepaths
def get_selected_song_filepath(self): def get_selected_song_filepath(self):
"""Returns the selected songs filepath""" """Returns the selected songs filepath"""
return self.selected_song_filepath return self.selected_song_filepath
def get_selected_song_metadata(self): def get_selected_song_metadata(self):
"""Returns the selected song's ID3 tags""" """Returns the selected song's ID3 tags"""
return get_id3_tags(self.selected_song_filepath) return get_id3_tags(self.selected_song_filepath)
def get_current_song_filepath(self): def get_current_song_filepath(self):
"""Returns the currently playing song filepath""" """Returns the currently playing song filepath"""
return self.current_song_filepath return self.current_song_filepath
@ -86,12 +117,11 @@ class MusicTable(QTableView):
def get_current_song_metadata(self): def get_current_song_metadata(self):
"""Returns the currently playing song's ID3 tags""" """Returns the currently playing song's ID3 tags"""
return get_id3_tags(self.current_song_filepath) return get_id3_tags(self.current_song_filepath)
def get_current_song_album_art(self): def get_current_song_album_art(self):
"""Returns the APIC data (album art lol) for the currently playing song""" """Returns the APIC data (album art lol) for the currently playing song"""
return get_album_art(self.current_song_filepath) return get_album_art(self.current_song_filepath)
def fetch_library(self): def fetch_library(self):
"""Initialize the tableview model""" """Initialize the tableview model"""
self.model = QStandardItemModel() self.model = QStandardItemModel()
@ -107,7 +137,7 @@ class MusicTable(QTableView):
self.setModel(self.model) self.setModel(self.model)
# self.update() # self.update()
self.viewport().update() self.viewport().update()
def add_files(self, files): def add_files(self, files):
"""When song(s) added to the library, update the tableview model """When song(s) added to the library, update the tableview model
- Drag & Drop song(s) on tableView - Drag & Drop song(s) on tableView
@ -117,11 +147,9 @@ class MusicTable(QTableView):
number_of_files_added = add_files_to_library(files) number_of_files_added = add_files_to_library(files)
if number_of_files_added: if number_of_files_added:
self.fetch_library() self.fetch_library()
def load_qapp(self, qapp): def load_qapp(self, qapp):
# why was this necessary again? :thinking: # why was this necessary again? :thinking:
self.qapp = qapp self.qapp = qapp

45
main.py
View File

@ -1,5 +1,6 @@
from ui import Ui_MainWindow from ui import Ui_MainWindow
from PyQt5.QtWidgets import QMainWindow, QApplication, QGraphicsScene, QHeaderView, QGraphicsPixmapItem from PyQt5.QtWidgets import QMainWindow, QApplication, QGraphicsScene, \
QHeaderView, QGraphicsPixmapItem
import qdarktheme import qdarktheme
from PyQt5.QtCore import QUrl, QTimer, QEvent, Qt from PyQt5.QtCore import QUrl, QTimer, QEvent, Qt
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QAudioProbe from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QAudioProbe
@ -14,6 +15,7 @@ import configparser
# Create ui.py file from Qt Designer # Create ui.py file from Qt Designer
# pyuic5 ui.ui -o ui.py # pyuic5 ui.ui -o ui.py
class ApplicationWindow(QMainWindow, Ui_MainWindow): class ApplicationWindow(QMainWindow, Ui_MainWindow):
def __init__(self, qapp): def __init__(self, qapp):
super(ApplicationWindow, self).__init__() super(ApplicationWindow, self).__init__()
@ -29,24 +31,21 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
self.tableView.load_qapp(self.qapp) self.tableView.load_qapp(self.qapp)
self.config = configparser.ConfigParser() self.config = configparser.ConfigParser()
self.config.read('config.ini') self.config.read('config.ini')
global stopped global stopped
stopped = False stopped = False
# Initialization # Initialization
self.player = QMediaPlayer() # Audio player self.player = QMediaPlayer() # Audio player
self.probe = QAudioProbe() # Get audio data self.probe = QAudioProbe() # Get audio data
self.timer = QTimer(self) # Audio timing things self.timer = QTimer(self) # Audio timing things
# self.model = QStandardItemModel() # Table library listing # self.model = QStandardItemModel() # Table library listing
self.audio_visualizer = AudioVisualizer(self.player) self.audio_visualizer = AudioVisualizer(self.player)
self.current_volume = 50 self.current_volume = 50
self.player.setVolume(self.current_volume) self.player.setVolume(self.current_volume)
# Audio probe for processing audio signal in real time # Audio probe for processing audio signal in real time
# Provides faster updates than move_slider # Provides faster updates than move_slider
self.probe.setSource(self.player) self.probe.setSource(self.player)
self.probe.audioBufferProbed.connect(self.process_probe) self.probe.audioBufferProbed.connect(self.process_probe)
# Slider Timer (realtime playback feedback horizontal bar) # Slider Timer (realtime playback feedback horizontal bar)
self.timer.start(150) # 150ms update interval solved problem with drag seeking halting playback self.timer.start(150) # 150ms update interval solved problem with drag seeking halting playback
self.timer.timeout.connect(self.move_slider) self.timer.timeout.connect(self.move_slider)
@ -56,7 +55,7 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
self.PlotWidget.setYRange(0,0.3,padding=0) # y axis range self.PlotWidget.setYRange(0,0.3,padding=0) # y axis range
self.PlotWidget.getAxis('bottom').setTicks([]) # Remove x-axis ticks self.PlotWidget.getAxis('bottom').setTicks([]) # Remove x-axis ticks
self.PlotWidget.getAxis('bottom').setLabel('') # Remove x-axis label self.PlotWidget.getAxis('bottom').setLabel('') # Remove x-axis label
self.PlotWidget.setLogMode(False,False) self.PlotWidget.setLogMode(False, False)
# Remove y-axis labels and decorations # Remove y-axis labels and decorations
self.PlotWidget.getAxis('left').setTicks([]) # Remove y-axis ticks self.PlotWidget.getAxis('left').setTicks([]) # Remove y-axis ticks
self.PlotWidget.getAxis('left').setLabel('') # Remove y-axis label self.PlotWidget.getAxis('left').setLabel('') # Remove y-axis label
@ -80,7 +79,7 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
self.tableView.playPauseSignal.connect(self.on_play_clicked) # Spacebar toggle play/pause signal self.tableView.playPauseSignal.connect(self.on_play_clicked) # Spacebar toggle play/pause signal
self.tableView.viewport().installEventFilter(self) # for drag & drop functionality self.tableView.viewport().installEventFilter(self) # for drag & drop functionality
# self.tableView.model.layoutChanged() # self.tableView.model.layoutChanged()
### set column widths # set column widths
table_view_column_widths = str(self.config['table']['column_widths']).split(',') table_view_column_widths = str(self.config['table']['column_widths']).split(',')
for i in range(self.tableView.model.columnCount()): for i in range(self.tableView.model.columnCount()):
self.tableView.setColumnWidth(i, int(table_view_column_widths[i])) self.tableView.setColumnWidth(i, int(table_view_column_widths[i]))
@ -88,15 +87,14 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.tableView.horizontalHeader().setStretchLastSection(False) self.tableView.horizontalHeader().setStretchLastSection(False)
def eventFilter(self, source, event): def eventFilter(self, source, event):
"""Handles events""" """Handles events"""
# tableView (drag & drop) # tableView (drag & drop)
if (source is self.tableView.viewport() and if (source is self.tableView.viewport() and
(event.type() == QEvent.DragEnter or (event.type() == QEvent.DragEnter or
event.type() == QEvent.DragMove or event.type() == QEvent.DragMove or
event.type() == QEvent.Drop) and event.type() == QEvent.Drop) and
event.mimeData().hasUrls()): event.mimeData().hasUrls()):
files = [] files = []
if event.type() == QEvent.Drop: if event.type() == QEvent.Drop:
for url in event.mimeData().urls(): for url in event.mimeData().urls():
@ -206,7 +204,7 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
self.startTimeLabel.setText(f"{int(current_minutes):02d}:{int(current_seconds):02d}") self.startTimeLabel.setText(f"{int(current_minutes):02d}:{int(current_seconds):02d}")
self.endTimeLabel.setText(f"{int(duration_minutes):02d}:{int(duration_seconds):02d}") self.endTimeLabel.setText(f"{int(duration_minutes):02d}:{int(duration_seconds):02d}")
def volume_changed(self): def volume_changed(self):
"""Handles volume changes""" """Handles volume changes"""
try: try:
@ -214,7 +212,7 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
self.player.setVolume(self.current_volume) self.player.setVolume(self.current_volume)
except Exception as e: except Exception as e:
print(f"Changing volume error: {e}") print(f"Changing volume error: {e}")
def on_play_clicked(self): def on_play_clicked(self):
"""Updates the Play & Pause buttons when clicked""" """Updates the Play & Pause buttons when clicked"""
if self.player.state() == QMediaPlayer.PlayingState: if self.player.state() == QMediaPlayer.PlayingState:
@ -227,31 +225,30 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
else: else:
self.play_audio_file() self.play_audio_file()
self.playButton.setText("⏸️") self.playButton.setText("⏸️")
def on_previous_clicked(self): def on_previous_clicked(self):
"""""" """"""
print('previous') print('previous')
def on_next_clicked(self): def on_next_clicked(self):
print('next') print('next')
def actionPreferencesClicked(self): def actionPreferencesClicked(self):
preferences_window = PreferencesWindow(self.config) preferences_window = PreferencesWindow(self.config)
preferences_window.exec_() # Display the preferences window modally preferences_window.exec_() # Display the preferences window modally
def scan_libraries(self): def scan_libraries(self):
scan_for_music() scan_for_music()
self.tableView.fetch_library() self.tableView.fetch_library()
def clear_database(self): def clear_database(self):
initialize_library_database() initialize_library_database()
self.tableView.fetch_library() self.tableView.fetch_library()
def process_probe(self, buff): def process_probe(self, buff):
buff.startTime() buff.startTime()
self.update_audio_visualization() self.update_audio_visualization()
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys