This commit is contained in:
Broque Thomas 2025-08-18 18:32:13 -07:00
parent ceeb4c22dc
commit 7c84b687fb
2 changed files with 83 additions and 33 deletions

View file

@ -1816,14 +1816,15 @@ class DownloadMissingAlbumTracksModal(QDialog):
"""Enhanced modal for downloading missing album tracks with live progress tracking""" """Enhanced modal for downloading missing album tracks with live progress tracking"""
process_finished = pyqtSignal() process_finished = pyqtSignal()
def __init__(self, album, album_card, parent_page, downloads_page, plex_client): def __init__(self, album, album_card, parent_page, downloads_page, media_client, server_type):
super().__init__(parent_page) super().__init__(parent_page)
self.album = album self.album = album
self.album_card = album_card self.album_card = album_card
self.parent_page = parent_page self.parent_page = parent_page
self.parent_artists_page = parent_page # Reference to artists page for scan manager self.parent_artists_page = parent_page # Reference to artists page for scan manager
self.downloads_page = downloads_page self.downloads_page = downloads_page
self.plex_client = plex_client self.media_client = media_client
self.server_type = server_type
self.matching_engine = MusicMatchingEngine() self.matching_engine = MusicMatchingEngine()
self.wishlist_service = get_wishlist_service() self.wishlist_service = get_wishlist_service()
@ -2395,9 +2396,9 @@ class DownloadMissingAlbumTracksModal(QDialog):
self.start_plex_analysis() self.start_plex_analysis()
def start_plex_analysis(self): def start_plex_analysis(self):
"""Start database analysis for album tracks (previously Plex analysis)""" """Start database analysis for album tracks (server-aware)"""
from ui.pages.sync import PlaylistTrackAnalysisWorker from ui.pages.sync import PlaylistTrackAnalysisWorker
worker = PlaylistTrackAnalysisWorker(self.album.tracks, self.plex_client) worker = PlaylistTrackAnalysisWorker(self.album.tracks, self.media_client)
worker.signals.analysis_started.connect(self.on_analysis_started) worker.signals.analysis_started.connect(self.on_analysis_started)
worker.signals.track_analyzed.connect(self.on_track_analyzed) worker.signals.track_analyzed.connect(self.on_track_analyzed)
worker.signals.analysis_completed.connect(self.on_analysis_completed) worker.signals.analysis_completed.connect(self.on_analysis_completed)
@ -3291,7 +3292,7 @@ class ArtistsPage(QWidget):
# Create worker for incremental update only # Create worker for incremental update only
self._auto_database_worker = DatabaseUpdateWorker( self._auto_database_worker = DatabaseUpdateWorker(
self.plex_client, self.media_client,
"database/music_library.db", "database/music_library.db",
full_refresh=False # Always incremental for automatic updates full_refresh=False # Always incremental for automatic updates
) )
@ -3338,12 +3339,27 @@ class ArtistsPage(QWidget):
def setup_clients(self): def setup_clients(self):
"""Initialize client connections""" """Initialize client connections"""
try: try:
from config.settings import config_manager
self.spotify_client = SpotifyClient() self.spotify_client = SpotifyClient()
self.plex_client = PlexClient() self.plex_client = PlexClient()
# Add Jellyfin client for multi-server support
from core.jellyfin_client import JellyfinClient
self.jellyfin_client = JellyfinClient()
# Set up unified media client based on active server
active_server = config_manager.get_active_media_server()
if active_server == "plex":
self.media_client = self.plex_client
self.server_type = "plex"
else: # jellyfin
self.media_client = self.jellyfin_client
self.server_type = "jellyfin"
self.soulseek_client = SoulseekClient() self.soulseek_client = SoulseekClient()
# --- FIX: Ensure the soulseek_client uses the download path from config --- # --- FIX: Ensure the soulseek_client uses the download path from config ---
from config.settings import config_manager
download_path = config_manager.get('soulseek.download_path') download_path = config_manager.get('soulseek.download_path')
if download_path and hasattr(self.soulseek_client, 'download_path'): if download_path and hasattr(self.soulseek_client, 'download_path'):
self.soulseek_client.download_path = download_path self.soulseek_client.download_path = download_path
@ -4332,7 +4348,7 @@ class ArtistsPage(QWidget):
QMessageBox.critical(self, "Error", "Downloads page is not connected. Cannot start download.") QMessageBox.critical(self, "Error", "Downloads page is not connected. Cannot start download.")
return return
if not self.plex_client: if not self.media_client:
QMessageBox.critical(self, "Error", "Music database is not available. Cannot verify existing tracks.") QMessageBox.critical(self, "Error", "Music database is not available. Cannot verify existing tracks.")
return return
@ -4414,12 +4430,28 @@ class ArtistsPage(QWidget):
album_with_tracks.tracks = tracks # Add tracks attribute dynamically album_with_tracks.tracks = tracks # Add tracks attribute dynamically
# Create and show the new sophisticated modal # Create and show the new sophisticated modal
# Get active server and media client
from config.settings import config_manager
active_server = config_manager.get_active_media_server()
if active_server == "jellyfin":
media_client = getattr(self, 'jellyfin_client', None)
if not media_client:
QMessageBox.critical(self, "Error", "Jellyfin client not available")
return
else:
media_client = self.plex_client
if not media_client:
QMessageBox.critical(self, "Error", "Plex client not available")
return
modal = DownloadMissingAlbumTracksModal( modal = DownloadMissingAlbumTracksModal(
album=album_with_tracks, # Use the album with tracks album=album_with_tracks, # Use the album with tracks
album_card=album_card, album_card=album_card,
parent_page=self, parent_page=self,
downloads_page=self.downloads_page, downloads_page=self.downloads_page,
plex_client=self.plex_client media_client=media_client,
server_type=active_server
) )
# Store the session for resumption # Store the session for resumption

