MS Cin-5: Drop per-server globals — engine owns the clients
Per-server web_server.py globals (plex_client / jellyfin_client /
navidrome_client / soulsync_library_client) are gone. The engine now
owns the per-server client instances; web_server.py constructs them
inline into the engine init and routes everything through
media_server_engine.client('<name>').
Multi-client consumers refactored to take the engine instead of
separate per-server kwargs:
- services/sync_service.py: PlaylistSyncService.__init__ now takes
media_server_engine. Internal _get_active_media_client resolves the
active server's client through self._engine.client(name) instead of
the per-server self.X_client attributes.
- core/listening_stats_worker.py: ListeningStatsWorker takes
media_server_engine. The plex/jellyfin/navidrome dispatch in _poll
collapses to engine.client(active_server) (gated to those three
servers — SoulSync standalone has no listening data).
- core/web_scan_manager.py: WebScanManager takes media_server_engine
instead of the hand-keyed media_clients dict that drifted out of
sync with the engine.
- core/discovery/sync.py: SyncDeps holds media_server_engine instead
of plex_client / jellyfin_client. Playlist-image dispatch routes
through engine.client(name).
Web_server.py:
- Per-server globals removed from the chained `= None` init line
+ their try/except construction blocks. Replaced with a
_safe_init_media_client(factory, name) helper that captures
per-server init failures + passes the resulting clients straight
into the MediaServerEngine init dict.
- All construction sites (PlaylistSyncService, WebScanManager,
ListeningStatsWorker, SyncDeps, library_check) updated to receive
the engine instead of per-server clients.
Test fixtures (tests/discovery/test_discovery_sync.py) gain a
_FakeMediaServerEngine stub + the SyncDeps build helper passes
that instead of separate plex/jellyfin clients.
This commit is contained in:
parent
d3f8a06d7a
commit
a6bb5f5b43
6 changed files with 114 additions and 106 deletions
|
|
@ -39,8 +39,7 @@ class SyncDeps:
|
||||||
"""Bundle of cross-cutting deps the sync worker needs."""
|
"""Bundle of cross-cutting deps the sync worker needs."""
|
||||||
config_manager: Any
|
config_manager: Any
|
||||||
sync_service: Any
|
sync_service: Any
|
||||||
plex_client: Any
|
media_server_engine: Any
|
||||||
jellyfin_client: Any
|
|
||||||
automation_engine: Any
|
automation_engine: Any
|
||||||
run_async: Callable[..., Any]
|
run_async: Callable[..., Any]
|
||||||
record_sync_history_start: Callable
|
record_sync_history_start: Callable
|
||||||
|
|
@ -227,8 +226,9 @@ def run_sync_task(playlist_id, playlist_name, tracks_json, automation_id=None, p
|
||||||
|
|
||||||
# Check sync service components
|
# Check sync service components
|
||||||
logger.info(f" spotify_client: {sync_service.spotify_client is not None}")
|
logger.info(f" spotify_client: {sync_service.spotify_client is not None}")
|
||||||
logger.info(f" deps.plex_client: {sync_service.plex_client is not None}")
|
_ms_engine = getattr(sync_service, '_engine', None)
|
||||||
logger.info(f" deps.jellyfin_client: {sync_service.jellyfin_client is not None}")
|
logger.info(f" plex_client: {(_ms_engine.client('plex') if _ms_engine else None) is not None}")
|
||||||
|
logger.info(f" jellyfin_client: {(_ms_engine.client('jellyfin') if _ms_engine else None) is not None}")
|
||||||
|
|
||||||
# Check media server connection before starting
|
# Check media server connection before starting
|
||||||
from config.settings import config_manager
|
from config.settings import config_manager
|
||||||
|
|
@ -404,11 +404,12 @@ def run_sync_task(playlist_id, playlist_name, tracks_json, automation_id=None, p
|
||||||
try:
|
try:
|
||||||
active_server = deps.config_manager.get_active_media_server()
|
active_server = deps.config_manager.get_active_media_server()
|
||||||
logger.info(f"[PLAYLIST IMAGE] active_server={active_server}")
|
logger.info(f"[PLAYLIST IMAGE] active_server={active_server}")
|
||||||
if active_server == 'plex' and deps.plex_client:
|
_engine = deps.media_server_engine
|
||||||
ok = deps.plex_client.set_playlist_image(playlist_name, playlist_image_url)
|
if active_server == 'plex' and _engine and _engine.client('plex'):
|
||||||
|
ok = _engine.client('plex').set_playlist_image(playlist_name, playlist_image_url)
|
||||||
logger.info(f"[PLAYLIST IMAGE] Plex upload result: {ok}")
|
logger.info(f"[PLAYLIST IMAGE] Plex upload result: {ok}")
|
||||||
elif active_server in ('jellyfin', 'emby') and deps.jellyfin_client:
|
elif active_server in ('jellyfin', 'emby') and _engine and _engine.client('jellyfin'):
|
||||||
ok = deps.jellyfin_client.set_playlist_image(playlist_name, playlist_image_url)
|
ok = _engine.client('jellyfin').set_playlist_image(playlist_name, playlist_image_url)
|
||||||
logger.info(f"[PLAYLIST IMAGE] Jellyfin upload result: {ok}")
|
logger.info(f"[PLAYLIST IMAGE] Jellyfin upload result: {ok}")
|
||||||
# Navidrome doesn't support custom playlist images
|
# Navidrome doesn't support custom playlist images
|
||||||
except Exception as img_err:
|
except Exception as img_err:
|
||||||
|
|
|
||||||
|
|
@ -19,13 +19,17 @@ logger = get_logger("listening_stats_worker")
|
||||||
class ListeningStatsWorker:
|
class ListeningStatsWorker:
|
||||||
"""Background worker that polls media servers for play data."""
|
"""Background worker that polls media servers for play data."""
|
||||||
|
|
||||||
def __init__(self, database, config_manager, plex_client=None,
|
def __init__(self, database, config_manager, media_server_engine=None):
|
||||||
jellyfin_client=None, navidrome_client=None):
|
"""Initialize the worker.
|
||||||
|
|
||||||
|
``media_server_engine`` owns the per-server clients (Plex /
|
||||||
|
Jellyfin / Navidrome). The worker resolves the active server's
|
||||||
|
client through ``self._engine.client(name)`` instead of holding
|
||||||
|
per-server kwargs.
|
||||||
|
"""
|
||||||
self.db = database
|
self.db = database
|
||||||
self.config_manager = config_manager
|
self.config_manager = config_manager
|
||||||
self.plex_client = plex_client
|
self._engine = media_server_engine
|
||||||
self.jellyfin_client = jellyfin_client
|
|
||||||
self.navidrome_client = navidrome_client
|
|
||||||
|
|
||||||
# Worker state
|
# Worker state
|
||||||
self.running = False
|
self.running = False
|
||||||
|
|
@ -145,13 +149,11 @@ class ListeningStatsWorker:
|
||||||
logger.info(f"Polling {active_server} for listening data...")
|
logger.info(f"Polling {active_server} for listening data...")
|
||||||
self.current_item = f"Polling {active_server}..."
|
self.current_item = f"Polling {active_server}..."
|
||||||
|
|
||||||
client = None
|
client = self._engine.client(active_server) if self._engine else None
|
||||||
if active_server == 'plex' and self.plex_client:
|
# SoulSync standalone has no listening data; only the three
|
||||||
client = self.plex_client
|
# streaming servers contribute. Mirror the legacy guard here.
|
||||||
elif active_server == 'jellyfin' and self.jellyfin_client:
|
if active_server not in ('plex', 'jellyfin', 'navidrome'):
|
||||||
client = self.jellyfin_client
|
client = None
|
||||||
elif active_server == 'navidrome' and self.navidrome_client:
|
|
||||||
client = self.navidrome_client
|
|
||||||
|
|
||||||
if not client:
|
if not client:
|
||||||
logger.warning(f"No client available for active server: {active_server}")
|
logger.warning(f"No client available for active server: {active_server}")
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,21 @@ class WebScanManager:
|
||||||
- Progress tracking and status reporting
|
- Progress tracking and status reporting
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, media_clients, delay_seconds: int = 60):
|
def __init__(self, media_server_engine, delay_seconds: int = 60):
|
||||||
"""
|
"""
|
||||||
Initialize the web scan manager.
|
Initialize the web scan manager.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
media_clients: Dict containing plex_client, jellyfin_client, navidrome_client
|
media_server_engine: MediaServerEngine that owns the per-server
|
||||||
|
clients. Replaces the legacy ``media_clients`` dict — the
|
||||||
|
manager now resolves the active server's client through
|
||||||
|
``self._engine.client(name)`` instead of a hand-keyed
|
||||||
|
dict that drifted out of sync with the engine's source
|
||||||
|
of truth.
|
||||||
delay_seconds: Debounce delay in seconds (default 60s)
|
delay_seconds: Debounce delay in seconds (default 60s)
|
||||||
"""
|
"""
|
||||||
self.delay = delay_seconds
|
self.delay = delay_seconds
|
||||||
self.media_clients = media_clients
|
self._engine = media_server_engine
|
||||||
self._timer = None
|
self._timer = None
|
||||||
self._scan_in_progress = False
|
self._scan_in_progress = False
|
||||||
self._downloads_during_scan = False
|
self._downloads_during_scan = False
|
||||||
|
|
@ -44,29 +49,19 @@ class WebScanManager:
|
||||||
logger.info(f"WebScanManager initialized with {delay_seconds}s debounce delay")
|
logger.info(f"WebScanManager initialized with {delay_seconds}s debounce delay")
|
||||||
|
|
||||||
def _get_active_media_client(self):
|
def _get_active_media_client(self):
|
||||||
"""Get the active media client based on config settings"""
|
"""Get the active media client through the engine."""
|
||||||
try:
|
try:
|
||||||
from config.settings import config_manager
|
from config.settings import config_manager
|
||||||
active_server = config_manager.get_active_media_server()
|
active_server = config_manager.get_active_media_server()
|
||||||
|
|
||||||
server_client_map = {
|
if not self._engine:
|
||||||
'jellyfin': 'jellyfin_client',
|
logger.error("Web scan manager has no engine reference")
|
||||||
'navidrome': 'navidrome_client',
|
return None, None
|
||||||
'plex': 'plex_client',
|
|
||||||
'soulsync': 'soulsync_library_client',
|
|
||||||
}
|
|
||||||
|
|
||||||
# Try to get the configured active server
|
client = self._engine.client(active_server)
|
||||||
if active_server in server_client_map:
|
if client and hasattr(client, 'is_connected') and client.is_connected():
|
||||||
client_key = server_client_map[active_server]
|
return client, active_server
|
||||||
client = self.media_clients.get(client_key)
|
logger.warning(f"{(active_server or 'unknown').title()} client not connected — scan skipped")
|
||||||
if client and hasattr(client, 'is_connected') and client.is_connected():
|
|
||||||
return client, active_server
|
|
||||||
else:
|
|
||||||
logger.warning(f"{active_server.title()} client not connected — scan skipped")
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
logger.error("No active media server configured for scanning")
|
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
|
|
@ -44,17 +44,31 @@ class SyncProgress:
|
||||||
failed_tracks: int = 0
|
failed_tracks: int = 0
|
||||||
|
|
||||||
class PlaylistSyncService:
|
class PlaylistSyncService:
|
||||||
def __init__(self, spotify_client: SpotifyClient, plex_client: PlexClient, soulseek_client: SoulseekClient, jellyfin_client: JellyfinClient = None, navidrome_client = None):
|
def __init__(self, spotify_client: SpotifyClient, soulseek_client: SoulseekClient, media_server_engine=None):
|
||||||
|
"""Initialize the sync service.
|
||||||
|
|
||||||
|
``media_server_engine`` is the central MediaServerEngine that owns
|
||||||
|
the per-server clients (Plex / Jellyfin / Navidrome / SoulSync).
|
||||||
|
Replaces the legacy per-server kwargs (plex_client / jellyfin_client
|
||||||
|
/ navidrome_client) — all media-server access now goes through
|
||||||
|
``self._engine.client(name)`` so swapping the active server doesn't
|
||||||
|
need a service rebuild.
|
||||||
|
"""
|
||||||
self.spotify_client = spotify_client
|
self.spotify_client = spotify_client
|
||||||
self.plex_client = plex_client
|
self._engine = media_server_engine
|
||||||
self.jellyfin_client = jellyfin_client
|
|
||||||
self.navidrome_client = navidrome_client
|
|
||||||
self.soulseek_client = soulseek_client
|
self.soulseek_client = soulseek_client
|
||||||
self.progress_callbacks = {} # Playlist-specific progress callbacks
|
self.progress_callbacks = {} # Playlist-specific progress callbacks
|
||||||
self.syncing_playlists = set() # Track multiple syncing playlists
|
self.syncing_playlists = set() # Track multiple syncing playlists
|
||||||
self._cancelled = False
|
self._cancelled = False
|
||||||
self.matching_engine = MusicMatchingEngine()
|
self.matching_engine = MusicMatchingEngine()
|
||||||
|
|
||||||
|
def _media_client(self, name: str):
|
||||||
|
"""Resolve a per-server client through the engine, or None when the
|
||||||
|
engine isn't wired (defensive — every production path passes one)."""
|
||||||
|
if self._engine is None:
|
||||||
|
return None
|
||||||
|
return self._engine.client(name)
|
||||||
|
|
||||||
def _get_active_media_client(self, profile_id=None):
|
def _get_active_media_client(self, profile_id=None):
|
||||||
"""Get the active media client based on config settings.
|
"""Get the active media client based on config settings.
|
||||||
|
|
||||||
|
|
@ -68,26 +82,27 @@ class PlaylistSyncService:
|
||||||
active_server = config_manager.get_active_media_server()
|
active_server = config_manager.get_active_media_server()
|
||||||
|
|
||||||
if active_server == "jellyfin":
|
if active_server == "jellyfin":
|
||||||
if not self.jellyfin_client:
|
client = self._media_client('jellyfin')
|
||||||
|
if not client:
|
||||||
logger.error("Jellyfin client not provided to sync service")
|
logger.error("Jellyfin client not provided to sync service")
|
||||||
return None, "jellyfin"
|
return None, "jellyfin"
|
||||||
# Apply per-profile Jellyfin library if set
|
|
||||||
if profile_id:
|
if profile_id:
|
||||||
self._apply_profile_library(profile_id, 'jellyfin', self.jellyfin_client)
|
self._apply_profile_library(profile_id, 'jellyfin', client)
|
||||||
return self.jellyfin_client, "jellyfin"
|
return client, "jellyfin"
|
||||||
elif active_server == "navidrome":
|
elif active_server == "navidrome":
|
||||||
if not self.navidrome_client:
|
client = self._media_client('navidrome')
|
||||||
|
if not client:
|
||||||
logger.error("Navidrome client not provided to sync service")
|
logger.error("Navidrome client not provided to sync service")
|
||||||
return None, "navidrome"
|
return None, "navidrome"
|
||||||
return self.navidrome_client, "navidrome"
|
return client, "navidrome"
|
||||||
else: # Default to Plex
|
else: # Default to Plex
|
||||||
# Apply per-profile Plex library if set
|
client = self._media_client('plex')
|
||||||
if profile_id:
|
if profile_id and client:
|
||||||
self._apply_profile_library(profile_id, 'plex', self.plex_client)
|
self._apply_profile_library(profile_id, 'plex', client)
|
||||||
return self.plex_client, "plex"
|
return client, "plex"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error determining active media server: {e}")
|
logger.error(f"Error determining active media server: {e}")
|
||||||
return self.plex_client, "plex" # Fallback to Plex
|
return self._media_client('plex'), "plex" # Fallback to Plex
|
||||||
|
|
||||||
def _apply_profile_library(self, profile_id, server_type, client):
|
def _apply_profile_library(self, profile_id, server_type, client):
|
||||||
"""Apply per-profile library selection to a media client if configured."""
|
"""Apply per-profile library selection to a media client if configured."""
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,15 @@ class _FakeMediaClient:
|
||||||
return self._connected
|
return self._connected
|
||||||
|
|
||||||
|
|
||||||
|
class _FakeMediaServerEngine:
|
||||||
|
"""Stand-in for MediaServerEngine — only the bits SyncDeps needs."""
|
||||||
|
def __init__(self, plex=None, jellyfin=None, navidrome=None):
|
||||||
|
self._clients = {'plex': plex, 'jellyfin': jellyfin, 'navidrome': navidrome}
|
||||||
|
|
||||||
|
def client(self, name):
|
||||||
|
return self._clients.get(name)
|
||||||
|
|
||||||
|
|
||||||
class _FakeSyncService:
|
class _FakeSyncService:
|
||||||
def __init__(self, *, media_client=None, server_type='plex',
|
def __init__(self, *, media_client=None, server_type='plex',
|
||||||
sync_result=None, raise_on_sync=None,
|
sync_result=None, raise_on_sync=None,
|
||||||
|
|
@ -44,8 +53,12 @@ class _FakeSyncService:
|
||||||
self._sync_result = sync_result or _FakeSyncResult()
|
self._sync_result = sync_result or _FakeSyncResult()
|
||||||
self._raise_on_sync = raise_on_sync
|
self._raise_on_sync = raise_on_sync
|
||||||
self.spotify_client = object() if spotify_client else None
|
self.spotify_client = object() if spotify_client else None
|
||||||
self.plex_client = object() if plex_client else None
|
# The sync_service exposes the engine so the discovery worker
|
||||||
self.jellyfin_client = object() if jellyfin_client else None
|
# can introspect per-server clients via self._engine.client(name).
|
||||||
|
self._engine = _FakeMediaServerEngine(
|
||||||
|
plex=object() if plex_client else None,
|
||||||
|
jellyfin=object() if jellyfin_client else None,
|
||||||
|
)
|
||||||
self.progress_callback = None
|
self.progress_callback = None
|
||||||
self.progress_playlist_name = None
|
self.progress_playlist_name = None
|
||||||
self.cleared_callbacks = []
|
self.cleared_callbacks = []
|
||||||
|
|
@ -130,8 +143,10 @@ def _build_deps(
|
||||||
return ds.SyncDeps(
|
return ds.SyncDeps(
|
||||||
config_manager=config or _FakeConfig(),
|
config_manager=config or _FakeConfig(),
|
||||||
sync_service=sync_service or _FakeSyncService(media_client=_FakeMediaClient()),
|
sync_service=sync_service or _FakeSyncService(media_client=_FakeMediaClient()),
|
||||||
plex_client=plex or _FakePlex(),
|
media_server_engine=_FakeMediaServerEngine(
|
||||||
jellyfin_client=jellyfin or _FakeJellyfin(),
|
plex=plex or _FakePlex(),
|
||||||
|
jellyfin=jellyfin or _FakeJellyfin(),
|
||||||
|
),
|
||||||
automation_engine=automation or _FakeAutomationEngine(),
|
automation_engine=automation or _FakeAutomationEngine(),
|
||||||
run_async=run_async or _run_async_sync,
|
run_async=run_async or _run_async_sync,
|
||||||
record_sync_history_start=record_sync_history_start or (lambda **kw: None),
|
record_sync_history_start=record_sync_history_start or (lambda **kw: None),
|
||||||
|
|
|
||||||
|
|
@ -570,7 +570,7 @@ IS_SHUTTING_DOWN = False
|
||||||
# Each client is initialized independently so one failure doesn't take down everything.
|
# Each client is initialized independently so one failure doesn't take down everything.
|
||||||
# Previously, a single exception set ALL clients to None, breaking the entire app.
|
# Previously, a single exception set ALL clients to None, breaking the entire app.
|
||||||
logger.info("Initializing SoulSync services for Web UI...")
|
logger.info("Initializing SoulSync services for Web UI...")
|
||||||
spotify_client = plex_client = jellyfin_client = navidrome_client = soulsync_library_client = soulseek_client = tidal_client = matching_engine = sync_service = web_scan_manager = None
|
spotify_client = soulseek_client = tidal_client = matching_engine = sync_service = web_scan_manager = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
spotify_client = get_spotify_client()
|
spotify_client = get_spotify_client()
|
||||||
|
|
@ -578,42 +578,31 @@ try:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f" Spotify client failed to initialize: {e}")
|
logger.error(f" Spotify client failed to initialize: {e}")
|
||||||
|
|
||||||
try:
|
|
||||||
plex_client = PlexClient()
|
|
||||||
logger.info(" Plex client initialized")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f" Plex client failed to initialize: {e}")
|
|
||||||
|
|
||||||
try:
|
def _safe_init_media_client(factory, name):
|
||||||
jellyfin_client = JellyfinClient()
|
"""Build a media-server client, capturing per-server init failures
|
||||||
logger.info(" Jellyfin client initialized")
|
so one broken server doesn't take the engine down with it."""
|
||||||
except Exception as e:
|
try:
|
||||||
logger.error(f" Jellyfin client failed to initialize: {e}")
|
instance = factory()
|
||||||
|
logger.info(f" {name} client initialized")
|
||||||
|
return instance
|
||||||
|
except Exception as exc:
|
||||||
|
logger.error(f" {name} client failed to initialize: {exc}")
|
||||||
|
return None
|
||||||
|
|
||||||
try:
|
|
||||||
navidrome_client = NavidromeClient()
|
|
||||||
logger.info(" Navidrome client initialized")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f" Navidrome client failed to initialize: {e}")
|
|
||||||
|
|
||||||
try:
|
# Build the MediaServerEngine. The engine OWNS the per-server client
|
||||||
from core.soulsync_client import SoulSyncClient
|
# instances — no separate web_server.py globals (Cin's standard from
|
||||||
soulsync_library_client = SoulSyncClient()
|
# the download refactor: drop redundant access paths). All callers go
|
||||||
logger.info(" SoulSync library client initialized")
|
# through media_server_engine.client('<name>').
|
||||||
except Exception as e:
|
|
||||||
logger.error(f" SoulSync library client failed to initialize: {e}")
|
|
||||||
|
|
||||||
# Build the MediaServerEngine on top of the per-client globals above.
|
|
||||||
# Engine wraps the same instances — no double-init. Provides
|
|
||||||
# ``engine.method()`` dispatch in place of the historic
|
|
||||||
# ``if active_server == 'plex' / 'jellyfin' / ...`` chains.
|
|
||||||
try:
|
try:
|
||||||
from core.media_server.engine import MediaServerEngine, set_media_server_engine
|
from core.media_server.engine import MediaServerEngine, set_media_server_engine
|
||||||
|
from core.soulsync_client import SoulSyncClient
|
||||||
media_server_engine = MediaServerEngine(clients={
|
media_server_engine = MediaServerEngine(clients={
|
||||||
'plex': plex_client,
|
'plex': _safe_init_media_client(PlexClient, "Plex"),
|
||||||
'jellyfin': jellyfin_client,
|
'jellyfin': _safe_init_media_client(JellyfinClient, "Jellyfin"),
|
||||||
'navidrome': navidrome_client,
|
'navidrome': _safe_init_media_client(NavidromeClient, "Navidrome"),
|
||||||
'soulsync': soulsync_library_client,
|
'soulsync': _safe_init_media_client(SoulSyncClient, "SoulSync library"),
|
||||||
})
|
})
|
||||||
# Install as process-wide singleton so callers reaching via
|
# Install as process-wide singleton so callers reaching via
|
||||||
# get_media_server_engine() see the same instance web_server.py
|
# get_media_server_engine() see the same instance web_server.py
|
||||||
|
|
@ -644,7 +633,7 @@ except Exception as e:
|
||||||
logger.error(f" Matching engine failed to initialize: {e}")
|
logger.error(f" Matching engine failed to initialize: {e}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sync_service = PlaylistSyncService(spotify_client, plex_client, soulseek_client, jellyfin_client, media_server_engine.client('navidrome'))
|
sync_service = PlaylistSyncService(spotify_client, soulseek_client, media_server_engine=media_server_engine)
|
||||||
logger.info(" Playlist sync service initialized")
|
logger.info(" Playlist sync service initialized")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f" Playlist sync service failed to initialize: {e}")
|
logger.error(f" Playlist sync service failed to initialize: {e}")
|
||||||
|
|
@ -669,13 +658,7 @@ if soulseek_client:
|
||||||
|
|
||||||
# Initialize web scan manager for automatic post-download scanning
|
# Initialize web scan manager for automatic post-download scanning
|
||||||
try:
|
try:
|
||||||
media_clients = {
|
web_scan_manager = WebScanManager(media_server_engine, delay_seconds=60)
|
||||||
'plex_client': plex_client,
|
|
||||||
'jellyfin_client': jellyfin_client,
|
|
||||||
'navidrome_client': navidrome_client,
|
|
||||||
'soulsync_library_client': soulsync_library_client,
|
|
||||||
}
|
|
||||||
web_scan_manager = WebScanManager(media_clients, delay_seconds=60)
|
|
||||||
logger.info(" Web scan manager initialized")
|
logger.info(" Web scan manager initialized")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f" Web scan manager failed to initialize: {e}")
|
logger.error(f" Web scan manager failed to initialize: {e}")
|
||||||
|
|
@ -6879,7 +6862,7 @@ def enhanced_search_library_check():
|
||||||
data = request.get_json() or {}
|
data = request.get_json() or {}
|
||||||
result = _search_library_check.check_library_presence(
|
result = _search_library_check.check_library_presence(
|
||||||
database=get_database(),
|
database=get_database(),
|
||||||
plex_client=plex_client,
|
plex_client=media_server_engine.client('plex') if media_server_engine else None,
|
||||||
config_manager=config_manager,
|
config_manager=config_manager,
|
||||||
profile_id=get_current_profile_id(),
|
profile_id=get_current_profile_id(),
|
||||||
albums=data.get('albums', []),
|
albums=data.get('albums', []),
|
||||||
|
|
@ -23266,8 +23249,7 @@ def _build_sync_deps():
|
||||||
return _discovery_sync.SyncDeps(
|
return _discovery_sync.SyncDeps(
|
||||||
config_manager=config_manager,
|
config_manager=config_manager,
|
||||||
sync_service=sync_service,
|
sync_service=sync_service,
|
||||||
plex_client=plex_client,
|
media_server_engine=media_server_engine,
|
||||||
jellyfin_client=jellyfin_client,
|
|
||||||
automation_engine=automation_engine,
|
automation_engine=automation_engine,
|
||||||
run_async=run_async,
|
run_async=run_async,
|
||||||
record_sync_history_start=_record_sync_history_start,
|
record_sync_history_start=_record_sync_history_start,
|
||||||
|
|
@ -32848,9 +32830,7 @@ try:
|
||||||
listening_stats_worker = ListeningStatsWorker(
|
listening_stats_worker = ListeningStatsWorker(
|
||||||
database=listening_stats_db,
|
database=listening_stats_db,
|
||||||
config_manager=config_manager,
|
config_manager=config_manager,
|
||||||
plex_client=plex_client,
|
media_server_engine=media_server_engine,
|
||||||
jellyfin_client=jellyfin_client,
|
|
||||||
navidrome_client=navidrome_client,
|
|
||||||
)
|
)
|
||||||
listening_stats_worker.start()
|
listening_stats_worker.start()
|
||||||
logger.info("Listening stats worker initialized and started")
|
logger.info("Listening stats worker initialized and started")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue