good
This commit is contained in:
parent
32bfdbaefe
commit
212d7d3cac
4 changed files with 8762 additions and 19 deletions
|
|
@ -1 +1 @@
|
||||||
{"access_token": "BQBLgCrkibuxPE8cTnfcwLA71kNHLIXZzSM_AJREWUi-6DqRirCDmGJSYfIt4PCBKGi43N6w20u1dmFo8Iyrxnx6V9I7GnMdrYjAtzO8Ooep_hoHT5bTrUitQ9Wk2AFm8vJih1_IBCxivds2GsVjupYXTKc3QcURVtm2rGGnRGpExwe9F8tsdXKJfC-wzzhWp9GnGySTXP43whzShjOl0L-TBL8_SpJKWtJi6_2e99-as9r0r_OmjiFzD9Oem4BG", "token_type": "Bearer", "expires_in": 3600, "scope": "user-library-read user-read-private playlist-read-private playlist-read-collaborative user-read-email", "expires_at": 1753595655, "refresh_token": "AQDmfQkPCGObfJeTUIbW1hAAwhSqkuHRA3Qh2dqVYMRh0eCkFMQgPNJDDzF8y-BiaVbj80zePkK_XSfYH1aJutMtNbnsqRKWuxP31BTrMc7pdUdbE7Fma4oH8wpDUKdG3MM"}
|
{"access_token": "BQAYT_i6OdAX8By8I4sb5ahKTItJ2tfF5UC5wo2yYvVO_vUuq396z82TZSoIWC2OxiUuCW4d2YlwoBB3e1btUvn90gk6vYBzQJC9400TE97ktF0q2hM6kZ-d4WEYUXfxZ_oK-0wAykADbb9p1iDomMJmypjy5KRgPAMx57nf5GoUy60F4k90p9zPg07piLkgTlsTkSo0-yJAxxAlpcSDXWHU6kYzbWVBxDlEHDhwhTYptWwZZwE7AjsbvKeoDvXs", "token_type": "Bearer", "expires_in": 3600, "scope": "user-library-read user-read-private playlist-read-private playlist-read-collaborative user-read-email", "expires_at": 1753599443, "refresh_token": "AQDmfQkPCGObfJeTUIbW1hAAwhSqkuHRA3Qh2dqVYMRh0eCkFMQgPNJDDzF8y-BiaVbj80zePkK_XSfYH1aJutMtNbnsqRKWuxP31BTrMc7pdUdbE7Fma4oH8wpDUKdG3MM"}
|
||||||
8606
logs/app.log
8606
logs/app.log
File diff suppressed because it is too large
Load diff
Binary file not shown.
|
|
@ -1217,16 +1217,24 @@ class AlbumCard(QFrame):
|
||||||
|
|
||||||
def set_download_completed(self):
|
def set_download_completed(self):
|
||||||
"""Set album card to download completed state"""
|
"""Set album card to download completed state"""
|
||||||
# Hide progress overlay
|
try:
|
||||||
self.progress_overlay.hide()
|
# Hide progress overlay if it still exists
|
||||||
|
if hasattr(self, 'progress_overlay') and self.progress_overlay is not None:
|
||||||
# Update to owned state
|
try:
|
||||||
self.update_ownership(True)
|
self.progress_overlay.hide()
|
||||||
|
except RuntimeError:
|
||||||
# Show completion message briefly
|
# Widget has been deleted, ignore
|
||||||
self.progress_overlay.setText("✅\nCompleted!")
|
pass
|
||||||
self.progress_overlay.setStyleSheet("""
|
|
||||||
QLabel {
|
# Update to owned state
|
||||||
|
self.update_ownership(True)
|
||||||
|
|
||||||
|
# Show completion message briefly if overlay still exists
|
||||||
|
if hasattr(self, 'progress_overlay') and self.progress_overlay is not None:
|
||||||
|
try:
|
||||||
|
self.progress_overlay.setText("✅\nCompleted!")
|
||||||
|
self.progress_overlay.setStyleSheet("""
|
||||||
|
QLabel {
|
||||||
background: rgba(29, 185, 84, 0.9);
|
background: rgba(29, 185, 84, 0.9);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
color: white;
|
color: white;
|
||||||
|
|
@ -1235,10 +1243,30 @@ class AlbumCard(QFrame):
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
""")
|
""")
|
||||||
self.progress_overlay.show()
|
self.progress_overlay.show()
|
||||||
|
|
||||||
# Hide completion message after 3 seconds
|
# Hide completion message after 3 seconds
|
||||||
QTimer.singleShot(3000, self.progress_overlay.hide)
|
QTimer.singleShot(3000, lambda: self.safe_hide_overlay())
|
||||||
|
except RuntimeError:
|
||||||
|
# Widget has been deleted, ignore
|
||||||
|
pass
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Error in set_download_completed: {e}")
|
||||||
|
# Still try to update ownership even if overlay fails
|
||||||
|
try:
|
||||||
|
self.update_ownership(True)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def safe_hide_overlay(self):
|
||||||
|
"""Safely hide the progress overlay with error checking"""
|
||||||
|
try:
|
||||||
|
if hasattr(self, 'progress_overlay') and self.progress_overlay is not None:
|
||||||
|
self.progress_overlay.hide()
|
||||||
|
except RuntimeError:
|
||||||
|
# Widget has been deleted, ignore
|
||||||
|
pass
|
||||||
|
|
||||||
def mousePressEvent(self, event):
|
def mousePressEvent(self, event):
|
||||||
"""Handle click for download"""
|
"""Handle click for download"""
|
||||||
|
|
@ -1699,6 +1727,8 @@ class DownloadMissingAlbumTracksModal(QDialog):
|
||||||
self.cancel_btn.hide()
|
self.cancel_btn.hide()
|
||||||
self.process_finished.emit()
|
self.process_finished.emit()
|
||||||
QMessageBox.information(self, "Analysis Complete", "All album tracks already exist in Plex! No downloads needed.")
|
QMessageBox.information(self, "Analysis Complete", "All album tracks already exist in Plex! No downloads needed.")
|
||||||
|
# Close with accept since all tracks are already available (success case)
|
||||||
|
self.accept()
|
||||||
|
|
||||||
def on_analysis_failed(self, error_message):
|
def on_analysis_failed(self, error_message):
|
||||||
print(f"❌ Album analysis failed: {error_message}")
|
print(f"❌ Album analysis failed: {error_message}")
|
||||||
|
|
@ -2038,6 +2068,9 @@ class DownloadMissingAlbumTracksModal(QDialog):
|
||||||
final_message = f"Completed downloading {self.successful_downloads}/{len(self.missing_tracks)} missing album tracks!\n\nAll tracks were downloaded successfully!"
|
final_message = f"Completed downloading {self.successful_downloads}/{len(self.missing_tracks)} missing album tracks!\n\nAll tracks were downloaded successfully!"
|
||||||
|
|
||||||
QMessageBox.information(self, "Downloads Complete", final_message)
|
QMessageBox.information(self, "Downloads Complete", final_message)
|
||||||
|
|
||||||
|
# Close the modal with accept() to indicate successful completion
|
||||||
|
self.accept()
|
||||||
|
|
||||||
def get_valid_candidates(self, results, spotify_track, query):
|
def get_valid_candidates(self, results, spotify_track, query):
|
||||||
"""Score and filter search results, then perform strict artist verification"""
|
"""Score and filter search results, then perform strict artist verification"""
|
||||||
|
|
@ -2199,6 +2232,9 @@ class ArtistsPage(QWidget):
|
||||||
self.download_status_pool.setMaxThreadCount(1) # One worker at a time to avoid conflicts
|
self.download_status_pool.setMaxThreadCount(1) # One worker at a time to avoid conflicts
|
||||||
self._is_status_update_running = False
|
self._is_status_update_running = False
|
||||||
|
|
||||||
|
# Album download session management
|
||||||
|
self.active_album_sessions = {} # {album_id: {'modal': modal_ref, 'album_with_tracks': album_obj}}
|
||||||
|
|
||||||
# UI setup
|
# UI setup
|
||||||
self.setup_ui()
|
self.setup_ui()
|
||||||
self.setup_clients()
|
self.setup_clients()
|
||||||
|
|
@ -2778,6 +2814,30 @@ class ArtistsPage(QWidget):
|
||||||
QMessageBox.critical(self, "Error", "Plex client is not available. Cannot verify existing tracks.")
|
QMessageBox.critical(self, "Error", "Plex client is not available. Cannot verify existing tracks.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Check if there's already an active session for this album
|
||||||
|
if album.id in self.active_album_sessions:
|
||||||
|
print(f"🔄 Resuming existing download session for album: {album.name}")
|
||||||
|
existing_session = self.active_album_sessions[album.id]
|
||||||
|
existing_modal = existing_session.get('modal')
|
||||||
|
|
||||||
|
# Check if the modal still exists and is valid
|
||||||
|
if existing_modal and not existing_modal.isVisible():
|
||||||
|
try:
|
||||||
|
# Show the existing modal
|
||||||
|
existing_modal.show()
|
||||||
|
existing_modal.activateWindow()
|
||||||
|
existing_modal.raise_()
|
||||||
|
return
|
||||||
|
except RuntimeError:
|
||||||
|
# Modal was deleted, remove from sessions
|
||||||
|
print("⚠️ Existing modal was deleted, creating new session")
|
||||||
|
del self.active_album_sessions[album.id]
|
||||||
|
elif existing_modal and existing_modal.isVisible():
|
||||||
|
# Modal is already visible, just bring it to front
|
||||||
|
existing_modal.activateWindow()
|
||||||
|
existing_modal.raise_()
|
||||||
|
return
|
||||||
|
|
||||||
print("🚀 Fetching album tracks and creating DownloadMissingAlbumTracksModal...")
|
print("🚀 Fetching album tracks and creating DownloadMissingAlbumTracksModal...")
|
||||||
|
|
||||||
# First, we need to fetch the tracks for this album
|
# First, we need to fetch the tracks for this album
|
||||||
|
|
@ -2823,8 +2883,15 @@ class ArtistsPage(QWidget):
|
||||||
plex_client=self.plex_client
|
plex_client=self.plex_client
|
||||||
)
|
)
|
||||||
|
|
||||||
# Connect the process finished signal to handle cleanup
|
# Store the session for resumption
|
||||||
modal.process_finished.connect(lambda: self.on_album_download_process_finished(album.id, album_card))
|
self.active_album_sessions[album.id] = {
|
||||||
|
'modal': modal,
|
||||||
|
'album_with_tracks': album_with_tracks,
|
||||||
|
'album_card': album_card
|
||||||
|
}
|
||||||
|
|
||||||
|
# Connect signals to handle cleanup - only use modal.finished to avoid double handling
|
||||||
|
modal.finished.connect(lambda result: self.on_album_modal_closed(album.id, album_card, result))
|
||||||
|
|
||||||
# Show the modal
|
# Show the modal
|
||||||
modal.exec()
|
modal.exec()
|
||||||
|
|
@ -2834,15 +2901,62 @@ class ArtistsPage(QWidget):
|
||||||
QMessageBox.critical(self, "Error", f"Failed to fetch album tracks: {str(e)}\n\nPlease check your Spotify connection and try again.")
|
QMessageBox.critical(self, "Error", f"Failed to fetch album tracks: {str(e)}\n\nPlease check your Spotify connection and try again.")
|
||||||
|
|
||||||
def on_album_download_process_finished(self, album_id: str, album_card):
|
def on_album_download_process_finished(self, album_id: str, album_card):
|
||||||
"""Handle cleanup when album download process is finished"""
|
"""Handle cleanup when album download process is actually finished (downloads completed)"""
|
||||||
print(f"🏁 Album download process finished for album: {album_id}")
|
print(f"🏁 Album download process finished for album: {album_id}")
|
||||||
|
|
||||||
# Reset the album card state if needed
|
# Only mark as completed if downloads actually finished successfully
|
||||||
if album_card and hasattr(album_card, 'set_download_completed'):
|
if album_card and hasattr(album_card, 'set_download_completed'):
|
||||||
album_card.set_download_completed()
|
album_card.set_download_completed()
|
||||||
|
print(f"✅ Marked album {album_id} as download completed")
|
||||||
|
|
||||||
print("✅ Album download process cleanup completed")
|
print("✅ Album download process cleanup completed")
|
||||||
|
|
||||||
|
def on_album_modal_closed(self, album_id: str, album_card, result):
|
||||||
|
"""Handle cleanup when album modal is closed (regardless of reason)"""
|
||||||
|
print(f"📋 Album modal closed for album: {album_id}, result: {'Accepted' if result == 1 else 'Rejected/Cancelled'}")
|
||||||
|
|
||||||
|
# Clean up the session when modal is definitely closing
|
||||||
|
if album_id in self.active_album_sessions:
|
||||||
|
if result == 1: # QDialog.Accepted = 1 (downloads completed or all tracks exist)
|
||||||
|
# Remove session since downloads are complete
|
||||||
|
del self.active_album_sessions[album_id]
|
||||||
|
print(f"🗑️ Removed completed session for album {album_id}")
|
||||||
|
else:
|
||||||
|
# Keep session for resumption, but hide the modal for now
|
||||||
|
print(f"💾 Keeping session for album {album_id} for potential resumption")
|
||||||
|
|
||||||
|
if album_card:
|
||||||
|
try:
|
||||||
|
if result == 1: # QDialog.Accepted = 1 (downloads actually completed)
|
||||||
|
# Only mark as completed if downloads were actually successful
|
||||||
|
if hasattr(album_card, 'set_download_completed'):
|
||||||
|
album_card.set_download_completed()
|
||||||
|
print(f"✅ Marked album {album_id} as download completed")
|
||||||
|
else:
|
||||||
|
# Modal was cancelled/closed - reset the card to allow reopening (but keep session)
|
||||||
|
# Reset any download-in-progress indicators
|
||||||
|
if hasattr(album_card, 'progress_overlay') and album_card.progress_overlay:
|
||||||
|
try:
|
||||||
|
album_card.progress_overlay.hide()
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Reset the card to allow clicking again (if not already owned)
|
||||||
|
if not album_card.is_owned:
|
||||||
|
# Show a visual indicator that this album has an active session
|
||||||
|
if hasattr(album_card, 'status_indicator'):
|
||||||
|
try:
|
||||||
|
album_card.status_indicator.setText("▶️")
|
||||||
|
album_card.status_indicator.setToolTip("Click to resume download session")
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
print(f"🔄 Reset album card for {album_id} to allow resumption")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Error handling album card state: {e}")
|
||||||
|
|
||||||
|
print("✅ Album modal cleanup completed")
|
||||||
|
|
||||||
# === LEGACY METHODS - NO LONGER USED WITH NEW MODAL SYSTEM ===
|
# === LEGACY METHODS - NO LONGER USED WITH NEW MODAL SYSTEM ===
|
||||||
# These methods were part of the old manual album download flow
|
# These methods were part of the old manual album download flow
|
||||||
# Keeping them commented for reference but they are replaced by DownloadMissingAlbumTracksModal
|
# Keeping them commented for reference but they are replaced by DownloadMissingAlbumTracksModal
|
||||||
|
|
@ -3583,6 +3697,26 @@ class ArtistsPage(QWidget):
|
||||||
|
|
||||||
print("🧹 Album download tracking cleanup completed")
|
print("🧹 Album download tracking cleanup completed")
|
||||||
|
|
||||||
|
def cleanup_album_sessions(self):
|
||||||
|
"""Clean up active album download sessions"""
|
||||||
|
if not self.active_album_sessions:
|
||||||
|
return
|
||||||
|
|
||||||
|
session_count = len(self.active_album_sessions)
|
||||||
|
print(f"🧹 Cleaning up {session_count} active album sessions...")
|
||||||
|
|
||||||
|
for album_id, session in list(self.active_album_sessions.items()):
|
||||||
|
try:
|
||||||
|
modal = session.get('modal')
|
||||||
|
if modal:
|
||||||
|
modal.cancel_operations()
|
||||||
|
modal.close()
|
||||||
|
except Exception as e:
|
||||||
|
print(f" ⚠️ Error cleaning up session for album {album_id}: {e}")
|
||||||
|
|
||||||
|
self.active_album_sessions.clear()
|
||||||
|
print(f"🧹 Cleaned up {session_count} album sessions")
|
||||||
|
|
||||||
def restart_download_tracking(self):
|
def restart_download_tracking(self):
|
||||||
"""Restart download tracking timer if stopped"""
|
"""Restart download tracking timer if stopped"""
|
||||||
if hasattr(self, 'download_status_timer') and not self.download_status_timer.isActive():
|
if hasattr(self, 'download_status_timer') and not self.download_status_timer.isActive():
|
||||||
|
|
@ -3632,6 +3766,9 @@ class ArtistsPage(QWidget):
|
||||||
# Stop download tracking (this includes its own worker cleanup)
|
# Stop download tracking (this includes its own worker cleanup)
|
||||||
self.cleanup_download_tracking()
|
self.cleanup_download_tracking()
|
||||||
|
|
||||||
|
# Clean up active album sessions
|
||||||
|
self.cleanup_album_sessions()
|
||||||
|
|
||||||
print("🛑 All workers stopped")
|
print("🛑 All workers stopped")
|
||||||
|
|
||||||
def clear_artist_results(self):
|
def clear_artist_results(self):
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue