export playlist working

This commit is contained in:
billypom on debian 2025-07-13 20:57:53 -04:00
parent 68e064b29f
commit be9f17a89d
4 changed files with 76 additions and 40 deletions

View File

@ -25,20 +25,19 @@ class ExportPlaylistWindow(QDialog):
super(ExportPlaylistWindow, self).__init__() super(ExportPlaylistWindow, self).__init__()
self.setWindowTitle("Export playlist") self.setWindowTitle("Export playlist")
self.setMinimumSize(600, 400) self.setMinimumSize(600, 400)
self.cfg_file = ( self.load_config()
Path(user_config_dir(appname="musicpom", appauthor="billypom"))
/ "config.ini"
)
self.config = ConfigParser()
self.config.read(self.cfg_file)
self.relative_path: str = self.config.get( self.relative_path: str = self.config.get(
"directories", "playlist_relative_path" "directories", "playlist_relative_path"
) )
self.current_relative_path: str = self.relative_path
self.export_path: str = self.config.get("directories", "playlist_export_path") self.export_path: str = self.config.get("directories", "playlist_export_path")
self.selected_playlist_name: str = "my-playlist.m3u" self.selected_playlist_name: str = "my-playlist.m3u"
self.current_m3u_path: str = self.export_path
self.current_relative_path: str = self.relative_path
self.chosen_list_widget_item: QListWidgetItem | None = None self.chosen_list_widget_item: QListWidgetItem | None = None
layout = self.setup_ui()
self.setLayout(layout)
self.show()
def setup_ui(self):
layout = QVBoxLayout() layout = QVBoxLayout()
# Header label # Header label
@ -54,15 +53,14 @@ class ExportPlaylistWindow(QDialog):
playlist_dict[row[0]] = row[1] playlist_dict[row[0]] = row[1]
# Playlist list widget # Playlist list widget
self.item_dict = {}
self.playlist_listWidget = QListWidget(self) self.playlist_listWidget = QListWidget(self)
self.item_dict = {}
for i, (k, v) in enumerate(playlist_dict.items()): for i, (k, v) in enumerate(playlist_dict.items()):
# item_text = f"{i+1} | {v}" # item_text = f"{i+1} | {v}"
item_text = v item_text = v
item = QListWidgetItem(item_text) item = QListWidgetItem(item_text)
self.playlist_listWidget.addItem(item) self.playlist_listWidget.addItem(item)
self.item_dict[item_text] = k self.item_dict[item_text] = k
layout.addWidget(self.playlist_listWidget) layout.addWidget(self.playlist_listWidget)
# Relative path checkbox widget # Relative path checkbox widget
@ -77,8 +75,8 @@ class ExportPlaylistWindow(QDialog):
layout.addWidget(label) layout.addWidget(label)
# Relative export path line edit widget # Relative export path line edit widget
self.relative_path_input = QLineEdit(self.relative_path) self.input_relative_path = QLineEdit(self.relative_path) # not needed
layout.addWidget(self.relative_path_input) layout.addWidget(self.input_relative_path)
# Playlist file save path label # Playlist file save path label
label = QLabel("Playlist file path") label = QLabel("Playlist file path")
@ -87,8 +85,8 @@ class ExportPlaylistWindow(QDialog):
layout.addWidget(label) layout.addWidget(label)
# Playlist file save path line edit widget # Playlist file save path line edit widget
self.m3u_path_input = QLineEdit(self.export_path) self.input_m3u_path = QLineEdit(self.export_path) # not needed
layout.addWidget(self.m3u_path_input) layout.addWidget(self.input_m3u_path)
# Save button # Save button
self.save_button = QPushButton("Export") self.save_button = QPushButton("Export")
@ -96,14 +94,21 @@ class ExportPlaylistWindow(QDialog):
# Signals # Signals
self.save_button.clicked.connect(self.save) self.save_button.clicked.connect(self.save)
self.checkbox.toggled.connect(self.relative_path_input.setEnabled) self.checkbox.toggled.connect(self.input_relative_path.setEnabled)
self.playlist_listWidget.currentRowChanged.connect( self.playlist_listWidget.currentRowChanged.connect(
self.set_current_selected_playlist self.handle_playlist_selected
) )
self.setLayout(layout) return layout
self.show()
def set_current_selected_playlist(self) -> None: def load_config(self):
self.cfg_file = (
Path(user_config_dir(appname="musicpom", appauthor="billypom"))
/ "config.ini"
)
self.config = ConfigParser()
self.config.read(self.cfg_file)
def handle_playlist_selected(self) -> None:
""" """
Sets the current playlist name, then edits the playlist export path Sets the current playlist name, then edits the playlist export path
""" """
@ -117,34 +122,45 @@ class ExportPlaylistWindow(QDialog):
# Create the filename for the playlist to be exported # Create the filename for the playlist to be exported
self.selected_playlist_name = self.chosen_list_widget_item.text() + ".m3u" self.selected_playlist_name = self.chosen_list_widget_item.text() + ".m3u"
# get the db id? i guess?
self.selected_playlist_db_id = self.item_dict[
self.chosen_list_widget_item.text()
]
# Change line edit text for playlist path # alter line edit text for playlist path
current_text: list = self.m3u_path_input.text().split("/") current_text: list = self.input_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.m3u_path_input.setText(m3u_text) self.input_m3u_path.setText(m3u_text)
def save(self) -> None: def save(self) -> None:
"""Exports the chosen database playlist to a .m3u file""" """
Exports the selected playlist to a .m3u file
- handles writing relative paths, if needed
"""
if self.chosen_list_widget_item is None: if self.chosen_list_widget_item is None:
return return
selected_db_id = self.item_dict[self.chosen_list_widget_item.text()] relative_path = self.input_relative_path.text()
relative_path = self.relative_path_input.text() output_filename = self.input_m3u_path.text()
m3u_path = self.m3u_path_input.text()
# If no output path is provided, just close the window... # If no output path is provided, just close the window...
if m3u_path == "" or m3u_path is None: if output_filename == "" or output_filename is None:
self.close() self.close()
return return
# Get filepaths for selected playlist from the database
try: try:
with DBA.DBAccess() as db: with DBA.DBAccess() as db:
db_paths = db.query( 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 = ?;", """SELECT s.filepath FROM song_playlist as sp
(selected_db_id,), JOIN song as s ON s.id = sp.song_id
)[0] WHERE sp.playlist_id = ?;""",
(int(self.selected_playlist_db_id),),
)
db_paths = [path[0] for path in data]
except Exception as e: except Exception as e:
logging.error( logging.error(
f"ExportPlaylistWindow.py save() | could not retrieve playlist songs: {e}" f"ExportPlaylistWindow.py save() | could not retrieve playlist songs: {e}"
@ -163,18 +179,17 @@ class ExportPlaylistWindow(QDialog):
for song in db_paths: for song in db_paths:
artist, album = get_reorganize_vars(song) artist, album = get_reorganize_vars(song)
write_path = os.path.join( write_path = os.path.join(
relative_path, artist, album, song.split("/")[-1] relative_path, artist, album, song.split("/")[-1], "\n"
) )
write_paths.append(write_path) write_paths.append(str(write_path))
else: else:
# Normal paths # Normal paths
for song in db_paths: for song in db_paths:
write_paths.append(song) write_paths.append(song)
# Write playlist file # Write playlist file TODO: add threading
filename = self.m3u_path_input.text() os.makedirs(os.path.dirname(output_filename), exist_ok=True)
os.makedirs(os.path.dirname(filename), exist_ok=True) with open(output_filename, "w") as f:
with open(filename, "w") as f:
f.writelines(write_paths) f.writelines(write_paths)
self.close() self.close()

View File

@ -71,6 +71,22 @@ class MusicTable(QTableView):
handleProgressSignal = pyqtSignal(str) handleProgressSignal = pyqtSignal(str)
getThreadPoolSignal = pyqtSignal() getThreadPoolSignal = pyqtSignal()
searchBoxSignal = pyqtSignal() searchBoxSignal = pyqtSignal()
focusEnterSignal = pyqtSignal()
focusLeaveSignal = pyqtSignal()
def focusInEvent(self, e):
"""
Event filter: when self is focused
"""
self.focusEnterSignal.emit()
# arrow keys act normal
super().focusInEvent(e)
def focusOutEvent(self, e):
"""
Event filter: when self becomes unfocused
"""
self.focusLeaveSignal.emit()
def __init__(self, parent=None, application_window=None): def __init__(self, parent=None, application_window=None):
super().__init__(parent) super().__init__(parent)
@ -766,7 +782,12 @@ class MusicTable(QTableView):
# print(real_index) # print(real_index)
self.current_song_qmodel_index = real_index self.current_song_qmodel_index = real_index
self.model2.layoutChanged.emit() # emits a signal that the view should be updated self.model2.layoutChanged.emit() # emits a signal that the view should be updated
self.playlistStatsSignal.emit(f"Songs: {row_count} | Total time: {total_time}")
db_name: str = self.config.get("db", "database").split("/").pop()
db_filename = self.config.get("db", "database")
self.playlistStatsSignal.emit(
f"Songs: {row_count} | Total time: {total_time} | {db_name} | {db_filename}"
)
self.loadMusicTableSignal.emit() self.loadMusicTableSignal.emit()
self.connect_data_changed() self.connect_data_changed()
self.connect_layout_changed() self.connect_layout_changed()

View File

@ -702,8 +702,8 @@ def update_database_file() -> bool:
with open(cfg_file, "w") as configfile: with open(cfg_file, "w") as configfile:
config.write(configfile) config.write(configfile)
config.read(cfg_file) config.read(cfg_file)
db_filepath: str = config.get("db", "database")
db_filepath: str = config.get("db", "database")
db_path = db_filepath.split("/") db_path = db_filepath.split("/")
db_path.pop() db_path.pop()
db_path = "/".join(db_path) db_path = "/".join(db_path)

View File

@ -4,7 +4,7 @@ from mutagen.id3 import ID3
def get_reorganize_vars(filepath: str) -> tuple[str, str]: def get_reorganize_vars(filepath: str) -> tuple[str, str]:
""" """
Takes in a path to an audio file Takes in a path to an audio file
returns the (artisr, album) as a tuple of strings returns the (artist, album) as a tuple of strings
if no artist or album or ID3 tags at all are found, if no artist or album or ID3 tags at all are found,
function will return ("Unknown Artist", "Unknown Album") function will return ("Unknown Artist", "Unknown Album")