This commit is contained in:
Broque Thomas 2025-07-23 21:46:31 -07:00
parent 6285ef6615
commit 0f74043512
4 changed files with 17772 additions and 70 deletions

View file

@ -1 +1 @@
{"access_token": "BQC18WaAYE4mMb-fxpccf3_kqLmSJKQ5P38RuSSkNj-QMbEew3fdEZeOt8sabT4OYFb93n4v859YdkpBt0hT0DlRoWHeiHkpb9Bg6n_i__jEIR2unKdNTdKFoupmP9Xed1-ZVczEAVhDb7LL-bPNMVWKseocEh72C5WdDq3jonUE_BzLW0g-8RKG9Qf2Sh8xVJCAUnUk3fhSegMW3jZHw0-72NKmnrkcGAhr5hmGzpeZ7qF-e4ecKp9olkFJl9vx", "token_type": "Bearer", "expires_in": 3600, "scope": "user-library-read user-read-private playlist-read-private playlist-read-collaborative user-read-email", "expires_at": 1753326837, "refresh_token": "AQDmfQkPCGObfJeTUIbW1hAAwhSqkuHRA3Qh2dqVYMRh0eCkFMQgPNJDDzF8y-BiaVbj80zePkK_XSfYH1aJutMtNbnsqRKWuxP31BTrMc7pdUdbE7Fma4oH8wpDUKdG3MM"}
{"access_token": "BQAk5vVhLVv9j1H0w4esI33zLvfuC6cw2icwwe1dYmbtpcH-1nEzhsPo7-RZsoaRLvZ4uLVjBFr9b0NWpKs2JBsFq1mVCxoDckML082ogffnsKhyP3QgKdETruf04FBetm5IY1s4THMwJyK-DzDQRuHHshpakw53pLaojKMp6dR1IJN98v6I-3RBexumJgX9pvf6mn-FFURWyi-Df8iB-2UWIWR4ANLhIdTte3LqRybl5tm63aFkG7D6Rcl_XZBc", "token_type": "Bearer", "expires_in": 3600, "scope": "user-library-read user-read-private playlist-read-private playlist-read-collaborative user-read-email", "expires_at": 1753334396, "refresh_token": "AQDmfQkPCGObfJeTUIbW1hAAwhSqkuHRA3Qh2dqVYMRh0eCkFMQgPNJDDzF8y-BiaVbj80zePkK_XSfYH1aJutMtNbnsqRKWuxP31BTrMc7pdUdbE7Fma4oH8wpDUKdG3MM"}

17673
logs/app.log

File diff suppressed because it is too large Load diff

View file

