ErrorDialog window geometry fix, Lyrics window simplification, PlaylistsPane no longer triggers cell data changed event, PlaylistsPane open root nodes by default

This commit is contained in:
billypom on debian 2024-08-06 19:24:00 -04:00
parent c9b78db5b1
commit dcd0844d99
7 changed files with 76 additions and 46 deletions

View File

@ -1,6 +1,6 @@
# MusicPom
PyQt5 music player inspired by MusicBee & iTunes
PyQt5 music player for Linux inspired by MusicBee & iTunes
## Installation:
clone the repo
@ -27,9 +27,9 @@ python3 main.py
## Todo:
- [x] Right-click menu
- [x] Editable lyrics textbox
- [ ] Delete songs from library (del key || right-click delete)
- [x] right-click menu
- [x] editable lyrics window
- [ ] playlists
- [ ] delete songs from library (del key || right-click delete)
- [ ] .wav, .ogg, .flac convertor
- [ ] batch metadata changer (red text on fields that have differing info)
- [ ] Alternatives to Gstreamer?

View File

@ -1,10 +1,10 @@
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QPushButton
class ErrorDialog(QDialog):
def __init__(self, message, parent=None):
super().__init__(parent)
self.setWindowTitle("An error occurred")
self.setGeometry(100,100,400,200)
layout = QVBoxLayout()
self.label = QLabel(message)

View File

@ -6,26 +6,20 @@ from PyQt5.QtWidgets import (
QPushButton,
)
from PyQt5.QtGui import QFont
from components.ErrorDialog import ErrorDialog
from utils import set_id3_tag
class LyricsWindow(QDialog):
def __init__(self, song_filepath, lyrics):
def __init__(self, song_filepath: str, lyrics: str):
super(LyricsWindow, self).__init__()
self.setWindowTitle("Lyrics")
self.lyrics = lyrics
self.song_filepath = song_filepath
self.input_field = "empty"
self.setMinimumSize(400, 400)
self.lyrics: str = lyrics
self.song_filepath: str = song_filepath
layout = QVBoxLayout()
# label = QLabel("Lyrics")
# layout.addWidget(label)
# Labels & input fields
self.input_fields = {}
lyrics_label = QLabel("Lyrics")
lyrics_label.setFont(QFont("Sans", weight=QFont.Bold)) # bold category
lyrics_label.setStyleSheet("text-transform:uppercase;") # uppercase category
layout.addWidget(lyrics_label)
self.input_field = QPlainTextEdit(self.lyrics)
layout.addWidget(self.input_field)
@ -44,6 +38,9 @@ class LyricsWindow(QDialog):
)
if success:
print("success! yay")
error_dialog = ErrorDialog("Could not save lyrics :( sad")
error_dialog.exec()
else:
print("NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN")
error_dialog = ErrorDialog("Could not save lyrics :( sad")
error_dialog.exec()
self.close()

View File

