auto export playlist works!!!

This commit is contained in:
billy 2025-09-29 22:43:26 -04:00
parent 3a9f7a27d3
commit dc4f7a4cbc
9 changed files with 188 additions and 17 deletions

View File

@ -0,0 +1,85 @@
import DBA
from PyQt5.QtWidgets import (
QDialog,
QLineEdit,
QLabel,
QPushButton,
QVBoxLayout,
)
from PyQt5.QtGui import QFont
class EditPlaylistOptionsWindow(QDialog):
def __init__(self, playlist_id):
super(EditPlaylistOptionsWindow, self).__init__()
self.setWindowTitle("Playlist options")
self.setMinimumSize(600, 400)
self.playlist_id = playlist_id
# self.playlist_path_prefix: str = self.config.get(
# "settings", "playlist_path_prefix"
# )
# self.export_path: str = self.config.get("settings", "playlist_export_path")
# self.selected_playlist_name: str = "my-playlist.m3u"
# self.chosen_list_widget_item: QListWidgetItem | None = None
layout = self.setup_ui()
self.setLayout(layout)
self.show()
def setup_ui(self):
layout = QVBoxLayout()
# Header label
label = QLabel("Options")
label.setFont(QFont("Sans", weight=QFont.Bold))
layout.addWidget(label)
# Get options from db
with DBA.DBAccess() as db:
data = db.query("SELECT auto_export_path, path_prefix from playlist WHERE id = ?;", (self.playlist_id,))
auto_export_path = data[0][0]
path_prefix = data[0][1]
# Relative export path label
label = QLabel("Auto export path (../music/playlists/my_playlist.m3u)")
label.setFont(QFont("Sans", weight=QFont.Bold)) # bold category
label.setStyleSheet("text-transform:lowercase;") # uppercase category
layout.addWidget(label)
# Relative export path line edit widget
self.auto_export_path = QLineEdit(auto_export_path)
layout.addWidget(self.auto_export_path)
# Playlist file save path label
label = QLabel("Path prefix (/prefix/song.mp3, /prefix/song2.mp3)")
label.setFont(QFont("Sans", weight=QFont.Bold))
label.setStyleSheet("text-transform:lowercase;")
layout.addWidget(label)
# Playlist file save path line edit widget
self.path_prefix = QLineEdit(path_prefix)
layout.addWidget(self.path_prefix)
# Save button
self.save_button = QPushButton("Save")
layout.addWidget(self.save_button)
# Signals
self.save_button.clicked.connect(self.save)
return layout
def save(self) -> None:
"""
Updates the options in the db
"""
with DBA.DBAccess() as db:
db.execute('''
UPDATE playlist SET auto_export_path = ?, path_prefix = ? WHERE id = ?
''', (self.auto_export_path.text(), self.path_prefix.text(), self.playlist_id)
)
self.close()
return
def cancel(self) -> None:
self.close()
return

View File

@ -4,7 +4,6 @@ import os
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QCheckBox, QCheckBox,
QDialog, QDialog,
QHBoxLayout,
QLineEdit, QLineEdit,
QLabel, QLabel,
QPushButton, QPushButton,
@ -26,8 +25,8 @@ class ExportPlaylistWindow(QDialog):
self.setWindowTitle("Export playlist") self.setWindowTitle("Export playlist")
self.setMinimumSize(600, 400) self.setMinimumSize(600, 400)
self.load_config() self.load_config()
self.relative_path: str = self.config.get( self.playlist_path_prefix: str = self.config.get(
"settings", "playlist_content_relative_path" "settings", "playlist_path_prefix"
) )
self.export_path: str = self.config.get("settings", "playlist_export_path") self.export_path: str = self.config.get("settings", "playlist_export_path")
self.selected_playlist_name: str = "my-playlist.m3u" self.selected_playlist_name: str = "my-playlist.m3u"
@ -74,7 +73,7 @@ class ExportPlaylistWindow(QDialog):
layout.addWidget(label) layout.addWidget(label)
# Relative export path line edit widget # Relative export path line edit widget
self.input_relative_path = QLineEdit(self.relative_path) # not needed self.input_relative_path = QLineEdit(self.playlist_path_prefix) # not needed
layout.addWidget(self.input_relative_path) layout.addWidget(self.input_relative_path)
# Playlist file save path label # Playlist file save path label
@ -112,9 +111,7 @@ class ExportPlaylistWindow(QDialog):
Sets the current playlist name, then edits the playlist export path Sets the current playlist name, then edits the playlist export path
""" """
# Get the current chosen list item # Get the current chosen list item
self.chosen_list_widget_item: QListWidgetItem | None = ( self.chosen_list_widget_item = self.playlist_listWidget.currentItem()
self.playlist_listWidget.currentItem()
)
# We don't care if nothing is chosen # We don't care if nothing is chosen
if self.chosen_list_widget_item is None: if self.chosen_list_widget_item is None:
return return
@ -129,13 +126,13 @@ class ExportPlaylistWindow(QDialog):
# alter line edit text for playlist path # alter line edit text for playlist path
# Make the thing react and show the correct thing or whatever # Make the thing react and show the correct thing or whatever
current_text: list = self.input_m3u_path.text().split("/") current_text: list = self.export_m3u_path.text().split("/")
if "." in current_text[-1]: if "." in current_text[-1]:
current_text = current_text[:-1] current_text = current_text[:-1]
else: else:
current_text = current_text current_text = current_text
m3u_text = os.path.join("/", *current_text, self.selected_playlist_name) m3u_text = os.path.join("/", *current_text, self.selected_playlist_name)
self.input_m3u_path.setText(m3u_text) self.export_m3u_path.setText(m3u_text)
def save(self) -> None: def save(self) -> None:
""" """
@ -145,7 +142,7 @@ class ExportPlaylistWindow(QDialog):
if self.chosen_list_widget_item is None: if self.chosen_list_widget_item is None:
return return
relative_path = self.input_relative_path.text() relative_path = self.input_relative_path.text()
output_filename = self.input_m3u_path.text() output_filename = self.export_m3u_path.text()
# If no output path is provided, just close the window... # If no output path is provided, just close the window...
if output_filename == "" or output_filename is None: if output_filename == "" or output_filename is None:

