stuffg
This commit is contained in:
parent
511b118790
commit
69ce4bc763
@ -7,16 +7,16 @@ from typing import Optional
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class SQLiteMap:
|
class SQLiteMap:
|
||||||
title: Optional[str] = None
|
title: str | None = None
|
||||||
artist: Optional[str] = None
|
artist: str | None = None
|
||||||
album: Optional[str] = None
|
album: str | None = None
|
||||||
album_artist: Optional[str] = None
|
album_artist: str | None = None
|
||||||
track_number: Optional[str] = None
|
track_number: str | None = None
|
||||||
genre: Optional[str] = None
|
genre: str | None = None
|
||||||
length_seconds: Optional[str] = None
|
length_seconds: str | None = None
|
||||||
album_date: Optional[str] = None
|
album_date: str | None = None
|
||||||
codec: Optional[str] = None
|
codec: str | None = None
|
||||||
filepath: Optional[str] = None
|
filepath: str | None = None
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|||||||
@ -118,8 +118,7 @@ class MusicTable(QTableView):
|
|||||||
)
|
)
|
||||||
self.config = ConfigParser()
|
self.config = ConfigParser()
|
||||||
self.config.read(cfg_file)
|
self.config.read(cfg_file)
|
||||||
print("music table config:")
|
debug(f"music table config: {self.config}")
|
||||||
print(self.config)
|
|
||||||
|
|
||||||
# Threads
|
# Threads
|
||||||
self.threadpool = QThreadPool
|
self.threadpool = QThreadPool
|
||||||
@ -138,7 +137,8 @@ class MusicTable(QTableView):
|
|||||||
|
|
||||||
# Properties
|
# Properties
|
||||||
self.setAcceptDrops(True)
|
self.setAcceptDrops(True)
|
||||||
self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
self.setHorizontalScrollBarPolicy(
|
||||||
|
Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
||||||
self.setEditTriggers(QAbstractItemView.EditTrigger.EditKeyPressed)
|
self.setEditTriggers(QAbstractItemView.EditTrigger.EditKeyPressed)
|
||||||
self.setAlternatingRowColors(True)
|
self.setAlternatingRowColors(True)
|
||||||
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
||||||
@ -146,7 +146,7 @@ class MusicTable(QTableView):
|
|||||||
# header
|
# header
|
||||||
self.horizontal_header: QHeaderView = self.horizontalHeader()
|
self.horizontal_header: QHeaderView = self.horizontalHeader()
|
||||||
assert self.horizontal_header is not None # i hate look at linting errors
|
assert self.horizontal_header is not None # i hate look at linting errors
|
||||||
self.horizontal_header.setStretchLastSection(True)
|
self.horizontal_header.setStretchLastSection(False)
|
||||||
self.horizontal_header.setSectionResizeMode(QHeaderView.Interactive)
|
self.horizontal_header.setSectionResizeMode(QHeaderView.Interactive)
|
||||||
self.horizontal_header.sortIndicatorChanged.connect(self.on_sort)
|
self.horizontal_header.sortIndicatorChanged.connect(self.on_sort)
|
||||||
# dumb vertical estupido
|
# dumb vertical estupido
|
||||||
@ -159,7 +159,8 @@ class MusicTable(QTableView):
|
|||||||
self.deleteKey.connect(self.delete_songs)
|
self.deleteKey.connect(self.delete_songs)
|
||||||
self.doubleClicked.connect(self.play_selected_audio_file)
|
self.doubleClicked.connect(self.play_selected_audio_file)
|
||||||
self.enterKey.connect(self.play_selected_audio_file)
|
self.enterKey.connect(self.play_selected_audio_file)
|
||||||
self.model2.dataChanged.connect(self.on_cell_data_changed) # editing cells
|
self.model2.dataChanged.connect(
|
||||||
|
self.on_cell_data_changed) # editing cells
|
||||||
# self.model2.layoutChanged.connect(self.restore_scroll_position)
|
# self.model2.layoutChanged.connect(self.restore_scroll_position)
|
||||||
self.horizontal_header.sectionResized.connect(self.on_header_resized)
|
self.horizontal_header.sectionResized.connect(self.on_header_resized)
|
||||||
# Final actions
|
# Final actions
|
||||||
@ -202,11 +203,13 @@ class MusicTable(QTableView):
|
|||||||
"""Right-click context menu"""
|
"""Right-click context menu"""
|
||||||
menu = QMenu(self)
|
menu = QMenu(self)
|
||||||
add_to_playlist_action = QAction("Add to playlist", self)
|
add_to_playlist_action = QAction("Add to playlist", self)
|
||||||
add_to_playlist_action.triggered.connect(self.add_selected_files_to_playlist)
|
add_to_playlist_action.triggered.connect(
|
||||||
|
self.add_selected_files_to_playlist)
|
||||||
menu.addAction(add_to_playlist_action)
|
menu.addAction(add_to_playlist_action)
|
||||||
# edit metadata
|
# edit metadata
|
||||||
edit_metadata_action = QAction("Edit metadata", self)
|
edit_metadata_action = QAction("Edit metadata", self)
|
||||||
edit_metadata_action.triggered.connect(self.edit_selected_files_metadata)
|
edit_metadata_action.triggered.connect(
|
||||||
|
self.edit_selected_files_metadata)
|
||||||
menu.addAction(edit_metadata_action)
|
menu.addAction(edit_metadata_action)
|
||||||
# edit lyrics
|
# edit lyrics
|
||||||
edit_lyrics_action = QAction("Lyrics (View/Edit)", self)
|
edit_lyrics_action = QAction("Lyrics (View/Edit)", self)
|
||||||
@ -214,10 +217,12 @@ class MusicTable(QTableView):
|
|||||||
menu.addAction(edit_lyrics_action)
|
menu.addAction(edit_lyrics_action)
|
||||||
# jump to current song in table
|
# jump to current song in table
|
||||||
jump_to_current_song_action = QAction("Jump to current song", self)
|
jump_to_current_song_action = QAction("Jump to current song", self)
|
||||||
jump_to_current_song_action.triggered.connect(self.jump_to_current_song)
|
jump_to_current_song_action.triggered.connect(
|
||||||
|
self.jump_to_current_song)
|
||||||
menu.addAction(jump_to_current_song_action)
|
menu.addAction(jump_to_current_song_action)
|
||||||
# open in file explorer
|
# open in file explorer
|
||||||
open_containing_folder_action = QAction("Open in system file manager", self)
|
open_containing_folder_action = QAction(
|
||||||
|
"Open in system file manager", self)
|
||||||
open_containing_folder_action.triggered.connect(self.open_directory)
|
open_containing_folder_action.triggered.connect(self.open_directory)
|
||||||
menu.addAction(open_containing_folder_action)
|
menu.addAction(open_containing_folder_action)
|
||||||
# view id3 tags (debug)
|
# view id3 tags (debug)
|
||||||
@ -380,59 +385,58 @@ class MusicTable(QTableView):
|
|||||||
self.set_selected_song_qmodel_index()
|
self.set_selected_song_qmodel_index()
|
||||||
self.viewport().update() # type: ignore
|
self.viewport().update() # type: ignore
|
||||||
|
|
||||||
def on_header_resized(self, logicalIndex, oldSize, newSize):
|
def on_header_resized(self, logicalIndex: int, oldSize: int, newSize: int):
|
||||||
"""Handles keeping headers inside the viewport"""
|
"""Handles keeping headers inside the viewport"""
|
||||||
# FIXME: how resize good
|
# FIXME: how resize good
|
||||||
|
pass
|
||||||
|
|
||||||
# https://stackoverflow.com/questions/46775438/how-to-limit-qheaderview-size-when-resizing-sections
|
# https://stackoverflow.com/questions/46775438/how-to-limit-qheaderview-size-when-resizing-sections
|
||||||
col_count = self.model2.columnCount()
|
# col_count = self.model2.columnCount()
|
||||||
qtableview_width = self.size().width()
|
# qtableview_width = self.size().width()
|
||||||
sum_of_cols = self.horizontal_header.length()
|
# sum_of_cols = self.horizontal_header.length()
|
||||||
# debug(f'qtable_width: {qtableview_width}')
|
# debug(f'qtable_width: {qtableview_width}')
|
||||||
# debug(f'sum of cols: {sum_of_cols}')
|
# debug(f'sum of cols: {sum_of_cols}')
|
||||||
|
|
||||||
# check for discrepancy
|
# if sum_of_cols != qtableview_width: # check for discrepancy
|
||||||
if sum_of_cols != qtableview_width:
|
# if logicalIndex < col_count: # if not the last header
|
||||||
# if not the last header
|
# next_header_size = self.horizontal_header.sectionSize(logicalIndex + 1)
|
||||||
if logicalIndex < col_count:
|
# if next_header_size > (sum_of_cols - qtableview_width): # if it should shrink
|
||||||
next_header_size = self.horizontal_header.sectionSize(logicalIndex + 1)
|
# self.horizontal_header.resizeSection(
|
||||||
# If it should shrink
|
# logicalIndex + 1,
|
||||||
if next_header_size > (sum_of_cols - qtableview_width):
|
# next_header_size - (sum_of_cols - qtableview_width),
|
||||||
# shrink it
|
# ) # shrink it
|
||||||
self.horizontal_header.resizeSection(
|
# else:
|
||||||
logicalIndex + 1,
|
# self.horizontal_header.resizeSection(logicalIndex, oldSize) # block the resize
|
||||||
next_header_size - (sum_of_cols - qtableview_width),
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# block the resize
|
|
||||||
self.horizontal_header.resizeSection(logicalIndex, oldSize)
|
|
||||||
|
|
||||||
def on_cell_data_changed(self, topLeft: QModelIndex, bottomRight: QModelIndex):
|
def on_cell_data_changed(self, topLeft: QModelIndex, bottomRight: QModelIndex):
|
||||||
"""Handles updating ID3 tags when data changes in a cell"""
|
"""Handles updating ID3 tags when data changes in a cell"""
|
||||||
# FIXME: broken
|
# FIXME: broken
|
||||||
if isinstance(self.model2, QStandardItemModel):
|
|
||||||
debug("on_cell_data_changed")
|
|
||||||
# get the ID of the row that was edited
|
|
||||||
id_index = self.model2.index(topLeft.row(), 0)
|
|
||||||
# get the db song_id from the row
|
|
||||||
song_id = self.model2.data(id_index, Qt.ItemDataRole.UserRole)
|
|
||||||
user_index = self.headers.user_fields.index("filepath")
|
|
||||||
filepath = self.currentIndex().siblingAtColumn(user_index).data()
|
|
||||||
# update the ID3 information
|
|
||||||
user_input_data = topLeft.data()
|
|
||||||
edited_column_name = self.headers.user_fields[topLeft.column()]
|
|
||||||
debug(f"on_cell_data_changed | edited column name: {edited_column_name}")
|
|
||||||
response = set_tag(filepath, edited_column_name, user_input_data)
|
|
||||||
if response:
|
|
||||||
# Update the library with new metadata
|
|
||||||
update_song_in_database(song_id, edited_column_name, user_input_data)
|
|
||||||
return
|
|
||||||
|
|
||||||
def handle_progress(self, data):
|
# if isinstance(self.model2, QStandardItemModel):
|
||||||
|
debug("on_cell_data_changed")
|
||||||
|
# get the ID of the row that was edited
|
||||||
|
id_index = self.model2.index(topLeft.row(), 0)
|
||||||
|
# get the db song_id from the row
|
||||||
|
song_id: int = self.model2.data(id_index, Qt.ItemDataRole.UserRole)
|
||||||
|
user_index = self.headers.user_fields.index("filepath")
|
||||||
|
filepath = self.currentIndex().siblingAtColumn(user_index).data()
|
||||||
|
# update the ID3 information
|
||||||
|
user_input_data: str = topLeft.data()
|
||||||
|
edited_column_name: str = self.headers.user_fields[topLeft.column()]
|
||||||
|
debug(
|
||||||
|
f"on_cell_data_changed | edited column name: {edited_column_name}")
|
||||||
|
response = set_tag(filepath, edited_column_name, user_input_data)
|
||||||
|
if response:
|
||||||
|
# Update the library with new metadata
|
||||||
|
_ = update_song_in_database(
|
||||||
|
song_id, edited_column_name, user_input_data)
|
||||||
|
return
|
||||||
|
|
||||||
|
def handle_progress(self, data: object):
|
||||||
"""Emits data to main"""
|
"""Emits data to main"""
|
||||||
self.handleProgressSignal.emit(data)
|
self.handleProgressSignal.emit(data)
|
||||||
|
|
||||||
def on_get_audio_files_recursively_finished(self, result):
|
def on_get_audio_files_recursively_finished(self, result: list[str]):
|
||||||
"""file search completion handler"""
|
"""file search completion handler"""
|
||||||
if result:
|
if result:
|
||||||
self.add_files_to_library(result)
|
self.add_files_to_library(result)
|
||||||
@ -456,7 +460,8 @@ class MusicTable(QTableView):
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
pass
|
pass
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug(f"on_add_files_to_database_finished() | Something went wrong: {e}")
|
debug(
|
||||||
|
f"on_add_files_to_database_finished() | Something went wrong: {e}")
|
||||||
|
|
||||||
# ____________________
|
# ____________________
|
||||||
# | |
|
# | |
|
||||||
@ -484,8 +489,8 @@ class MusicTable(QTableView):
|
|||||||
- File > Open > List of song(s)
|
- File > Open > List of song(s)
|
||||||
"""
|
"""
|
||||||
worker = Worker(add_files_to_database, files)
|
worker = Worker(add_files_to_database, files)
|
||||||
worker.signals.signal_progress.connect(self.qapp.handle_progress)
|
_ = worker.signals.signal_progress.connect(self.qapp.handle_progress)
|
||||||
worker.signals.signal_result.connect(self.on_add_files_to_database_finished)
|
_ = worker.signals.signal_result.connect(self.on_add_files_to_database_finished)
|
||||||
worker.signals.signal_finished.connect(self.load_music_table)
|
worker.signals.signal_finished.connect(self.load_music_table)
|
||||||
if self.qapp:
|
if self.qapp:
|
||||||
threadpool = self.qapp.threadpool
|
threadpool = self.qapp.threadpool
|
||||||
@ -495,15 +500,15 @@ class MusicTable(QTableView):
|
|||||||
|
|
||||||
def add_selected_files_to_playlist(self):
|
def add_selected_files_to_playlist(self):
|
||||||
"""Opens a playlist choice menu and adds the currently selected files to the chosen playlist"""
|
"""Opens a playlist choice menu and adds the currently selected files to the chosen playlist"""
|
||||||
playlist_choice_window = AddToPlaylistWindow(self.get_selected_songs_db_ids())
|
playlist_choice_window = AddToPlaylistWindow(
|
||||||
|
self.get_selected_songs_db_ids())
|
||||||
playlist_choice_window.exec_()
|
playlist_choice_window.exec_()
|
||||||
|
|
||||||
def delete_songs(self):
|
def delete_songs(self):
|
||||||
"""Asks to delete the currently selected songs from the db and music table (not the filesystem)"""
|
"""Asks to delete the currently selected songs from the db and music table (not the filesystem)"""
|
||||||
# FIXME: determine if we are in a playlist or not
|
# NOTE: provide extra questionbox option?
|
||||||
# then delete songs from only the playlist
|
|
||||||
# or provide extra questionbox option
|
|
||||||
# | Delete from playlist & lib | Delete from playlist only | Cancel |
|
# | Delete from playlist & lib | Delete from playlist only | Cancel |
|
||||||
|
# Currently, this just deletes from the playlist, or the main lib & any playlists
|
||||||
selected_filepaths = self.get_selected_songs_filepaths()
|
selected_filepaths = self.get_selected_songs_filepaths()
|
||||||
if self.selected_playlist_id:
|
if self.selected_playlist_id:
|
||||||
question_dialog = QuestionBoxDetails(
|
question_dialog = QuestionBoxDetails(
|
||||||
@ -513,9 +518,12 @@ class MusicTable(QTableView):
|
|||||||
)
|
)
|
||||||
reply = question_dialog.execute()
|
reply = question_dialog.execute()
|
||||||
if reply:
|
if reply:
|
||||||
worker = Worker(batch_delete_filepaths_from_playlist, selected_filepaths, self.selected_playlist_id)
|
worker = Worker(batch_delete_filepaths_from_playlist,
|
||||||
worker.signals.signal_progress.connect(self.qapp.handle_progress)
|
selected_filepaths, self.selected_playlist_id)
|
||||||
worker.signals.signal_finished.connect(self.delete_selected_row_indices)
|
worker.signals.signal_progress.connect(
|
||||||
|
self.qapp.handle_progress)
|
||||||
|
worker.signals.signal_finished.connect(
|
||||||
|
self.delete_selected_row_indices)
|
||||||
if self.qapp:
|
if self.qapp:
|
||||||
threadpool = self.qapp.threadpool
|
threadpool = self.qapp.threadpool
|
||||||
threadpool.start(worker)
|
threadpool.start(worker)
|
||||||
@ -527,9 +535,12 @@ class MusicTable(QTableView):
|
|||||||
)
|
)
|
||||||
reply = question_dialog.execute()
|
reply = question_dialog.execute()
|
||||||
if reply:
|
if reply:
|
||||||
worker = Worker(batch_delete_filepaths_from_database, selected_filepaths)
|
worker = Worker(
|
||||||
worker.signals.signal_progress.connect(self.qapp.handle_progress)
|
batch_delete_filepaths_from_database, selected_filepaths)
|
||||||
worker.signals.signal_finished.connect(self.delete_selected_row_indices)
|
worker.signals.signal_progress.connect(
|
||||||
|
self.qapp.handle_progress)
|
||||||
|
worker.signals.signal_finished.connect(
|
||||||
|
self.delete_selected_row_indices)
|
||||||
if self.qapp:
|
if self.qapp:
|
||||||
threadpool = self.qapp.threadpool
|
threadpool = self.qapp.threadpool
|
||||||
threadpool.start(worker)
|
threadpool.start(worker)
|
||||||
@ -563,7 +574,8 @@ class MusicTable(QTableView):
|
|||||||
"""Moves screen to the selected song, then selects the row"""
|
"""Moves screen to the selected song, then selects the row"""
|
||||||
debug("jump_to_selected_song")
|
debug("jump_to_selected_song")
|
||||||
# get the proxy model index
|
# get the proxy model index
|
||||||
proxy_index = self.proxymodel.mapFromSource(self.selected_song_qmodel_index)
|
proxy_index = self.proxymodel.mapFromSource(
|
||||||
|
self.selected_song_qmodel_index)
|
||||||
self.scrollTo(proxy_index)
|
self.scrollTo(proxy_index)
|
||||||
self.selectRow(proxy_index.row())
|
self.selectRow(proxy_index.row())
|
||||||
|
|
||||||
@ -573,7 +585,8 @@ class MusicTable(QTableView):
|
|||||||
# get the proxy model index
|
# get the proxy model index
|
||||||
debug(self.current_song_filepath)
|
debug(self.current_song_filepath)
|
||||||
debug(self.current_song_qmodel_index)
|
debug(self.current_song_qmodel_index)
|
||||||
proxy_index = self.proxymodel.mapFromSource(self.current_song_qmodel_index)
|
proxy_index = self.proxymodel.mapFromSource(
|
||||||
|
self.current_song_qmodel_index)
|
||||||
self.scrollTo(proxy_index)
|
self.scrollTo(proxy_index)
|
||||||
self.selectRow(proxy_index.row())
|
self.selectRow(proxy_index.row())
|
||||||
|
|
||||||
@ -684,7 +697,8 @@ class MusicTable(QTableView):
|
|||||||
)
|
)
|
||||||
# debug(f"reorganize_files() | Moved: {filepath} -> {new_path}")
|
# debug(f"reorganize_files() | Moved: {filepath} -> {new_path}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error(f"reorganize_files() | Error moving file: {filepath} | {e}")
|
error(
|
||||||
|
f"reorganize_files() | Error moving file: {filepath} | {e}")
|
||||||
# Draw the rest of the owl
|
# Draw the rest of the owl
|
||||||
# QMessageBox.information(
|
# QMessageBox.information(
|
||||||
# self, "Reorganization complete", "Files successfully reorganized"
|
# self, "Reorganization complete", "Files successfully reorganized"
|
||||||
@ -709,7 +723,8 @@ class MusicTable(QTableView):
|
|||||||
self.disconnect_layout_changed()
|
self.disconnect_layout_changed()
|
||||||
self.vertical_scroll_position = self.verticalScrollBar().value() # type: ignore
|
self.vertical_scroll_position = self.verticalScrollBar().value() # type: ignore
|
||||||
self.model2.clear()
|
self.model2.clear()
|
||||||
self.model2.setHorizontalHeaderLabels(self.headers.get_user_gui_headers())
|
self.model2.setHorizontalHeaderLabels(
|
||||||
|
self.headers.get_user_gui_headers())
|
||||||
fields = ", ".join(self.headers.user_fields)
|
fields = ", ".join(self.headers.user_fields)
|
||||||
search_clause = (
|
search_clause = (
|
||||||
"title LIKE ? OR artist LIKE ? OR album LIKE ?"
|
"title LIKE ? OR artist LIKE ? OR album LIKE ?"
|
||||||
@ -731,7 +746,8 @@ class MusicTable(QTableView):
|
|||||||
query = f"{query} WHERE {search_clause};"
|
query = f"{query} WHERE {search_clause};"
|
||||||
else:
|
else:
|
||||||
query = f"{query} AND {search_clause};"
|
query = f"{query} AND {search_clause};"
|
||||||
data = db.query(query, (self.selected_playlist_id, params))
|
data = db.query(
|
||||||
|
query, (self.selected_playlist_id, params))
|
||||||
else:
|
else:
|
||||||
data = db.query(query, (self.selected_playlist_id,))
|
data = db.query(query, (self.selected_playlist_id,))
|
||||||
|
|
||||||
@ -788,7 +804,8 @@ class MusicTable(QTableView):
|
|||||||
# reloading the model destroys and makes new indexes
|
# reloading the model destroys and makes new indexes
|
||||||
# so we look for the new index of the current song on load
|
# so we look for the new index of the current song on load
|
||||||
current_song_filepath = self.get_current_song_filepath()
|
current_song_filepath = self.get_current_song_filepath()
|
||||||
debug(f"load_music_table() | current filepath: {current_song_filepath}")
|
debug(
|
||||||
|
f"load_music_table() | current filepath: {current_song_filepath}")
|
||||||
for row in range(self.model2.rowCount()):
|
for row in range(self.model2.rowCount()):
|
||||||
real_index = self.model2.index(
|
real_index = self.model2.index(
|
||||||
row, self.headers.user_fields.index("filepath")
|
row, self.headers.user_fields.index("filepath")
|
||||||
@ -814,7 +831,8 @@ class MusicTable(QTableView):
|
|||||||
"""
|
"""
|
||||||
Loads the header widths from the last application close.
|
Loads the header widths from the last application close.
|
||||||
"""
|
"""
|
||||||
table_view_column_widths = str(self.config["table"]["column_widths"]).split(",")
|
table_view_column_widths = str(
|
||||||
|
self.config["table"]["column_widths"]).split(",")
|
||||||
debug(f"loaded header widths: {table_view_column_widths}")
|
debug(f"loaded header widths: {table_view_column_widths}")
|
||||||
if not isinstance(table_view_column_widths, list):
|
if not isinstance(table_view_column_widths, list):
|
||||||
for i in range(self.model2.columnCount() - 1):
|
for i in range(self.model2.columnCount() - 1):
|
||||||
@ -898,7 +916,8 @@ class MusicTable(QTableView):
|
|||||||
selected_rows = self.get_selected_rows()
|
selected_rows = self.get_selected_rows()
|
||||||
filepaths = []
|
filepaths = []
|
||||||
for row in selected_rows:
|
for row in selected_rows:
|
||||||
idx = self.proxymodel.index(row, self.headers.user_fields.index("filepath"))
|
idx = self.proxymodel.index(
|
||||||
|
row, self.headers.user_fields.index("filepath"))
|
||||||
filepaths.append(idx.data())
|
filepaths.append(idx.data())
|
||||||
return filepaths
|
return filepaths
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
from PyQt5.QtWidgets import (
|
from PyQt5.QtWidgets import (
|
||||||
QAction,
|
QAction,
|
||||||
QInputDialog,
|
QInputDialog,
|
||||||
QListWidget,
|
|
||||||
QMenu,
|
QMenu,
|
||||||
QMessageBox,
|
QMessageBox,
|
||||||
QTreeWidget,
|
QTreeWidget,
|
||||||
@ -10,6 +9,7 @@ from PyQt5.QtWidgets import (
|
|||||||
from PyQt5.QtCore import QThreadPool, pyqtSignal, Qt, QPoint
|
from PyQt5.QtCore import QThreadPool, pyqtSignal, Qt, QPoint
|
||||||
import DBA
|
import DBA
|
||||||
from logging import debug
|
from logging import debug
|
||||||
|
from components.ErrorDialog import ErrorDialog
|
||||||
from utils import Worker
|
from utils import Worker
|
||||||
|
|
||||||
from components import CreatePlaylistWindow
|
from components import CreatePlaylistWindow
|
||||||
@ -43,10 +43,6 @@ class PlaylistsPane(QTreeWidget):
|
|||||||
self.customContextMenuRequested.connect(self.showContextMenu)
|
self.customContextMenuRequested.connect(self.showContextMenu)
|
||||||
self.currentItemChanged.connect(self.playlist_clicked)
|
self.currentItemChanged.connect(self.playlist_clicked)
|
||||||
self.playlist_db_id_choice: int | None = None
|
self.playlist_db_id_choice: int | None = None
|
||||||
self.threadpool: QThreadPool | None = None
|
|
||||||
|
|
||||||
def set_threadpool(self, threadpool: QThreadPool):
|
|
||||||
self.threadpool = threadpool
|
|
||||||
|
|
||||||
def reload_playlists(self, progress_callback=None):
|
def reload_playlists(self, progress_callback=None):
|
||||||
"""
|
"""
|
||||||
@ -60,7 +56,7 @@ class PlaylistsPane(QTreeWidget):
|
|||||||
playlists = db.query(
|
playlists = db.query(
|
||||||
"SELECT id, name FROM playlist ORDER BY date_created DESC;", ()
|
"SELECT id, name FROM playlist ORDER BY date_created DESC;", ()
|
||||||
)
|
)
|
||||||
debug(f'PlaylistsPane: | playlists = {playlists}')
|
# debug(f'PlaylistsPane: | playlists = {playlists}')
|
||||||
for playlist in playlists:
|
for playlist in playlists:
|
||||||
branch = PlaylistWidgetItem(self, playlist[0], playlist[1])
|
branch = PlaylistWidgetItem(self, playlist[0], playlist[1])
|
||||||
self._playlists_root.addChild(branch)
|
self._playlists_root.addChild(branch)
|
||||||
@ -86,7 +82,7 @@ class PlaylistsPane(QTreeWidget):
|
|||||||
def create_playlist(self):
|
def create_playlist(self):
|
||||||
"""Creates a database record for a playlist, given a name"""
|
"""Creates a database record for a playlist, given a name"""
|
||||||
window = CreatePlaylistWindow(self.playlistCreatedSignal)
|
window = CreatePlaylistWindow(self.playlistCreatedSignal)
|
||||||
window.playlistCreatedSignal.connect(self.reload_playlists) # type: ignore
|
window.playlistCreatedSignal.connect(self.reload_playlists)
|
||||||
window.exec_()
|
window.exec_()
|
||||||
|
|
||||||
def rename_playlist(self, *args):
|
def rename_playlist(self, *args):
|
||||||
@ -110,8 +106,9 @@ class PlaylistsPane(QTreeWidget):
|
|||||||
(text, self.playlist_db_id_choice),
|
(text, self.playlist_db_id_choice),
|
||||||
)
|
)
|
||||||
worker = Worker(self.reload_playlists)
|
worker = Worker(self.reload_playlists)
|
||||||
if self.threadpool:
|
if self.qapp:
|
||||||
self.threadpool.start(worker)
|
threadpool = self.qapp.threadpool
|
||||||
|
threadpool.start(worker)
|
||||||
|
|
||||||
def delete_playlist(self, *args):
|
def delete_playlist(self, *args):
|
||||||
"""Deletes a playlist"""
|
"""Deletes a playlist"""
|
||||||
@ -123,14 +120,18 @@ class PlaylistsPane(QTreeWidget):
|
|||||||
QMessageBox.Yes,
|
QMessageBox.Yes,
|
||||||
)
|
)
|
||||||
if reply == QMessageBox.Yes:
|
if reply == QMessageBox.Yes:
|
||||||
with DBA.DBAccess() as db:
|
if self.qapp:
|
||||||
db.execute(
|
with DBA.DBAccess() as db:
|
||||||
"DELETE FROM playlist WHERE id = ?;", (self.playlist_db_id_choice,)
|
db.execute(
|
||||||
)
|
"DELETE FROM playlist WHERE id = ?;", (self.playlist_db_id_choice,)
|
||||||
# reload
|
)
|
||||||
worker = Worker(self.reload_playlists)
|
# reload
|
||||||
if self.threadpool:
|
worker = Worker(self.reload_playlists)
|
||||||
self.threadpool.start(worker)
|
threadpool = self.qapp.threadpool
|
||||||
|
threadpool.start(worker)
|
||||||
|
else:
|
||||||
|
error_dialog = ErrorDialog("Main application [qapp] not loaded - aborting operation")
|
||||||
|
error_dialog.exec()
|
||||||
|
|
||||||
def playlist_clicked(self, item):
|
def playlist_clicked(self, item):
|
||||||
"""Specific playlist pane index was clicked"""
|
"""Specific playlist pane index was clicked"""
|
||||||
@ -139,10 +140,14 @@ class PlaylistsPane(QTreeWidget):
|
|||||||
# self.all_songs_selected()
|
# self.all_songs_selected()
|
||||||
self.allSongsSignal.emit()
|
self.allSongsSignal.emit()
|
||||||
elif isinstance(item, PlaylistWidgetItem):
|
elif isinstance(item, PlaylistWidgetItem):
|
||||||
debug(f"ID: {item.id}, name: {item.text(0)}")
|
# debug(f"ID: {item.id}, name: {item.text(0)}")
|
||||||
self.playlist_db_id_choice = item.id
|
self.playlist_db_id_choice = item.id
|
||||||
self.playlistChoiceSignal.emit(int(item.id))
|
self.playlistChoiceSignal.emit(int(item.id))
|
||||||
|
|
||||||
|
def load_qapp(self, qapp) -> None:
|
||||||
|
"""Necessary for using members and methods of main application window"""
|
||||||
|
self.qapp = qapp
|
||||||
|
|
||||||
# def all_songs_selected(self):
|
# def all_songs_selected(self):
|
||||||
# """Emits a signal to display all songs in the library"""
|
# """Emits a signal to display all songs in the library"""
|
||||||
# # I have no idea why this has to be in its own function, but it does
|
# # I have no idea why this has to be in its own function, but it does
|
||||||
|
|||||||
@ -42,7 +42,7 @@ class SearchLineEdit(QLineEdit):
|
|||||||
|
|
||||||
def on_text_changed(self):
|
def on_text_changed(self):
|
||||||
"""Reset a timer each time text is changed"""
|
"""Reset a timer each time text is changed"""
|
||||||
self.timer.start(300)
|
self.timer.start(1500)
|
||||||
|
|
||||||
def on_typing_stopped(self):
|
def on_typing_stopped(self):
|
||||||
"""When timer reaches end, emit the text that is currently entered"""
|
"""When timer reaches end, emit the text that is currently entered"""
|
||||||
|
|||||||
12
main.py
12
main.py
@ -92,7 +92,6 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
/ "config.ini"
|
/ "config.ini"
|
||||||
)
|
)
|
||||||
self.config.read(self.cfg_file)
|
self.config.read(self.cfg_file)
|
||||||
debug(f"\tmain config: {self.config}")
|
|
||||||
self.threadpool: QThreadPool = QThreadPool()
|
self.threadpool: QThreadPool = QThreadPool()
|
||||||
# UI
|
# UI
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
@ -105,6 +104,7 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
self.status_bar.addPermanentWidget(self.permanent_status_label)
|
self.status_bar.addPermanentWidget(self.permanent_status_label)
|
||||||
self.setStatusBar(self.status_bar)
|
self.setStatusBar(self.status_bar)
|
||||||
|
|
||||||
|
# table
|
||||||
self.selected_song_filepath: str | None = None
|
self.selected_song_filepath: str | None = None
|
||||||
self.current_song_filepath: str | None = None
|
self.current_song_filepath: str | None = None
|
||||||
self.current_song_metadata: ID3 | dict | None = None
|
self.current_song_metadata: ID3 | dict | None = None
|
||||||
@ -113,7 +113,6 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
# widget bits
|
# widget bits
|
||||||
self.tableView: MusicTable
|
self.tableView: MusicTable
|
||||||
self.album_art_scene: QGraphicsScene = QGraphicsScene()
|
self.album_art_scene: QGraphicsScene = QGraphicsScene()
|
||||||
# self.player: QMediaPlayer = QMediaPlayer() # Audio player object
|
|
||||||
self.player: QMediaPlayer = MediaPlayer()
|
self.player: QMediaPlayer = MediaPlayer()
|
||||||
# set index on choose song
|
# set index on choose song
|
||||||
# index is the model2's row number? i guess?
|
# index is the model2's row number? i guess?
|
||||||
@ -143,6 +142,7 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
# sharing functions with other classes and that
|
# sharing functions with other classes and that
|
||||||
self.tableView.load_qapp(self)
|
self.tableView.load_qapp(self)
|
||||||
self.albumGraphicsView.load_qapp(self)
|
self.albumGraphicsView.load_qapp(self)
|
||||||
|
self.playlistTreeView.load_qapp(self)
|
||||||
self.headers = HeaderTags()
|
self.headers = HeaderTags()
|
||||||
|
|
||||||
# Settings init
|
# Settings init
|
||||||
@ -214,7 +214,6 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
self.tableView.load_music_table
|
self.tableView.load_music_table
|
||||||
)
|
)
|
||||||
self.playlistTreeView.allSongsSignal.connect(self.tableView.load_music_table)
|
self.playlistTreeView.allSongsSignal.connect(self.tableView.load_music_table)
|
||||||
self.playlistTreeView.set_threadpool(self.threadpool)
|
|
||||||
|
|
||||||
# albumGraphicsView
|
# albumGraphicsView
|
||||||
self.albumGraphicsView.albumArtDropped.connect(
|
self.albumGraphicsView.albumArtDropped.connect(
|
||||||
@ -393,16 +392,11 @@ class ApplicationWindow(QMainWindow, Ui_MainWindow):
|
|||||||
def load_config(self) -> None:
|
def load_config(self) -> None:
|
||||||
"""does what it says"""
|
"""does what it says"""
|
||||||
cfg_file = (
|
cfg_file = (
|
||||||
Path(user_config_dir(appname="musicpom", appauthor="billypom"))
|
Path(user_config_dir(appname="musicpom", appauthor="billypom")) / "config.ini"
|
||||||
/ "config.ini"
|
|
||||||
)
|
)
|
||||||
self.config.read(cfg_file)
|
self.config.read(cfg_file)
|
||||||
debug("load_config()")
|
debug("load_config()")
|
||||||
|
|
||||||
def get_thread_pool(self) -> QThreadPool:
|
|
||||||
"""Returns the threadpool instance"""
|
|
||||||
return self.threadpool
|
|
||||||
|
|
||||||
def set_permanent_status_bar_message(self, message: str) -> None:
|
def set_permanent_status_bar_message(self, message: str) -> None:
|
||||||
"""
|
"""
|
||||||
Sets the permanent message label in the status bar
|
Sets the permanent message label in the status bar
|
||||||
|
|||||||
@ -27,10 +27,10 @@ class WorkerSignals(QObject):
|
|||||||
# pandas DataFrames or numpy arrays. Instead, pass the minimum amount of information needed
|
# pandas DataFrames or numpy arrays. Instead, pass the minimum amount of information needed
|
||||||
# (i.e. lists of filepaths)
|
# (i.e. lists of filepaths)
|
||||||
|
|
||||||
signal_started = pyqtSignal()
|
signal_started: pyqtSignal = pyqtSignal()
|
||||||
signal_result = pyqtSignal(object)
|
signal_result: pyqtSignal = pyqtSignal(object)
|
||||||
signal_finished = pyqtSignal()
|
signal_finished: pyqtSignal = pyqtSignal()
|
||||||
signal_progress = pyqtSignal(str)
|
signal_progress: pyqtSignal = pyqtSignal(str)
|
||||||
|
|
||||||
|
|
||||||
class Worker(QRunnable):
|
class Worker(QRunnable):
|
||||||
@ -46,12 +46,12 @@ class Worker(QRunnable):
|
|||||||
:param kwargs: Keywords to pass to the callback function
|
:param kwargs: Keywords to pass to the callback function
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, fn, *args, **kwargs):
|
def __init__(self, fn, *args: object, **kwargs: object):
|
||||||
super(Worker, self).__init__()
|
super(Worker, self).__init__()
|
||||||
# Store constructor arguments (re-used for processing)
|
# Store constructor arguments (re-used for processing)
|
||||||
self.fn = fn
|
self.fn = fn
|
||||||
self.args = args
|
self.args: object = args
|
||||||
self.kwargs = kwargs
|
self.kwargs: object = kwargs
|
||||||
self.signals: WorkerSignals = WorkerSignals()
|
self.signals: WorkerSignals = WorkerSignals()
|
||||||
|
|
||||||
# Add a callback to our kwargs
|
# Add a callback to our kwargs
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user