Load remote playlists from Plex during sync
This commit is contained in:
@@ -3,6 +3,10 @@ from dataclasses import dataclass
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Literal, Sequence
|
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
|
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
|
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(
|
def _sync_single_playlist(
|
||||||
playlist: str,
|
playlist: str,
|
||||||
mode: SyncMode,
|
mode: SyncMode,
|
||||||
@@ -349,8 +395,11 @@ def sync_all_playlists(
|
|||||||
|
|
||||||
_ensure_test_dir(test_folder)
|
_ensure_test_dir(test_folder)
|
||||||
local_playlists = _load_local_playlists(local_dir)
|
local_playlists = _load_local_playlists(local_dir)
|
||||||
|
remote_playlists = _fetch_remote_playlists()
|
||||||
playlist_names: set[str] = set(local_playlists.keys())
|
playlist_names: set[str] = set(local_playlists.keys())
|
||||||
|
|
||||||
|
playlist_names.update(remote_playlists.keys())
|
||||||
|
|
||||||
for entry in os.scandir(test_folder):
|
for entry in os.scandir(test_folder):
|
||||||
if entry.is_dir():
|
if entry.is_dir():
|
||||||
playlist_names.add(entry.name)
|
playlist_names.add(entry.name)
|
||||||
@@ -358,11 +407,18 @@ def sync_all_playlists(
|
|||||||
results: list[PlaylistSyncResult] = []
|
results: list[PlaylistSyncResult] = []
|
||||||
|
|
||||||
for playlist in sorted(playlist_names):
|
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
|
playlist, test_folder
|
||||||
)
|
)
|
||||||
local_text = local_playlists.get(playlist)
|
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.
|
# Treat missing remote text as absent playlist.
|
||||||
result = _sync_single_playlist(
|
result = _sync_single_playlist(
|
||||||
|
|||||||
Reference in New Issue
Block a user