View file

@ -521,12 +521,13 @@ class PlaylistTrackAnalysisWorkerSignals(QObject):
analysis_failed = pyqtSignal(str) # error_message analysis_failed = pyqtSignal(str) # error_message
class PlaylistTrackAnalysisWorker(QRunnable): class PlaylistTrackAnalysisWorker(QRunnable):
"""Background worker to analyze playlist tracks against Plex library""" """Background worker to analyze playlist tracks against media library"""
def __init__(self, playlist_tracks, plex_client): def __init__(self, playlist_tracks, media_client, server_type="plex"):
super().__init__() super().__init__()
self.playlist_tracks = playlist_tracks self.playlist_tracks = playlist_tracks
self.plex_client = plex_client self.media_client = media_client # Can be plex_client or jellyfin_client
self.server_type = server_type
self.signals = PlaylistTrackAnalysisWorkerSignals() self.signals = PlaylistTrackAnalysisWorkerSignals()
self._cancelled = False self._cancelled = False
# Instantiate the matching engine once per worker for efficiency # Instantiate the matching engine once per worker for efficiency
@ -545,14 +546,14 @@ class PlaylistTrackAnalysisWorker(QRunnable):
self.signals.analysis_started.emit(len(self.playlist_tracks)) self.signals.analysis_started.emit(len(self.playlist_tracks))
results = [] results = []
# Check if Plex is connected # Check if media server is connected
plex_connected = False server_connected = False
try: try:
if self.plex_client: if self.media_client:
plex_connected = self.plex_client.is_connected() server_connected = self.media_client.is_connected()
except Exception as e: except Exception as e:
print(f"Plex connection check failed: {e}") print(f"{self.server_type.title()} connection check failed: {e}")
plex_connected = False server_connected = False
for i, track in enumerate(self.playlist_tracks): for i, track in enumerate(self.playlist_tracks):
if self._cancelled: if self._cancelled:
@ -563,17 +564,17 @@ class PlaylistTrackAnalysisWorker(QRunnable):
exists_in_plex=False exists_in_plex=False
) )
if plex_connected: if server_connected:
# Check if track exists in Plex # Check if track exists in media server
try: try:
plex_match, confidence = self._check_track_in_plex(track) match, confidence = self._check_track_in_library(track)
# Use the 0.8 confidence threshold # Use the 0.8 confidence threshold
if plex_match and confidence >= 0.8: if match and confidence >= 0.8:
result.exists_in_plex = True result.exists_in_plex = True # Keep existing field name for compatibility
result.plex_match = plex_match result.plex_match = match # Keep existing field name for compatibility
result.confidence = confidence result.confidence = confidence
except Exception as e: except Exception as e:
result.error_message = f"Plex check failed: {str(e)}" result.error_message = f"{self.server_type.title()} check failed: {str(e)}"
results.append(result) results.append(result)
self.signals.track_analyzed.emit(i + 1, result) self.signals.track_analyzed.emit(i + 1, result)
@ -587,11 +588,11 @@ class PlaylistTrackAnalysisWorker(QRunnable):
traceback.print_exc() traceback.print_exc()
self.signals.analysis_failed.emit(str(e)) self.signals.analysis_failed.emit(str(e))
def _check_track_in_plex(self, spotify_track): def _check_track_in_library(self, spotify_track):
""" """
Check if a Spotify track exists in the database by searching for each artist and Check if a Spotify track exists in the database by searching for each artist and
stopping as soon as a confident match is found. stopping as soon as a confident match is found.
Now uses local database instead of Plex API for much faster performance. Now uses local database instead of media server API for much faster performance.
""" """
try: try:
original_title = spotify_track.name original_title = spotify_track.name
@ -1715,14 +1716,24 @@ class PlaylistDetailsModal(QDialog):
self.start_track_analysis() self.start_track_analysis()
# Show analysis started message # Show analysis started message
from config.settings import config_manager
active_server = config_manager.get_active_media_server()
server_name = active_server.title()
QMessageBox.information(self, "Analysis Started", QMessageBox.information(self, "Analysis Started",
f"Starting analysis of {track_count} tracks.\nChecking Plex library for existing tracks...") f"Starting analysis of {track_count} tracks.\nChecking {server_name} library for existing tracks...")
def start_track_analysis(self): def start_track_analysis(self):
"""Start background track analysis against Plex library""" """Start background track analysis against media library"""
# Create analysis worker # Create analysis worker
plex_client = getattr(self.parent_page, 'plex_client', None) from config.settings import config_manager
worker = PlaylistTrackAnalysisWorker(self.playlist.tracks, plex_client) active_server = config_manager.get_active_media_server()
if active_server == "plex":
media_client = getattr(self.parent_page, 'plex_client', None)
else: # jellyfin
media_client = getattr(self.parent_page, 'jellyfin_client', None)
worker = PlaylistTrackAnalysisWorker(self.playlist.tracks, media_client, active_server)
# Connect signals # Connect signals
worker.signals.analysis_started.connect(self.on_analysis_started) worker.signals.analysis_started.connect(self.on_analysis_started)
@ -6699,9 +6710,16 @@ class DownloadMissingTracksModal(QDialog):
def start_plex_analysis(self): def start_plex_analysis(self):
"""Start Plex analysis using existing worker""" """Start media server analysis using existing worker"""
plex_client = getattr(self.parent_page, 'plex_client', None) from config.settings import config_manager
worker = PlaylistTrackAnalysisWorker(self.playlist.tracks, plex_client) active_server = config_manager.get_active_media_server()
if active_server == "plex":
media_client = getattr(self.parent_page, 'plex_client', None)
else: # jellyfin
media_client = getattr(self.parent_page, 'jellyfin_client', None)
worker = PlaylistTrackAnalysisWorker(self.playlist.tracks, media_client, active_server)
worker.signals.analysis_started.connect(self.on_analysis_started) worker.signals.analysis_started.connect(self.on_analysis_started)
worker.signals.track_analyzed.connect(self.on_track_analyzed) worker.signals.track_analyzed.connect(self.on_track_analyzed)
worker.signals.analysis_completed.connect(self.on_analysis_completed) worker.signals.analysis_completed.connect(self.on_analysis_completed)