@ -796,61 +796,26 @@ class PlaylistDetailsModal(QDialog):
def on_download_missing_tracks_clicked(self):
"""Handle Download Missing Tracks button click"""
print("🔄 Download Missing Tracks button clicked!")
if not self.playlist:
print("❌ No playlist selected")
QMessageBox.warning(self, "Error", "No playlist selected")
return
if not self.playlist.tracks:
print("❌ Playlist tracks not loaded")
if not self.playlist or not self.playlist.tracks:
QMessageBox.warning(self, "Error", "Playlist tracks not loaded")
return
print(f"✅ Playlist: {self.playlist.name} with {len(self.playlist.tracks)} tracks")
# Get access to parent's Plex and Soulseek clients through parent reference
if not hasattr(self.parent_page, 'plex_client'):
print("❌ Plex client not available")
QMessageBox.warning(self, "Service Unavailable",
"Plex client not available. Please check your configuration.")
playlist_item_widget = self.parent_page.find_playlist_item_widget(self.playlist.id)
if not playlist_item_widget:
QMessageBox.critical(self, "Error", "Could not find the associated playlist item on the main page.")
return
if not hasattr(self.parent_page, 'soulseek_client') or not self.parent_page.soulseek_client:
print("❌ Soulseek client not available")
QMessageBox.warning(self, "Service Unavailable",
"Soulseek client not available. Please check your configuration.")
return
print("✅ Plex and Soulseek clients available")
# Create and show the enhanced download missing tracks modal
try:
print("🚀 Creating modal...")
modal = DownloadMissingTracksModal(self.playlist, self.parent_page, self, self.parent_page.downloads_page)
print("✅ Modal created successfully")
# Store modal reference to prevent garbage collection
self.download_modal = modal
# Find and store modal reference in playlist item for reopening
playlist_item = self.find_playlist_item_from_sync_modal()
if playlist_item:
playlist_item.set_download_modal(modal)
print("🖥️ Closing current sync modal...")
self.accept() # Close the current sync modal
print("🖥️ Showing download modal...")
result = modal.exec()
print(f"✅ Modal closed with result: {result}")
except Exception as e:
print(f"❌ Exception creating modal: {e}")
import traceback
traceback.print_exc()
QMessageBox.critical(self, "Modal Error", f"Failed to open download modal: {str(e)}")
print("🚀 Creating DownloadMissingTracksModal...")
modal = DownloadMissingTracksModal(self.playlist, playlist_item_widget, self.parent_page, self.parent_page.downloads_page)
playlist_item_widget.download_modal = modal
self.parent_page.on_download_process_started(self.playlist.id, playlist_item_widget)
self.accept()
modal.show()
def find_playlist_item_from_sync_modal(self):
"""Find the PlaylistItem widget for this playlist from sync modal"""
if not hasattr(self.parent_page, 'current_playlists'):
@ -1164,8 +1129,9 @@ class PlaylistItem(QFrame):
self.name = name
self.track_count = track_count
self.sync_status = sync_status
self.playlist = playlist # Store playlist object reference
self.playlist = playlist
self.is_selected = False
self.download_modal = None # This line is new
self.setup_ui()
def setup_ui(self):
@ -1241,7 +1207,7 @@ class PlaylistItem(QFrame):
# Action button or status indicator
self.action_btn = QPushButton("Sync / Download")
self.action_btn.setFixedSize(120, 30) # Slightly wider for longer text
self.action_btn.clicked.connect(self.on_view_details_clicked)
self.action_btn.clicked.connect(self.on_action_clicked)
self.action_btn.setStyleSheet("""
QPushButton {
background: transparent;
@ -1288,17 +1254,34 @@ class PlaylistItem(QFrame):
layout.addWidget(self.action_btn)
layout.addWidget(self.status_label)
def show_operation_status(self, status_text="View Progress"):
"""Changes the button to show an operation is in progress."""
self.status_label.setText(status_text)
self.status_label.show()
self.action_btn.hide()
def hide_operation_status(self):
"""Resets the button to its default state."""
self.status_label.hide()
self.action_btn.show()
def mousePressEvent(self, event):
if event.button() == Qt.MouseButton.LeftButton:
self.checkbox.setChecked(not self.checkbox.isChecked())
super().mousePressEvent(event)
def on_view_details_clicked(self):
"""Handle View Details button click"""
if self.playlist:
def on_action_clicked(self):
"""
If a download is in progress, show the modal. Otherwise, open details.
"""
if self.download_modal:
self.download_modal.show()
self.download_modal.activateWindow()
else:
self.view_details_clicked.emit(self.playlist)
def show_operation_status(self, status_text):
def show_operation_status(self, status_text="View Progress"):
"""Show operation status and hide action button"""
self.status_label.setText(status_text)
self.status_label.show()
@ -1411,7 +1394,7 @@ class SyncPage(QWidget):
self.downloads_page = downloads_page
self.current_playlists = []
self.playlist_loader = None
self.active_download_processes = {}
# Track cache for performance
self.track_cache = {} # playlist_id -> tracks
@ -1425,6 +1408,42 @@ class SyncPage(QWidget):
self.show_initial_state()
self.playlists_loaded = False
# Add these three methods inside the SyncPage class
def find_playlist_item_widget(self, playlist_id):
"""Finds the PlaylistItem widget in the UI that corresponds to a given playlist ID."""
for i in range(self.playlist_layout.count()):
item = self.playlist_layout.itemAt(i)
widget = item.widget()
if isinstance(widget, PlaylistItem) and widget.playlist.id == playlist_id:
return widget
return None
def on_download_process_started(self, playlist_id, playlist_item_widget):
"""Disables refresh button and updates the playlist item UI."""
print(f"Download process started for playlist: {playlist_id}. Disabling refresh.")
self.active_download_processes[playlist_id] = playlist_item_widget
playlist_item_widget.show_operation_status()
self.refresh_btn.setEnabled(False)
self.refresh_btn.setText("🔄 Working...")
# Connect the finished signal from the modal
playlist_item_widget.download_modal.process_finished.connect(
lambda: self.on_download_process_finished(playlist_id)
)
def on_download_process_finished(self, playlist_id):
"""Re-enables refresh button if no other downloads are active."""
print(f"Download process finished for playlist: {playlist_id}.")
if playlist_id in self.active_download_processes:
playlist_item_widget = self.active_download_processes.pop(playlist_id)
playlist_item_widget.download_modal = None
playlist_item_widget.hide_operation_status()
if not self.active_download_processes:
print("All download processes finished. Re-enabling refresh.")
self.refresh_btn.setEnabled(True)
self.refresh_btn.setText("🔄 Refresh")
def showEvent(self, event):
"""Auto-load playlists when page becomes visible (but not during app startup)"""
super().showEvent(event)
@ -2417,13 +2436,12 @@ class ManualMatchModal(QDialog):
class DownloadMissingTracksModal(QDialog):
"""Enhanced modal for downloading missing tracks with live progress tracking"""
def __init__(self, playlist, parent_page, sync_modal, downloads_page):
print(f"🏗️ Initializing DownloadMissingTracksModal...")
super().__init__(sync_modal)
process_finished = pyqtSignal()
def __init__(self, playlist, playlist_item, parent_page, downloads_page):
super().__init__(parent_page)
self.playlist = playlist
self.playlist_item = playlist_item
self.parent_page = parent_page
self.sync_modal = sync_modal
self.downloads_page = downloads_page
# State tracking
@ -2507,7 +2525,7 @@ class DownloadMissingTracksModal(QDialog):
"""Set up the enhanced modal UI"""
self.setWindowTitle(f"Download Missing Tracks - {self.playlist.name}")
self.resize(1200, 900)
self.setModal(True)
self.setWindowFlags(Qt.WindowType.Window)
self.setWindowFlags(Qt.WindowType.Dialog)
self.setStyleSheet("""
@ -2811,6 +2829,7 @@ class DownloadMissingTracksModal(QDialog):
self.analysis_progress.setVisible(True)
self.analysis_progress.setMaximum(self.total_tracks)
self.analysis_progress.setValue(0)
self.download_in_progress = True # Set flag
self.start_plex_analysis()
def start_plex_analysis(self):
@ -3141,10 +3160,20 @@ class DownloadMissingTracksModal(QDialog):
print("🛑 All operations cancelled successfully")
def closeEvent(self, event):
"""Handle modal close event"""
self.download_status_timer.stop()
self.cancel_operations()
event.accept()
"""
Override close event. If the user clicks the 'X', we just hide the window.
The window is only truly closed (and destroyed) when the process is finished
or explicitly cancelled.
"""
if self.cancel_requested or not self.download_in_progress:
# If cancelled or finished, let it close for real.
self.cancel_operations()
self.process_finished.emit()
event.accept()
else:
# If downloads are running, just hide the window.
self.hide()
event.ignore()
# Inner class for the search worker
class ParallelSearchWorker(QRunnable):