View File

@ -6,13 +6,12 @@ from PyQt5.QtWidgets import (
QTreeWidget, QTreeWidget,
QTreeWidgetItem, QTreeWidgetItem,
) )
from PyQt5.QtCore import QThreadPool, pyqtSignal, Qt, QPoint from PyQt5.QtCore import pyqtSignal, Qt, QPoint
import DBA import DBA
from logging import debug
from components.ErrorDialog import ErrorDialog from components.ErrorDialog import ErrorDialog
from utils import Worker from utils import Worker
from components import CreatePlaylistWindow from components import CreatePlaylistWindow, EditPlaylistOptionsWindow
class PlaylistWidgetItem(QTreeWidgetItem): class PlaylistWidgetItem(QTreeWidgetItem):
@ -70,10 +69,13 @@ class PlaylistsPane(QTreeWidget):
# only allow delete/rename non-root nodes # only allow delete/rename non-root nodes
rename_action = QAction("Rename", self) rename_action = QAction("Rename", self)
delete_action = QAction("Delete", self) delete_action = QAction("Delete", self)
options_action = QAction("Options", self)
rename_action.triggered.connect(self.rename_playlist) rename_action.triggered.connect(self.rename_playlist)
delete_action.triggered.connect(self.delete_playlist) delete_action.triggered.connect(self.delete_playlist)
options_action.triggered.connect(self.options)
menu.addAction(rename_action) menu.addAction(rename_action)
menu.addAction(delete_action) menu.addAction(delete_action)
menu.addAction(options_action)
create_action = QAction("New playlist", self) create_action = QAction("New playlist", self)
create_action.triggered.connect(self.create_playlist) create_action.triggered.connect(self.create_playlist)
menu.addAction(create_action) menu.addAction(create_action)
@ -85,6 +87,10 @@ class PlaylistsPane(QTreeWidget):
window.playlistCreatedSignal.connect(self.reload_playlists) window.playlistCreatedSignal.connect(self.reload_playlists)
window.exec_() window.exec_()
def options(self):
window = EditPlaylistOptionsWindow(self.playlist_db_id_choice)
window.exec_()
def rename_playlist(self, *args): def rename_playlist(self, *args):
""" """
Asks user for input Asks user for input

View File

@ -6,6 +6,7 @@ from .ErrorDialog import ErrorDialog
from .LyricsWindow import LyricsWindow from .LyricsWindow import LyricsWindow
from .DebugWindow import DebugWindow from .DebugWindow import DebugWindow
from .AddToPlaylistWindow import AddToPlaylistWindow from .AddToPlaylistWindow import AddToPlaylistWindow
from .EditPlaylistOptionsWindow import EditPlaylistOptionsWindow
from .CreatePlaylistWindow import CreatePlaylistWindow from .CreatePlaylistWindow import CreatePlaylistWindow
from .PlaylistsPane import PlaylistsPane from .PlaylistsPane import PlaylistsPane
from .ExportPlaylistWindow import ExportPlaylistWindow from .ExportPlaylistWindow import ExportPlaylistWindow

14
main.py
View File

