From a912213e2e4fd8e3d50f31d36b99526c45b0885f Mon Sep 17 00:00:00 2001 From: Koha9 <36852125+Koha9@users.noreply.github.com> Date: Tue, 25 Nov 2025 04:39:30 +0900 Subject: [PATCH] Load remote playlists from Plex during sync --- app/utils/playlist_merge.py | 60 +++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/app/utils/playlist_merge.py b/app/utils/playlist_merge.py index 2f40394..a3b6450 100644 --- a/app/utils/playlist_merge.py +++ b/app/utils/playlist_merge.py @@ -3,6 +3,10 @@ from dataclasses import dataclass from enum import Enum from typing import Literal, Sequence +from app.utils.config import server_config +from app.utils.logger import logger +from app.utils.plex_client import plex_client + from merge3 import Merge3 @@ -273,6 +277,48 @@ def _load_playlist_snapshots(playlist: str, folder: str) -> tuple[str, str, str, return base_text, remote_text, playlist_folder, remote_exists, base_exists +def _fetch_remote_playlists() -> dict[str, str]: + """Retrieve remote playlists from Plex when connected. + + Returns a mapping of playlist name to serialized playlist text. Failures to + connect or fetch playlists are logged and result in an empty mapping to + avoid blocking local testing. + """ + + server_config.load() + if not server_config.url: + return {} + + try: + plex_client.connect( + token=server_config.token, + scheme=server_config.scheme, + url=server_config.url, + port=server_config.port, + ) + except Exception as exc: # pragma: no cover - network access + logger.warning(f"Failed to connect to Plex for remote playlists: {exc}") + return {} + + playlists: dict[str, str] = {} + try: + for playlist in plex_client.server.playlists(): + paths: list[str] = [] + try: + for track in playlist.items(): + locations = getattr(track, "locations", None) or [] + if locations: + paths.append(locations[0]) + except Exception as exc: # pragma: no cover - plex runtime + logger.warning(f"Failed to read playlist '{playlist.title}': {exc}") + playlists[playlist.title] = save_paths(paths) + except Exception as exc: # pragma: no cover - plex runtime + logger.warning(f"Failed to enumerate remote playlists: {exc}") + return {} + + return playlists + + def _sync_single_playlist( playlist: str, mode: SyncMode, @@ -349,8 +395,11 @@ def sync_all_playlists( _ensure_test_dir(test_folder) local_playlists = _load_local_playlists(local_dir) + remote_playlists = _fetch_remote_playlists() playlist_names: set[str] = set(local_playlists.keys()) + playlist_names.update(remote_playlists.keys()) + for entry in os.scandir(test_folder): if entry.is_dir(): playlist_names.add(entry.name) @@ -358,11 +407,18 @@ def sync_all_playlists( results: list[PlaylistSyncResult] = [] for playlist in sorted(playlist_names): - base_text, remote_text, playlist_folder, remote_exists, _ = _load_playlist_snapshots( + base_text, snapshot_remote_text, playlist_folder, remote_exists, _ = _load_playlist_snapshots( playlist, test_folder ) local_text = local_playlists.get(playlist) - remote_present = bool(remote_text.strip()) or remote_exists + + remote_text = remote_playlists.get(playlist) + remote_present = False + if remote_text: + remote_present = True + else: + remote_text = snapshot_remote_text + remote_present = bool(remote_text.strip()) or remote_exists # Treat missing remote text as absent playlist. result = _sync_single_playlist(