From 1bc5017592e98e74489cfbcb4db836181b7104e3 Mon Sep 17 00:00:00 2001 From: Broque Thomas <26755000+Nezreka@users.noreply.github.com> Date: Tue, 5 May 2026 17:40:27 -0700 Subject: [PATCH] MS Cin-3 + Cin-4: Route web_server through engine instead of per-client globals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pre-change web_server.py had ~70 direct attribute reaches against the per-server globals (plex_client.X, jellyfin_client.X, navidrome_client.X, soulsync_library_client.X) plus ~60 standalone refs (truthy checks, media_client assignments, source-name tuples). The engine was wired but only used in 4 places, so most of the codebase still hand-dispatched — the exact "partially defeats the purpose of this refactor" critique Cin landed on the download PR initially. - All ~70 client.attribute reaches migrated to media_server_engine.client('').attribute. The chains in web_server.py do server-specific work (Plex raw API, Jellyfin / Navidrome client methods, all returning different shapes), so the if/elif structure stays — but the per-server CLIENT REACH now goes through the engine like Cin's POC pattern intended. - All ~60 standalone refs migrated: - if plex_client → if media_server_engine.client('plex') - media_client = plex_client → media_client = media_server_engine.client('plex') - ('plex', plex_client) tuples → ('plex', media_server_engine.client('plex')) - Per-server globals (plex_client / jellyfin_client / navidrome_client / soulsync_library_client) kept for now — external modules (PlaylistSyncService, WebScanManager, ListeningStatsWorker, search library check, discovery sync deps) still take them as kwargs. Dropping them entirely needs a follow-up sweep across those modules. Suite green (1961 pass). --- web_server.py | 220 +++++++++++++++++++++++++------------------------- 1 file changed, 110 insertions(+), 110 deletions(-) diff --git a/web_server.py b/web_server.py index d913d0b3..0c554964 100644 --- a/web_server.py +++ b/web_server.py @@ -644,7 +644,7 @@ except Exception as e: logger.error(f" Matching engine failed to initialize: {e}") try: - sync_service = PlaylistSyncService(spotify_client, plex_client, soulseek_client, jellyfin_client, navidrome_client) + sync_service = PlaylistSyncService(spotify_client, plex_client, soulseek_client, jellyfin_client, media_server_engine.client('navidrome')) logger.info(" Playlist sync service initialized") except Exception as e: logger.error(f" Playlist sync service failed to initialize: {e}") @@ -4154,12 +4154,12 @@ def handle_settings(): # Reload service clients with new settings (guard against None from partial init) if spotify_client: spotify_client.reload_config() - if plex_client: - plex_client.server = None - if jellyfin_client: - jellyfin_client.reload_config() - if navidrome_client: - navidrome_client.reload_config() + if media_server_engine.client('plex'): + media_server_engine.client('plex').server = None + if media_server_engine.client('jellyfin'): + media_server_engine.client('jellyfin').reload_config() + if media_server_engine.client('navidrome'): + media_server_engine.client('navidrome').reload_config() # Reload orchestrator settings (download source mode, hybrid_primary, etc.) if soulseek_client: soulseek_client.reload_settings() @@ -5122,8 +5122,8 @@ def clear_plex_library_preference(): from database.music_database import MusicDatabase db = MusicDatabase() db.set_preference('plex_music_library', '') - if plex_client: - plex_client.music_library = None + if media_server_engine.client('plex'): + media_server_engine.client('plex').music_library = None return jsonify({"success": True, "message": "Plex library preference cleared."}) except Exception as e: logger.error(f"Error clearing Plex library preference: {e}") @@ -5134,7 +5134,7 @@ def clear_plex_library_preference(): def get_plex_music_libraries(): """Get list of all available music libraries from Plex""" try: - libraries = plex_client.get_available_music_libraries() + libraries = media_server_engine.client('plex').get_available_music_libraries() # Get currently selected library from database.music_database import MusicDatabase @@ -5143,8 +5143,8 @@ def get_plex_music_libraries(): # Get the currently active library name current_library = None - if plex_client.music_library: - current_library = plex_client.music_library.title + if media_server_engine.client('plex').music_library: + current_library = media_server_engine.client('plex').music_library.title return jsonify({ "success": True, @@ -5166,7 +5166,7 @@ def select_plex_music_library(): if not library_name: return jsonify({"success": False, "error": "No library name provided"}), 400 - success = plex_client.set_music_library_by_name(library_name) + success = media_server_engine.client('plex').set_music_library_by_name(library_name) if success: add_activity_item("", "Library Selected", f"Plex music library set to: {library_name}", "Now") @@ -5182,7 +5182,7 @@ def select_plex_music_library(): def get_jellyfin_users(): """Get list of Jellyfin users that have music libraries""" try: - users = jellyfin_client.get_available_users() + users = media_server_engine.client('jellyfin').get_available_users() # Get currently selected user from database.music_database import MusicDatabase @@ -5191,9 +5191,9 @@ def get_jellyfin_users(): # Determine the current user name from user_id current_user = None - if jellyfin_client.user_id: + if media_server_engine.client('jellyfin').user_id: for u in users: - if u['id'] == jellyfin_client.user_id: + if u['id'] == media_server_engine.client('jellyfin').user_id: current_user = u['name'] break @@ -5217,7 +5217,7 @@ def select_jellyfin_user(): if not username: return jsonify({"success": False, "error": "No username provided"}), 400 - success = jellyfin_client.set_user_by_name(username) + success = media_server_engine.client('jellyfin').set_user_by_name(username) if success: add_activity_item("", "User Selected", f"Jellyfin user set to: {username}", "Now") @@ -5233,7 +5233,7 @@ def select_jellyfin_user(): def get_jellyfin_music_libraries(): """Get list of all available music libraries from Jellyfin""" try: - libraries = jellyfin_client.get_available_music_libraries() + libraries = media_server_engine.client('jellyfin').get_available_music_libraries() # Get currently selected library from database.music_database import MusicDatabase @@ -5242,10 +5242,10 @@ def get_jellyfin_music_libraries(): # Get the currently active library name (match Plex behavior) current_library = None - if jellyfin_client.music_library_id: + if media_server_engine.client('jellyfin').music_library_id: # Look up library name from ID for lib in libraries: - if lib['key'] == jellyfin_client.music_library_id: + if lib['key'] == media_server_engine.client('jellyfin').music_library_id: current_library = lib['title'] break @@ -5269,7 +5269,7 @@ def select_jellyfin_music_library(): if not library_name: return jsonify({"success": False, "error": "No library name provided"}), 400 - success = jellyfin_client.set_music_library_by_name(library_name) + success = media_server_engine.client('jellyfin').set_music_library_by_name(library_name) if success: add_activity_item("", "Library Selected", f"Jellyfin music library set to: {library_name}", "Now") @@ -5285,19 +5285,19 @@ def select_jellyfin_music_library(): def get_navidrome_music_folders(): """Get list of available music folders from Navidrome""" try: - if not navidrome_client: + if not media_server_engine.client('navidrome'): return jsonify({"success": False, "error": "Navidrome client not configured"}), 400 - folders = navidrome_client.get_music_folders() + folders = media_server_engine.client('navidrome').get_music_folders() from database.music_database import MusicDatabase db = MusicDatabase() selected_folder = db.get_preference('navidrome_music_folder') current_folder = None - if navidrome_client.music_folder_id: + if media_server_engine.client('navidrome').music_folder_id: for f in folders: - if f['key'] == navidrome_client.music_folder_id: + if f['key'] == media_server_engine.client('navidrome').music_folder_id: current_folder = f['title'] break @@ -5318,7 +5318,7 @@ def select_navidrome_music_folder(): data = request.get_json() folder_name = data.get('folder_name', '') - success = navidrome_client.set_music_folder_by_name(folder_name) + success = media_server_engine.client('navidrome').set_music_folder_by_name(folder_name) if success: if folder_name: @@ -8046,9 +8046,9 @@ def test_automation_workflow(): results['media_clients'] = {'active_server': active_server} for client_name, client in [ - ('plex', plex_client), - ('jellyfin', jellyfin_client), - ('navidrome', navidrome_client) + ('plex', media_server_engine.client('plex')), + ('jellyfin', media_server_engine.client('jellyfin')), + ('navidrome', media_server_engine.client('navidrome')) ]: try: is_connected = client.is_connected() if client else False @@ -10810,7 +10810,7 @@ def _sync_tracks_to_server(track_rows, server_type): if track_data.get('year'): metadata['year'] = track_data['year'] if metadata: - success = plex_client.update_track_metadata(str(track_data['id']), metadata) + success = media_server_engine.client('plex').update_track_metadata(str(track_data['id']), metadata) if success: result['synced'] += 1 else: @@ -10825,7 +10825,7 @@ def _sync_tracks_to_server(track_rows, server_type): elif server_type == 'jellyfin': # Jellyfin: just trigger a library scan once after all file writes try: - success = jellyfin_client.trigger_library_scan() + success = media_server_engine.client('jellyfin').trigger_library_scan() if success: result['synced'] = len(track_rows) else: @@ -10853,8 +10853,8 @@ def _resolve_library_file_path(file_path): # Also check the media server's music library path (handles Docker↔host path mismatch) library_dirs = set() try: - if plex_client and plex_client.server and plex_client.music_library: - for loc in plex_client.music_library.locations: + if media_server_engine.client('plex') and media_server_engine.client('plex').server and media_server_engine.client('plex').music_library: + for loc in media_server_engine.client('plex').music_library.locations: library_dirs.add(loc) except Exception: pass @@ -11526,9 +11526,9 @@ def redownload_search_metadata(track_id): if thumb_url and not thumb_url.startswith('http'): _ab = '' _at = '' - if plex_client and plex_client.server: - _ab = getattr(plex_client.server, '_baseurl', '') or '' - _at = getattr(plex_client.server, '_token', '') or '' + if media_server_engine.client('plex') and media_server_engine.client('plex').server: + _ab = getattr(media_server_engine.client('plex').server, '_baseurl', '') or '' + _at = getattr(media_server_engine.client('plex').server, '_token', '') or '' if not _ab: _pc = config_manager.get_plex_config() _ab = (_pc.get('base_url', '') or '').rstrip('/') @@ -11834,12 +11834,12 @@ def sync_artist_library(artist_id): if server_source: media_client = None - if server_source == 'plex' and plex_client and plex_client.server: - media_client = plex_client - elif server_source == 'jellyfin' and jellyfin_client: - media_client = jellyfin_client - elif server_source == 'navidrome' and navidrome_client: - media_client = navidrome_client + if server_source == 'plex' and media_server_engine.client('plex') and media_server_engine.client('plex').server: + media_client = media_server_engine.client('plex') + elif server_source == 'jellyfin' and media_server_engine.client('jellyfin'): + media_client = media_server_engine.client('jellyfin') + elif server_source == 'navidrome' and media_server_engine.client('navidrome'): + media_client = media_server_engine.client('navidrome') if media_client: try: @@ -15122,11 +15122,11 @@ def _run_db_update_task(full_refresh, server_type): media_client = None if server_type == "plex": - media_client = plex_client + media_client = media_server_engine.client('plex') elif server_type == "jellyfin": - media_client = jellyfin_client + media_client = media_server_engine.client('jellyfin') elif server_type == "navidrome": - media_client = navidrome_client + media_client = media_server_engine.client('navidrome') if not media_client: _db_update_error_callback(f"Media client for '{server_type}' not available.") @@ -15168,11 +15168,11 @@ def _run_deep_scan_task(server_type): media_client = None if server_type == "plex": - media_client = plex_client + media_client = media_server_engine.client('plex') elif server_type == "jellyfin": - media_client = jellyfin_client + media_client = media_server_engine.client('jellyfin') elif server_type == "navidrome": - media_client = navidrome_client + media_client = media_server_engine.client('navidrome') elif server_type == "soulsync": # SoulSync standalone deep scan: find untracked files → move to Staging, # remove stale DB records where files no longer exist on disk @@ -18045,10 +18045,10 @@ def get_server_playlists(): return jsonify({"success": False, "error": "No media server configured"}), 400 playlists_data = [] - if active_server == 'plex' and plex_client and plex_client.is_connected(): + if active_server == 'plex' and media_server_engine.client('plex') and media_server_engine.client('plex').is_connected(): # Use raw Plex API to get playlist metadata without fetching all tracks try: - raw_playlists = plex_client.server.playlists() + raw_playlists = media_server_engine.client('plex').server.playlists() logger.info(f"[ServerPlaylists] Plex returned {len(raw_playlists)} total playlists") for playlist in raw_playlists: if getattr(playlist, 'playlistType', None) == 'audio': @@ -18061,22 +18061,22 @@ def get_server_playlists(): except Exception as e: logger.error(f"[ServerPlaylists] Error fetching Plex playlists: {e}", exc_info=True) return jsonify({"success": False, "error": f"Plex error: {str(e)}"}), 500 - elif active_server == 'jellyfin' and jellyfin_client and jellyfin_client.is_connected(): - for pl in jellyfin_client.get_all_playlists(): + elif active_server == 'jellyfin' and media_server_engine.client('jellyfin') and media_server_engine.client('jellyfin').is_connected(): + for pl in media_server_engine.client('jellyfin').get_all_playlists(): playlists_data.append({ 'id': pl.id, 'name': pl.title, 'track_count': pl.leaf_count, }) - elif active_server == 'navidrome' and navidrome_client and navidrome_client.is_connected(): - for pl in navidrome_client.get_all_playlists(): + elif active_server == 'navidrome' and media_server_engine.client('navidrome') and media_server_engine.client('navidrome').is_connected(): + for pl in media_server_engine.client('navidrome').get_all_playlists(): playlists_data.append({ 'id': pl.id, 'name': pl.title, 'track_count': pl.leaf_count, }) else: - logger.warning(f"[ServerPlaylists] Server '{active_server}' not connected. plex_client={plex_client is not None}, jellyfin_client={jellyfin_client is not None}, navidrome_client={navidrome_client is not None}") + logger.warning(f"[ServerPlaylists] Server '{active_server}' not connected. plex_client={media_server_engine.client('plex') is not None}, jellyfin_client={media_server_engine.client('jellyfin') is not None}, navidrome_client={media_server_engine.client('navidrome') is not None}") return jsonify({"success": False, "error": f"{active_server} not connected"}), 400 return jsonify({"success": True, "server_type": active_server, "playlists": playlists_data}) @@ -18094,24 +18094,24 @@ def get_server_playlist_tracks(playlist_id): # Get tracks from server server_tracks = [] - if active_server == 'plex' and plex_client: + if active_server == 'plex' and media_server_engine.client('plex'): try: # Try by ID first, fall back to name lookup (ID changes when playlist is recreated) raw_playlist = None try: - raw_playlist = plex_client.server.fetchItem(int(playlist_id)) + raw_playlist = media_server_engine.client('plex').server.fetchItem(int(playlist_id)) except Exception: pass if not raw_playlist and playlist_name: try: - raw_playlist = plex_client.server.playlist(playlist_name) + raw_playlist = media_server_engine.client('plex').server.playlist(playlist_name) except Exception: pass if raw_playlist: if not playlist_name: playlist_name = raw_playlist.title - plex_base = getattr(plex_client.server, '_baseurl', '') or '' - plex_token = getattr(plex_client.server, '_token', '') or '' + plex_base = getattr(media_server_engine.client('plex').server, '_baseurl', '') or '' + plex_token = getattr(media_server_engine.client('plex').server, '_token', '') or '' if not plex_base: # Fallback: get from config _pc = config_manager.get_plex_config() @@ -18136,9 +18136,9 @@ def get_server_playlist_tracks(playlist_id): }) except Exception as e: logger.error(f"[ServerPlaylistTracks] Plex error: {e}", exc_info=True) - elif active_server == 'jellyfin' and jellyfin_client: - tracks = jellyfin_client.get_playlist_tracks(playlist_id) - jf_base = jellyfin_client.base_url or '' + elif active_server == 'jellyfin' and media_server_engine.client('jellyfin'): + tracks = media_server_engine.client('jellyfin').get_playlist_tracks(playlist_id) + jf_base = media_server_engine.client('jellyfin').base_url or '' for t in (tracks or []): raw = t._data if hasattr(t, '_data') else {} artists = raw.get('Artists', []) @@ -18153,8 +18153,8 @@ def get_server_playlist_tracks(playlist_id): 'duration': t.duration, 'thumb': thumb, }) - elif active_server == 'navidrome' and navidrome_client: - tracks = navidrome_client.get_playlist_tracks(playlist_id) + elif active_server == 'navidrome' and media_server_engine.client('navidrome'): + tracks = media_server_engine.client('navidrome').get_playlist_tracks(playlist_id) for t in (tracks or []): raw = t._data if hasattr(t, '_data') else {} # Navidrome cover art via Subsonic API @@ -18179,9 +18179,9 @@ def get_server_playlist_tracks(playlist_id): # Build server art URL prefix for resolving relative thumb paths _art_prefix = '' _art_suffix = '' - if active_server == 'plex' and plex_client and plex_client.server: - _ab = getattr(plex_client.server, '_baseurl', '') or '' - _at = getattr(plex_client.server, '_token', '') or '' + if active_server == 'plex' and media_server_engine.client('plex') and media_server_engine.client('plex').server: + _ab = getattr(media_server_engine.client('plex').server, '_baseurl', '') or '' + _at = getattr(media_server_engine.client('plex').server, '_token', '') or '' if not _ab: _pc = config_manager.get_plex_config() _ab = (_pc.get('base_url', '') or '').rstrip('/') @@ -18364,10 +18364,10 @@ def server_playlist_replace_track(playlist_id): active_server = config_manager.get_active_media_server() - if active_server == 'plex' and plex_client: + if active_server == 'plex' and media_server_engine.client('plex'): # Use raw Plex API - fetch playlist directly try: - raw_playlist = plex_client.server.playlist(playlist_name) + raw_playlist = media_server_engine.client('plex').server.playlist(playlist_name) except Exception: return jsonify({"success": False, "error": "Playlist not found on server"}), 404 @@ -18376,7 +18376,7 @@ def server_playlist_replace_track(playlist_id): replaced = False for item in raw_playlist.items(): if str(item.ratingKey) == str(old_track_id) and not replaced: - new_item = plex_client.server.fetchItem(int(new_track_id)) + new_item = media_server_engine.client('plex').server.fetchItem(int(new_track_id)) if new_item: new_tracks.append(new_item) replaced = True @@ -18389,13 +18389,13 @@ def server_playlist_replace_track(playlist_id): # Delete old and recreate directly (avoid update_playlist's backup logic) raw_playlist.delete() from plexapi.playlist import Playlist - new_pl = Playlist.create(plex_client.server, playlist_name, items=new_tracks) + new_pl = Playlist.create(media_server_engine.client('plex').server, playlist_name, items=new_tracks) return jsonify({"success": True, "message": "Track replaced", "new_playlist_id": str(new_pl.ratingKey)}) else: return jsonify({"success": False, "error": "Old track not found in playlist"}), 404 - elif active_server == 'jellyfin' and jellyfin_client: - current_tracks = jellyfin_client.get_playlist_tracks(playlist_id) + elif active_server == 'jellyfin' and media_server_engine.client('jellyfin'): + current_tracks = media_server_engine.client('jellyfin').get_playlist_tracks(playlist_id) new_track_ids = [] replaced = False for t in (current_tracks or []): @@ -18408,12 +18408,12 @@ def server_playlist_replace_track(playlist_id): if replaced: new_track_objs = [type('T', (), {'ratingKey': tid, 'title': ''})() for tid in new_track_ids] - jellyfin_client.update_playlist(playlist_name, new_track_objs) + media_server_engine.client('jellyfin').update_playlist(playlist_name, new_track_objs) return jsonify({"success": True, "message": "Track replaced"}) return jsonify({"success": False, "error": "Old track not found"}), 404 - elif active_server == 'navidrome' and navidrome_client: - current_tracks = navidrome_client.get_playlist_tracks(playlist_id) + elif active_server == 'navidrome' and media_server_engine.client('navidrome'): + current_tracks = media_server_engine.client('navidrome').get_playlist_tracks(playlist_id) new_track_ids = [] replaced = False for t in (current_tracks or []): @@ -18426,7 +18426,7 @@ def server_playlist_replace_track(playlist_id): if replaced: new_track_objs = [type('T', (), {'ratingKey': tid, 'title': ''})() for tid in new_track_ids] - navidrome_client.create_playlist(playlist_name, new_track_objs, playlist_id=playlist_id) + media_server_engine.client('navidrome').create_playlist(playlist_name, new_track_objs, playlist_id=playlist_id) return jsonify({"success": True, "message": "Track replaced"}) return jsonify({"success": False, "error": "Old track not found"}), 404 @@ -18452,12 +18452,12 @@ def server_playlist_add_track(playlist_id): active_server = config_manager.get_active_media_server() - if active_server == 'plex' and plex_client: + if active_server == 'plex' and media_server_engine.client('plex'): try: - raw_playlist = plex_client.server.playlist(playlist_name) + raw_playlist = media_server_engine.client('plex').server.playlist(playlist_name) except Exception: return jsonify({"success": False, "error": "Playlist not found"}), 404 - new_item = plex_client.server.fetchItem(int(track_id)) + new_item = media_server_engine.client('plex').server.fetchItem(int(track_id)) if not new_item: return jsonify({"success": False, "error": "Track not found on server"}), 404 @@ -18484,22 +18484,22 @@ def server_playlist_add_track(playlist_id): logger.info(f"[ServerPlaylist] Added track to playlist, playlist ID: {new_id}") return jsonify({"success": True, "message": "Track added", "new_playlist_id": new_id}) - elif active_server == 'jellyfin' and jellyfin_client: - current_tracks = jellyfin_client.get_playlist_tracks(playlist_id) or [] + elif active_server == 'jellyfin' and media_server_engine.client('jellyfin'): + current_tracks = media_server_engine.client('jellyfin').get_playlist_tracks(playlist_id) or [] track_ids = [str(t.ratingKey) for t in current_tracks] pos = max(0, min(int(position), len(track_ids))) if position is not None else len(track_ids) track_ids.insert(pos, track_id) new_track_objs = [type('T', (), {'ratingKey': tid, 'title': ''})() for tid in track_ids] - jellyfin_client.update_playlist(playlist_name, new_track_objs) + media_server_engine.client('jellyfin').update_playlist(playlist_name, new_track_objs) return jsonify({"success": True, "message": "Track added"}) - elif active_server == 'navidrome' and navidrome_client: - current_tracks = navidrome_client.get_playlist_tracks(playlist_id) or [] + elif active_server == 'navidrome' and media_server_engine.client('navidrome'): + current_tracks = media_server_engine.client('navidrome').get_playlist_tracks(playlist_id) or [] track_ids = [str(t.ratingKey) for t in current_tracks] pos = max(0, min(int(position), len(track_ids))) if position is not None else len(track_ids) track_ids.insert(pos, track_id) new_track_objs = [type('T', (), {'ratingKey': tid, 'title': ''})() for tid in track_ids] - navidrome_client.create_playlist(playlist_name, new_track_objs, playlist_id=playlist_id) + media_server_engine.client('navidrome').create_playlist(playlist_name, new_track_objs, playlist_id=playlist_id) return jsonify({"success": True, "message": "Track added"}) return jsonify({"success": False, "error": f"Unsupported server: {active_server}"}), 400 @@ -18523,9 +18523,9 @@ def server_playlist_remove_track(playlist_id): active_server = config_manager.get_active_media_server() - if active_server == 'plex' and plex_client: + if active_server == 'plex' and media_server_engine.client('plex'): try: - raw_playlist = plex_client.server.playlist(playlist_name) + raw_playlist = media_server_engine.client('plex').server.playlist(playlist_name) except Exception: return jsonify({"success": False, "error": "Playlist not found"}), 404 @@ -18537,26 +18537,26 @@ def server_playlist_remove_track(playlist_id): raw_playlist.delete() if new_items: from plexapi.playlist import Playlist - new_pl = Playlist.create(plex_client.server, playlist_name, items=new_items) + new_pl = Playlist.create(media_server_engine.client('plex').server, playlist_name, items=new_items) return jsonify({"success": True, "message": "Track removed", "new_playlist_id": str(new_pl.ratingKey)}) return jsonify({"success": True, "message": "Track removed (playlist now empty)"}) - elif active_server == 'jellyfin' and jellyfin_client: - current_tracks = jellyfin_client.get_playlist_tracks(playlist_id) or [] + elif active_server == 'jellyfin' and media_server_engine.client('jellyfin'): + current_tracks = media_server_engine.client('jellyfin').get_playlist_tracks(playlist_id) or [] new_ids = [str(t.ratingKey) for t in current_tracks if str(t.ratingKey) != str(remove_track_id)] if len(new_ids) == len(current_tracks): return jsonify({"success": False, "error": "Track not found in playlist"}), 404 new_track_objs = [type('T', (), {'ratingKey': tid, 'title': ''})() for tid in new_ids] - jellyfin_client.update_playlist(playlist_name, new_track_objs) + media_server_engine.client('jellyfin').update_playlist(playlist_name, new_track_objs) return jsonify({"success": True, "message": "Track removed"}) - elif active_server == 'navidrome' and navidrome_client: - current_tracks = navidrome_client.get_playlist_tracks(playlist_id) or [] + elif active_server == 'navidrome' and media_server_engine.client('navidrome'): + current_tracks = media_server_engine.client('navidrome').get_playlist_tracks(playlist_id) or [] new_ids = [str(t.ratingKey) for t in current_tracks if str(t.ratingKey) != str(remove_track_id)] if len(new_ids) == len(current_tracks): return jsonify({"success": False, "error": "Track not found in playlist"}), 404 new_track_objs = [type('T', (), {'ratingKey': tid, 'title': ''})() for tid in new_ids] - navidrome_client.create_playlist(playlist_name, new_track_objs, playlist_id=playlist_id) + media_server_engine.client('navidrome').create_playlist(playlist_name, new_track_objs, playlist_id=playlist_id) return jsonify({"success": True, "message": "Track removed"}) return jsonify({"success": False, "error": f"Unsupported server: {active_server}"}), 400 @@ -18580,9 +18580,9 @@ def library_search_tracks(): # Build thumb URL resolver for this server _art_prefix = '' _art_suffix = '' - if active_server == 'plex' and plex_client and plex_client.server: - _ab = getattr(plex_client.server, '_baseurl', '') or '' - _at = getattr(plex_client.server, '_token', '') or '' + if active_server == 'plex' and media_server_engine.client('plex') and media_server_engine.client('plex').server: + _ab = getattr(media_server_engine.client('plex').server, '_baseurl', '') or '' + _at = getattr(media_server_engine.client('plex').server, '_token', '') or '' if not _ab: _pc = config_manager.get_plex_config() _ab = (_pc.get('base_url', '') or '').rstrip('/') @@ -23399,12 +23399,12 @@ def test_database_access(): # Test media clients logger.info(" Media clients status:") - logger.info(f" plex_client: {plex_client is not None}") - if plex_client: - logger.info(f" plex_client.is_connected(): {plex_client.is_connected()}") - logger.info(f" jellyfin_client: {jellyfin_client is not None}") - if jellyfin_client: - logger.info(f" jellyfin_client.is_connected(): {jellyfin_client.is_connected()}") + logger.info(f" media_server_engine.client('plex'): {media_server_engine.client('plex') is not None}") + if media_server_engine.client('plex'): + logger.info(f" media_server_engine.client('plex').is_connected(): {media_server_engine.client('plex').is_connected()}") + logger.info(f" media_server_engine.client('jellyfin'): {media_server_engine.client('jellyfin') is not None}") + if media_server_engine.client('jellyfin'): + logger.info(f" media_server_engine.client('jellyfin').is_connected(): {media_server_engine.client('jellyfin').is_connected()}") return jsonify({ "success": True, @@ -23413,8 +23413,8 @@ def test_database_access(): "database_initialized": db is not None, "database_stats": stats, "active_server": active_server, - "plex_connected": plex_client.is_connected() if plex_client else False, - "jellyfin_connected": jellyfin_client.is_connected() if jellyfin_client else False, + "plex_connected": media_server_engine.client('plex').is_connected() if media_server_engine.client('plex') else False, + "jellyfin_connected": media_server_engine.client('jellyfin').is_connected() if media_server_engine.client('jellyfin') else False, } }) @@ -29133,17 +29133,17 @@ def start_metadata_update(): # Get appropriate media client - Support all three servers if active_server == "jellyfin": - media_client = jellyfin_client + media_client = media_server_engine.client('jellyfin') if not media_client: add_activity_item("", "Metadata Update", "Jellyfin client not available", "Now") return jsonify({"success": False, "error": "Jellyfin client not available"}), 400 elif active_server == "navidrome": - media_client = navidrome_client + media_client = media_server_engine.client('navidrome') if not media_client: add_activity_item("", "Metadata Update", "Navidrome client not available", "Now") return jsonify({"success": False, "error": "Navidrome client not available"}), 400 else: # plex - media_client = plex_client + media_client = media_server_engine.client('plex') if not media_client: add_activity_item("", "Metadata Update", "Plex client not available", "Now") return jsonify({"success": False, "error": "Plex client not available"}), 400