@ -17,6 +17,7 @@ from PyQt5.QtWidgets import (
QAbstractItemView,
)
from PyQt5.QtCore import QAbstractItemModel, QModelIndex, Qt, pyqtSignal, QTimer
from components.ErrorDialog import ErrorDialog
from components.LyricsWindow import LyricsWindow
from components.AddToPlaylistWindow import AddToPlaylistWindow
from utils.delete_song_id_from_database import delete_song_id_from_database
@ -42,7 +43,9 @@ class MusicTable(QTableView):
super().__init__(parent)
# Necessary for actions related to cell values
self.model = QStandardItemModel(self)
self.setModel(self.model) # Same as above
self.setModel(self.model)
# Config
self.config = configparser.ConfigParser()
self.config.read("config.ini")
# gui names of headers
@ -81,11 +84,12 @@ class MusicTable(QTableView):
self.doubleClicked.connect(self.set_current_song_filepath)
self.enterKey.connect(self.set_current_song_filepath)
self.deleteKey.connect(self.delete_songs)
self.load_music_table()
self.setup_keyboard_shortcuts()
self.model.dataChanged.connect(self.on_cell_data_changed) # editing cells
self.model.layoutChanged.connect(self.restore_scroll_position)
self.load_music_table()
self.setup_keyboard_shortcuts()
def contextMenuEvent(self, event):
"""Right-click context menu for rows in Music Table"""
menu = QMenu(self)
@ -181,10 +185,12 @@ class MusicTable(QTableView):
if selected_song_filepath is None:
return
current_song = self.get_selected_song_metadata()
# print(f"MusicTable.py | show_lyrics_menu | current song: {current_song}")
try:
# Have to use USLT::XXX to retrieve
lyrics = current_song["USLT::XXX"].text
uslt_tags = [tag for tag in current_song.keys() if tag.startswith("USLT::")]
if uslt_tags:
lyrics = next((current_song[tag].text for tag in uslt_tags), "")
else:
raise RuntimeError("No USLT tags found in song metadata")
except Exception as e:
print(f"MusicTable.py | show_lyrics_menu | could not retrieve lyrics | {e}")
lyrics = ""
@ -292,15 +298,24 @@ class MusicTable(QTableView):
try:
# Read file metadata
audio = ID3(filepath)
artist = (
audio["TPE1"].text[0] if not "" or None else "Unknown Artist"
)
album = audio["TALB"].text[0] if not "" or None else "Unknown Album"
try:
artist = audio["TPE1"].text[0]
if artist == "":
artist = "Unknown Artist"
except KeyError:
artist = "Unknown Artist"
try:
album = audio["TALB"].text[0]
if album == "":
album = "Unknown Album"
except KeyError:
album = "Unknown Album"
# Determine the new path that needs to be made
new_path = os.path.join(
target_dir, artist, album, os.path.basename(filepath)
)
print(new_path)
# Create the directories if they dont exist
os.makedirs(os.path.dirname(new_path), exist_ok=True)
# Move the file to the new directory
@ -313,11 +328,16 @@ class MusicTable(QTableView):
)
print(f"Moved: {filepath} -> {new_path}")
except Exception as e:
print(f"Error moving file: {filepath} | {e}")
logging.warning(
f"MusicTable.py reorganize_selected_files() | Error moving file: {filepath} | {e}"
)
print(
f"MusicTable.py reorganize_selected_files() | Error moving file: {filepath} | {e}"
)
# Draw the rest of the owl
self.model.dataChanged.disconnect(self.on_cell_data_changed)
# self.model.dataChanged.disconnect(self.on_cell_data_changed)
self.load_music_table()
self.model.dataChanged.connect(self.on_cell_data_changed)
# self.model.dataChanged.connect(self.on_cell_data_changed)
QMessageBox.information(
self, "Reorganization complete", "Files successfully reorganized"
)
@ -329,7 +349,21 @@ class MusicTable(QTableView):
self.playPauseSignal.emit()
def load_music_table(self, *playlist_id):
"""Initializes the tableview model"""
"""
Loads data into self (QTableView)
Default to loading all songs.
If playlist_id is given, load songs in a particular playlist
"""
try:
# Loading the table also causes cell data to change, technically
# so we must disconnect the dataChanged trigger before loading
# then re-enable after we are done loading
self.model.dataChanged.disconnect(self.on_cell_data_changed)
except Exception as e:
print(
f"MusicTable.py load_music_table() | could not disconnect on_cell_data_changed trigger: {e}"
)
pass
self.vertical_scroll_position = (
self.verticalScrollBar().value()
) # Get my scroll position before clearing
@ -339,9 +373,6 @@ class MusicTable(QTableView):
if playlist_id:
playlist_id = playlist_id[0]
# Fetch playlist data
print(
f"MusicTable.py load_music_table() | fetching playlist data, playlist_id: {playlist_id}"
)
try:
with DBA.DBAccess() as db:
data = db.query(
@ -354,7 +385,6 @@ class MusicTable(QTableView):
)
return
else:
print("MusicTable.py load_music_table() | fetching library data")
# Fetch library data
try:
with DBA.DBAccess() as db:
@ -376,11 +406,15 @@ class MusicTable(QTableView):
# row = self.model.rowCount() - 1
for item in items:
item.setData(id, Qt.UserRole)
# Update the viewport/model
self.model.layoutChanged.emit() # emits a signal that the view should be updated
try:
self.model.dataChanged.connect(self.on_cell_data_changed)
except Exception:
pass
def restore_scroll_position(self) -> None:
"""Restores the scroll position"""
print("restore_scroll_position")
QTimer.singleShot(
100,
lambda: self.verticalScrollBar().setValue(self.vertical_scroll_position),

View File

@ -11,6 +11,7 @@ class PlaylistWidgetItem(QTreeWidgetItem):
class PlaylistsPane(QTreeWidget):
playlistChoiceSignal = pyqtSignal(int)
allSongsSignal = pyqtSignal()
def __init__(self: QTreeWidget, parent=None):
super().__init__(parent)
@ -27,6 +28,9 @@ class PlaylistsPane(QTreeWidget):
branch = PlaylistWidgetItem(self, playlist[0], playlist[1])
playlists_root.addChild(branch)
library_root.setExpanded(True)
playlists_root.setExpanded(True)
self.currentItemChanged.connect(self.playlist_clicked)
self.playlist_db_id_choice: int | None = None
@ -39,4 +43,4 @@ class PlaylistsPane(QTreeWidget):
self.all_songs_selected()
def all_songs_selected(self):
print("all songs")
self.allSongsSignal.emit()

View File

@ -121,6 +121,7 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
self.playlistTreeView.playlistChoiceSignal.connect(
self.tableView.load_music_table
)
self.playlistTreeView.allSongsSignal.connect(self.tableView.load_music_table)
# albumGraphicsView
self.albumGraphicsView.albumArtDropped.connect(

View File

@ -38,28 +38,22 @@ def get_id3_tags(file):
try:
# Open the MP3 file and read its content
audio = ID3(file)
print("1")
if os.path.exists(file):
audio.save(os.path.abspath(file))
print("a")
# If 'TIT2' tag is not set, add it with a default value (title will be the filename without extension)
title = os.path.splitext(os.path.basename(file))[0]
for key in list(audio.keys()):
if key == "TIT2":
print("key = tit2")
audio[key].text[0] = title
break
else:
tit2_tag = TIT2(encoding=3, text=[title])
audio["TIT2"] = tit2_tag
print("b")
# Save the updated tags
audio.save()
print("c")
except Exception as e:
print(f"get_id3_tags.py | Could not assign file ID3 tag: {e}")