@ -4,7 +4,7 @@ import logging
from PyQt5 import QtCore from PyQt5 import QtCore
import qdarktheme import qdarktheme
import typing import typing
# import DBA import DBA
from subprocess import run from subprocess import run
# from pyqtgraph import mkBrush # from pyqtgraph import mkBrush
from mutagen.id3 import ID3 from mutagen.id3 import ID3
@ -60,6 +60,7 @@ from components import (
CreatePlaylistWindow, CreatePlaylistWindow,
ExportPlaylistWindow, ExportPlaylistWindow,
) )
from utils.export_playlist_by_id import export_playlist_by_id
# good help with signals slots in threads # good help with signals slots in threads
# https://stackoverflow.com/questions/52993677/how-do-i-setup-signals-and-slots-in-pyqt-with-qthreads-in-both-directions # https://stackoverflow.com/questions/52993677/how-do-i-setup-signals-and-slots-in-pyqt-with-qthreads-in-both-directions
@ -246,13 +247,22 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
self.config["settings"]["window_size"] = ( self.config["settings"]["window_size"] = (
str(self.width()) + "," + str(self.height()) str(self.width()) + "," + str(self.height())
) )
# Save the config # Save the config
try: try:
with open(self.cfg_file, "w") as configfile: with open(self.cfg_file, "w") as configfile:
self.config.write(configfile) self.config.write(configfile)
except Exception as e: except Exception as e:
debug(f"wtf man {e}") debug(f"wtf man {e}")
# auto export any playlists that want it
try:
with DBA.DBAccess() as db:
result = db.query('SELECT id FROM playlist WHERE auto_export_path IS NOT NULL;', ())
ids = [id[0] for id in result]
for id in ids:
export_playlist_by_id(id)
except Exception:
pass
if a0 is not None: if a0 is not None:
super().closeEvent(a0) super().closeEvent(a0)

View File

@ -4,7 +4,7 @@ library = /path/to/music
reorganize_destination = /where/to/reorganize/to reorganize_destination = /where/to/reorganize/to
playlist_auto_export_enabled = 0 playlist_auto_export_enabled = 0
playlist_export_path = /where/to/export/to playlist_export_path = /where/to/export/to
playlist_relative_path = /relative/prefix playlist_path_prefix = /relative/prefix
extensions = mp3,wav,ogg,flac extensions = mp3,wav,ogg,flac
volume = 100 volume = 100
window_size=1152,894 window_size=1152,894

View File

@ -17,3 +17,4 @@ from .convert_date_str_to_tyer_tdat_id3_tag import convert_date_str_to_tyer_tdat
from .set_album_art import set_album_art from .set_album_art import set_album_art
from .delete_album_art import delete_album_art from .delete_album_art import delete_album_art
from .Worker import Worker from .Worker import Worker
from .export_playlist_by_id import export_playlist_by_id

View File

@ -0,0 +1,69 @@
import os
from PyQt5.QtCore import QThreadPool
import DBA
import logging
from utils import get_reorganize_vars, Worker
def export_playlist_by_id(playlist_db_id: int) -> bool:
"""
Exports a playlist to its defined auto_export_path, by database ID
"""
threadpool = QThreadPool()
try:
with DBA.DBAccess() as db:
result = db.query('''
SELECT auto_export_path, path_prefix FROM playlist WHERE id = ?
''', (playlist_db_id,)
)
auto_export_path = result[0][0]
path_prefix = result[0][1]
except Exception as e:
logging.error(
f"export_playlist_by_id.py | Could not contact database: {e}"
)
return False
# If the prefix is null, then use an empty string
if not path_prefix:
path_prefix = ""
# Get filepaths for selected playlist from the database
try:
with DBA.DBAccess() as db:
data = db.query(
"""SELECT s.filepath FROM song_playlist as sp
JOIN song as s ON s.id = sp.song_id
WHERE sp.playlist_id = ?;""",
(playlist_db_id,),
)
db_paths = [path[0] for path in data]
except Exception as e:
logging.error(
f"export_playlist_by_id.py | Could not retrieve records from playlist: {e}"
)
return False
# Gather playlist song paths
write_paths = []
# Relative paths
for song in db_paths:
artist, album = get_reorganize_vars(song)
write_path = os.path.join(
path_prefix, artist, album, song.split("/")[-1] + "\n"
)
write_paths.append(str(write_path))
worker = Worker(write_to_playlist_file, write_paths, auto_export_path)
# worker.signals.signal_finished.connect(None)
# worker.signals.signal_progress.connect()
threadpool.start(worker)
return True
def write_to_playlist_file(paths: list[str], outfile: str, progress_callback=None) -> None:
"""
Writes a list of strings to a m3u file
"""
os.makedirs(os.path.dirname(outfile), exist_ok=True)
with open(outfile, "w") as f:
f.writelines(paths)

View File

@ -22,7 +22,9 @@ CREATE TABLE song(
CREATE TABLE playlist( CREATE TABLE playlist(
id integer primary key, id integer primary key,
name varchar(64), name varchar(64),
date_created TIMESTAMP default CURRENT_TIMESTAMP date_created TIMESTAMP default CURRENT_TIMESTAMP,
auto_export_path varchar(512),
path_prefix varchar(255)
); );
CREATE TABLE song_playlist( CREATE TABLE song_playlist(