From 6e9ae84db416b9e03cd10ac924238d495bcb5314 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 4 Oct 2026 20:31:48 -0400 Subject: [PATCH 01/11] refactor: isolate Plex behind media server interfaces --- docs/source/contributing/media_servers.rst | 44 +++ docs/source/index.md | 1 + locale/themerr-plex.po | 170 +++++------ src/common/config.py | 10 +- src/common/helpers.py | 2 +- src/common/notifications.py | 4 +- src/common/server_ui.py | 122 ++------ src/common/webapp.py | 246 +++------------ src/media_servers/__init__.py | 26 ++ src/media_servers/base.py | 280 ++++++++++++++++++ src/media_servers/processing.py | 90 ++++++ src/plex/backend.py | 141 +++++++++ src/plex/config.py | 17 ++ src/plex/constants.py | 58 ++++ src/plex/dashboard.py | 214 +++++++++++++ src/{themerr/general.py => plex/media.py} | 2 +- src/plex/plexapi.py | 133 +-------- src/plex/tmdb.py | 77 +++++ src/plex/web.py | 210 +++++++++++++ src/themerr/cache.py | 225 +------------- src/themerr/constants.py | 59 ---- src/themerr/scheduled_tasks.py | 2 +- src/themerr/tmdb.py | 90 +----- src/themerr_plex.py | 6 +- tests/functional/test_server_ui.py | 3 +- tests/functional/test_theme_upload.py | 3 +- tests/functional/test_webapp.py | 4 +- tests/unit/common/test_log_viewer.py | 2 +- tests/unit/common/test_notifications.py | 8 +- tests/unit/media_servers/test_cache.py | 55 ++++ tests/unit/media_servers/test_processing.py | 86 ++++++ tests/unit/plex/test_backend.py | 57 ++++ .../test_cache.py => plex/test_dashboard.py} | 2 +- .../test_general.py => plex/test_media.py} | 2 +- tests/unit/plex/test_plexapi.py | 19 +- tests/unit/plex/test_servers.py | 42 +-- tests/unit/plex/test_tmdb.py | 43 +++ tests/unit/test_entrypoint.py | 5 +- tests/unit/themerr/test_tmdb.py | 23 +- web/templates/home.html | 4 +- 40 files changed, 1665 insertions(+), 922 deletions(-) create mode 100644 docs/source/contributing/media_servers.rst create mode 100644 src/media_servers/__init__.py create mode 100644 src/media_servers/base.py create mode 100644 src/media_servers/processing.py create mode 100644 src/plex/backend.py create mode 100644 src/plex/config.py create mode 100644 src/plex/constants.py create mode 100644 src/plex/dashboard.py rename src/{themerr/general.py => plex/media.py} (99%) create mode 100644 src/plex/tmdb.py create mode 100644 src/plex/web.py create mode 100644 tests/unit/media_servers/test_cache.py create mode 100644 tests/unit/media_servers/test_processing.py create mode 100644 tests/unit/plex/test_backend.py rename tests/unit/{themerr/test_cache.py => plex/test_dashboard.py} (99%) rename tests/unit/{themerr/test_general.py => plex/test_media.py} (99%) create mode 100644 tests/unit/plex/test_tmdb.py diff --git a/docs/source/contributing/media_servers.rst b/docs/source/contributing/media_servers.rst new file mode 100644 index 00000000..104a9833 --- /dev/null +++ b/docs/source/contributing/media_servers.rst @@ -0,0 +1,44 @@ +Media-server integration boundary +================================= + +Plex is the only supported media server. Its implementation lives in ``src/plex``. +Shared services use the abstract contracts in ``src/media_servers/base.py``; +``media_servers.get_backend()`` selects the Plex implementation in one place. + +``MediaServerBackend`` owns saved connections, public connection settings, +account authorization status, event-listener lifecycle, and integration-specific +web routes. ``MediaServer`` owns operations on one server: library discovery, +dashboard snapshots, scanning, item updates, artwork, selected-theme streams, +browser links, and an optional TMDB metadata service. Implementations retain the +server identity and use ``storage.server_scope`` around storage and native work. + +The Plex package owns SDK objects, agent and GUID interpretation, upload methods +and verification, metadata-directory layout and cleanup, account sign-in, +discovery protocols, TMDB proxy credentials, and native media paths. Shared +callers receive public dictionaries or HTTP streams. Stream consumers must close +upstream responses on rejection, HEAD requests, disconnects, and completion; +credentials and upstream response bodies must never enter public failures. + +``media_servers.processing`` owns the worker queue and scheduled scan loop. Queue +entries contain a server ID and an opaque string item ID. Duplicate queued and +active work is suppressed within a server, while identical IDs on different +servers remain independent. Paused or removed servers are skipped by workers. +Failed work releases its active entry so a later scan can retry it. + +``themerr.cache`` owns refresh orchestration and successful-refresh notifications. +An unavailable server keeps its last dashboard snapshot and does not interrupt +other servers. Each implementation publishes the existing section and item +dictionary format through ``storage.replace_dashboard`` only after a complete +scan, using the dashboard revision to preserve concurrent successful uploads. + +The existing database tables, ``rating_key`` fields, HTTP routes, configuration +keys, and credential-store namespaces remain compatible with Plex installations. +Dashboard storage still uses integer library keys, and the existing playback +routes accept integer rating keys. Changing those persisted fields and public +routes belongs to the later work that introduces another integration. +The current browser interface and onboarding still describe Plex. This refactor +does not introduce a server-type setting or another server implementation. + +Tests under ``tests/unit/plex`` cover native integration behavior with mocked +services. Tests under ``tests/unit/media_servers`` cover the shared contracts +and orchestration with opaque identifiers, without a live media server. diff --git a/docs/source/index.md b/docs/source/index.md index a413392f..4b2fac18 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -20,6 +20,7 @@ about/changelog contributing/contributing contributing/database +contributing/media_servers contributing/build contributing/testing ``` diff --git a/locale/themerr-plex.po b/locale/themerr-plex.po index 538ba585..97c2dfb5 100644 --- a/locale/themerr-plex.po +++ b/locale/themerr-plex.po @@ -14,362 +14,362 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.18.0\n" -#: src/common/config.py:90 +#: src/common/config.py:84 msgid "Info" msgstr "" -#: src/common/config.py:91 +#: src/common/config.py:85 msgid "For information purposes only." msgstr "" -#: src/common/config.py:95 +#: src/common/config.py:89 msgid "Config version" msgstr "" -#: src/common/config.py:96 +#: src/common/config.py:90 msgid "The configuration version." msgstr "" -#: src/common/config.py:105 +#: src/common/config.py:99 msgid "First run complete" msgstr "" -#: src/common/config.py:106 +#: src/common/config.py:100 msgid "Todo: Indicates if the user has completed the initial setup." msgstr "" -#: src/common/config.py:113 +#: src/common/config.py:107 msgid "General" msgstr "" -#: src/common/config.py:114 +#: src/common/config.py:108 msgid "General settings." msgstr "" -#: src/common/config.py:118 +#: src/common/config.py:112 msgid "Locale" msgstr "" -#: src/common/config.py:119 +#: src/common/config.py:113 msgid "The localization setting to use." msgstr "" -#: src/common/config.py:128 +#: src/common/config.py:122 msgid "Launch Browser on Startup " msgstr "" -#: src/common/config.py:134 +#: src/common/config.py:128 msgid "Enable System Tray Icon" msgstr "" -#: src/common/config.py:142 +#: src/common/config.py:136 msgid "Notifications" msgstr "" -#: src/common/config.py:143 +#: src/common/config.py:137 msgid "" "Desktop notifications appear on the computer running Themerr. Docker and " "headless installations do not display them." msgstr "" -#: src/common/config.py:148 src/common/notifications.py:138 +#: src/common/config.py:142 src/common/notifications.py:138 msgid "New release available" msgstr "" -#: src/common/config.py:149 +#: src/common/config.py:143 msgid "" "Notify when a newer Themerr release is available. Checks run once per " "hour." msgstr "" -#: src/common/config.py:154 +#: src/common/config.py:148 msgid "Follow pre-releases" msgstr "" -#: src/common/config.py:155 +#: src/common/config.py:149 msgid "Include pre-releases in new release notifications." msgstr "" -#: src/common/config.py:160 +#: src/common/config.py:154 msgid "Theme coverage increased" msgstr "" -#: src/common/config.py:161 +#: src/common/config.py:155 msgid "" "Notify after a successful refresh when total theme coverage increases. " "Refreshes with no increase stay quiet." msgstr "" -#: src/common/config.py:168 +#: src/common/config.py:162 msgid "Logging" msgstr "" -#: src/common/config.py:169 +#: src/common/config.py:163 msgid "Logging settings." msgstr "" -#: src/common/config.py:173 +#: src/common/config.py:167 msgid "Log directory" msgstr "" -#: src/common/config.py:175 +#: src/common/config.py:169 msgid "The directory where to store the log files." msgstr "" -#: src/common/config.py:182 +#: src/common/config.py:176 msgid "Debug logging" msgstr "" -#: src/common/config.py:184 +#: src/common/config.py:178 msgid "Enable debug logging." msgstr "" -#: src/common/config.py:190 +#: src/common/config.py:184 msgid "Network" msgstr "" -#: src/common/config.py:191 +#: src/common/config.py:185 msgid "Network settings." msgstr "" -#: src/common/config.py:195 +#: src/common/config.py:189 msgid "HTTP host address" msgstr "" -#: src/common/config.py:197 +#: src/common/config.py:191 msgid "The HTTP address to bind to." msgstr "" -#: src/common/config.py:206 +#: src/common/config.py:200 msgid "HTTP port" msgstr "" -#: src/common/config.py:208 +#: src/common/config.py:202 msgid "Port to bind web server to. Note that ports below 1024 may require root." msgstr "" -#: src/common/config.py:216 +#: src/common/config.py:210 msgid "HTTP root" msgstr "" -#: src/common/config.py:218 +#: src/common/config.py:212 msgid "Todo: The base URL of the web server. Used for reverse proxies." msgstr "" -#: src/common/config.py:223 +#: src/common/config.py:217 msgid "SSL" msgstr "" -#: src/common/config.py:225 +#: src/common/config.py:219 msgid "" "Run the web server with HTTPS. Disabling this can be a security risk, do " "so at your own risk." msgstr "" -#: src/common/config.py:231 +#: src/common/config.py:225 msgid "Updater" msgstr "" -#: src/common/config.py:232 +#: src/common/config.py:226 msgid "Updater settings." msgstr "" -#: src/common/config.py:236 +#: src/common/config.py:230 msgid "Auto update" msgstr "" -#: src/common/config.py:244 +#: src/common/config.py:238 msgid "Plex" msgstr "" -#: src/common/config.py:245 +#: src/common/config.py:239 msgid "Plex settings." msgstr "" -#: src/common/config.py:249 +#: src/common/config.py:243 msgid "Plex URL" msgstr "" -#: src/common/config.py:250 +#: src/common/config.py:244 msgid "The URL to the Plex server." msgstr "" -#: src/common/config.py:259 web/templates/servers.html:20 +#: src/common/config.py:253 web/templates/servers.html:20 msgid "Plex data directory" msgstr "" -#: src/common/config.py:260 +#: src/common/config.py:254 msgid "" "https://support.plex.tv/articles/202915258-where-is-the-plex-media-" "server-data-directory-located/" msgstr "" -#: src/common/config.py:270 +#: src/common/config.py:264 msgid "Themerr" msgstr "" -#: src/common/config.py:271 +#: src/common/config.py:265 msgid "Themerr settings." msgstr "" -#: src/common/config.py:275 +#: src/common/config.py:269 msgid "Themerr Enabled" msgstr "" -#: src/common/config.py:276 +#: src/common/config.py:270 msgid "When enabled, Themerr will attempt to update themes." msgstr "" -#: src/common/config.py:281 web/templates/home.html:30 +#: src/common/config.py:275 web/templates/home.html:30 msgid "Collections" msgstr "" -#: src/common/config.py:282 +#: src/common/config.py:276 msgid "Add themes to collections." msgstr "" -#: src/common/config.py:287 +#: src/common/config.py:281 msgid "Movie support" msgstr "" -#: src/common/config.py:288 +#: src/common/config.py:282 msgid "Add themes to movies using the Plex Movie agent." msgstr "" -#: src/common/config.py:293 +#: src/common/config.py:287 msgid "Series support" msgstr "" -#: src/common/config.py:294 +#: src/common/config.py:288 msgid "Add themes to series using the Plex Series agent." msgstr "" -#: src/common/config.py:299 +#: src/common/config.py:293 msgid "Overwrite Plex themes" msgstr "" -#: src/common/config.py:300 +#: src/common/config.py:294 msgid "When enabled, Themerr will overwrite themes provided by Plex." msgstr "" -#: src/common/config.py:305 +#: src/common/config.py:299 msgid "Prefer MP4A AAC Codec" msgstr "" -#: src/common/config.py:306 +#: src/common/config.py:300 msgid "This can improve theme compatibility with Apple devices." msgstr "" -#: src/common/config.py:311 +#: src/common/config.py:305 msgid "Remove unused themes" msgstr "" -#: src/common/config.py:312 +#: src/common/config.py:306 msgid "" "Frees up space in your Plex metadata directory. This option requires that" " the \"Plex data directory\" path is set" msgstr "" -#: src/common/config.py:318 +#: src/common/config.py:312 msgid "Remove unused art" msgstr "" -#: src/common/config.py:319 src/common/config.py:327 +#: src/common/config.py:313 src/common/config.py:321 msgid "" "Applies to collection. Frees up space in your Plex metadata directory. " "This option requires that the \"Plex data directory\" path is set" msgstr "" -#: src/common/config.py:326 +#: src/common/config.py:320 msgid "Remove unused posters" msgstr "" -#: src/common/config.py:334 +#: src/common/config.py:328 msgid "Collection metadata" msgstr "" -#: src/common/config.py:335 +#: src/common/config.py:329 msgid "" "Update collection metadata (poster, art, and summary) during scheduled " "update." msgstr "" -#: src/common/config.py:340 +#: src/common/config.py:334 msgid "Ignore locked fields." msgstr "" -#: src/common/config.py:341 +#: src/common/config.py:335 msgid "" "If you used Themerr-plex v2024.813.13709 or lower, you may need to enable" " this to update items." msgstr "" -#: src/common/config.py:347 +#: src/common/config.py:341 msgid "Ignored library IDs" msgstr "" -#: src/common/config.py:348 +#: src/common/config.py:342 msgid "" "Comma-separated library IDs to exclude from scheduled updates. IDs appear" " on Home." msgstr "" -#: src/common/config.py:354 +#: src/common/config.py:348 msgid "Update themes interval" msgstr "" -#: src/common/config.py:355 +#: src/common/config.py:349 msgid "Interval for automatic update task, in minutes (min: 15)." msgstr "" -#: src/common/config.py:362 +#: src/common/config.py:356 msgid "Update database cache interval" msgstr "" -#: src/common/config.py:363 +#: src/common/config.py:357 msgid "Interval for database cache update task, in minutes (min: 15)." msgstr "" -#: src/common/config.py:370 +#: src/common/config.py:364 msgid "PlexAPI timeout" msgstr "" -#: src/common/config.py:371 +#: src/common/config.py:365 msgid "" "Increase this slightly if you experience timeouts when adding themes. " "(min: 1)" msgstr "" -#: src/common/config.py:378 +#: src/common/config.py:372 msgid "Theme upload retries" msgstr "" -#: src/common/config.py:379 +#: src/common/config.py:373 msgid "If uploading themes fail, retry this many times. (min: 0)" msgstr "" -#: src/common/config.py:386 +#: src/common/config.py:380 msgid "Multiprocessing thread count" msgstr "" -#: src/common/config.py:387 +#: src/common/config.py:381 msgid "" "The number of threads to use when adding themes. Restart required. (min: " "1)" msgstr "" -#: src/common/config.py:394 +#: src/common/config.py:388 msgid "YouTube Cookies" msgstr "" -#: src/common/config.py:396 +#: src/common/config.py:390 msgid "" "Optional: export youtube.com cookies with Get cookies.txt LOCALLY using " "its JSON format, then paste the entire JSON array here and save. Cookies " "contain your browser session; keep them private." msgstr "" -#: src/common/config.py:401 +#: src/common/config.py:395 msgid "Cookie export instructions" msgstr "" @@ -458,12 +458,12 @@ msgstr "" msgid "Quit" msgstr "" -#: src/common/webapp.py:84 web/templates/logs.html:4 +#: src/common/webapp.py:80 web/templates/logs.html:4 #: web/templates/navbar.html:6 msgid "Logs" msgstr "" -#: src/common/webapp.py:463 web/templates/config.html:22 +#: src/common/webapp.py:430 web/templates/config.html:22 #: web/templates/navbar.html:6 msgid "Settings" msgstr "" diff --git a/src/common/config.py b/src/common/config.py index 53489d1a..af966323 100644 --- a/src/common/config.py +++ b/src/common/config.py @@ -74,14 +74,8 @@ def on_change_plex_url() -> None: -------- >>> on_change_plex_url() """ - from plex import plexapi - plexapi.stop_plex_listener() - plexapi.plex_server = None - if plexapi.auth.get_token(): - try: - plexapi.plex_listener() - except Exception: - log.exception('Unable to reconnect to the changed Plex URL') + from plex.config import reconnect + reconnect() _CONFIG_SPEC_DICT = { diff --git a/src/common/helpers.py b/src/common/helpers.py index fb37231d..fcf71942 100644 --- a/src/common/helpers.py +++ b/src/common/helpers.py @@ -236,7 +236,7 @@ def get_logger(name: str) -> logging.Logger: >>> get_logger(name='my_log') """ - if name.split('.')[0] in ('common', 'plex', 'themerr', 'youtube', 'themerr_plex', 'schedule'): + if name.split('.')[0] in ('common', 'media_servers', 'plex', 'themerr', 'youtube', 'themerr_plex', 'schedule'): name = 'themerr' return logging.getLogger(name=name) diff --git a/src/common/notifications.py b/src/common/notifications.py index 7ca33e7d..cb96f213 100644 --- a/src/common/notifications.py +++ b/src/common/notifications.py @@ -15,7 +15,7 @@ # local imports from common import config, definitions, locales, logger, version -from plex import servers +from media_servers import get_backend from themerr import storage log = logger.get_logger(__name__) @@ -153,7 +153,7 @@ def refresh_completed() -> None: """ try: with _lock: - records = servers.list_servers() + records = get_backend().list_servers() items = [] for record in records: with storage.server_scope(record['id']): diff --git a/src/common/server_ui.py b/src/common/server_ui.py index b7bd43e2..0b48704c 100644 --- a/src/common/server_ui.py +++ b/src/common/server_ui.py @@ -2,14 +2,11 @@ # standard imports import re -from urllib.parse import quote, urlencode # lib imports from fastapi import APIRouter, Depends, Request from starlette.responses import JSONResponse from fastapi import HTTPException -from plexapi.exceptions import Unauthorized -from requests.exceptions import ConnectionError, RequestException, SSLError, Timeout from sqlalchemy import select from sqlalchemy.orm import Session @@ -17,7 +14,8 @@ from common.http import read_json, render_template from common import logger from common.validation import ValidationError -from plex import auth, plexapi, servers, token_store +from media_servers import get_backend, processing +from media_servers.base import MediaServerError from themerr import storage, theme_errors router = APIRouter() @@ -64,7 +62,7 @@ def _metadata_url(item: dict) -> str | None: Parameters ---------- item : dict - Cached Plex item and resolved external identifiers. + Cached media item and resolved external identifiers. Returns ------- @@ -96,24 +94,24 @@ def dashboard() -> tuple[dict, dict, dict]: Libraries, scoped errors, and real summary counts. """ libraries, errors = {}, {} - registered = servers.list_servers() - records = registered - for record in records: + backend = get_backend() + registered = backend.list_servers() + for record in registered: + server = backend.server(record['id']) with storage.server_scope(record['id']): snapshot = storage.get_dashboard() or {} failures = storage.get_errors() for key, section in snapshot.items(): identity = record['id'] + ':' + key - server_url = ('https://app.plex.tv/desktop/#!/media/' + quote(record['id'], safe='') + - '/com.plexapp.plugins.library') + urls = server.web_urls(str(section['key'])) libraries[identity] = {**section, 'server_id': record['id'], 'server_name': record['name'], - 'enabled': record['enabled'], 'server_url': server_url, - 'library_url': server_url + '?' + urlencode({'source': section['key']})} + 'enabled': record['enabled'], 'server_url': urls['server'], + 'library_url': urls['library']} for item in section['items']: item['server_id'] = record['id'] item['error'] = failures.get(item['rating_key']) - item['plex_url'] = 'https://app.plex.tv/desktop/#!/server/' + quote(record['id'], safe='') + ( - '/details?' + urlencode({'key': '/library/metadata/' + str(item['rating_key'])})) + item['server_item_url'] = server.web_urls( + str(section['key']), item['rating_key'])['item'] item['metadata_url'] = _metadata_url(item) item['show_edit'] = theme_errors.is_video_issue(item['error']) errors[record['id'] + ':' + item['rating_key']] = item['error'] @@ -139,8 +137,8 @@ def server_page(request: Request): request, 'servers.html', title='Servers', - servers=servers.list_servers(), - plex_connected=bool(auth.get_token()), + servers=get_backend().list_servers(), + plex_connected=get_backend().account_connected(), ) @@ -152,14 +150,14 @@ def server_libraries(server_id: str): Parameters ---------- server_id : str - Saved Plex machine identifier. + Saved server identifier. Returns ------- Response Library choices, using cached metadata when the server is paused or offline. """ - record = servers.get_server(server_id) + record = get_backend().get_server(server_id) if record is None: return JSONResponse({'message': 'Server not found.'}, status_code=404) with Session(storage.engine()) as session: @@ -168,71 +166,14 @@ def server_libraries(server_id: str): storage.LibrarySection.server_id == server_id).order_by(storage.LibrarySection.title))] if record['enabled']: try: - connection = servers.connect(server_id) - if connection is not None: - libraries = [{'id': str(section.key), 'title': section.title} - for section in connection.library.sections()] + libraries = get_backend().server(server_id).libraries() + if libraries is not None: return JSONResponse({'libraries': libraries, 'cached': False}) except Exception as exc: - log.warning('Could not load Plex libraries (%s)', type(exc).__name__) + log.warning('Could not load media-server libraries (%s)', type(exc).__name__) return JSONResponse({'libraries': cached, 'cached': True}) -@router.api_route('/api/servers/discover', methods=['POST'], name='server_ui.discover', response_model=None) -def discover(payload=Depends(_payload)): - """List account or LAN resources without disclosing access tokens. - - Returns - ------- - Response - Safe discovery results or a connection error. - """ - source = payload.get('source') - if source not in ('account', 'local'): - return JSONResponse({'message': 'Choose account or local discovery.'}, status_code=400) - try: - resources = servers.discover_account() if source == 'account' else servers.discover_local() - except Exception as exc: - return _failure(exc, 'Discovery failed. Check the Plex connection, or enter an address manually.') - return JSONResponse({'servers': resources}) - - -@router.api_route('/api/servers', methods=['POST'], name='server_ui.add_server', response_model=None) -def add_server(payload=Depends(_payload)): - """Connect to a selected or manually addressed server. - - Returns - ------- - Response - Saved public settings or a sanitized failure. - """ - try: - record = servers.add_server(payload.get('url', ''), payload.get('resource_id')) - except token_store.TokenStorageError as exc: - return _failure(exc, 'Unable to save the Plex token. Check the configured credential store.', 500) - except SSLError as exc: - return _failure(exc, 'The secure connection to Plex failed. Use its advertised HTTPS address and check ' - 'the server certificate.') - except Timeout as exc: - return _failure(exc, 'The Plex connection timed out. Check that the server is running and this address is ' - 'reachable from the machine running Themerr. Try another advertised or manual address.') - except ConnectionError as exc: - return _failure(exc, 'Could not connect to this Plex address. Check its hostname, port, and network access ' - 'from the machine running Themerr, or try another address.') - except Unauthorized as exc: - return _failure(exc, 'Plex denied access to this server. Check the linked account has permission, or ' - 'reconnect your Plex account.') - except RequestException as exc: - return _failure(exc, 'Plex returned an invalid response. Check the address or try another connection.') - except ValidationError as exc: - return _failure(exc, exc.reason.value, 400) - except Exception as exc: - return _failure(exc, 'Could not connect to this Plex server. Check the address and account access.') - plexapi.plex_listener() - _refresh() - return JSONResponse({'server': record}, status_code=201) - - @router.api_route( '/api/servers/{server_id}', methods=['POST', 'DELETE'], @@ -245,29 +186,30 @@ def edit_server(request: Request, server_id: str, payload=Depends(read_json)): Parameters ---------- server_id : str - Plex machine identifier. + Server identifier. Returns ------- Response Updated settings or removal result. """ - if not servers.get_server(server_id): + if not get_backend().get_server(server_id): return JSONResponse({'message': 'Server not found.'}, status_code=404) if request.method != 'DELETE': payload = _validated_payload(payload) try: if request.method == 'DELETE': - servers.remove_server(server_id) + get_backend().remove_server(server_id) else: - servers.update_server(server_id, payload) + get_backend().update_server(server_id, payload) except ValidationError as exc: return _failure(exc, exc.reason.value, 400) - except token_store.TokenStorageError as exc: - return _failure(exc, 'Could not update the secure credential store.', 500) + except MediaServerError as exc: + return _failure(exc, str(exc), exc.status_code) except Exception as exc: - return _failure(exc, 'Could not update this Plex server. Check its connection and settings.', 500) - plexapi.plex_listener() + return _failure(exc, f'Could not update this {get_backend().name} server. ' + 'Check its connection and settings.', 500) + get_backend().start_listeners() return JSONResponse({'message': 'Server removed.' if request.method == 'DELETE' else 'Server settings saved.'}) @@ -300,7 +242,7 @@ def refresh(payload=Depends(_payload)): {'message': 'Enable theme updates in Settings before starting a scan.'}, status_code=400, ) - scheduled_tasks.run_threaded(target=plexapi.scheduled_update, task_name='Theme scan and queue') + scheduled_tasks.run_threaded(target=processing.scheduled_update, task_name='Theme scan and queue') job = _refresh() return JSONResponse( {'message': 'Refreshing libraries. Follow progress in Activity.', 'job_id': job.job_id}, @@ -335,7 +277,7 @@ def activity(request: Request): failures = [{ 'server': section['server_name'], 'library': section['title'], **item, 'reason': item['error'] or ( - 'TMDB ID unavailable. Review the item metadata in Plex.' + f'TMDB ID unavailable. Review the item metadata in {get_backend().name}.' if item['theme_status'] == 'unresolved' else 'The theme could not be added.' ), } for section in libraries.values() for item in section['items'] @@ -346,7 +288,7 @@ def activity(request: Request): title='Activity', jobs=scheduled_tasks.job_history(), failures=failures, - queue_size=plexapi.q.qsize(), + queue_size=processing.q.qsize(), ) @@ -360,4 +302,4 @@ def task_status(): Recent tasks and current queued item count. """ from themerr import scheduled_tasks - return JSONResponse({'jobs': scheduled_tasks.job_history(), 'queue_size': plexapi.q.qsize()}) + return JSONResponse({'jobs': scheduled_tasks.job_history(), 'queue_size': processing.q.qsize()}) diff --git a/src/common/webapp.py b/src/common/webapp.py index 8bc49989..b24d1381 100644 --- a/src/common/webapp.py +++ b/src/common/webapp.py @@ -6,9 +6,7 @@ # standard imports import copy import os -import re import secrets -import time from threading import Event # lib imports @@ -20,7 +18,6 @@ from starlette.exceptions import HTTPException import uvicorn import anyio -from plexapi import exceptions as plex_exceptions import requests # local imports @@ -33,8 +30,8 @@ from common.definitions import Paths from common import locales from common import logger -from plex import auth as plex_auth -from themerr import storage +from media_servers import get_backend +from media_servers.base import MediaServerError # variables URL_SCHEME = None @@ -57,7 +54,6 @@ router = APIRouter() log = logger.get_logger(__name__) -PLEX_LOGIN_LIFETIME = 600 @router.api_route('/logs', methods=['GET', 'HEAD'], name='logs', response_model=None) @@ -128,7 +124,7 @@ def home(request: Request) -> Response: """ Serve the webapp home page. - Show cached Plex library data once it is available. Until then, show a progress page. + Show cached library data once it is available. Until then, show a progress page. Parameters ---------- @@ -169,7 +165,7 @@ def home(request: Request) -> Response: items=items, theme_errors=errors, stats=stats, - servers=server_ui.servers.list_servers(), + servers=get_backend().list_servers(), ) @@ -178,7 +174,7 @@ def home(request: Request) -> Response: @router.api_route('/api/servers/{server_id}/themes/{rating_key:int}/poster', methods=['GET', 'HEAD'], name='theme_poster', response_model=None) def theme_poster(request: Request, rating_key: int, server_id: str = 'default') -> Response: - """Serve a bounded Plex poster without exposing server credentials. + """Serve a bounded media-server poster without exposing server credentials. Resolve artwork from the saved server's current item metadata. Only raster images are returned, with redirects disabled and a five-megabyte response limit. @@ -188,9 +184,9 @@ def theme_poster(request: Request, rating_key: int, server_id: str = 'default') request : Request Authenticated browser request. rating_key : int - Plex item identifier. + Media-server item identifier. server_id : str, optional - Saved Plex server identifier. + Saved server identifier. Returns ------- @@ -202,21 +198,10 @@ def theme_poster(request: Request, rating_key: int, server_id: str = 'default') >>> theme_poster(request, 42) """ - from plex import plexapi - try: - with storage.server_scope(server_id): - server = plexapi.setup_plexapi() - if server is None: - return Response(status_code=404) - item = server.fetchItem(rating_key) - thumbnail = getattr(item, 'thumb', None) - if not isinstance(thumbnail, str) or not re.fullmatch(r'/library/metadata/\d+/thumb(?:/\d+)?', thumbnail): + upstream = get_backend().server(server_id).open_poster(str(rating_key)) + if upstream is None: return Response(status_code=404) - upstream = server._session.get( - server.url(thumbnail, includeToken=False), headers=server._headers(), - stream=True, allow_redirects=False, timeout=config.CONFIG['Themerr']['INT_PLEXAPI_PLEXAPI_TIMEOUT'], - ) try: media_type = upstream.headers.get('Content-Type', '').split(';', 1)[0].lower() if upstream.status_code != 200 or media_type not in ( @@ -234,22 +219,22 @@ def theme_poster(request: Request, rating_key: int, server_id: str = 'default') return Response(b''.join(chunks), media_type=media_type, headers={'Cache-Control': 'no-store'}) finally: upstream.close() - except plex_exceptions.NotFound: - return Response(status_code=404) - except (plex_exceptions.PlexApiException, OSError, ValueError) as error: + except MediaServerError as error: + return Response(status_code=error.status_code) + except (OSError, ValueError) as error: log.warning('Unable to load poster for rating_key=%s (%s)', rating_key, type(error).__name__) return Response(status_code=502) async def _stream_theme_audio(upstream: requests.Response, rating_key: int): - """Stream Plex audio and release the connection when playback stops. + """Stream theme audio and release the connection when playback stops. Parameters ---------- upstream : requests.Response - Open streaming response from the configured Plex server. + Open streaming response from the media server. rating_key : int - Plex item identifier for diagnostic logging. + Media-server item identifier for diagnostic logging. Yields ------ @@ -270,12 +255,12 @@ async def _stream_theme_audio(upstream: requests.Response, rating_key: int): class ThemeAudioResponse(StreamingResponse): """Stream theme audio with guaranteed upstream cleanup. - Close the Plex connection even when sending headers or chunks is cancelled. + Close the upstream connection even when sending headers or chunks is cancelled. Parameters ---------- upstream : requests.Response - Open audio response from Plex. + Open audio response from the media server. rating_key : int Item identifier used for playback diagnostics. headers : dict @@ -332,9 +317,9 @@ async def __call__(self, scope, receive, send): response_model=None, ) def play_theme(request: Request, rating_key: int, server_id: str = 'default') -> Response: - """Serve the item's current Plex theme without exposing the Plex token. + """Serve the item's current server theme without exposing server credentials. - Resolve the selected audio from fresh Plex metadata and forward byte range requests + Resolve the selected audio from fresh server metadata and forward byte range requests so browsers can determine the duration. Only media response headers reach the browser. Parameters @@ -344,7 +329,7 @@ def play_theme(request: Request, rating_key: int, server_id: str = 'default') -> rating_key : int Item whose selected theme should be played, regardless of its provider. server_id : str - Plex machine identifier. + Server identifier. Returns ------- @@ -356,49 +341,29 @@ def play_theme(request: Request, rating_key: int, server_id: str = 'default') -> >>> play_theme(request, rating_key=42) # FastAPI invokes this for GET /api/themes/42 """ - from plex import plexapi - + headers = {'Accept-Encoding': 'identity'} + for header in ('Range', 'If-Range'): + value = request.headers.get(header) + if value is not None: + headers[header] = value try: - with storage.server_scope(server_id): - server = plexapi.setup_plexapi() - if server is None: - return JSONResponse({'message': 'Connect to Plex before playing themes.'}, status_code=503) - item = server.fetchItem(rating_key) - theme_path = getattr(item, 'theme', None) - if not theme_path: - return JSONResponse({'message': 'This item has no theme.'}, status_code=404) - if not theme_path.startswith('/library/metadata/') or '/theme/' not in theme_path: - return JSONResponse({'message': 'Plex returned an unsupported theme path.'}, status_code=502) - - headers = {'Accept-Encoding': 'identity'} - for header in ('Range', 'If-Range'): - value = request.headers.get(header) - if value is not None: - headers[header] = value - upstream = server._session.get( - server.url(theme_path, includeToken=False), headers=server._headers(**headers), - stream=True, allow_redirects=False, timeout=config.CONFIG['Themerr']['INT_PLEXAPI_PLEXAPI_TIMEOUT'], - ) - except plex_exceptions.NotFound: - return JSONResponse({'message': 'This Plex item is no longer available.'}, status_code=404) - except (plex_exceptions.PlexApiException, OSError, ValueError) as error: - log.warning('Unable to load theme for rating_key=%s (%s)', rating_key, type(error).__name__) - return JSONResponse({'message': 'Unable to load theme audio from Plex.'}, status_code=502) - + upstream = get_backend().server(server_id).open_theme(str(rating_key), headers) + except MediaServerError as error: + return JSONResponse({'message': str(error)}, status_code=error.status_code) return _theme_audio_response(request, upstream, rating_key) def _theme_audio_response(request: Request, upstream: requests.Response, rating_key: int) -> Response: - """Build a browser response and close rejected Plex audio streams. + """Build a browser response and close rejected theme audio streams. Parameters ---------- request : Request Incoming browser or API request. upstream : requests.Response - Stream returned by Plex. + Stream returned by the media server. rating_key : int - Plex item identifier for playback diagnostics. + Media-server item identifier for playback diagnostics. Returns ------- @@ -415,12 +380,14 @@ def _theme_audio_response(request: Request, upstream: requests.Response, rating_ headers['Content-Length'] = '0' return Response(status_code=416, headers=headers) status = 404 if upstream.status_code == 404 else 502 - log.warning('Plex rejected theme playback for rating_key=%s (HTTP %s)', rating_key, upstream.status_code) - return JSONResponse({'message': 'Plex could not provide theme audio.'}, status_code=status) + log.warning('Media server rejected theme playback for rating_key=%s (HTTP %s)', + rating_key, upstream.status_code) + return JSONResponse({'message': f'{get_backend().name} could not provide theme audio.'}, status_code=status) content_type = upstream.headers.get('Content-Type', 'application/octet-stream').split(';', 1)[0].lower() if not content_type.startswith('audio/') and content_type not in ('video/mp4', 'application/octet-stream'): upstream.close() - return JSONResponse({'message': 'Plex returned an unsupported audio format.'}, status_code=502) + return JSONResponse({'message': f'{get_backend().name} returned an unsupported audio format.'}, + status_code=502) headers['Content-Type'] = content_type if request.method == 'HEAD': upstream.close() @@ -772,143 +739,6 @@ def api_settings(request: Request, form=Depends(read_form)) -> Response: return _save_settings(candidate, changed) -@router.api_route('/api/plex/auth', methods=['GET', 'HEAD'], name='plex_auth_status', response_model=None) -def plex_auth_status() -> Response: - """Report whether this installation has a token from Plex sign-in. - - The response contains only a connection flag and never includes the token. - - Returns - ------- - Response - Authentication status without exposing the token. - - Examples - -------- - >>> plex_auth_status() - - """ - return JSONResponse({'connected': bool(plex_auth.get_token())}) - - -@router.api_route('/api/plex/auth/start', methods=['POST'], name='plex_auth_start', response_model=None) -def plex_auth_start(request: Request) -> Response: - """Start a Plex browser sign-in for this web session. - - Store the PIN in the browser session so it can be checked later. - - Parameters - ---------- - request : Request - Incoming browser or API request. - - Returns - ------- - Response - Plex authorization URL or a sanitized error. - - Examples - -------- - >>> plex_auth_start(request) - - """ - try: - login = plex_auth.start_login() - except (OSError, KeyError, TypeError, ValueError): - log.exception('Unable to start Plex sign-in') - return JSONResponse({'message': 'Unable to start Plex sign-in. Please try again.'}, status_code=502) - - request.session['plex_login'] = {'pin_id': login['pin_id'], 'code': login['code'], 'started': time.time()} - return JSONResponse({'auth_url': login['auth_url']}) - - -@router.api_route('/api/plex/auth/check', methods=['POST'], name='plex_auth_check', response_model=None) -def plex_auth_check(request: Request) -> Response: - """Finish a Plex sign-in once its PIN has been claimed. - - Keep the previous connection until the new token reaches the selected server. - - Parameters - ---------- - request : Request - Incoming browser or API request. - - Returns - ------- - Response - Pending, connected, expired, or error status. - - Examples - -------- - >>> plex_auth_check(request) - - """ - login = request.session.get('plex_login') - if not login: - return JSONResponse({'message': 'Start Plex sign-in first.'}, status_code=400) - if time.time() - login['started'] > PLEX_LOGIN_LIFETIME: - request.session.pop('plex_login', None) - return JSONResponse({'message': 'Plex sign-in expired. Please try again.'}, status_code=410) - - try: - token = plex_auth.check_login(pin_id=login['pin_id'], code=login['code']) - except requests.HTTPError as error: - if error.response is not None and error.response.status_code in (404, 410): - request.session.pop('plex_login', None) - return JSONResponse({'message': 'Plex sign-in expired. Please try again.'}, status_code=410) - log.warning('Unable to check Plex sign-in: %s', error) - return JSONResponse({'message': 'Unable to check Plex sign-in. Please try again.'}, status_code=502) - except (requests.RequestException, KeyError, TypeError, ValueError): - log.exception('Unable to check Plex sign-in') - return JSONResponse({'message': 'Unable to check Plex sign-in. Please try again.'}, status_code=502) - - if not token: - return JSONResponse({'connected': False}, status_code=202) - - try: - plex_auth.set_token(token) - except OSError: - log.exception('Unable to save Plex sign-in') - return JSONResponse({'message': 'Unable to save Plex sign-in.'}, status_code=500) - request.session.pop('plex_login', None) - return JSONResponse({'connected': True}) - - -@router.api_route('/api/plex/auth/disconnect', methods=['POST'], name='plex_auth_disconnect', response_model=None) -def plex_auth_disconnect(request: Request) -> Response: - """Remove the local Plex connection. - - Clear the saved token and stop the active event listener. - - Parameters - ---------- - request : Request - Incoming browser or API request. - - Returns - ------- - Response - Disconnected status. - - Examples - -------- - >>> plex_auth_disconnect(request) - - """ - try: - server_ui.servers.disconnect_account() - except OSError: - log.exception('Unable to disconnect Plex') - return JSONResponse({'message': 'Unable to disconnect Plex.'}, status_code=500) - - from plex import plexapi - plexapi.stop_plex_listener() - server_ui.servers.clear_connections() - plexapi.plex_server = None - request.session.pop('plex_login', None) - return JSONResponse({'connected': False}) - - @router.api_route('/translations', methods=['GET', 'HEAD'], name='translations', response_model=None) def translations() -> Response: """ @@ -1068,12 +898,14 @@ def create_app(*, https_only: bool | None = None) -> FastAPI: application.add_middleware(SessionMiddleware, secret_key=application.state.secret_key, max_age=12 * 60 * 60, same_site='lax', https_only=https_only) application.include_router(admin.router) + integration_router = get_backend().web_router() + application.include_router(integration_router) application.include_router(server_ui.router) application.include_router(router) def openapi(): if application.openapi_schema is None: - routes = [*admin.router.routes, *server_ui.router.routes, *router.routes] + routes = [*admin.router.routes, *server_ui.router.routes, *integration_router.routes, *router.routes] application.openapi_schema = api_docs.schema(routes) return application.openapi_schema diff --git a/src/media_servers/__init__.py b/src/media_servers/__init__.py new file mode 100644 index 00000000..384c21c7 --- /dev/null +++ b/src/media_servers/__init__.py @@ -0,0 +1,26 @@ +"""Select the installed media-server integration for shared application services.""" + +from functools import lru_cache + +from media_servers.base import MediaServerBackend + + +@lru_cache(maxsize=1) +def get_backend() -> MediaServerBackend: + """Return the application's media-server backend. + + Plex is the sole supported implementation. Keep that selection here so shared + services do not import the integration or its SDK directly. + + Returns + ------- + MediaServerBackend + The Plex integration. + + Examples + -------- + >>> get_backend().name + 'Plex' + """ + from plex.backend import PlexBackend + return PlexBackend() diff --git a/src/media_servers/base.py b/src/media_servers/base.py new file mode 100644 index 00000000..6834d4f5 --- /dev/null +++ b/src/media_servers/base.py @@ -0,0 +1,280 @@ +"""Contracts between media-server integrations and Themerr's shared services.""" + +from abc import ABC, abstractmethod +from typing import Callable, Mapping + +from fastapi import APIRouter +from requests import Response + + +class MediaServerError(Exception): + """An integration failure safe to report to an authenticated client. + + Adapters translate native SDK failures into fixed messages and HTTP statuses. + Callers can expose this exception without serializing the original cause. + + Parameters + ---------- + message : str + Fixed public explanation without upstream credentials or response bodies. + status_code : int + HTTP status for the public failure. + + Examples + -------- + >>> error = MediaServerError('This item has no theme.', 404) + >>> error.status_code + 404 + """ + + def __init__(self, message: str, status_code: int): + super().__init__(message) + self.status_code = status_code + + +class MediaServer(ABC): + """Operations on one server, using opaque item and library identifiers. + + Implementations own native SDK objects, credentials, protocol paths, metadata + interpretation, and upload handling. Shared callers receive dashboard data or + an HTTP stream, never native SDK objects. Each operation must retain the + server's storage scope, including when invoked by a background worker. + + Parameters + ---------- + server_id : str + Saved server identifier, or the legacy default connection identifier. + """ + + def __init__(self, server_id: str): + self.server_id = server_id + + @abstractmethod + def libraries(self) -> list[dict] | None: + """Return library choices, or None when a live connection is unavailable. + + Returns + ------- + list of dict or None + Library dictionaries with string ``id`` and ``title`` fields. + """ + + @abstractmethod + def cache_dashboard(self) -> bool: + """Publish a complete dashboard snapshot in this server's storage scope. + + Returns + ------- + bool + Whether a complete snapshot was published. + """ + + @abstractmethod + def scan(self, enqueue: Callable[[str], bool]) -> None: + """Queue eligible items while honoring integration processing settings. + + Parameters + ---------- + enqueue : callable + Callback accepting an opaque item identifier in this server's scope. + """ + + @abstractmethod + def update_item(self, item_id: str) -> bool: + """Apply theme and metadata updates to an eligible item. + + Parameters + ---------- + item_id : str + Opaque server item identifier. + + Returns + ------- + bool + Whether the item was processed successfully. + """ + + @abstractmethod + def open_poster(self, item_id: str) -> Response | None: + """Open a credentialed poster stream without following redirects. + + Parameters + ---------- + item_id : str + Opaque server item identifier. + + Returns + ------- + requests.Response or None + Stream owned and closed by the caller, or no poster. + """ + + @abstractmethod + def open_theme(self, item_id: str, headers: Mapping[str, str]) -> Response: + """Open the selected theme stream without following redirects. + + Parameters + ---------- + item_id : str + Opaque server item identifier. + headers : mapping of str to str + Byte-range and encoding headers supplied by the playback service. + + Returns + ------- + requests.Response + Stream owned and closed by the caller. + """ + + @abstractmethod + def web_urls(self, library_id: str, item_id: str | None = None) -> dict[str, str]: + """Build browser links using the integration's native URL format. + + Parameters + ---------- + library_id : str + Library identifier. + item_id : str or None, optional + Item identifier when an item link is needed. + + Returns + ------- + dict of str to str + ``server``, ``library``, and optionally ``item`` URLs. + """ + + def query_tmdb(self, path: str, params: dict) -> dict: + """Optionally resolve metadata through this server's TMDB service. + + Parameters + ---------- + path : str + TMDB service path. + params : dict + TMDB query parameters. + + Returns + ------- + dict + JSON data, or an empty dictionary when this capability is unavailable. + """ + return {} + + +class MediaServerBackend(ABC): + """Manage saved connections and the lifecycle of one integration. + + Attributes + ---------- + name : str + Display name used in public status messages. + """ + + name: str + + @abstractmethod + def server(self, server_id: str) -> MediaServer: + """Create an operations adapter for a saved or legacy default connection. + + Parameters + ---------- + server_id : str + Server identifier. + + Returns + ------- + MediaServer + Adapter retaining this identity throughout its operations. + """ + + @abstractmethod + def list_servers(self, enabled_only: bool = False) -> list[dict]: + """Read saved public connection settings without credentials. + + Parameters + ---------- + enabled_only : bool, optional + Include only connections enabled for processing. + + Returns + ------- + list of dict + Saved server settings including identity, name, and enabled state. + """ + + @abstractmethod + def get_server(self, server_id: str) -> dict | None: + """Find public settings for one saved connection. + + Parameters + ---------- + server_id : str + Server identifier. + + Returns + ------- + dict or None + Saved settings, or no matching server. + """ + + @abstractmethod + def update_server(self, server_id: str, values: dict) -> None: + """Validate and save processing preferences. + + Parameters + ---------- + server_id : str + Server identifier. + values : dict + Submitted preferences. + """ + + @abstractmethod + def remove_server(self, server_id: str) -> None: + """Remove a connection and its credentials and cached state. + + Parameters + ---------- + server_id : str + Server identifier. + """ + + @abstractmethod + def record_refresh(self, server_id: str, error: str | None = None) -> None: + """Record a successful refresh or a sanitized failure. + + Parameters + ---------- + server_id : str + Server identifier. + error : str or None, optional + Fixed public error, cleared on success. + """ + + @abstractmethod + def account_connected(self) -> bool: + """Report whether account authorization is available. + + Returns + ------- + bool + Whether account credentials are present. + """ + + @abstractmethod + def start_listeners(self) -> None: + """Replace event listeners for the currently enabled connections.""" + + @abstractmethod + def stop_listeners(self) -> None: + """Stop the integration's event listeners.""" + + @abstractmethod + def web_router(self) -> APIRouter: + """Expose integration-specific sign-in and discovery routes. + + Returns + ------- + APIRouter + Routes protected by the application's authentication and CSRF middleware. + """ diff --git a/src/media_servers/processing.py b/src/media_servers/processing.py new file mode 100644 index 00000000..e25aadff --- /dev/null +++ b/src/media_servers/processing.py @@ -0,0 +1,90 @@ +"""Server-scoped theme queue and scan orchestration.""" + +import queue +import threading + +from common import config, logger +from media_servers import get_backend +from themerr import storage + +log = logger.get_logger(__name__) +_active_items: set[tuple[str, str]] = set() + + +class _WorkQueue(queue.Queue): + """Mark dequeued items active while the queue lock is still held.""" + + def _get(self): + work = super()._get() + _active_items.add(work) + return work + + +q = _WorkQueue() + + +def enqueue(item_id: str) -> bool: + """Queue an item once within the current server's storage scope. + + Parameters + ---------- + item_id : str + Opaque media-server item identifier. + + Returns + ------- + bool + Whether new work was queued, including active-work deduplication. + """ + work = (storage.current_server_id(), str(item_id)) + with q.mutex: + if work in q.queue or work in _active_items: + return False + q._put(work) + q.unfinished_tasks += 1 + q.not_empty.notify() + return True + + +def process_queue() -> None: + """Process queued items independently, releasing active work even on failure.""" + while True: + server_id, item_id = q.get() + try: + backend = get_backend() + with storage.server_scope(server_id): + if server_id == 'default' or (backend.get_server(server_id) or {}).get('enabled'): + backend.server(server_id).update_item(item_id) + except Exception: + log.exception('Unexpected error processing item %s on server %s', item_id, server_id) + finally: + with q.mutex: + _active_items.discard((server_id, item_id)) + q.task_done() + + +def start_queue_threads() -> None: + """Start the configured number of daemon theme-processing workers.""" + # Preserve the existing preference key and its configured value. + for _ in range(max(1, int(config.CONFIG['Themerr']['INT_PLEXAPI_UPLOAD_THREADS']))): + try: + threading.Thread(target=process_queue, daemon=True).start() + except RuntimeError: + log.exception('Unable to start a theme-processing worker') + break + + +def scheduled_update() -> None: + """Scan enabled connections while isolating unavailable servers.""" + if not config.CONFIG['Themerr']['BOOL_THEMERR_ENABLED']: + return + backend = get_backend() + for record in backend.list_servers(enabled_only=True): + try: + with storage.server_scope(record['id']): + log.info('Scanning server %r (%s)', record['name'], record['id']) + backend.server(record['id']).scan(enqueue) + except Exception: + log.exception('Theme scan failed for server %s', record['id']) + backend.record_refresh(record['id'], + f'Theme scan could not reach {backend.name}. Check its address and access.') diff --git a/src/plex/backend.py b/src/plex/backend.py new file mode 100644 index 00000000..2b3ea97d --- /dev/null +++ b/src/plex/backend.py @@ -0,0 +1,141 @@ +"""Plex implementation of the media-server contracts.""" + +import re +from typing import Callable, Mapping +from urllib.parse import quote, urlencode + +from plexapi import exceptions +from requests import Response + +from common import config, logger +from media_servers.base import MediaServer, MediaServerBackend, MediaServerError +from plex import auth, dashboard, plexapi, servers, tmdb, token_store +from themerr import storage + +log = logger.get_logger(__name__) + + +class PlexMediaServer(MediaServer): + """Keep native Plex operations within one server's storage scope. + + Parameters + ---------- + server_id : str + Plex machine identifier, or the legacy default scope. + """ + + def libraries(self) -> list[dict] | None: + with storage.server_scope(self.server_id): + connection = plexapi.setup_plexapi() + if connection is None: + return None + return [{'id': str(section.key), 'title': section.title} for section in connection.library.sections()] + + def cache_dashboard(self) -> bool: + with storage.server_scope(self.server_id): + return dashboard._cache_server() + + def scan(self, enqueue: Callable[[str], bool]) -> None: + with storage.server_scope(self.server_id): + plexapi.scan_items(enqueue) + + def update_item(self, item_id: str) -> bool: + with storage.server_scope(self.server_id): + return plexapi.update_plex_item(rating_key=int(item_id)) + + def query_tmdb(self, path: str, params: dict) -> dict: + with storage.server_scope(self.server_id): + return tmdb.query(path, params) + + def web_urls(self, library_id: str, item_id: str | None = None) -> dict[str, str]: + identifier = quote(self.server_id, safe='') + server_url = 'https://app.plex.tv/desktop/#!/media/' + identifier + '/com.plexapp.plugins.library' + urls = {'server': server_url, 'library': server_url + '?' + urlencode({'source': library_id})} + if item_id is not None: + urls['item'] = ('https://app.plex.tv/desktop/#!/server/' + identifier + '/details?' + + urlencode({'key': '/library/metadata/' + str(item_id)})) + return urls + + def open_poster(self, item_id: str) -> Response | None: + try: + with storage.server_scope(self.server_id): + server = plexapi.setup_plexapi() + if server is None: + return None + item = server.fetchItem(int(item_id)) + thumbnail = getattr(item, 'thumb', None) + if not isinstance(thumbnail, str) or not re.fullmatch(r'/library/metadata/\d+/thumb(?:/\d+)?', thumbnail): + return None + return self._open_stream(server, thumbnail, {}) + except exceptions.NotFound: + return None + except (exceptions.PlexApiException, OSError, ValueError) as error: + log.warning('Unable to load poster for item_id=%s (%s)', item_id, type(error).__name__) + raise MediaServerError('Unable to load poster from Plex.', 502) from error + + def open_theme(self, item_id: str, headers: Mapping[str, str]) -> Response: + try: + with storage.server_scope(self.server_id): + server = plexapi.setup_plexapi() + if server is None: + raise MediaServerError('Connect to Plex before playing themes.', 503) + item = server.fetchItem(int(item_id)) + theme_path = getattr(item, 'theme', None) + if not theme_path: + raise MediaServerError('This item has no theme.', 404) + if not theme_path.startswith('/library/metadata/') or '/theme/' not in theme_path: + raise MediaServerError('Plex returned an unsupported theme path.', 502) + return self._open_stream(server, theme_path, headers) + except exceptions.NotFound as error: + raise MediaServerError('This Plex item is no longer available.', 404) from error + except (exceptions.PlexApiException, OSError, ValueError) as error: + log.warning('Unable to load theme for item_id=%s (%s)', item_id, type(error).__name__) + raise MediaServerError('Unable to load theme audio from Plex.', 502) from error + + @staticmethod + def _open_stream(server, path: str, headers: Mapping[str, str]) -> Response: + """Open a validated native Plex media path using backend-only credentials.""" + return server._session.get( + server.url(path, includeToken=False), headers=server._headers(**headers), + stream=True, allow_redirects=False, timeout=config.CONFIG['Themerr']['INT_PLEXAPI_PLEXAPI_TIMEOUT'], + ) + + +class PlexBackend(MediaServerBackend): + """Manage Plex connections without changing persisted settings or credentials.""" + + name = 'Plex' + + def server(self, server_id: str) -> MediaServer: + return PlexMediaServer(server_id) + + def list_servers(self, enabled_only: bool = False) -> list[dict]: + return servers.list_servers(enabled_only) + + def get_server(self, server_id: str) -> dict | None: + return servers.get_server(server_id) + + def update_server(self, server_id: str, values: dict) -> None: + servers.update_server(server_id, values) + + def remove_server(self, server_id: str) -> None: + try: + servers.remove_server(server_id) + except token_store.TokenStorageError as error: + raise MediaServerError('Could not update the secure credential store.', 500) from error + + def record_refresh(self, server_id: str, error: str | None = None) -> None: + servers.record_refresh(server_id, error) + + def account_connected(self) -> bool: + return bool(auth.get_token()) + + def start_listeners(self) -> None: + plexapi.plex_listener() + + def stop_listeners(self) -> None: + plexapi.stop_plex_listener() + + def web_router(self): + from plex.web import router + return router diff --git a/src/plex/config.py b/src/plex/config.py new file mode 100644 index 00000000..213feef6 --- /dev/null +++ b/src/plex/config.py @@ -0,0 +1,17 @@ +"""Lifecycle callbacks for compatible Plex configuration keys.""" + +from common import logger +from plex import plexapi + +log = logger.get_logger(__name__) + + +def reconnect() -> None: + """Reconnect after the configured legacy Plex URL changes.""" + plexapi.stop_plex_listener() + plexapi.plex_server = None + if plexapi.auth.get_token(): + try: + plexapi.plex_listener() + except Exception: + log.exception('Unable to reconnect to the changed Plex URL') diff --git a/src/plex/constants.py b/src/plex/constants.py new file mode 100644 index 00000000..db453566 --- /dev/null +++ b/src/plex/constants.py @@ -0,0 +1,58 @@ +contributes_to = [ + 'tv.plex.agents.movie', # new movie agent + 'tv.plex.agents.series', # new tv show agent +] + +guid_map = dict( + imdb='imdb', + tmdb='themoviedb', + tvdb='thetvdb', + **{ + 'com.plexapp.agents.imdb': 'imdb', + 'com.plexapp.agents.themoviedb': 'themoviedb', + 'com.plexapp.agents.thetvdb': 'thetvdb', + }, +) + +metadata_type_map = { + 'album': 'Albums', + 'artist': 'Artists', + 'collection': 'Collections', + 'movie': 'Movies', + 'show': 'TV Shows', +} + +plex_section_type_settings_map = { + 'album': 9, + 'artist': 8, + 'movie': 1, + 'photo': 13, + 'show': 2, +} + +media_type_dict = { + 'art': { + 'method': lambda item: item.uploadArt, + 'type': 'art', + 'name': 'art', + 'themerr_data_key': 'art_url', + 'remove_pref': 'BOOL_REMOVE_UNUSED_ART', + 'plex_field': 'art', + }, + 'posters': { + 'method': lambda item: item.uploadPoster, + 'type': 'posters', + 'name': 'poster', + 'themerr_data_key': 'poster_url', + 'remove_pref': 'BOOL_REMOVE_UNUSED_POSTERS', + 'plex_field': 'thumb', + }, + 'themes': { + 'method': lambda item: item.uploadTheme, + 'type': 'themes', + 'name': 'theme', + 'themerr_data_key': 'youtube_theme_url', + 'remove_pref': 'BOOL_REMOVE_UNUSED_THEMES', + 'plex_field': 'theme', + }, +} diff --git a/src/plex/dashboard.py b/src/plex/dashboard.py new file mode 100644 index 00000000..e50c4f36 --- /dev/null +++ b/src/plex/dashboard.py @@ -0,0 +1,214 @@ +# standard imports +from urllib.parse import quote_plus + +# local imports +from common import config +from common import logger +from plex.constants import contributes_to +from themerr.constants import issue_urls +from plex import media as general +from plex.plexapi import PLEX_SETUP_ERROR, get_database_info, get_external_id, setup_plexapi +from themerr import storage +from themerr import themerr_db +from themerr import tmdb + +log = logger.get_logger(name=__name__) + + +def _item_issue_url(item, database_type: str, database_id: str | None, year: int | None) -> str | None: + """Build the ThemerrDB issue link for an item. + + Parameters + ---------- + item : PlexPartialObject + Plex media or collection. + database_type : str + ThemerrDB database type. + database_id : str or None + External database identifier. + year : int or None + Release year. + + Returns + ------- + str or None + Encoded issue URL when supported. + """ + issue_url = issue_urls.get(database_type) + if not issue_url or not database_id: + return None + if item.type == 'movie': + issue_title = f'{getattr(item, "originalTitle", None) or item.title} ({year})' + elif item.type == 'show': + issue_title = f'{item.title} ({year})' + else: + issue_title = item.title + return issue_url.format(quote_plus(issue_title), database_id) + + +def _cache_item(item, errors: dict[str, str] | None = None) -> dict: + """Convert one Plex item to dashboard data. + + Parameters + ---------- + item : PlexPartialObject + Plex media or collection. + errors : dict or None, optional + Current item processing failures. + + Returns + ------- + dict + Dashboard fields for the item. + """ + database_type, database, item_agent, database_id = get_database_info(item=item) + source_database, source_id = get_external_id(item) + original_id = database_id + year = getattr(item, 'year', None) + if item.type == 'movie' and database_id and ( + item_agent == 'com.plexapp.agents.imdb' or database_id.startswith('tt') + ): + database_id = tmdb.get_tmdb_id_from_external_id( + external_id=database_id, database='imdb', item_type='movie') or None + + exists = bool(database_type and database and original_id and themerr_db.item_exists( + database_type=database_type, database=database, id=original_id, + )) + issue_action = 'edit' if exists else 'add' + if item.theme: + theme_status = 'complete' + elif errors and str(item.ratingKey) in errors: + theme_status = 'failed' + elif not original_id: + theme_status = 'unresolved' + elif exists: + theme_status = 'pending' + else: + theme_status = 'missing' + + return { + 'rating_key': str(item.ratingKey), + 'title': item.title, + 'agent': item_agent, + 'database': database, + 'database_type': database_type, + 'database_id': database_id, + 'source_database': source_database, + 'source_id': source_id, + 'issue_action': issue_action, + 'issue_url': _item_issue_url(item, database_type, database_id, year), + 'theme': bool(item.theme), + 'theme_provider': general.get_theme_provider(item=item), + 'theme_status': theme_status, + 'type': item.type, + 'year': year, + } + + +def _section_media_items(section) -> list: + """Select media matched by supported Plex agents in a library section. + + Parameters + ---------- + section : LibrarySection + Plex library section. + + Returns + ------- + list + Media with a supported agent. + """ + if section.agent in contributes_to: + return section.all() + if section.type not in ('movie', 'show'): + return [] + guid_prefix = f'plex://{section.type}/' + return [item for item in section.all() if (getattr(item, 'guid', None) or '').startswith(guid_prefix)] + + +def _cache_section(section, errors: dict[str, str] | None = None) -> dict: + """Collect dashboard counts and item details for a Plex section. + + Parameters + ---------- + section : LibrarySection + Plex library section. + errors : dict or None, optional + Current item processing failures. + + Returns + ------- + dict + Dashboard data for the section. + """ + media_items = _section_media_items(section) + if section.agent in contributes_to: + media_items_with_themes = section.all(theme__exists=True) + else: + media_items_with_themes = [item for item in media_items if item.theme] + collections_enabled = ( + config.CONFIG['Themerr']['BOOL_PLEX_COLLECTION_SUPPORT'] and section.agent in contributes_to + ) + collections = section.collections() if collections_enabled else [] + collections_with_themes = section.collections(theme__exists=True) if collections_enabled else [] + all_items = media_items + collections + + return { + 'key': section.key, + 'title': section.title, + 'agent': section.agent, + 'items': [_cache_item(item, errors=errors) for item in all_items], + 'media_count': len(media_items), + 'media_percent_complete': int(len(media_items_with_themes) / len(media_items) * 100) + if media_items_with_themes else 0, + 'collection_count': len(collections), + 'collection_percent_complete': int(len(collections_with_themes) / len(collections) * 100) + if collections_with_themes else 0, + 'collections_enabled': collections_enabled, + 'total_count': len(all_items), + 'type': section.type, + } + + +def _cache_server() -> bool: + """ + Cache data for use in the Web UI dashboard. + + Because there are many http requests that must be made to gather the data for the dashboard, it can be + time-consuming to populate; therefore, this is performed within this caching function, which runs on a schedule. + This function atomically publishes a snapshot in SQLite. + + Returns + ------- + bool + Whether Plex was available and a complete snapshot was published. + """ + revision = storage.dashboard_revision() + + # get all Plex items from supported metadata agents + plex_server = setup_plexapi() + if not plex_server: + log.error(PLEX_SETUP_ERROR) + return False + + plex_library = plex_server.library + + themerr_db.update_cache() + + sections = plex_library.sections() + + items = {} + errors = storage.get_errors() + + for section in sections: + if section.agent not in contributes_to and getattr(section, 'type', None) not in ('movie', 'show'): + continue + log.info('Caching dashboard library %r (ID %s)', section.title, section.key) + section_data = _cache_section(section, errors=errors) + log.info('Cached %d items from library %r (ID %s)', + section_data['total_count'], section.title, section.key) + if section.agent in contributes_to or section_data['total_count']: + items[section.key] = section_data + + storage.replace_dashboard(items, since_revision=revision) + return True diff --git a/src/themerr/general.py b/src/plex/media.py similarity index 99% rename from src/themerr/general.py rename to src/plex/media.py index d0a86573..1860711d 100644 --- a/src/themerr/general.py +++ b/src/plex/media.py @@ -10,7 +10,7 @@ # local imports from common import config from common import logger -from themerr.constants import metadata_type_map +from plex.constants import metadata_type_map from themerr import storage from plex import servers diff --git a/src/plex/plexapi.py b/src/plex/plexapi.py index 2cda5489..ad1f1b5e 100644 --- a/src/plex/plexapi.py +++ b/src/plex/plexapi.py @@ -1,6 +1,5 @@ # standard imports import hashlib -import queue import os import time import threading @@ -20,8 +19,9 @@ from common import helpers from common import logger from plex import auth, servers -from themerr.constants import contributes_to, guid_map, media_type_dict -from themerr import general +from media_servers import processing +from plex.constants import contributes_to, guid_map, media_type_dict +from plex import media as general from themerr import storage from themerr import theme_errors from themerr import themerr_db @@ -40,20 +40,6 @@ alert_listener = None _alert_listeners = {} _listener_lock = threading.RLock() -_active_items = set() - - -class _WorkQueue(queue.Queue): - """Track active work before releasing the queue lock to a worker.""" - - def _get(self): - work = super()._get() - server_id, rating_key = work if isinstance(work, tuple) else ('default', work) - _active_items.add((server_id, int(rating_key))) - return work - - -q = _WorkQueue() _library_names: dict[tuple[str, str], str] = {} # disable auto-reload, because Themerr doesn't rely on it, so it will only slow down the app @@ -977,58 +963,6 @@ def get_plex_item(rating_key: int) -> Optional[PlexPartialObject]: return item -def process_queue() -> None: - """ - Add items to the queue. - - This is an endless loop to add items to the queue. - - Examples - -------- - >>> process_queue() - ... - """ - while True: - work = q.get() - server_id, rating_key = work if isinstance(work, tuple) else ('default', work) - try: - with storage.server_scope(server_id): - if server_id == 'default' or (servers.get_server(server_id) or {}).get('enabled'): - update_plex_item(rating_key=rating_key) - except Exception as e: - log.exception(f'Unexpected error processing rating key: {rating_key}, error: {e}') - finally: - with q.mutex: - _active_items.discard((server_id, int(rating_key))) - q.task_done() - - -def start_queue_threads() -> None: - """ - Start queue threads. - - Start the queue threads based on the number of threads set in the preferences. - - Examples - -------- - >>> start_queue_threads() - ... - """ - # create multiple threads for processing themes faster - # minimum value of 1 - for t in range(max(1, int(config.CONFIG['Themerr']['INT_PLEXAPI_UPLOAD_THREADS']))): - try: - # for each thread, start it - t = threading.Thread(target=process_queue) - # when we set daemon to true, that thread will end when the main thread ends - t.daemon = True - # start the daemon thread - t.start() - except RuntimeError as e: - log.error(f'RuntimeError encountered: {e}') - break - - def plex_listener() -> None: """Start one independently scoped event listener per enabled server.""" with _listener_lock: @@ -1085,30 +1019,6 @@ def stop_plex_listener(server_id: str | None = None) -> None: alert_listener = None -def enqueue(rating_key: int | str) -> bool: - """Queue a server-scoped item once, including while another worker processes it. - - Parameters - ---------- - rating_key : int or str - Plex item rating key. - - Returns - ------- - bool - Whether new work was queued. - """ - key = (storage.current_server_id(), int(rating_key)) - work = int(rating_key) if key[0] == 'default' else key - with q.mutex: - if work in q.queue or key in _active_items: - return False - q._put(work) - q.unfinished_tasks += 1 - q.not_empty.notify() - return True - - def plex_listener_handler(data: dict) -> None: """ Process events from ``plex_listener()``. @@ -1142,7 +1052,7 @@ def plex_listener_handler(data: dict) -> None: continue rating_key = int(entry['itemID']) - enqueue(rating_key) + processing.enqueue(str(rating_key)) def _items_for_section(section) -> list: @@ -1167,20 +1077,20 @@ def _items_for_section(section) -> list: return [] -def _scheduled_update_server() -> None: +def scan_items(enqueue: Callable[[str], bool]) -> None: """ Update all items in the Plex Server. This is used to update all items in the Plex Server. It is called from a scheduled task. - Examples - -------- - >>> scheduled_update() + Parameters + ---------- + enqueue : callable + Queue an opaque item ID within the current server's storage scope. - See Also + Examples -------- - scheduled_tasks.setup_scheduling : The method where the scheduled task is configurerd. - scheduled_tasks.schedule_loop : The method that runs the pending scheduled tasks. + >>> scan_items(enqueue=...) """ plex = setup_plexapi() if not plex: @@ -1219,31 +1129,14 @@ def _scheduled_update_server() -> None: section_queued = 0 for item in _items_for_section(section): - if enqueue(item.ratingKey): + if enqueue(str(item.ratingKey)): section_queued += 1 queued += section_queued log.info('Queued %d items from library %r (ID %s)', section_queued, section.title, section.key) - log.info('Theme scan finished queuing %d items; %d remain in the worker queue', queued, q.qsize()) + log.info('Theme scan finished queuing %d items', queued) def _ignored_libraries() -> str: """Read ignored library IDs for the current server.""" record = servers.get_server(storage.current_server_id()) return record['ignored_libraries'] if record else config.CONFIG['Themerr']['IGNORED_LIBRARY_IDS'] - - -def scheduled_update() -> None: - """Scan every enabled server; isolate an unavailable server from the others.""" - if not config.CONFIG['Themerr']['BOOL_THEMERR_ENABLED']: - return - registered = servers.list_servers(enabled_only=True) - if not registered: - return - for record in registered: - try: - with storage.server_scope(record['id']): - log.info('Scanning server %r (%s)', record['name'], record['id']) - _scheduled_update_server() - except Exception: - log.exception('Theme scan failed for server %s', record['id']) - servers.record_refresh(record['id'], 'Theme scan could not reach Plex. Check its address and access.') diff --git a/src/plex/tmdb.py b/src/plex/tmdb.py new file mode 100644 index 00000000..901cdc7c --- /dev/null +++ b/src/plex/tmdb.py @@ -0,0 +1,77 @@ +"""Credentialed, server-scoped access to Plex's TMDB proxy.""" + +from threading import Lock +from time import monotonic +from urllib.parse import quote, urlencode + +import requests + +from common import config, logger +from plex import auth, servers, token_store +from themerr import storage + +PROXY_TIMEOUT = 10 +PROXY_CACHE_SECONDS = 86400 +_proxy_cache: dict[tuple[str, ...], tuple[float, dict]] = {} +_proxy_lock = Lock() +log = logger.get_logger(__name__) + + +def query(path: str, params: dict) -> dict: + """Query the Plex server's TMDB service without persisting its token. + + Parameters + ---------- + path : str + TMDB service path. + params : dict + TMDB service query parameters. + + Returns + ------- + dict + JSON response, or an empty dictionary if the proxy is unavailable. + """ + if path == 'search/collection' and isinstance(params.get('query'), str): + params = {**params, 'query': params['query'].replace(' ', '-')} + try: + server_id = storage.current_server_id() + record = servers.get_server(server_id) if server_id != 'default' else None + if server_id != 'default' and (record is None or not record['enabled']): + return {} + base_url = (record['url'] if record else config.CONFIG['Plex']['PLEX_URL']).rstrip('/') + except (KeyError, TypeError, AttributeError): + return {} + if not base_url: + return {} + uri = f'/{path}?{urlencode(params, quote_via=quote)}' + key = (server_id, base_url, uri) + with _proxy_lock: + cached = _proxy_cache.get(key) + if cached and cached[0] > monotonic(): + return cached[1] + token = token_store.get_token(servers.credential_id(server_id)) if record else auth.get_token() + if not token: + return {} + data = _request_json(base_url, token, uri) + if data is None: + return {} + with _proxy_lock: + if len(_proxy_cache) >= 2048: + _proxy_cache.clear() + _proxy_cache[key] = (monotonic() + PROXY_CACHE_SECONDS, data) + return data + + +def _request_json(base_url: str, token: str, uri: str) -> dict | None: + """Return valid proxy JSON, distinguishing an empty result from a failed request.""" + try: + response = requests.get(f'{base_url}/services/tmdb', params={'uri': uri}, + headers={'X-Plex-Token': token, 'Accept': 'application/json'}, + timeout=PROXY_TIMEOUT) + response.raise_for_status() + data = response.json() + except (requests.RequestException, ValueError) as exc: + log.debug('Plex TMDB lookup failed: %s', exc) + return None + return data if isinstance(data, dict) else None diff --git a/src/plex/web.py b/src/plex/web.py new file mode 100644 index 00000000..ce909109 --- /dev/null +++ b/src/plex/web.py @@ -0,0 +1,210 @@ +"""Plex sign-in and discovery routes protected by the shared browser middleware.""" + +import time + +from fastapi import APIRouter, Depends, Request +from starlette.responses import JSONResponse, Response +from plexapi.exceptions import Unauthorized +from requests.exceptions import ConnectionError, RequestException, SSLError, Timeout +import requests + +from common import logger, server_ui +from common.server_ui import _failure, _payload +from common.validation import ValidationError +from plex import auth as plex_auth +from plex import plexapi, servers, token_store + +router = APIRouter() +log = logger.get_logger(__name__) +PLEX_LOGIN_LIFETIME = 600 + + +@router.api_route('/api/plex/auth', methods=['GET', 'HEAD'], name='plex_auth_status', response_model=None) +def plex_auth_status() -> Response: + """Report whether this installation has a token from Plex sign-in. + + The response contains only a connection flag and never includes the token. + + Returns + ------- + Response + Authentication status without exposing the token. + + Examples + -------- + >>> plex_auth_status() + + """ + return JSONResponse({'connected': bool(plex_auth.get_token())}) + + +@router.api_route('/api/plex/auth/start', methods=['POST'], name='plex_auth_start', response_model=None) +def plex_auth_start(request: Request) -> Response: + """Start a Plex browser sign-in for this web session. + + Store the PIN in the browser session so it can be checked later. + + Parameters + ---------- + request : Request + Incoming browser or API request. + + Returns + ------- + Response + Plex authorization URL or a sanitized error. + + Examples + -------- + >>> plex_auth_start(request) + + """ + try: + login = plex_auth.start_login() + except (OSError, KeyError, TypeError, ValueError): + log.exception('Unable to start Plex sign-in') + return JSONResponse({'message': 'Unable to start Plex sign-in. Please try again.'}, status_code=502) + + request.session['plex_login'] = {'pin_id': login['pin_id'], 'code': login['code'], 'started': time.time()} + return JSONResponse({'auth_url': login['auth_url']}) + + +@router.api_route('/api/plex/auth/check', methods=['POST'], name='plex_auth_check', response_model=None) +def plex_auth_check(request: Request) -> Response: + """Finish a Plex sign-in once its PIN has been claimed. + + Keep the previous connection until the new token reaches the selected server. + + Parameters + ---------- + request : Request + Incoming browser or API request. + + Returns + ------- + Response + Pending, connected, expired, or error status. + + Examples + -------- + >>> plex_auth_check(request) + + """ + login = request.session.get('plex_login') + if not login: + return JSONResponse({'message': 'Start Plex sign-in first.'}, status_code=400) + if time.time() - login['started'] > PLEX_LOGIN_LIFETIME: + request.session.pop('plex_login', None) + return JSONResponse({'message': 'Plex sign-in expired. Please try again.'}, status_code=410) + + try: + token = plex_auth.check_login(pin_id=login['pin_id'], code=login['code']) + except requests.HTTPError as error: + if error.response is not None and error.response.status_code in (404, 410): + request.session.pop('plex_login', None) + return JSONResponse({'message': 'Plex sign-in expired. Please try again.'}, status_code=410) + log.warning('Unable to check Plex sign-in: %s', error) + return JSONResponse({'message': 'Unable to check Plex sign-in. Please try again.'}, status_code=502) + except (requests.RequestException, KeyError, TypeError, ValueError): + log.exception('Unable to check Plex sign-in') + return JSONResponse({'message': 'Unable to check Plex sign-in. Please try again.'}, status_code=502) + + if not token: + return JSONResponse({'connected': False}, status_code=202) + + try: + plex_auth.set_token(token) + except OSError: + log.exception('Unable to save Plex sign-in') + return JSONResponse({'message': 'Unable to save Plex sign-in.'}, status_code=500) + request.session.pop('plex_login', None) + return JSONResponse({'connected': True}) + + +@router.api_route('/api/plex/auth/disconnect', methods=['POST'], name='plex_auth_disconnect', response_model=None) +def plex_auth_disconnect(request: Request) -> Response: + """Remove the local Plex connection. + + Clear the saved token and stop the active event listener. + + Parameters + ---------- + request : Request + Incoming browser or API request. + + Returns + ------- + Response + Disconnected status. + + Examples + -------- + >>> plex_auth_disconnect(request) + + """ + try: + servers.disconnect_account() + except OSError: + log.exception('Unable to disconnect Plex') + return JSONResponse({'message': 'Unable to disconnect Plex.'}, status_code=500) + + plexapi.stop_plex_listener() + servers.clear_connections() + plexapi.plex_server = None + request.session.pop('plex_login', None) + return JSONResponse({'connected': False}) + + +@router.api_route('/api/servers/discover', methods=['POST'], name='server_ui.discover', response_model=None) +def discover(payload=Depends(_payload)): + """List account or LAN resources without disclosing access tokens. + + Returns + ------- + Response + Safe discovery results or a connection error. + """ + source = payload.get('source') + if source not in ('account', 'local'): + return JSONResponse({'message': 'Choose account or local discovery.'}, status_code=400) + try: + resources = servers.discover_account() if source == 'account' else servers.discover_local() + except Exception as exc: + return _failure(exc, 'Discovery failed. Check the Plex connection, or enter an address manually.') + return JSONResponse({'servers': resources}) + + +@router.api_route('/api/servers', methods=['POST'], name='server_ui.add_server', response_model=None) +def add_server(payload=Depends(_payload)): + """Connect to a selected or manually addressed server. + + Returns + ------- + Response + Saved public settings or a sanitized failure. + """ + try: + record = servers.add_server(payload.get('url', ''), payload.get('resource_id')) + except token_store.TokenStorageError as exc: + return _failure(exc, 'Unable to save the Plex token. Check the configured credential store.', 500) + except SSLError as exc: + return _failure(exc, 'The secure connection to Plex failed. Use its advertised HTTPS address and check ' + 'the server certificate.') + except Timeout as exc: + return _failure(exc, 'The Plex connection timed out. Check that the server is running and this address is ' + 'reachable from the machine running Themerr. Try another advertised or manual address.') + except ConnectionError as exc: + return _failure(exc, 'Could not connect to this Plex address. Check its hostname, port, and network access ' + 'from the machine running Themerr, or try another address.') + except Unauthorized as exc: + return _failure(exc, 'Plex denied access to this server. Check the linked account has permission, or ' + 'reconnect your Plex account.') + except RequestException as exc: + return _failure(exc, 'Plex returned an invalid response. Check the address or try another connection.') + except ValidationError as exc: + return _failure(exc, exc.reason.value, 400) + except Exception as exc: + return _failure(exc, 'Could not connect to this Plex server. Check the address and account access.') + plexapi.plex_listener() + server_ui._refresh() + return JSONResponse({'server': record}, status_code=201) diff --git a/src/themerr/cache.py b/src/themerr/cache.py index 58549fad..a53fb7cc 100644 --- a/src/themerr/cache.py +++ b/src/themerr/cache.py @@ -1,222 +1,16 @@ -# standard imports -from urllib.parse import quote_plus +"""Refresh media-server dashboards without discarding offline snapshots.""" -# local imports -from common import config from common import logger -from themerr.constants import contributes_to, issue_urls -from themerr import general -from plex import servers -from plex.plexapi import PLEX_SETUP_ERROR, get_database_info, get_external_id, setup_plexapi +from media_servers import get_backend from themerr import storage -from themerr import themerr_db -from themerr import tmdb -log = logger.get_logger(name=__name__) - - -def _item_issue_url(item, database_type: str, database_id: str | None, year: int | None) -> str | None: - """Build the ThemerrDB issue link for an item. - - Parameters - ---------- - item : PlexPartialObject - Plex media or collection. - database_type : str - ThemerrDB database type. - database_id : str or None - External database identifier. - year : int or None - Release year. - - Returns - ------- - str or None - Encoded issue URL when supported. - """ - issue_url = issue_urls.get(database_type) - if not issue_url or not database_id: - return None - if item.type == 'movie': - issue_title = f'{getattr(item, "originalTitle", None) or item.title} ({year})' - elif item.type == 'show': - issue_title = f'{item.title} ({year})' - else: - issue_title = item.title - return issue_url.format(quote_plus(issue_title), database_id) - - -def _cache_item(item, errors: dict[str, str] | None = None) -> dict: - """Convert one Plex item to dashboard data. - - Parameters - ---------- - item : PlexPartialObject - Plex media or collection. - errors : dict or None, optional - Current item processing failures. - - Returns - ------- - dict - Dashboard fields for the item. - """ - database_type, database, item_agent, database_id = get_database_info(item=item) - source_database, source_id = get_external_id(item) - original_id = database_id - year = getattr(item, 'year', None) - if item.type == 'movie' and database_id and ( - item_agent == 'com.plexapp.agents.imdb' or database_id.startswith('tt') - ): - database_id = tmdb.get_tmdb_id_from_external_id( - external_id=database_id, database='imdb', item_type='movie') or None - - exists = bool(database_type and database and original_id and themerr_db.item_exists( - database_type=database_type, database=database, id=original_id, - )) - issue_action = 'edit' if exists else 'add' - if item.theme: - theme_status = 'complete' - elif errors and str(item.ratingKey) in errors: - theme_status = 'failed' - elif not original_id: - theme_status = 'unresolved' - elif exists: - theme_status = 'pending' - else: - theme_status = 'missing' - - return { - 'rating_key': str(item.ratingKey), - 'title': item.title, - 'agent': item_agent, - 'database': database, - 'database_type': database_type, - 'database_id': database_id, - 'source_database': source_database, - 'source_id': source_id, - 'issue_action': issue_action, - 'issue_url': _item_issue_url(item, database_type, database_id, year), - 'theme': bool(item.theme), - 'theme_provider': general.get_theme_provider(item=item), - 'theme_status': theme_status, - 'type': item.type, - 'year': year, - } - - -def _section_media_items(section) -> list: - """Select media matched by supported Plex agents in a library section. - - Parameters - ---------- - section : LibrarySection - Plex library section. - - Returns - ------- - list - Media with a supported agent. - """ - if section.agent in contributes_to: - return section.all() - if section.type not in ('movie', 'show'): - return [] - guid_prefix = f'plex://{section.type}/' - return [item for item in section.all() if (getattr(item, 'guid', None) or '').startswith(guid_prefix)] - - -def _cache_section(section, errors: dict[str, str] | None = None) -> dict: - """Collect dashboard counts and item details for a Plex section. - - Parameters - ---------- - section : LibrarySection - Plex library section. - errors : dict or None, optional - Current item processing failures. - - Returns - ------- - dict - Dashboard data for the section. - """ - media_items = _section_media_items(section) - if section.agent in contributes_to: - media_items_with_themes = section.all(theme__exists=True) - else: - media_items_with_themes = [item for item in media_items if item.theme] - collections_enabled = ( - config.CONFIG['Themerr']['BOOL_PLEX_COLLECTION_SUPPORT'] and section.agent in contributes_to - ) - collections = section.collections() if collections_enabled else [] - collections_with_themes = section.collections(theme__exists=True) if collections_enabled else [] - all_items = media_items + collections - - return { - 'key': section.key, - 'title': section.title, - 'agent': section.agent, - 'items': [_cache_item(item, errors=errors) for item in all_items], - 'media_count': len(media_items), - 'media_percent_complete': int(len(media_items_with_themes) / len(media_items) * 100) - if media_items_with_themes else 0, - 'collection_count': len(collections), - 'collection_percent_complete': int(len(collections_with_themes) / len(collections) * 100) - if collections_with_themes else 0, - 'collections_enabled': collections_enabled, - 'total_count': len(all_items), - 'type': section.type, - } - - -def _cache_server() -> bool: - """ - Cache data for use in the Web UI dashboard. - - Because there are many http requests that must be made to gather the data for the dashboard, it can be - time-consuming to populate; therefore, this is performed within this caching function, which runs on a schedule. - This function atomically publishes a snapshot in SQLite. - - Returns - ------- - bool - Whether Plex was available and a complete snapshot was published. - """ - revision = storage.dashboard_revision() - - # get all Plex items from supported metadata agents - plex_server = setup_plexapi() - if not plex_server: - log.error(PLEX_SETUP_ERROR) - return False - - plex_library = plex_server.library - - themerr_db.update_cache() - - sections = plex_library.sections() - - items = {} - errors = storage.get_errors() - - for section in sections: - if section.agent not in contributes_to and getattr(section, 'type', None) not in ('movie', 'show'): - continue - log.info('Caching dashboard library %r (ID %s)', section.title, section.key) - section_data = _cache_section(section, errors=errors) - log.info('Cached %d items from library %r (ID %s)', - section_data['total_count'], section.title, section.key) - if section.agent in contributes_to or section_data['total_count']: - items[section.key] = section_data - - storage.replace_dashboard(items, since_revision=revision) - return True +log = logger.get_logger(__name__) def cache_data() -> None: """Refresh enabled server dashboards independently without discarding offline snapshots.""" - registered = servers.list_servers(enabled_only=True) + backend = get_backend() + registered = backend.list_servers(enabled_only=True) if not registered: return successful = True @@ -224,15 +18,16 @@ def cache_data() -> None: try: with storage.server_scope(record['id']): log.info('Refreshing dashboard for server %r (%s)', record['name'], record['id']) - if not _cache_server(): + if not backend.server(record['id']).cache_dashboard(): successful = False - servers.record_refresh(record['id'], 'Dashboard refresh could not reach Plex.') + backend.record_refresh(record['id'], f'Dashboard refresh could not reach {backend.name}.') continue - servers.record_refresh(record['id']) + backend.record_refresh(record['id']) except Exception: successful = False log.exception('Dashboard refresh failed for server %s', record['id']) - servers.record_refresh(record['id'], 'Dashboard refresh failed. Check the Plex address and access.') + backend.record_refresh(record['id'], + f'Dashboard refresh failed. Check the {backend.name} address and access.') if successful: from common.notifications import refresh_completed refresh_completed() diff --git a/src/themerr/constants.py b/src/themerr/constants.py index 12e14547..3beadc7a 100644 --- a/src/themerr/constants.py +++ b/src/themerr/constants.py @@ -1,35 +1,3 @@ -contributes_to = [ - 'tv.plex.agents.movie', # new movie agent - 'tv.plex.agents.series', # new tv show agent -] - -guid_map = dict( - imdb='imdb', - tmdb='themoviedb', - tvdb='thetvdb', - **{ - 'com.plexapp.agents.imdb': 'imdb', - 'com.plexapp.agents.themoviedb': 'themoviedb', - 'com.plexapp.agents.thetvdb': 'thetvdb', - }, -) - -metadata_type_map = { - 'album': 'Albums', - 'artist': 'Artists', - 'collection': 'Collections', - 'movie': 'Movies', - 'show': 'TV Shows', -} - -plex_section_type_settings_map = { - 'album': 9, - 'artist': 8, - 'movie': 1, - 'photo': 13, - 'show': 2, -} - # issue url constants base_url = 'https://github.com/LizardByte/ThemerrDB/issues/new?assignees=' issue_label = 'request-theme' @@ -64,30 +32,3 @@ f'&title={title_prefix['tv_shows']}{'{}'}' f'&{url_name}={url_prefix['tv_shows']}{'{}'}', } - -media_type_dict = { - 'art': { - 'method': lambda item: item.uploadArt, - 'type': 'art', - 'name': 'art', - 'themerr_data_key': 'art_url', - 'remove_pref': 'BOOL_REMOVE_UNUSED_ART', - 'plex_field': 'art', - }, - 'posters': { - 'method': lambda item: item.uploadPoster, - 'type': 'posters', - 'name': 'poster', - 'themerr_data_key': 'poster_url', - 'remove_pref': 'BOOL_REMOVE_UNUSED_POSTERS', - 'plex_field': 'thumb', - }, - 'themes': { - 'method': lambda item: item.uploadTheme, - 'type': 'themes', - 'name': 'theme', - 'themerr_data_key': 'youtube_theme_url', - 'remove_pref': 'BOOL_REMOVE_UNUSED_THEMES', - 'plex_field': 'theme', - }, -} diff --git a/src/themerr/scheduled_tasks.py b/src/themerr/scheduled_tasks.py index 762bb96e..1e0971ca 100644 --- a/src/themerr/scheduled_tasks.py +++ b/src/themerr/scheduled_tasks.py @@ -13,7 +13,7 @@ from common import config from common import logger from common.notifications import check_for_releases -from plex.plexapi import scheduled_update +from media_servers.processing import scheduled_update from themerr.cache import cache_data log = logger.get_logger(name=__name__) diff --git a/src/themerr/tmdb.py b/src/themerr/tmdb.py index c8a5f902..e3a4c455 100644 --- a/src/themerr/tmdb.py +++ b/src/themerr/tmdb.py @@ -1,80 +1,19 @@ -"""Resolve TMDB IDs from ThemerrDB, Plex's TMDB proxy, or TMDB.""" +"""Resolve TMDB IDs from ThemerrDB, the media server, or TMDB.""" # standard imports import os -from threading import Lock -from time import monotonic -from urllib.parse import quote, urlencode - -# lib imports -import requests +from urllib.parse import quote # local imports -from common import config, helpers, logger -from plex import auth, servers, token_store +from common import helpers, logger +from media_servers import get_backend from themerr import storage, themerr_db TMDB_API_URL = 'https://api.themoviedb.org/3' -PROXY_TIMEOUT = 10 -PROXY_CACHE_SECONDS = 86400 -_proxy_cache: dict[tuple[str, ...], tuple[float, dict]] = {} -_proxy_lock = Lock() log = logger.get_logger(__name__) -def _plex_get(path: str, params: dict) -> dict: - """Query the Plex server's TMDB service without persisting its token. - - Parameters - ---------- - path : str - TMDB service path. - params : dict - TMDB service query parameters. - - Returns - ------- - dict - JSON response, or an empty dictionary if the proxy is unavailable. - """ - try: - server_id = storage.current_server_id() - record = servers.get_server(server_id) if server_id != 'default' else None - if server_id != 'default' and (record is None or not record['enabled']): - return {} - base_url = (record['url'] if record else config.CONFIG['Plex']['PLEX_URL']).rstrip('/') - except (KeyError, TypeError, AttributeError): - return {} - if not base_url: - return {} - uri = f'/{path}?{urlencode(params, quote_via=quote)}' - key = (server_id, base_url, uri) - with _proxy_lock: - cached = _proxy_cache.get(key) - if cached and cached[0] > monotonic(): - return cached[1] - token = token_store.get_token(servers.credential_id(server_id)) if record else auth.get_token() - if not token: - return {} - try: - response = requests.get(f'{base_url}/services/tmdb', params={'uri': uri}, - headers={'X-Plex-Token': token, 'Accept': 'application/json'}, - timeout=PROXY_TIMEOUT) - response.raise_for_status() - data = response.json() - except (requests.RequestException, ValueError) as exc: - log.debug('Plex TMDB lookup failed: %s', exc) - return {} - if not isinstance(data, dict): - return {} - with _proxy_lock: - if len(_proxy_cache) >= 2048: - _proxy_cache.clear() - _proxy_cache[key] = (monotonic() + PROXY_CACHE_SECONDS, data) - return data - - def _api_get(path: str, params: dict) -> dict: """Query TMDB when an optional API read token is configured. @@ -105,19 +44,19 @@ def get_tmdb_id_from_external_id(external_id: int | str, database: str, item_typ title: str | None = None) -> int | None: """Resolve an external movie or show identifier to a TMDB ID. - ThemerrDB publishes IMDb IDs for movies and titles for TV shows. The Plex - proxy or an optional TMDB API token can resolve other items. + ThemerrDB publishes IMDb IDs for movies and titles for TV shows. A server + metadata service or an optional TMDB API token can resolve other items. Parameters ---------- external_id : int or str - IMDb or TVDB identifier from Plex. + IMDb or TVDB identifier from the media server. database : str ``imdb`` or ``tvdb``. item_type : str ``movie`` or ``tv``. title : str or None - Plex show title, used when ThemerrDB has no external ID mapping. + Show title, used when ThemerrDB has no external ID mapping. Returns ------- @@ -138,7 +77,8 @@ def get_tmdb_id_from_external_id(external_id: int | str, database: str, item_typ if item_type == 'movie' and database == 'tvdb': return None # TMDB's find endpoint does not support TVDB IDs for movies. params = {'external_source': f'{database}_id'} - data = _plex_get(f'find/{quote(str(external_id), safe="")}', params) + server = get_backend().server(storage.current_server_id()) + data = server.query_tmdb(f'find/{quote(str(external_id), safe="")}', params) try: return int(data[f'{item_type}_results'][0]['id']) except (IndexError, KeyError, TypeError, ValueError): @@ -151,14 +91,14 @@ def get_tmdb_id_from_external_id(external_id: int | str, database: str, item_typ def get_tmdb_id_from_collection(search_query: str, language: str | None = None) -> int | None: - """Find a collection by title in ThemerrDB, Plex, or TMDB. + """Find a collection by title in ThemerrDB, the media server, or TMDB. Parameters ---------- search_query : str - Plex collection title, with or without the ``Collection`` suffix. + Collection title, with or without the ``Collection`` suffix. language : str or None - Plex library language for the TMDB API fallback. + Library language for the TMDB API fallback. Returns ------- @@ -173,9 +113,7 @@ def get_tmdb_id_from_collection(search_query: str, language: str | None = None) if language: params['language'] = language search_key = themerr_db._title_key(search_query) - # Plex's proxy rejects spaces in collection searches; hyphens work. - proxy_params = {**params, 'query': search_query.replace(' ', '-')} - data = _plex_get('search/collection', proxy_params) + data = get_backend().server(storage.current_server_id()).query_tmdb('search/collection', params) for result in data.get('results', []): try: result_key = themerr_db._title_key(result['name']) diff --git a/src/themerr_plex.py b/src/themerr_plex.py index 983e90e3..28f707ab 100644 --- a/src/themerr_plex.py +++ b/src/themerr_plex.py @@ -229,7 +229,7 @@ def main(): time.sleep(3) # show splash screen for a min of 3 seconds splash.close() # close the splash screen from common import webapp # import at use due to translations - from plex import plexapi # import at use due to config + from media_servers import get_backend, processing # import after config from themerr import scheduled_tasks scheme = 'https' if config.CONFIG['Network']['SSL'] else 'http' @@ -244,8 +244,8 @@ def main(): helpers.open_url_in_browser(url=browser_url) # start plex listener - plexapi.start_queue_threads() - plexapi.plex_listener() + processing.start_queue_threads() + get_backend().start_listeners() # scheduled tasks scheduled_tasks.setup_scheduling() diff --git a/tests/functional/test_server_ui.py b/tests/functional/test_server_ui.py index d7a085dc..815ebcbe 100644 --- a/tests/functional/test_server_ui.py +++ b/tests/functional/test_server_ui.py @@ -18,6 +18,7 @@ from common import admin, server_ui, webapp from common.validation import ValidationError, ValidationMessage from plex import auth, plexapi, servers, token_store +from media_servers import processing from themerr import storage @@ -339,7 +340,7 @@ def test_refresh_dispatch_and_task_status(client, configured, monkeypatch): response = client.post('/api/tasks/refresh', json={'scan': True}, headers=headers(client)) assert response.status_code == 202 assert response.json()['job_id'] == 'dashboard-job' - run.assert_called_once_with(target=plexapi.scheduled_update, task_name='Theme scan and queue') + run.assert_called_once_with(target=processing.scheduled_update, task_name='Theme scan and queue') server_ui._refresh.assert_called_once() configured['Themerr']['BOOL_THEMERR_ENABLED'] = False assert client.post('/api/tasks/refresh', json={'scan': True}, headers=headers(client)).status_code == 400 diff --git a/tests/functional/test_theme_upload.py b/tests/functional/test_theme_upload.py index 5a076090..74de9568 100644 --- a/tests/functional/test_theme_upload.py +++ b/tests/functional/test_theme_upload.py @@ -14,7 +14,8 @@ # local imports from plex import plexapi -from themerr import general, storage, theme_errors +from plex import media as general +from themerr import storage, theme_errors from youtube import youtube_dl diff --git a/tests/functional/test_webapp.py b/tests/functional/test_webapp.py index 1447524a..4aec3973 100644 --- a/tests/functional/test_webapp.py +++ b/tests/functional/test_webapp.py @@ -16,7 +16,7 @@ from tests.http_helpers import get_session, set_session from common import admin, webapp from plex import servers -from plex import auth, plexapi +from plex import auth, plexapi, web as plex_web from themerr import storage from themerr import theme_errors @@ -563,7 +563,7 @@ def test_plex_sign_in_expired_and_upstream_failure(client, configured, monkeypat monkeypatch.setattr(auth, 'check_login', Mock(side_effect=RuntimeError('not called'))) client.post('/api/plex/auth/start') browser_session = get_session(client) - browser_session['plex_login']['started'] -= webapp.PLEX_LOGIN_LIFETIME + 1 + browser_session['plex_login']['started'] -= plex_web.PLEX_LOGIN_LIFETIME + 1 set_session(client, browser_session) assert client.post('/api/plex/auth/check').status_code == 410 assert client.post('/api/plex/auth/check').status_code == 400 diff --git a/tests/unit/common/test_log_viewer.py b/tests/unit/common/test_log_viewer.py index e7708771..1c4c6358 100644 --- a/tests/unit/common/test_log_viewer.py +++ b/tests/unit/common/test_log_viewer.py @@ -285,7 +285,7 @@ def test_three_channels_route_application_uvicorn_scheduler_and_ytdlp_once(confi try: logger.setup_loggers() logger.setup_loggers() # Reconfiguration must not duplicate or retain old file handlers. - for name in ('common.webapp', 'common.helpers', 'plex.plexapi', 'themerr.general', 'youtube.youtube_dl'): + for name in ('common.webapp', 'common.helpers', 'plex.plexapi', 'plex.media', 'youtube.youtube_dl'): assert logger.get_logger(name) is logging.getLogger('themerr') assert helpers.get_logger(name) is logger.get_logger(name) logger.get_logger(name).info('application %s', name) diff --git a/tests/unit/common/test_notifications.py b/tests/unit/common/test_notifications.py index 2eb97e93..14bfbb69 100644 --- a/tests/unit/common/test_notifications.py +++ b/tests/unit/common/test_notifications.py @@ -10,6 +10,8 @@ from common import definitions, notifications, version from plex import servers from themerr import cache, storage +from media_servers import get_backend +from plex import dashboard @pytest.fixture @@ -197,7 +199,7 @@ def test_coverage_no_increase_or_empty_library_stays_quiet(desktop, total, insta def test_small_coverage_increase_is_not_rounded_to_zero(desktop, monkeypatch): - monkeypatch.setattr(notifications.servers, 'list_servers', lambda: [{'id': 'a', 'enabled': True}]) + monkeypatch.setattr(get_backend(), 'list_servers', lambda: [{'id': 'a', 'enabled': True}]) notifications._save(notifications._COVERAGE_KEY, { 'servers': [['a', True]], 'total': 100000, 'installed': 50000, }) @@ -240,12 +242,12 @@ def refresh(): storage.replace_dashboard(snapshot(8, 6)) return True - monkeypatch.setattr(cache, '_cache_server', refresh) + monkeypatch.setattr(dashboard, '_cache_server', refresh) cache.cache_data() desktop.assert_not_called() assert notifications._load(notifications._COVERAGE_KEY)['installed'] == 4 assert servers.get_server('a')['last_error'] - monkeypatch.setattr(cache, '_cache_server', lambda: True) + monkeypatch.setattr(dashboard, '_cache_server', lambda: True) cache.cache_data() desktop.assert_called_once() assert '20 percentage points' in desktop.call_args.args[1] diff --git a/tests/unit/media_servers/test_cache.py b/tests/unit/media_servers/test_cache.py new file mode 100644 index 00000000..42eca205 --- /dev/null +++ b/tests/unit/media_servers/test_cache.py @@ -0,0 +1,55 @@ +"""Refresh orchestration consumes only the backend contract.""" + +from unittest.mock import Mock + +import pytest + +from common import notifications +from media_servers.base import MediaServer, MediaServerBackend +from themerr import cache, storage + + +@pytest.mark.parametrize('outcome', [False, OSError('Offline'), True]) +def test_failed_refresh_preserves_snapshot_and_suppresses_notification(configured, monkeypatch, outcome): + snapshot = {'1': {'key': 1, 'title': 'Saved library', 'agent': 'saved', 'type': 'movie', + 'media_count': 0, 'media_percent_complete': 0, 'collection_count': 0, + 'collection_percent_complete': 0, 'collections_enabled': False, + 'total_count': 0, 'items': []}} + with storage.server_scope('a'): + storage.replace_dashboard(snapshot) + backend = Mock(spec=MediaServerBackend) + backend.name = 'Plex' + backend.list_servers.return_value = [{'id': 'a', 'name': 'A'}, {'id': 'b', 'name': 'B'}] + first, second = Mock(spec=MediaServer), Mock(spec=MediaServer) + if isinstance(outcome, Exception): + first.cache_dashboard.side_effect = outcome + else: + first.cache_dashboard.return_value = outcome + second.cache_dashboard.return_value = True + backend.server.side_effect = [first, second] + monkeypatch.setattr(cache, 'get_backend', lambda: backend) + published = Mock() + monkeypatch.setattr(notifications, 'refresh_completed', published) + cache.cache_data() + + first.cache_dashboard.assert_called_once() + second.cache_dashboard.assert_called_once() + with storage.server_scope('a'): + assert storage.get_dashboard() == snapshot + assert storage.current_server_id() == 'default' + if outcome is True: + published.assert_called_once() + else: + published.assert_not_called() + assert backend.record_refresh.call_args_list[-1].args == ('b',) + + +def test_empty_registry_does_not_connect_or_notify(configured, monkeypatch): + backend = Mock(spec=MediaServerBackend) + backend.list_servers.return_value = [] + monkeypatch.setattr(cache, 'get_backend', lambda: backend) + published = Mock() + monkeypatch.setattr(notifications, 'refresh_completed', published) + cache.cache_data() + backend.server.assert_not_called() + published.assert_not_called() diff --git a/tests/unit/media_servers/test_processing.py b/tests/unit/media_servers/test_processing.py new file mode 100644 index 00000000..16f26487 --- /dev/null +++ b/tests/unit/media_servers/test_processing.py @@ -0,0 +1,86 @@ +"""Shared workers accept opaque IDs and do not depend on native SDK objects.""" + +from unittest.mock import Mock + +import pytest + +from media_servers import processing +from media_servers.base import MediaServer, MediaServerBackend +from themerr import storage + + +def test_interfaces_require_implementations(): + with pytest.raises(TypeError): + MediaServer('server') + with pytest.raises(TypeError): + MediaServerBackend() + + +def test_workers_scope_opaque_ids_skip_inactive_servers_and_release_failed_work(monkeypatch): + queue = processing._WorkQueue() + monkeypatch.setattr(processing, 'q', queue) + monkeypatch.setattr(processing, '_active_items', set()) + backend = Mock(spec=MediaServerBackend) + backend.get_server.side_effect = lambda server_id: {'enabled': True} if server_id in ('a', 'b') else None + adapter = Mock(spec=MediaServer) + backend.server.return_value = adapter + monkeypatch.setattr(processing, 'get_backend', lambda: backend) + seen = [] + + def update(item_id): + server_id = storage.current_server_id() + seen.append((server_id, item_id)) + assert not processing.enqueue(item_id) + if server_id == 'a': + raise OSError('Temporary upstream failure') + + adapter.update_item.side_effect = update + for server_id in ('a', 'b', 'removed'): + with storage.server_scope(server_id): + assert processing.enqueue('opaque-item-uuid') + assert not processing.enqueue('opaque-item-uuid') + get = queue.get + + def next_work(): + if queue.empty(): + raise KeyboardInterrupt + return get() + + monkeypatch.setattr(queue, 'get', next_work) + with pytest.raises(KeyboardInterrupt): + processing.process_queue() + + assert seen == [('a', 'opaque-item-uuid'), ('b', 'opaque-item-uuid')] + assert queue.unfinished_tasks == 0 + assert not processing._active_items + assert storage.current_server_id() == 'default' + with storage.server_scope('a'): + assert processing.enqueue('opaque-item-uuid') + + +def test_scan_uses_the_contract_and_keeps_other_servers_running(configured, monkeypatch): + backend = Mock(spec=MediaServerBackend) + backend.name = 'Plex' + backend.list_servers.return_value = [{'id': 'a', 'name': 'A'}, {'id': 'b', 'name': 'B'}] + first, second = Mock(spec=MediaServer), Mock(spec=MediaServer) + first.scan.side_effect = OSError('Offline') + backend.server.side_effect = [first, second] + monkeypatch.setattr(processing, 'get_backend', lambda: backend) + + def scan(enqueue): + assert storage.current_server_id() == 'b' + assert enqueue is processing.enqueue + + second.scan.side_effect = scan + processing.scheduled_update() + + backend.list_servers.assert_called_once_with(enabled_only=True) + backend.record_refresh.assert_called_once_with( + 'a', 'Theme scan could not reach Plex. Check its address and access.') + second.scan.assert_called_once() + assert storage.current_server_id() == 'default' + + configured['Themerr']['BOOL_THEMERR_ENABLED'] = False + backend.reset_mock() + processing.scheduled_update() + backend.list_servers.assert_not_called() diff --git a/tests/unit/plex/test_backend.py b/tests/unit/plex/test_backend.py new file mode 100644 index 00000000..499002f4 --- /dev/null +++ b/tests/unit/plex/test_backend.py @@ -0,0 +1,57 @@ +"""Plex adapters retain identity and translate only at native boundaries.""" + +from types import SimpleNamespace +from unittest.mock import Mock + +import pytest + +from media_servers import get_backend +from media_servers.base import MediaServerBackend +from plex import dashboard, plexapi, tmdb +from plex.backend import PlexBackend, PlexMediaServer +from themerr import storage + + +def test_selection_contains_only_the_plex_implementation(): + assert isinstance(get_backend(), MediaServerBackend) + assert isinstance(get_backend(), PlexBackend) + assert get_backend() is get_backend() + + +def test_adapter_scopes_library_cache_scan_update_and_metadata_operations(monkeypatch): + adapter = PlexMediaServer('saved') + seen = [] + + def scoped(result): + seen.append(storage.current_server_id()) + return result + + section = SimpleNamespace(key=1, title='Movies') + connection = SimpleNamespace(library=SimpleNamespace(sections=lambda: scoped([section]))) + monkeypatch.setattr(plexapi, 'setup_plexapi', lambda: connection) + monkeypatch.setattr(dashboard, '_cache_server', lambda: scoped(True)) + monkeypatch.setattr(plexapi, 'scan_items', lambda enqueue: enqueue('42')) + monkeypatch.setattr(tmdb, 'query', lambda path, params: scoped({'id': 7})) + update = Mock(side_effect=lambda rating_key: scoped(True)) + monkeypatch.setattr(plexapi, 'update_plex_item', update) + with storage.server_scope('parent'): + assert adapter.libraries() == [{'id': '1', 'title': 'Movies'}] + assert adapter.cache_dashboard() + assert adapter.update_item('42') + assert adapter.query_tmdb('find/tt42', {}) == {'id': 7} + adapter.scan(lambda item_id: scoped(item_id == '42')) + assert storage.current_server_id() == 'parent' + update.assert_called_once_with(rating_key=42) + assert seen == ['saved'] * 5 + + update.side_effect = OSError('Offline') + with pytest.raises(OSError): + adapter.update_item('42') + assert storage.current_server_id() == 'default' + + +def test_plex_browser_urls_encode_identifiers(): + urls = PlexMediaServer('server /?').web_urls('library &', 'item ?') + assert urls['server'] == 'https://app.plex.tv/desktop/#!/media/server%20%2F%3F/com.plexapp.plugins.library' + assert urls['library'].endswith('?source=library+%26') + assert urls['item'].endswith('/details?key=%2Flibrary%2Fmetadata%2Fitem+%3F') diff --git a/tests/unit/themerr/test_cache.py b/tests/unit/plex/test_dashboard.py similarity index 99% rename from tests/unit/themerr/test_cache.py rename to tests/unit/plex/test_dashboard.py index fb987830..8ba3284d 100644 --- a/tests/unit/themerr/test_cache.py +++ b/tests/unit/plex/test_dashboard.py @@ -5,7 +5,7 @@ from unittest.mock import Mock # local imports -from themerr import cache +from plex import dashboard as cache from themerr import storage diff --git a/tests/unit/themerr/test_general.py b/tests/unit/plex/test_media.py similarity index 99% rename from tests/unit/themerr/test_general.py rename to tests/unit/plex/test_media.py index 622a1b7f..1da3c11c 100644 --- a/tests/unit/themerr/test_general.py +++ b/tests/unit/plex/test_media.py @@ -9,7 +9,7 @@ import pytest # local imports -from themerr import general +from plex import media as general def test_metadata_and_upload_paths(configured, item, tmp_path): diff --git a/tests/unit/plex/test_plexapi.py b/tests/unit/plex/test_plexapi.py index 3163f25b..eafb5fff 100644 --- a/tests/unit/plex/test_plexapi.py +++ b/tests/unit/plex/test_plexapi.py @@ -13,6 +13,7 @@ # local imports from common import config from plex import plexapi +from media_servers import processing from youtube.youtube_dl import AudioFile @@ -513,12 +514,12 @@ def test_update_handles_themerrdb_failure(configured, item, monkeypatch): def test_listener_handler(configured, monkeypatch): - monkeypatch.setattr(plexapi, 'q', Queue()) + monkeypatch.setattr(processing, 'q', Queue()) configured['Themerr']['BOOL_PLEX_MOVIE_SUPPORT'] = True entry = {'type': 1, 'state': 5, 'identifier': 'com.plexapp.plugins.library', 'itemID': '42'} plexapi.plex_listener_handler({'type': 'timeline', 'TimelineEntry': [entry, entry]}) - assert plexapi.q.qsize() == 1 - assert plexapi.q.get_nowait() == 42 + assert processing.q.qsize() == 1 + assert processing.q.get_nowait() == ('default', '42') def test_scheduled_update(configured, item, monkeypatch): @@ -530,9 +531,9 @@ def test_scheduled_update(configured, item, monkeypatch): library = SimpleNamespace(sections=lambda: [section]) monkeypatch.setattr(plexapi, 'setup_plexapi', lambda: SimpleNamespace(library=library)) monkeypatch.setattr(plexapi.themerr_db, 'update_cache', Mock()) - monkeypatch.setattr(plexapi, 'q', Queue()) - plexapi._scheduled_update_server() - assert plexapi.q.get_nowait() == 42 + monkeypatch.setattr(processing, 'q', Queue()) + plexapi.scan_items(processing.enqueue) + assert processing.q.get_nowait() == ('default', '42') assert "library_id=1 library_name='Movies' item='Example'" in plexapi._item_log_context(item) @@ -543,9 +544,9 @@ def test_scheduled_update_ignores_configured_library(configured, item, monkeypat library = SimpleNamespace(sections=lambda: [section]) monkeypatch.setattr(plexapi, 'setup_plexapi', lambda: SimpleNamespace(library=library)) monkeypatch.setattr(plexapi.themerr_db, 'update_cache', Mock()) - monkeypatch.setattr(plexapi, 'q', Queue()) + monkeypatch.setattr(processing, 'q', Queue()) - plexapi._scheduled_update_server() + plexapi.scan_items(processing.enqueue) section.all.assert_not_called() - assert plexapi.q.empty() + assert processing.q.empty() diff --git a/tests/unit/plex/test_servers.py b/tests/unit/plex/test_servers.py index 3d6ba713..14125a9e 100644 --- a/tests/unit/plex/test_servers.py +++ b/tests/unit/plex/test_servers.py @@ -12,7 +12,9 @@ # local imports from plex import auth, plexapi, servers, token_store -from themerr import cache, general, storage, tmdb +from plex import dashboard, media as general, tmdb +from media_servers import processing +from themerr import cache, storage @pytest.fixture(autouse=True) @@ -236,23 +238,23 @@ def refresh(): storage.replace_dashboard(snapshot(identifier)) return True - monkeypatch.setattr(cache, '_cache_server', refresh) + monkeypatch.setattr(dashboard, '_cache_server', refresh) cache.cache_data() assert seen == ['a', 'b'] assert servers.get_server('a')['last_error'] assert servers.get_server('b')['last_refresh'] seen.clear() - monkeypatch.setattr(plexapi, '_scheduled_update_server', refresh) - plexapi.scheduled_update() + monkeypatch.setattr(plexapi, 'scan_items', lambda enqueue: refresh()) + processing.scheduled_update() assert seen == ['a', 'b'] assert storage.current_server_id() == 'default' def test_registry_empty_never_falls_back_to_the_retired_single_connection(configured, monkeypatch): scan, refresh = Mock(), Mock() - monkeypatch.setattr(plexapi, '_scheduled_update_server', scan) - monkeypatch.setattr(cache, '_cache_server', refresh) - plexapi.scheduled_update() + monkeypatch.setattr(plexapi, 'scan_items', scan) + monkeypatch.setattr(dashboard, '_cache_server', refresh) + processing.scheduled_update() cache.cache_data() scan.assert_not_called() refresh.assert_not_called() @@ -261,7 +263,7 @@ def test_registry_empty_never_falls_back_to_the_retired_single_connection(config def test_listener_callbacks_and_queue_carry_server_identity(configured, monkeypatch): from queue import Queue queue = Queue() - monkeypatch.setattr(plexapi, 'q', queue) + monkeypatch.setattr(processing, 'q', queue) monkeypatch.setattr(plexapi, '_alert_listeners', {}) for identifier in ('a', 'b'): save_server(identifier) @@ -280,7 +282,7 @@ def listener(**kwargs): for kwargs, _ in listeners: kwargs['callback'](event) kwargs['callback'](event) - assert list(queue.queue) == [('a', 42), ('b', 42)] + assert list(queue.queue) == [('a', '42'), ('b', '42')] servers.update_server('a', {'enabled': False}) listeners[0][0]['callback'](event) assert queue.qsize() == 2 @@ -302,31 +304,31 @@ def test_proxy_and_metadata_paths_follow_current_server(configured, monkeypatch, tmdb._proxy_cache.clear() for identifier in ('a', 'b'): with storage.server_scope(identifier): - assert tmdb._plex_get('find/tt42', {})['movie_results'][0]['id'] == 99 + assert tmdb.query('find/tt42', {})['movie_results'][0]['id'] == 99 assert get.call_args.args[0] == 'http://' + identifier + ':32400/services/tmdb' assert get.call_args.kwargs['headers']['X-Plex-Token'] == 'token-' + identifier assert general.get_media_upload_path(item, 'themes').startswith('C:/Plex-' + identifier) servers.update_server('a', {'data_directory': '', 'enabled': False}) with storage.server_scope('a'): assert general.get_media_upload_path(item, 'themes') == '' - assert tmdb._plex_get('find/tt42', {}) == {} + assert tmdb.query('find/tt42', {}) == {} def test_workers_keep_server_scope_deduplicate_active_work_and_release_failed_items(monkeypatch): - monkeypatch.setattr(plexapi, '_active_items', set()) - queue = plexapi._WorkQueue() - monkeypatch.setattr(plexapi, 'q', queue) + monkeypatch.setattr(processing, '_active_items', set()) + queue = processing._WorkQueue() + monkeypatch.setattr(processing, 'q', queue) monkeypatch.setattr(servers, 'get_server', lambda identifier: {'enabled': identifier != 'paused'}) for identifier in ('a', 'b', 'paused'): with storage.server_scope(identifier): - assert plexapi.enqueue(42) - assert not plexapi.enqueue(42) + assert processing.enqueue(42) + assert not processing.enqueue(42) seen = [] def update(rating_key): identifier = storage.current_server_id() seen.append((identifier, rating_key)) - assert not plexapi.enqueue(rating_key) + assert not processing.enqueue(rating_key) if identifier == 'a': raise RuntimeError('One server failed') @@ -340,13 +342,13 @@ def next_work(): monkeypatch.setattr(queue, 'get', next_work) monkeypatch.setattr(plexapi, 'update_plex_item', update) with pytest.raises(KeyboardInterrupt): - plexapi.process_queue() + processing.process_queue() assert seen == [('a', 42), ('b', 42)] assert queue.unfinished_tasks == 0 - assert not plexapi._active_items + assert not processing._active_items assert storage.current_server_id() == 'default' with storage.server_scope('a'): - assert plexapi.enqueue(42) + assert processing.enqueue(42) def test_manual_address_uses_shared_server_resource_token(configured, monkeypatch): diff --git a/tests/unit/plex/test_tmdb.py b/tests/unit/plex/test_tmdb.py new file mode 100644 index 00000000..adc10f0c --- /dev/null +++ b/tests/unit/plex/test_tmdb.py @@ -0,0 +1,43 @@ +"""Plex TMDB proxy response validation and cache behavior.""" + +from unittest.mock import Mock + +import pytest +import requests + +from plex import tmdb + + +@pytest.mark.parametrize('data, cached', [({}, True), ([], False), (None, False), ('invalid', False)]) +def test_only_valid_dictionary_responses_are_cached(configured, monkeypatch, data, cached): + configured['Plex']['PLEX_URL'] = 'http://plex.example:32400' + monkeypatch.setattr(tmdb, '_proxy_cache', {}) + monkeypatch.setattr(tmdb.auth, 'get_token', lambda: 'test-token') + response = Mock() + response.json.return_value = data + request = Mock(return_value=response) + monkeypatch.setattr(tmdb.requests, 'get', request) + + assert tmdb.query('find/tt123', {'external_source': 'imdb_id'}) == {} + assert tmdb.query('find/tt123', {'external_source': 'imdb_id'}) == {} + assert request.call_count == (1 if cached else 2) + assert bool(tmdb._proxy_cache) == cached + + +@pytest.mark.parametrize('stage, error', [ + ('raise_for_status', requests.HTTPError('bad status')), + ('json', ValueError('invalid JSON')), +]) +def test_failed_responses_are_retried(configured, monkeypatch, stage, error): + configured['Plex']['PLEX_URL'] = 'http://plex.example:32400' + monkeypatch.setattr(tmdb, '_proxy_cache', {}) + monkeypatch.setattr(tmdb.auth, 'get_token', lambda: 'test-token') + response = Mock() + getattr(response, stage).side_effect = error + request = Mock(return_value=response) + monkeypatch.setattr(tmdb.requests, 'get', request) + + assert tmdb.query('find/tt123', {'external_source': 'imdb_id'}) == {} + assert tmdb.query('find/tt123', {'external_source': 'imdb_id'}) == {} + assert request.call_count == 2 + assert tmdb._proxy_cache == {} diff --git a/tests/unit/test_entrypoint.py b/tests/unit/test_entrypoint.py index 601883dc..2f85353b 100644 --- a/tests/unit/test_entrypoint.py +++ b/tests/unit/test_entrypoint.py @@ -13,6 +13,7 @@ from common import helpers from common import threads from plex import plexapi +from media_servers import processing from themerr import scheduled_tasks import themerr_plex @@ -52,7 +53,7 @@ def test_main_starts_services(configured, monkeypatch): worker = Mock() worker.start = Mock() monkeypatch.setattr(threads, 'run_in_thread', Mock(return_value=worker)) - monkeypatch.setattr(plexapi, 'start_queue_threads', Mock()) + monkeypatch.setattr(processing, 'start_queue_threads', Mock()) monkeypatch.setattr(plexapi, 'plex_listener', Mock()) monkeypatch.setattr(scheduled_tasks, 'setup_scheduling', Mock()) monkeypatch.setattr(themerr_plex, 'wait', Mock()) @@ -61,7 +62,7 @@ def test_main_starts_services(configured, monkeypatch): initialized.assert_called_once() worker.start.assert_called_once() - plexapi.start_queue_threads.assert_called_once() + processing.start_queue_threads.assert_called_once() plexapi.plex_listener.assert_called_once() scheduled_tasks.setup_scheduling.assert_called_once() assert configured['Network']['HTTP_PORT'] == 9495 diff --git a/tests/unit/themerr/test_tmdb.py b/tests/unit/themerr/test_tmdb.py index 1e6fd9e1..24a98bd0 100644 --- a/tests/unit/themerr/test_tmdb.py +++ b/tests/unit/themerr/test_tmdb.py @@ -8,6 +8,7 @@ # local imports from themerr import tmdb +from plex import tmdb as proxy def test_external_lookup(monkeypatch): @@ -26,7 +27,7 @@ def test_external_lookup(monkeypatch): def test_collection_lookup(monkeypatch): - monkeypatch.setattr(tmdb, '_plex_get', lambda *_: {}) + monkeypatch.setattr(proxy, 'query', lambda *_: {}) monkeypatch.setattr(tmdb.themerr_db, 'find_id_by_title', lambda database_type, title: 645 if title == 'James Bond' else None) @@ -38,7 +39,7 @@ def test_external_api_fallback(monkeypatch): calls = [] monkeypatch.setattr(tmdb.themerr_db, 'find_movie_id_by_imdb', lambda _: None) monkeypatch.setattr(tmdb.themerr_db, 'find_id_by_title', lambda *_: None) - monkeypatch.setattr(tmdb, '_plex_get', lambda *_: {}) + monkeypatch.setattr(proxy, 'query', lambda *_: {}) monkeypatch.delenv('TMDB_API_READ_ACCESS_TOKEN', raising=False) monkeypatch.setattr(tmdb.helpers, 'json_get', lambda **kwargs: calls.append(kwargs) or { 'movie_results': [{'id': 123}], 'tv_results': [{'id': 456}], @@ -61,7 +62,7 @@ def test_external_api_fallback(monkeypatch): def test_collection_api_fallback(monkeypatch): calls = [] monkeypatch.setattr(tmdb.themerr_db, 'find_id_by_title', lambda *_: None) - monkeypatch.setattr(tmdb, '_plex_get', lambda *_: {}) + monkeypatch.setattr(proxy, 'query', lambda *_: {}) monkeypatch.setenv('TMDB_API_READ_ACCESS_TOKEN', 'test-token') monkeypatch.setattr(tmdb.helpers, 'json_get', lambda **kwargs: calls.append(kwargs) or { 'results': [{'name': 'Other Collection', 'id': 1}, @@ -77,11 +78,11 @@ def test_collection_api_fallback(monkeypatch): def test_plex_proxy_resolves_imdb_tvdb_and_collection(configured, monkeypatch): configured['Plex']['PLEX_URL'] = 'http://plex.example:32400/' - tmdb._proxy_cache.clear() + proxy._proxy_cache.clear() monkeypatch.setattr(tmdb.themerr_db, 'find_movie_id_by_imdb', lambda *_: None) monkeypatch.setattr(tmdb.themerr_db, 'find_id_by_title', lambda *_: None) monkeypatch.delenv('TMDB_API_READ_ACCESS_TOKEN', raising=False) - tmdb.auth.set_token('secret-plex-token') + proxy.auth.set_token('secret-plex-token') calls = [] def proxy_response(url, **kwargs): @@ -93,14 +94,14 @@ def proxy_response(url, **kwargs): else {'results': [{'name': 'James Bond Collection', 'id': 645}]}) return response - monkeypatch.setattr(tmdb.requests, 'get', proxy_response) + monkeypatch.setattr(proxy.requests, 'get', proxy_response) assert tmdb.get_tmdb_id_from_external_id('tt0497329', 'imdb', 'movie') == 10005 assert tmdb.get_tmdb_id_from_external_id(268592, 'tvdb', 'tv') == 48866 assert tmdb.get_tmdb_id_from_collection('James Bond', language='en-US') == 645 assert len(calls) == 3 assert all(url == 'http://plex.example:32400/services/tmdb' for url, _ in calls) assert all(kwargs['headers']['X-Plex-Token'] == 'secret-plex-token' for _, kwargs in calls) - assert all(kwargs['timeout'] == tmdb.PROXY_TIMEOUT for _, kwargs in calls) + assert all(kwargs['timeout'] == proxy.PROXY_TIMEOUT for _, kwargs in calls) assert calls[0][1]['params']['uri'] == '/find/tt0497329?external_source=imdb_id' assert calls[1][1]['params']['uri'] == '/find/268592?external_source=tvdb_id' assert calls[2][1]['params']['uri'] == '/search/collection?query=James-Bond&language=en-US' @@ -110,12 +111,12 @@ def proxy_response(url, **kwargs): def test_plex_proxy_failure_falls_back_to_tmdb(configured, monkeypatch): configured['Plex']['PLEX_URL'] = 'http://plex.example:32400' - tmdb._proxy_cache.clear() - tmdb.auth.set_token('secret-plex-token') + proxy._proxy_cache.clear() + proxy.auth.set_token('secret-plex-token') monkeypatch.setattr(tmdb.themerr_db, 'find_movie_id_by_imdb', lambda *_: None) monkeypatch.setenv('TMDB_API_READ_ACCESS_TOKEN', 'tmdb-token') - monkeypatch.setattr(tmdb.requests, 'get', Mock(side_effect=requests.Timeout('timed out'))) + monkeypatch.setattr(proxy.requests, 'get', Mock(side_effect=requests.Timeout('timed out'))) monkeypatch.setattr(tmdb.helpers, 'json_get', lambda **_: {'movie_results': [{'id': 10005}]}) assert tmdb.get_tmdb_id_from_external_id('tt0497329', 'imdb', 'movie') == 10005 - assert tmdb._proxy_cache == {} + assert proxy._proxy_cache == {} diff --git a/web/templates/home.html b/web/templates/home.html index d5440eca..ba18e9d2 100644 --- a/web/templates/home.html +++ b/web/templates/home.html @@ -52,7 +52,7 @@

{{ _('Your library deserves a soundtrack.') if not servers else _('Getting t {% if item.theme %} {% endif %} - {{ item.title }}{{ item.year or '—' }} · {{ item.type|capitalize }} · {{ _('Plex ID') }}: {{ item.rating_key }} + {{ item.title }}{{ item.year or '—' }} · {{ item.type|capitalize }} · {{ _('Plex ID') }}: {{ item.rating_key }} {% if item.theme %}{% endif %} From 643551fbe299708c99cf029e8783db0e06baf771 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Sun, 4 Oct 2026 22:20:18 -0400 Subject: [PATCH 02/11] feat: add Jellyfin support with bundled connectors --- .github/workflows/ci.yml | 11 + .gitignore | 5 + Dockerfile | 12 + .../jellyfin.tests/Connector.Tests.csproj | 12 + connectors/jellyfin.tests/Program.cs | 67 +++++ connectors/jellyfin/Controller.cs | 67 +++++ connectors/jellyfin/NuGet.Config | 7 + connectors/jellyfin/Plugin.cs | 15 ++ connectors/jellyfin/ThemeFiles.cs | 116 ++++++++ connectors/jellyfin/Themerr.Connector.csproj | 19 ++ docs/source/about/usage.rst | 48 +++- docs/source/contributing/build.rst | 22 +- docs/source/contributing/media_servers.rst | 72 +++-- locale/themerr-plex.po | 186 ++++++++----- scripts/build.py | 8 + scripts/build_connector.py | 87 ++++++ src/common/admin.py | 3 + src/common/api_docs.py | 15 +- src/common/config.py | 18 ++ src/common/credentials.py | 120 +++++++++ src/common/helpers.py | 4 +- src/common/server_ui.py | 7 +- src/common/webapp.py | 92 +++++-- src/jellyfin/__init__.py | 1 + src/jellyfin/audio.py | 43 +++ src/jellyfin/backend.py | 247 ++++++++++++++++++ src/jellyfin/client.py | 104 ++++++++ src/jellyfin/connector.py | 125 +++++++++ src/jellyfin/metadata.py | 46 ++++ src/jellyfin/servers.py | 137 ++++++++++ src/jellyfin/web.py | 85 ++++++ src/media_servers/__init__.py | 65 ++++- src/media_servers/base.py | 15 ++ src/plex/backend.py | 4 + src/plex/token_store.py | 127 +-------- src/themerr/migrations/env.py | 2 + .../versions/d09a42b638a1_jellyfin_servers.py | 43 +++ src/themerr/storage.py | 18 +- tests/conftest.py | 20 ++ tests/functional/test_jellyfin.py | 77 ++++++ tests/unit/jellyfin/test_audio.py | 54 ++++ tests/unit/jellyfin/test_backend.py | 109 ++++++++ tests/unit/jellyfin/test_connector.py | 89 +++++++ tests/unit/jellyfin/test_metadata.py | 62 +++++ tests/unit/jellyfin/test_servers.py | 95 +++++++ tests/unit/media_servers/test_cache.py | 2 +- tests/unit/plex/test_backend.py | 6 +- tests/unit/test_notification_build.py | 2 + tests/unit/themerr/test_storage.py | 27 +- tests/web/servers.test.mjs | 57 +++- web/js/servers.js | 35 ++- web/templates/activity.html | 2 +- web/templates/home.html | 6 +- web/templates/servers.html | 21 +- web/templates/theme_player.html | 2 +- 55 files changed, 2466 insertions(+), 275 deletions(-) create mode 100644 connectors/jellyfin.tests/Connector.Tests.csproj create mode 100644 connectors/jellyfin.tests/Program.cs create mode 100644 connectors/jellyfin/Controller.cs create mode 100644 connectors/jellyfin/NuGet.Config create mode 100644 connectors/jellyfin/Plugin.cs create mode 100644 connectors/jellyfin/ThemeFiles.cs create mode 100644 connectors/jellyfin/Themerr.Connector.csproj create mode 100644 scripts/build_connector.py create mode 100644 src/common/credentials.py create mode 100644 src/jellyfin/__init__.py create mode 100644 src/jellyfin/audio.py create mode 100644 src/jellyfin/backend.py create mode 100644 src/jellyfin/client.py create mode 100644 src/jellyfin/connector.py create mode 100644 src/jellyfin/metadata.py create mode 100644 src/jellyfin/servers.py create mode 100644 src/jellyfin/web.py create mode 100644 src/themerr/migrations/versions/d09a42b638a1_jellyfin_servers.py create mode 100644 tests/functional/test_jellyfin.py create mode 100644 tests/unit/jellyfin/test_audio.py create mode 100644 tests/unit/jellyfin/test_backend.py create mode 100644 tests/unit/jellyfin/test_connector.py create mode 100644 tests/unit/jellyfin/test_metadata.py create mode 100644 tests/unit/jellyfin/test_servers.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fee2bf04..d540d0ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -119,6 +119,17 @@ jobs: uv sync --locked --all-extras --no-install-project --no-python-downloads --python ${{ env.PYTHON_VERSION }} + - name: Setup .NET + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 + with: + dotnet-version: '10.0.x' + + - name: Build Jellyfin connectors + run: uv run --locked --all-extras --no-build --no-sync python scripts/build_connector.py + + - name: Test Jellyfin connector file handling + run: dotnet run --project connectors/jellyfin.tests/Connector.Tests.csproj --configuration Release + - name: Build and test web assets run: | npm ci --ignore-scripts diff --git a/.gitignore b/.gitignore index 14b6c73e..71138615 100644 --- a/.gitignore +++ b/.gitignore @@ -165,3 +165,8 @@ coverage/ junit-python.xml http_cache.sqlite http_cache.sqlite-* + +# connectors +jellyfin-connector/ +connectors/**/bin/ +connectors/**/obj/ diff --git a/Dockerfile b/Dockerfile index 560266dc..9f6ea27a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,13 @@ # syntax=docker/dockerfile:1 +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0 AS connector +RUN apt-get update && apt-get install -y --no-install-recommends python3 && rm -rf /var/lib/apt/lists/* +WORKDIR /connector +COPY connectors/jellyfin/ connectors/jellyfin/ +COPY scripts/build_connector.py scripts/build_connector.py +COPY src/common/version.py src/common/version.py +ARG THEMERR_VERSION +RUN python3 scripts/build_connector.py + FROM ghcr.io/astral-sh/uv:0.12-python3.14-trixie-slim AS base COPY --from=denoland/deno:bin-2.9.7 /deno /usr/local/bin/deno @@ -27,6 +36,8 @@ ENV PATH="/opt/venv/bin:$PATH" # setup app directory WORKDIR /build COPY . . +COPY --from=connector /connector/jellyfin-connector/ /build/jellyfin-connector/ +COPY --from=connector /connector/src/common/version.py /build/src/common/version.py # setup locked Python dependencies RUN uv sync --locked --extra docs --no-build --no-install-project --no-python-downloads @@ -45,6 +56,7 @@ COPY --from=build /build/web/ /app/web/ COPY --from=build /build/locale/ /app/locale/ COPY --from=build /build/_site/ /app/_site/ COPY --from=build /build/LICENSE /app/LICENSE +COPY --from=build /build/jellyfin-connector/ /app/jellyfin-connector/ # copy python venv COPY --from=build /opt/venv/ /opt/venv/ diff --git a/connectors/jellyfin.tests/Connector.Tests.csproj b/connectors/jellyfin.tests/Connector.Tests.csproj new file mode 100644 index 00000000..9bc7ef70 --- /dev/null +++ b/connectors/jellyfin.tests/Connector.Tests.csproj @@ -0,0 +1,12 @@ + + + Exe + net10.0 + enable + enable + true + + + + + diff --git a/connectors/jellyfin.tests/Program.cs b/connectors/jellyfin.tests/Program.cs new file mode 100644 index 00000000..0c71f0e9 --- /dev/null +++ b/connectors/jellyfin.tests/Program.cs @@ -0,0 +1,67 @@ +using System.Security.Cryptography; +using Themerr.Connector; + +var root = Path.Combine(Path.GetTempPath(), "themerr-connector-test-" + Guid.NewGuid()); +Directory.CreateDirectory(root); +var bytes = "complete validated audio"u8.ToArray(); +var digest = Convert.ToHexString(SHA256.HashData(bytes)).ToLowerInvariant(); +static void Check(bool condition) { if (!condition) throw new Exception("Connector regression check failed."); } +static async Task Reject(Func action) where T : Exception +{ + try { await action(); } + catch (T) { return; } + throw new Exception("Expected " + typeof(T).Name); +} +Task Save(string type = "audio/mp4", string? hash = null, byte[]? data = null) => + ThemeFiles.Save(root, new MemoryStream(data ?? bytes), type, hash ?? digest, 0, CancellationToken.None); +try +{ + await Reject(() => Save("../../file")); + await Reject(() => Save(hash: new string('z', 64))); + await Reject(() => Save(hash: new string('a', 64))); + await Reject(() => Save(data: [])); + Check(!Directory.EnumerateFiles(root).Any()); + File.WriteAllText(Path.Combine(root, "theme.mp3"), "user theme"); + await Reject(() => Save()); + Check(File.ReadAllText(Path.Combine(root, "theme.mp3")) == "user theme"); + File.Delete(Path.Combine(root, "theme.mp3")); + var saved = await Save(); + Check(saved.Owned && saved.Sha256 == digest); + Check(ThemeFiles.State(root).Owned); + Check(!ThemeFiles.State(root, 2).Owned); + await Reject(() => ThemeFiles.Save(root, new MemoryStream(bytes), + "audio/mp4", digest, 2, CancellationToken.None)); + var switched = await Save("audio/ogg"); + Check(switched.Owned && !File.Exists(Path.Combine(root, "theme.m4a"))); + Check(File.ReadAllBytes(Path.Combine(root, "theme.opus")).SequenceEqual(bytes)); + File.WriteAllText(Path.Combine(root, "theme.opus"), "manually changed"); + Check(!ThemeFiles.State(root).Owned); + await Reject(() => Save()); + File.Delete(Path.Combine(root, "theme.opus")); + File.Delete(Path.Combine(root, ".themerr-connector.json")); + var sibling = root + "-sibling"; + Directory.CreateDirectory(sibling); + try + { + var target = Path.Combine(sibling, "outside.m4a"); + File.WriteAllText(target, "outside"); + bool links = true; + try { File.CreateSymbolicLink(Path.Combine(root, "theme.m4a"), target); } + catch (UnauthorizedAccessException) { links = false; } + catch (IOException error) when (OperatingSystem.IsWindows() && (error.HResult & 0xffff) == 1314) + { links = false; } + if (links) + { + await Reject(() => Save()); + Check(File.ReadAllText(target) == "outside"); + File.Delete(Path.Combine(root, "theme.m4a")); + File.CreateSymbolicLink(Path.Combine(root, ".themerr-connector.json"), target); + await Reject(() => Save()); + Check(File.ReadAllText(target) == "outside"); + } + else { Console.WriteLine("Symbolic-link checks skipped: creation privilege unavailable on this host."); } + } + finally { Directory.Delete(sibling, true); } + Console.WriteLine("Connector file ownership, integrity, and format checks passed."); +} +finally { Directory.Delete(root, true); } diff --git a/connectors/jellyfin/Controller.cs b/connectors/jellyfin/Controller.cs new file mode 100644 index 00000000..a7f31385 --- /dev/null +++ b/connectors/jellyfin/Controller.cs @@ -0,0 +1,67 @@ +using System.Collections.Concurrent; +using System.Reflection; +using MediaBrowser.Common.Api; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace Themerr.Connector; + +[ApiController] +[Route("Themerr")] +[Authorize(Policy = Policies.RequiresElevation)] +public sealed class Controller : ControllerBase +{ + private static readonly ConcurrentDictionary Locks = new(); + private readonly ILibraryManager _library; + private static string Metadata(string key) => typeof(Plugin).Assembly.GetCustomAttributes() + .Single(a => a.Key == key).Value!; + + public Controller(ILibraryManager library) => _library = library; + + [HttpGet("Connector")] + public object Identity() => new { protocol = 1, build = Metadata("ThemerrBuild"), targetAbi = Metadata("JellyfinAbi") }; + + [HttpGet("Items/{itemId:guid}/Theme")] + public ActionResult State(Guid itemId) + { + var item = _library.GetItemById(itemId); + if (item is null) return NotFound(); + var root = Root(item); + if (root is null) return BadRequest(); + return ThemeFiles.State(root, item.GetThemeSongs().Count); + } + + [HttpPost("Items/{itemId:guid}/Theme")] + [RequestSizeLimit(104857600)] + public async Task> Upload(Guid itemId, CancellationToken cancellationToken) + { + if (Request.Headers["X-Themerr-Connector"] != Metadata("ThemerrBuild")) return Conflict(); + var item = _library.GetItemById(itemId); + if (item is null) return NotFound(); + var root = Root(item); + if (root is null || (item.IsLocked && Request.Headers["X-Themerr-Ignore-Locked"] != "true")) return Conflict(); + var gate = Locks.GetOrAdd(itemId, _ => new SemaphoreSlim(1, 1)); + await gate.WaitAsync(cancellationToken).ConfigureAwait(false); + try + { + var state = await ThemeFiles.Save(root, Request.Body, Request.ContentType, + Request.Headers["X-Themerr-SHA256"].ToString(), item.GetThemeSongs().Count, + cancellationToken).ConfigureAwait(false); + await item.RefreshMetadata(cancellationToken).ConfigureAwait(false); + return state; + } + catch (ThemeConflictException) { return Conflict(); } + catch (InvalidDataException) { return BadRequest(); } + catch (IOException) { return StatusCode(500); } + catch (UnauthorizedAccessException) { return StatusCode(500); } + finally { gate.Release(); } + } + + private static string? Root(BaseItem item) => + item is MediaBrowser.Controller.Entities.Movies.Movie or MediaBrowser.Controller.Entities.TV.Series or + MediaBrowser.Controller.Entities.Movies.BoxSet && + item.IsFileProtocol && !item.IsInMixedFolder && Directory.Exists(item.ContainingFolderPath) + ? Path.GetFullPath(item.ContainingFolderPath) : null; +} diff --git a/connectors/jellyfin/NuGet.Config b/connectors/jellyfin/NuGet.Config new file mode 100644 index 00000000..765346e5 --- /dev/null +++ b/connectors/jellyfin/NuGet.Config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/connectors/jellyfin/Plugin.cs b/connectors/jellyfin/Plugin.cs new file mode 100644 index 00000000..5126bc17 --- /dev/null +++ b/connectors/jellyfin/Plugin.cs @@ -0,0 +1,15 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Plugins; +using MediaBrowser.Model.Plugins; +using MediaBrowser.Model.Serialization; + +namespace Themerr.Connector; + +/// Only exposes authenticated theme upload; Themerr owns all processing. +public sealed class Plugin : BasePlugin +{ + public Plugin(IApplicationPaths paths, IXmlSerializer serializer) : base(paths, serializer) { } + public override string Name => "Themerr Connector"; + public override string Description => "Theme upload connector for a matching Themerr installation."; + public override Guid Id => new("f9a117dc-b44a-4507-9706-241837784369"); +} diff --git a/connectors/jellyfin/ThemeFiles.cs b/connectors/jellyfin/ThemeFiles.cs new file mode 100644 index 00000000..9a022007 --- /dev/null +++ b/connectors/jellyfin/ThemeFiles.cs @@ -0,0 +1,116 @@ +using System.Security.Cryptography; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Themerr.Connector; + +public sealed record ThemeState( + [property: JsonPropertyName("present")] bool Present, + [property: JsonPropertyName("owned")] bool Owned, + [property: JsonPropertyName("sha256")] string? Sha256); +public sealed class ThemeConflictException : Exception; + +/// Writes only server-owned filenames beneath a library item's directory. +public static class ThemeFiles +{ + private const string OwnershipFile = ".themerr-connector.json"; + private const long MaximumBytes = 104857600; + private static readonly Dictionary Formats = new() + { + ["audio/mp4"] = "theme.m4a", + ["audio/ogg"] = "theme.opus" + }; + private sealed record Ownership(string File, string Sha256); + + public static ThemeState State(string root, int themeSongCount = 0) + { + var files = Directory.GetFiles(root, "theme.*"); + var present = files.Length > 0 || themeSongCount > 0; + var metadata = Path.Combine(root, OwnershipFile); + RejectLink(metadata); + if (!File.Exists(metadata)) return new(present, false, null); + Ownership? ownership; + try { ownership = JsonSerializer.Deserialize(File.ReadAllText(metadata)); } + catch (JsonException) { return new(present, false, null); } + // A metadata value selects a code-owned filename; it never becomes a path. + var file = Formats.Values.FirstOrDefault(value => value == ownership?.File); + if (file is null || files.Length != 1 || themeSongCount > 1) return new(present, false, null); + var path = Path.Combine(root, file); + RejectLink(path); + if (!File.Exists(path)) return new(present, false, null); + using var stream = File.OpenRead(path); + var digest = Convert.ToHexString(SHA256.HashData(stream)).ToLowerInvariant(); + return new(true, digest == ownership!.Sha256, digest); + } + + public static async Task Save(string root, Stream body, string? contentType, + string expectedDigest, int themeSongCount, CancellationToken cancellationToken) + { + if (contentType is null || !Formats.TryGetValue(contentType, out var file) || + expectedDigest.Length != 64 || !expectedDigest.All(Uri.IsHexDigit)) throw new InvalidDataException(); + var state = State(root, themeSongCount); + if (state.Present && !state.Owned) throw new ThemeConflictException(); + var path = Path.Combine(root, file); + var metadata = Path.Combine(root, OwnershipFile); + RejectLink(path); + RejectLink(metadata); + var temporary = Path.Combine(root, Path.GetRandomFileName()); + var metadataTemporary = Path.Combine(root, Path.GetRandomFileName()); + try + { + var digest = await Write(body, temporary, cancellationToken).ConfigureAwait(false); + if (!string.Equals(digest, expectedDigest, StringComparison.OrdinalIgnoreCase)) throw new InvalidDataException(); + await using (var metadataStream = new FileStream(metadataTemporary, FileMode.CreateNew, + FileAccess.Write, FileShare.None, 4096, FileOptions.Asynchronous)) + { + await JsonSerializer.SerializeAsync(metadataStream, new Ownership(file, digest), + cancellationToken: cancellationToken).ConfigureAwait(false); + } + // Recheck the ownership and link boundaries after receiving the complete body. + var current = State(root, themeSongCount); + if (current.Present && (!current.Owned || current.Sha256 != state.Sha256)) throw new ThemeConflictException(); + RejectLink(path); + RejectLink(metadata); + File.Move(temporary, path, true); + File.Move(metadataTemporary, metadata, true); + foreach (var other in Formats.Values.Where(value => value != file)) + { + var old = Path.Combine(root, other); + RejectLink(old); + if (state.Owned && File.Exists(old)) File.Delete(old); + } + return new(true, true, digest); + } + finally + { + File.Delete(temporary); + File.Delete(metadataTemporary); + } + } + + private static async Task Write(Stream body, string temporary, CancellationToken cancellationToken) + { + using var hash = IncrementalHash.CreateHash(HashAlgorithmName.SHA256); + await using var output = new FileStream(temporary, FileMode.CreateNew, FileAccess.Write, FileShare.None, + 65536, FileOptions.Asynchronous); + var buffer = new byte[65536]; + long size = 0; + int read; + while ((read = await body.ReadAsync(buffer, cancellationToken).ConfigureAwait(false)) != 0) + { + size += read; + if (size > MaximumBytes) throw new InvalidDataException(); + hash.AppendData(buffer, 0, read); + await output.WriteAsync(buffer.AsMemory(0, read), cancellationToken).ConfigureAwait(false); + } + if (size == 0) throw new InvalidDataException(); + return Convert.ToHexString(hash.GetHashAndReset()).ToLowerInvariant(); + } + + private static void RejectLink(string path) + { + var info = new FileInfo(path); + if (info.LinkTarget is not null || (info.Exists && (info.Attributes & FileAttributes.ReparsePoint) != 0)) + throw new ThemeConflictException(); + } +} diff --git a/connectors/jellyfin/Themerr.Connector.csproj b/connectors/jellyfin/Themerr.Connector.csproj new file mode 100644 index 00000000..bc4251e2 --- /dev/null +++ b/connectors/jellyfin/Themerr.Connector.csproj @@ -0,0 +1,19 @@ + + + net10.0 + $(ConnectorFramework) + 12.1.0 + development + enable + enable + true + Themerr.Connector + 0.0.0.0 + + + + + + + + diff --git a/docs/source/about/usage.rst b/docs/source/about/usage.rst index daa48cd0..29ff9e3f 100644 --- a/docs/source/about/usage.rst +++ b/docs/source/about/usage.rst @@ -80,7 +80,7 @@ server address. Each saved server has its own processing toggle, ignored library IDs, data directory, dashboard snapshot, upload history, and errors. Pausing a server prevents new work; an upload already in progress can finish. Removing a server erases its saved connection and local records, while themes already uploaded to Plex remain there. -Select **Disconnect Plex** to erase Plex credentials and pause saved servers. After signing in again, reconnect +Select **Disconnect Plex** to erase Plex credentials and pause saved Plex servers. After signing in again, reconnect each server to resume processing with its retained upload history. On desktop systems, account and server tokens are saved in the operating system's credential store. @@ -134,12 +134,12 @@ open the item on Plex or its metadata provider in a new tab. Media type icons re regional failures do not by themselves require replacing the ThemerrDB video. ThemerrDB checks US availability when accepting themes; a regional failure elsewhere cannot establish that it is currently unavailable in the US. -Select the play button beside an item's title to listen to its currently selected Plex theme, regardless of provider. +Select the play button beside an item's title to listen to its currently selected media-server theme, regardless of provider. The button changes to pause during playback, and the ring around it shows playback progress. Pausing retains your position; selecting another item stops the previous theme. Items without an installed theme have no play button. The player at the bottom of the workspace shows the item's poster, title, year, media type, and server. -Select the title to open the item in Plex. If its poster is unavailable, a music icon appears instead. +Select the title to open the item in its media server. If its poster is unavailable, a music icon appears instead. Use the playback slider to seek, the volume slider to adjust sound, and the previous and next buttons to browse installed themes. **Surprise me** picks a random installed theme across your servers. **Shuffle** plays themes in random order without repeats until the library has played; **Repeat this theme** @@ -161,13 +161,51 @@ resolve other IMDb or TVDB IDs and collection names. If the Plex proxy is unavai ``TMDB_API_READ_ACCESS_TOKEN`` environment variable to your TMDB API Read Access Token. Keep this token outside the web settings and configuration file. +Jellyfin servers +---------------- + +Jellyfin 10.11 and 12.1 are supported. Under **Servers**, enter the server's HTTP or HTTPS base address and an +API key created under **Jellyfin Dashboard > Advanced > API Keys**, then select **Connect Jellyfin**. Use an +address reachable from the machine running Themerr. Each saved server has independent processing settings, +ignored libraries, dashboard records, and upload history. + +Theme uploads require the small **Themerr Connector** bundled with this Themerr build. On the server card, +enter a Themerr address reachable from the Jellyfin machine and select **Install matching connector**. +Themerr registers its local plugin repository and installs the exact compatible connector through Jellyfin's API, +preserving other registered repositories. Restart Jellyfin to load the plugin, then refresh libraries in Themerr. +After upgrading Themerr, install its matching connector again if the server card reports a mismatch. + +The download address must use a certificate trusted by Jellyfin or HTTP on your private network. Themerr's +default self-signed HTTPS certificate is not automatically trusted by Jellyfin. A trusted reverse proxy can +provide the download address; alternatively, configure Themerr's network settings for HTTP on your private network. +Jellyfin downloads the fixed manifest and archive routes without a Themerr admin session; all connection, +installation, library, and theme APIs retain administrator authentication. + +The connector only accepts matching uploads from Themerr and stores files beside the native library item. +Movies must have their own local folders; a shared folder containing unrelated movies cannot have independent themes. +Existing user themes are preserved, including themes created by the older Themerr-jellyfin plugin. Disable that +older plugin's processing when using this integration. Theme files changed manually after an upload are also +preserved. Themerr updates only themes whose current digest matches the connector's ownership record. +Locked items and ignored libraries follow the processing settings. Jellyfin uses Themerr's scheduled scans; +**Activity > Scan for themes** also starts a scan. Movies, series, and collections can be enabled separately +under **Settings > Jellyfin**. + +API keys use the operating system credential store on desktops. Headless and Docker installations use the +same ``THEMERR_PLEX_TOKEN_KEY_FILE`` encryption key as Plex, with a separate namespace for every Jellyfin server. +The historical environment variable name remains supported. Removing a connection deletes its saved API key +and local records; themes already installed on Jellyfin remain there. + +Jellyfin provider IDs are resolved through ThemerrDB's index. Set ``TMDB_API_READ_ACCESS_TOKEN`` if you also +want TMDB lookups for other IMDb or TVDB IDs and collection names. Native IDs, posters, and theme playback +remain scoped to their Jellyfin server. + Local data ---------- Themerr-plex stores its server registry, admin password hash, dashboard snapshots, upload records, processing errors, -and non-secret Plex client ID in +and non-secret client IDs in ``themerr-plex.db`` beside the active configuration file (``config/themerr-plex.db`` by default, or -``/config/themerr-plex.db`` in Docker). Docker and headless installs also store encrypted Plex tokens there. +``/config/themerr-plex.db`` in Docker). Docker and headless installs also store encrypted tokens and API keys there. The /status endpoint returns a JSON health response. The /docs/ endpoint serves the documentation bundled with packaged and Docker builds. diff --git a/docs/source/contributing/build.rst b/docs/source/contributing/build.rst index 61157ad8..e68ae39d 100644 --- a/docs/source/contributing/build.rst +++ b/docs/source/contributing/build.rst @@ -3,12 +3,12 @@ Build ===== -Themerr-plex uses Python 3.14, uv, npm, Deno, and Dockle. Python dependencies are specified in pyproject.toml and +Themerr-plex uses Python 3.14, uv, npm, Deno, Dockle, and the .NET 10 SDK. Python dependencies are specified in pyproject.toml and resolved in uv.lock. The npm lockfile supplies reproducible web assets. esbuild bundles browser dependencies and local scripts into ``web/assets``. The browser interface and JSON API use FastAPI with Uvicorn. Uvicorn runs in the application's web thread and -uses the existing host, port, and TLS settings. Blocking Plex and database calls run in worker threads, while +uses the existing host, port, and TLS settings. Blocking media-server and database calls run in worker threads, while theme audio streams through ASGI. The ``/docs/`` route serves the bundled project documentation. Signed-in administrators can explore the API at ``/api/docs`` or retrieve its OpenAPI schema at ``/api/openapi.json``. The sidebar links to both kinds of documentation. @@ -23,6 +23,18 @@ From the repository root, install dependencies and build browser assets: uv sync --locked --all-extras npm ci --ignore-scripts npm run build + uv run --locked python scripts/build_connector.py + +The connector builder produces separate Jellyfin 10.11 and 12.1 assemblies, targeting .NET 9 and .NET 10. +The .NET 10 SDK builds both targets; Jellyfin supplies their runtime dependencies. Generated ZIP archives and +their descriptor live in the ignored ``jellyfin-connector`` directory. Rebuild them after changing connector +source or the Themerr version. To use a SDK outside ``PATH``, pass ``--dotnet /path/to/dotnet``. + +Run the connector's file ownership and upload integrity checks with: + +.. code-block:: shell + + dotnet run --project connectors/jellyfin.tests/Connector.Tests.csproj --configuration Release Install the commit hook once per checkout: @@ -61,13 +73,15 @@ Build the documentation and standalone executable: uv run --locked --all-extras python scripts/build.py Dockle writes the site to _site. PyInstaller includes that site, the web assets, and translations in the -executable under dist, including Deno for yt-dlp. macOS also produces ``dist/themerr_plex.app``, a directory +executable under dist, including Deno for yt-dlp and both Jellyfin connector archives. The standalone build +rebuilds the connectors after stamping the release version. macOS also produces ``dist/themerr_plex.app``, a directory bundle needed for native notifications. Set ``THEMERR_VERSION`` to the release version before running ``scripts/build.py`` to stamp ``src/common/version.py`` with the version used by release notifications and ``--version``. CI supplies this value from the release setup action's ``release_version`` output. The module is included through normal imports. Unversioned builds compare releases against ``0.0.0``. -Docker uses the same lockfile and Dockle build: +Docker uses the same lockfile and Dockle build. A separate .NET build stage compiles both connectors; +the runtime image contains their artifacts without the SDK: .. code-block:: shell diff --git a/docs/source/contributing/media_servers.rst b/docs/source/contributing/media_servers.rst index 104a9833..20dadfd9 100644 --- a/docs/source/contributing/media_servers.rst +++ b/docs/source/contributing/media_servers.rst @@ -1,9 +1,11 @@ Media-server integration boundary ================================= -Plex is the only supported media server. Its implementation lives in ``src/plex``. +Plex and Jellyfin implementations live in ``src/plex`` and ``src/jellyfin``. Shared services use the abstract contracts in ``src/media_servers/base.py``; -``media_servers.get_backend()`` selects the Plex implementation in one place. +``media_servers.get_backend()`` returns a registry that routes saved server IDs +to their implementation. Jellyfin IDs have a ``jellyfin:`` prefix. Existing Plex +IDs and the default Plex connection retain their original meaning. ``MediaServerBackend`` owns saved connections, public connection settings, account authorization status, event-listener lifecycle, and integration-specific @@ -12,10 +14,12 @@ dashboard snapshots, scanning, item updates, artwork, selected-theme streams, browser links, and an optional TMDB metadata service. Implementations retain the server identity and use ``storage.server_scope`` around storage and native work. -The Plex package owns SDK objects, agent and GUID interpretation, upload methods -and verification, metadata-directory layout and cleanup, account sign-in, -discovery protocols, TMDB proxy credentials, and native media paths. Shared -callers receive public dictionaries or HTTP streams. Stream consumers must close +The Plex package owns SDK objects, agent and GUID interpretation, upload methods, +metadata-directory cleanup, account sign-in, discovery, and TMDB proxy credentials. +The Jellyfin package owns REST requests, pagination, UUID validation, provider +metadata, API-key connections, and connector installation and verification. +Credentials use the shared secure store with independent account and server +namespaces. Public records never include tokens or API keys. Stream consumers must close upstream responses on rejection, HEAD requests, disconnects, and completion; credentials and upstream response bodies must never enter public failures. @@ -23,7 +27,8 @@ credentials and upstream response bodies must never enter public failures. entries contain a server ID and an opaque string item ID. Duplicate queued and active work is suppressed within a server, while identical IDs on different servers remain independent. Paused or removed servers are skipped by workers. -Failed work releases its active entry so a later scan can retry it. +Failed work releases its active entry so a later scan can retry it. Plex also +listens for library events; Jellyfin uses the shared scheduled scans. ``themerr.cache`` owns refresh orchestration and successful-refresh notifications. An unavailable server keeps its last dashboard snapshot and does not interrupt @@ -31,14 +36,45 @@ other servers. Each implementation publishes the existing section and item dictionary format through ``storage.replace_dashboard`` only after a complete scan, using the dashboard revision to preserve concurrent successful uploads. -The existing database tables, ``rating_key`` fields, HTTP routes, configuration -keys, and credential-store namespaces remain compatible with Plex installations. -Dashboard storage still uses integer library keys, and the existing playback -routes accept integer rating keys. Changing those persisted fields and public -routes belongs to the later work that introduces another integration. -The current browser interface and onboarding still describe Plex. This refactor -does not introduce a server-type setting or another server implementation. - -Tests under ``tests/unit/plex`` cover native integration behavior with mocked -services. Tests under ``tests/unit/media_servers`` cover the shared contracts -and orchestration with opaque identifiers, without a live media server. +The database migration changes library keys to strings while retaining existing +Plex records. Server-scoped playback routes accept opaque item IDs; legacy integer +Plex playback routes remain available. Existing Plex configuration keys and +credential-store namespaces remain compatible. + +Jellyfin connector +------------------ + +The small connector under ``connectors/jellyfin`` supplies administrator-only +identity, theme-state, and upload endpoints. It has no download service, scheduled +task, or settings UI. Themerr resolves metadata and downloads audio through the +shared Python services. AAC remains in MP4; WebM Opus is remuxed to Ogg without +re-encoding. + +``scripts/build_connector.py`` builds one assembly for each explicitly supported +Jellyfin ABI: 10.11 and 12.1. Its descriptor records a fingerprint of the connector +source, build profiles, and Themerr version. Installation selects the exact +bundled version for the target server, and uploads require a matching fingerprint, +protocol, and ABI. Unsupported versions and stale connectors fail with an +installation error. Jellyfin must restart to load a newly installed plugin. + +Themerr serves the manifest and two archives at fixed routes under +``/jellyfin/connector``. These exact GET and HEAD routes are public so Jellyfin +can download them. Connection and installation APIs require the usual admin +session and CSRF token. Downloads use the shared canonical path policy and file +response helper. Request values never select filenames. The manifest uses the +administrator-selected Themerr address, rather than the request's Host header. +Installing the connector preserves the server's other plugin repositories. + +The connector maps native item GUIDs through Jellyfin's library manager to local +movie, series, and collection folders. Uploads use fixed ``theme.m4a`` or +``theme.opus`` filenames. A digest-verified ownership record permits subsequent +updates; existing user themes and manually changed files are preserved. An +upload is bounded, verified, and staged before replacing an owned file. File +links are rejected. Python records successful uploads only after the connector +acknowledges the expected digest. + +Tests under ``tests/unit/plex`` and ``tests/unit/jellyfin`` mock native services. +``tests/unit/media_servers`` covers shared orchestration. Functional Jellyfin +tests cover authentication, CSRF, fixed resource routes, and rejected paths. +The C# harness under ``connectors/jellyfin.tests`` checks native file ownership, +integrity, replacement, and link protection. diff --git a/locale/themerr-plex.po b/locale/themerr-plex.po index 97c2dfb5..ea26df4b 100644 --- a/locale/themerr-plex.po +++ b/locale/themerr-plex.po @@ -179,197 +179,219 @@ msgid "Auto update" msgstr "" #: src/common/config.py:238 -msgid "Plex" +msgid "Jellyfin" msgstr "" #: src/common/config.py:239 -msgid "Plex settings." +msgid "" +"Jellyfin theme processing settings. Connections are managed on the " +"Servers page." +msgstr "" + +#: src/common/config.py:242 src/common/config.py:299 +msgid "Movie support" msgstr "" #: src/common/config.py:243 +msgid "Add themes to Jellyfin movies." +msgstr "" + +#: src/common/config.py:246 src/common/config.py:305 +msgid "Series support" +msgstr "" + +#: src/common/config.py:247 +msgid "Add themes to Jellyfin series." +msgstr "" + +#: src/common/config.py:250 src/common/config.py:293 web/templates/home.html:30 +msgid "Collections" +msgstr "" + +#: src/common/config.py:251 +msgid "Add themes to Jellyfin collections." +msgstr "" + +#: src/common/config.py:256 +msgid "Plex" +msgstr "" + +#: src/common/config.py:257 +msgid "Plex settings." +msgstr "" + +#: src/common/config.py:261 msgid "Plex URL" msgstr "" -#: src/common/config.py:244 +#: src/common/config.py:262 msgid "The URL to the Plex server." msgstr "" -#: src/common/config.py:253 web/templates/servers.html:20 +#: src/common/config.py:271 web/templates/servers.html:20 msgid "Plex data directory" msgstr "" -#: src/common/config.py:254 +#: src/common/config.py:272 msgid "" "https://support.plex.tv/articles/202915258-where-is-the-plex-media-" "server-data-directory-located/" msgstr "" -#: src/common/config.py:264 +#: src/common/config.py:282 msgid "Themerr" msgstr "" -#: src/common/config.py:265 +#: src/common/config.py:283 msgid "Themerr settings." msgstr "" -#: src/common/config.py:269 +#: src/common/config.py:287 msgid "Themerr Enabled" msgstr "" -#: src/common/config.py:270 +#: src/common/config.py:288 msgid "When enabled, Themerr will attempt to update themes." msgstr "" -#: src/common/config.py:275 web/templates/home.html:30 -msgid "Collections" -msgstr "" - -#: src/common/config.py:276 +#: src/common/config.py:294 msgid "Add themes to collections." msgstr "" -#: src/common/config.py:281 -msgid "Movie support" -msgstr "" - -#: src/common/config.py:282 +#: src/common/config.py:300 msgid "Add themes to movies using the Plex Movie agent." msgstr "" -#: src/common/config.py:287 -msgid "Series support" -msgstr "" - -#: src/common/config.py:288 +#: src/common/config.py:306 msgid "Add themes to series using the Plex Series agent." msgstr "" -#: src/common/config.py:293 +#: src/common/config.py:311 msgid "Overwrite Plex themes" msgstr "" -#: src/common/config.py:294 +#: src/common/config.py:312 msgid "When enabled, Themerr will overwrite themes provided by Plex." msgstr "" -#: src/common/config.py:299 +#: src/common/config.py:317 msgid "Prefer MP4A AAC Codec" msgstr "" -#: src/common/config.py:300 +#: src/common/config.py:318 msgid "This can improve theme compatibility with Apple devices." msgstr "" -#: src/common/config.py:305 +#: src/common/config.py:323 msgid "Remove unused themes" msgstr "" -#: src/common/config.py:306 +#: src/common/config.py:324 msgid "" "Frees up space in your Plex metadata directory. This option requires that" " the \"Plex data directory\" path is set" msgstr "" -#: src/common/config.py:312 +#: src/common/config.py:330 msgid "Remove unused art" msgstr "" -#: src/common/config.py:313 src/common/config.py:321 +#: src/common/config.py:331 src/common/config.py:339 msgid "" "Applies to collection. Frees up space in your Plex metadata directory. " "This option requires that the \"Plex data directory\" path is set" msgstr "" -#: src/common/config.py:320 +#: src/common/config.py:338 msgid "Remove unused posters" msgstr "" -#: src/common/config.py:328 +#: src/common/config.py:346 msgid "Collection metadata" msgstr "" -#: src/common/config.py:329 +#: src/common/config.py:347 msgid "" "Update collection metadata (poster, art, and summary) during scheduled " "update." msgstr "" -#: src/common/config.py:334 +#: src/common/config.py:352 msgid "Ignore locked fields." msgstr "" -#: src/common/config.py:335 +#: src/common/config.py:353 msgid "" "If you used Themerr-plex v2024.813.13709 or lower, you may need to enable" " this to update items." msgstr "" -#: src/common/config.py:341 +#: src/common/config.py:359 msgid "Ignored library IDs" msgstr "" -#: src/common/config.py:342 +#: src/common/config.py:360 msgid "" "Comma-separated library IDs to exclude from scheduled updates. IDs appear" " on Home." msgstr "" -#: src/common/config.py:348 +#: src/common/config.py:366 msgid "Update themes interval" msgstr "" -#: src/common/config.py:349 +#: src/common/config.py:367 msgid "Interval for automatic update task, in minutes (min: 15)." msgstr "" -#: src/common/config.py:356 +#: src/common/config.py:374 msgid "Update database cache interval" msgstr "" -#: src/common/config.py:357 +#: src/common/config.py:375 msgid "Interval for database cache update task, in minutes (min: 15)." msgstr "" -#: src/common/config.py:364 +#: src/common/config.py:382 msgid "PlexAPI timeout" msgstr "" -#: src/common/config.py:365 +#: src/common/config.py:383 msgid "" "Increase this slightly if you experience timeouts when adding themes. " "(min: 1)" msgstr "" -#: src/common/config.py:372 +#: src/common/config.py:390 msgid "Theme upload retries" msgstr "" -#: src/common/config.py:373 +#: src/common/config.py:391 msgid "If uploading themes fail, retry this many times. (min: 0)" msgstr "" -#: src/common/config.py:380 +#: src/common/config.py:398 msgid "Multiprocessing thread count" msgstr "" -#: src/common/config.py:381 +#: src/common/config.py:399 msgid "" "The number of threads to use when adding themes. Restart required. (min: " "1)" msgstr "" -#: src/common/config.py:388 +#: src/common/config.py:406 msgid "YouTube Cookies" msgstr "" -#: src/common/config.py:390 +#: src/common/config.py:408 msgid "" "Optional: export youtube.com cookies with Get cookies.txt LOCALLY using " "its JSON format, then paste the entire JSON array here and save. Cookies " "contain your browser session; keep them private." msgstr "" -#: src/common/config.py:395 +#: src/common/config.py:413 msgid "Cookie export instructions" msgstr "" @@ -463,7 +485,7 @@ msgstr "" msgid "Logs" msgstr "" -#: src/common/webapp.py:430 web/templates/config.html:22 +#: src/common/webapp.py:478 web/templates/config.html:22 #: web/templates/navbar.html:6 msgid "Settings" msgstr "" @@ -651,7 +673,7 @@ msgid "No processing failures recorded. All quiet here." msgstr "" #: web/templates/activity.html:8 web/templates/home.html:64 -msgid "Plex ID" +msgid "ID" msgstr "" #: web/templates/activity.html:8 web/templates/home.html:78 @@ -938,8 +960,8 @@ msgstr "" #: web/templates/home.html:21 msgid "" -"Connect your Plex account, choose your servers, and let Themerr take care" -" of the music." +"Connect Plex or Jellyfin, choose your servers, and let Themerr take care " +"of the music." msgstr "" #: web/templates/home.html:21 @@ -1050,12 +1072,13 @@ msgstr "" #: web/templates/home.html:57 web/templates/theme_player.html:2 msgid "" -"Unable to play this theme. Check the Plex connection and browser audio " +"Unable to play this theme. Check the server connection and browser audio " "support." msgstr "" #: web/templates/home.html:64 -msgid "Open in Plex" +#, python-format +msgid "Open in %(server)s" msgstr "" #: web/templates/home.html:68 web/templates/home.html:76 @@ -1277,7 +1300,7 @@ msgid "ONE WORKSPACE. EVERY SERVER." msgstr "" #: web/templates/servers.html:3 -msgid "Connect your Plex account, then choose where the music goes." +msgid "Connect Plex or Jellyfin, then choose where the music goes." msgstr "" #: web/templates/servers.html:3 @@ -1324,6 +1347,21 @@ msgstr "" msgid "Enabled" msgstr "" +#: web/templates/servers.html:16 +msgid "Themerr address reachable from Jellyfin" +msgstr "" + +#: web/templates/servers.html:16 +msgid "" +"Install the connector bundled with this Themerr build, then restart " +"Jellyfin. Use a trusted HTTPS certificate or an HTTP address on your " +"private network." +msgstr "" + +#: web/templates/servers.html:16 +msgid "Install matching connector" +msgstr "" + #: web/templates/servers.html:16 msgid "Last library refresh" msgstr "" @@ -1385,7 +1423,7 @@ msgid "Your first server starts here." msgstr "" #: web/templates/servers.html:40 -msgid "Link Plex above, then discover or add an address below." +msgid "Link Plex above or add a Jellyfin server below." msgstr "" #: web/templates/servers.html:43 @@ -1435,6 +1473,34 @@ msgid "" " must have access. Adding an existing server updates its address." msgstr "" +#: web/templates/servers.html:52 +msgid "Add Jellyfin" +msgstr "" + +#: web/templates/servers.html:52 +msgid "" +"Jellyfin 10.11 and 12.1 are supported. Create an administrator API key in" +" Jellyfin Dashboard - API Keys." +msgstr "" + +#: web/templates/servers.html:54 +msgid "Jellyfin server address" +msgstr "" + +#: web/templates/servers.html:56 +msgid "Jellyfin API key" +msgstr "" + +#: web/templates/servers.html:58 +msgid "" +"Keys stay in the secure credential store. After connecting, install the " +"matching connector on the server card above." +msgstr "" + +#: web/templates/servers.html:59 +msgid "Connect Jellyfin" +msgstr "" + #: web/templates/theme_player.html:1 msgid "Theme player" msgstr "" diff --git a/scripts/build.py b/scripts/build.py index 0bad228e..c10b4079 100644 --- a/scripts/build.py +++ b/scripts/build.py @@ -8,11 +8,17 @@ from pathlib import Path import shutil import sys +import runpy # lib imports import PyInstaller.__main__ +def build_connector(): + """Compile all supported Jellyfin ABIs before collecting package resources.""" + runpy.run_path(str(Path(__file__).with_name('build_connector.py')))['build']() + + def build(): """Sets arguments for pyinstaller, creates spec, and builds binaries.""" release_version = os.environ.get('THEMERR_VERSION') @@ -22,6 +28,7 @@ def build(): f'"""Release identity stamped by the release setup action."""\n\nVERSION = {release_version!r}\n', encoding='utf-8', ) + build_connector() deno = shutil.which('deno') if deno is None: executable = 'deno.exe' if sys.platform == 'win32' else 'deno' @@ -43,6 +50,7 @@ def build(): f'--add-data=web{os.pathsep}web', f'--add-data=locale{os.pathsep}locale', f'--add-data=src/themerr/migrations{os.pathsep}themerr/migrations', + f'--add-data=jellyfin-connector{os.pathsep}jellyfin-connector', f'--add-binary={deno}{os.pathsep}.', '--icon=./web/images/favicon.ico' ] diff --git a/scripts/build_connector.py b/scripts/build_connector.py new file mode 100644 index 00000000..5c1126aa --- /dev/null +++ b/scripts/build_connector.py @@ -0,0 +1,87 @@ +"""Build the portable Jellyfin connector artifacts bundled with this Themerr build.""" + +import argparse +from datetime import datetime, timezone +import hashlib +import json +import os +from pathlib import Path +import runpy +import subprocess +import tempfile +import zipfile + +ROOT = Path(__file__).resolve().parents[1] +PROFILES = {'10.11': ('10.11.0', 'net9.0'), '12.1': ('12.1.0', 'net10.0')} +PLUGIN_ID = 'f9a117dc-b44a-4507-9706-241837784369' +PLUGIN_NAME = 'Themerr Connector' + + +def build_identity(root=ROOT, version=None): + """Hash connector sources, compatibility profiles, and the Themerr release identity.""" + version = version or runpy.run_path(str(root / 'src/common/version.py'))['VERSION'] + digest = hashlib.sha256(version.encode()) + digest.update(json.dumps(PROFILES, sort_keys=True).encode()) + for path in sorted((root / 'connectors/jellyfin').glob('*')): + if path.suffix in ('.cs', '.csproj'): + digest.update(path.name.encode()) + digest.update(path.read_bytes().replace(b'\r\n', b'\n')) + return digest.hexdigest() + + +def assembly_version(version): + """Map timestamp release tags to four valid monotonic .NET version components.""" + if version == '0.0.0': + now = datetime.now(timezone.utc) + return f'{now.year}.{now.month * 100 + now.day}.{now.hour * 100 + now.minute}.{now.second}' + parts = version.split('.') + if len(parts) == 3 and len(parts[2]) == 6: + parts = [parts[0], parts[1], str(int(parts[2][:4])), str(int(parts[2][4:]))] + parts += ['0'] * (4 - len(parts)) + if len(parts) != 4 or any(not p.isascii() or not p.isdigit() or int(p) > 65535 for p in parts): + raise ValueError('Themerr release version must map to a valid four-part .NET version.') + return '.'.join(str(int(p)) for p in parts) + + +def build(dotnet='dotnet', root=ROOT): + """Compile every supported ABI and write fixed archive names and their checksums.""" + release = os.environ.get('THEMERR_VERSION') or runpy.run_path(str(root / 'src/common/version.py'))['VERSION'] + if os.environ.get('THEMERR_VERSION'): + (root / 'src/common/version.py').write_text( + f'"""Release identity stamped by the release setup action."""\n\nVERSION = {release!r}\n', encoding='utf-8') + identity = build_identity(root, release) + base_version = assembly_version(release) + directory = root / 'jellyfin-connector' + directory.mkdir(exist_ok=True) + artifacts = {} + for index, (profile, (abi, framework)) in enumerate(PROFILES.items()): + parts = base_version.split('.') + parts[-1] = str(int(parts[-1]) * len(PROFILES) + index) + version = '.'.join(parts) + if int(parts[-1]) > 65535: + raise ValueError('Release revision is too large for the connector compatibility matrix.') + with tempfile.TemporaryDirectory(prefix='themerr-connector-') as temp: + output = Path(temp) / 'out' + subprocess.run([ + dotnet, 'build', str(root / 'connectors/jellyfin/Themerr.Connector.csproj'), + '--configuration', 'Release', '--output', str(output), + '--configfile', str(root / 'connectors/jellyfin/NuGet.Config'), + f'-p:ConnectorFramework={framework}', f'-p:JellyfinVersion={abi}', + f'-p:ConnectorBuild={identity}', f'-p:AssemblyVersion={version}', + f'-p:BaseIntermediateOutputPath={Path(temp) / "obj"}{os.sep}', + ], check=True) + archive = directory / f'connector-{profile}.zip' + with zipfile.ZipFile(archive, 'w', compression=zipfile.ZIP_DEFLATED) as bundle: + bundle.write(output / 'Themerr.Connector.dll', 'Themerr.Connector.dll') + artifacts[profile] = {'targetAbi': abi, 'version': version, + 'timestamp': datetime.now(timezone.utc).isoformat(), + 'checksum': hashlib.md5(archive.read_bytes(), usedforsecurity=False).hexdigest()} + descriptor = {'build': identity, 'themerrVersion': release, 'protocol': 1, 'artifacts': artifacts} + (directory / 'bundle.json').write_text(json.dumps(descriptor, indent=2) + '\n', encoding='utf-8') + return descriptor + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--dotnet', default='dotnet') + build(parser.parse_args().dotnet) diff --git a/src/common/admin.py b/src/common/admin.py index c388d513..ea1e7fcb 100644 --- a/src/common/admin.py +++ b/src/common/admin.py @@ -303,6 +303,9 @@ def _require_admin(request: Request): Login response, or no response when access is permitted. """ path = request.url.path + from jellyfin.connector import PUBLIC_PATHS + if request.method in ('GET', 'HEAD') and path in PUBLIC_PATHS: + return None if path in ('/login', '/setup', '/favicon.ico', '/status') or path.startswith(('/web/assets/', '/images/')): return None current = account() diff --git a/src/common/api_docs.py b/src/common/api_docs.py index fb599dbd..fcf998cb 100644 --- a/src/common/api_docs.py +++ b/src/common/api_docs.py @@ -11,6 +11,10 @@ _STRING = {'type': 'string'} _ADD_SERVER = 'server_ui.add_server' _BODIES = { + 'jellyfin.add_server': ({'url': _STRING, 'api_key': {'type': 'string', 'format': 'password'}}, + ['url', 'api_key'], {'url': 'http://192.168.1.10:8096'}), + 'jellyfin.install_connector': ({'themerr_url': _STRING}, ['themerr_url'], + {'themerr_url': 'http://192.168.1.20:9494'}), 'browse_directories': ({'path': _STRING}, [], {'path': ''}), 'server_ui.discover': ({'source': {'type': 'string', 'enum': ['account', 'local']}}, ['source'], {'source': 'account'}), @@ -66,9 +70,10 @@ def _describe_operation(route: APIRoute, operation: dict) -> None: """Add browser authentication, request bodies, and response metadata.""" method = next(iter(route.methods)).lower() operation['responses']['200']['content'] = {'application/json': {'schema': {'type': 'object'}}} - if route.name in (_ADD_SERVER, 'server_ui.refresh'): - status = '201' if route.name == _ADD_SERVER else '202' - operation['responses'][status] = operation['responses'].pop('200') + statuses = {_ADD_SERVER: '201', 'jellyfin.add_server': '201', + 'server_ui.refresh': '202', 'jellyfin.install_connector': '202'} + if route.name in statuses: + operation['responses'][statuses[route.name]] = operation['responses'].pop('200') operation['tags'] = [route.path.split('/')[2].title() if route.path.startswith('/api/') else 'Status'] if route.path != '/status': operation['security'] = [{'BrowserSession': []}] @@ -93,12 +98,12 @@ def _describe_operation(route: APIRoute, operation: dict) -> None: operation['responses']['416'] = {'description': 'Requested audio range is unavailable.'} if route.name.startswith('theme_poster'): operation['responses']['200'] = { - 'description': 'Plex item poster.', + 'description': 'Media-server item poster.', 'content': {kind: {'schema': {'type': 'string', 'format': 'binary'}} for kind in ('image/jpeg', 'image/png', 'image/webp', 'image/gif')}, } operation['responses']['404'] = {'description': 'Poster is unavailable.'} - operation['responses']['502'] = {'description': 'Unable to load a bounded poster from Plex.'} + operation['responses']['502'] = {'description': 'Unable to load a bounded poster from the media server.'} def schema(routes: list) -> dict: diff --git a/src/common/config.py b/src/common/config.py index af966323..61c522a3 100644 --- a/src/common/config.py +++ b/src/common/config.py @@ -233,6 +233,24 @@ def on_change_plex_url() -> None: 'default': False, }, }, + 'Jellyfin': { + 'type': 'section', + 'name': _('Jellyfin'), + 'description': _('Jellyfin theme processing settings. Connections are managed on the Servers page.'), + 'icon': 'server', + 'BOOL_MOVIE_SUPPORT': { + 'type': 'boolean', 'name': _('Movie support'), 'default': True, + 'description': _('Add themes to Jellyfin movies.'), + }, + 'BOOL_SERIES_SUPPORT': { + 'type': 'boolean', 'name': _('Series support'), 'default': True, + 'description': _('Add themes to Jellyfin series.'), + }, + 'BOOL_COLLECTION_SUPPORT': { + 'type': 'boolean', 'name': _('Collections'), 'default': True, + 'description': _('Add themes to Jellyfin collections.'), + }, + }, 'Plex': { 'type': 'section', 'name': _('Plex'), diff --git a/src/common/credentials.py b/src/common/credentials.py new file mode 100644 index 00000000..c0056ae8 --- /dev/null +++ b/src/common/credentials.py @@ -0,0 +1,120 @@ +"""Store media-server tokens outside plaintext application data.""" + +# standard imports +import os +from pathlib import Path + +# lib imports +from cryptography.fernet import Fernet, InvalidToken +import keyring +from keyring.errors import PasswordDeleteError + +# local imports +from common.definitions import Names +from themerr import storage + + +SERVICE = Names.name +KEY_FILE_ENV = 'THEMERR_PLEX_TOKEN_KEY_FILE' +STORE_UNAVAILABLE = 'The OS credential store is unavailable.' + + +class TokenStorageError(OSError): + """The configured secure token store cannot be used.""" + + +def _cipher() -> Fernet | None: + """Return encryption from an external key file, or use the desktop vault. + + Returns + ------- + Fernet or None + Cipher for an external key, or ``None`` for the OS credential store. + + Raises + ------ + TokenStorageError + The key is missing or invalid in a headless installation. + """ + key_path = os.environ.get(KEY_FILE_ENV) + if key_path: + try: + return Fernet(Path(key_path).read_bytes().strip()) + except (OSError, ValueError) as exc: + raise TokenStorageError(f'Unable to read a valid Plex token key from {KEY_FILE_ENV}.') from exc + if os.environ.get('THEMERR_DOCKER'): + raise TokenStorageError(f'Set {KEY_FILE_ENV} to a mounted secret before signing in to Plex.') + return None + + +def get_token(client_id: str) -> str: + """Read a token from the selected secure store. + + Parameters + ---------- + client_id : str + Installation-scoped credential identifier. + + Returns + ------- + str + Stored token, or an empty string when not signed in. + """ + cipher = _cipher() + if cipher is not None: + encrypted = storage.get_encrypted_token(_namespace(client_id)) + if not encrypted: + return '' + try: + return cipher.decrypt(encrypted.encode('ascii')).decode('utf-8') + except (InvalidToken, UnicodeError) as exc: + raise TokenStorageError('The Plex token key does not match the stored token.') from exc + try: + return keyring.get_password(SERVICE, client_id) or '' + # Native backends can raise errors outside keyring's exception hierarchy. + except Exception as exc: + raise TokenStorageError(STORE_UNAVAILABLE) from exc + + +def save_token(client_id: str, token: str) -> None: + """Persist a server token without storing plaintext in SQLite. + + Parameters + ---------- + client_id : str + Installation-scoped credential identifier. + token : str + Plex account token. + """ + cipher = _cipher() + if cipher is not None: + storage.save_encrypted_token(cipher.encrypt(token.encode('utf-8')).decode('ascii'), _namespace(client_id)) + return + try: + keyring.set_password(SERVICE, client_id, token) + except Exception as exc: + raise TokenStorageError(STORE_UNAVAILABLE) from exc + + +def delete_token(client_id: str) -> None: + """Remove this installation's saved server token. + + Parameters + ---------- + client_id : str + Installation-scoped credential identifier. + """ + storage.save_encrypted_token(None, _namespace(client_id)) + if os.environ.get(KEY_FILE_ENV) or os.environ.get('THEMERR_DOCKER'): + return + try: + keyring.delete_password(SERVICE, client_id) + except PasswordDeleteError: + pass + except Exception as exc: + raise TokenStorageError(STORE_UNAVAILABLE) from exc + + +def _namespace(client_id: str) -> str: + """Choose a separate encrypted slot for a server credential.""" + return ':' + client_id if client_id.startswith(('server:', 'jellyfin:')) else '' diff --git a/src/common/helpers.py b/src/common/helpers.py index fcf71942..2a4430aa 100644 --- a/src/common/helpers.py +++ b/src/common/helpers.py @@ -236,7 +236,9 @@ def get_logger(name: str) -> logging.Logger: >>> get_logger(name='my_log') """ - if name.split('.')[0] in ('common', 'media_servers', 'plex', 'themerr', 'youtube', 'themerr_plex', 'schedule'): + if name.split('.')[0] in ( + 'common', 'media_servers', 'jellyfin', 'plex', 'themerr', 'youtube', 'themerr_plex', 'schedule', + ): name = 'themerr' return logging.getLogger(name=name) diff --git a/src/common/server_ui.py b/src/common/server_ui.py index 0b48704c..145c4ffd 100644 --- a/src/common/server_ui.py +++ b/src/common/server_ui.py @@ -105,6 +105,7 @@ def dashboard() -> tuple[dict, dict, dict]: identity = record['id'] + ':' + key urls = server.web_urls(str(section['key'])) libraries[identity] = {**section, 'server_id': record['id'], 'server_name': record['name'], + 'server_label': backend.display_name(record['id']), 'enabled': record['enabled'], 'server_url': urls['server'], 'library_url': urls['library']} for item in section['items']: @@ -207,7 +208,7 @@ def edit_server(request: Request, server_id: str, payload=Depends(read_json)): except MediaServerError as exc: return _failure(exc, str(exc), exc.status_code) except Exception as exc: - return _failure(exc, f'Could not update this {get_backend().name} server. ' + return _failure(exc, f'Could not update this {get_backend().display_name(server_id)} server. ' 'Check its connection and settings.', 500) get_backend().start_listeners() return JSONResponse({'message': 'Server removed.' if request.method == 'DELETE' else 'Server settings saved.'}) @@ -275,9 +276,9 @@ def activity(request: Request): from themerr import scheduled_tasks libraries, _, _ = dashboard() failures = [{ - 'server': section['server_name'], 'library': section['title'], **item, + 'server': section['server_name'], 'server_label': section['server_label'], 'library': section['title'], **item, 'reason': item['error'] or ( - f'TMDB ID unavailable. Review the item metadata in {get_backend().name}.' + f'TMDB ID unavailable. Review the item metadata in {get_backend().display_name(item["server_id"])}.' if item['theme_status'] == 'unresolved' else 'The theme could not be added.' ), } for section in libraries.values() for item in section['items'] diff --git a/src/common/webapp.py b/src/common/webapp.py index b24d1381..e19f44b4 100644 --- a/src/common/webapp.py +++ b/src/common/webapp.py @@ -169,11 +169,9 @@ def home(request: Request) -> Response: ) -@router.api_route('/api/themes/{rating_key:int}/poster', methods=['GET', 'HEAD'], - name='theme_poster_default', response_model=None) -@router.api_route('/api/servers/{server_id}/themes/{rating_key:int}/poster', methods=['GET', 'HEAD'], +@router.api_route('/api/servers/{server_id}/themes/{rating_key}/poster', methods=['GET', 'HEAD'], name='theme_poster', response_model=None) -def theme_poster(request: Request, rating_key: int, server_id: str = 'default') -> Response: +def theme_poster(request: Request, rating_key: str, server_id: str = 'default') -> Response: """Serve a bounded media-server poster without exposing server credentials. Resolve artwork from the saved server's current item metadata. Only raster images @@ -183,7 +181,7 @@ def theme_poster(request: Request, rating_key: int, server_id: str = 'default') ---------- request : Request Authenticated browser request. - rating_key : int + rating_key : str Media-server item identifier. server_id : str, optional Saved server identifier. @@ -226,14 +224,41 @@ def theme_poster(request: Request, rating_key: int, server_id: str = 'default') return Response(status_code=502) -async def _stream_theme_audio(upstream: requests.Response, rating_key: int): +@router.api_route('/api/themes/{rating_key:int}/poster', methods=['GET', 'HEAD'], + name='theme_poster_default', response_model=None) +def theme_poster_default(request: Request, rating_key: int) -> Response: + """Serve posters through the legacy integer Plex route. + + Convert the legacy path parameter before using the shared poster handler. + + Parameters + ---------- + request : Request + Incoming browser request. + rating_key : int + Plex item identifier. + + Returns + ------- + Response + Bounded poster or a sanitized error. + + Examples + -------- + >>> theme_poster_default(request, 42) + + """ + return theme_poster(request, str(rating_key)) + + +async def _stream_theme_audio(upstream: requests.Response, rating_key: str): """Stream theme audio and release the connection when playback stops. Parameters ---------- upstream : requests.Response Open streaming response from the media server. - rating_key : int + rating_key : str Media-server item identifier for diagnostic logging. Yields @@ -261,7 +286,7 @@ class ThemeAudioResponse(StreamingResponse): ---------- upstream : requests.Response Open audio response from the media server. - rating_key : int + rating_key : str Item identifier used for playback diagnostics. headers : dict Validated media headers for the browser response. @@ -271,7 +296,7 @@ class ThemeAudioResponse(StreamingResponse): >>> response = ThemeAudioResponse(upstream, 42, {'Content-Type': 'audio/mpeg'}) """ - def __init__(self, upstream: requests.Response, rating_key: int, headers: dict): + def __init__(self, upstream: requests.Response, rating_key: str, headers: dict): """Stream the open upstream response without buffering its audio.""" self.upstream = upstream super().__init__(_stream_theme_audio(upstream, rating_key), status_code=upstream.status_code, @@ -305,18 +330,12 @@ async def __call__(self, scope, receive, send): @router.api_route( - '/api/themes/{rating_key:int}', - methods=['GET', 'HEAD'], - name='play_theme_default', - response_model=None, -) -@router.api_route( - '/api/servers/{server_id}/themes/{rating_key:int}', + '/api/servers/{server_id}/themes/{rating_key}', methods=['GET', 'HEAD'], name='play_theme', response_model=None, ) -def play_theme(request: Request, rating_key: int, server_id: str = 'default') -> Response: +def play_theme(request: Request, rating_key: str, server_id: str = 'default') -> Response: """Serve the item's current server theme without exposing server credentials. Resolve the selected audio from fresh server metadata and forward byte range requests @@ -326,7 +345,7 @@ def play_theme(request: Request, rating_key: int, server_id: str = 'default') -> ---------- request : Request Incoming browser or API request. - rating_key : int + rating_key : str Item whose selected theme should be played, regardless of its provider. server_id : str Server identifier. @@ -353,7 +372,34 @@ def play_theme(request: Request, rating_key: int, server_id: str = 'default') -> return _theme_audio_response(request, upstream, rating_key) -def _theme_audio_response(request: Request, upstream: requests.Response, rating_key: int) -> Response: +@router.api_route('/api/themes/{rating_key:int}', methods=['GET', 'HEAD'], + name='play_theme_default', response_model=None) +def play_theme_default(request: Request, rating_key: int) -> Response: + """Serve audio through the legacy integer Plex route. + + Convert the legacy path parameter before using the shared streaming handler. + + Parameters + ---------- + request : Request + Incoming browser request. + rating_key : int + Plex item identifier. + + Returns + ------- + Response + Audio stream or a sanitized playback error. + + Examples + -------- + >>> play_theme_default(request, 42) + + """ + return play_theme(request, str(rating_key)) + + +def _theme_audio_response(request: Request, upstream: requests.Response, rating_key: str) -> Response: """Build a browser response and close rejected theme audio streams. Parameters @@ -362,7 +408,7 @@ def _theme_audio_response(request: Request, upstream: requests.Response, rating_ Incoming browser or API request. upstream : requests.Response Stream returned by the media server. - rating_key : int + rating_key : str Media-server item identifier for playback diagnostics. Returns @@ -382,11 +428,13 @@ def _theme_audio_response(request: Request, upstream: requests.Response, rating_ status = 404 if upstream.status_code == 404 else 502 log.warning('Media server rejected theme playback for rating_key=%s (HTTP %s)', rating_key, upstream.status_code) - return JSONResponse({'message': f'{get_backend().name} could not provide theme audio.'}, status_code=status) + name = get_backend().display_name(request.path_params.get('server_id', 'default')) + return JSONResponse({'message': f'{name} could not provide theme audio.'}, status_code=status) content_type = upstream.headers.get('Content-Type', 'application/octet-stream').split(';', 1)[0].lower() if not content_type.startswith('audio/') and content_type not in ('video/mp4', 'application/octet-stream'): upstream.close() - return JSONResponse({'message': f'{get_backend().name} returned an unsupported audio format.'}, + name = get_backend().display_name(request.path_params.get('server_id', 'default')) + return JSONResponse({'message': f'{name} returned an unsupported audio format.'}, status_code=502) headers['Content-Type'] = content_type if request.method == 'HEAD': diff --git a/src/jellyfin/__init__.py b/src/jellyfin/__init__.py new file mode 100644 index 00000000..1201ed82 --- /dev/null +++ b/src/jellyfin/__init__.py @@ -0,0 +1 @@ +"""Jellyfin integration and its bundled theme-upload connector.""" diff --git a/src/jellyfin/audio.py b/src/jellyfin/audio.py new file mode 100644 index 00000000..d09d58e1 --- /dev/null +++ b/src/jellyfin/audio.py @@ -0,0 +1,43 @@ +"""Prepare lossless container changes for Jellyfin's local theme-file formats.""" + +from contextlib import contextmanager +from dataclasses import replace +import hashlib +from pathlib import Path +import tempfile + +import av + +from youtube.youtube_dl import validate_audio + + +@contextmanager +def theme_file(audio): + """Remux WebM Opus into Ogg without re-encoding and verify the resulting complete audio. + + Parameters + ---------- + audio : AudioFile + Downloaded and validated YouTube audio. + + Yields + ------ + AudioFile + Validated MP4 AAC or Ogg Opus, removed when the upload finishes. + """ + if audio.codec == 'mp4a': + yield audio + return + with tempfile.TemporaryDirectory(prefix='themerr-jellyfin-audio-') as directory: + path = Path(directory) / 'theme.opus' + with av.open(audio.path) as source, av.open(str(path), 'w', format='ogg') as target: + stream = source.streams.audio[0] + output = target.add_stream_from_template(stream) + for packet in source.demux(stream): + if packet.dts is not None: + packet.stream = output + target.mux(packet) + duration = validate_audio(str(path), audio.duration, 'opus') + with path.open('rb') as stream: + digest = hashlib.file_digest(stream, 'sha256').hexdigest() + yield replace(audio, path=str(path), sha256=digest, size=path.stat().st_size, duration=duration) diff --git a/src/jellyfin/backend.py b/src/jellyfin/backend.py new file mode 100644 index 00000000..67e1b05b --- /dev/null +++ b/src/jellyfin/backend.py @@ -0,0 +1,247 @@ +"""Jellyfin implementations of shared dashboard, scanning, upload, and streaming contracts.""" + +from urllib.parse import urlencode + +from common import config, helpers, logger +from jellyfin import connector, metadata, servers +from jellyfin.audio import theme_file +from jellyfin.client import identifier +from media_servers.base import MediaServer, MediaServerBackend, MediaServerError +from themerr import storage, theme_errors +from youtube.youtube_dl import download_youtube + +log = logger.get_logger(__name__) +_SETTINGS = {'Movie': 'BOOL_MOVIE_SUPPORT', 'Series': 'BOOL_SERIES_SUPPORT', 'BoxSet': 'BOOL_COLLECTION_SUPPORT'} + + +def _items(connection, **params): + """Iterate complete bounded pages, including libraries larger than one API page.""" + offset = 0 + while True: + page = connection.json('GET', '/Items', params={ + 'Recursive': True, 'IncludeItemTypes': 'Movie,Series,BoxSet', 'Fields': 'ProviderIds,LockData', + 'StartIndex': offset, 'Limit': 200, **params, + }) + rows = page['Items'] + yield from rows + offset += len(rows) + if not rows or offset >= page['TotalRecordCount']: + break + + +def _eligible(item): + """Honor Jellyfin category settings and the shared locked-metadata preference.""" + setting = _SETTINGS.get(item.get('Type')) + return bool(setting and config.CONFIG['Jellyfin'][setting] and + (not item.get('IsLocked') or config.CONFIG['Themerr']['BOOL_IGNORE_LOCKED_FIELDS'])) + + +class JellyfinMediaServer(MediaServer): + """Apply native Jellyfin operations within an independent storage scope.""" + + def libraries(self): + connection = servers.client(self.server_id) + return [{'id': identifier(row['ItemId']), 'title': row['Name']} + for row in connection.json('GET', '/Library/VirtualFolders')] + + def web_urls(self, library_id, item_id=None): + record = servers.get_server(self.server_id) + url = record['url'] + '/web/index.html#!/' + library_query = urlencode({'topParentId': identifier(library_id), + 'serverId': self.server_id.removeprefix('jellyfin:')}) + urls = {'server': url + 'home.html', 'library': url + 'movies.html?' + library_query} + if item_id is not None: + urls['item'] = url + 'details?' + urlencode( + {'id': identifier(item_id), 'serverId': self.server_id.removeprefix('jellyfin:')}) + return urls + + def cache_dashboard(self): + with storage.server_scope(self.server_id): + connection = servers.client(self.server_id) + revision = storage.dashboard_revision() + snapshots = {} + errors = storage.get_errors() + try: + connector.verify(connection) + connected = True + except MediaServerError: + connected = False + for library in connection.json('GET', '/Library/VirtualFolders'): + key = identifier(library['ItemId']) + themed = {identifier(row['Id']) for row in _items(connection, ParentId=key, HasThemeSong=True)} + rows = [self._dashboard_item(item, themed, errors, connection if connected else None) + for item in _items(connection, ParentId=key)] + media = [row for row in rows if row['type'] != 'collection'] + collections = [row for row in rows if row['type'] == 'collection'] + snapshots[key] = {'key': key, 'title': library['Name'], 'agent': 'jellyfin', + 'type': {'tvshows': 'show', 'boxsets': 'collection'}.get( + library.get('CollectionType'), 'movie'), 'items': rows, + 'media_count': len(media), 'media_percent_complete': self._percent(media), + 'collection_count': len(collections), + 'collection_percent_complete': self._percent(collections), + 'collections_enabled': config.CONFIG['Jellyfin']['BOOL_COLLECTION_SUPPORT'], + 'total_count': len(rows)} + storage.replace_dashboard(snapshots, since_revision=revision) + return True + + @staticmethod + def _percent(rows): + """Calculate installed-theme coverage for one category.""" + return int(sum(row['theme'] for row in rows) / len(rows) * 100) if rows else 0 + + @staticmethod + def _dashboard_item(item, themed, errors, connection): + """Map native metadata to the existing dashboard dictionary.""" + details = metadata.resolve(item) + key = identifier(item['Id']) + theme = key in themed + status = 'complete' if theme else 'failed' if key in errors else 'unresolved' + if not theme and key not in errors and details['database_id']: + status = 'pending' if details['exists'] else 'missing' + owned = False + if theme and connection is not None: + owned = connection.json('GET', '/Themerr/Items/' + key + '/Theme')['owned'] + return {**{name: value for name, value in details.items() if name != 'exists'}, 'rating_key': key, + 'title': item['Name'], 'year': item.get('ProductionYear'), 'agent': 'jellyfin', 'theme': theme, + 'theme_provider': 'themerr' if owned else 'uploaded' if theme else None, + 'theme_status': status} + + def scan(self, enqueue): + with storage.server_scope(self.server_id): + connection = servers.client(self.server_id) + connector.verify(connection) + ignored = set(servers.get_server(self.server_id)['ignored_libraries'].split(',')) + for library in connection.json('GET', '/Library/VirtualFolders'): + key = identifier(library['ItemId']) + if key in ignored: + continue + for item in _items(connection, ParentId=key): + if _eligible(item): + enqueue(identifier(item['Id'])) + + def update_item(self, item_id): + with storage.server_scope(self.server_id): + item_id = identifier(item_id) + if not config.CONFIG['Themerr']['BOOL_THEMERR_ENABLED']: + return False + try: + return self._update(item_id) + except MediaServerError as exc: + theme_errors.set_error(item_id, str(exc)) + return False + + def _update(self, item_id): + """Resolve a theme and upload verified audio only when native ownership permits it.""" + connection = servers.client(self.server_id) + descriptor = connector.verify(connection) + item = connection.json('GET', '/Items/' + item_id, params={'Fields': 'ProviderIds,LockData'}) + if not _eligible(item): + return False + ignored = set(servers.get_server(self.server_id)['ignored_libraries'].split(',')) - {''} + if ignored: + ancestors = connection.json('GET', '/Items/' + item_id + '/Ancestors') + if ignored.intersection(identifier(row['Id']) for row in ancestors): + return False + state = connection.json('GET', '/Themerr/Items/' + item_id + '/Theme') + if state['present'] and not state['owned']: + theme_errors.set_error(item_id, None) + return True + details = metadata.resolve(item) + if not details['exists']: + theme_errors.set_error(item_id, None) + return False + data = helpers.json_get(cache_time=3600, url='https://app.lizardbyte.dev/ThemerrDB/' + + f'{details["database_type"]}/{details["database"]}/{details["database_id"]}.json') + source = data.get('youtube_theme_url') if data else None + if not source: + return False + tracked = storage.get_tracking(item_id) + if self._unchanged(source, state, tracked): + theme_errors.set_error(item_id, None) + return True + return self._upload(connection, item_id, item, source, descriptor) + + @staticmethod + def _unchanged(source, state, tracked): + """Require current bytes to match tracking before skipping a source or codec update.""" + return bool(state['owned'] and state.get('sha256') == tracked.get('audio_sha256') and + source == tracked.get('youtube_theme_url') and + (not config.CONFIG['Themerr']['BOOL_PREFER_MP4A_CODEC'] or + tracked.get('audio_codec') == 'mp4a' or tracked.get('mp4a_available') is False)) + + @staticmethod + def _upload(connection, item_id, item, source, descriptor): + """Send validated bytes and persist tracking only after verifying the connector's digest.""" + with download_youtube(source, on_error=lambda reason: theme_errors.set_error(item_id, reason)) as downloaded: + if downloaded is None: + return False + with theme_file(downloaded) as audio: + return JellyfinMediaServer._send_audio(connection, item_id, item, source, descriptor, audio) + + @staticmethod + def _send_audio(connection, item_id, item, source, descriptor, audio): + """Verify acknowledged bytes before recording a successful upload.""" + headers = {'Content-Type': 'audio/mp4' if audio.codec == 'mp4a' else 'audio/ogg', + 'X-Themerr-Connector': descriptor['build'], 'X-Themerr-SHA256': audio.sha256, + 'X-Themerr-Ignore-Locked': str(config.CONFIG['Themerr']['BOOL_IGNORE_LOCKED_FIELDS']).lower()} + with open(audio.path, 'rb') as stream: + result = connection.json('POST', '/Themerr/Items/' + item_id + '/Theme', + data=stream, headers=headers, timeout=120) + if not result.get('owned') or result.get('sha256') != audio.sha256: + raise MediaServerError('Jellyfin could not verify the uploaded theme.', 502) + storage.save_tracking(item_id, metadata.TYPES[item['Type']][0], { + 'youtube_theme_url': source, 'audio_codec': audio.codec, + 'mp4a_available': audio.mp4a_available, 'audio_sha256': audio.sha256, + }) + storage.mark_dashboard_theme_uploaded(item_id, 'themerr') + theme_errors.set_error(item_id, None) + return True + + def open_poster(self, item_id): + item_id = identifier(item_id) + connection = servers.client(self.server_id) + try: + return connection.request('GET', '/Items/' + identifier(item_id) + '/Images/Primary', + params={'MaxWidth': 600}, stream=True) + except MediaServerError as exc: + if exc.status_code == 404: + return None + raise + + def open_theme(self, item_id, headers): + item_id = identifier(item_id) + connection = servers.client(self.server_id) + songs = connection.json('GET', '/Items/' + identifier(item_id) + '/ThemeSongs', + params={'InheritFromParent': False})['Items'] + if not songs: + raise MediaServerError('This item has no theme.', 404) + return connection.request('GET', '/Audio/' + identifier(songs[0]['Id']) + '/stream', + params={'Static': True}, headers=dict(headers), stream=True) + + +class JellyfinBackend(MediaServerBackend): + """Manage Jellyfin connections and use shared scheduled scans for theme updates.""" + + name = 'Jellyfin' + + def server(self, server_id): + return JellyfinMediaServer(server_id) + + list_servers = staticmethod(servers.list_servers) + get_server = staticmethod(servers.get_server) + update_server = staticmethod(servers.update_server) + remove_server = staticmethod(servers.remove_server) + record_refresh = staticmethod(servers.record_refresh) + + def account_connected(self): + return False + + def start_listeners(self): + """Jellyfin uses shared scheduled scans; no separate listener is required.""" + + def stop_listeners(self): + """Shared workers own the Jellyfin scan lifecycle.""" + + def web_router(self): + from jellyfin.web import router + return router diff --git a/src/jellyfin/client.py b/src/jellyfin/client.py new file mode 100644 index 00000000..0883d136 --- /dev/null +++ b/src/jellyfin/client.py @@ -0,0 +1,104 @@ +"""Credentialed Jellyfin REST access with fixed routes and opaque UUIDs.""" + +from urllib.parse import quote, urlsplit +from uuid import UUID + +import requests + +from media_servers.base import MediaServerError + + +def identifier(value: str) -> str: + """Normalize a native Jellyfin UUID before constructing an API route. + + Parameters + ---------- + value : str + Native item, library, or server identifier. + + Returns + ------- + str + Hexadecimal UUID without separators. + """ + try: + return UUID(str(value)).hex + except (ValueError, TypeError, AttributeError) as exc: + raise MediaServerError('Invalid Jellyfin identifier.', 404) from exc + + +def base_url(value: str) -> str: + """Validate an administrator-configured HTTP base URL, including a proxy prefix. + + Parameters + ---------- + value : str + Server address without credentials, query parameters, or fragments. + + Returns + ------- + str + Address without a trailing slash. + """ + try: + parts = urlsplit(value) + valid = (parts.scheme in ('http', 'https') and parts.hostname and not parts.username and + not parts.password and not parts.query and not parts.fragment and + all(ord(c) > 32 and c not in '\\%' for c in value) and len(value) <= 2048 and + not any(segment in ('.', '..') for segment in parts.path.split('/'))) + if not valid or (parts.port is not None and not 1 <= parts.port <= 65535): + raise ValueError + except (ValueError, TypeError, AttributeError) as exc: + raise MediaServerError('Enter a valid HTTP or HTTPS server address.', 400) from exc + return value.rstrip('/') + + +class Client: + """Keep a saved Jellyfin API key in backend-only request headers. + + Parameters + ---------- + url : str + Validated administrator-configured server URL. + token : str + Jellyfin administrator API key. + """ + + def __init__(self, url: str, token: str): + self.url = base_url(url) + self.token = token + self.server_version = None + + def request(self, method, path, **kwargs): + """Request a code-owned route and translate failures to fixed client messages.""" + headers = {'Authorization': 'MediaBrowser Token="' + quote(self.token, safe='') + '"', + **kwargs.pop('headers', {})} + try: + response = requests.request(method, self.url + path, headers=headers, allow_redirects=False, + timeout=kwargs.pop('timeout', 30), **kwargs) + except requests.RequestException as exc: + raise MediaServerError('Could not reach Jellyfin. Check its address and API key.', 502) from exc + if not 200 <= response.status_code < 300: + status = response.status_code + response.close() + message = {401: 'Jellyfin rejected the API key.', 403: 'An administrator Jellyfin API key is required.', + 404: 'This Jellyfin resource is unavailable.', + 409: 'Jellyfin protected an existing theme or rejected the connector version.'}.get( + status, 'Jellyfin could not complete this request.') + raise MediaServerError(message, status if status in (401, 403, 404, 409) else 502) + return response + + def json(self, method, path, **kwargs): + """Read JSON without retaining upstream connections or error bodies.""" + # Jellyfin 12 defaults to camelCase; pin the documented profile used by both ABIs. + kwargs['headers'] = {'Accept': 'application/json; profile="PascalCase"', **kwargs.pop('headers', {})} + response = self.request(method, path, **kwargs) + try: + data = response.json() + if path == '/System/Info' and isinstance(data, dict): + self.server_version = data.get('Version') + return data + except ValueError as exc: + raise MediaServerError('Jellyfin returned an invalid response.', 502) from exc + finally: + response.close() diff --git a/src/jellyfin/connector.py b/src/jellyfin/connector.py new file mode 100644 index 00000000..bd0a6333 --- /dev/null +++ b/src/jellyfin/connector.py @@ -0,0 +1,125 @@ +"""Serve and install only connector artifacts bundled with this Themerr release.""" + +import json +from pathlib import Path +from threading import RLock + +from sqlalchemy.orm import Session + +from common import definitions, version +from common.path_policy import resolve_file_path +from jellyfin.client import base_url +from media_servers.base import MediaServerError +from themerr import storage + +PLUGIN_ID = 'f9a117dc-b44a-4507-9706-241837784369' +PLUGIN_NAME = 'Themerr Connector' +ARCHIVES = {'10.11': 'connector-10.11.zip', '12.1': 'connector-12.1.zip'} +MANIFEST_PATH = '/jellyfin/connector/manifest.json' +PUBLIC_PATHS = frozenset([MANIFEST_PATH, *('/jellyfin/connector/' + name for name in ARCHIVES.values())]) +_install_lock = RLock() + + +def directory(): + """Locate immutable connector data in source, Docker, or PyInstaller resources.""" + return Path(definitions.Paths.ROOT_DIR) / 'jellyfin-connector' + + +def bundle(): + """Read the packaged descriptor through the shared canonical containment policy.""" + try: + path = resolve_file_path(directory(), 'bundle.json') + data = json.loads(Path(path).read_text(encoding='utf-8')) + if data['protocol'] != 1 or data['themerrVersion'] != version.VERSION: + raise ValueError + if set(data['artifacts']) != set(ARCHIVES) or len(data['build']) != 64: + raise ValueError + return data + except (OSError, ValueError, KeyError, TypeError) as exc: + raise MediaServerError('The bundled Jellyfin connector is unavailable. ' + 'Rebuild or reinstall Themerr.', 503) from exc + + +def profile(server_version): + """Select an explicitly supported Jellyfin minor version instead of guessing ABI compatibility.""" + key = '.'.join(str(server_version).split('.')[:2]) + if key not in ARCHIVES: + raise MediaServerError('This build supports Jellyfin 10.11 and 12.1.', 400) + return key + + +def repository_url(value=None): + """Save or read the administrator-selected Themerr URL reachable from Jellyfin.""" + with Session(storage.engine()) as session: + row = session.get(storage.AppSetting, 'jellyfin_repository_base_url') + if value is not None: + value = base_url(value) + if row is None: + row = storage.AppSetting(key='jellyfin_repository_base_url', value=value) + session.add(row) + else: + row.value = value + session.commit() + return value + if row is None: + raise MediaServerError('Set the Themerr address reachable from Jellyfin ' + 'before installing the connector.', 409) + return row.value + + +def manifest(): + """Advertise exactly the locally bundled versions using the configured download address.""" + data = bundle() + url = repository_url() + return [{'guid': PLUGIN_ID, 'name': PLUGIN_NAME, 'overview': 'Theme uploads for the matching Themerr build.', + 'description': 'Managed by your Themerr installation. Restart Jellyfin after installation.', + 'owner': 'LizardByte', 'category': 'General', + 'versions': [{**artifact, 'sourceUrl': url + '/jellyfin/connector/' + ARCHIVES[key], + 'changelog': 'Connector bundled with Themerr ' + data['themerrVersion']} + for key, artifact in data['artifacts'].items()]}] + + +def verify(connection): + """Require the exact connector identity and protocol before accessing theme operations.""" + expected = bundle() + artifact = expected['artifacts'][profile(connection.server_version)] + try: + actual = connection.json('GET', '/Themerr/Connector') + except MediaServerError as exc: + raise MediaServerError('Install the matching Themerr connector and restart Jellyfin.', 409) from exc + if (not isinstance(actual, dict) or actual.get('protocol') != expected['protocol'] or + actual.get('build') != expected['build'] or actual.get('targetAbi') != artifact['targetAbi']): + raise MediaServerError('Install the matching Themerr connector and restart Jellyfin.', 409) + return expected + + +def install(connection, themerr_url): + """Preserve existing repositories and request the exact bundled compatible plugin version.""" + data = bundle() + info = connection.json('GET', '/System/Info') + key = profile(info.get('Version')) + artifact = data['artifacts'][key] + url = base_url(themerr_url) + manifest_url = url + MANIFEST_PATH + with _install_lock: + try: + verify(connection) + active = True + except MediaServerError: + active = False + # Jellyfin replaces the repository list, so merge instead of overwriting it. + repositories = connection.json('GET', '/Repositories') + if not isinstance(repositories, list) or any(not isinstance(entry, dict) for entry in repositories): + raise MediaServerError('Jellyfin returned an invalid repository list.', 502) + repositories = [entry for entry in repositories if entry.get('Url') != manifest_url] + repositories.append({'Name': PLUGIN_NAME + ' (Themerr)', 'Url': manifest_url, 'Enabled': True}) + repository_url(url) + connection.request('POST', '/Repositories', json=repositories).close() + if active: + return {'message': 'Matching connector is already active.', 'version': artifact['version'], + 'restart_required': False} + connection.request('POST', '/Packages/Installed/' + PLUGIN_NAME, + params={'assemblyGuid': PLUGIN_ID, 'version': artifact['version'], + 'repositoryUrl': manifest_url}, timeout=120).close() + return {'message': 'Connector installed. Restart Jellyfin, then refresh its libraries in Themerr.', + 'version': artifact['version'], 'restart_required': True} diff --git a/src/jellyfin/metadata.py b/src/jellyfin/metadata.py new file mode 100644 index 00000000..7294017d --- /dev/null +++ b/src/jellyfin/metadata.py @@ -0,0 +1,46 @@ +"""Resolve Jellyfin provider identifiers into ThemerrDB metadata.""" + +import re +from urllib.parse import quote_plus + +from themerr import themerr_db, tmdb +from themerr.constants import issue_urls + +TYPES = {'Movie': ('movie', 'movies'), 'Series': ('show', 'tv_shows'), 'BoxSet': ('collection', 'movie_collections')} + + +def resolve(item): + """Select validated provider IDs, using shared title and external-ID fallbacks.""" + kind, database_type = TYPES.get(item.get('Type'), (None, None)) + providers = item.get('ProviderIds') or {} + tmdb_id = str(providers.get('Tmdb', '')) + if not re.fullmatch(r'[0-9]+', tmdb_id): + tmdb_id = None + imdb_id = str(providers.get('Imdb', '')) + if not re.fullmatch(r'tt[0-9]+', imdb_id): + imdb_id = None + source_database, source_id = ('themoviedb', tmdb_id) if tmdb_id else ('imdb', imdb_id) + if not tmdb_id and kind == 'movie' and imdb_id: + tmdb_id = tmdb.get_tmdb_id_from_external_id(imdb_id, 'imdb', 'movie') + if not tmdb_id and kind == 'show': + tvdb_id = str(providers.get('Tvdb', '')) + source_database, source_id = 'thetvdb', tvdb_id if re.fullmatch(r'[0-9]+', tvdb_id) else None + tmdb_id = tmdb.get_tmdb_id_from_external_id(tvdb_id, 'tvdb', 'tv', title=item.get('Name')) + if not tmdb_id and kind == 'collection': + tmdb_id = tmdb.get_tmdb_id_from_collection(item.get('Name', '')) + if tmdb_id: + database, database_id = 'themoviedb', str(tmdb_id) + elif kind == 'movie' and imdb_id: + database, database_id = 'imdb', imdb_id + else: + database, database_id = None, None + exists = bool(database_type and database and database_id and + themerr_db.item_exists(database_type, database, database_id)) + url = issue_urls.get(database_type) + title = item.get('Name', '') + if kind != 'collection' and item.get('ProductionYear'): + title += f' ({item["ProductionYear"]})' + return {'type': kind, 'database_type': database_type, 'database': database, 'database_id': database_id, + 'source_database': source_database, 'source_id': source_id, 'exists': exists, + 'issue_action': 'edit' if exists else 'add', + 'issue_url': url.format(quote_plus(title), database_id) if url and database_id else None} diff --git a/src/jellyfin/servers.py b/src/jellyfin/servers.py new file mode 100644 index 00000000..353bbdf5 --- /dev/null +++ b/src/jellyfin/servers.py @@ -0,0 +1,137 @@ +"""Persist independent Jellyfin connections without plaintext credentials.""" + +from datetime import datetime, timezone +from uuid import uuid4 +from threading import RLock + +from sqlalchemy import Boolean, String, delete, select +from sqlalchemy.orm import Mapped, Session, mapped_column + +from common import credentials, logger +from common.validation import ValidationError, ValidationMessage +from jellyfin.client import Client, base_url, identifier +from media_servers.base import MediaServerError +from themerr import storage + +_credential_lock = RLock() + + +class ServerRecord(storage.Base): + """Public Jellyfin connection settings; the API key lives in the secure store.""" + + __tablename__ = 'jellyfin_servers' + id: Mapped[str] = mapped_column(String, primary_key=True) + name: Mapped[str] = mapped_column(String, nullable=False) + url: Mapped[str] = mapped_column(String, nullable=False) + version: Mapped[str] = mapped_column(String, nullable=False) + enabled: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True) + ignored_libraries: Mapped[str] = mapped_column(String, nullable=False, default='') + last_refresh: Mapped[str | None] = mapped_column(String) + last_error: Mapped[str | None] = mapped_column(String) + + +def list_servers(enabled_only=False): + """Read saved connection settings without exposing API keys.""" + with Session(storage.engine()) as session: + query = select(ServerRecord).order_by(ServerRecord.name) + if enabled_only: + query = query.where(ServerRecord.enabled.is_(True)) + return [{c.name: getattr(row, c.name) for c in ServerRecord.__table__.columns} + for row in session.scalars(query)] + + +def get_server(server_id): + """Find a Jellyfin connection by its namespaced identity.""" + return next((row for row in list_servers() if row['id'] == server_id), None) + + +def credential_id(server_id): + """Separate server keys by installation, including shared headless vaults.""" + with _credential_lock, Session(storage.engine()) as session: + row = session.get(storage.AppSetting, 'jellyfin_client_id') + if row is None: + row = storage.AppSetting(key='jellyfin_client_id', value=uuid4().hex) + session.add(row) + session.commit() + return 'jellyfin:' + row.value + ':' + server_id + + +def add_server(url, token): + """Verify the server and save its API key separately from public settings.""" + url = base_url(url) + if not isinstance(token, str) or not token or len(token) > 256 or any(ord(c) <= 32 for c in token): + raise MediaServerError('Enter a valid Jellyfin API key.', 400) + logger.blacklist_config({'Jellyfin': {'API_TOKEN': token}}) + info = Client(url, token).json('GET', '/System/Info') + try: + server_id = 'jellyfin:' + identifier(info['Id']) + name, version = str(info['ServerName'])[:128], str(info['Version'])[:32] + except (KeyError, TypeError) as exc: + raise MediaServerError('Jellyfin did not provide valid server information.', 502) from exc + from jellyfin.connector import profile + profile(version) + credentials.save_token(credential_id(server_id), token) + with Session(storage.engine()) as session: + row = session.get(ServerRecord, server_id) + if row is None: + row = ServerRecord(id=server_id, enabled=True, ignored_libraries='') + session.add(row) + row.name, row.url, row.version = name, url, version + session.commit() + return get_server(server_id) + + +def client(server_id): + """Open a saved enabled connection and verify that its address still matches.""" + record = get_server(server_id) + if record is None: + raise MediaServerError('Server not found.', 404) + if not record['enabled']: + raise MediaServerError('This Jellyfin server is paused.', 409) + token = credentials.get_token(credential_id(server_id)) + if not token: + raise MediaServerError('Reconnect this server to restore its Jellyfin API key.', 503) + connection = Client(record['url'], token) + info = connection.json('GET', '/System/Info') + if 'jellyfin:' + identifier(info.get('Id')) != server_id: + raise MediaServerError('This address now belongs to a different Jellyfin server.', 409) + return connection + + +def update_server(server_id, values): + """Validate processing settings without accepting credentials as preferences.""" + with Session(storage.engine()) as session: + row = session.get(ServerRecord, server_id) + if row is None: + raise ValidationError(ValidationMessage.SERVER_NOT_FOUND) + if 'enabled' in values: + if not isinstance(values['enabled'], bool): + raise ValidationError(ValidationMessage.SERVER_ENABLED_INVALID) + row.enabled = values['enabled'] + if 'ignored_libraries' in values: + value = values['ignored_libraries'] + if not isinstance(value, str) or len(value) > 8192: + raise ValidationError(ValidationMessage.SERVER_SETTING_INVALID) + row.ignored_libraries = ','.join(identifier(part.strip()) for part in value.split(',') if part.strip()) + session.commit() + + +def record_refresh(server_id, error=None): + """Record only sanitized failures and successful refresh timestamps.""" + with Session(storage.engine()) as session: + row = session.get(ServerRecord, server_id) + if row: + row.last_error = error + if error is None: + row.last_refresh = datetime.now(timezone.utc).isoformat(timespec='seconds') + session.commit() + + +def remove_server(server_id): + """Remove local history and credentials while retaining media on Jellyfin.""" + credentials.delete_token(credential_id(server_id)) + with Session(storage.engine()) as session: + for model in (storage.LibraryItem, storage.LibrarySection, storage.ThemeRecord, storage.ThemeError): + session.execute(delete(model).where(model.server_id == server_id)) + session.execute(delete(ServerRecord).where(ServerRecord.id == server_id)) + session.commit() diff --git a/src/jellyfin/web.py b/src/jellyfin/web.py new file mode 100644 index 00000000..5b96c125 --- /dev/null +++ b/src/jellyfin/web.py @@ -0,0 +1,85 @@ +"""Authenticated Jellyfin setup and exact public routes for bundled plugin artifacts.""" + +from fastapi import APIRouter, Depends, Request +from starlette.responses import JSONResponse + +from common import credentials, logger +from common.http import file_response, read_json +from jellyfin import connector, servers +from media_servers.base import MediaServerError + +router = APIRouter() +log = logger.get_logger(__name__) +_ARCHIVE_PATHS = {'/jellyfin/connector/' + name: name for name in connector.ARCHIVES.values()} + + +def _failure(exc): + """Expose fixed adapter messages and log only the failure category.""" + if isinstance(exc, MediaServerError): + return JSONResponse({'message': str(exc)}, status_code=exc.status_code) + log.warning('Jellyfin setup failed (%s)', type(exc).__name__) + message = ('Could not access the secure credential store.' if isinstance(exc, credentials.TokenStorageError) + else 'Could not complete Jellyfin setup. Check the server connection and logs.') + return JSONResponse({'message': message}, status_code=502) + + +def _payload(value): + """Reject arrays and scalars without echoing submitted credentials.""" + if not isinstance(value, dict): + raise MediaServerError('Invalid JSON object.', 400) + return value + + +@router.post('/api/jellyfin/servers', name='jellyfin.add_server', response_model=None) +def add_server(payload=Depends(read_json)): + """Verify and securely save a Jellyfin server using an administrator API key.""" + try: + payload = _payload(payload) + result = servers.add_server(payload.get('url'), payload.get('api_key')) + return JSONResponse({'message': 'Jellyfin server connected.', 'server': result}, status_code=201) + except Exception as exc: + return _failure(exc) + + +@router.get('/api/jellyfin/servers/{server_id}/connector', name='jellyfin.connector_status', response_model=None) +def connector_status(server_id: str): + """Check the installed connector without changing Jellyfin or its repositories.""" + try: + connection = servers.client(server_id) + connector.verify(connection) + return JSONResponse({'installed': True, 'message': 'Matching connector is active.'}) + except Exception as exc: + return _failure(exc) + + +@router.post('/api/jellyfin/servers/{server_id}/connector', name='jellyfin.install_connector', response_model=None) +def install_connector(server_id: str, payload=Depends(read_json)): + """Register the local repository and install the exact matching connector; restart is manual.""" + try: + payload = _payload(payload) + result = connector.install(servers.client(server_id), payload.get('themerr_url')) + return JSONResponse(result, status_code=202) + except Exception as exc: + return _failure(exc) + + +@router.api_route(connector.MANIFEST_PATH, methods=['GET', 'HEAD'], name='jellyfin.manifest', response_model=None) +def manifest(): + """Serve only bundled connector metadata so Jellyfin can install from this Themerr instance.""" + try: + return JSONResponse(connector.manifest()) + except MediaServerError as exc: + return _failure(exc) + + +@router.api_route('/jellyfin/connector/connector-10.11.zip', methods=['GET', 'HEAD'], + name='jellyfin.archive_10_11', response_model=None) +@router.api_route('/jellyfin/connector/connector-12.1.zip', methods=['GET', 'HEAD'], + name='jellyfin.archive_12_1', response_model=None) +def archive(request: Request): + """Serve fixed build-owned plugin filenames through the shared file policy.""" + try: + connector.bundle() + return file_response(str(connector.directory()), _ARCHIVE_PATHS[request.url.path], 'application/zip') + except MediaServerError as exc: + return _failure(exc) diff --git a/src/media_servers/__init__.py b/src/media_servers/__init__.py index 384c21c7..59dff2b6 100644 --- a/src/media_servers/__init__.py +++ b/src/media_servers/__init__.py @@ -1,26 +1,77 @@ -"""Select the installed media-server integration for shared application services.""" +"""Dispatch shared services to independently scoped media-server integrations.""" from functools import lru_cache from media_servers.base import MediaServerBackend +class ServerRegistry(MediaServerBackend): + """Combine installed integrations while preserving existing Plex identifiers.""" + + name = 'media server' + + def __init__(self): + from plex.backend import PlexBackend + from jellyfin.backend import JellyfinBackend + self.plex = PlexBackend() + self.jellyfin = JellyfinBackend() + + def _backend(self, server_id): + return self.jellyfin if server_id.startswith('jellyfin:') else self.plex + + def server(self, server_id): + return self._backend(server_id).server(server_id) + + def display_name(self, server_id): + return self._backend(server_id).name + + def list_servers(self, enabled_only=False): + return [{**record, 'type': kind} for kind, backend in (('plex', self.plex), ('jellyfin', self.jellyfin)) + for record in backend.list_servers(enabled_only)] + + def get_server(self, server_id): + return self._backend(server_id).get_server(server_id) + + def update_server(self, server_id, values): + self._backend(server_id).update_server(server_id, values) + + def remove_server(self, server_id): + self._backend(server_id).remove_server(server_id) + + def record_refresh(self, server_id, error=None): + self._backend(server_id).record_refresh(server_id, error) + + def account_connected(self): + return self.plex.account_connected() + + def start_listeners(self): + self.plex.start_listeners() + self.jellyfin.start_listeners() + + def stop_listeners(self): + self.plex.stop_listeners() + self.jellyfin.stop_listeners() + + def web_router(self): + from fastapi import APIRouter + return APIRouter(routes=[*self.plex.web_router().routes, *self.jellyfin.web_router().routes]) + + @lru_cache(maxsize=1) def get_backend() -> MediaServerBackend: """Return the application's media-server backend. - Plex is the sole supported implementation. Keep that selection here so shared - services do not import the integration or its SDK directly. + Saved identities select Plex or Jellyfin without importing their native APIs + into the shared dashboard, queue, or playback services. Returns ------- MediaServerBackend - The Plex integration. + Registry of installed integrations. Examples -------- >>> get_backend().name - 'Plex' + 'media server' """ - from plex.backend import PlexBackend - return PlexBackend() + return ServerRegistry() diff --git a/src/media_servers/base.py b/src/media_servers/base.py index 6834d4f5..d8602e77 100644 --- a/src/media_servers/base.py +++ b/src/media_servers/base.py @@ -172,6 +172,21 @@ class MediaServerBackend(ABC): name: str + def display_name(self, server_id: str) -> str: + """Return the integration name for public messages about one saved server. + + Parameters + ---------- + server_id : str + Saved server identity. + + Returns + ------- + str + Integration display name. + """ + return self.name + @abstractmethod def server(self, server_id: str) -> MediaServer: """Create an operations adapter for a saved or legacy default connection. diff --git a/src/plex/backend.py b/src/plex/backend.py index 2b3ea97d..c0cb3f0a 100644 --- a/src/plex/backend.py +++ b/src/plex/backend.py @@ -57,6 +57,8 @@ def web_urls(self, library_id: str, item_id: str | None = None) -> dict[str, str return urls def open_poster(self, item_id: str) -> Response | None: + if not str(item_id).isascii() or not str(item_id).isdigit(): + raise MediaServerError('Invalid Plex item identifier.', 404) try: with storage.server_scope(self.server_id): server = plexapi.setup_plexapi() @@ -74,6 +76,8 @@ def open_poster(self, item_id: str) -> Response | None: raise MediaServerError('Unable to load poster from Plex.', 502) from error def open_theme(self, item_id: str, headers: Mapping[str, str]) -> Response: + if not str(item_id).isascii() or not str(item_id).isdigit(): + raise MediaServerError('Invalid Plex item identifier.', 404) try: with storage.server_scope(self.server_id): server = plexapi.setup_plexapi() diff --git a/src/plex/token_store.py b/src/plex/token_store.py index 2620e686..63674a17 100644 --- a/src/plex/token_store.py +++ b/src/plex/token_store.py @@ -1,120 +1,11 @@ -"""Store Plex account tokens outside plaintext application data.""" +"""Compatibility imports for the shared secure credential store.""" -# standard imports -import os -from pathlib import Path +from common.credentials import ( + KEY_FILE_ENV, SERVICE, STORE_UNAVAILABLE, TokenStorageError, + delete_token, get_token, keyring, save_token, +) -# lib imports -from cryptography.fernet import Fernet, InvalidToken -import keyring -from keyring.errors import PasswordDeleteError - -# local imports -from common.definitions import Names -from themerr import storage - - -SERVICE = Names.name -KEY_FILE_ENV = 'THEMERR_PLEX_TOKEN_KEY_FILE' -STORE_UNAVAILABLE = 'The OS credential store is unavailable.' - - -class TokenStorageError(OSError): - """The configured secure token store cannot be used.""" - - -def _cipher() -> Fernet | None: - """Return encryption from an external key file, or use the desktop vault. - - Returns - ------- - Fernet or None - Cipher for an external key, or ``None`` for the OS credential store. - - Raises - ------ - TokenStorageError - The key is missing or invalid in a headless installation. - """ - key_path = os.environ.get(KEY_FILE_ENV) - if key_path: - try: - return Fernet(Path(key_path).read_bytes().strip()) - except (OSError, ValueError) as exc: - raise TokenStorageError(f'Unable to read a valid Plex token key from {KEY_FILE_ENV}.') from exc - if os.environ.get('THEMERR_DOCKER'): - raise TokenStorageError(f'Set {KEY_FILE_ENV} to a mounted secret before signing in to Plex.') - return None - - -def get_token(client_id: str) -> str: - """Read a token from the selected secure store. - - Parameters - ---------- - client_id : str - Installation's Plex OAuth client identifier. - - Returns - ------- - str - Stored token, or an empty string when not signed in. - """ - cipher = _cipher() - if cipher is not None: - encrypted = storage.get_encrypted_token(_namespace(client_id)) - if not encrypted: - return '' - try: - return cipher.decrypt(encrypted.encode('ascii')).decode('utf-8') - except (InvalidToken, UnicodeError) as exc: - raise TokenStorageError('The Plex token key does not match the stored token.') from exc - try: - return keyring.get_password(SERVICE, client_id) or '' - # Native backends can raise errors outside keyring's exception hierarchy. - except Exception as exc: - raise TokenStorageError(STORE_UNAVAILABLE) from exc - - -def save_token(client_id: str, token: str) -> None: - """Persist a Plex token without storing plaintext in SQLite. - - Parameters - ---------- - client_id : str - Installation's Plex OAuth client identifier. - token : str - Plex account token. - """ - cipher = _cipher() - if cipher is not None: - storage.save_encrypted_token(cipher.encrypt(token.encode('utf-8')).decode('ascii'), _namespace(client_id)) - return - try: - keyring.set_password(SERVICE, client_id, token) - except Exception as exc: - raise TokenStorageError(STORE_UNAVAILABLE) from exc - - -def delete_token(client_id: str) -> None: - """Remove this installation's saved Plex token. - - Parameters - ---------- - client_id : str - Installation's Plex OAuth client identifier. - """ - storage.save_encrypted_token(None, _namespace(client_id)) - if os.environ.get(KEY_FILE_ENV) or os.environ.get('THEMERR_DOCKER'): - return - try: - keyring.delete_password(SERVICE, client_id) - except PasswordDeleteError: - pass - except Exception as exc: - raise TokenStorageError(STORE_UNAVAILABLE) from exc - - -def _namespace(client_id: str) -> str: - """Choose a separate encrypted slot for a server credential.""" - return ':' + client_id if client_id.startswith('server:') else '' +__all__ = [ + 'KEY_FILE_ENV', 'SERVICE', 'STORE_UNAVAILABLE', 'TokenStorageError', + 'delete_token', 'get_token', 'keyring', 'save_token', +] diff --git a/src/themerr/migrations/env.py b/src/themerr/migrations/env.py index bd31441e..b4e26538 100644 --- a/src/themerr/migrations/env.py +++ b/src/themerr/migrations/env.py @@ -5,8 +5,10 @@ # local imports from plex.servers import ServerRecord +from jellyfin.servers import ServerRecord as JellyfinServerRecord target_metadata = ServerRecord.metadata +assert JellyfinServerRecord.metadata is target_metadata def run_migrations_online() -> None: diff --git a/src/themerr/migrations/versions/d09a42b638a1_jellyfin_servers.py b/src/themerr/migrations/versions/d09a42b638a1_jellyfin_servers.py new file mode 100644 index 00000000..0ffac726 --- /dev/null +++ b/src/themerr/migrations/versions/d09a42b638a1_jellyfin_servers.py @@ -0,0 +1,43 @@ +"""Support opaque library IDs and Jellyfin connections. + +Revision ID: d09a42b638a1 +Revises: c78c7a5bd3a3 +""" + +from alembic import op +import sqlalchemy as sa + +revision = 'd09a42b638a1' +down_revision = 'c78c7a5bd3a3' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + """Preserve existing Plex state while enabling Jellyfin library identifiers.""" + with op.batch_alter_table('library_sections') as batch: + batch.alter_column('key', existing_type=sa.Integer(), type_=sa.String(), existing_nullable=False) + with op.batch_alter_table('library_items') as batch: + batch.alter_column('section_key', existing_type=sa.Integer(), type_=sa.String(), existing_nullable=False) + op.create_table( + 'jellyfin_servers', + sa.Column('id', sa.String(), primary_key=True), + sa.Column('name', sa.String(), nullable=False), + sa.Column('url', sa.String(), nullable=False), + sa.Column('version', sa.String(), nullable=False), + sa.Column('enabled', sa.Boolean(), nullable=False), + sa.Column('ignored_libraries', sa.String(), nullable=False), + sa.Column('last_refresh', sa.String()), + sa.Column('last_error', sa.String()), + ) + + +def downgrade() -> None: + """Drop Jellyfin caches before restoring Plex-only library keys.""" + for table in ('library_items', 'library_sections', 'theme_records', 'theme_errors'): + op.execute(sa.text(f"DELETE FROM {table} WHERE server_id LIKE 'jellyfin:%'")) + op.drop_table('jellyfin_servers') + with op.batch_alter_table('library_items') as batch: + batch.alter_column('section_key', existing_type=sa.String(), type_=sa.Integer(), existing_nullable=False) + with op.batch_alter_table('library_sections') as batch: + batch.alter_column('key', existing_type=sa.String(), type_=sa.Integer(), existing_nullable=False) diff --git a/src/themerr/storage.py b/src/themerr/storage.py index cb4b8666..b57b7161 100644 --- a/src/themerr/storage.py +++ b/src/themerr/storage.py @@ -24,7 +24,7 @@ _dashboard_lock = RLock() _dashboard_revision = 0 _theme_uploads: dict[tuple[str, str], tuple[int, str]] = {} -_server_scope = ContextVar('plex_server_id', default='default') +_server_scope = ContextVar('media_server_id', default='default') def current_server_id() -> str: @@ -33,14 +33,14 @@ def current_server_id() -> str: Returns ------- str - Plex machine identifier, or the default scope. + Saved server identity, or the default Plex scope. """ return _server_scope.get() @contextmanager def server_scope(server_id: str): - """Scope storage and Plex operations to one server. + """Scope storage and integration operations to one server. Parameters ---------- @@ -64,13 +64,13 @@ class Base(DeclarativeBase): class LibrarySection(Base): - """One cached Plex library section.""" + """One cached media-server library section.""" __tablename__ = 'library_sections' server_id: Mapped[str] = mapped_column(String, primary_key=True, default=current_server_id) - key: Mapped[int] = mapped_column(Integer, primary_key=True) + key: Mapped[str] = mapped_column(String, primary_key=True) title: Mapped[str] = mapped_column(String, nullable=False) agent: Mapped[str] = mapped_column(String, nullable=False) type: Mapped[str] = mapped_column(String, nullable=False) @@ -83,7 +83,7 @@ class LibrarySection(Base): class LibraryItem(Base): - """One dashboard row keyed by its stable Plex rating key.""" + """One dashboard row keyed by its opaque server item identifier.""" __tablename__ = 'library_items' @@ -94,7 +94,7 @@ class LibraryItem(Base): ['server_id', 'section_key'], ['library_sections.server_id', 'library_sections.key'], ),) - section_key: Mapped[int] = mapped_column(Integer, nullable=False) + section_key: Mapped[str] = mapped_column(String, nullable=False) position: Mapped[int] = mapped_column(Integer, nullable=False) title: Mapped[str] = mapped_column(String, nullable=False) type: Mapped[str] = mapped_column(String, nullable=False) @@ -177,7 +177,7 @@ def _replace_dashboard(session: Session, sections: dict) -> None: session.execute(delete(LibrarySection).where(LibrarySection.server_id == current_server_id())) for section in sections.values(): session.add(LibrarySection(**{ - name: section[name] for name in ( + name: str(section[name]) if name == 'key' else section[name] for name in ( 'key', 'title', 'agent', 'type', 'media_count', 'media_percent_complete', 'collection_count', 'collection_percent_complete', 'collections_enabled', 'total_count', ) @@ -191,7 +191,7 @@ def _replace_dashboard(session: Session, sections: dict) -> None: if fields['database_id'] is not None: fields['database_id'] = str(fields['database_id']) session.add(LibraryItem( - rating_key=str(item['rating_key']), section_key=int(section['key']), position=position, **fields, + rating_key=str(item['rating_key']), section_key=str(section['key']), position=position, **fields, )) diff --git a/tests/conftest.py b/tests/conftest.py index 1fd3188b..11dc7af8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,11 +3,31 @@ # standard imports from types import SimpleNamespace from unittest.mock import Mock +import hashlib +import json # lib imports import pytest +@pytest.fixture +def connector_bundle(tmp_path, monkeypatch): + from jellyfin import connector + from common.version import VERSION + directory = tmp_path / 'connector' + directory.mkdir() + artifacts = {} + for index, (profile, filename) in enumerate(connector.ARCHIVES.items()): + content = ('connector-' + profile).encode() + (directory / filename).write_bytes(content) + artifacts[profile] = {'version': f'2026.1004.1234.{index}', 'targetAbi': profile + '.0', + 'checksum': hashlib.md5(content, usedforsecurity=False).hexdigest()} + data = {'build': 'a' * 64, 'protocol': 1, 'themerrVersion': VERSION, 'artifacts': artifacts} + (directory / 'bundle.json').write_text(json.dumps(data), encoding='utf-8') + monkeypatch.setattr(connector, 'directory', lambda: directory) + return data + + @pytest.fixture def configured(tmp_path, monkeypatch): import common diff --git a/tests/functional/test_jellyfin.py b/tests/functional/test_jellyfin.py new file mode 100644 index 00000000..2ee42114 --- /dev/null +++ b/tests/functional/test_jellyfin.py @@ -0,0 +1,77 @@ +"""Browser authentication and fixed public Jellyfin repository resources.""" + +import re +from unittest.mock import Mock + +from fastapi.testclient import TestClient +import pytest + +from common import admin, webapp +from jellyfin import connector, servers +from tests.http_helpers import set_session + + +@pytest.fixture +def browser(configured): + with TestClient(webapp.create_app(https_only=False), follow_redirects=False) as client: + yield client + + +def sign_in(browser): + current = admin._save('admin', 'a long unique passphrase') + set_session(browser, {'admin_revision': current['revision']}) + token = re.search(rb'name="csrf-token" content="([^"]+)"', browser.get('/servers').content).group(1).decode() + return {'X-CSRFToken': token} + + +def test_repository_is_public_but_setup_and_sibling_paths_are_private(browser, connector_bundle, monkeypatch): + connector.repository_url('http://themerr.example:9494') + assert browser.get(connector.MANIFEST_PATH).status_code == 200 + for name in connector.ARCHIVES.values(): + result = browser.get('/jellyfin/connector/' + name) + assert result.status_code == 200 + assert result.headers['Content-Type'] == 'application/zip' + assert browser.head('/jellyfin/connector/' + name).content == b'' + add = Mock() + monkeypatch.setattr(servers, 'add_server', add) + assert browser.post('/api/jellyfin/servers', json={'api_key': 'secret'}).status_code == 401 + assert browser.get('/jellyfin/connector/bundle.json').status_code == 302 + headers = sign_in(browser) + assert browser.post('/api/jellyfin/servers', json={}).status_code == 400 + assert browser.get('/jellyfin/connector/bundle.json').status_code == 404 + assert browser.post('/jellyfin/connector/connector-12.1.zip', headers=headers).status_code == 405 + add.assert_not_called() + + +@pytest.mark.parametrize('path', [ + '../config.ini', '%2e%2e%2fconfig.ini', 'C:%5cconfig.ini', + '..%5cconfig.ini', 'connector-12.1.zip:secret', 'CON', +]) +def test_repository_rejects_unmapped_paths(browser, connector_bundle, path): + sign_in(browser) + assert browser.get('/jellyfin/connector/' + path).status_code == 404 + + +def test_archive_symlink_escape_is_rejected(browser, connector_bundle, tmp_path): + directory = connector.directory() + target = tmp_path / 'connector-sibling' + target.mkdir() + (target / 'outside.zip').write_bytes(b'private') + archive = directory / 'connector-12.1.zip' + archive.unlink() + try: + archive.symlink_to(target / 'outside.zip') + except OSError: + pytest.skip('Creating symlinks requires privileges on this host.') + assert browser.get('/jellyfin/connector/connector-12.1.zip').status_code == 404 + + +def test_api_key_is_not_echoed_in_setup_response(browser, monkeypatch): + headers = sign_in(browser) + add = Mock(return_value={'id': 'jellyfin:' + '3' * 32, 'name': 'Jellyfin', 'enabled': True}) + monkeypatch.setattr(servers, 'add_server', add) + result = browser.post('/api/jellyfin/servers', json={'url': 'http://jellyfin.example', 'api_key': 'secret'}, + headers=headers) + assert result.status_code == 201 + assert b'secret' not in result.content + add.assert_called_once_with('http://jellyfin.example', 'secret') diff --git a/tests/unit/jellyfin/test_audio.py b/tests/unit/jellyfin/test_audio.py new file mode 100644 index 00000000..d33f1f64 --- /dev/null +++ b/tests/unit/jellyfin/test_audio.py @@ -0,0 +1,54 @@ +"""Verify Jellyfin's lossless Opus remux and temporary-file lifecycle.""" + +from fractions import Fraction +import hashlib +from pathlib import Path + +import av +import pytest + +from jellyfin.audio import theme_file +from youtube.youtube_dl import AudioFile, validate_audio + + +def test_opus_is_remuxed_to_ogg_without_changing_audio(tmp_path): + path = tmp_path / 'download.webm' + with av.open(str(path), 'w', format='webm') as output: + stream = output.add_stream('libopus', rate=48000) + stream.layout = 'stereo' + for index in range(50): + frame = av.AudioFrame(format='fltp', layout='stereo', samples=960) + for plane in frame.planes: + plane.update(bytes(plane.buffer_size)) + frame.sample_rate = 48000 + frame.time_base = Fraction(1, 48000) + frame.pts = index * 960 + for packet in stream.encode(frame): + output.mux(packet) + for packet in stream.encode(): + output.mux(packet) + audio = AudioFile(str(path), 'opus', False, hashlib.sha256(path.read_bytes()).hexdigest(), + validate_audio(str(path), 1, 'opus'), path.stat().st_size) + with av.open(str(path)) as source: + packets = [bytes(packet) for packet in source.demux(audio=0) if packet.size] + with theme_file(audio) as remuxed: + result = Path(remuxed.path) + assert result.exists() + assert remuxed.sha256 == hashlib.sha256(result.read_bytes()).hexdigest() + assert remuxed.codec == 'opus' + assert remuxed.duration == pytest.approx(audio.duration, abs=0.001) + with av.open(str(result)) as container: + assert container.format.name == 'ogg' + assert container.streams.audio[0].codec_context.name == 'opus' + assert [bytes(packet) for packet in container.demux(audio=0) if packet.size] == packets + assert not result.exists() + assert path.exists() + + +def test_aac_keeps_original_validated_file(tmp_path): + path = tmp_path / 'download.m4a' + path.write_bytes(b'validated by the shared downloader') + audio = AudioFile(str(path), 'mp4a', True, 'digest', 1, path.stat().st_size) + with theme_file(audio) as prepared: + assert prepared is audio + assert path.exists() diff --git a/tests/unit/jellyfin/test_backend.py b/tests/unit/jellyfin/test_backend.py new file mode 100644 index 00000000..008b9471 --- /dev/null +++ b/tests/unit/jellyfin/test_backend.py @@ -0,0 +1,109 @@ +"""Mocked Jellyfin API pagination, scoped theme processing, and credentialed playback.""" + +from contextlib import nullcontext +from types import SimpleNamespace +from unittest.mock import Mock + +import pytest + +from common import helpers +from jellyfin import backend, connector, metadata, servers +from media_servers import get_backend +from media_servers.base import MediaServerError +from themerr import storage + +ITEM = '1' * 32 +LIBRARY = '2' * 32 +SERVER = 'jellyfin:' + '3' * 32 + + +@pytest.fixture +def connected(configured, connector_bundle, monkeypatch): + connection = Mock(server_version='12.1.0') + monkeypatch.setattr(servers, 'client', lambda _: connection) + monkeypatch.setattr(servers, 'get_server', lambda _: { + 'ignored_libraries': '', 'enabled': True, 'url': 'http://jellyfin.example:8096'}) + monkeypatch.setattr(connector, 'verify', lambda _: connector_bundle) + return connection + + +def test_complete_pagination(): + connection = Mock() + connection.json.side_effect = [{'Items': [{'Id': ITEM}], 'TotalRecordCount': 2}, + {'Items': [{'Id': LIBRARY}], 'TotalRecordCount': 2}] + assert [row['Id'] for row in backend._items(connection)] == [ITEM, LIBRARY] + assert [call.kwargs['params']['StartIndex'] for call in connection.json.call_args_list] == [0, 1] + + +def test_registry_dispatch_keeps_plex_and_jellyfin_separate(): + registry = get_backend() + assert isinstance(registry.server(SERVER), backend.JellyfinMediaServer) + assert registry.server('plex-machine').__class__.__name__ == 'PlexMediaServer' + assert registry.display_name(SERVER) == 'Jellyfin' + assert registry.display_name('plex-machine') == 'Plex' + + +def test_scan_honors_paused_categories_ignored_libraries_and_scope(connected, configured, monkeypatch): + monkeypatch.setattr(servers, 'get_server', lambda _: {'ignored_libraries': LIBRARY}) + connected.json.side_effect = [[{'ItemId': LIBRARY}, {'ItemId': '4' * 32}], { + 'Items': [{'Id': ITEM, 'Type': 'Movie'}, {'Id': '5' * 32, 'Type': 'Series'}, + {'Id': '6' * 32, 'Type': 'Movie', 'IsLocked': True}], 'TotalRecordCount': 3, + }] + configured['Jellyfin']['BOOL_SERIES_SUPPORT'] = False + configured['Themerr']['BOOL_IGNORE_LOCKED_FIELDS'] = False + seen = [] + backend.JellyfinMediaServer(SERVER).scan(lambda key: seen.append((storage.current_server_id(), key))) + assert seen == [(SERVER, ITEM)] + assert storage.current_server_id() == 'default' + + +@pytest.mark.parametrize('owned, present', [(False, True), (True, True), (False, False)]) +def test_verified_upload_and_user_theme_protection(connected, configured, monkeypatch, tmp_path, owned, present): + item = {'Id': ITEM, 'Name': 'Example', 'Type': 'Movie'} + state = {'present': present, 'owned': owned, 'sha256': 'old'} + connected.json.side_effect = [item, state, {'owned': True, 'sha256': 'digest'}] + monkeypatch.setattr(metadata, 'resolve', lambda _: { + 'exists': True, 'database_type': 'movies', 'database': 'themoviedb', 'database_id': '42'}) + monkeypatch.setattr(helpers, 'json_get', lambda **_: {'youtube_theme_url': 'https://youtube.example'}) + path = tmp_path / 'audio.m4a' + path.write_bytes(b'complete audio') + audio = SimpleNamespace(path=str(path), codec='mp4a', sha256='digest', mp4a_available=True) + download = Mock(return_value=nullcontext(audio)) + monkeypatch.setattr(backend, 'download_youtube', download) + assert backend.JellyfinMediaServer(SERVER).update_item(ITEM) is True + with storage.server_scope(SERVER): + tracked = storage.get_tracking(ITEM) + if present and not owned: + download.assert_not_called() + assert tracked == {} + else: + assert tracked['audio_sha256'] == 'digest' + assert connected.json.call_args.kwargs['headers']['X-Themerr-Connector'] == 'a' * 64 + assert connected.json.call_args.kwargs['headers']['Content-Type'] == 'audio/mp4' + assert storage.current_server_id() == 'default' + assert storage.get_tracking(ITEM) == {} + + +def test_failed_upload_never_records_success(connected, monkeypatch, tmp_path): + connected.json.return_value = {'owned': True, 'sha256': 'wrong'} + path = tmp_path / 'audio.m4a' + path.write_bytes(b'complete audio') + audio = SimpleNamespace(path=str(path), codec='mp4a', sha256='digest', mp4a_available=True) + with storage.server_scope(SERVER), pytest.raises(MediaServerError): + backend.JellyfinMediaServer._send_audio(connected, ITEM, {'Type': 'Movie'}, 'source', {'build': 'a'}, audio) + with storage.server_scope(SERVER): + assert storage.get_tracking(ITEM) == {} + + +def test_playback_uses_fixed_native_paths_and_header_credentials(connected): + connected.json.return_value = {'Items': [{'Id': '7' * 32}]} + adapter = backend.JellyfinMediaServer(SERVER) + result = adapter.open_theme(ITEM, {'Range': 'bytes=1-4'}) + assert result is connected.request.return_value + assert connected.request.call_args.args == ('GET', '/Audio/' + '7' * 32 + '/stream') + assert connected.request.call_args.kwargs['headers'] == {'Range': 'bytes=1-4'} + assert connected.request.call_args.kwargs['stream'] is True + connected.json.return_value = {'Items': []} + with pytest.raises(MediaServerError) as error: + adapter.open_theme(ITEM, {}) + assert error.value.status_code == 404 diff --git a/tests/unit/jellyfin/test_connector.py b/tests/unit/jellyfin/test_connector.py new file mode 100644 index 00000000..a548b4bd --- /dev/null +++ b/tests/unit/jellyfin/test_connector.py @@ -0,0 +1,89 @@ +"""Exact artifact selection, repository preservation, and build identity validation.""" + +import json +from unittest.mock import Mock + +import pytest + +from common import definitions, version +from jellyfin import connector +from media_servers.base import MediaServerError + + +def test_manifest_has_only_bundled_versions_and_ignores_request_hosts(configured, connector_bundle): + connector.repository_url('http://themerr.example:9494/prefix') + manifest = connector.manifest()[0] + assert manifest['guid'] == connector.PLUGIN_ID + assert {item['targetAbi'] for item in manifest['versions']} == {'10.11.0', '12.1.0'} + assert len({item['version'] for item in manifest['versions']}) == 2 + assert [item['sourceUrl'] for item in manifest['versions']] == [ + 'http://themerr.example:9494/prefix/jellyfin/connector/' + filename + for filename in connector.ARCHIVES.values() + ] + + +@pytest.mark.parametrize('server_version, profile', [('10.11.6', '10.11'), ('12.1.0', '12.1')]) +def test_install_preserves_repositories_and_selects_exact_artifact( + configured, connector_bundle, server_version, profile): + connection = Mock(server_version=server_version) + repositories = [{'Name': 'Other plugins', 'Url': 'https://other.example/repo.json', 'Enabled': False}] + connection.json.side_effect = [ + {'Version': server_version}, MediaServerError('Missing connector.', 404), repositories, + ] + result = connector.install(connection, 'http://themerr.example:9494') + assert result['restart_required'] is True + assert result['version'] == connector_bundle['artifacts'][profile]['version'] + posted = connection.request.call_args_list[0].kwargs['json'] + assert posted[0] == repositories[0] + assert posted[1]['Url'] == 'http://themerr.example:9494' + connector.MANIFEST_PATH + assert connection.request.call_args_list[1].kwargs['params'] == { + 'assemblyGuid': connector.PLUGIN_ID, + 'version': connector_bundle['artifacts'][profile]['version'], + 'repositoryUrl': 'http://themerr.example:9494' + connector.MANIFEST_PATH, + } + + +def test_install_does_not_overwrite_an_active_matching_assembly(configured, connector_bundle): + connection = Mock(server_version='12.1.0') + connection.json.side_effect = [ + {'Version': '12.1.0'}, {'protocol': 1, 'build': 'a' * 64, 'targetAbi': '12.1.0'}, [], + ] + assert connector.install(connection, 'http://themerr.example')['restart_required'] is False + connection.request.assert_called_once_with('POST', '/Repositories', json=[ + {'Name': connector.PLUGIN_NAME + ' (Themerr)', + 'Url': 'http://themerr.example' + connector.MANIFEST_PATH, 'Enabled': True}, + ]) + + +@pytest.mark.parametrize('field, value', [('protocol', 2), ('build', 'b' * 64), ('targetAbi', '10.11.0')]) +def test_mismatched_connector_is_rejected(configured, connector_bundle, field, value): + connection = Mock(server_version='12.1.0') + connection.json.return_value = {'protocol': 1, 'build': 'a' * 64, 'targetAbi': '12.1.0', field: value} + with pytest.raises(MediaServerError, match='matching Themerr connector') as error: + connector.verify(connection) + assert error.value.status_code == 409 + + +@pytest.mark.parametrize('release', ['10.10.7', '12.0.0', '13.0.0', None]) +def test_unsupported_abi_is_not_guessed(release): + with pytest.raises(MediaServerError): + connector.profile(release) + + +def test_bundle_stale_version_is_rejected(configured, connector_bundle, monkeypatch): + monkeypatch.setattr(version, 'VERSION', 'other-build') + with pytest.raises(MediaServerError) as error: + connector.bundle() + assert error.value.status_code == 503 + + +def test_bundle_is_loaded_from_frozen_resources(configured, monkeypatch, tmp_path): + frozen = tmp_path / 'frozen' + directory = frozen / 'jellyfin-connector' + directory.mkdir(parents=True) + descriptor = {'protocol': 1, 'themerrVersion': version.VERSION, 'build': 'a' * 64, + 'artifacts': dict.fromkeys(connector.ARCHIVES, {})} + (directory / 'bundle.json').write_text(json.dumps(descriptor), encoding='utf-8') + monkeypatch.setattr(definitions.Paths, 'ROOT_DIR', str(frozen)) + assert connector.directory() == directory + assert connector.bundle() == descriptor diff --git a/tests/unit/jellyfin/test_metadata.py b/tests/unit/jellyfin/test_metadata.py new file mode 100644 index 00000000..7b0b7b8d --- /dev/null +++ b/tests/unit/jellyfin/test_metadata.py @@ -0,0 +1,62 @@ +"""Provider resolution and dashboard status remain independent of native clients.""" + +from unittest.mock import Mock + +import pytest + +from jellyfin import backend, metadata +from themerr import storage + + +@pytest.mark.parametrize('native_type, kind, database_type', [ + ('Movie', 'movie', 'movies'), ('Series', 'show', 'tv_shows'), ('BoxSet', 'collection', 'movie_collections'), +]) +def test_provider_ids_map_to_shared_database_categories(monkeypatch, native_type, kind, database_type): + lookup = Mock(return_value=True) + monkeypatch.setattr(metadata.themerr_db, 'item_exists', lookup) + result = metadata.resolve({'Type': native_type, 'Name': 'Example', 'ProviderIds': {'Tmdb': '42'}}) + assert result['type'] == kind + assert result['database_type'] == database_type + assert result['database_id'] == '42' + assert result['issue_action'] == 'edit' + lookup.assert_called_once_with(database_type, 'themoviedb', '42') + + +def test_movie_imdb_fallback_and_invalid_provider_values(monkeypatch): + monkeypatch.setattr(metadata.tmdb, 'get_tmdb_id_from_external_id', lambda *_: None) + monkeypatch.setattr(metadata.themerr_db, 'item_exists', lambda *_: False) + result = metadata.resolve({'Type': 'Movie', 'Name': 'Example', 'ProductionYear': 2020, + 'ProviderIds': {'Tmdb': '../other', 'Imdb': 'tt123'}}) + assert result['database'] == 'imdb' + assert result['database_id'] == 'tt123' + assert 'Example+%282020%29' in result['issue_url'] + invalid = metadata.resolve({'Type': 'Movie', 'Name': 'Example', 'ProviderIds': {'Imdb': 'bad/path'}}) + assert invalid['database_id'] is None + assert invalid['issue_url'] is None + + +def test_dashboard_snapshot_uses_uuid_keys_and_owned_theme_status(configured, monkeypatch): + connection = Mock() + library, first, second = '1' * 32, '2' * 32, '3' * 32 + server_id = 'jellyfin:' + '4' * 32 + monkeypatch.setattr(backend.servers, 'client', lambda _: connection) + monkeypatch.setattr(backend.connector, 'verify', lambda _: {}) + monkeypatch.setattr(metadata, 'resolve', lambda _: { + 'type': 'movie', 'database_type': 'movies', 'database': 'themoviedb', 'database_id': '42', + 'source_database': 'themoviedb', 'source_id': '42', 'exists': True, + 'issue_action': 'edit', 'issue_url': 'https://example.test', + }) + connection.json.side_effect = [ + [{'ItemId': library, 'Name': 'Movies', 'CollectionType': 'movies'}], + {'Items': [{'Id': first}], 'TotalRecordCount': 1}, + {'Items': [{'Id': first, 'Name': 'With theme'}, {'Id': second, 'Name': 'Pending'}], 'TotalRecordCount': 2}, + {'owned': True}, + ] + assert backend.JellyfinMediaServer(server_id).cache_dashboard() + with storage.server_scope(server_id): + snapshot = storage.get_dashboard()[library] + assert snapshot['media_count'] == 2 + assert snapshot['media_percent_complete'] == 50 + assert snapshot['items'][0]['theme_provider'] == 'themerr' + assert snapshot['items'][1]['theme_status'] == 'pending' + assert storage.get_dashboard() is None diff --git a/tests/unit/jellyfin/test_servers.py b/tests/unit/jellyfin/test_servers.py new file mode 100644 index 00000000..05a297d5 --- /dev/null +++ b/tests/unit/jellyfin/test_servers.py @@ -0,0 +1,95 @@ +"""Connection validation and credential isolation across Plex and Jellyfin.""" + +from pathlib import Path +from unittest.mock import Mock + +from cryptography.fernet import Fernet +import pytest +import requests + +from common import credentials +from jellyfin import servers +from jellyfin.client import Client, base_url, identifier +from media_servers.base import MediaServerError +from themerr import storage + + +def response(data, status=200): + return Mock(status_code=status, json=Mock(return_value=data)) + + +def test_saved_key_never_appears_in_public_settings(configured, monkeypatch): + monkeypatch.setattr(requests, 'request', lambda *_args, **_kwargs: response( + {'Id': '3' * 32, 'ServerName': 'Jellyfin', 'Version': '12.1.0'})) + result = servers.add_server('http://jellyfin.example:8096/prefix', 'a-private-jellyfin-key') + assert result['id'] == 'jellyfin:' + '3' * 32 + assert 'a-private-jellyfin-key' not in str(result) + assert credentials.get_token(servers.credential_id(result['id'])) == 'a-private-jellyfin-key' + servers.update_server(result['id'], {'enabled': False, 'ignored_libraries': '4' * 32}) + with pytest.raises(MediaServerError, match='paused'): + servers.client(result['id']) + servers.remove_server(result['id']) + assert servers.list_servers() == [] + assert credentials.get_token(servers.credential_id(result['id'])) == '' + + +def test_headless_plex_and_jellyfin_keys_use_independent_encrypted_slots(configured, tmp_path, monkeypatch): + key = tmp_path / 'secret.key' + key.write_bytes(Fernet.generate_key()) + monkeypatch.setenv(credentials.KEY_FILE_ENV, str(key)) + credentials.save_token('plex-account', 'private-plex-key') + credentials.save_token('server:installation:plex-server', 'private-plex-server-key') + credentials.save_token('jellyfin:installation:jellyfin-server', 'private-jellyfin-key') + assert credentials.get_token('plex-account') == 'private-plex-key' + assert credentials.get_token('server:installation:plex-server') == 'private-plex-server-key' + assert credentials.get_token('jellyfin:installation:jellyfin-server') == 'private-jellyfin-key' + assert b'private-jellyfin-key' not in Path(storage.database_path()).read_bytes() + credentials.delete_token('jellyfin:installation:jellyfin-server') + assert credentials.get_token('plex-account') == 'private-plex-key' + + +@pytest.mark.parametrize('url', [ + None, 'file:///etc/passwd', 'http://u:p@host', 'http://host?token=secret', + 'http://host/%2fpath', 'http://host/../other', 'http://host\\other', + 'http://host:99999', 'http://host\n/', +]) +def test_invalid_addresses_are_rejected(url): + with pytest.raises(MediaServerError): + base_url(url) + + +@pytest.mark.parametrize('item_id', ['../file', 'C:\\file', '%2fetc', 'bad', None]) +def test_item_identifiers_cannot_supply_paths(item_id): + with pytest.raises(MediaServerError): + identifier(item_id) + + +def test_client_keeps_credentials_in_headers_and_rejects_redirects(monkeypatch): + request = Mock(return_value=response({}, 302)) + monkeypatch.setattr(requests, 'request', request) + with pytest.raises(MediaServerError): + Client('http://jellyfin.example', 'secret').request('GET', '/System/Info') + assert request.call_args.kwargs['headers'] == {'Authorization': 'MediaBrowser Token="secret"'} + assert request.call_args.kwargs['allow_redirects'] is False + request.return_value.close.assert_called_once() + + +def test_changed_server_address_is_rejected(configured, monkeypatch): + info = {'Id': '3' * 32, 'ServerName': 'Jellyfin', 'Version': '12.1.0'} + request = Mock(return_value=response(info)) + monkeypatch.setattr(requests, 'request', request) + saved = servers.add_server('http://jellyfin.example', 'private-key') + request.return_value = response({**info, 'Id': '4' * 32}) + with pytest.raises(MediaServerError, match='different Jellyfin server'): + servers.client(saved['id']) + + +def test_client_pins_json_profile_for_both_jellyfin_versions(monkeypatch): + result = response({'Version': '12.1.0'}) + request = Mock(return_value=result) + monkeypatch.setattr(requests, 'request', request) + connection = Client('http://jellyfin.example', 'secret') + assert connection.json('GET', '/System/Info') == {'Version': '12.1.0'} + assert connection.server_version == '12.1.0' + assert request.call_args.kwargs['headers']['Accept'] == 'application/json; profile="PascalCase"' + result.close.assert_called_once() diff --git a/tests/unit/media_servers/test_cache.py b/tests/unit/media_servers/test_cache.py index 42eca205..9e1b5b57 100644 --- a/tests/unit/media_servers/test_cache.py +++ b/tests/unit/media_servers/test_cache.py @@ -11,7 +11,7 @@ @pytest.mark.parametrize('outcome', [False, OSError('Offline'), True]) def test_failed_refresh_preserves_snapshot_and_suppresses_notification(configured, monkeypatch, outcome): - snapshot = {'1': {'key': 1, 'title': 'Saved library', 'agent': 'saved', 'type': 'movie', + snapshot = {'1': {'key': '1', 'title': 'Saved library', 'agent': 'saved', 'type': 'movie', 'media_count': 0, 'media_percent_complete': 0, 'collection_count': 0, 'collection_percent_complete': 0, 'collections_enabled': False, 'total_count': 0, 'items': []}} diff --git a/tests/unit/plex/test_backend.py b/tests/unit/plex/test_backend.py index 499002f4..ad296da4 100644 --- a/tests/unit/plex/test_backend.py +++ b/tests/unit/plex/test_backend.py @@ -8,13 +8,13 @@ from media_servers import get_backend from media_servers.base import MediaServerBackend from plex import dashboard, plexapi, tmdb -from plex.backend import PlexBackend, PlexMediaServer +from plex.backend import PlexMediaServer from themerr import storage -def test_selection_contains_only_the_plex_implementation(): +def test_registry_retains_default_plex_implementation(): assert isinstance(get_backend(), MediaServerBackend) - assert isinstance(get_backend(), PlexBackend) + assert isinstance(get_backend().server('default'), PlexMediaServer) assert get_backend() is get_backend() diff --git a/tests/unit/test_notification_build.py b/tests/unit/test_notification_build.py index 3441ee07..33984227 100644 --- a/tests/unit/test_notification_build.py +++ b/tests/unit/test_notification_build.py @@ -18,6 +18,7 @@ def test_build_stamps_ci_version_before_packaging(monkeypatch, tmp_path, platfor spec = importlib.util.spec_from_file_location('notification_build', source) builder = importlib.util.module_from_spec(spec) spec.loader.exec_module(builder) + monkeypatch.setattr(builder, 'build_connector', Mock()) module = tmp_path / 'src' / 'common' / 'version.py' module.parent.mkdir(parents=True) monkeypatch.setattr(builder, '__file__', str(tmp_path / 'scripts' / 'build.py')) @@ -29,6 +30,7 @@ def test_build_stamps_ci_version_before_packaging(monkeypatch, tmp_path, platfor def bundle(arguments): assert runpy.run_path(str(module))['VERSION'] == '2026.1003.120000' assert not any('version.txt' in value for value in arguments) + assert any(value.startswith('--add-data=jellyfin-connector') for value in arguments) assert not any(value.startswith('--codesign-identity=') for value in arguments) if platform == 'darwin': assert '--onedir' in arguments diff --git a/tests/unit/themerr/test_storage.py b/tests/unit/themerr/test_storage.py index c96b1db6..28be1796 100644 --- a/tests/unit/themerr/test_storage.py +++ b/tests/unit/themerr/test_storage.py @@ -65,8 +65,8 @@ def test_initial_migration_creates_complete_model_schema(configured): with storage.engine().connect() as connection: script = ScriptDirectory.from_config(_migration_config(connection)) revisions = list(script.walk_revisions()) - assert len(revisions) == 1 - assert revisions[0].down_revision is None + assert len(revisions) == 2 + assert revisions[-1].down_revision is None assert len(revisions[0].revision) == 12 assert int(revisions[0].revision, 16) >= 0 revision = connection.execute(text('SELECT version_num FROM alembic_version')).scalar_one() @@ -84,6 +84,29 @@ def test_initial_migration_can_downgrade_and_rebuild(configured): assert compare_metadata(MigrationContext.configure(connection), ServerRecord.metadata) == [] +def test_jellyfin_migration_preserves_plex_dashboard_and_theme_history(configured): + storage.replace_dashboard(_dashboard()) + storage.save_tracking(42, 'movie', {'youtube_theme_url': 'source', 'audio_sha256': 'digest'}) + with storage.engine().begin() as connection: + migration = _migration_config(connection) + command.downgrade(migration, 'c78c7a5bd3a3') + assert connection.execute(text('SELECT key FROM library_sections')).scalar_one() == 1 + command.upgrade(migration, 'head') + assert connection.execute(text('SELECT key FROM library_sections')).scalar_one() == '1' + assert connection.execute(text('PRAGMA foreign_key_check')).all() == [] + assert storage.get_dashboard()['1']['items'][0]['rating_key'] == '42' + assert storage.get_tracking(42)['audio_sha256'] == 'digest' + + +def test_opaque_library_and_item_keys_remain_scoped(configured): + snapshot = _dashboard()['1'] + with storage.server_scope('jellyfin:server'): + section = {**snapshot, 'key': 'a' * 32, 'items': [{**snapshot['items'][0], 'rating_key': 'b' * 32}]} + storage.replace_dashboard({'a' * 32: section}) + assert storage.get_dashboard()['a' * 32]['items'][0]['rating_key'] == 'b' * 32 + assert storage.get_dashboard() is None + + def test_dashboard_replacement_is_atomic(configured): storage.replace_dashboard(_dashboard()) broken = _dashboard() diff --git a/tests/web/servers.test.mjs b/tests/web/servers.test.mjs index a4897042..8d0bbac5 100644 --- a/tests/web/servers.test.mjs +++ b/tests/web/servers.test.mjs @@ -55,28 +55,45 @@ function serverForm(id, selected) { return { form, picker, options, summary, status }; } -function page(context, responses, savedServers = []) { +function page(context, responses, savedServers = [], jellyfin = {}) { const previous = { document: globalThis.document, window: globalThis.window, FormData: globalThis.FormData }; context.after(() => Object.assign(globalThis, previous)); const elements = Object.fromEntries(['plex-auth-start', 'plex-auth-status', 'plex-auth-link', 'plex-auth-disconnect', 'discovery-results', 'manual-server-form', 'toast-region'].map(id => [id, new Element()])); const buttons = ['account', 'local'].map(source => Object.assign(new Element('button'), { dataset: { discover: source } })); const forms = savedServers.map(({ id, selected = [] }) => serverForm(id, selected)); + if (jellyfin.connect) { + const form = new Element('form'); + form.values = { url: 'http://jellyfin.example:8096', api_key: 'private-key' }; + form.querySelector = () => new Element('button'); + form.reset = context.mock.fn(() => { form.values.api_key = ''; }); + elements['jellyfin-server-form'] = form; + } + const connectors = (jellyfin.servers || []).map(id => { + const form = new Element('form'); + const status = new Element('output'); + const button = new Element('button'); + form.dataset.serverId = id; + form.elements = { themerr_url: { value: '' } }; + form.querySelector = selector => selector === '[data-connector-status]' ? status : button; + return { form, status, button }; + }); const events = new EventTarget(); globalThis.document = { getElementById: id => elements[id], createElement: tag => new Element(tag), querySelectorAll: selector => ({ '[data-discover]': buttons, - '[data-server-form]': forms.map(({ form }) => form) })[selector] || [], + '[data-server-form]': forms.map(({ form }) => form), + '[data-connector-form]': connectors.map(({ form }) => form) })[selector] || [], querySelector: () => ({ content: 'csrf-token' }), addEventListener: events.addEventListener.bind(events), dispatchEvent: events.dispatchEvent.bind(events), }; globalThis.FormData = class { constructor(form) { this.form = form; } - get() { return ''; } + get(name) { return this.form.values?.[name] || this.form.elements?.[name]?.value || ''; } getAll() { return this.form.querySelectorAll('input:checked').map(input => input.value); } }; const reload = context.mock.fn(); - globalThis.window = { addEventListener() {}, location: { reload } }; + globalThis.window = { addEventListener() {}, location: { reload, origin: 'http://themerr.example:9494' } }; const fetch = context.mock.method(globalThis, 'fetch', async () => { const response = responses.shift(); return { ok: !response.error, status: response.error ? 502 : 200, json: async () => response.body }; @@ -84,9 +101,39 @@ function page(context, responses, savedServers = []) { context.mock.method(globalThis, 'setTimeout', () => 0); const controller = new AbortController(); initServers(controller.signal); - return { elements, buttons, fetch, reload, forms, controller }; + return { elements, buttons, fetch, reload, forms, controller, connectors }; } +test('Jellyfin connection submits its key with CSRF and clears it after success', async context => { + const { elements, fetch, reload } = page(context, [{ body: { message: 'Connected' } }], [], { connect: true }); + const form = elements['jellyfin-server-form']; + form.listeners.submit({ preventDefault() {} }); + await new Promise(setImmediate); + assert.equal(fetch.mock.calls[0].arguments[0], '/api/jellyfin/servers'); + assert.deepEqual(JSON.parse(fetch.mock.calls[0].arguments[1].body), { + url: 'http://jellyfin.example:8096', api_key: 'private-key', + }); + assert.equal(fetch.mock.calls[0].arguments[1].headers['X-CSRFToken'], 'csrf-token'); + assert.equal(form.values.api_key, ''); + assert.equal(form.reset.mock.callCount(), 1); + assert.equal(reload.mock.callCount(), 1); +}); + +test('connector status and installation stay scoped to the selected Jellyfin server', async context => { + const { connectors, fetch } = page(context, [{ body: { message: 'Install the matching connector.' } }, + { body: { message: 'Restart Jellyfin.' } }], [], { servers: ['jellyfin:abc'] }); + await new Promise(setImmediate); + const { form, status } = connectors[0]; + assert.equal(status.textContent, 'Install the matching connector.'); + assert.equal(form.elements.themerr_url.value, 'http://themerr.example:9494'); + form.elements.themerr_url.value = 'https://trusted.example/themerr'; + form.listeners.submit({ preventDefault() {} }); + await new Promise(setImmediate); + assert.equal(fetch.mock.calls[1].arguments[0], '/api/jellyfin/servers/jellyfin%3Aabc/connector'); + assert.deepEqual(JSON.parse(fetch.mock.calls[1].arguments[1].body), { themerr_url: 'https://trusted.example/themerr' }); + assert.equal(status.textContent, 'Restart Jellyfin.'); +}); + test('empty LAN discovery explains GDM and offers account or manual connections', async context => { const { elements, buttons } = page(context, [{ body: { servers: [] } }]); await buttons[1].click(); diff --git a/web/js/servers.js b/web/js/servers.js index 2c2653f2..b18a4996 100644 --- a/web/js/servers.js +++ b/web/js/servers.js @@ -107,6 +107,7 @@ function discoveredServer(resource, source) { } export function initServers(signal) { + initJellyfin(signal); const authStart = document.getElementById('plex-auth-start'); if (!authStart) return; const status = document.getElementById('plex-auth-status'); @@ -156,7 +157,7 @@ export function initServers(signal) { }); const disconnect = document.getElementById('plex-auth-disconnect'); disconnect.addEventListener('click', () => { - if (!window.confirm('Disconnect Plex and pause all saved servers? Your local theme history will be kept.')) return; + if (!window.confirm('Disconnect Plex and pause all saved Plex servers? Your local theme history will be kept.')) return; void busy(disconnect, async () => { await api('/api/plex/auth/disconnect'); if (active) await refreshPage(); @@ -206,10 +207,40 @@ export function initServers(signal) { }); }); document.querySelectorAll('[data-remove-server]').forEach(button => button.addEventListener('click', () => { - if (!window.confirm('Remove this server and its local theme history? Plex media will stay on the server.')) return; + if (!window.confirm('Remove this server and its local theme history? Media will stay on the server.')) return; void busy(button, async () => { await api(`/api/servers/${encodeURIComponent(button.dataset.removeServer)}`, { method: 'DELETE' }); if (active) await refreshPage(); }); })); } + +function initJellyfin(signal) { + const form = document.getElementById('jellyfin-server-form'); + form?.addEventListener('submit', event => { + event.preventDefault(); + void busy(form.querySelector('button[type="submit"]'), async () => { + const data = new FormData(form); + await api('/api/jellyfin/servers', { body: { url: data.get('url'), api_key: data.get('api_key') } }); + form.reset(); + if (!signal?.aborted && form.isConnected) await refreshPage(); + }); + }, { signal }); + document.querySelectorAll('[data-connector-form]').forEach(connectorForm => { + const status = connectorForm.querySelector('[data-connector-status]'); + connectorForm.elements.themerr_url.value = window.location.origin; + const url = `/api/jellyfin/servers/${encodeURIComponent(connectorForm.dataset.serverId)}/connector`; + void api(url, { method: 'GET' }).then(result => { + if (!signal?.aborted && status.isConnected) status.textContent = result.message; + }).catch(error => { + if (!signal?.aborted && status.isConnected) status.textContent = error.message; + }); + connectorForm.addEventListener('submit', event => { + event.preventDefault(); + void busy(connectorForm.querySelector('button[type="submit"]'), async () => { + const result = await api(url, { body: { themerr_url: new FormData(connectorForm).get('themerr_url') } }); + if (!signal?.aborted && status.isConnected) status.textContent = result.message; + }); + }, { signal }); + }); +} diff --git a/web/templates/activity.html b/web/templates/activity.html index 6dfe6716..e641b3d9 100644 --- a/web/templates/activity.html +++ b/web/templates/activity.html @@ -5,5 +5,5 @@

{{ _('Recent tasks') }}

{{ _('Live') }}
{% for job in jobs %}
{{ job.name }}
{{ job.status|capitalize }}{{ (job.duration|string + 's') if job.duration is not none else 'In progress' }}
{% else %}

{{ _('No tasks have run yet. Request a scan, or wait for the next scheduled refresh.') }}

{% endfor %}

{{ _('Needs attention') }}

{{ failures|length }}
-{% for item in failures %}

{{ item.title }}

{{ item.server }} · {{ item.library }} · {{ _('Plex ID') }} {{ item.rating_key }}

{{ item.reason }}

{% if item.theme and item.error %}{{ _('Outdated from ThemerrDB') }}{% endif %}
{% else %}
{{ _('No processing failures recorded. All quiet here.') }}
{% endfor %}
+{% for item in failures %}

{{ item.title }}

{{ item.server }} · {{ item.library }} · {{ item.server_label }} {{ _('ID') }} {{ item.rating_key }}

{{ item.reason }}

{% if item.theme and item.error %}{{ _('Outdated from ThemerrDB') }}{% endif %}
{% else %}
{{ _('No processing failures recorded. All quiet here.') }}
{% endfor %} {% endblock %} diff --git a/web/templates/home.html b/web/templates/home.html index ba18e9d2..3d3261cb 100644 --- a/web/templates/home.html +++ b/web/templates/home.html @@ -18,7 +18,7 @@ {{ _('LET’S SET THE TONE') }}

{{ _('Your library deserves a soundtrack.') if not servers else _('Getting to know your library.') }}

-

{{ _('Connect your Plex account, choose your servers, and let Themerr take care of the music.') if not servers else _('Your first refresh is building the library view. Check Activity for progress, or request a refresh above.') }}

+

{{ _('Connect Plex or Jellyfin, choose your servers, and let Themerr take care of the music.') if not servers else _('Your first refresh is building the library view. Check Activity for progress, or request a refresh above.') }}

{{ _('Connect a server') if not servers else _('View activity') }} {% else %} @@ -54,14 +54,14 @@

{{ _('Your library deserves a soundtrack.') if not servers else _('Getting t data-theme-title="{{ item.title }}" data-theme-year="{{ item.year or '' }}" data-theme-type="{{ item.type|capitalize }}" data-theme-server="{{ section.server_name }}" data-theme-poster="{{ url_for('theme_poster_default', rating_key=item.rating_key) if section.server_id == 'default' else url_for('theme_poster', server_id=section.server_id, rating_key=item.rating_key) }}" data-theme-item-url="{{ item.server_item_url }}" data-play-label="{{ _('Play theme for') }} {{ item.title }}" data-pause-label="{{ _('Pause theme for') }} {{ item.title }}" - data-error-label="{{ _('Unable to play this theme. Check the Plex connection and browser audio support.') }}" + data-error-label="{{ _('Unable to play this theme. Check the server connection and browser audio support.') }}" aria-label="{{ _('Play theme for') }} {{ item.title }}" aria-pressed="false" aria-describedby="playback-error-{{ loop.index }}-{{ loop.index0 }}-{{ section.server_id }}-{{ section.key }}"> {% endif %} - {{ item.title }}{{ item.year or '—' }} · {{ item.type|capitalize }} · {{ _('Plex ID') }}: {{ item.rating_key }} + {{ item.title }}{{ item.year or '—' }} · {{ item.type|capitalize }} · {{ section.server_label }} {{ _('ID') }}: {{ item.rating_key }} {% if item.theme %}{% endif %} diff --git a/web/templates/servers.html b/web/templates/servers.html index 0d326919..39c098ce 100644 --- a/web/templates/servers.html +++ b/web/templates/servers.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% block content %} -
{{ _('ONE WORKSPACE. EVERY SERVER.') }}

{{ _('Servers') }}.

{{ _('Connect your Plex account, then choose where the music goes.') }}

{{ servers|length }} {{ _('saved') }}
+
{{ _('ONE WORKSPACE. EVERY SERVER.') }}

{{ _('Servers') }}.

{{ _('Connect Plex or Jellyfin, then choose where the music goes.') }}

{{ servers|length }} {{ _('saved') }}
+
+

{{ _('Add Jellyfin') }}

{{ _('Jellyfin 10.11 and 12.1 are supported. Create an administrator API key in Jellyfin Dashboard - API Keys.') }}

+
+ + + + +

{{ _('Keys stay in the secure credential store. After connecting, install the matching connector on the server card above.') }}

+ +
+
{% endblock %} {% block modals %}{% include 'directory_picker.html' %}{% endblock %} diff --git a/web/templates/theme_player.html b/web/templates/theme_player.html index c697640e..d58ff106 100644 --- a/web/templates/theme_player.html +++ b/web/templates/theme_player.html @@ -1,5 +1,5 @@
From be8cbddd62a19c15a69a9717700af0c1c33fa5ac Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 5 Oct 2026 00:14:59 -0400 Subject: [PATCH 03/11] feat: improve Jellyfin connector lifecycle and settings --- .dockerignore | 3 + .github/workflows/ci.yml | 79 ++- .gitignore | 1 + DOCKER_README.md | 40 +- Dockerfile | 8 +- .../jellyfin.tests/Connector.Tests.csproj | 15 +- connectors/jellyfin.tests/ControllerTests.cs | 117 ++++ connectors/jellyfin.tests/Program.cs | 67 --- connectors/jellyfin.tests/ThemeFilesTests.cs | 206 +++++++ connectors/jellyfin/Controller.cs | 3 +- connectors/jellyfin/ThemeFiles.cs | 95 +++- connectors/jellyfin/Themerr.Connector.csproj | 1 + docs/source/about/usage.rst | 37 +- docs/source/contributing/build.rst | 6 +- docs/source/contributing/testing.rst | 11 + locale/themerr-plex.po | 501 +++++++++++------- scripts/build.py | 8 +- scripts/build_connector.py | 36 +- scripts/test_connector.py | 36 ++ src/common/config.py | 76 ++- src/common/credentials.py | 15 +- src/common/server_ui.py | 3 +- src/common/webapp.py | 4 + src/jellyfin/backend.py | 16 +- src/jellyfin/connector.py | 84 ++- src/jellyfin/discovery.py | 38 ++ src/jellyfin/maintenance.py | 200 +++++++ src/jellyfin/repository.py | 72 +++ src/jellyfin/web.py | 42 +- tests/conftest.py | 3 + tests/functional/test_jellyfin.py | 22 +- tests/functional/test_webapp.py | 29 +- tests/unit/common/test_credentials.py | 30 ++ tests/unit/jellyfin/test_backend.py | 12 +- tests/unit/jellyfin/test_connector.py | 50 +- tests/unit/jellyfin/test_discovery.py | 36 ++ tests/unit/jellyfin/test_maintenance.py | 164 ++++++ tests/unit/jellyfin/test_repository.py | 56 ++ tests/unit/test_connector_build.py | 65 +++ tests/web/config.test.mjs | 23 +- tests/web/servers.test.mjs | 30 ++ tests/web/workspace_navigation.test.mjs | 41 +- web/css/custom.css | 8 + web/js/config.js | 4 +- web/js/icons.js | 2 + web/js/servers.js | 77 ++- web/js/workspace_navigation.js | 8 +- web/templates/auth.html | 6 +- web/templates/config.html | 17 +- web/templates/home.html | 2 + web/templates/navbar.html | 2 +- web/templates/server_brand.html | 6 + web/templates/servers.html | 21 +- 53 files changed, 2163 insertions(+), 371 deletions(-) create mode 100644 connectors/jellyfin.tests/ControllerTests.cs delete mode 100644 connectors/jellyfin.tests/Program.cs create mode 100644 connectors/jellyfin.tests/ThemeFilesTests.cs create mode 100644 scripts/test_connector.py create mode 100644 src/jellyfin/discovery.py create mode 100644 src/jellyfin/maintenance.py create mode 100644 src/jellyfin/repository.py create mode 100644 tests/unit/common/test_credentials.py create mode 100644 tests/unit/jellyfin/test_discovery.py create mode 100644 tests/unit/jellyfin/test_maintenance.py create mode 100644 tests/unit/jellyfin/test_repository.py create mode 100644 tests/unit/test_connector_build.py create mode 100644 web/templates/server_brand.html diff --git a/.dockerignore b/.dockerignore index 0126900c..e55e0b2d 100644 --- a/.dockerignore +++ b/.dockerignore @@ -21,3 +21,6 @@ node_modules/ web/assets/ .build-tools/ config/ +connectors/**/bin/ +connectors/**/obj/ +connectors/**/TestResults/ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d540d0ea..d15e7272 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,6 +39,70 @@ jobs: with: github_token: ${{ secrets.GITHUB_TOKEN }} + connector: + name: Jellyfin Connector + needs: setup_release + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup .NET + uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 + with: + dotnet-version: | + 9.0.x + 10.0.x + + - name: Install uv + uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0 + with: + enable-cache: true + + - name: Build Jellyfin connectors + env: + THEMERR_VERSION: ${{ needs.setup_release.outputs.release_version }} + run: uv run --no-project --python ${{ env.PYTHON_VERSION }} scripts/build_connector.py + + - name: Test Jellyfin ABIs with coverage + id: test + run: uv run --no-project --python ${{ env.PYTHON_VERSION }} scripts/test_connector.py + + - name: Upload connector coverage + if: >- + always() && + (steps.test.outcome == 'success' || steps.test.outcome == 'failure') && + startsWith(github.repository, 'LizardByte/') + uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1 + with: + fail_ci_if_error: true + files: ./coverage/connector-10.11/coverage.opencover.xml,./coverage/connector-12.1/coverage.opencover.xml + flags: jellyfin-connector + report_type: coverage + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Upload connector test results + if: >- + always() && + (steps.test.outcome == 'success' || steps.test.outcome == 'failure') && + startsWith(github.repository, 'LizardByte/') + uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1 + with: + fail_ci_if_error: true + files: ./coverage/connector-10.11/junit.xml,./coverage/connector-12.1/junit.xml + flags: jellyfin-connector + report_type: test_results + token: ${{ secrets.CODECOV_TOKEN }} + + - name: Upload connector artifacts + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: jellyfin-connector + path: jellyfin-connector/ + if-no-files-found: error + build-docker: name: Docker needs: setup_release @@ -62,6 +126,7 @@ jobs: build: needs: - setup_release + - connector runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -119,16 +184,11 @@ jobs: uv sync --locked --all-extras --no-install-project --no-python-downloads --python ${{ env.PYTHON_VERSION }} - - name: Setup .NET - uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 + - name: Download Jellyfin connectors + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - dotnet-version: '10.0.x' - - - name: Build Jellyfin connectors - run: uv run --locked --all-extras --no-build --no-sync python scripts/build_connector.py - - - name: Test Jellyfin connector file handling - run: dotnet run --project connectors/jellyfin.tests/Connector.Tests.csproj --configuration Release + name: jellyfin-connector + path: jellyfin-connector/ - name: Build and test web assets run: | @@ -195,6 +255,7 @@ jobs: - name: Build pyinstaller package env: THEMERR_VERSION: ${{ needs.setup_release.outputs.release_version }} + THEMERR_PREBUILT_CONNECTOR: '1' APPLE_CODESIGN_IDENTITY: >- ${{ runner.os == 'macOS' && needs.setup_release.outputs.publish_release == 'true' && secrets.APPLE_CODESIGN_IDENTITY || '' }} diff --git a/.gitignore b/.gitignore index 71138615..02e94eab 100644 --- a/.gitignore +++ b/.gitignore @@ -170,3 +170,4 @@ http_cache.sqlite-* jellyfin-connector/ connectors/**/bin/ connectors/**/obj/ +connectors/**/TestResults/ diff --git a/DOCKER_README.md b/DOCKER_README.md index dbc0646e..a7f52787 100644 --- a/DOCKER_README.md +++ b/DOCKER_README.md @@ -2,12 +2,17 @@ ## lizardbyte/themerr-plex -Before Plex sign-in, create a persistent key file outside the `/config` volume. Keep this file private and ensure the +Before connecting a media server, create a persistent key file outside the `/config` volume. Keep this file private and ensure the container's `PUID` can read it. Keep the same key when recreating the container; losing it requires signing in again. +`THEMERR_TOKEN_KEY_FILE` encrypts credentials for all media servers. The older `THEMERR_PLEX_TOKEN_KEY_FILE` +name is still accepted; use the same key when renaming it. The generic variable takes precedence if both are set. +Port 9495 serves only the Jellyfin connector repository over HTTP when the admin UI uses a self-signed certificate. +Use a host address reachable from Jellyfin; `localhost` inside another container refers to that container itself. + ```bash -python3 -c 'import base64, os; print(base64.urlsafe_b64encode(os.urandom(32)).decode())' > themerr-plex.key -chmod 600 themerr-plex.key +python3 -c 'import base64, os; print(base64.urlsafe_b64encode(os.urandom(32)).decode())' > themerr-tokens.key +chmod 600 themerr-tokens.key ``` ### Using docker run @@ -18,12 +23,13 @@ docker run -d \ --name=themerr-plex \ --restart=unless-stopped \ -v :/config \ - -v :/run/secrets/themerr_plex_key:ro \ - -e THEMERR_PLEX_TOKEN_KEY_FILE=/run/secrets/themerr_plex_key \ + -v :/run/secrets/themerr_token_key:ro \ + -e THEMERR_TOKEN_KEY_FILE=/run/secrets/themerr_token_key \ -e PUID= \ -e PGID= \ -e TZ= \ -p 9494:9494 \ + -p 9495:9495 \ lizardbyte/themerr-plex ``` @@ -53,14 +59,15 @@ services: restart: unless-stopped volumes: - :/config - - :/run/secrets/themerr_plex_key:ro + - :/run/secrets/themerr_token_key:ro environment: - - THEMERR_PLEX_TOKEN_KEY_FILE=/run/secrets/themerr_plex_key + - THEMERR_TOKEN_KEY_FILE=/run/secrets/themerr_token_key - PUID= - PGID= - TZ= ports: - 9494:9494 + - 9495:9495 ``` Create and start the container (run the command from the same folder as your `docker-compose.yml` file): @@ -88,15 +95,16 @@ Therefore `-p 9494:9494` exposes port `9494` from inside the container on the ho The internal port is `9494`; the host port may be changed (e.g. `-p 8080:9494`). -| Parameter | Function | Example Value | Required | -|-----------------------------|--------------------------------------------------------------------------------------|----------------------|:--------:| -| `-p :9494` | Web UI Port | `9494` | True | -| `-v :/config` | Volume mapping | `/home/themerr-plex` | True | -| `-v :/run/secrets/themerr_plex_key:ro` | Read-only token encryption key | `/home/me/themerr-plex.key` | True | -| `-e THEMERR_PLEX_TOKEN_KEY_FILE=...` | Container path to the token encryption key | `/run/secrets/themerr_plex_key` | True | -| `-e PUID=` | User ID | `1001` | False | -| `-e PGID=` | Group ID | `1001` | False | -| `-e TZ=` | Lookup TZ value [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | `America/New_York` | True | +| Parameter | Function | Example Value | Required | +|------------------------------------------------------|--------------------------------------------------------------------------------------|----------------------------------|:--------:| +| `-p :9494` | Web UI Port | `9494` | True | +| `-p :9495` | Jellyfin connector repository with self-signed TLS on the UI | `9495` | False | +| `-v :/config` | Volume mapping | `/home/themerr-plex` | True | +| `-v :/run/secrets/themerr_token_key:ro` | Read-only token encryption key | `/home/me/themerr-tokens.key` | True | +| `-e THEMERR_TOKEN_KEY_FILE=...` | Container path to the token encryption key | `/run/secrets/themerr_token_key` | True | +| `-e PUID=` | User ID | `1001` | False | +| `-e PGID=` | Group ID | `1001` | False | +| `-e TZ=` | Lookup TZ value [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | `America/New_York` | True | ### User / Group Identifiers: diff --git a/Dockerfile b/Dockerfile index 9f6ea27a..fb3aa3b5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,13 @@ # syntax=docker/dockerfile:1 FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0 AS connector -RUN apt-get update && apt-get install -y --no-install-recommends python3 && rm -rf /var/lib/apt/lists/* +COPY --from=ghcr.io/astral-sh/uv:0.12 /uv /usr/local/bin/uv WORKDIR /connector COPY connectors/jellyfin/ connectors/jellyfin/ COPY scripts/build_connector.py scripts/build_connector.py COPY src/common/version.py src/common/version.py -ARG THEMERR_VERSION -RUN python3 scripts/build_connector.py +ARG BUILD_VERSION +ARG THEMERR_VERSION=${BUILD_VERSION} +RUN uv run --no-project --python 3.14 scripts/build_connector.py FROM ghcr.io/astral-sh/uv:0.12-python3.14-trixie-slim AS base @@ -69,6 +70,7 @@ ENV THEMERR_DOCKER=True # network setup EXPOSE 9494 +EXPOSE 9495 # setup user ARG PGID=1000 diff --git a/connectors/jellyfin.tests/Connector.Tests.csproj b/connectors/jellyfin.tests/Connector.Tests.csproj index 9bc7ef70..b4e40ea4 100644 --- a/connectors/jellyfin.tests/Connector.Tests.csproj +++ b/connectors/jellyfin.tests/Connector.Tests.csproj @@ -1,12 +1,23 @@ Exe - net10.0 + net10.0 + $(ConnectorFramework) + 12.1.0 enable enable true + true + false - + + + + + + + + diff --git a/connectors/jellyfin.tests/ControllerTests.cs b/connectors/jellyfin.tests/ControllerTests.cs new file mode 100644 index 00000000..a5363ca2 --- /dev/null +++ b/connectors/jellyfin.tests/ControllerTests.cs @@ -0,0 +1,117 @@ +using System.Reflection; +using MediaBrowser.Common.Api; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.MediaInfo; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Moq; +using Xunit; + +namespace Themerr.Connector.Tests; + +public sealed class ControllerTests : IDisposable +{ + private readonly string _root = Path.Combine(Path.GetTempPath(), "themerr-controller-test-" + Guid.NewGuid()); + private readonly Mock _library = new(); + private readonly Controller _controller; + public ControllerTests() + { + Directory.CreateDirectory(_root); + var media = new Mock(); + media.Setup(value => value.GetPathProtocol(It.IsAny())).Returns(MediaProtocol.File); + BaseItem.MediaSourceManager = media.Object; + BaseItem.LibraryManager = _library.Object; + _library.Setup(value => value.GetItemList(It.IsAny())).Returns([]); + _controller = new Controller(_library.Object) + { ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() } }; + } + public void Dispose() => Directory.Delete(_root, true); + + [Fact] + public void EndpointsRequireAnAdministrator() + { + var policy = typeof(Controller).GetCustomAttribute(); + Assert.Equal(Policies.RequiresElevation, policy?.Policy); + Assert.Empty(typeof(Controller).GetMethods().SelectMany(method => method.GetCustomAttributes())); + } + + [Fact] + public void IdentityReportsEmbeddedBuildAndAbi() + { + var value = _controller.Identity(); + Assert.Equal(1, value.GetType().GetProperty("protocol")!.GetValue(value)); + Assert.Equal("development", value.GetType().GetProperty("build")!.GetValue(value)); + Assert.Equal(typeof(Plugin).Assembly.GetCustomAttributes() + .Single(attribute => attribute.Key == "JellyfinAbi").Value, + value.GetType().GetProperty("targetAbi")!.GetValue(value)); + } + + [Fact] + public void PluginHasStableIdentityAndNoBackgroundProcessing() + { + var paths = new Mock(); + paths.SetupGet(value => value.PluginsPath).Returns(_root); + paths.SetupGet(value => value.PluginConfigurationsPath).Returns(_root); + var plugin = new Plugin(paths.Object, Mock.Of()); + Assert.Equal("Themerr Connector", plugin.Name); + Assert.Equal(new Guid("f9a117dc-b44a-4507-9706-241837784369"), plugin.Id); + Assert.Contains("connector", plugin.Description); + } + + [Fact] + public async Task UnknownIdsNeverAccessTheFilesystem() + { + Assert.IsType(_controller.State(Guid.NewGuid()).Result); + _controller.Request.Headers["X-Themerr-Connector"] = "development"; + Assert.IsType((await _controller.Upload(Guid.NewGuid(), CancellationToken.None)).Result); + Assert.Empty(Directory.GetFiles(_root)); + } + + [Fact] + public async Task UploadRejectsWrongBuildBeforeResolvingAnItem() + { + _controller.Request.Headers["X-Themerr-Connector"] = "different"; + Assert.IsType((await _controller.Upload(Guid.NewGuid(), CancellationToken.None)).Result); + _library.Verify(value => value.GetItemById(It.IsAny()), Times.Never); + } + + [Fact] + public void CollectionsUseTheirNativeMetadataDirectory() + { + var id = Guid.NewGuid(); + _library.Setup(value => value.GetItemById(id)).Returns(new BoxSet { Id = id, Path = _root }); + var result = _controller.State(id); + Assert.Null(result.Result); + Assert.False(result.Value!.Present); + } + + [Fact] + public async Task LockedAndUnsupportedItemsAreRejected() + { + var id = Guid.NewGuid(); + _controller.Request.Headers["X-Themerr-Connector"] = "development"; + _library.Setup(value => value.GetItemById(id)).Returns(new BoxSet { Id = id, Path = _root, IsLocked = true }); + Assert.IsType((await _controller.Upload(id, CancellationToken.None)).Result); + _library.Setup(value => value.GetItemById(id)).Returns(new Folder { Id = id, Path = _root }); + Assert.IsType(_controller.State(id).Result); + Assert.IsType((await _controller.Upload(id, CancellationToken.None)).Result); + } + + [Fact] + public async Task InvalidAndProtectedBodiesReturnFixedErrors() + { + var id = Guid.NewGuid(); + _library.Setup(value => value.GetItemById(id)).Returns(new BoxSet { Id = id, Path = _root }); + _controller.Request.Headers["X-Themerr-Connector"] = "development"; + Assert.IsType((await _controller.Upload(id, CancellationToken.None)).Result); + File.WriteAllText(Path.Combine(_root, "theme.mp3"), "user theme"); + _controller.Request.ContentType = "audio/mp4"; + _controller.Request.Headers["X-Themerr-SHA256"] = new string('a', 64); + Assert.IsType((await _controller.Upload(id, CancellationToken.None)).Result); + } +} diff --git a/connectors/jellyfin.tests/Program.cs b/connectors/jellyfin.tests/Program.cs deleted file mode 100644 index 0c71f0e9..00000000 --- a/connectors/jellyfin.tests/Program.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Security.Cryptography; -using Themerr.Connector; - -var root = Path.Combine(Path.GetTempPath(), "themerr-connector-test-" + Guid.NewGuid()); -Directory.CreateDirectory(root); -var bytes = "complete validated audio"u8.ToArray(); -var digest = Convert.ToHexString(SHA256.HashData(bytes)).ToLowerInvariant(); -static void Check(bool condition) { if (!condition) throw new Exception("Connector regression check failed."); } -static async Task Reject(Func action) where T : Exception -{ - try { await action(); } - catch (T) { return; } - throw new Exception("Expected " + typeof(T).Name); -} -Task Save(string type = "audio/mp4", string? hash = null, byte[]? data = null) => - ThemeFiles.Save(root, new MemoryStream(data ?? bytes), type, hash ?? digest, 0, CancellationToken.None); -try -{ - await Reject(() => Save("../../file")); - await Reject(() => Save(hash: new string('z', 64))); - await Reject(() => Save(hash: new string('a', 64))); - await Reject(() => Save(data: [])); - Check(!Directory.EnumerateFiles(root).Any()); - File.WriteAllText(Path.Combine(root, "theme.mp3"), "user theme"); - await Reject(() => Save()); - Check(File.ReadAllText(Path.Combine(root, "theme.mp3")) == "user theme"); - File.Delete(Path.Combine(root, "theme.mp3")); - var saved = await Save(); - Check(saved.Owned && saved.Sha256 == digest); - Check(ThemeFiles.State(root).Owned); - Check(!ThemeFiles.State(root, 2).Owned); - await Reject(() => ThemeFiles.Save(root, new MemoryStream(bytes), - "audio/mp4", digest, 2, CancellationToken.None)); - var switched = await Save("audio/ogg"); - Check(switched.Owned && !File.Exists(Path.Combine(root, "theme.m4a"))); - Check(File.ReadAllBytes(Path.Combine(root, "theme.opus")).SequenceEqual(bytes)); - File.WriteAllText(Path.Combine(root, "theme.opus"), "manually changed"); - Check(!ThemeFiles.State(root).Owned); - await Reject(() => Save()); - File.Delete(Path.Combine(root, "theme.opus")); - File.Delete(Path.Combine(root, ".themerr-connector.json")); - var sibling = root + "-sibling"; - Directory.CreateDirectory(sibling); - try - { - var target = Path.Combine(sibling, "outside.m4a"); - File.WriteAllText(target, "outside"); - bool links = true; - try { File.CreateSymbolicLink(Path.Combine(root, "theme.m4a"), target); } - catch (UnauthorizedAccessException) { links = false; } - catch (IOException error) when (OperatingSystem.IsWindows() && (error.HResult & 0xffff) == 1314) - { links = false; } - if (links) - { - await Reject(() => Save()); - Check(File.ReadAllText(target) == "outside"); - File.Delete(Path.Combine(root, "theme.m4a")); - File.CreateSymbolicLink(Path.Combine(root, ".themerr-connector.json"), target); - await Reject(() => Save()); - Check(File.ReadAllText(target) == "outside"); - } - else { Console.WriteLine("Symbolic-link checks skipped: creation privilege unavailable on this host."); } - } - finally { Directory.Delete(sibling, true); } - Console.WriteLine("Connector file ownership, integrity, and format checks passed."); -} -finally { Directory.Delete(root, true); } diff --git a/connectors/jellyfin.tests/ThemeFilesTests.cs b/connectors/jellyfin.tests/ThemeFilesTests.cs new file mode 100644 index 00000000..f8d2e5c5 --- /dev/null +++ b/connectors/jellyfin.tests/ThemeFilesTests.cs @@ -0,0 +1,206 @@ +using System.Security.Cryptography; +using System.Diagnostics; +using Themerr.Connector; +using Xunit; + +namespace Themerr.Connector.Tests; + +public sealed class ThemeFilesTests : IDisposable +{ + private readonly string _root = Path.Combine(Path.GetTempPath(), "themerr-connector-test-" + Guid.NewGuid()); + private readonly byte[] _audio = "complete validated audio"u8.ToArray(); + private string Digest => Convert.ToHexString(SHA256.HashData(_audio)).ToLowerInvariant(); + public ThemeFilesTests() => Directory.CreateDirectory(_root); + public void Dispose() => Directory.Delete(_root, true); + private string FilePath(string name) => Path.Combine(_root, name); + private Task Save(string? type = "audio/mp4", string? digest = null, int count = 0, + bool overwrite = false, Stream? stream = null, bool backup = true) => ThemeFiles.Save(_root, + stream ?? new MemoryStream(_audio), type, digest ?? Digest, count, CancellationToken.None, overwrite, backup); + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task WindowsJunctionsNeverDeleteSiblingThemes(bool nested) + { + if (!OperatingSystem.IsWindows()) Assert.Skip("Windows junction test."); + var sibling = _root + "-sibling"; + Directory.CreateDirectory(sibling); + var outside = Path.Combine(sibling, "outside.mp3"); + File.WriteAllText(outside, "outside theme"); + var music = FilePath("theme-music"); + if (nested) Directory.CreateDirectory(music); + var link = nested ? Path.Combine(music, "nested") : music; + var command = new ProcessStartInfo(Path.Combine(Environment.SystemDirectory, "cmd.exe")) + { UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true }; + foreach (var argument in new[] { "/c", "mklink", "/J", link, sibling }) command.ArgumentList.Add(argument); + try + { + using var process = Process.Start(command)!; + await process.WaitForExitAsync(TestContext.Current.CancellationToken); + Assert.Equal(0, process.ExitCode); + await Assert.ThrowsAsync(() => Save(count: 1, overwrite: true, backup: false)); + Assert.Equal("outside theme", File.ReadAllText(outside)); + } + finally + { + if (Directory.Exists(link)) Directory.Delete(link); + Directory.Delete(sibling, true); + } + } + + [Fact] + public async Task ReplacementCanRemoveUserThemesWithoutCreatingBackups() + { + File.WriteAllText(FilePath("theme.mp3"), "user theme"); + var music = Directory.CreateDirectory(FilePath("theme-music")); + File.WriteAllText(Path.Combine(music.FullName, "user.mp3"), "another theme"); + await Save(count: 2, overwrite: true, backup: false); + Assert.True(ThemeFiles.State(_root).Owned); + Assert.False(File.Exists(FilePath("theme.mp3"))); + Assert.False(Directory.Exists(music.FullName)); + Assert.False(Directory.Exists(FilePath(".themerr-user-themes"))); + Assert.False(Directory.Exists(FilePath(".themerr-user-theme-music"))); + } + + [Theory] + [InlineData(null)] + [InlineData("../../file")] + [InlineData("audio/mp3")] + public async Task RejectsUnknownContentTypes(string? type) + { + await Assert.ThrowsAsync(() => Save(type)); + Assert.Empty(Directory.GetFiles(_root)); + } + + [Theory] + [InlineData("short")] + [InlineData("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz")] + [InlineData("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")] + public async Task RejectsInvalidOrMismatchedHashes(string digest) + { + await Assert.ThrowsAsync(() => Save(digest: digest)); + Assert.Empty(Directory.GetFiles(_root)); + } + + [Fact] + public async Task RejectsEmptyAudioAndCleansTemporaryFiles() + { + await Assert.ThrowsAsync(() => Save(stream: new MemoryStream())); + Assert.Empty(Directory.GetFiles(_root)); + } + + [Fact] + public async Task SavesVerifiedAudioAndSwitchesFormats() + { + Assert.False(ThemeFiles.State(_root).Present); + var saved = await Save(); + Assert.True(saved.Owned); + Assert.Equal(Digest, saved.Sha256); + Assert.True(ThemeFiles.State(_root).Owned); + Assert.False(ThemeFiles.State(_root, 2).Owned); + await Assert.ThrowsAsync(() => Save(count: 2)); + await Save("audio/ogg", Digest.ToUpperInvariant()); + Assert.False(File.Exists(FilePath("theme.m4a"))); + Assert.Equal(_audio, File.ReadAllBytes(FilePath("theme.opus"))); + } + + [Fact] + public async Task PreservesUserThemesByDefaultIncludingManuallyEditedUploads() + { + File.WriteAllText(FilePath("theme.mp3"), "user theme"); + await Assert.ThrowsAsync(() => Save()); + Assert.Equal("user theme", File.ReadAllText(FilePath("theme.mp3"))); + File.Delete(FilePath("theme.mp3")); + await Save(); + File.WriteAllText(FilePath("theme.m4a"), "manually changed"); + Assert.False(ThemeFiles.State(_root).Owned); + await Assert.ThrowsAsync(() => Save()); + Assert.Equal("manually changed", File.ReadAllText(FilePath("theme.m4a"))); + } + + [Theory] + [InlineData("theme.mp3")] + [InlineData("theme.m4a")] + [InlineData("theme.opus")] + public async Task ExplicitReplacementBacksUpUserFiles(string name) + { + File.WriteAllText(FilePath(name), "user theme"); + var state = await Save(overwrite: true); + Assert.True(state.Owned); + Assert.True(ThemeFiles.State(_root).Owned); + Assert.Equal("user theme", File.ReadAllText(Path.Combine(_root, ".themerr-user-themes", name))); + Assert.Equal(_audio, File.ReadAllBytes(FilePath("theme.m4a"))); + } + + [Fact] + public async Task ReplacementBacksUpThemeMusicFolderAndNeverOverwritesEarlierBackups() + { + var music = FilePath("theme-music"); + Directory.CreateDirectory(music); + File.WriteAllText(Path.Combine(music, "my song.mp3"), "user theme"); + Assert.True(ThemeFiles.State(_root).Present); + await Assert.ThrowsAsync(() => Save(count: 1)); + await Save(count: 1, overwrite: true); + Assert.Equal("user theme", File.ReadAllText(Path.Combine(_root, ".themerr-user-theme-music", "my song.mp3"))); + Directory.CreateDirectory(music); + File.WriteAllText(Path.Combine(music, "second.mp3"), "another theme"); + await Assert.ThrowsAsync(() => Save(count: 2, overwrite: true)); + Assert.True(File.Exists(Path.Combine(music, "second.mp3"))); + } + + [Fact] + public async Task NativeThemesOutsideFixedResourcesStayProtected() + { + await Assert.ThrowsAsync(() => Save(count: 1, overwrite: true)); + Assert.False(File.Exists(FilePath("theme.m4a"))); + } + + [Theory] + [InlineData("not json")] + [InlineData("null")] + [InlineData("{\"File\":\"../outside.m4a\",\"Sha256\":\"anything\"}")] + public async Task CorruptOrTraversingOwnershipDoesNotAuthorizeReplacement(string ownership) + { + File.WriteAllText(FilePath("theme.m4a"), "user theme"); + File.WriteAllText(FilePath(".themerr-connector.json"), ownership); + Assert.False(ThemeFiles.State(_root).Owned); + await Assert.ThrowsAsync(() => Save()); + } + + [Fact] + public async Task OwnershipWithoutItsFileIsNotOwned() + { + await Save(); + File.Delete(FilePath("theme.m4a")); + Assert.False(ThemeFiles.State(_root).Present); + Assert.False(ThemeFiles.State(_root).Owned); + } + + [Theory] + [InlineData("theme.m4a", false)] + [InlineData(".themerr-connector.json", false)] + [InlineData("theme-music", true)] + [InlineData(".themerr-user-themes", true)] + public async Task RejectsLinksIntoSiblingDirectories(string name, bool directory) + { + var sibling = _root + "-sibling"; + Directory.CreateDirectory(sibling); + var target = Path.Combine(sibling, "outside.m4a"); + File.WriteAllText(target, "outside"); + try + { + try + { + if (directory) Directory.CreateSymbolicLink(FilePath(name), sibling); + else File.CreateSymbolicLink(FilePath(name), target); + } + catch (UnauthorizedAccessException) { Assert.Skip("Symbolic link creation privilege unavailable."); } + catch (IOException error) when (OperatingSystem.IsWindows() && (error.HResult & 0xffff) == 1314) + { Assert.Skip("Symbolic link creation privilege unavailable."); } + File.WriteAllText(FilePath("theme.mp3"), "user theme"); + await Assert.ThrowsAsync(() => Save(overwrite: true)); + Assert.Equal("outside", File.ReadAllText(target)); + } + finally { Directory.Delete(sibling, true); } + } +} diff --git a/connectors/jellyfin/Controller.cs b/connectors/jellyfin/Controller.cs index a7f31385..d87fe9e4 100644 --- a/connectors/jellyfin/Controller.cs +++ b/connectors/jellyfin/Controller.cs @@ -48,7 +48,8 @@ public async Task> Upload(Guid itemId, CancellationToke { var state = await ThemeFiles.Save(root, Request.Body, Request.ContentType, Request.Headers["X-Themerr-SHA256"].ToString(), item.GetThemeSongs().Count, - cancellationToken).ConfigureAwait(false); + cancellationToken, Request.Headers["X-Themerr-Overwrite-User"] == "true", + Request.Headers["X-Themerr-Backup-User"] != "false").ConfigureAwait(false); await item.RefreshMetadata(cancellationToken).ConfigureAwait(false); return state; } diff --git a/connectors/jellyfin/ThemeFiles.cs b/connectors/jellyfin/ThemeFiles.cs index 9a022007..56c6f947 100644 --- a/connectors/jellyfin/ThemeFiles.cs +++ b/connectors/jellyfin/ThemeFiles.cs @@ -15,6 +15,12 @@ public static class ThemeFiles { private const string OwnershipFile = ".themerr-connector.json"; private const long MaximumBytes = 104857600; + private const string UserBackup = ".themerr-user-themes"; + private const string ThemeMusic = "theme-music"; + private const string UserMusicBackup = ".themerr-user-theme-music"; + private static readonly string[] ThemeNames = + ["theme.m4a", "theme.opus", "theme.mp3", "theme.aac", "theme.ogg", "theme.oga", + "theme.flac", "theme.wav", "theme.wma", "theme.aiff", "theme.aif", "theme.webm"]; private static readonly Dictionary Formats = new() { ["audio/mp4"] = "theme.m4a", @@ -24,8 +30,11 @@ private sealed record Ownership(string File, string Sha256); public static ThemeState State(string root, int themeSongCount = 0) { - var files = Directory.GetFiles(root, "theme.*"); - var present = files.Length > 0 || themeSongCount > 0; + RejectDirectoryLink(root); + var files = ThemeNames.Where(name => File.Exists(Path.Combine(root, name))).ToArray(); + var music = Path.Combine(root, ThemeMusic); + RejectDirectoryLink(music); + var present = files.Length > 0 || themeSongCount > 0 || Directory.Exists(music); var metadata = Path.Combine(root, OwnershipFile); RejectLink(metadata); if (!File.Exists(metadata)) return new(present, false, null); @@ -34,7 +43,8 @@ public static ThemeState State(string root, int themeSongCount = 0) catch (JsonException) { return new(present, false, null); } // A metadata value selects a code-owned filename; it never becomes a path. var file = Formats.Values.FirstOrDefault(value => value == ownership?.File); - if (file is null || files.Length != 1 || themeSongCount > 1) return new(present, false, null); + if (file is null || files.Length != 1 || themeSongCount > 1 || Directory.Exists(music)) + return new(present, false, null); var path = Path.Combine(root, file); RejectLink(path); if (!File.Exists(path)) return new(present, false, null); @@ -44,12 +54,14 @@ public static ThemeState State(string root, int themeSongCount = 0) } public static async Task Save(string root, Stream body, string? contentType, - string expectedDigest, int themeSongCount, CancellationToken cancellationToken) + string expectedDigest, int themeSongCount, CancellationToken cancellationToken, + bool overwriteUser = false, bool backupUser = true) { if (contentType is null || !Formats.TryGetValue(contentType, out var file) || expectedDigest.Length != 64 || !expectedDigest.All(Uri.IsHexDigit)) throw new InvalidDataException(); var state = State(root, themeSongCount); - if (state.Present && !state.Owned) throw new ThemeConflictException(); + if (state.Present && !state.Owned && !overwriteUser) throw new ThemeConflictException(); + var originals = Snapshot(root); var path = Path.Combine(root, file); var metadata = Path.Combine(root, OwnershipFile); RejectLink(path); @@ -68,9 +80,11 @@ public static async Task Save(string root, Stream body, string? cont } // Recheck the ownership and link boundaries after receiving the complete body. var current = State(root, themeSongCount); - if (current.Present && (!current.Owned || current.Sha256 != state.Sha256)) throw new ThemeConflictException(); + if ((current.Present && !current.Owned && !overwriteUser) || + !originals.SequenceEqual(Snapshot(root))) throw new ThemeConflictException(); RejectLink(path); RejectLink(metadata); + if (current.Present && !current.Owned) ReplaceUserThemes(root, themeSongCount, backupUser); File.Move(temporary, path, true); File.Move(metadataTemporary, metadata, true); foreach (var other in Formats.Values.Where(value => value != file)) @@ -113,4 +127,73 @@ private static void RejectLink(string path) if (info.LinkTarget is not null || (info.Exists && (info.Attributes & FileAttributes.ReparsePoint) != 0)) throw new ThemeConflictException(); } + + private static void RejectDirectoryLink(string path) + { + var info = new DirectoryInfo(path); + if (info.LinkTarget is not null || (info.Exists && (info.Attributes & FileAttributes.ReparsePoint) != 0)) + throw new ThemeConflictException(); + } + + private static string[] Snapshot(string root) + { + var snapshot = new List(); + foreach (var name in ThemeNames) + { + var path = Path.Combine(root, name); + RejectLink(path); + if (!File.Exists(path)) continue; + using var stream = File.OpenRead(path); + snapshot.Add(name + ":" + Convert.ToHexString(SHA256.HashData(stream))); + } + var music = Path.Combine(root, ThemeMusic); + RejectDirectoryLink(music); + if (Directory.Exists(music)) snapshot.Add(Directory.GetLastWriteTimeUtc(music).Ticks.ToString()); + return snapshot.ToArray(); + } + + private static void ReplaceUserThemes(string root, int themeSongCount, bool backupUser) + { + var backup = Path.Combine(root, UserBackup); + var music = Path.Combine(root, ThemeMusic); + var musicBackup = Path.Combine(root, UserMusicBackup); + RejectDirectoryLink(music); + var names = ThemeNames.Where(name => File.Exists(Path.Combine(root, name))).ToArray(); + // Native themes outside these fixed resources must remain untouched. + if (themeSongCount > names.Length && !Directory.Exists(music)) throw new ThemeConflictException(); + if (!backupUser) + { + // Validate every entry before recursive deletion, including directory junctions. + if (Directory.Exists(music)) ValidateTree(new DirectoryInfo(music)); + foreach (var name in names) RejectLink(Path.Combine(root, name)); + foreach (var name in names) File.Delete(Path.Combine(root, name)); + if (Directory.Exists(music)) Directory.Delete(music, true); + return; + } + RejectDirectoryLink(backup); + RejectDirectoryLink(musicBackup); + if (names.Any(name => File.Exists(Path.Combine(backup, name))) || + (Directory.Exists(music) && Directory.Exists(musicBackup))) throw new ThemeConflictException(); + Directory.CreateDirectory(backup); + foreach (var name in names) + { + var source = Path.Combine(root, name); + var destination = Path.Combine(backup, name); + RejectLink(source); + RejectLink(destination); + } + foreach (var name in names) File.Move(Path.Combine(root, name), Path.Combine(backup, name)); + if (Directory.Exists(music)) Directory.Move(music, musicBackup); + } + + private static void ValidateTree(DirectoryInfo directory) + { + RejectDirectoryLink(directory.FullName); + foreach (var entry in directory.GetFileSystemInfos()) + { + if (entry.LinkTarget is not null || (entry.Attributes & FileAttributes.ReparsePoint) != 0) + throw new ThemeConflictException(); + if (entry is DirectoryInfo child) ValidateTree(child); + } + } } diff --git a/connectors/jellyfin/Themerr.Connector.csproj b/connectors/jellyfin/Themerr.Connector.csproj index bc4251e2..159deed4 100644 --- a/connectors/jellyfin/Themerr.Connector.csproj +++ b/connectors/jellyfin/Themerr.Connector.csproj @@ -11,6 +11,7 @@ 0.0.0.0 + diff --git a/docs/source/about/usage.rst b/docs/source/about/usage.rst index 29ff9e3f..16d9e75e 100644 --- a/docs/source/about/usage.rst +++ b/docs/source/about/usage.rst @@ -84,7 +84,7 @@ Select **Disconnect Plex** to erase Plex credentials and pause saved Plex server each server to resume processing with its retained upload history. On desktop systems, account and server tokens are saved in the operating system's credential store. -For Docker or headless systems, provide ``THEMERR_PLEX_TOKEN_KEY_FILE`` pointing to a persistent Fernet key file outside +For Docker or headless systems, provide ``THEMERR_TOKEN_KEY_FILE`` pointing to a persistent Fernet key file outside the configuration directory. Themerr-plex uses that key to encrypt tokens stored in SQLite. Keep the key file private; losing it requires signing in again. Docker sign-in requires this key file. @@ -172,27 +172,46 @@ ignored libraries, dashboard records, and upload history. Theme uploads require the small **Themerr Connector** bundled with this Themerr build. On the server card, enter a Themerr address reachable from the Jellyfin machine and select **Install matching connector**. Themerr registers its local plugin repository and installs the exact compatible connector through Jellyfin's API, -preserving other registered repositories. Restart Jellyfin to load the plugin, then refresh libraries in Themerr. -After upgrading Themerr, install its matching connector again if the server card reports a mismatch. +preserving other registered repositories. Themerr shows the pending restart on the server card, waits at least +30 seconds and, by default, waits until playing and paused streams finish before restarting Jellyfin. Libraries +are refreshed when the connector becomes active. Installations that cannot restart themselves, including some +service and container configurations, show instructions to restart manually. Automatic updates install a +mismatched connector when Themerr starts or checks the connection. Both automatic updates and restarts can be +disabled under **Settings > Jellyfin**. The download address must use a certificate trusted by Jellyfin or HTTP on your private network. Themerr's default self-signed HTTPS certificate is not automatically trusted by Jellyfin. A trusted reverse proxy can -provide the download address; alternatively, configure Themerr's network settings for HTTP on your private network. +provide the download address. For self-signed certificates, Themerr also serves only the connector repository +over HTTP on port 9495 by default; the administrator UI remains on HTTPS. Use an address Jellyfin can reach and +expose that port when running Docker. Configure or disable it under **Settings > Jellyfin > Connector HTTP port**. Jellyfin downloads the fixed manifest and archive routes without a Themerr admin session; all connection, installation, library, and theme APIs retain administrator authentication. The connector only accepts matching uploads from Themerr and stores files beside the native library item. Movies must have their own local folders; a shared folder containing unrelated movies cannot have independent themes. -Existing user themes are preserved, including themes created by the older Themerr-jellyfin plugin. Disable that -older plugin's processing when using this integration. Theme files changed manually after an upload are also -preserved. Themerr updates only themes whose current digest matches the connector's ownership record. +Existing user themes are preserved by default, including themes created by the older Themerr-jellyfin plugin. +Enable **Overwrite user themes** to replace them. **Back up replaced user themes** is enabled by default and +keeps replaced files in ``.themerr-user-themes`` and ``.themerr-user-theme-music`` beside the item. Disable that +separate option to remove replaced themes permanently. Existing backups are never overwritten. Themes outside +the connector's supported item resources remain protected. Manual edits are treated as user themes. +You can also enable **Remove the older Themerr-jellyfin plugin** to uninstall only that plugin and remove its +dedicated repository after installing the connector. Otherwise, disable its processing yourself. Locked items and ignored libraries follow the processing settings. Jellyfin uses Themerr's scheduled scans; **Activity > Scan for themes** also starts a scan. Movies, series, and collections can be enabled separately under **Settings > Jellyfin**. +Collection themes are stored in Jellyfin's own collection metadata directory, separately from media folders. +Jellyfin must have permission to write to the item's directory. **Discover Jellyfin on LAN** uses UDP port 7359; +discovery must be enabled on Jellyfin and allowed by the local network and firewall. An API key is still required. + +For theme playback, open **Jellyfin user settings > Display** and enable **Theme songs** in the **Library** section. +The server card links directly to this page. Jellyfin Web stores this choice locally in each browser, so its +REST API cannot enable it for all clients; repeat the choice in each browser or app that supports theme songs. + API keys use the operating system credential store on desktops. Headless and Docker installations use the -same ``THEMERR_PLEX_TOKEN_KEY_FILE`` encryption key as Plex, with a separate namespace for every Jellyfin server. -The historical environment variable name remains supported. Removing a connection deletes its saved API key +same ``THEMERR_TOKEN_KEY_FILE`` encryption key as Plex, with a separate namespace for every Jellyfin server. +The historical ``THEMERR_PLEX_TOKEN_KEY_FILE`` name remains supported; the generic name takes precedence if +both are set. Keep the same key when renaming the variable. Removing a connection deletes its saved API key and local records; themes already installed on Jellyfin remain there. Jellyfin provider IDs are resolved through ThemerrDB's index. Set ``TMDB_API_READ_ACCESS_TOKEN`` if you also diff --git a/docs/source/contributing/build.rst b/docs/source/contributing/build.rst index e68ae39d..70e738a6 100644 --- a/docs/source/contributing/build.rst +++ b/docs/source/contributing/build.rst @@ -74,13 +74,15 @@ Build the documentation and standalone executable: Dockle writes the site to _site. PyInstaller includes that site, the web assets, and translations in the executable under dist, including Deno for yt-dlp and both Jellyfin connector archives. The standalone build -rebuilds the connectors after stamping the release version. macOS also produces ``dist/themerr_plex.app``, a directory +rebuilds the connectors after stamping the release version. CI builds both connectors once and shares the artifact +with all desktop packaging jobs. ``THEMERR_PREBUILT_CONNECTOR=1`` validates the artifact's source identity, release, +ABI and checksums instead of compiling again. macOS also produces ``dist/themerr_plex.app``, a directory bundle needed for native notifications. Set ``THEMERR_VERSION`` to the release version before running ``scripts/build.py`` to stamp ``src/common/version.py`` with the version used by release notifications and ``--version``. CI supplies this value from the release setup action's ``release_version`` output. The module is included through normal imports. Unversioned builds compare releases against ``0.0.0``. -Docker uses the same lockfile and Dockle build. A separate .NET build stage compiles both connectors; +Docker uses the same lockfile and Dockle build. A separate .NET stage uses uv to run the connector build script; the runtime image contains their artifacts without the SDK: .. code-block:: shell diff --git a/docs/source/contributing/testing.rst b/docs/source/contributing/testing.rst index d19ba2f0..ffe0845e 100644 --- a/docs/source/contributing/testing.rst +++ b/docs/source/contributing/testing.rst @@ -24,6 +24,17 @@ metadata lookups, and theme playback isolation. Browser playback and dashboard f Use an isolated configuration file for manual browser testing so sample data and credentials do not affect your normal installation. +Connector tests run the same xUnit suite against Jellyfin 10.11 and 12.1. Install the .NET 10 SDK and +the .NET 9 and 10 ASP.NET Core runtimes, then run: + +.. code-block:: shell + + uv run --no-project --python 3.14 scripts/test_connector.py + +The script writes OpenCover coverage and JUnit test reports into separate ``coverage/connector-*`` directories. +Use ``--output`` to place reports outside the checkout. CI uploads both profiles to Codecov. Tests use temporary +item directories and mocked Jellyfin services; no running media server is required. + For documentation validation, install the docs extra and build with Dockle: .. code-block:: shell diff --git a/locale/themerr-plex.po b/locale/themerr-plex.po index ea26df4b..e35a6158 100644 --- a/locale/themerr-plex.po +++ b/locale/themerr-plex.po @@ -188,7 +188,7 @@ msgid "" "Servers page." msgstr "" -#: src/common/config.py:242 src/common/config.py:299 +#: src/common/config.py:242 src/common/config.py:338 msgid "Movie support" msgstr "" @@ -196,7 +196,7 @@ msgstr "" msgid "Add themes to Jellyfin movies." msgstr "" -#: src/common/config.py:246 src/common/config.py:305 +#: src/common/config.py:246 src/common/config.py:344 msgid "Series support" msgstr "" @@ -204,194 +204,273 @@ msgstr "" msgid "Add themes to Jellyfin series." msgstr "" -#: src/common/config.py:250 src/common/config.py:293 web/templates/home.html:30 +#: src/common/config.py:250 src/common/config.py:332 web/templates/home.html:31 msgid "Collections" msgstr "" #: src/common/config.py:251 -msgid "Add themes to Jellyfin collections." +msgid "" +"Add themes inside Jellyfin’s collection directories. Media files can " +"remain elsewhere." +msgstr "" + +#: src/common/config.py:254 +msgid "Overwrite user themes" +msgstr "" + +#: src/common/config.py:255 +msgid "" +"Replace existing theme files, including themes created by the older " +"Themerr-jellyfin plugin." +msgstr "" + +#: src/common/config.py:259 +msgid "Back up replaced user themes" +msgstr "" + +#: src/common/config.py:260 +msgid "" +"Keep existing user themes in a backup folder when replacing them. Disable" +" to permanently remove replaced themes." +msgstr "" + +#: src/common/config.py:264 +msgid "Ignore locked metadata" +msgstr "" + +#: src/common/config.py:265 +msgid "Allow Jellyfin theme updates for items with locked metadata." +msgstr "" + +#: src/common/config.py:268 +msgid "Update connector automatically" +msgstr "" + +#: src/common/config.py:269 +msgid "" +"Install the matching connector when Themerr starts or detects a version " +"mismatch. Set the reachable Themerr address on the Servers page first." +msgstr "" + +#: src/common/config.py:273 +msgid "Restart Jellyfin automatically" +msgstr "" + +#: src/common/config.py:274 +msgid "" +"Restart Jellyfin after installing or updating the connector. A pending " +"restart is shown on the Servers page before it happens." msgstr "" -#: src/common/config.py:256 +#: src/common/config.py:278 +msgid "Wait for playback to finish" +msgstr "" + +#: src/common/config.py:279 +msgid "Defer connector restarts until there are no playing or paused streams." +msgstr "" + +#: src/common/config.py:282 +msgid "Remove the older Themerr-jellyfin plugin" +msgstr "" + +#: src/common/config.py:283 +msgid "" +"After installing the connector, uninstall the older Themerr plugin and " +"remove only its dedicated repository. Existing theme files are kept." +msgstr "" + +#: src/common/config.py:287 +msgid "Connector HTTP port" +msgstr "" + +#: src/common/config.py:288 +msgid "" +"Serve only connector downloads over HTTP when Themerr uses a self-signed " +"certificate. The admin UI stays on HTTPS. Use 0 to disable. Restart " +"Themerr after changing this port." +msgstr "" + +#: src/common/config.py:295 msgid "Plex" msgstr "" -#: src/common/config.py:257 +#: src/common/config.py:296 msgid "Plex settings." msgstr "" -#: src/common/config.py:261 +#: src/common/config.py:300 msgid "Plex URL" msgstr "" -#: src/common/config.py:262 +#: src/common/config.py:301 msgid "The URL to the Plex server." msgstr "" -#: src/common/config.py:271 web/templates/servers.html:20 +#: src/common/config.py:310 web/templates/servers.html:33 msgid "Plex data directory" msgstr "" -#: src/common/config.py:272 +#: src/common/config.py:311 msgid "" "https://support.plex.tv/articles/202915258-where-is-the-plex-media-" "server-data-directory-located/" msgstr "" -#: src/common/config.py:282 +#: src/common/config.py:321 msgid "Themerr" msgstr "" -#: src/common/config.py:283 +#: src/common/config.py:322 msgid "Themerr settings." msgstr "" -#: src/common/config.py:287 +#: src/common/config.py:326 msgid "Themerr Enabled" msgstr "" -#: src/common/config.py:288 +#: src/common/config.py:327 msgid "When enabled, Themerr will attempt to update themes." msgstr "" -#: src/common/config.py:294 +#: src/common/config.py:333 msgid "Add themes to collections." msgstr "" -#: src/common/config.py:300 +#: src/common/config.py:339 msgid "Add themes to movies using the Plex Movie agent." msgstr "" -#: src/common/config.py:306 +#: src/common/config.py:345 msgid "Add themes to series using the Plex Series agent." msgstr "" -#: src/common/config.py:311 +#: src/common/config.py:350 msgid "Overwrite Plex themes" msgstr "" -#: src/common/config.py:312 +#: src/common/config.py:351 msgid "When enabled, Themerr will overwrite themes provided by Plex." msgstr "" -#: src/common/config.py:317 +#: src/common/config.py:356 msgid "Prefer MP4A AAC Codec" msgstr "" -#: src/common/config.py:318 +#: src/common/config.py:357 msgid "This can improve theme compatibility with Apple devices." msgstr "" -#: src/common/config.py:323 +#: src/common/config.py:362 msgid "Remove unused themes" msgstr "" -#: src/common/config.py:324 +#: src/common/config.py:363 msgid "" "Frees up space in your Plex metadata directory. This option requires that" " the \"Plex data directory\" path is set" msgstr "" -#: src/common/config.py:330 +#: src/common/config.py:369 msgid "Remove unused art" msgstr "" -#: src/common/config.py:331 src/common/config.py:339 +#: src/common/config.py:370 src/common/config.py:378 msgid "" "Applies to collection. Frees up space in your Plex metadata directory. " "This option requires that the \"Plex data directory\" path is set" msgstr "" -#: src/common/config.py:338 +#: src/common/config.py:377 msgid "Remove unused posters" msgstr "" -#: src/common/config.py:346 +#: src/common/config.py:385 msgid "Collection metadata" msgstr "" -#: src/common/config.py:347 +#: src/common/config.py:386 msgid "" "Update collection metadata (poster, art, and summary) during scheduled " "update." msgstr "" -#: src/common/config.py:352 +#: src/common/config.py:391 msgid "Ignore locked fields." msgstr "" -#: src/common/config.py:353 +#: src/common/config.py:392 msgid "" "If you used Themerr-plex v2024.813.13709 or lower, you may need to enable" " this to update items." msgstr "" -#: src/common/config.py:359 +#: src/common/config.py:398 msgid "Ignored library IDs" msgstr "" -#: src/common/config.py:360 +#: src/common/config.py:399 msgid "" "Comma-separated library IDs to exclude from scheduled updates. IDs appear" " on Home." msgstr "" -#: src/common/config.py:366 +#: src/common/config.py:405 msgid "Update themes interval" msgstr "" -#: src/common/config.py:367 +#: src/common/config.py:406 msgid "Interval for automatic update task, in minutes (min: 15)." msgstr "" -#: src/common/config.py:374 +#: src/common/config.py:413 msgid "Update database cache interval" msgstr "" -#: src/common/config.py:375 +#: src/common/config.py:414 msgid "Interval for database cache update task, in minutes (min: 15)." msgstr "" -#: src/common/config.py:382 +#: src/common/config.py:421 msgid "PlexAPI timeout" msgstr "" -#: src/common/config.py:383 +#: src/common/config.py:422 msgid "" "Increase this slightly if you experience timeouts when adding themes. " "(min: 1)" msgstr "" -#: src/common/config.py:390 +#: src/common/config.py:429 msgid "Theme upload retries" msgstr "" -#: src/common/config.py:391 +#: src/common/config.py:430 msgid "If uploading themes fail, retry this many times. (min: 0)" msgstr "" -#: src/common/config.py:398 +#: src/common/config.py:437 msgid "Multiprocessing thread count" msgstr "" -#: src/common/config.py:399 +#: src/common/config.py:438 msgid "" "The number of threads to use when adding themes. Restart required. (min: " "1)" msgstr "" -#: src/common/config.py:406 +#: src/common/config.py:445 msgid "YouTube Cookies" msgstr "" -#: src/common/config.py:408 +#: src/common/config.py:447 msgid "" "Optional: export youtube.com cookies with Get cookies.txt LOCALLY using " "its JSON format, then paste the entire JSON array here and save. Cookies " "contain your browser session; keep them private." msgstr "" -#: src/common/config.py:413 +#: src/common/config.py:452 msgid "Cookie export instructions" msgstr "" @@ -485,7 +564,7 @@ msgstr "" msgid "Logs" msgstr "" -#: src/common/webapp.py:478 web/templates/config.html:22 +#: src/common/webapp.py:478 web/templates/config.html:23 #: web/templates/navbar.html:6 msgid "Settings" msgstr "" @@ -552,7 +631,7 @@ msgstr "" msgid "Discard unsaved settings changes?" msgstr "" -#: web/js/config.js:44 web/templates/config.html:36 +#: web/js/config.js:44 web/templates/config.html:37 msgid "All changes saved." msgstr "" @@ -596,8 +675,8 @@ msgstr "" msgid "Live" msgstr "" -#: web/js/logs.js:153 web/js/logs.js:206 web/templates/home.html:38 -#: web/templates/servers.html:13 +#: web/js/logs.js:153 web/js/logs.js:206 web/templates/home.html:40 +#: web/templates/servers.html:14 msgid "Paused" msgstr "" @@ -605,7 +684,7 @@ msgstr "" msgid "Refresh failed. Showing the last loaded records." msgstr "" -#: web/js/servers.js:13 web/templates/servers.html:25 +#: web/js/servers.js:13 web/templates/servers.html:38 msgid "Select libraries to ignore" msgstr "" @@ -629,6 +708,20 @@ msgid "" "list to retry." msgstr "" +#: web/js/servers.js:232 +msgid "Looking for Jellyfin servers…" +msgstr "" + +#: web/js/servers.js:238 +msgid "" +"No Jellyfin servers responded. Check UDP port 7359 and local network " +"discovery, or enter an address manually." +msgstr "" + +#: web/js/servers.js:250 +msgid "Use this address" +msgstr "" + #: web/templates/activity.html:3 msgid "See what Themerr is working on, and why an item might need your attention." msgstr "" @@ -663,8 +756,8 @@ msgid "" "refresh." msgstr "" -#: web/templates/activity.html:7 web/templates/home.html:14 -#: web/templates/home.html:31 web/templates/servers.html:13 +#: web/templates/activity.html:7 web/templates/home.html:15 +#: web/templates/home.html:32 web/templates/servers.html:14 msgid "Needs attention" msgstr "" @@ -672,11 +765,11 @@ msgstr "" msgid "No processing failures recorded. All quiet here." msgstr "" -#: web/templates/activity.html:8 web/templates/home.html:64 +#: web/templates/activity.html:8 web/templates/home.html:66 msgid "ID" msgstr "" -#: web/templates/activity.html:8 web/templates/home.html:78 +#: web/templates/activity.html:8 web/templates/home.html:80 msgid "Outdated from ThemerrDB" msgstr "" @@ -684,6 +777,10 @@ msgstr "" msgid "Explore the API using your signed-in session." msgstr "" +#: web/templates/auth.html:5 web/templates/navbar.html:3 +msgid "YOUR LIBRARY’S SOUNDTRACK" +msgstr "" + #: web/templates/auth.html:7 msgid "THE SOUND OF YOUR LIBRARY" msgstr "" @@ -698,8 +795,8 @@ msgstr "" #: web/templates/auth.html:9 msgid "" -"Bring your Plex libraries to life. One calm workspace for your servers, " -"your themes, and everything in between." +"Bring your libraries to life. One calm workspace for your servers, your " +"themes, and everything in between." msgstr "" #: web/templates/auth.html:18 @@ -767,7 +864,7 @@ msgid "Sign in" msgstr "" #: web/templates/auth.html:39 -msgid "Plex account linking happens inside your workspace." +msgid "Connect your media servers inside your workspace." msgstr "" #: web/templates/base.html:21 @@ -794,59 +891,59 @@ msgstr "" msgid "Theme: Dark. Switch to automatic mode." msgstr "" -#: web/templates/config.html:15 +#: web/templates/config.html:16 msgid "Browse directories for" msgstr "" -#: web/templates/config.html:22 +#: web/templates/config.html:23 msgid "TUNE YOUR WORKSPACE" msgstr "" -#: web/templates/config.html:22 +#: web/templates/config.html:23 msgid "Make Themerr work the way your library does." msgstr "" -#: web/templates/config.html:24 +#: web/templates/config.html:25 msgid "Settings sections" msgstr "" -#: web/templates/config.html:24 web/templates/config.html:33 +#: web/templates/config.html:25 web/templates/config.html:34 msgid "Security" msgstr "" -#: web/templates/config.html:28 +#: web/templates/config.html:29 msgid "" "Server connections, data directories, and ignored libraries are managed " "on the Servers page." msgstr "" -#: web/templates/config.html:28 +#: web/templates/config.html:29 msgid "Changes to network settings take effect after restarting Themerr." msgstr "" -#: web/templates/config.html:33 +#: web/templates/config.html:34 msgid "" "One administrator for this installation. Change your password and sign " "out other sessions." msgstr "" -#: web/templates/config.html:34 +#: web/templates/config.html:35 msgid "Current password" msgstr "" -#: web/templates/config.html:34 +#: web/templates/config.html:35 msgid "New password" msgstr "" -#: web/templates/config.html:34 +#: web/templates/config.html:35 msgid "Confirm new password" msgstr "" -#: web/templates/config.html:34 +#: web/templates/config.html:35 msgid "Change password" msgstr "" -#: web/templates/config.html:36 +#: web/templates/config.html:37 msgid "Save changes" msgstr "" @@ -878,258 +975,258 @@ msgstr "" msgid "Back to workspace" msgstr "" -#: web/templates/home.html:4 +#: web/templates/home.html:5 msgid "YOUR LIBRARY, IN HARMONY" msgstr "" -#: web/templates/home.html:4 +#: web/templates/home.html:5 msgid "" "A soundtrack for every story. See what’s playing, and what needs a little" " attention." msgstr "" -#: web/templates/home.html:4 web/templates/navbar.html:6 +#: web/templates/home.html:5 web/templates/navbar.html:6 msgid "Overview" msgstr "" -#: web/templates/home.html:5 +#: web/templates/home.html:6 msgid "Refresh libraries" msgstr "" -#: web/templates/home.html:7 +#: web/templates/home.html:8 msgid "Unknown" msgstr "" -#: web/templates/home.html:7 +#: web/templates/home.html:8 msgid "" "GitHub check failed. Showing the last known deployment; checks run at " "most hourly." msgstr "" -#: web/templates/home.html:8 +#: web/templates/home.html:9 msgid "Last deployed" msgstr "" -#: web/templates/home.html:8 +#: web/templates/home.html:9 msgid "Checking…" msgstr "" -#: web/templates/home.html:8 +#: web/templates/home.html:9 msgid "Check unavailable" msgstr "" -#: web/templates/home.html:11 +#: web/templates/home.html:12 msgid "Library items" msgstr "" -#: web/templates/home.html:11 +#: web/templates/home.html:12 msgid "libraries across" msgstr "" -#: web/templates/home.html:11 +#: web/templates/home.html:12 msgid "servers" msgstr "" -#: web/templates/home.html:12 +#: web/templates/home.html:13 msgid "Themes installed" msgstr "" -#: web/templates/home.html:12 +#: web/templates/home.html:13 msgid "Your library sounds better already." msgstr "" -#: web/templates/home.html:13 +#: web/templates/home.html:14 msgid "Theme coverage" msgstr "" -#: web/templates/home.html:14 +#: web/templates/home.html:15 msgid "Review activity" msgstr "" -#: web/templates/home.html:19 +#: web/templates/home.html:20 msgid "LET’S SET THE TONE" msgstr "" -#: web/templates/home.html:20 +#: web/templates/home.html:21 msgid "Your library deserves a soundtrack." msgstr "" -#: web/templates/home.html:20 +#: web/templates/home.html:21 msgid "Getting to know your library." msgstr "" -#: web/templates/home.html:21 +#: web/templates/home.html:22 msgid "" "Connect Plex or Jellyfin, choose your servers, and let Themerr take care " "of the music." msgstr "" -#: web/templates/home.html:21 +#: web/templates/home.html:22 msgid "" "Your first refresh is building the library view. Check Activity for " "progress, or request a refresh above." msgstr "" -#: web/templates/home.html:22 +#: web/templates/home.html:23 msgid "Connect a server" msgstr "" -#: web/templates/home.html:22 web/templates/home_db_not_cached.html:3 +#: web/templates/home.html:23 web/templates/home_db_not_cached.html:3 msgid "View activity" msgstr "" -#: web/templates/home.html:26 +#: web/templates/home.html:27 msgid "Your libraries" msgstr "" -#: web/templates/home.html:26 +#: web/templates/home.html:27 msgid "items" msgstr "" -#: web/templates/home.html:28 +#: web/templates/home.html:29 msgid "Search library items" msgstr "" -#: web/templates/home.html:28 +#: web/templates/home.html:29 msgid "Search titles…" msgstr "" -#: web/templates/home.html:28 web/templates/logs.html:9 +#: web/templates/home.html:29 web/templates/logs.html:9 msgid "Clear search" msgstr "" -#: web/templates/home.html:29 +#: web/templates/home.html:30 msgid "Server" msgstr "" -#: web/templates/home.html:29 +#: web/templates/home.html:30 msgid "All servers" msgstr "" -#: web/templates/home.html:30 +#: web/templates/home.html:31 msgid "Type" msgstr "" -#: web/templates/home.html:30 +#: web/templates/home.html:31 msgid "All types" msgstr "" -#: web/templates/home.html:30 +#: web/templates/home.html:31 msgid "Movies" msgstr "" -#: web/templates/home.html:30 +#: web/templates/home.html:31 msgid "TV shows" msgstr "" -#: web/templates/home.html:31 +#: web/templates/home.html:32 msgid "Theme status" msgstr "" -#: web/templates/home.html:31 +#: web/templates/home.html:32 msgid "All statuses" msgstr "" -#: web/templates/home.html:31 +#: web/templates/home.html:32 msgid "Has a theme" msgstr "" -#: web/templates/home.html:31 +#: web/templates/home.html:32 msgid "No theme" msgstr "" -#: web/templates/home.html:32 +#: web/templates/home.html:33 msgid "Reset filters" msgstr "" -#: web/templates/home.html:38 +#: web/templates/home.html:40 msgid "Library ID" msgstr "" -#: web/templates/home.html:43 +#: web/templates/home.html:45 msgid "Title" msgstr "" -#: web/templates/home.html:43 +#: web/templates/home.html:45 msgid "Metadata ID" msgstr "" -#: web/templates/home.html:43 +#: web/templates/home.html:45 msgid "Theme" msgstr "" -#: web/templates/home.html:43 +#: web/templates/home.html:45 msgid "ThemerrDB" msgstr "" -#: web/templates/home.html:56 +#: web/templates/home.html:58 msgid "Pause theme for" msgstr "" -#: web/templates/home.html:56 web/templates/home.html:58 +#: web/templates/home.html:58 web/templates/home.html:60 msgid "Play theme for" msgstr "" -#: web/templates/home.html:57 web/templates/theme_player.html:2 +#: web/templates/home.html:59 web/templates/theme_player.html:2 msgid "" "Unable to play this theme. Check the server connection and browser audio " "support." msgstr "" -#: web/templates/home.html:64 +#: web/templates/home.html:66 #, python-format msgid "Open in %(server)s" msgstr "" -#: web/templates/home.html:68 web/templates/home.html:76 +#: web/templates/home.html:70 web/templates/home.html:78 msgid "External ID unavailable" msgstr "" -#: web/templates/home.html:70 +#: web/templates/home.html:72 msgid "Failed to add theme" msgstr "" -#: web/templates/home.html:71 +#: web/templates/home.html:73 msgid "Plex provided" msgstr "" -#: web/templates/home.html:72 +#: web/templates/home.html:74 msgid "User provided" msgstr "" -#: web/templates/home.html:73 +#: web/templates/home.html:75 msgid "Themerr provided" msgstr "" -#: web/templates/home.html:74 +#: web/templates/home.html:76 msgid "Unknown provider" msgstr "" -#: web/templates/home.html:75 +#: web/templates/home.html:77 msgid "Missing from ThemerrDB" msgstr "" -#: web/templates/home.html:76 +#: web/templates/home.html:78 msgid "TMDB ID unavailable" msgstr "" -#: web/templates/home.html:77 +#: web/templates/home.html:79 msgid "Theme not installed yet" msgstr "" -#: web/templates/home.html:80 +#: web/templates/home.html:82 msgid "Edit" msgstr "" -#: web/templates/home.html:80 +#: web/templates/home.html:82 msgid "Add" msgstr "" -#: web/templates/home.html:87 +#: web/templates/home.html:89 msgid "No titles match." msgstr "" -#: web/templates/home.html:87 +#: web/templates/home.html:89 msgid "Try another search or reset your filters." msgstr "" @@ -1267,7 +1364,7 @@ msgstr "" msgid "Workspace pages" msgstr "" -#: web/templates/navbar.html:6 web/templates/servers.html:3 +#: web/templates/navbar.html:6 web/templates/servers.html:4 msgid "Servers" msgstr "" @@ -1295,212 +1392,252 @@ msgstr "" msgid "Sign out" msgstr "" -#: web/templates/servers.html:3 +#: web/templates/servers.html:4 msgid "ONE WORKSPACE. EVERY SERVER." msgstr "" -#: web/templates/servers.html:3 +#: web/templates/servers.html:4 msgid "Connect Plex or Jellyfin, then choose where the music goes." msgstr "" -#: web/templates/servers.html:3 +#: web/templates/servers.html:4 msgid "saved" msgstr "" -#: web/templates/servers.html:6 +#: web/templates/servers.html:7 msgid "Your Plex account" msgstr "" -#: web/templates/servers.html:6 +#: web/templates/servers.html:7 msgid "Account linked. Choose a server below." msgstr "" -#: web/templates/servers.html:6 +#: web/templates/servers.html:7 msgid "Link your account securely through Plex to access your servers." msgstr "" -#: web/templates/servers.html:6 +#: web/templates/servers.html:7 msgid "Continue Plex sign-in" msgstr "" -#: web/templates/servers.html:7 +#: web/templates/servers.html:8 msgid "Reconnect account" msgstr "" -#: web/templates/servers.html:7 +#: web/templates/servers.html:8 msgid "Connect Plex account" msgstr "" -#: web/templates/servers.html:7 +#: web/templates/servers.html:8 msgid "Disconnect" msgstr "" -#: web/templates/servers.html:9 +#: web/templates/servers.html:10 msgid "Connected servers" msgstr "" -#: web/templates/servers.html:9 +#: web/templates/servers.html:10 msgid "Theme history and library settings are kept separately for each server." msgstr "" -#: web/templates/servers.html:13 +#: web/templates/servers.html:14 msgid "Enabled" msgstr "" -#: web/templates/servers.html:16 +#: web/templates/servers.html:20 msgid "Themerr address reachable from Jellyfin" msgstr "" -#: web/templates/servers.html:16 +#: web/templates/servers.html:22 +msgid "" +"Use the connector-only HTTP address for Themerr’s self-signed " +"certificate, or an HTTPS address with a certificate Jellyfin trusts. For " +"remote Jellyfin or Docker, replace localhost with an address reachable " +"from Jellyfin." +msgstr "" + +#: web/templates/servers.html:23 +msgid "Jellyfin will restart after installation, once playback finishes." +msgstr "" + +#: web/templates/servers.html:23 +msgid "" +"Jellyfin will restart after installation. Active playback may be " +"interrupted." +msgstr "" + +#: web/templates/servers.html:23 +msgid "Automatic restarts are disabled. Restart Jellyfin after installation." +msgstr "" + +#: web/templates/servers.html:24 msgid "" -"Install the connector bundled with this Themerr build, then restart " -"Jellyfin. Use a trusted HTTPS certificate or an HTTP address on your " -"private network." +"The older Themerr-jellyfin plugin and its repository will be removed. " +"Existing themes will be kept." msgstr "" -#: web/templates/servers.html:16 +#: web/templates/servers.html:25 msgid "Install matching connector" msgstr "" -#: web/templates/servers.html:16 +#: web/templates/servers.html:28 +msgid "" +"Enable Theme songs in each Jellyfin client’s Display settings. Jellyfin " +"stores this preference in the client, so Themerr cannot change it through" +" the server API." +msgstr "" + +#: web/templates/servers.html:28 +msgid "Open Jellyfin display settings" +msgstr "" + +#: web/templates/servers.html:29 msgid "Last library refresh" msgstr "" -#: web/templates/servers.html:16 +#: web/templates/servers.html:29 msgid "Not yet refreshed" msgstr "" -#: web/templates/servers.html:17 +#: web/templates/servers.html:30 msgid "Processing settings" msgstr "" -#: web/templates/servers.html:19 +#: web/templates/servers.html:32 msgid "Process this server" msgstr "" -#: web/templates/servers.html:20 +#: web/templates/servers.html:33 msgid "optional" msgstr "" -#: web/templates/servers.html:21 +#: web/templates/servers.html:34 msgid "Browse Plex data directory" msgstr "" -#: web/templates/servers.html:22 +#: web/templates/servers.html:35 msgid "" "A local or mounted Plex data directory is only needed to remove older " "uploaded files. Leave this empty for remote servers." msgstr "" -#: web/templates/servers.html:23 +#: web/templates/servers.html:36 msgid "Ignored libraries" msgstr "" -#: web/templates/servers.html:29 +#: web/templates/servers.html:42 msgid "Library" msgstr "" -#: web/templates/servers.html:32 +#: web/templates/servers.html:45 msgid "Open the list to load this server’s libraries." msgstr "" -#: web/templates/servers.html:35 +#: web/templates/servers.html:48 msgid "" "Select the libraries to exclude from theme updates. Leave all unchecked " "to process every library." msgstr "" -#: web/templates/servers.html:36 +#: web/templates/servers.html:49 msgid "Save settings" msgstr "" -#: web/templates/servers.html:36 +#: web/templates/servers.html:49 msgid "Remove server" msgstr "" -#: web/templates/servers.html:40 +#: web/templates/servers.html:53 msgid "Your first server starts here." msgstr "" -#: web/templates/servers.html:40 +#: web/templates/servers.html:53 msgid "Link Plex above or add a Jellyfin server below." msgstr "" -#: web/templates/servers.html:43 +#: web/templates/servers.html:56 msgid "Add a server" msgstr "" -#: web/templates/servers.html:43 +#: web/templates/servers.html:56 msgid "" "Choose a server from your Plex account, discover one nearby, or use an " "address you know." msgstr "" -#: web/templates/servers.html:44 +#: web/templates/servers.html:57 msgid "Find account servers" msgstr "" -#: web/templates/servers.html:44 +#: web/templates/servers.html:57 msgid "Discover on LAN" msgstr "" -#: web/templates/servers.html:44 +#: web/templates/servers.html:57 msgid "LAN discovery uses Plex GDM on your application’s network." msgstr "" -#: web/templates/servers.html:45 +#: web/templates/servers.html:58 msgid "" "Account addresses are advertised by Plex and may be unreachable. HTTPS " "plex.direct addresses use Plex’s certificate. Choose an address reachable" " from the machine running Themerr, or enter a known address manually." msgstr "" -#: web/templates/servers.html:47 +#: web/templates/servers.html:60 msgid "Enter an address manually" msgstr "" -#: web/templates/servers.html:48 +#: web/templates/servers.html:61 msgid "Plex server address" msgstr "" -#: web/templates/servers.html:48 +#: web/templates/servers.html:61 msgid "Connect server" msgstr "" -#: web/templates/servers.html:48 +#: web/templates/servers.html:61 msgid "" "Use a LAN address or a valid HTTPS Plex address. Your linked Plex account" " must have access. Adding an existing server updates its address." msgstr "" -#: web/templates/servers.html:52 +#: web/templates/servers.html:65 msgid "Add Jellyfin" msgstr "" -#: web/templates/servers.html:52 +#: web/templates/servers.html:65 msgid "" "Jellyfin 10.11 and 12.1 are supported. Create an administrator API key in" " Jellyfin Dashboard - API Keys." msgstr "" -#: web/templates/servers.html:54 +#: web/templates/servers.html:67 msgid "Jellyfin server address" msgstr "" -#: web/templates/servers.html:56 +#: web/templates/servers.html:69 msgid "Jellyfin API key" msgstr "" -#: web/templates/servers.html:58 +#: web/templates/servers.html:71 msgid "" "Keys stay in the secure credential store. After connecting, install the " "matching connector on the server card above." msgstr "" -#: web/templates/servers.html:59 +#: web/templates/servers.html:72 msgid "Connect Jellyfin" msgstr "" +#: web/templates/servers.html:74 +msgid "Discover Jellyfin on LAN" +msgstr "" + +#: web/templates/servers.html:74 +msgid "Uses UDP port 7359 on Themerr’s network. An API key is still required." +msgstr "" + #: web/templates/theme_player.html:1 msgid "Theme player" msgstr "" diff --git a/scripts/build.py b/scripts/build.py index c10b4079..843c61a5 100644 --- a/scripts/build.py +++ b/scripts/build.py @@ -15,8 +15,12 @@ def build_connector(): - """Compile all supported Jellyfin ABIs before collecting package resources.""" - runpy.run_path(str(Path(__file__).with_name('build_connector.py')))['build']() + """Validate CI's shared artifacts or compile the connectors for a local build.""" + builder = runpy.run_path(str(Path(__file__).with_name('build_connector.py'))) + if os.environ.get('THEMERR_PREBUILT_CONNECTOR') == '1': + builder['check_bundle']() + else: + builder['build']() def build(): diff --git a/scripts/build_connector.py b/scripts/build_connector.py index 5c1126aa..2728347a 100644 --- a/scripts/build_connector.py +++ b/scripts/build_connector.py @@ -12,14 +12,18 @@ import zipfile ROOT = Path(__file__).resolve().parents[1] -PROFILES = {'10.11': ('10.11.0', 'net9.0'), '12.1': ('12.1.0', 'net10.0')} +PROFILES = { + '10.11': ('10.11.0', 'net9.0'), + '12.1': ('12.1.0', 'net10.0'), +} PLUGIN_ID = 'f9a117dc-b44a-4507-9706-241837784369' PLUGIN_NAME = 'Themerr Connector' +VERSION_FILE = 'src/common/version.py' def build_identity(root=ROOT, version=None): """Hash connector sources, compatibility profiles, and the Themerr release identity.""" - version = version or runpy.run_path(str(root / 'src/common/version.py'))['VERSION'] + version = version or runpy.run_path(str(root / VERSION_FILE))['VERSION'] digest = hashlib.sha256(version.encode()) digest.update(json.dumps(PROFILES, sort_keys=True).encode()) for path in sorted((root / 'connectors/jellyfin').glob('*')): @@ -45,9 +49,9 @@ def assembly_version(version): def build(dotnet='dotnet', root=ROOT): """Compile every supported ABI and write fixed archive names and their checksums.""" - release = os.environ.get('THEMERR_VERSION') or runpy.run_path(str(root / 'src/common/version.py'))['VERSION'] + release = os.environ.get('THEMERR_VERSION') or runpy.run_path(str(root / VERSION_FILE))['VERSION'] if os.environ.get('THEMERR_VERSION'): - (root / 'src/common/version.py').write_text( + (root / VERSION_FILE).write_text( f'"""Release identity stamped by the release setup action."""\n\nVERSION = {release!r}\n', encoding='utf-8') identity = build_identity(root, release) base_version = assembly_version(release) @@ -81,7 +85,29 @@ def build(dotnet='dotnet', root=ROOT): return descriptor +def check_bundle(root=ROOT): + """Validate reusable connector artifacts against source, release, ABI and archive checksums.""" + directory = root / 'jellyfin-connector' + descriptor = json.loads((directory / 'bundle.json').read_text(encoding='utf-8')) + release = os.environ.get('THEMERR_VERSION') or runpy.run_path(str(root / VERSION_FILE))['VERSION'] + if (descriptor.get('protocol') != 1 or descriptor.get('build') != build_identity(root, release) or + descriptor.get('themerrVersion') != release or set(descriptor['artifacts']) != set(PROFILES)): + raise ValueError('Prebuilt Jellyfin connector does not match this Themerr source and release.') + for profile, (abi, _) in PROFILES.items(): + artifact = descriptor['artifacts'][profile] + archive = directory / f'connector-{profile}.zip' + if (artifact['targetAbi'] != abi or + artifact['checksum'] != hashlib.md5(archive.read_bytes(), usedforsecurity=False).hexdigest()): + raise ValueError('Prebuilt Jellyfin connector checksum or ABI does not match.') + with zipfile.ZipFile(archive) as content: + if content.namelist() != ['Themerr.Connector.dll']: + raise ValueError('Prebuilt Jellyfin connector contains unexpected files.') + return descriptor + + if __name__ == '__main__': parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--dotnet', default='dotnet') - build(parser.parse_args().dotnet) + parser.add_argument('--check', action='store_true', help='Validate existing artifacts without using .NET.') + arguments = parser.parse_args() + check_bundle() if arguments.check else build(arguments.dotnet) diff --git a/scripts/test_connector.py b/scripts/test_connector.py new file mode 100644 index 00000000..d0746445 --- /dev/null +++ b/scripts/test_connector.py @@ -0,0 +1,36 @@ +"""Run connector unit tests and produce separate coverage and JUnit reports for each Jellyfin ABI.""" + +import argparse +from pathlib import Path +import subprocess + +from build_connector import PROFILES, ROOT + + +def test(dotnet='dotnet', output=None): + """Run the same native test suite against both supported server assemblies.""" + output = Path(output or ROOT / 'coverage').resolve() + failed = False + for profile, (abi, framework) in PROFILES.items(): + directory = output / ('connector-' + profile) + directory.mkdir(parents=True, exist_ok=True) + result = subprocess.run([ + dotnet, 'test', str(ROOT / 'connectors/jellyfin.tests/Connector.Tests.csproj'), + '--configuration', 'Release', f'-p:ConnectorFramework={framework}', f'-p:JellyfinVersion={abi}', + f'-p:RestoreConfigFile={ROOT / "connectors/jellyfin/NuGet.Config"}', + '-p:CollectCoverage=true', f'-p:CoverletOutput={directory.as_posix()}/', + '-p:CoverletOutputFormat=opencover', '-p:Include=[Themerr.Connector]*', + '--logger', f'junit;LogFilePath={directory / "junit.xml"}', + '--logger', 'console;verbosity=normal', '--results-directory', str(directory), + ], check=False) + failed |= result.returncode != 0 + if failed: + raise SystemExit('Jellyfin connector tests failed.') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--dotnet', default='dotnet') + parser.add_argument('--output', help='Directory for coverage and test results.') + arguments = parser.parse_args() + test(arguments.dotnet, arguments.output) diff --git a/src/common/config.py b/src/common/config.py index 61c522a3..5948da61 100644 --- a/src/common/config.py +++ b/src/common/config.py @@ -248,7 +248,46 @@ def on_change_plex_url() -> None: }, 'BOOL_COLLECTION_SUPPORT': { 'type': 'boolean', 'name': _('Collections'), 'default': True, - 'description': _('Add themes to Jellyfin collections.'), + 'description': _('Add themes inside Jellyfin’s collection directories. Media files can remain elsewhere.'), + }, + 'BOOL_OVERWRITE_USER_THEMES': { + 'type': 'boolean', 'name': _('Overwrite user themes'), 'default': False, + 'description': _('Replace existing theme files, including themes created by the older ' + 'Themerr-jellyfin plugin.'), + }, + 'BOOL_BACKUP_USER_THEMES': { + 'type': 'boolean', 'name': _('Back up replaced user themes'), 'default': True, + 'description': _('Keep existing user themes in a backup folder when replacing them. ' + 'Disable to permanently remove replaced themes.'), + }, + 'BOOL_IGNORE_LOCKED_FIELDS': { + 'type': 'boolean', 'name': _('Ignore locked metadata'), 'default': False, + 'description': _('Allow Jellyfin theme updates for items with locked metadata.'), + }, + 'AUTO_UPDATE_CONNECTOR': { + 'type': 'boolean', 'name': _('Update connector automatically'), 'default': True, + 'description': _('Install the matching connector when Themerr starts or detects a version mismatch. ' + 'Set the reachable Themerr address on the Servers page first.'), + }, + 'AUTO_RESTART': { + 'type': 'boolean', 'name': _('Restart Jellyfin automatically'), 'default': True, + 'description': _('Restart Jellyfin after installing or updating the connector. ' + 'A pending restart is shown on the Servers page before it happens.'), + }, + 'WAIT_FOR_IDLE': { + 'type': 'boolean', 'name': _('Wait for playback to finish'), 'default': True, + 'description': _('Defer connector restarts until there are no playing or paused streams.'), + }, + 'REMOVE_LEGACY_PLUGIN': { + 'type': 'boolean', 'name': _('Remove the older Themerr-jellyfin plugin'), 'default': False, + 'description': _('After installing the connector, uninstall the older Themerr plugin and remove only ' + 'its dedicated repository. Existing theme files are kept.'), + }, + 'REPOSITORY_HTTP_PORT': { + 'type': 'integer', 'name': _('Connector HTTP port'), 'default': 9495, 'min': 0, 'max': 65535, + 'description': _('Serve only connector downloads over HTTP when Themerr uses a self-signed certificate. ' + 'The admin UI stays on HTTPS. Use 0 to disable. ' + 'Restart Themerr after changing this port.'), }, }, 'Plex': { @@ -416,6 +455,41 @@ def on_change_plex_url() -> None: }, } +# Keep existing config keys and values while presenting server-specific settings separately. +PLEX_SETTING_KEYS = frozenset({ + 'BOOL_PLEX_COLLECTION_SUPPORT', 'BOOL_PLEX_MOVIE_SUPPORT', 'BOOL_PLEX_SERIES_SUPPORT', + 'BOOL_OVERWRITE_PLEX_PROVIDED_THEMES', 'BOOL_REMOVE_UNUSED_THEMES', 'BOOL_REMOVE_UNUSED_ART', + 'BOOL_REMOVE_UNUSED_POSTERS', 'BOOL_UPDATE_COLLECTION_METADATA', 'BOOL_IGNORE_LOCKED_FIELDS', + 'INT_PLEXAPI_PLEXAPI_TIMEOUT', 'INT_PLEXAPI_UPLOAD_RETRIES_MAX', +}) + + +def settings_groups(): + """Group existing configuration fields by their server integration for display. + + Retain the stored section and key names while separating the settings page into integrations. + + Returns + ------- + list of dict + Display sections with their original configuration source and field keys. + + Examples + -------- + >>> settings_groups()[0]['section'] + 'Themerr' + """ + groups = [] + for section in ('Themerr', 'Plex', 'Jellyfin', 'General', 'Notifications', 'Logging', 'Network'): + source = 'Themerr' if section == 'Plex' else section + keys = [key for key, value in _CONFIG_SPEC_DICT[source].items() if isinstance(value, dict)] + if section == 'Themerr': + keys = [key for key in keys if key not in PLEX_SETTING_KEYS] + elif section == 'Plex': + keys = [key for key in keys if key in PLEX_SETTING_KEYS] + groups.append({'section': section, 'source': source, 'keys': keys}) + return groups + def is_masked_field(section: str, key: str) -> bool: """ diff --git a/src/common/credentials.py b/src/common/credentials.py index c0056ae8..c91ba50a 100644 --- a/src/common/credentials.py +++ b/src/common/credentials.py @@ -15,7 +15,8 @@ SERVICE = Names.name -KEY_FILE_ENV = 'THEMERR_PLEX_TOKEN_KEY_FILE' +KEY_FILE_ENV = 'THEMERR_TOKEN_KEY_FILE' +LEGACY_KEY_FILE_ENV = 'THEMERR_PLEX_TOKEN_KEY_FILE' STORE_UNAVAILABLE = 'The OS credential store is unavailable.' @@ -36,14 +37,14 @@ def _cipher() -> Fernet | None: TokenStorageError The key is missing or invalid in a headless installation. """ - key_path = os.environ.get(KEY_FILE_ENV) + key_path = os.environ.get(KEY_FILE_ENV) or os.environ.get(LEGACY_KEY_FILE_ENV) if key_path: try: return Fernet(Path(key_path).read_bytes().strip()) except (OSError, ValueError) as exc: - raise TokenStorageError(f'Unable to read a valid Plex token key from {KEY_FILE_ENV}.') from exc + raise TokenStorageError(f'Unable to read a valid token encryption key from {KEY_FILE_ENV}.') from exc if os.environ.get('THEMERR_DOCKER'): - raise TokenStorageError(f'Set {KEY_FILE_ENV} to a mounted secret before signing in to Plex.') + raise TokenStorageError(f'Set {KEY_FILE_ENV} to a mounted secret before connecting a media server.') return None @@ -68,7 +69,7 @@ def get_token(client_id: str) -> str: try: return cipher.decrypt(encrypted.encode('ascii')).decode('utf-8') except (InvalidToken, UnicodeError) as exc: - raise TokenStorageError('The Plex token key does not match the stored token.') from exc + raise TokenStorageError('The encryption key does not match the stored token.') from exc try: return keyring.get_password(SERVICE, client_id) or '' # Native backends can raise errors outside keyring's exception hierarchy. @@ -84,7 +85,7 @@ def save_token(client_id: str, token: str) -> None: client_id : str Installation-scoped credential identifier. token : str - Plex account token. + Media-server account token or API key. """ cipher = _cipher() if cipher is not None: @@ -105,7 +106,7 @@ def delete_token(client_id: str) -> None: Installation-scoped credential identifier. """ storage.save_encrypted_token(None, _namespace(client_id)) - if os.environ.get(KEY_FILE_ENV) or os.environ.get('THEMERR_DOCKER'): + if os.environ.get(KEY_FILE_ENV) or os.environ.get(LEGACY_KEY_FILE_ENV) or os.environ.get('THEMERR_DOCKER'): return try: keyring.delete_password(SERVICE, client_id) diff --git a/src/common/server_ui.py b/src/common/server_ui.py index 145c4ffd..c3681418 100644 --- a/src/common/server_ui.py +++ b/src/common/server_ui.py @@ -12,7 +12,7 @@ # local imports from common.http import read_json, render_template -from common import logger +from common import config, logger from common.validation import ValidationError from media_servers import get_backend, processing from media_servers.base import MediaServerError @@ -140,6 +140,7 @@ def server_page(request: Request): title='Servers', servers=get_backend().list_servers(), plex_connected=get_backend().account_connected(), + jellyfin_settings=config.CONFIG['Jellyfin'], ) diff --git a/src/common/webapp.py b/src/common/webapp.py index e19f44b4..2bc84cd6 100644 --- a/src/common/webapp.py +++ b/src/common/webapp.py @@ -478,6 +478,7 @@ def settings(request: Request) -> Response: title=_('Settings'), config_settings=config_settings, config_spec=config._CONFIG_SPEC_DICT, + settings_groups=config.settings_groups(), ) @@ -997,8 +998,11 @@ def start_webapp() -> None: _server = uvicorn.Server(server_config) _server_stopped.clear() try: + from jellyfin import repository + repository.start(cert_file) _server.run() finally: + repository.stop() _server = None _server_stopped.set() diff --git a/src/jellyfin/backend.py b/src/jellyfin/backend.py index 67e1b05b..8c933b28 100644 --- a/src/jellyfin/backend.py +++ b/src/jellyfin/backend.py @@ -33,7 +33,7 @@ def _eligible(item): """Honor Jellyfin category settings and the shared locked-metadata preference.""" setting = _SETTINGS.get(item.get('Type')) return bool(setting and config.CONFIG['Jellyfin'][setting] and - (not item.get('IsLocked') or config.CONFIG['Themerr']['BOOL_IGNORE_LOCKED_FIELDS'])) + (not item.get('IsLocked') or config.CONFIG['Jellyfin']['BOOL_IGNORE_LOCKED_FIELDS'])) class JellyfinMediaServer(MediaServer): @@ -143,7 +143,7 @@ def _update(self, item_id): if ignored.intersection(identifier(row['Id']) for row in ancestors): return False state = connection.json('GET', '/Themerr/Items/' + item_id + '/Theme') - if state['present'] and not state['owned']: + if state['present'] and not state['owned'] and not config.CONFIG['Jellyfin']['BOOL_OVERWRITE_USER_THEMES']: theme_errors.set_error(item_id, None) return True details = metadata.resolve(item) @@ -183,7 +183,9 @@ def _send_audio(connection, item_id, item, source, descriptor, audio): """Verify acknowledged bytes before recording a successful upload.""" headers = {'Content-Type': 'audio/mp4' if audio.codec == 'mp4a' else 'audio/ogg', 'X-Themerr-Connector': descriptor['build'], 'X-Themerr-SHA256': audio.sha256, - 'X-Themerr-Ignore-Locked': str(config.CONFIG['Themerr']['BOOL_IGNORE_LOCKED_FIELDS']).lower()} + 'X-Themerr-Ignore-Locked': str(config.CONFIG['Jellyfin']['BOOL_IGNORE_LOCKED_FIELDS']).lower(), + 'X-Themerr-Overwrite-User': str(config.CONFIG['Jellyfin']['BOOL_OVERWRITE_USER_THEMES']).lower(), + 'X-Themerr-Backup-User': str(config.CONFIG['Jellyfin']['BOOL_BACKUP_USER_THEMES']).lower()} with open(audio.path, 'rb') as stream: result = connection.json('POST', '/Themerr/Items/' + item_id + '/Theme', data=stream, headers=headers, timeout=120) @@ -237,10 +239,14 @@ def account_connected(self): return False def start_listeners(self): - """Jellyfin uses shared scheduled scans; no separate listener is required.""" + """Start connector maintenance alongside shared scheduled theme scans.""" + from jellyfin import maintenance + maintenance.start() def stop_listeners(self): - """Shared workers own the Jellyfin scan lifecycle.""" + """Stop connector maintenance when Themerr shuts down.""" + from jellyfin import maintenance + maintenance.stop() def web_router(self): from jellyfin.web import router diff --git a/src/jellyfin/connector.py b/src/jellyfin/connector.py index bd0a6333..8085bf10 100644 --- a/src/jellyfin/connector.py +++ b/src/jellyfin/connector.py @@ -3,6 +3,9 @@ import json from pathlib import Path from threading import RLock +from urllib.parse import quote + +import requests from sqlalchemy.orm import Session @@ -18,6 +21,8 @@ MANIFEST_PATH = '/jellyfin/connector/manifest.json' PUBLIC_PATHS = frozenset([MANIFEST_PATH, *('/jellyfin/connector/' + name for name in ARCHIVES.values())]) _install_lock = RLock() +LEGACY_PLUGIN_ID = 'e41ef0c4-c413-41ba-b4fa-8c565dc3c969' +LEGACY_REPOSITORY = 'https://app.lizardbyte.dev/jellyfin-plugin-repo/manifest.json' def directory(): @@ -48,7 +53,7 @@ def profile(server_version): return key -def repository_url(value=None): +def repository_url(value=None, required=True): """Save or read the administrator-selected Themerr URL reachable from Jellyfin.""" with Session(storage.engine()) as session: row = session.get(storage.AppSetting, 'jellyfin_repository_base_url') @@ -62,6 +67,8 @@ def repository_url(value=None): session.commit() return value if row is None: + if not required: + return None raise MediaServerError('Set the Themerr address reachable from Jellyfin ' 'before installing the connector.', 409) return row.value @@ -112,14 +119,79 @@ def install(connection, themerr_url): if not isinstance(repositories, list) or any(not isinstance(entry, dict) for entry in repositories): raise MediaServerError('Jellyfin returned an invalid repository list.', 502) repositories = [entry for entry in repositories if entry.get('Url') != manifest_url] - repositories.append({'Name': PLUGIN_NAME + ' (Themerr)', 'Url': manifest_url, 'Enabled': True}) + repositories.append({'Name': PLUGIN_NAME, 'Url': manifest_url, 'Enabled': True}) + _check_certificate(url) repository_url(url) connection.request('POST', '/Repositories', json=repositories).close() if active: return {'message': 'Matching connector is already active.', 'version': artifact['version'], 'restart_required': False} - connection.request('POST', '/Packages/Installed/' + PLUGIN_NAME, - params={'assemblyGuid': PLUGIN_ID, 'version': artifact['version'], - 'repositoryUrl': manifest_url}, timeout=120).close() - return {'message': 'Connector installed. Restart Jellyfin, then refresh its libraries in Themerr.', + _check_repository(connection, url, artifact) + plugins = connection.json('GET', '/Plugins') + if not isinstance(plugins, list) or any(not isinstance(plugin, dict) for plugin in plugins): + raise MediaServerError('Jellyfin returned an invalid plugin list.', 502) + pending = any(str(plugin.get('Id', '')).lower() == PLUGIN_ID and + plugin.get('Version') == artifact['version'] and + plugin.get('Status') != 'Deleted' for plugin in plugins) + if not pending: + connection.request('POST', '/Packages/Installed/' + PLUGIN_NAME, + params={'assemblyGuid': PLUGIN_ID, 'version': artifact['version'], + 'repositoryUrl': manifest_url}, timeout=120).close() + return {'message': 'Connector installed. Jellyfin must restart to load it.', 'version': artifact['version'], 'restart_required': True} + + +def _check_certificate(url): + """Reject untrusted HTTPS before storing or registering a repository address.""" + if url.startswith('https:'): + try: + # No credentials are sent to this administrator-selected repository URL. + response = requests.get(url + MANIFEST_PATH, timeout=10, allow_redirects=False, stream=True) + response.close() + except requests.exceptions.SSLError as exc: + raise MediaServerError('Themerr’s HTTPS certificate is not trusted or does not match its address. ' + 'Use the connector-only HTTP port or a trusted HTTPS address.', 400) from exc + except requests.RequestException as exc: + raise MediaServerError('Could not reach the Themerr repository address. Check its host and port.', + 502) from exc + + +def _check_repository(connection, url, artifact): + """Prove that Jellyfin can fetch the matching package before requesting installation.""" + try: + package = connection.json('GET', '/Packages/' + PLUGIN_NAME, params={'assemblyGuid': PLUGIN_ID}, timeout=120) + except MediaServerError as exc: + if exc.status_code != 404: + raise + raise MediaServerError('Jellyfin could not read the Themerr connector repository. Check the reachable ' + 'address, port and firewall. HTTPS requires a certificate Jellyfin trusts; ' + 'use the connector-only HTTP port for self-signed certificates.', 502) from exc + versions = package.get('versions', package.get('Versions', [])) if isinstance(package, dict) else [] + if not isinstance(versions, list) or not any( + isinstance(entry, dict) and entry.get('version', entry.get('Version')) == artifact['version'] and + entry.get('repositoryUrl', entry.get('RepositoryUrl')) == url + MANIFEST_PATH for entry in versions): + raise MediaServerError('Jellyfin did not find the matching connector in this repository. ' + 'Check the Themerr address and rebuild or reinstall Themerr.', 502) + + +def remove_legacy(connection): + """Uninstall only the known legacy plugin and its exact dedicated repository.""" + changed = False + plugins = connection.json('GET', '/Plugins') + if not isinstance(plugins, list) or any(not isinstance(plugin, dict) for plugin in plugins): + raise MediaServerError('Jellyfin returned an invalid plugin list.', 502) + for plugin in plugins: + if str(plugin.get('Id', '')).lower() == LEGACY_PLUGIN_ID and plugin.get('Status') != 'Deleted': + version = str(plugin.get('Version', '')) + parts = version.split('.') + if len(parts) != 4 or not all(part.isascii() and part.isdigit() for part in parts): + raise MediaServerError('Jellyfin returned an invalid legacy plugin version.', 502) + connection.request('DELETE', '/Plugins/' + LEGACY_PLUGIN_ID + '/' + quote(version, safe='')).close() + changed = True + repositories = connection.json('GET', '/Repositories') + if not isinstance(repositories, list) or any(not isinstance(entry, dict) for entry in repositories): + raise MediaServerError('Jellyfin returned an invalid repository list.', 502) + remaining = [entry for entry in repositories if entry.get('Url', '').rstrip('/') != LEGACY_REPOSITORY] + if len(remaining) != len(repositories): + connection.request('POST', '/Repositories', json=remaining).close() + return changed diff --git a/src/jellyfin/discovery.py b/src/jellyfin/discovery.py new file mode 100644 index 00000000..09723969 --- /dev/null +++ b/src/jellyfin/discovery.py @@ -0,0 +1,38 @@ +"""Discover nearby Jellyfin addresses without transmitting credentials.""" + +import json +import socket +import time + +from jellyfin.client import base_url, identifier +from media_servers.base import MediaServerError + + +def discover(): + """Collect bounded Jellyfin UDP discovery replies from the application's network.""" + results = {} + deadline = time.monotonic() + 2 + try: + with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as transport: + transport.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + transport.bind(('', 0)) + for address in ('255.255.255.255', '127.0.0.1'): + transport.sendto(b'Who is JellyfinServer?', (address, 7359)) + while len(results) < 32 and time.monotonic() < deadline: + transport.settimeout(max(0.001, deadline - time.monotonic())) + try: + payload, _ = transport.recvfrom(4096) + except socket.timeout: + break + try: + data = json.loads(payload) + server_id = identifier(data['Id']) + url = base_url(data['Address']) + name = str(data['Name'])[:128] + except (ValueError, TypeError, KeyError, MediaServerError): + continue + results[(server_id, url)] = {'id': server_id, 'name': name, 'url': url} + except OSError as exc: + raise MediaServerError('Jellyfin discovery is unavailable. Check local network access or enter an address.', + 503) from exc + return sorted(results.values(), key=lambda row: (row['name'], row['url'])) diff --git a/src/jellyfin/maintenance.py b/src/jellyfin/maintenance.py new file mode 100644 index 00000000..d22f463d --- /dev/null +++ b/src/jellyfin/maintenance.py @@ -0,0 +1,200 @@ +"""Keep bundled connectors current and defer their restarts until playback finishes.""" + +import json +from threading import Event, RLock, Thread +import time + +from sqlalchemy.orm import Session + +from common import config, logger +from jellyfin import connector, servers +from media_servers.base import MediaServerError +from themerr import storage + +log = logger.get_logger(__name__) +_lock = RLock() +_stop = Event() +_thread = None +_STATE_PREFIX = 'jellyfin_connector_state:' + + +def state(server_id): + """Read restart progress without exposing credentials.""" + with Session(storage.engine()) as session: + row = session.get(storage.AppSetting, _STATE_PREFIX + server_id) + return json.loads(row.value) if row else {} + + +def _save(server_id, **values): + """Persist pending restarts so a Themerr restart does not lose their progress.""" + with Session(storage.engine()) as session: + key = _STATE_PREFIX + server_id + row = session.get(storage.AppSetting, key) + current = json.loads(row.value) if row else {} + current.update(values) + if row is None: + row = storage.AppSetting(key=key, value='') + session.add(row) + row.value = json.dumps(current) + session.commit() + return current + + +def install(server_id, url): + """Install the exact connector and announce the pending restart before it happens.""" + with _lock: + connection = servers.client(server_id) + result = connector.install(connection, url) + restart = result['restart_required'] + legacy_removed = False + if config.CONFIG['Jellyfin']['REMOVE_LEGACY_PLUGIN']: + legacy_removed = connector.remove_legacy(connection) + restart = legacy_removed or restart + legacy_checked = connector.bundle()['build'] if config.CONFIG['Jellyfin']['REMOVE_LEGACY_PLUGIN'] else None + if restart: + message = 'Connector installed. Jellyfin will restart when playback finishes.' + if not config.CONFIG['Jellyfin']['WAIT_FOR_IDLE']: + message = 'Connector installed. Jellyfin will restart shortly to load it.' + if not config.CONFIG['Jellyfin']['AUTO_RESTART']: + message = 'Connector installed. Automatic restarts are disabled; restart Jellyfin to load it.' + _save(server_id, phase='pending', message=message, restart_required=True, + restart_after=time.time() + 30, build=connector.bundle()['build'], force_restart=legacy_removed, + legacy_checked=legacy_checked) + log.info('%s (%s)', message, server_id) + result['message'] = message + else: + _save(server_id, phase='active', message=result['message'], restart_required=False, force_restart=False, + legacy_checked=legacy_checked) + return {**result, 'restart_required': restart} + + +def _restart(server_id, connection, current): + """Restart only a known pending connector after the notice and idle checks.""" + if (_stop.is_set() or not config.CONFIG['Jellyfin']['AUTO_RESTART'] or + time.time() < current.get('restart_after', 0)): + return + if config.CONFIG['Jellyfin']['WAIT_FOR_IDLE']: + sessions = connection.json('GET', '/Sessions') + if not isinstance(sessions, list): + raise MediaServerError('Jellyfin returned an invalid session list; restart deferred.', 502) + if any(not isinstance(session, dict) for session in sessions): + raise MediaServerError('Jellyfin returned an invalid session list; restart deferred.', 502) + if any(session.get('NowPlayingItem') for session in sessions): + _save(server_id, message='Waiting for Jellyfin playback to finish before restarting.') + return + info = connection.json('GET', '/System/Info') + if info.get('CanSelfRestart') is False: + _save(server_id, phase='manual', message='This Jellyfin installation cannot restart itself. ' + 'Restart its service or container to load the connector.') + return + if _stop.is_set(): + return + _save(server_id, phase='restarting', message='Restarting Jellyfin to load the connector.', + restart_started=time.time()) + log.info('Restarting Jellyfin for the connector (%s)', server_id) + # A closed connection can mean that Jellyfin already accepted the restart. + try: + connection.request('POST', '/System/Restart').close() + except MediaServerError as exc: + if exc.status_code != 502: + raise + + +def maintain(server_id): + """Advance one connection's installation or restart without blocking other work.""" + with _lock: + current = state(server_id) + connection = servers.client(server_id) + if current.get('phase') == 'restarting': + try: + _verify_loaded(connection, current) + except MediaServerError: + if time.time() - current['restart_started'] > 180: + _save(server_id, phase='manual', message='Jellyfin has not loaded the connector after restarting. ' + 'Check its service or container and restart it manually.') + return + from jellyfin.backend import JellyfinMediaServer + JellyfinMediaServer(server_id).cache_dashboard() + servers.record_refresh(server_id) + _save(server_id, phase='active', message='Matching connector is active. Libraries refreshed.', + restart_required=False, force_restart=False) + return + if current.get('restart_required') and current.get('build') == connector.bundle()['build']: + try: + _verify_loaded(connection, current) + except MediaServerError: + if current.get('phase') != 'manual': + _restart(server_id, connection, current) + return + from jellyfin.backend import JellyfinMediaServer + JellyfinMediaServer(server_id).cache_dashboard() + servers.record_refresh(server_id) + _save(server_id, phase='active', message='Matching connector is active. Libraries refreshed.', + restart_required=False, force_restart=False) + return + if not config.CONFIG['Jellyfin']['AUTO_UPDATE_CONNECTOR']: + return + url = connector.repository_url(required=False) + if not url: + return + try: + connector.verify(connection) + except MediaServerError: + if not _stop.is_set(): + install(server_id, url) + else: + if (config.CONFIG['Jellyfin']['REMOVE_LEGACY_PLUGIN'] and + current.get('legacy_checked') != connector.bundle()['build'] and not _stop.is_set()): + install(server_id, url) + + +def _verify_loaded(connection, current): + """Require legacy cleanup's restart even when the matching connector was already active.""" + connector.verify(connection) + if current.get('force_restart'): + info = connection.json('GET', '/System/Info') + if not isinstance(info, dict) or info.get('HasPendingRestart') is not False: + raise MediaServerError('Restart Jellyfin to complete legacy plugin removal.', 409) + + +def reconcile(): + """Check enabled servers independently and retain fixed, actionable failure messages.""" + for server in servers.list_servers(enabled_only=True): + if _stop.is_set(): + return + try: + maintain(server['id']) + except MediaServerError as exc: + current = state(server['id']) + if current.get('phase') == 'restarting': + if time.time() - current['restart_started'] <= 180: + continue + _save(server['id'], phase='manual', message='Jellyfin did not become available after restarting. ' + 'Check its service or container.') + continue + _save(server['id'], message=str(exc)) + log.warning('Jellyfin connector maintenance: %s (%s)', str(exc), server['id']) + except Exception as exc: + log.warning('Jellyfin connector maintenance failed (%s)', type(exc).__name__) + + +def start(): + """Start one maintenance worker; repeated settings saves reuse it.""" + global _thread + with _lock: + if _thread is not None and _thread.is_alive(): + return + _stop.clear() + + def loop(): + while not _stop.is_set(): + reconcile() + _stop.wait(30) + + _thread = Thread(target=loop, name='Jellyfin connector maintenance', daemon=True) + _thread.start() + + +def stop(): + """Signal the worker to stop before application shutdown.""" + _stop.set() diff --git a/src/jellyfin/repository.py b/src/jellyfin/repository.py new file mode 100644 index 00000000..cffcfcff --- /dev/null +++ b/src/jellyfin/repository.py @@ -0,0 +1,72 @@ +"""Serve fixed connector artifacts over HTTP without exposing the administrator UI.""" + +from threading import Thread + +from cryptography import x509 +from fastapi import FastAPI +import uvicorn + +from common import config, logger +from jellyfin import connector + +log = logger.get_logger(__name__) +_server = None +_thread = None +_port = None + + +def create_app(): + """Expose only the three code-owned public connector routes.""" + from jellyfin.web import router + app = FastAPI(docs_url=None, redoc_url=None, openapi_url=None) + app.router.routes.extend(route for route in router.routes if route.path in connector.PUBLIC_PATHS) + return app + + +def http_port(): + """Return the active repository port, excluding failed or disabled listeners.""" + return _port if _server is not None and _server.started else None + + +def start(cert_file): + """Start a repository-only listener for a self-signed HTTPS installation.""" + global _server, _thread, _port + port = config.CONFIG['Jellyfin']['REPOSITORY_HTTP_PORT'] + if not cert_file or not port or _server is not None: + return + with open(cert_file, 'rb') as stream: + cert = x509.load_pem_x509_certificate(stream.read()) + try: + cert.verify_directly_issued_by(cert) + except (ValueError, TypeError): + return + if port == config.CONFIG['Network']['HTTP_PORT']: + log.error('Connector HTTP port must differ from the administrator UI port.') + return + _port = port + _server = uvicorn.Server(uvicorn.Config( + create_app(), host=config.CONFIG['Network']['HTTP_HOST'], port=port, + loop='asyncio', http='h11', ws='none', lifespan='off', proxy_headers=False, + log_config=None, timeout_graceful_shutdown=5, + )) + server = _server + + def serve(): + try: + server.run() + except SystemExit: + log.error('Could not bind the connector HTTP listener. Check its configured port.') + + _thread = Thread(target=serve, name='Jellyfin connector repository', daemon=True) + _thread.start() + log.info('Connector downloads use HTTP port %s; the administrator UI stays on HTTPS.', port) + + +def stop(): + """Release the repository socket before a Themerr restart.""" + global _server, _thread, _port + if _server is not None: + _server.should_exit = True + if _thread is not None: + _thread.join(timeout=6) + _server, _thread, _port = None, None, None diff --git a/src/jellyfin/web.py b/src/jellyfin/web.py index 5b96c125..bc7d13e5 100644 --- a/src/jellyfin/web.py +++ b/src/jellyfin/web.py @@ -1,11 +1,12 @@ """Authenticated Jellyfin setup and exact public routes for bundled plugin artifacts.""" from fastapi import APIRouter, Depends, Request +from typing import Annotated from starlette.responses import JSONResponse from common import credentials, logger from common.http import file_response, read_json -from jellyfin import connector, servers +from jellyfin import connector, discovery, maintenance, repository, servers from media_servers.base import MediaServerError router = APIRouter() @@ -31,7 +32,7 @@ def _payload(value): @router.post('/api/jellyfin/servers', name='jellyfin.add_server', response_model=None) -def add_server(payload=Depends(read_json)): +def add_server(payload: Annotated[object, Depends(read_json)]): """Verify and securely save a Jellyfin server using an administrator API key.""" try: payload = _payload(payload) @@ -45,24 +46,49 @@ def add_server(payload=Depends(read_json)): def connector_status(server_id: str): """Check the installed connector without changing Jellyfin or its repositories.""" try: - connection = servers.client(server_id) - connector.verify(connection) - return JSONResponse({'installed': True, 'message': 'Matching connector is active.'}) + if not servers.get_server(server_id): + raise MediaServerError('Jellyfin server not found.', 404) + result = maintenance.state(server_id) + try: + connection = servers.client(server_id) + except MediaServerError: + if result.get('phase') != 'restarting': + raise + return JSONResponse({**result, 'installed': False, + 'repository_url': connector.repository_url(required=False), + 'http_port': repository.http_port()}) + try: + connector.verify(connection) + message = result['message'] if result.get('restart_required') else 'Matching connector is active.' + result = {**result, 'installed': True, 'message': message} + except MediaServerError: + result = {'installed': False, 'message': 'Install the matching Themerr connector.', **result} + return JSONResponse({**result, 'repository_url': connector.repository_url(required=False), + 'http_port': repository.http_port()}) except Exception as exc: return _failure(exc) @router.post('/api/jellyfin/servers/{server_id}/connector', name='jellyfin.install_connector', response_model=None) -def install_connector(server_id: str, payload=Depends(read_json)): - """Register the local repository and install the exact matching connector; restart is manual.""" +def install_connector(server_id: str, payload: Annotated[object, Depends(read_json)]): + """Install the exact connector and schedule its restart according to Jellyfin settings.""" try: payload = _payload(payload) - result = connector.install(servers.client(server_id), payload.get('themerr_url')) + result = maintenance.install(server_id, payload.get('themerr_url')) return JSONResponse(result, status_code=202) except Exception as exc: return _failure(exc) +@router.post('/api/jellyfin/discover', name='jellyfin.discover', response_model=None) +def discover(): + """Discover nearby addresses; an administrator must still provide each server's API key.""" + try: + return JSONResponse({'servers': discovery.discover()}) + except Exception as exc: + return _failure(exc) + + @router.api_route(connector.MANIFEST_PATH, methods=['GET', 'HEAD'], name='jellyfin.manifest', response_model=None) def manifest(): """Serve only bundled connector metadata so Jellyfin can install from this Themerr instance.""" diff --git a/tests/conftest.py b/tests/conftest.py index 11dc7af8..556457b4 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -32,12 +32,15 @@ def connector_bundle(tmp_path, monkeypatch): def configured(tmp_path, monkeypatch): import common from common import config, definitions, notifications + from jellyfin import maintenance from plex import token_store from themerr import storage old_config, old_common = config.CONFIG, common.CONFIG passwords = {} monkeypatch.delenv(token_store.KEY_FILE_ENV, raising=False) + monkeypatch.delenv('THEMERR_PLEX_TOKEN_KEY_FILE', raising=False) + monkeypatch.setattr(maintenance, 'start', lambda: None) monkeypatch.delenv('THEMERR_DOCKER', raising=False) monkeypatch.setattr(token_store.keyring, 'get_password', lambda service, client: passwords.get((service, client))) monkeypatch.setattr(token_store.keyring, 'set_password', diff --git a/tests/functional/test_jellyfin.py b/tests/functional/test_jellyfin.py index 2ee42114..4d5455f1 100644 --- a/tests/functional/test_jellyfin.py +++ b/tests/functional/test_jellyfin.py @@ -7,7 +7,8 @@ import pytest from common import admin, webapp -from jellyfin import connector, servers +from jellyfin import connector, maintenance, servers +from media_servers.base import MediaServerError from tests.http_helpers import set_session @@ -75,3 +76,22 @@ def test_api_key_is_not_echoed_in_setup_response(browser, monkeypatch): assert result.status_code == 201 assert b'secret' not in result.content add.assert_called_once_with('http://jellyfin.example', 'secret') + + +@pytest.mark.parametrize('online', [True, False]) +def test_connector_status_retains_restart_notices_while_offline_or_already_matching( + browser, connector_bundle, monkeypatch, online): + sign_in(browser) + server_id = 'jellyfin:' + '3' * 32 + monkeypatch.setattr(servers, 'get_server', lambda _: {'id': server_id}) + client = Mock() + if not online: + client.side_effect = MediaServerError('Unavailable', 502) + monkeypatch.setattr(servers, 'client', client) + monkeypatch.setattr(connector, 'verify', Mock(return_value=connector_bundle)) + maintenance._save(server_id, phase='restarting', message='Restarting Jellyfin.', restart_required=True) + result = browser.get('/api/jellyfin/servers/' + server_id + '/connector') + assert result.status_code == 200 + assert result.json()['message'] == 'Restarting Jellyfin.' + assert result.json()['restart_required'] is True + assert result.json()['installed'] is online diff --git a/tests/functional/test_webapp.py b/tests/functional/test_webapp.py index 4aec3973..5f3147d4 100644 --- a/tests/functional/test_webapp.py +++ b/tests/functional/test_webapp.py @@ -425,18 +425,41 @@ def test_settings(client): assert 'PLEX_TOKEN' not in response.json()['Plex'] assert b'Your Plex account' in client.get('/servers').content assert b'id="plex-auth-status"' in client.get('/servers').content - assert b'data-directory-target="LOG_DIR"' in client.get('/settings/').content + assert b'data-directory-target="Logging-LOG_DIR"' in client.get('/settings/').content assert b'Plex data directory' in client.get('/servers').content assert b'PLEX_TOKEN' not in client.get('/settings/').content +def test_settings_separate_integrations_and_use_unique_field_identifiers(client, configured): + page = client.get('/settings/').text + ids = re.findall(r'\bid="([^"]+)"', page) + assert len(ids) == len(set(ids)) + assert 'id="plex"' in page and 'href="#plex"' in page + assert 'id="jellyfin"' in page and 'href="#jellyfin"' in page + assert 'id="Themerr-BOOL_IGNORE_LOCKED_FIELDS"' in page + assert 'id="Jellyfin-BOOL_IGNORE_LOCKED_FIELDS"' in page + assert 'id="Jellyfin-BOOL_BACKUP_USER_THEMES"' in page + assert 'data-lucide="bell"' in page + assert configured['Jellyfin']['BOOL_OVERWRITE_USER_THEMES'] is False + assert configured['Jellyfin']['BOOL_BACKUP_USER_THEMES'] is True + response = client.post('/api/settings', data={ + 'Jellyfin|BOOL_OVERWRITE_USER_THEMES': 'true', 'Jellyfin|BOOL_BACKUP_USER_THEMES': 'false', + 'Jellyfin|BOOL_IGNORE_LOCKED_FIELDS': 'true', 'Themerr|BOOL_IGNORE_LOCKED_FIELDS': 'false', + }) + assert response.status_code == 200 + assert configured['Jellyfin']['BOOL_OVERWRITE_USER_THEMES'] is True + assert configured['Jellyfin']['BOOL_BACKUP_USER_THEMES'] is False + assert configured['Jellyfin']['BOOL_IGNORE_LOCKED_FIELDS'] is True + assert configured['Themerr']['BOOL_IGNORE_LOCKED_FIELDS'] is False + + def test_notification_preferences_render_validate_and_persist(client, configured): from configobj import ConfigObj page = client.get('/settings/').text assert 'id="notifications"' in page assert 'href="#notifications"' in page - assert 'id="NEW_RELEASE"' in page - assert 'id="COVERAGE_INCREASE"' in page + assert 'id="Notifications-NEW_RELEASE"' in page + assert 'id="Notifications-COVERAGE_INCREASE"' in page assert configured['Notifications']['FOLLOW_PRERELEASES'] is False assert configured['Notifications']['NEW_RELEASE'] is True assert configured['Notifications']['COVERAGE_INCREASE'] is True diff --git a/tests/unit/common/test_credentials.py b/tests/unit/common/test_credentials.py new file mode 100644 index 00000000..6ea3525f --- /dev/null +++ b/tests/unit/common/test_credentials.py @@ -0,0 +1,30 @@ +"""Generic encryption configuration retains old tokens and separate server namespaces.""" + +from cryptography.fernet import Fernet + +from common import credentials + + +def test_legacy_key_alias_can_be_renamed_without_losing_tokens(configured, tmp_path, monkeypatch): + key = tmp_path / 'tokens.key' + key.write_bytes(Fernet.generate_key()) + monkeypatch.setenv(credentials.LEGACY_KEY_FILE_ENV, str(key)) + credentials.save_token('account', 'plex-account') + credentials.save_token('jellyfin:server-one', 'jellyfin-key') + monkeypatch.delenv(credentials.LEGACY_KEY_FILE_ENV) + monkeypatch.setenv(credentials.KEY_FILE_ENV, str(key)) + assert credentials.get_token('account') == 'plex-account' + assert credentials.get_token('jellyfin:server-one') == 'jellyfin-key' + assert credentials.get_token('jellyfin:server-two') == '' + credentials.delete_token('jellyfin:server-one') + assert credentials.get_token('jellyfin:server-one') == '' + assert credentials.get_token('account') == 'plex-account' + + +def test_generic_key_takes_precedence_over_legacy_alias(configured, tmp_path, monkeypatch): + key = tmp_path / 'tokens.key' + key.write_bytes(Fernet.generate_key()) + monkeypatch.setenv(credentials.LEGACY_KEY_FILE_ENV, str(tmp_path / 'missing.key')) + monkeypatch.setenv(credentials.KEY_FILE_ENV, str(key)) + credentials.save_token('jellyfin:server', 'key') + assert credentials.get_token('jellyfin:server') == 'key' diff --git a/tests/unit/jellyfin/test_backend.py b/tests/unit/jellyfin/test_backend.py index 008b9471..01a622df 100644 --- a/tests/unit/jellyfin/test_backend.py +++ b/tests/unit/jellyfin/test_backend.py @@ -57,8 +57,12 @@ def test_scan_honors_paused_categories_ignored_libraries_and_scope(connected, co assert storage.current_server_id() == 'default' -@pytest.mark.parametrize('owned, present', [(False, True), (True, True), (False, False)]) -def test_verified_upload_and_user_theme_protection(connected, configured, monkeypatch, tmp_path, owned, present): +@pytest.mark.parametrize('owned, present, overwrite', [ + (False, True, False), (True, True, False), (False, False, False), (False, True, True)]) +def test_verified_upload_and_user_theme_protection( + connected, configured, monkeypatch, tmp_path, owned, present, overwrite): + configured['Jellyfin']['BOOL_OVERWRITE_USER_THEMES'] = overwrite + configured['Jellyfin']['BOOL_BACKUP_USER_THEMES'] = not overwrite item = {'Id': ITEM, 'Name': 'Example', 'Type': 'Movie'} state = {'present': present, 'owned': owned, 'sha256': 'old'} connected.json.side_effect = [item, state, {'owned': True, 'sha256': 'digest'}] @@ -73,13 +77,15 @@ def test_verified_upload_and_user_theme_protection(connected, configured, monkey assert backend.JellyfinMediaServer(SERVER).update_item(ITEM) is True with storage.server_scope(SERVER): tracked = storage.get_tracking(ITEM) - if present and not owned: + if present and not owned and not overwrite: download.assert_not_called() assert tracked == {} else: assert tracked['audio_sha256'] == 'digest' assert connected.json.call_args.kwargs['headers']['X-Themerr-Connector'] == 'a' * 64 assert connected.json.call_args.kwargs['headers']['Content-Type'] == 'audio/mp4' + assert connected.json.call_args.kwargs['headers']['X-Themerr-Overwrite-User'] == str(overwrite).lower() + assert connected.json.call_args.kwargs['headers']['X-Themerr-Backup-User'] == str(not overwrite).lower() assert storage.current_server_id() == 'default' assert storage.get_tracking(ITEM) == {} diff --git a/tests/unit/jellyfin/test_connector.py b/tests/unit/jellyfin/test_connector.py index a548b4bd..a1b39fc9 100644 --- a/tests/unit/jellyfin/test_connector.py +++ b/tests/unit/jellyfin/test_connector.py @@ -4,6 +4,7 @@ from unittest.mock import Mock import pytest +import requests from common import definitions, version from jellyfin import connector @@ -29,6 +30,8 @@ def test_install_preserves_repositories_and_selects_exact_artifact( repositories = [{'Name': 'Other plugins', 'Url': 'https://other.example/repo.json', 'Enabled': False}] connection.json.side_effect = [ {'Version': server_version}, MediaServerError('Missing connector.', 404), repositories, + {'versions': [{'version': connector_bundle['artifacts'][profile]['version'], + 'repositoryUrl': 'http://themerr.example:9494' + connector.MANIFEST_PATH}]}, [], ] result = connector.install(connection, 'http://themerr.example:9494') assert result['restart_required'] is True @@ -50,7 +53,7 @@ def test_install_does_not_overwrite_an_active_matching_assembly(configured, conn ] assert connector.install(connection, 'http://themerr.example')['restart_required'] is False connection.request.assert_called_once_with('POST', '/Repositories', json=[ - {'Name': connector.PLUGIN_NAME + ' (Themerr)', + {'Name': connector.PLUGIN_NAME, 'Url': 'http://themerr.example' + connector.MANIFEST_PATH, 'Enabled': True}, ]) @@ -87,3 +90,48 @@ def test_bundle_is_loaded_from_frozen_resources(configured, monkeypatch, tmp_pat monkeypatch.setattr(definitions.Paths, 'ROOT_DIR', str(frozen)) assert connector.directory() == directory assert connector.bundle() == descriptor + + +def test_repository_certificate_error_explains_the_http_fallback(configured, connector_bundle, monkeypatch): + get = Mock(side_effect=requests.exceptions.SSLError('private TLS details')) + monkeypatch.setattr(connector.requests, 'get', get) + connection = Mock(server_version='12.1.0') + connection.json.side_effect = [{'Version': '12.1.0'}, MediaServerError('Mismatch', 409), []] + with pytest.raises(MediaServerError, match='connector-only HTTP port') as error: + connector.install(connection, 'https://themerr.example') + assert error.value.status_code == 400 + assert 'private TLS details' not in str(error.value) + assert 'headers' not in get.call_args.kwargs + assert 'verify' not in get.call_args.kwargs + connection.request.assert_not_called() + assert connector.repository_url(required=False) is None + + +def test_repository_failure_reports_jellyfins_download_problem(): + connection = Mock() + connection.json.side_effect = MediaServerError('Unavailable', 404) + with pytest.raises(MediaServerError, match='firewall') as error: + connector._check_repository(connection, 'http://themerr.example', {}) + assert error.value.status_code == 502 + + +def test_legacy_cleanup_preserves_unrelated_plugins_and_repositories(): + connection = Mock() + legacy = {'Id': connector.LEGACY_PLUGIN_ID, 'Version': '2026.1004.1.0', 'Status': 'Active'} + other = {'Id': '1' * 32, 'Version': '1.0.0.0', 'Name': 'Themerr', 'Status': 'Active'} + dedicated = {'Name': 'Themerr', 'Url': connector.LEGACY_REPOSITORY} + unrelated = {'Name': 'Other', 'Url': 'https://other.example/manifest.json', 'Enabled': False} + similar = {'Name': 'Other', 'Url': connector.LEGACY_REPOSITORY + '?other=true'} + connection.json.side_effect = [[legacy, other], [dedicated, unrelated, similar]] + assert connector.remove_legacy(connection) + assert connection.request.call_args_list[0].args == ( + 'DELETE', '/Plugins/' + connector.LEGACY_PLUGIN_ID + '/2026.1004.1.0') + assert connection.request.call_args_list[1].kwargs['json'] == [unrelated, similar] + + +def test_legacy_cleanup_rejects_invalid_versions_without_deleting_anything(): + connection = Mock() + connection.json.return_value = [{'Id': connector.LEGACY_PLUGIN_ID, 'Version': '../outside'}] + with pytest.raises(MediaServerError, match='invalid legacy plugin version'): + connector.remove_legacy(connection) + connection.request.assert_not_called() diff --git a/tests/unit/jellyfin/test_discovery.py b/tests/unit/jellyfin/test_discovery.py new file mode 100644 index 00000000..40dfbfc3 --- /dev/null +++ b/tests/unit/jellyfin/test_discovery.py @@ -0,0 +1,36 @@ +"""Bounded LAN discovery parses untrusted UDP replies without transmitting API keys.""" + +import json +import socket +from unittest.mock import Mock + +import pytest + +from jellyfin import discovery +from media_servers.base import MediaServerError + + +def test_discovery_validates_and_deduplicates_replies(monkeypatch): + server = {'Id': '3' * 32, 'Name': '', 'Address': 'http://jellyfin.example:8096/'} + transport = Mock() + transport.__enter__ = Mock(return_value=transport) + transport.__exit__ = Mock(return_value=False) + replies = [b'invalid', b'[]', json.dumps({**server, 'Address': 'file:///private'}).encode(), + json.dumps({**server, 'Id': '../private'}).encode(), json.dumps(server).encode(), + json.dumps(server).encode()] + transport.recvfrom.side_effect = [(reply, ('127.0.0.1', 7359)) for reply in replies] + [socket.timeout()] + monkeypatch.setattr(discovery.socket, 'socket', Mock(return_value=transport)) + monkeypatch.setattr(discovery.time, 'monotonic', lambda: 1) + assert discovery.discover() == [{'id': '3' * 32, 'name': '', 'url': 'http://jellyfin.example:8096'}] + assert [call.args for call in transport.sendto.call_args_list] == [ + (b'Who is JellyfinServer?', ('255.255.255.255', 7359)), + (b'Who is JellyfinServer?', ('127.0.0.1', 7359)), + ] + + +def test_discovery_socket_failure_has_a_fixed_client_message(monkeypatch): + monkeypatch.setattr(discovery.socket, 'socket', Mock(side_effect=OSError('private details'))) + with pytest.raises(MediaServerError, match='local network access') as error: + discovery.discover() + assert error.value.status_code == 503 + assert 'private details' not in str(error.value) diff --git a/tests/unit/jellyfin/test_maintenance.py b/tests/unit/jellyfin/test_maintenance.py new file mode 100644 index 00000000..9f725a02 --- /dev/null +++ b/tests/unit/jellyfin/test_maintenance.py @@ -0,0 +1,164 @@ +"""Connector lifecycle decisions with mocked Jellyfin APIs and a real temporary state store.""" + +from unittest.mock import Mock +from threading import Event + +import pytest + +from jellyfin import connector, maintenance, servers +from media_servers.base import MediaServerError + +SERVER = 'jellyfin:' + '3' * 32 + + +@pytest.fixture +def connection(configured, connector_bundle, monkeypatch): + value = Mock(server_version='12.1.0') + monkeypatch.setattr(servers, 'client', lambda _: value) + monkeypatch.setattr(maintenance.time, 'time', lambda: 1000) + monkeypatch.setattr(maintenance, '_stop', Event()) + monkeypatch.setattr(connector, 'install', Mock(return_value={ + 'restart_required': True, 'message': 'Installed', 'version': '2026.1004.1.0'})) + return value + + +def test_install_notifies_before_restart_and_persists_progress(connection, monkeypatch): + result = maintenance.install(SERVER, 'http://themerr.example') + assert 'playback finishes' in result['message'] + assert maintenance.state(SERVER)['restart_after'] == 1030 + maintenance._restart(SERVER, connection, maintenance.state(SERVER)) + connection.request.assert_not_called() + monkeypatch.setattr(maintenance.time, 'time', lambda: 1031) + connection.json.side_effect = [[{'NowPlayingItem': {'Id': 'playing'}, 'PlayState': {'IsPaused': True}}]] + maintenance._restart(SERVER, connection, maintenance.state(SERVER)) + assert 'Waiting' in maintenance.state(SERVER)['message'] + connection.request.assert_not_called() + connection.json.side_effect = [[], {'CanSelfRestart': True}] + maintenance._restart(SERVER, connection, maintenance.state(SERVER)) + connection.request.assert_called_once_with('POST', '/System/Restart') + assert maintenance.state(SERVER)['phase'] == 'restarting' + + +@pytest.mark.parametrize('option', ['AUTO_RESTART', 'WAIT_FOR_IDLE']) +def test_restart_preferences_are_independent(connection, configured, monkeypatch, option): + configured['Jellyfin'][option] = False + result = maintenance.install(SERVER, 'http://themerr.example') + monkeypatch.setattr(maintenance.time, 'time', lambda: 1031) + connection.json.return_value = {'CanSelfRestart': True} + maintenance._restart(SERVER, connection, maintenance.state(SERVER)) + if option == 'AUTO_RESTART': + assert 'disabled' in result['message'] + connection.request.assert_not_called() + else: + assert 'shortly' in result['message'] + connection.json.assert_called_once_with('GET', '/System/Info') + connection.request.assert_called_once_with('POST', '/System/Restart') + + +@pytest.mark.parametrize('sessions', [None, ['invalid']]) +def test_invalid_session_data_never_triggers_a_restart(connection, sessions): + connection.json.return_value = sessions + with pytest.raises(MediaServerError, match='restart deferred'): + maintenance._restart(SERVER, connection, {}) + connection.request.assert_not_called() + + +def test_service_or_container_without_self_restart_gets_manual_instructions(connection): + connection.json.side_effect = [[], {'CanSelfRestart': False}] + maintenance._restart(SERVER, connection, {}) + assert maintenance.state(SERVER)['phase'] == 'manual' + connection.request.assert_not_called() + + +def test_startup_installs_a_mismatch_only_when_configured(connection, configured, monkeypatch): + monkeypatch.setattr(connector, 'verify', Mock(side_effect=MediaServerError('Mismatch', 409))) + maintenance.maintain(SERVER) + connector.install.assert_not_called() + connector.repository_url('http://themerr.example') + configured['Jellyfin']['AUTO_UPDATE_CONNECTOR'] = False + maintenance.maintain(SERVER) + connector.install.assert_not_called() + configured['Jellyfin']['AUTO_UPDATE_CONNECTOR'] = True + maintenance.maintain(SERVER) + connector.install.assert_called_once_with(connection, 'http://themerr.example') + + +def test_loaded_connector_refreshes_libraries_after_restart(connection, monkeypatch): + from jellyfin.backend import JellyfinMediaServer + maintenance._save(SERVER, phase='restarting', restart_started=999, restart_required=True) + monkeypatch.setattr(connector, 'verify', Mock(return_value={})) + refresh = Mock(return_value=True) + record = Mock() + monkeypatch.setattr(JellyfinMediaServer, 'cache_dashboard', refresh) + monkeypatch.setattr(servers, 'record_refresh', record) + maintenance.maintain(SERVER) + refresh.assert_called_once_with() + record.assert_called_once_with(SERVER) + assert maintenance.state(SERVER)['restart_required'] is False + + +def test_legacy_cleanup_requires_restart_even_with_matching_connector(connection, configured, monkeypatch): + configured['Jellyfin']['REMOVE_LEGACY_PLUGIN'] = True + connector.install.return_value['restart_required'] = False + monkeypatch.setattr(connector, 'remove_legacy', Mock(return_value=True)) + monkeypatch.setattr(connector, 'verify', Mock(return_value={})) + assert maintenance.install(SERVER, 'http://themerr.example')['restart_required'] + monkeypatch.setattr(maintenance.time, 'time', lambda: 1031) + connection.json.side_effect = [{'HasPendingRestart': True}, [], {'CanSelfRestart': True}] + maintenance.maintain(SERVER) + connection.request.assert_called_once_with('POST', '/System/Restart') + + +@pytest.mark.parametrize('phase', ['pending', 'manual', 'restarting']) +def test_legacy_cleanup_waits_for_and_recognizes_a_manual_restart(connection, monkeypatch, phase): + from jellyfin.backend import JellyfinMediaServer + maintenance._save(SERVER, phase=phase, restart_started=999, restart_after=2000, restart_required=True, + force_restart=True, build=connector.bundle()['build']) + monkeypatch.setattr(connector, 'verify', Mock(return_value={})) + refresh = Mock() + record = Mock() + monkeypatch.setattr(JellyfinMediaServer, 'cache_dashboard', refresh) + monkeypatch.setattr(servers, 'record_refresh', record) + connection.json.return_value = {'HasPendingRestart': True} + maintenance.maintain(SERVER) + refresh.assert_not_called() + assert maintenance.state(SERVER)['restart_required'] + connection.request.assert_not_called() + connection.json.return_value = {'HasPendingRestart': False} + maintenance.maintain(SERVER) + refresh.assert_called_once_with() + record.assert_called_once_with(SERVER) + assert maintenance.state(SERVER)['restart_required'] is False + assert maintenance.state(SERVER)['force_restart'] is False + connection.request.assert_not_called() + + +def test_failed_library_refresh_retains_restart_progress_for_retry(connection, monkeypatch): + from jellyfin.backend import JellyfinMediaServer + maintenance._save(SERVER, phase='restarting', restart_started=999, restart_required=True) + monkeypatch.setattr(connector, 'verify', Mock(return_value={})) + monkeypatch.setattr(JellyfinMediaServer, 'cache_dashboard', Mock(side_effect=MediaServerError('Unavailable', 502))) + with pytest.raises(MediaServerError): + maintenance.maintain(SERVER) + assert maintenance.state(SERVER)['restart_required'] + assert maintenance.state(SERVER)['phase'] == 'restarting' + + +def test_unavailable_server_after_restart_times_out_without_restarting_again(connection, monkeypatch): + maintenance._save(SERVER, phase='restarting', restart_started=1, restart_required=True) + monkeypatch.setattr(servers, 'list_servers', lambda **_: [{'id': SERVER}]) + monkeypatch.setattr(servers, 'client', Mock(side_effect=MediaServerError('Unavailable', 502))) + maintenance.reconcile() + assert maintenance.state(SERVER)['phase'] == 'manual' + connection.request.assert_not_called() + + +def test_shutdown_cancels_a_restart_after_an_in_flight_idle_check(connection): + def check(method, route): + if route == '/System/Info': + maintenance.stop() + return {'CanSelfRestart': True} + return [] + connection.json.side_effect = check + maintenance._restart(SERVER, connection, {}) + connection.request.assert_not_called() diff --git a/tests/unit/jellyfin/test_repository.py b/tests/unit/jellyfin/test_repository.py new file mode 100644 index 00000000..2bb8f64d --- /dev/null +++ b/tests/unit/jellyfin/test_repository.py @@ -0,0 +1,56 @@ +"""The self-signed certificate fallback exposes only fixed connector downloads.""" + +from unittest.mock import Mock + +from fastapi.testclient import TestClient +import pytest + +from jellyfin import connector, repository + + +def test_http_repository_exposes_no_admin_or_setup_routes(configured, connector_bundle): + connector.repository_url('http://themerr.example:9495') + with TestClient(repository.create_app()) as client: + assert client.get(connector.MANIFEST_PATH).status_code == 200 + for filename in connector.ARCHIVES.values(): + assert client.get('/jellyfin/connector/' + filename).status_code == 200 + assert client.head('/jellyfin/connector/' + filename).content == b'' + for path in ('/login', '/setup', '/status', '/api/settings', '/api/jellyfin/servers', + '/openapi.json', '/docs', '/jellyfin/connector/bundle.json', + '/jellyfin/connector/%2e%2e%2fconfig.ini', '/jellyfin/connector/CON', + '/jellyfin/connector/C:%5cconfig.ini', '/jellyfin/connector/connector-12.1.zip:secret'): + assert client.get(path).status_code == 404 + assert client.post(connector.MANIFEST_PATH).status_code == 405 + + +@pytest.mark.parametrize('self_signed, port, expected', [ + (True, 9495, True), (False, 9495, False), (True, 0, False), (True, 9494, False)]) +def test_listener_runs_only_for_self_signed_tls_on_a_separate_enabled_port( + configured, monkeypatch, tmp_path, self_signed, port, expected): + configured['Jellyfin']['REPOSITORY_HTTP_PORT'] = port + certificate = Mock() + if not self_signed: + certificate.verify_directly_issued_by.side_effect = ValueError('Not self-signed') + monkeypatch.setattr(repository.x509, 'load_pem_x509_certificate', Mock(return_value=certificate)) + server = Mock(started=True) + factory = Mock(return_value=server) + monkeypatch.setattr(repository.uvicorn, 'Server', factory) + worker = Mock() + thread = Mock(return_value=worker) + monkeypatch.setattr(repository, 'Thread', thread) + monkeypatch.setattr(repository, '_server', None) + monkeypatch.setattr(repository, '_thread', None) + monkeypatch.setattr(repository, '_port', None) + path = tmp_path / 'certificate.pem' + path.write_bytes(b'certificate') + repository.start(str(path)) + assert factory.called is expected + assert repository.http_port() == (port if expected else None) + if expected: + assert factory.call_args.args[0].ssl_certfile is None + thread.call_args.kwargs['target']() + server.run.assert_called_once_with() + repository.stop() + assert server.should_exit is True + worker.join.assert_called_once_with(timeout=6) + assert repository.http_port() is None diff --git a/tests/unit/test_connector_build.py b/tests/unit/test_connector_build.py new file mode 100644 index 00000000..de68252b --- /dev/null +++ b/tests/unit/test_connector_build.py @@ -0,0 +1,65 @@ +"""Reused connector artifacts must match their source, release, ABI and file contents.""" + +import hashlib +import json +from pathlib import Path +import runpy +import zipfile + +import pytest + + +@pytest.fixture +def builder(tmp_path, monkeypatch): + monkeypatch.delenv('THEMERR_VERSION', raising=False) + script = Path(__file__).resolve().parents[2] / 'scripts/build_connector.py' + module = runpy.run_path(str(script)) + version = tmp_path / module['VERSION_FILE'] + version.parent.mkdir(parents=True) + version.write_text('VERSION = "2026.1004.120000"', encoding='utf-8') + sources = tmp_path / 'connectors/jellyfin' + sources.mkdir(parents=True) + (sources / 'Controller.cs').write_bytes(b'source\r\n') + artifacts = {} + directory = tmp_path / 'jellyfin-connector' + directory.mkdir() + for profile, (abi, _) in module['PROFILES'].items(): + archive = directory / ('connector-' + profile + '.zip') + with zipfile.ZipFile(archive, 'w') as content: + content.writestr('Themerr.Connector.dll', b'assembly') + artifacts[profile] = {'version': '2026.1004.1200.0', 'targetAbi': abi, + 'checksum': hashlib.md5(archive.read_bytes(), usedforsecurity=False).hexdigest()} + data = {'protocol': 1, 'build': module['build_identity'](tmp_path), + 'themerrVersion': '2026.1004.120000', 'artifacts': artifacts} + (directory / 'bundle.json').write_text(json.dumps(data), encoding='utf-8') + return module, tmp_path, data + + +def test_reused_bundle_is_portable_across_line_endings(builder): + module, root, data = builder + assert module['check_bundle'](root) == data + (root / 'connectors/jellyfin/Controller.cs').write_bytes(b'source\n') + assert module['check_bundle'](root) == data + + +@pytest.mark.parametrize('change', ['source', 'release', 'checksum', 'abi', 'extra_file']) +def test_stale_or_modified_bundles_fail_before_packaging(builder, monkeypatch, change): + module, root, data = builder + directory = root / 'jellyfin-connector' + if change == 'source': + (root / 'connectors/jellyfin/Controller.cs').write_text('changed', encoding='utf-8') + elif change == 'release': + monkeypatch.setenv('THEMERR_VERSION', '2026.1004.130000') + elif change == 'checksum': + (directory / 'connector-12.1.zip').write_bytes(b'changed') + elif change == 'abi': + data['artifacts']['12.1']['targetAbi'] = '13.0.0' + else: + archive = directory / 'connector-12.1.zip' + with zipfile.ZipFile(archive, 'a') as content: + content.writestr('unexpected.txt', 'unexpected') + data['artifacts']['12.1']['checksum'] = hashlib.md5( + archive.read_bytes(), usedforsecurity=False).hexdigest() + (directory / 'bundle.json').write_text(json.dumps(data), encoding='utf-8') + with pytest.raises(ValueError): + module['check_bundle'](root) diff --git a/tests/web/config.test.mjs b/tests/web/config.test.mjs index 05abaf7b..fa19a4cd 100644 --- a/tests/web/config.test.mjs +++ b/tests/web/config.test.mjs @@ -16,12 +16,12 @@ class Element { } function settings(t, request) { - const ids = ['configForm', 'save-button', 'settings-save-status', 'LOCALE', 'password-form', 'toast-region']; + const ids = ['configForm', 'save-button', 'settings-save-status', 'General-LOCALE', 'password-form', 'toast-region']; const elements = Object.fromEntries(ids.map(id => [id, new Element()])); - const locale = elements.LOCALE; + const locale = elements['General-LOCALE']; locale.value = 'en'; - locale.id = 'LOCALE'; - locale.getAttribute = () => 'General'; + locale.id = 'General-LOCALE'; + locale.getAttribute = name => name === 'category' ? 'General' : 'LOCALE'; const form = elements.configForm; form.reportValidity = () => true; form.querySelectorAll = () => [locale]; @@ -52,6 +52,21 @@ test('a saved locale change reloads immediately', async t => { assert.equal(page.elements['save-button'].disabled, true); }); +test('settings with the same key in Plex and Jellyfin retain their config namespaces', async t => { + let submitted; + const page = settings(t, async (url, options) => { + submitted = options.body; + return { ok: true, status: 200, json: async () => ({}) }; + }); + page.elements.configForm.querySelectorAll = () => ['Themerr', 'Jellyfin'].map(section => ({ + id: `${section}-BOOL_IGNORE_LOCKED_FIELDS`, type: 'checkbox', checked: section === 'Jellyfin', + getAttribute: name => name === 'category' ? section : 'BOOL_IGNORE_LOCKED_FIELDS', + })); + await page.save(); + assert.equal(submitted.get('Themerr|BOOL_IGNORE_LOCKED_FIELDS'), 'false'); + assert.equal(submitted.get('Jellyfin|BOOL_IGNORE_LOCKED_FIELDS'), 'true'); +}); + test('failed locale saves keep the page and edits', async t => { const page = settings(t, async () => ({ ok: false, status: 400, json: async () => ({ message: 'Invalid locale' }) })); page.change('fr'); diff --git a/tests/web/servers.test.mjs b/tests/web/servers.test.mjs index 8d0bbac5..d8137d14 100644 --- a/tests/web/servers.test.mjs +++ b/tests/web/servers.test.mjs @@ -65,10 +65,15 @@ function page(context, responses, savedServers = [], jellyfin = {}) { if (jellyfin.connect) { const form = new Element('form'); form.values = { url: 'http://jellyfin.example:8096', api_key: 'private-key' }; + form.elements = { url: new Element('input'), api_key: new Element('input') }; form.querySelector = () => new Element('button'); form.reset = context.mock.fn(() => { form.values.api_key = ''; }); elements['jellyfin-server-form'] = form; } + if (jellyfin.discover) { + elements['jellyfin-discover'] = new Element('button'); + elements['jellyfin-discovery-results'] = new Element('div'); + } const connectors = (jellyfin.servers || []).map(id => { const form = new Element('form'); const status = new Element('output'); @@ -134,6 +139,31 @@ test('connector status and installation stay scoped to the selected Jellyfin ser assert.equal(status.textContent, 'Restart Jellyfin.'); }); +test('connector suggestions preserve reverse proxy paths and use the HTTP fallback for local TLS', async context => { + const { connectors } = page(context, [ + { body: { repository_url: 'https://trusted.example/themerr', http_port: 9495 } }, + { body: { http_port: 9495 } }, + ], [], { servers: ['jellyfin:a', 'jellyfin:b'] }); + await new Promise(setImmediate); + assert.equal(connectors[0].form.elements.themerr_url.value, 'https://trusted.example/themerr'); + assert.equal(connectors[1].form.elements.themerr_url.value, 'http://themerr.example:9495'); +}); + +test('Jellyfin discovery selects an address safely without transmitting credentials', async context => { + const { elements, fetch } = page(context, [{ body: { servers: [{ + name: '', url: 'http://jellyfin.example:8096', + }] } }], [], { connect: true, discover: true }); + await elements['jellyfin-discover'].click(); + const [name, address, choose] = elements['jellyfin-discovery-results'].children[0].children; + assert.equal(name.textContent, ''); + assert.equal(address.textContent, 'http://jellyfin.example:8096'); + await choose.click(); + assert.equal(elements['jellyfin-server-form'].elements.url.value, address.textContent); + assert.equal(elements['jellyfin-server-form'].elements.api_key.focused, true); + assert.equal(fetch.mock.calls[0].arguments[0], '/api/jellyfin/discover'); + assert.equal(fetch.mock.calls[0].arguments[1].body, undefined); +}); + test('empty LAN discovery explains GDM and offers account or manual connections', async context => { const { elements, buttons } = page(context, [{ body: { servers: [] } }]); await buttons[1].click(); diff --git a/tests/web/workspace_navigation.test.mjs b/tests/web/workspace_navigation.test.mjs index bda11d17..7890f2e7 100644 --- a/tests/web/workspace_navigation.test.mjs +++ b/tests/web/workspace_navigation.test.mjs @@ -54,7 +54,7 @@ function fixture(t, customLoad) { const breadcrumb = {}; const meta = { content: 'old-token' }; const logout = { value: 'old-token' }; - const nav = [link('/home'), link('/servers'), link('/logs')]; + const nav = [link('/home'), link('/servers'), link('/logs'), link('/settings/')]; const main = { setAttribute() {}, removeAttribute() {}, focus: t.mock.fn() }; let content; let modals; @@ -93,6 +93,45 @@ function fixture(t, customLoad) { } const flush = () => new Promise(resolve => setImmediate(resolve)); +test('settings anchors with no history state scroll normally without reloading the page', async t => { + const f = fixture(t); + await f.controller.navigate(origin + '/settings/'); + for (const section of ['Jellyfin', 'Notifications', 'Security']) { + f.host.location.href = origin + '/settings/#' + section; + const event = new Event('popstate'); + event.state = null; + f.host.dispatchEvent(event); + await flush(); + assert.equal(f.host.location.reload.mock.callCount(), 0); + assert.equal(f.host.location.assign.mock.callCount(), 0); + assert.equal(f.load.mock.callCount(), 1); + assert.equal(f.host.location.href, origin + '/settings/#' + section); + } +}); + +test('leaving a settings anchor records a workspace position so Back preserves the player', async t => { + const f = fixture(t); + await f.controller.navigate(origin + '/settings/'); + f.host.location.href += '#jellyfin'; + f.host.history.state = null; + let saved; + const replace = f.host.history.replaceState.bind(f.host.history); + f.host.history.replaceState = (state, unused, url) => { + saved = { state, url }; + replace(state, unused, url); + }; + await f.controller.navigate(origin + '/servers'); + assert.equal(saved.state.workspacePosition, 1); + assert.equal(saved.url, origin + '/settings/#jellyfin'); + f.host.location.href = saved.url; + const event = new Event('popstate'); + event.state = saved.state; + f.host.dispatchEvent(event); + await flush(); + assert.equal(f.host.location.reload.mock.callCount(), 0); + assert.equal(f.audio.paused, false); +}); + test('page changes and refreshes preserve the player, update history, navigation and CSRF tokens', async t => { const f = fixture(t); await f.controller.navigate(origin + '/servers'); diff --git a/web/css/custom.css b/web/css/custom.css index b831fb6e..b583c9ae 100644 --- a/web/css/custom.css +++ b/web/css/custom.css @@ -279,6 +279,14 @@ details[open] > summary .details-chevron { transform: rotate(180deg); } .server-refresh { font-size: 10px; } .server-refresh time { color: var(--ink-soft); } .server-settings { margin-top: 18px; border-top: 1px solid var(--line); padding-top: 15px; } +.server-brand { display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0; font-size: 13px; font-weight: 650; } +.server-card header > .server-brand { margin-left: auto; } +.server-brand .plex-logo { font-size: 16px; } +.server-brand-jellyfin { color: #aa7cfa; } +.server-brand svg { width: 24px; height: 22px; } +.library-header > .server-brand { margin-right: 4px; } +.connector-notice { display: flex; align-items: center; gap: 8px; } +[data-connector-status] { display: block; margin-top: 10px; } .server-settings > summary, .manual-server > summary { display: flex; align-items: center; gap: 8px; list-style: none; color: var(--ink-soft); font-size: 11px; cursor: pointer; } .server-settings > summary .lucide:last-child, .manual-server > summary .lucide:last-child { margin-left: auto; width: 13px; height: 13px; } .server-settings form { margin-top: 17px; } diff --git a/web/js/config.js b/web/js/config.js index e4327ac3..daa1bf94 100644 --- a/web/js/config.js +++ b/web/js/config.js @@ -7,7 +7,7 @@ export function initSettings(signal) { if (!form) return; const save = document.getElementById('save-button'); const status = document.getElementById('settings-save-status'); - const locale = document.getElementById('LOCALE'); + const locale = document.getElementById('General-LOCALE') || document.getElementById('LOCALE'); const pageLocale = locale.value; let dirty = false; let revision = 0; @@ -30,7 +30,7 @@ export function initSettings(signal) { if (!form.reportValidity()) return; const data = new FormData(); form.querySelectorAll('[category]').forEach(field => { - if (!field.disabled) data.append(`${field.getAttribute('category')}|${field.id}`, + if (!field.disabled) data.append(`${field.getAttribute('category')}|${field.getAttribute('data-setting-key') || field.id}`, field.type === 'checkbox' ? String(field.checked) : field.value); }); save.disabled = true; diff --git a/web/js/icons.js b/web/js/icons.js index 1bf6524b..388659df 100644 --- a/web/js/icons.js +++ b/web/js/icons.js @@ -8,6 +8,7 @@ import { ArrowUp, ArrowUpRight, AudioLines, + Bell, BookOpenText, ChartNoAxesCombined, Check, @@ -77,6 +78,7 @@ const icons = { ArrowUp, ArrowUpRight, AudioLines, + Bell, BookOpenText, ChartNoAxesCombined, Check, diff --git a/web/js/servers.js b/web/js/servers.js index b18a4996..6e76d471 100644 --- a/web/js/servers.js +++ b/web/js/servers.js @@ -226,20 +226,83 @@ function initJellyfin(signal) { if (!signal?.aborted && form.isConnected) await refreshPage(); }); }, { signal }); + const discover = document.getElementById('jellyfin-discover'); + discover?.addEventListener('click', () => busy(discover, async () => { + const results = document.getElementById('jellyfin-discovery-results'); + results.textContent = _('Looking for Jellyfin servers…'); + try { + const result = await api('/api/jellyfin/discover'); + if (signal?.aborted || !results.isConnected) return; + results.replaceChildren(); + if (!result.servers.length) { + results.textContent = _('No Jellyfin servers responded. Check UDP port 7359 and local network discovery, or enter an address manually.'); + } + result.servers.forEach(server => { + const row = document.createElement('div'); + row.className = 'discovered-server'; + const name = document.createElement('strong'); + name.textContent = server.name; + const address = document.createElement('span'); + address.textContent = server.url; + const choose = document.createElement('button'); + choose.type = 'button'; + choose.className = 'btn btn-outline-light btn-sm'; + choose.textContent = _('Use this address'); + choose.addEventListener('click', () => { + form.elements.url.value = server.url; + form.elements.api_key.focus(); + }, { signal }); + row.append(name, address, choose); + results.append(row); + }); + } catch (error) { + if (!signal?.aborted && results.isConnected) results.textContent = error.message; + throw error; + } + }), { signal }); document.querySelectorAll('[data-connector-form]').forEach(connectorForm => { const status = connectorForm.querySelector('[data-connector-status]'); - connectorForm.elements.themerr_url.value = window.location.origin; const url = `/api/jellyfin/servers/${encodeURIComponent(connectorForm.dataset.serverId)}/connector`; - void api(url, { method: 'GET' }).then(result => { - if (!signal?.aborted && status.isConnected) status.textContent = result.message; - }).catch(error => { - if (!signal?.aborted && status.isConnected) status.textContent = error.message; - }); + let timer; + let suggested = false; + async function check() { + try { + const result = await api(url, { method: 'GET' }); + if (signal?.aborted || !status.isConnected) return; + status.textContent = result.message; + if (!suggested) { + const address = new URL(result.repository_url || window.location.origin); + if (result.http_port && (!result.repository_url || + (address.hostname === window.location.hostname && address.port === window.location.port))) { + address.protocol = 'http:'; + address.port = String(result.http_port); + address.pathname = '/'; + } + if (!connectorForm.elements.themerr_url.value) { + connectorForm.elements.themerr_url.value = address.href.replace(/\/$/, ''); + } + suggested = true; + } + timer = setTimeout(check, result.restart_required ? 5000 : 30000); + } catch (error) { + if (!signal?.aborted && status.isConnected) { + status.textContent = error.message; + timer = setTimeout(check, 10000); + } + } + } + signal?.addEventListener('abort', () => clearTimeout(timer), { once: true }); + void check(); connectorForm.addEventListener('submit', event => { event.preventDefault(); void busy(connectorForm.querySelector('button[type="submit"]'), async () => { const result = await api(url, { body: { themerr_url: new FormData(connectorForm).get('themerr_url') } }); - if (!signal?.aborted && status.isConnected) status.textContent = result.message; + if (!signal?.aborted && status.isConnected) { + status.textContent = result.message; + toast(result.message); + clearTimeout(timer); + timer = setTimeout(check, 5000); + } }); }, { signal }); }); diff --git a/web/js/workspace_navigation.js b/web/js/workspace_navigation.js index d18e43e4..80baf01d 100644 --- a/web/js/workspace_navigation.js +++ b/web/js/workspace_navigation.js @@ -75,7 +75,11 @@ export function initWorkspaceNavigation({ enter, leave, reportError = () => {} } function recordNavigation(destination, state, replace) { if (state) position = state.workspacePosition; else { - host.history.replaceState({ ...host.history.state, scroll: [host.scrollX, host.scrollY] }, '', currentUrl); + // Same-page anchors update the browser URL without a workspace navigation. + // Preserve that URL when recording scroll, otherwise the next anchor resolves + // against the previous section and can reload the page at the top. + host.history.replaceState({ ...host.history.state, workspacePosition: position, + scroll: [host.scrollX, host.scrollY] }, '', host.location.href); if (!replace) ++position; host.history[replace ? 'replaceState' : 'pushState']({ workspacePosition: position }, '', destination.href); } @@ -148,10 +152,10 @@ export function initWorkspaceNavigation({ enter, leave, reportError = () => {} } }); host.addEventListener('popstate', event => { if (restoring) { restoring = false; return; } - if (!Number.isInteger(event.state?.workspacePosition)) { host.location.reload(); return; } const url = new URL(host.location.href); const previous = new URL(currentUrl); if (url.pathname === previous.pathname && url.search === previous.search) { currentUrl = url.href; return; } + if (!Number.isInteger(event.state?.workspacePosition)) { host.location.reload(); return; } if (!canLeave()) { restoring = true; host.history.go(position - event.state.workspacePosition); diff --git a/web/templates/auth.html b/web/templates/auth.html index 273f4a0e..bfa87cf1 100644 --- a/web/templates/auth.html +++ b/web/templates/auth.html @@ -2,11 +2,11 @@ {% block content %}
- ThemerrFOR PLEX + Themerr{{ _('YOUR LIBRARY’S SOUNDTRACK') }}
{{ _('THE SOUND OF YOUR LIBRARY') }}

{{ _('Every story') }}
{{ _('has a soundtrack.') }}

-

{{ _('Bring your Plex libraries to life. One calm workspace for your servers, your themes, and everything in between.') }}

+

{{ _('Bring your libraries to life. One calm workspace for your servers, your themes, and everything in between.') }}

Made by LizardByte. Built for your library. @@ -36,7 +36,7 @@

{{ _('Make it yours.') if mode == 'setup' else _('Sign in to Themerr.') }}{{ _('Create workspace') if mode == 'setup' else _('Sign in') }} -

{{ _('Plex account linking happens inside your workspace.') }}

+

{{ _('Connect your media servers inside your workspace.') }}

{% endif %}

diff --git a/web/templates/config.html b/web/templates/config.html index f1f26472..d601ac74 100644 --- a/web/templates/config.html +++ b/web/templates/config.html @@ -1,18 +1,19 @@ {% extends 'base.html' %} {% macro setting_field(section, key, spec) %} {% set value = config_settings[section][key] %} +{% set field_id = section ~ '-' ~ key %}
-

{{ _(spec.description) }}{% if spec.help_url %} {{ _(spec.help_label) }}{% endif %}

+

{{ _(spec.description) }}{% if spec.help_url %} {{ _(spec.help_label) }}{% endif %}

- {% if spec.type == 'boolean' %} - {% elif spec.type == 'option' %} + {% if spec.type == 'boolean' %} + {% elif spec.type == 'option' %} {% else %}
- - {% if spec.button_directory %}{% endif %} + {% if spec.button_directory %}{% endif %}
{% endif %}
@@ -21,12 +22,12 @@ {% block content %}
{{ _('TUNE YOUR WORKSPACE') }}

{{ _('Settings') }}.

{{ _('Make Themerr work the way your library does.') }}

- +
- {% for section in ['Themerr','General','Notifications','Logging','Network'] %} + {% for group in settings_groups %}{% set section = group.section %}

{{ _(config_spec[section].name) }}

{{ _('Server connections, data directories, and ignored libraries are managed on the Servers page.') if section == 'Themerr' else _('Changes to network settings take effect after restarting Themerr.') if section == 'Network' else _(config_spec[section].description) }}

- {% for key, spec in config_spec[section].items() %}{% if spec is mapping and not spec.locked and not spec.beta and key != 'IGNORED_LIBRARY_IDS' %}{{ setting_field(section, key, spec) }}{% endif %}{% endfor %} + {% for key in group['keys'] %}{% set spec = config_spec[group.source][key] %}{% if not spec.locked and not spec.beta and key != 'IGNORED_LIBRARY_IDS' %}{{ setting_field(group.source, key, spec) }}{% endif %}{% endfor %}
{% endfor %}
diff --git a/web/templates/home.html b/web/templates/home.html index 3d3261cb..a83d0ffb 100644 --- a/web/templates/home.html +++ b/web/templates/home.html @@ -1,4 +1,5 @@ {% extends 'base.html' %} +{% from 'server_brand.html' import server_brand %} {% block content %}
{{ _('YOUR LIBRARY, IN HARMONY') }}

{{ _('Overview') }}.

{{ _('A soundtrack for every story. See what’s playing, and what needs a little attention.') }}

@@ -34,6 +35,7 @@

{{ _('Your library deserves a soundtrack.') if not servers else _('Getting t {% for section in items.values() %}
+ {{ server_brand('jellyfin' if section.server_label == 'Jellyfin' else 'plex') }} {{ section.title }}{{ section.server_name }}·{{ _('Library ID') }} {{ section.key }}{% if not section.enabled %} · {{ _('Paused') }}{% endif %} {{ section.media_percent_complete }}% diff --git a/web/templates/navbar.html b/web/templates/navbar.html index 1cdb8339..d9af3d6f 100644 --- a/web/templates/navbar.html +++ b/web/templates/navbar.html @@ -1,6 +1,6 @@

-

{{ _('Add a server') }}

{{ _('Choose a server from your Plex account, discover one nearby, or use an address you know.') }}

+

{{ _('Add a server') }}

+
+ + +
+
+

{{ _('Choose a server from your Plex account, discover one nearby, or use an address you know.') }}

{{ _('LAN discovery uses Plex GDM on your application’s network.') }}

{{ _('Account addresses are advertised by Plex and may be unreachable. HTTPS plex.direct addresses use Plex’s certificate. Choose an address reachable from the machine running Themerr, or enter a known address manually.') }}

{{ _('Enter an address manually') }} -

{{ _('Use a LAN address or a valid HTTPS Plex address. Your linked Plex account must have access. Adding an existing server updates its address.') }}

+

{{ _('Use a LAN address or a valid HTTPS Plex address. Your linked Plex account must have access. Adding an existing server updates its address.') }}

-
-
-

{{ _('Add Jellyfin') }}

{{ _('Jellyfin 10.11 and 12.1 are supported. Create an administrator API key in Jellyfin Dashboard - API Keys.') }}

+
+ {% endblock %} {% block modals %}{% include 'directory_picker.html' %}{% endblock %} From 2b1e97b0e8f099305b2af18335efc09dec6399f3 Mon Sep 17 00:00:00 2001 From: ReenigneArcher <42013603+ReenigneArcher@users.noreply.github.com> Date: Mon, 5 Oct 2026 08:46:21 -0400 Subject: [PATCH 05/11] feat: add connector artwork and SQLite ownership tracking --- .../jellyfin.tests/Connector.Tests.csproj | 2 + connectors/jellyfin.tests/ControllerTests.cs | 25 +++- connectors/jellyfin.tests/ThemeFilesTests.cs | 58 +++++--- .../jellyfin.tests/ThemeOwnershipTests.cs | 130 ++++++++++++++++++ connectors/jellyfin/Controller.cs | 29 +++- connectors/jellyfin/ThemeFiles.cs | 64 ++++----- connectors/jellyfin/ThemeOwnership.cs | 89 ++++++++++++ connectors/jellyfin/Themerr.Connector.csproj | 2 + connectors/jellyfin/thumb.png | Bin 0 -> 1534085 bytes docs/source/about/usage.rst | 17 ++- docs/source/contributing/build.rst | 5 +- docs/source/contributing/media_servers.rst | 30 +++- scripts/build_connector.py | 10 +- src/jellyfin/connector.py | 6 +- src/jellyfin/repository.py | 2 +- src/jellyfin/web.py | 10 ++ tests/conftest.py | 1 + tests/functional/test_jellyfin.py | 42 +++++- tests/unit/jellyfin/test_connector.py | 1 + tests/unit/jellyfin/test_metadata.py | 34 +++++ tests/unit/jellyfin/test_repository.py | 6 + tests/unit/test_connector_build.py | 12 +- 22 files changed, 487 insertions(+), 88 deletions(-) create mode 100644 connectors/jellyfin.tests/ThemeOwnershipTests.cs create mode 100644 connectors/jellyfin/ThemeOwnership.cs create mode 100644 connectors/jellyfin/thumb.png diff --git a/connectors/jellyfin.tests/Connector.Tests.csproj b/connectors/jellyfin.tests/Connector.Tests.csproj index b4e40ea4..8141e56f 100644 --- a/connectors/jellyfin.tests/Connector.Tests.csproj +++ b/connectors/jellyfin.tests/Connector.Tests.csproj @@ -13,6 +13,8 @@ + + diff --git a/connectors/jellyfin.tests/ControllerTests.cs b/connectors/jellyfin.tests/ControllerTests.cs index a5363ca2..6438b0cc 100644 --- a/connectors/jellyfin.tests/ControllerTests.cs +++ b/connectors/jellyfin.tests/ControllerTests.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging.Abstractions; using Moq; using Xunit; @@ -27,7 +28,9 @@ public ControllerTests() BaseItem.MediaSourceManager = media.Object; BaseItem.LibraryManager = _library.Object; _library.Setup(value => value.GetItemList(It.IsAny())).Returns([]); - _controller = new Controller(_library.Object) + var paths = new Mock(); + paths.SetupGet(value => value.DataPath).Returns(Path.Combine(_root, "server-data")); + _controller = new Controller(_library.Object, paths.Object, NullLogger.Instance) { ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext() } }; } public void Dispose() => Directory.Delete(_root, true); @@ -114,4 +117,24 @@ public async Task InvalidAndProtectedBodiesReturnFixedErrors() _controller.Request.Headers["X-Themerr-SHA256"] = new string('a', 64); Assert.IsType((await _controller.Upload(id, CancellationToken.None)).Result); } + + [Fact] + public async Task DatabaseErrorsReturnFixedMessagesAndLeaveAudioUntouched() + { + var id = Guid.NewGuid(); + _library.Setup(value => value.GetItemById(id)).Returns(new BoxSet { Id = id, Path = _root }); + var data = Path.Combine(_root, "server-data", "themerr-connector"); + Directory.CreateDirectory(data); + File.WriteAllText(Path.Combine(data, "ownership.db"), "private database contents"); + var state = Assert.IsType(_controller.State(id).Result); + Assert.Equal(500, state.StatusCode); + Assert.Equal("Could not access connector theme storage.", state.Value); + _controller.Request.Headers["X-Themerr-Connector"] = "development"; + _controller.Request.ContentType = "audio/mp4"; + _controller.Request.Headers["X-Themerr-SHA256"] = new string('a', 64); + var upload = Assert.IsType((await _controller.Upload(id, CancellationToken.None)).Result); + Assert.Equal(500, upload.StatusCode); + Assert.Equal(state.Value, upload.Value); + Assert.Empty(Directory.GetFiles(_root)); + } } diff --git a/connectors/jellyfin.tests/ThemeFilesTests.cs b/connectors/jellyfin.tests/ThemeFilesTests.cs index f8d2e5c5..bdb9e7bc 100644 --- a/connectors/jellyfin.tests/ThemeFilesTests.cs +++ b/connectors/jellyfin.tests/ThemeFilesTests.cs @@ -9,12 +9,20 @@ public sealed class ThemeFilesTests : IDisposable { private readonly string _root = Path.Combine(Path.GetTempPath(), "themerr-connector-test-" + Guid.NewGuid()); private readonly byte[] _audio = "complete validated audio"u8.ToArray(); + private readonly Guid _itemId = Guid.NewGuid(); + private readonly ThemeFiles _themes; + private readonly ThemeOwnership _ownership; private string Digest => Convert.ToHexString(SHA256.HashData(_audio)).ToLowerInvariant(); - public ThemeFilesTests() => Directory.CreateDirectory(_root); + public ThemeFilesTests() + { + Directory.CreateDirectory(_root); + _ownership = new ThemeOwnership(Path.Combine(_root, "server-data")); + _themes = new ThemeFiles(_ownership); + } public void Dispose() => Directory.Delete(_root, true); private string FilePath(string name) => Path.Combine(_root, name); private Task Save(string? type = "audio/mp4", string? digest = null, int count = 0, - bool overwrite = false, Stream? stream = null, bool backup = true) => ThemeFiles.Save(_root, + bool overwrite = false, Stream? stream = null, bool backup = true) => _themes.Save(_itemId, _root, stream ?? new MemoryStream(_audio), type, digest ?? Digest, count, CancellationToken.None, overwrite, backup); [Theory] @@ -55,7 +63,7 @@ public async Task ReplacementCanRemoveUserThemesWithoutCreatingBackups() var music = Directory.CreateDirectory(FilePath("theme-music")); File.WriteAllText(Path.Combine(music.FullName, "user.mp3"), "another theme"); await Save(count: 2, overwrite: true, backup: false); - Assert.True(ThemeFiles.State(_root).Owned); + Assert.True(_themes.State(_itemId, _root).Owned); Assert.False(File.Exists(FilePath("theme.mp3"))); Assert.False(Directory.Exists(music.FullName)); Assert.False(Directory.Exists(FilePath(".themerr-user-themes"))); @@ -92,16 +100,19 @@ public async Task RejectsEmptyAudioAndCleansTemporaryFiles() [Fact] public async Task SavesVerifiedAudioAndSwitchesFormats() { - Assert.False(ThemeFiles.State(_root).Present); + Assert.False(_themes.State(_itemId, _root).Present); var saved = await Save(); Assert.True(saved.Owned); Assert.Equal(Digest, saved.Sha256); - Assert.True(ThemeFiles.State(_root).Owned); - Assert.False(ThemeFiles.State(_root, 2).Owned); + Assert.True(_themes.State(_itemId, _root).Owned); + Assert.False(_themes.State(_itemId, _root, 2).Owned); await Assert.ThrowsAsync(() => Save(count: 2)); await Save("audio/ogg", Digest.ToUpperInvariant()); Assert.False(File.Exists(FilePath("theme.m4a"))); Assert.Equal(_audio, File.ReadAllBytes(FilePath("theme.opus"))); + Assert.Equal([FilePath("theme.opus")], Directory.GetFiles(_root)); + Assert.True(new ThemeFiles(new ThemeOwnership(FilePath("server-data"))).State(_itemId, _root).Owned); + Assert.False(_themes.State(Guid.NewGuid(), _root).Owned); } [Fact] @@ -113,7 +124,7 @@ public async Task PreservesUserThemesByDefaultIncludingManuallyEditedUploads() File.Delete(FilePath("theme.mp3")); await Save(); File.WriteAllText(FilePath("theme.m4a"), "manually changed"); - Assert.False(ThemeFiles.State(_root).Owned); + Assert.False(_themes.State(_itemId, _root).Owned); await Assert.ThrowsAsync(() => Save()); Assert.Equal("manually changed", File.ReadAllText(FilePath("theme.m4a"))); } @@ -127,7 +138,7 @@ public async Task ExplicitReplacementBacksUpUserFiles(string name) File.WriteAllText(FilePath(name), "user theme"); var state = await Save(overwrite: true); Assert.True(state.Owned); - Assert.True(ThemeFiles.State(_root).Owned); + Assert.True(_themes.State(_itemId, _root).Owned); Assert.Equal("user theme", File.ReadAllText(Path.Combine(_root, ".themerr-user-themes", name))); Assert.Equal(_audio, File.ReadAllBytes(FilePath("theme.m4a"))); } @@ -138,7 +149,7 @@ public async Task ReplacementBacksUpThemeMusicFolderAndNeverOverwritesEarlierBac var music = FilePath("theme-music"); Directory.CreateDirectory(music); File.WriteAllText(Path.Combine(music, "my song.mp3"), "user theme"); - Assert.True(ThemeFiles.State(_root).Present); + Assert.True(_themes.State(_itemId, _root).Present); await Assert.ThrowsAsync(() => Save(count: 1)); await Save(count: 1, overwrite: true); Assert.Equal("user theme", File.ReadAllText(Path.Combine(_root, ".themerr-user-theme-music", "my song.mp3"))); @@ -156,15 +167,27 @@ public async Task NativeThemesOutsideFixedResourcesStayProtected() } [Theory] - [InlineData("not json")] - [InlineData("null")] - [InlineData("{\"File\":\"../outside.m4a\",\"Sha256\":\"anything\"}")] - public async Task CorruptOrTraversingOwnershipDoesNotAuthorizeReplacement(string ownership) + [InlineData("../outside.m4a")] + [InlineData("theme.mp3")] + public async Task DatabaseValuesNeverBecomePaths(string filename) { File.WriteAllText(FilePath("theme.m4a"), "user theme"); - File.WriteAllText(FilePath(".themerr-connector.json"), ownership); - Assert.False(ThemeFiles.State(_root).Owned); + _ownership.Record(_itemId, filename, Digest, () => { }); + Assert.False(_themes.State(_itemId, _root).Owned); + await Assert.ThrowsAsync(() => Save()); + } + + [Fact] + public async Task JsonSidecarsAreIgnoredAndNeverMigratedOrRemoved() + { + File.WriteAllBytes(FilePath("theme.m4a"), _audio); + var json = "{\"File\":\"theme.m4a\",\"Sha256\":\"" + Digest + "\"}"; + File.WriteAllText(FilePath(".themerr-connector.json"), json); + Assert.False(_themes.State(_itemId, _root).Owned); await Assert.ThrowsAsync(() => Save()); + await Save(overwrite: true, backup: false); + Assert.Equal(json, File.ReadAllText(FilePath(".themerr-connector.json"))); + Assert.True(_themes.State(_itemId, _root).Owned); } [Fact] @@ -172,13 +195,12 @@ public async Task OwnershipWithoutItsFileIsNotOwned() { await Save(); File.Delete(FilePath("theme.m4a")); - Assert.False(ThemeFiles.State(_root).Present); - Assert.False(ThemeFiles.State(_root).Owned); + Assert.False(_themes.State(_itemId, _root).Present); + Assert.False(_themes.State(_itemId, _root).Owned); } [Theory] [InlineData("theme.m4a", false)] - [InlineData(".themerr-connector.json", false)] [InlineData("theme-music", true)] [InlineData(".themerr-user-themes", true)] public async Task RejectsLinksIntoSiblingDirectories(string name, bool directory) diff --git a/connectors/jellyfin.tests/ThemeOwnershipTests.cs b/connectors/jellyfin.tests/ThemeOwnershipTests.cs new file mode 100644 index 00000000..76c51422 --- /dev/null +++ b/connectors/jellyfin.tests/ThemeOwnershipTests.cs @@ -0,0 +1,130 @@ +using System.Diagnostics; +using Microsoft.Data.Sqlite; +using Themerr.Connector; +using Xunit; + +namespace Themerr.Connector.Tests; + +public sealed class ThemeOwnershipTests : IDisposable +{ + private readonly string _root = Path.Combine(Path.GetTempPath(), "themerr-ownership-test-" + Guid.NewGuid()); + private string DirectoryPath => Path.Combine(_root, "themerr-connector"); + private string DatabasePath => Path.Combine(DirectoryPath, "ownership.db"); + private readonly ThemeOwnership _ownership; + + public ThemeOwnershipTests() + { + Directory.CreateDirectory(_root); + _ownership = new ThemeOwnership(_root); + } + + public void Dispose() => Directory.Delete(_root, true); + + [Fact] + public void ReadingMissingOwnershipDoesNotCreateFiles() + { + Assert.Null(_ownership.Find(Guid.NewGuid())); + Assert.Empty(Directory.GetFileSystemEntries(_root)); + } + + [Fact] + public void MultipleItemsAndFormatChangesPersistInOneDatabaseAcrossInstances() + { + var first = Guid.NewGuid(); + var second = Guid.NewGuid(); + _ownership.Record(first, "theme.m4a", "first", () => { }); + _ownership.Record(second, "theme.m4a", "second", () => { }); + _ownership.Record(first, "theme.opus", "replacement", () => { }); + var reopened = new ThemeOwnership(_root); + Assert.Equal(new Ownership("theme.opus", "replacement"), reopened.Find(first)); + Assert.Equal(new Ownership("theme.m4a", "second"), reopened.Find(second)); + Assert.Null(reopened.Find(Guid.NewGuid())); + Assert.Equal([DatabasePath], Directory.GetFiles(DirectoryPath)); + using var database = new SqliteConnection(new SqliteConnectionStringBuilder + { DataSource = DatabasePath, Pooling = false }.ToString()); + database.Open(); + using var count = database.CreateCommand(); + count.CommandText = "SELECT COUNT(*) FROM theme_ownership"; + Assert.Equal(2L, count.ExecuteScalar()); + } + + [Fact] + public void FailedAudioWritesNeverCommitOwnership() + { + var id = Guid.NewGuid(); + Assert.Throws(() => _ownership.Record(id, "theme.m4a", "failed", () => throw new IOException())); + Assert.Null(_ownership.Find(id)); + _ownership.Record(id, "theme.m4a", "original", () => { }); + Assert.Throws(() => _ownership.Record(id, "theme.opus", "failed", () => throw new IOException())); + Assert.Equal(new Ownership("theme.m4a", "original"), new ThemeOwnership(_root).Find(id)); + } + + [Fact] + public async Task ConcurrentItemsKeepSeparateRecords() + { + var ids = Enumerable.Range(0, 8).Select(_ => Guid.NewGuid()).ToArray(); + await Task.WhenAll(ids.Select(id => Task.Run(() => + new ThemeOwnership(_root).Record(id, "theme.m4a", id.ToString("N"), () => { })))); + foreach (var id in ids) Assert.Equal(id.ToString("N"), _ownership.Find(id)!.Sha256); + Assert.Equal([DatabasePath], Directory.GetFiles(DirectoryPath)); + } + + [Fact] + public void BrokenDatabasesNeverAuthorizeOrWriteAudio() + { + Directory.CreateDirectory(DirectoryPath); + File.WriteAllText(DatabasePath, "invalid database"); + Assert.Throws(() => _ownership.Find(Guid.NewGuid())); + var wroteAudio = false; + Assert.Throws(() => _ownership.Record(Guid.NewGuid(), "theme.m4a", "digest", + () => wroteAudio = true)); + Assert.False(wroteAudio); + Assert.Equal("invalid database", File.ReadAllText(DatabasePath)); + } + + [Theory] + [InlineData("ownership.db")] + [InlineData("ownership.db-journal")] + [InlineData("ownership.db-wal")] + [InlineData("ownership.db-shm")] + public void DatabaseAndJournalLinksCannotAccessSiblingFiles(string filename) + { + Directory.CreateDirectory(DirectoryPath); + var outside = Path.Combine(_root, "outside.db"); + File.WriteAllText(outside, "outside"); + try { File.CreateSymbolicLink(Path.Combine(DirectoryPath, filename), outside); } + catch (UnauthorizedAccessException) { Assert.Skip("Symbolic link creation privilege unavailable."); } + catch (IOException error) when (OperatingSystem.IsWindows() && (error.HResult & 0xffff) == 1314) + { Assert.Skip("Symbolic link creation privilege unavailable."); } + Assert.Throws(() => _ownership.Find(Guid.NewGuid())); + Assert.Throws(() => _ownership.Record(Guid.NewGuid(), "theme.m4a", "digest", () => { })); + Assert.Equal("outside", File.ReadAllText(outside)); + } + + [Fact] + public async Task DatabaseDirectoryJunctionCannotAccessSiblingFiles() + { + if (!OperatingSystem.IsWindows()) Assert.Skip("Windows junction test."); + var sibling = _root + "-sibling"; + Directory.CreateDirectory(sibling); + var outside = Path.Combine(sibling, "ownership.db"); + File.WriteAllText(outside, "outside"); + var command = new ProcessStartInfo(Path.Combine(Environment.SystemDirectory, "cmd.exe")) + { UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true }; + foreach (var argument in new[] { "/c", "mklink", "/J", DirectoryPath, sibling }) command.ArgumentList.Add(argument); + try + { + using var process = Process.Start(command)!; + await process.WaitForExitAsync(TestContext.Current.CancellationToken); + Assert.Equal(0, process.ExitCode); + Assert.Throws(() => _ownership.Find(Guid.NewGuid())); + Assert.Throws(() => _ownership.Record(Guid.NewGuid(), "theme.m4a", "digest", () => { })); + Assert.Equal("outside", File.ReadAllText(outside)); + } + finally + { + if (Directory.Exists(DirectoryPath)) Directory.Delete(DirectoryPath); + Directory.Delete(sibling, true); + } + } +} diff --git a/connectors/jellyfin/Controller.cs b/connectors/jellyfin/Controller.cs index d87fe9e4..dc6683e5 100644 --- a/connectors/jellyfin/Controller.cs +++ b/connectors/jellyfin/Controller.cs @@ -1,10 +1,13 @@ using System.Collections.Concurrent; using System.Reflection; using MediaBrowser.Common.Api; +using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using Microsoft.Data.Sqlite; +using Microsoft.Extensions.Logging; namespace Themerr.Connector; @@ -15,10 +18,17 @@ public sealed class Controller : ControllerBase { private static readonly ConcurrentDictionary Locks = new(); private readonly ILibraryManager _library; + private readonly ThemeFiles _themes; + private readonly ILogger _logger; private static string Metadata(string key) => typeof(Plugin).Assembly.GetCustomAttributes() .Single(a => a.Key == key).Value!; - public Controller(ILibraryManager library) => _library = library; + public Controller(ILibraryManager library, IApplicationPaths paths, ILogger logger) + { + _library = library; + _themes = new(new ThemeOwnership(paths.DataPath)); + _logger = logger; + } [HttpGet("Connector")] public object Identity() => new { protocol = 1, build = Metadata("ThemerrBuild"), targetAbi = Metadata("JellyfinAbi") }; @@ -30,7 +40,10 @@ public ActionResult State(Guid itemId) if (item is null) return NotFound(); var root = Root(item); if (root is null) return BadRequest(); - return ThemeFiles.State(root, item.GetThemeSongs().Count); + try { return _themes.State(itemId, root, item.GetThemeSongs().Count); } + catch (ThemeConflictException) { return Conflict(); } + catch (Exception error) when (error is SqliteException or IOException or UnauthorizedAccessException) + { return StorageFailure(error); } } [HttpPost("Items/{itemId:guid}/Theme")] @@ -46,7 +59,7 @@ public async Task> Upload(Guid itemId, CancellationToke await gate.WaitAsync(cancellationToken).ConfigureAwait(false); try { - var state = await ThemeFiles.Save(root, Request.Body, Request.ContentType, + var state = await _themes.Save(itemId, root, Request.Body, Request.ContentType, Request.Headers["X-Themerr-SHA256"].ToString(), item.GetThemeSongs().Count, cancellationToken, Request.Headers["X-Themerr-Overwrite-User"] == "true", Request.Headers["X-Themerr-Backup-User"] != "false").ConfigureAwait(false); @@ -55,11 +68,17 @@ public async Task> Upload(Guid itemId, CancellationToke } catch (ThemeConflictException) { return Conflict(); } catch (InvalidDataException) { return BadRequest(); } - catch (IOException) { return StatusCode(500); } - catch (UnauthorizedAccessException) { return StatusCode(500); } + catch (Exception error) when (error is SqliteException or IOException or UnauthorizedAccessException) + { return StorageFailure(error); } finally { gate.Release(); } } + private ObjectResult StorageFailure(Exception error) + { + _logger.LogError(error, "Could not access connector theme storage."); + return StatusCode(500, "Could not access connector theme storage."); + } + private static string? Root(BaseItem item) => item is MediaBrowser.Controller.Entities.Movies.Movie or MediaBrowser.Controller.Entities.TV.Series or MediaBrowser.Controller.Entities.Movies.BoxSet && diff --git a/connectors/jellyfin/ThemeFiles.cs b/connectors/jellyfin/ThemeFiles.cs index 56c6f947..d92b1d45 100644 --- a/connectors/jellyfin/ThemeFiles.cs +++ b/connectors/jellyfin/ThemeFiles.cs @@ -1,5 +1,4 @@ using System.Security.Cryptography; -using System.Text.Json; using System.Text.Json.Serialization; namespace Themerr.Connector; @@ -11,9 +10,8 @@ public sealed record ThemeState( public sealed class ThemeConflictException : Exception; /// Writes only server-owned filenames beneath a library item's directory. -public static class ThemeFiles +public sealed class ThemeFiles(ThemeOwnership ownership) { - private const string OwnershipFile = ".themerr-connector.json"; private const long MaximumBytes = 104857600; private const string UserBackup = ".themerr-user-themes"; private const string ThemeMusic = "theme-music"; @@ -26,23 +24,16 @@ public static class ThemeFiles ["audio/mp4"] = "theme.m4a", ["audio/ogg"] = "theme.opus" }; - private sealed record Ownership(string File, string Sha256); - - public static ThemeState State(string root, int themeSongCount = 0) + public ThemeState State(Guid itemId, string root, int themeSongCount = 0) { RejectDirectoryLink(root); var files = ThemeNames.Where(name => File.Exists(Path.Combine(root, name))).ToArray(); var music = Path.Combine(root, ThemeMusic); RejectDirectoryLink(music); var present = files.Length > 0 || themeSongCount > 0 || Directory.Exists(music); - var metadata = Path.Combine(root, OwnershipFile); - RejectLink(metadata); - if (!File.Exists(metadata)) return new(present, false, null); - Ownership? ownership; - try { ownership = JsonSerializer.Deserialize(File.ReadAllText(metadata)); } - catch (JsonException) { return new(present, false, null); } - // A metadata value selects a code-owned filename; it never becomes a path. - var file = Formats.Values.FirstOrDefault(value => value == ownership?.File); + var record = ownership.Find(itemId); + // A database value selects a code-owned filename; it never becomes a path. + var file = Formats.Values.FirstOrDefault(value => value == record?.File); if (file is null || files.Length != 1 || themeSongCount > 1 || Directory.Exists(music)) return new(present, false, null); var path = Path.Combine(root, file); @@ -50,55 +41,46 @@ public static ThemeState State(string root, int themeSongCount = 0) if (!File.Exists(path)) return new(present, false, null); using var stream = File.OpenRead(path); var digest = Convert.ToHexString(SHA256.HashData(stream)).ToLowerInvariant(); - return new(true, digest == ownership!.Sha256, digest); + return new(true, digest == record!.Sha256, digest); } - public static async Task Save(string root, Stream body, string? contentType, + public async Task Save(Guid itemId, string root, Stream body, string? contentType, string expectedDigest, int themeSongCount, CancellationToken cancellationToken, bool overwriteUser = false, bool backupUser = true) { if (contentType is null || !Formats.TryGetValue(contentType, out var file) || expectedDigest.Length != 64 || !expectedDigest.All(Uri.IsHexDigit)) throw new InvalidDataException(); - var state = State(root, themeSongCount); + var state = State(itemId, root, themeSongCount); if (state.Present && !state.Owned && !overwriteUser) throw new ThemeConflictException(); var originals = Snapshot(root); var path = Path.Combine(root, file); - var metadata = Path.Combine(root, OwnershipFile); RejectLink(path); - RejectLink(metadata); var temporary = Path.Combine(root, Path.GetRandomFileName()); - var metadataTemporary = Path.Combine(root, Path.GetRandomFileName()); try { var digest = await Write(body, temporary, cancellationToken).ConfigureAwait(false); if (!string.Equals(digest, expectedDigest, StringComparison.OrdinalIgnoreCase)) throw new InvalidDataException(); - await using (var metadataStream = new FileStream(metadataTemporary, FileMode.CreateNew, - FileAccess.Write, FileShare.None, 4096, FileOptions.Asynchronous)) - { - await JsonSerializer.SerializeAsync(metadataStream, new Ownership(file, digest), - cancellationToken: cancellationToken).ConfigureAwait(false); - } - // Recheck the ownership and link boundaries after receiving the complete body. - var current = State(root, themeSongCount); - if ((current.Present && !current.Owned && !overwriteUser) || - !originals.SequenceEqual(Snapshot(root))) throw new ThemeConflictException(); - RejectLink(path); - RejectLink(metadata); - if (current.Present && !current.Owned) ReplaceUserThemes(root, themeSongCount, backupUser); - File.Move(temporary, path, true); - File.Move(metadataTemporary, metadata, true); - foreach (var other in Formats.Values.Where(value => value != file)) + ownership.Record(itemId, file, digest, () => { - var old = Path.Combine(root, other); - RejectLink(old); - if (state.Owned && File.Exists(old)) File.Delete(old); - } + // Recheck ownership and link boundaries after receiving the complete body. + var current = State(itemId, root, themeSongCount); + if ((current.Present && !current.Owned && !overwriteUser) || + !originals.SequenceEqual(Snapshot(root))) throw new ThemeConflictException(); + RejectLink(path); + if (current.Present && !current.Owned) ReplaceUserThemes(root, themeSongCount, backupUser); + File.Move(temporary, path, true); + foreach (var other in Formats.Values.Where(value => value != file)) + { + var old = Path.Combine(root, other); + RejectLink(old); + if (state.Owned && File.Exists(old)) File.Delete(old); + } + }); return new(true, true, digest); } finally { File.Delete(temporary); - File.Delete(metadataTemporary); } } diff --git a/connectors/jellyfin/ThemeOwnership.cs b/connectors/jellyfin/ThemeOwnership.cs new file mode 100644 index 00000000..b216436f --- /dev/null +++ b/connectors/jellyfin/ThemeOwnership.cs @@ -0,0 +1,89 @@ +using Microsoft.Data.Sqlite; + +namespace Themerr.Connector; + +public sealed record Ownership(string File, string Sha256); + +/// Stores ownership in one database beneath Jellyfin's trusted server data directory. +public sealed class ThemeOwnership +{ + private readonly string _directory; + private readonly string _database; + + public ThemeOwnership(string serverDataPath) + { + _directory = Path.Combine(serverDataPath, "themerr-connector"); + _database = Path.Combine(_directory, "ownership.db"); + } + + public Ownership? Find(Guid itemId) + { + ValidatePaths(); + if (!File.Exists(_database)) return null; + using var connection = Open(SqliteOpenMode.ReadOnly); + using var command = connection.CreateCommand(); + command.CommandText = "SELECT filename, sha256 FROM theme_ownership WHERE item_id = $id"; + command.Parameters.AddWithValue("$id", itemId.ToString("N")); + using var reader = command.ExecuteReader(); + return reader.Read() ? new(reader.GetString(0), reader.GetString(1)) : null; + } + + public void Record(Guid itemId, string file, string digest, Action writeTheme) + { + ValidatePaths(); + Directory.CreateDirectory(_directory); + using var connection = Open(SqliteOpenMode.ReadWriteCreate); + using (var schema = connection.CreateCommand()) + { + schema.CommandText = """ + CREATE TABLE IF NOT EXISTS theme_ownership ( + item_id TEXT PRIMARY KEY NOT NULL, + filename TEXT NOT NULL, + sha256 TEXT NOT NULL + ) + """; + schema.ExecuteNonQuery(); + } + // Acquire the database write lock before changing audio; failed writes roll back ownership. + using var transaction = connection.BeginTransaction(); + using var command = connection.CreateCommand(); + command.Transaction = transaction; + command.CommandText = """ + INSERT INTO theme_ownership (item_id, filename, sha256) VALUES ($id, $file, $digest) + ON CONFLICT(item_id) DO UPDATE SET filename = excluded.filename, sha256 = excluded.sha256 + """; + command.Parameters.AddWithValue("$id", itemId.ToString("N")); + command.Parameters.AddWithValue("$file", file); + command.Parameters.AddWithValue("$digest", digest); + command.ExecuteNonQuery(); + writeTheme(); + transaction.Commit(); + } + + private SqliteConnection Open(SqliteOpenMode mode) + { + ValidatePaths(); + var connection = new SqliteConnection(new SqliteConnectionStringBuilder + { + DataSource = _database, Mode = mode, Pooling = false + }.ToString()); + try { connection.Open(); } + catch { connection.Dispose(); throw; } + return connection; + } + + private void ValidatePaths() + { + var directory = new DirectoryInfo(_directory); + if (directory.LinkTarget is not null || + (directory.Exists && (directory.Attributes & FileAttributes.ReparsePoint) != 0)) + throw new ThemeConflictException(); + // SQLite can create these fixed companion files while a transaction is in progress. + foreach (var path in new[] { _database, _database + "-journal", _database + "-wal", _database + "-shm" }) + { + var info = new FileInfo(path); + if (info.LinkTarget is not null || (info.Exists && (info.Attributes & FileAttributes.ReparsePoint) != 0)) + throw new ThemeConflictException(); + } + } +} diff --git a/connectors/jellyfin/Themerr.Connector.csproj b/connectors/jellyfin/Themerr.Connector.csproj index 159deed4..ff2735e2 100644 --- a/connectors/jellyfin/Themerr.Connector.csproj +++ b/connectors/jellyfin/Themerr.Connector.csproj @@ -14,6 +14,8 @@ + + diff --git a/connectors/jellyfin/thumb.png b/connectors/jellyfin/thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..6342a3f1a1ccdac9d70649a6eff2ceab15700f95 GIT binary patch literal 1534085 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≠S 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

G9FJY%#LvSV#a1#{p`-SE|u=E_n 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

3hYV{|V4N$kWNI5RQ|Okiyb)3vglgqRINH@v=L3 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

bOy?F8cSFir<%a?!h z;^kl6+1abTYHhXIONYm)X7eEC@e<8 zz!*Q7`x-?THDNi9f|4jH$~R^5suOgK@6nD{4S=uPJV~FH3g-4`1M}nf#|!=t$LG7< zJrJI4_6{)l|n70tI+WCZ17=tz;4GC2)-&F|mLN zEgUnMF+yxOHa(13jaccho%8^}RE<;$2ZTN}fgGFqJ-d2;x4XRaFU)+p-+!^+-&x1u z>FNAC{$K9)$$tO-;qbxL)w@@R_YXIBd-l$3{ce{G?xJ3jHM?0=MDpN@J1;np4ILdp zOJ5>dV`4W~m3ey^WE3-w^;&@;F{L&?h|wn?ue`Egs&Vy3Eg?r;cBcxhfK8lMD`oh? z9-G0|hRz3P)J}BSjdbzWJh54^GEH}yA->GJwqhFM(m3&Tg0M@Qp&_!Yw{$w1DVIWv zp*%H@G-;0~M#rrFQVEb4t18YuARSy)Iu5l;oTKVpq;cULp}{IgCvE+ZxYqPxvTm#c z%&DQOtiU=+5K|`_Ow9GLa4em*cU5L*;wfrGS;8n+{45Ru z;E|207x<_~9-UN2c*Bfu5m+aa0n{o$zj&oF8OJ#Ch^IlP#x}>LM4FGPF$K)>Mdp+6 zOoEk-&p~O0PT?KUSFyxmb2XjfhLh4Jdi1phjUSa*Yhj0YJvUvse31%N z)(*BcY29KbM{|uh#spHmP*XY=8H7z=v_`~+2wZKD4a?|4ijbEsk!hA%kL@Ir$C#_l zrGU1+?H-7n3KNHc4Tl-I-U9d^P>Q1;sW=4VJ24?$HO4qKZ3xJaw?JH^a)M~EV46d& zLOLQ6$Uw6Cl7{f37bTsj^7Sc#fpNqWbNbmjv;JnEmLy5@%zr=?PoVp6f9JQ1z*&Hy zXsu;5#`Uu8EDN`~h^$g~Xhx6jG4fF`mXv$+<7*LQ3X0^MXVqrG}9DtZH z<8g?S$c%}Tq`X>6yKhi2Ll4lDq0n4e+5QMRhg08_#qCEwyD_#PHFGOU{t(;Ek^ta13Lsro^oN!f} z13gx3KAoKzqn2;t$vO7uTF?oCn z6w_AW5J$i_Cxp^)QVuYEz*i;1zKklTG!{T>KX{01X)r4Yju#$oY+Pz>@{O2-$O%r< zGAF2wvMenob4A#$V4w&Vuvlr}^$uuBW`ZxeMK4M=%_x+U=Vp;Beba0KVD!28V(e?d z>rIaY^)9zh!)X=+h|Z^Kdlq6yQ1zn_WQR8QFNQdz!=vPN0|6wFO=MXf!%Yx_4bODM0ae&g=Vx9;wL_520&m%V+w z-~ZVQ{-?p;f9Lh@zJBNXuU`M?=K9ma{(kKZ?@1A}=`wm3Fx$~xW1GEHD|^|{weuX< zTkdiY>H4y-S`np5Q2O>mlq0A`Mcabsm4FWWys0nzHso0(RbYl{qpc^Mo@MiXCAiZo z&(Nrjmp%su?N9zOllQOq%I3YRtDhaN9(I>6pPpVlKCU;8bNk=7JzQM;*hO;kLO>7{&KgEcl;LohyE7)yH{8I4QRJa4tGG?he*da6{*@ku5G`WlJWaEg*&g zO1Mfn28Ddl7f7S>qb7LpluYZ)$VT~@z8TXdjAYtJty$1+y?YU0R3|vU5;0oAtbbi{AT%eXrnLH?QxR&!V zyMCIdLu&M9p6ourH>Myy0FLYmBcN7M^+b<7{F~%RNqE_;hL^S_o;LNB2Uz3PU{w+2 z(3fsCbK;pn@o0!3N2KvS&1f2WyU@+eWEoSVV+du@gHkva5X9Ojdm_Vl%vnrybwb|2|>n&3Q z7|VAOjFz5f)sg8Wx_sV$`?tTH5vKsFm@Gb})O8eQY&OtLyNfk&N$|<60H=X*fM*G? ztY%mNYQ>j<6L1nxu8k*tK{4h)DUDDP2N(n2D6-Q5nuC|{P7My4*6L8efT^^a2C!Oo z`ElP}KG9h)&H3j^`hc*?YwN4Ss5R|TJgUt^v6Eh{zK$R&hPZpoy;_MApqx0ywY@!E zT|IHcri-P9m;e#hKxv%*tbs%(B3OWZ-9FY#Bi5(6w94fhA5BY!bdDNl0%r1P1pNYO zaHuaZ4GgRa(^HE;YW-_sIyC=+!sHJsCla}wH0

KsNxu89WNuI=TNu4QEKQ=N*QZ|hp#pB1}(g6(?ARvsR!^#TZ*I9NS2{Gz2*0{ z7&nSLMkU^3`32J^QI2KngzC+}bFc-GLX!_pNijAdQQaxWC^KsT3d0J+X1=UNFJ4<~ znbeaRk;piC2?^VK&D!!V0n<{M6U$MUL(*uw6^d(BqRGFh7~Fq^Q^s zL4l!MJr)BaJ$?ZhidM5tjd%cvl4)Y;JXgISnnHVvwrj02#~PEb_KbOAD3b<$To~b* zFQD)5e)G-!x9;!1`EdXG`~~!vvE`&bUS58(-~aXP?Z5RKI{)JH%MY*kpU0jq_OnT{ zP399cJv%0{n+kU}7E+9BuhPv=cE{wASFEDiz}fu27+Z=) zcDo*$giVUtmtA34nqr*kP->}xZv!T~Q4^-qu`0dav*hE&?#*uh@nQeL;pz?lEg?5r zw@*(u{t|Q`U7K_JlC@9wyPxqt5MKS{>iXTB`hEWfG_IU+w}v>jL$^}5t-B40ZC?{R zRMnS2IUKT;Whu&Tcmj*Thc{PJFc9w@+OilIxl9vH7@E%28>N)NkIWVGY@uaXm^65z z@oHMGQdh_2TTeja$Lq~v(=(4*gtoaZbSWolrPXG{iIa%j;Ue=&j|4?O?C9rm&=CYG zC>I|qvZu#7h>tJY6otvtB!JkmU~m81;xWo^Hb*lm4gV%hADGuNp9o}X{32EYJ56zdx;_z@7ST#Ep+!d02(}kZ zcSYT4(Ty?8)02i&=AJ9kD=KAJP56o$f3qP^O$-7s6;u535Rbq_ao6bq8zLv4LDxVA zNUGya7HxoD{E?+OjfvKGhD^@gN&yjNGUMa}(rRO^MKac+3#nl{T{C%%Jytwxq>qmG z#rg-*RiQ@FsQAX%MFTV=Jopk=M#~$*WkxS28leNxPC*3KTRTJjKKhhCT@)yE)CH#u zv9HzCq1@iVsY7FtG|JaC|DG|jKo(}snt9`3cSh)s3IZaT7Y$Yvmyxho41ofZalL*m z3r}xI1`xyS==9d33`$lD(3%c;%FKh=fbIzy0K~8~TSz0fhfxqJ5F&Fy7*rO|N6pcb zbxsY8!tjFDKe#lbLV*rHE2wNQl;g#myVq2B;WgTXru?T>H2>Q5UUtxU= zfqo+D1&VF8O*74;rf*nXMX5!~!;%r6EGP0r@Ei>JM<9?kg0QY?#{QN#dHJhurFIqC zNbS_PFiau#L;+1;`~_}JW7j-y!@1-D6H*TXX8~z;HLN5O2}NwJt7lr(oXg`@pQdDn zmL$EVc7dj60xp<2F`IZ$MikqqBa7BKcttX_Hc26z2tcsTh8q$RgKymOrdv7N4N=pm z0v;`WFU2Sl+B<_T@rtnvk(YXdCkXZk_EMvTG8#0JIzP}Rp!V1rO(MC*1D5|tr#uu# zMlRTsbmi(-Wa9(|4Gz-*2Tf{bX^h+PUgX5yc6i!}ew`gSMT)c||*^!)2@ z?tbg;&A0CFzJ7my{tLEWzV_&!-S~^!+dq8$`VU^e{=-)<{^I7vPp+;%JM7tBd~ANQ z50<@@VfAfnCib`6H4mP?_T~x&1cZBx9j*!#LLTaUNwP(4T8c4+uZ1SS#>R$7=!`?$ z1k*LYNcbTdebYotF0~4_6S3_CmR9z38!m!Z1PnEj!MX5#=?NlWQ80Jr)(cJ&`2r8SnNxxG15RtQp|u=9xpHFKi=H!KH2YcF^QTjee?9x_w{r8 zGPTb*#rqHT{6~~O$wh3Jw~6cw?yCG<$8H$63;Uz@MBEQ|>#$!Dx(_jELvuglz=X@* z9g7HP40;-W1dC&frk__x8oYTOt+ch2W8&zQN_aE}L`~rrk08oBNN11UAaGE9MHH3k_FCMGb|;o98TP~i@5k?62KPXnoJcA8GEAquz(x5 z?=8M!64*$Q>EpCuV0B}h>PS?FI6q+Nq=1RB1Vof6j2`;Q?=aM|<#5T6BN$cpfQTB& z1~w1#w(LTCH0ju^aT->e7lrir&E)!QUoCHDnFdC0J|J^4?3%wT9Gs9-c!i4egtn@t zw#%b44&wwG!Wz>v=I9(XPV^|0wkjzmXYx7MB_zApoO{D&K{3|7 zvb(g#S7z;EXq?N6vz{`wA3}rkpq?}_RSm?=p#qP+%6Q|;t2=f#qJ|1`iI`_Y+f$=| zTQAe-4hGY#4vO=i5XPpOG{GWDv#Rf#14g}G+j$g7KbM@ubR)41V9TRWkdem}oj{Sp zFs6R7h0vHBl#w2^k%n>NM9cwfA>l*EoE~=3bE>2+R=~n_$oxe0X{`22<Dp(?=m)BSXl-mqMNR6oEk)x3OJwNNxIYwErq8CTs9TPRBO;OY%a@(eKXNZ zTK(A3EN`_&*|1vQ_N2CPmW|_BCpH4~rCXSSbg>}V;xamlW*K18T9=Q814C>#0Q`!D znL@K_yW>*OVHBkfLc%^h3pmi&nfTD$i^^msN(Hdk=5US~^<&l7vK&vYR}pHZHF&fR zT$8s}XSZj>zGk38Wt#=ld5cDOhj%WR%%HhfAvB*J?F=Nz(o7fGSA`&kVazdp%h0GG z$wEz4L!?t#d%B7Y_H76)I8eq;uHlWY@z`|ZV|r0mp##uy}e|z=nFK=$%ySe#n?^Vp^7IH(ZEy{-L+iZ4^#(FEWZ{qT0 zfNK!h;d6Ut z#&i37w$nxXe7Ae|>gwO!-u^Bd`reLkdv4y{H)}T{?0It6p+u2K$fLczQ z7kr}!y;U-ui(zCD3rSaOV`h%1u46p2kPT?1Q{|F8aZy}_mLG6m4jYfI;gE1*0D&nN zB*tuivQNf5s?FM)2;USexn>E=rE9?rp|Ujoy=h@KrRbYQ(WkHE$-r|v=;OoB#sk9{ z<-ueZXd6Rc`_$JIb>h&Y)`mQMdx@he1kdD02%GeN{^%aM^4*5rBF@(5*^ zQa2vJ_BDg)a02GOK(oeU0LH#rF{qAnWJS-`$c~Cz=bXGO9AON3Ho5B9W10&{TZ9cX z%>oEsR(5ENEz|YEGC`uSGjuKh0OAC>h=eGVDiHdfx9Y4@RM;098OU0N1hJ}~A)ZqS z_`<*!$;$%!)-SB%>~fK7MA$S74+(orN?Xx73+%Cm33R!ZjmB?4&t5%drdWw<7%#$t zbWC<@ft=#=q7r4Oi6*`8g2T2YPCDH$Q?Kx>LkgU)&oV#Ov3rRSj@ zLFt`aJR%sa!0U)qSXV*Lr5Wrro{ z!Oh+@XWcV1Oq>Bg;D#h-5FiKWYBoYGJV%jQkn6%T{Gbx{KQ26u=iFE)~(U7|p-IvCR=% ze_@)UbrKb6vQ4bfptX)oe+*iyBh%y34D%Kz5tsr?t_D!-SfsOFP+b|sji{D1g{6U* zIky6hoA>Vs?mc0x(^)%eRVmocp_iUs|LL*zZFtch>DRl&7A`*%s6lltkzJRLLBnDV z5!fE-+3et^VEa6Rl^j6*5j*l&r2QOcKaCLQexqsU6 zuSw@bHsG{h-*CZX}?r_hG%^oZFY6eX-kpd^r5<>hR;M>pXM1v-{W$>$@dSd$!2h zTWgo#W*4#<*uH^e|FOLXXlV9IEp9R}m{HiQqCCjKhAkid0gMG{)Jj9LP$gHIx;JAG z)mNu4VSQ87jERq8_0lQyjiQCxV(n$6$yzo;JpLQfsLUcT2VN>D_=WY2H=2dktXxgVUYDL(*mM%}r5WEc|ObIkeAz_8V>q40Njt8wXq-JDIqE>AP z+v_3N7-lZ^=~xb={u|;#RzbsJ`O#`biqNISL#5d&_UJJJ9I#bkRZqvJs#u2dPNeq8 zJ^6yh+Wo1RBj>;kkGsq!s+?nH)qrRSb&Wv3roJWuZX{&P$XyZyIvcoUZCksNkiK@7 zyEU!ls)KkL^cQM4U7}+%xUMK@+SEXD$To3|6SN?Mufa1*oe7Fq1o)B|xy26&QXYQH zDnfYx&0QiWu~gB^yh-WIJz1siOM-|6&QNwmk5jd*R?k|!?Hvvrv&5E#fo~&_Ts^|$ zWj)K(i)SA=6!k00uX8EP}9|>sbc~V@91`-p- zqQPm;QKekSYjq44Rwm|yeLH|4B1FogB-0Oo0?g!cm=ec8=posjHKpsM+DfGAAK+#- zmL%465mwq?XwB*w*&rSH4XrK9cCM7;7LZeUIsb#e%%0S`1|h>#(?fukh{;J1v2$rD z4G^Ez(xJIb@wsg97(Aw>N5Nu`v9UfTBO@BMwE*oJGc*Fa5o)rg94v64gh|fUYAp8l zTQeKn!?op}zJ(+A4D^Xsdo_T|n{FsKe+=a*LZ~)J`?~58*Y%NrSSC_0z@CT#N^YVY zrBlkl@>P#{5cIB~bdbVn0w4T|k}3;=n^0L0Fl(uGjnYKaW4T<8D3oDg)6~V8J%{J) zWWozQehWEH&BcrEi86^i;z!bm3IO&P%HW6( zlNf{rAg~=^@&ts!0F%}TFarZq1epex7RQ_xwH`UUnh^SyUo|GO8r?_XVivEO$WtPNSLL$`BY72UM# zvsl=KNHHAmKJocd(M89`>Lx?)W}WZ|blrPmXuj~90|P>V84Qyf3;BX<#W3?kTS3zk zLlC4g4MRqg3S*Ly#-jxvcywwRvF@~7&-0>)s^!FN5g5q0-T{h><^J;W&F=DNhr`bg z{0DBK*k4?{e0=P8(C79a*<9meyT7>j$@TT0`xl>om%k_T>Tj-ZK0X{c*PH^*z!4uS z=cXcOc6}w}LEErBnRbeeV@p-2kIjPCEu|xeTZkUd%0MHi;_N{T2&lXuFv;M?LkHg{ zvM-vAWzvZ8GYXalA@D#Ro8mZ48kuZ$>S+@NaaBgiS2;F55XF$}50@98@Ae;D9e%#& zZ*g2bUtB%&JN^rKZvV8~7nhfx?)JHG{rKkkM>l!P;GcTrOk-1E6q$IO$j*eCc-3eqanCp4Au1b zT{!@w$liE~z{L_7R(xaN?O&1{jc`CBkj}Lze4G(uY}5)F59MH}QS;_E$M`$fVy2wUwyK$xsVje83m4O$mxm}eKPHZ|POtLdpEs2QAW(t~FA?nOirgEpbU5+)2&H5RO?i7fs zi7?UAFyhiOpP@Fkij`^cZ?B=W@-(7A+Js^%TQ`2hl^1J-sOnw98F9^5yS3&Rb2wz+ zL@?%v&v{98Vi#4u_}h;sY)dCfJ3RI3?>Jxt<|W&1(a+ zWI=?%%c#){R0g$~14Ftn7Sri8Af2&!^f@tOfJ}*+N7va-br2coXy9k;qpjmyvbEDmE&NJ7;>B>R_wnXiY5keo0QA37F>2bQSoTiFiVc0)!?y#X%*>v zQWDcxyoDjlsJ|}lSa>#_;<5~^j%BM!+j!fHw=wjB5$TlSSUkR^rLhQ+J5z)W$iZ4-#|o0$K=T=AVNvw_2yUi zK*hqN*#IHuQJ6-}nDDR*N_@&Js*0sbOylBPH2%1^i!m9cl1;M%qHgj3v@* z#fl<0@YYG29W@fm);kz(`nB^lKLsARs+Cv9UD+j z02c0&V2CM(M!QnrSyzV!;N~(`zP0Eu0~ozv>p_9X^F=T=+J+f|CLCdTWFA&Df(nzh z0H1BTr(uU|j*FYe$M3v(^UuC`^Q-sw?>s!*rlNEEr{6x=@BjAt`p<7~fB*G6|I1h2 z`Qw+b-oLtf%zb44Pm7-WHFQsP=P2dj_I4|wxp}*31ZpxSHpXs0B(WmgpajBISa&EK z+91Rg0fKaCC^b$O8ne777jj#G?@BBuA0t_j8d(wVFdDqO-l#Q7TpYQkY-SFReJ?tSqJU_pFdOZJ?o4;3^RbB0a!{N_fy!ighm*0E+ z`g^b6`K#NTEOPAbxi-#Hh8%gWv$5QszHz7PcEp(e__?U3wL6uMT&SFR%{i5bMh{JM z4)>UVy7N}jdDk_z7<-x$ozXJ}3DzJfP&98L3PKjX1IF5xyB5{$Xg7Le=70rHTMVZQ zlFP`4S64r|y88Ly5a8|e)7A4ce`@CZ-tnbqZ}h4~z5;_LeQjVz`G-7?mRD2#6f`A9KM74ER~TmgD@^N27R2q>tyV{g9FeGW z{)HWlmEkePG)^djgNtNoLMku_p{YBZlw(XJ(F^8SpIKT+%!598rc1+!Qa^n=En+0m zS3{p3dMh&|s&CfOT3eoM&PBHxg#B=k*0ce*bcNv)j&tJt(kBY*W6TM<<=GlmqV+YsujB2uln?O**3j)u8Mc4yozz~y`an)4W(F3jt z>XD9T78>F3M3GuD9?nAt9Cp-$a%|cr&M$-XOz%b8I(^(AC=*!zCPDrFDKaY|2 z1XIOn%vEb9H30V5b~@UW#PkJ)Nmde7vhv=2OohYEI-r0sg2JjZ%ArV9Ewu@1VW^#j z#mOWb7=jp6;^kpP#XR)5%tWBp*sz*bWgc2L5)TGC3W=55t3t6y$kJ3~&9_2$dXfvD zR-hyPO<4{~2}@dk?=}QY7BFk9pm(gsS^sQVgkEKWYd8fQaj|9SGl!>X&8X)~7^Deb3OMUhVgaO^=rg8+# z2$V2;wVCY{(*UevDOMYuKkX9`A0qYRYv9PnEjAJLZu&xJbiNzSX|o`qU9SajZ!R-1 z%~2wy*M-@kKmctKhpAzMHyWEK52M-XjTRgANUzRgYQ)>xR5H}UP)=|VPpRZqsT@T$ z$5GB&L>obl)$~XAFB?w$%l8`QhR4m$&?N zfx|x%uwrQ{;ET09Jt+8?X!RLm)eB$(wiYcbkWWS4P}2r{ zM7OrGR#xc3q=)|U^Zovje>*b&d?YJ-^>_Xx^tt^9+h@Do-(Fq)>5CVC^zzmBU%mKu zw=aHjeO=c&4LJ|#{31a!B5UsH-nI)1Zv=*cxuqv)yu!5Llp|`a%Mp}eeLpAzzV-c3 z-H9|B1Mx<31XI1{v3KvlgcThYhOLB=+ZtAlh$5=B48l*^KqvjuS02T!s-+HT8E4Gr zsn7W@HZS{M6sGOn)#qRE|EJ#WcDuaRes(zg@aFb!Zmz$$Jb2D&>-?{MhLk?dD|WYP z?{eIs8Syn%pCsbrTCW_8?l%vd;!qcriI?H6{E_suHk8!S*HcN7MWO1F%!&;A# zA~JWLeTI8+(SMSHtArIX#qP%(l;mUH)55@<5dBtXlmTRbg%8;@E{54F6xEKnSkeTv z!ttF}j5Ga7g~Yfi*f_n6-PuYaS#R>t0kiguI#@^1Ia&?466h}Pbm;vkfC@$tP#(sy z^NXvaR8x^{UKEv%e!>|wwwgm9x#td*nQp&37+*|B-P$ok%;*)dp86SzvmKvm&uvAf zw>oI5ADct)*t)hBpm~f&ldCXfS5bq;kc)EE*u-5)aExgo9)b!RI#I~xP=%z2m25kKy z^LlM22bk<^|MLKbp(em5?X5N*R^;k$_yRhc8Mp~;T&hO18X7ZwL15hbn(Hbbjt=dI zPe?mfLHJa)C`FdSDx9b31sJ!gEmjBuUwktcGaxqE315NH5M=#G4MWnzl(o>US21Jr znxvE9Y;h3{rJ$@&t~RN*oRS+ss#*cW9`j+UDx}hi_`^K$=x(ZnM)%c%WdKCjri%o! zvwp+tPf_g{C|)lnEOV!iqdvsXFVELCD}vZ9qsTpGm8>jEpQP(~Lr|cV>%S&s>ROTscv)R1nSCcn0Cc z;Ax7uBAAAm%xc(lFixC~bI}VGFKh`EXF?e^^>imCeaEz5OpmB4S5D0!3vJ(K{YS_& zg8VD#v2_mRiG|IlpR;I$a>O$~PaJpW;a+MD>Z*>e5Cx(cqE}OwW6A28I1)K%A6vRv zrQJo+jBgUja?l-DvT<8a0ii3=+1mHyQP-#rN1rUA$=zdrN(EIN<>Dy56B&7n9kt`w z(&~~bBKc2|K57h&$-Ge27l*x}u}|htFrVx1T@%`TgC?%YI6mXSAIWM`ZMQ3wyphYDpUdPX#tw z^$LBF@b;vhfz9kwpH&1(Ic>dyB&mTX$d-%=m&Fk)HI^Vwo^hS&@7lUh~ijHhPA5#&21o5|Fk%Dss-yoa{N?@Bx~s8R(3^x_I{K<@IlGu0OrJ z>@TR~<>STc*NsX$7WjSp8nrJj_|d}eudjb{clXn~yWFpMLhOCijhSohwe1C-J?Pd2 zdTgWl5T54(qn|(U@el?P6zP4A9Lz;ICS+d>CPNE2D+A=g=yQsrmZ3R1T`M0_yBguG zW;#sD9aTNkS3Qja;z?N>*=|*;W#~u9BUjCU27rx;dP{P7V`nXR?6i3@BGa%ad#xH6 zH3mZ*S_8o%OC|LgE`EldjbH?lhvbLGm?#}6T5N}wGrm)7aQn^h@OtaEdL|WM*ChSo z2<{+BG}Sh#E1a4Wqp~`OI_-FubGY)J#v584nyCmU6Vuilqp~#2nch6A{VJ(>3KnsO zE_J$D_lX5tX1So^XeBqt+v>YHP%}0^(7J z6(Pr#p~ZO8G8Ujg52h2qu)f9AdeDhNri>VjWAsWn_k#p}LQ9~fy@$LStS|_!8>HkF zf!z_e@UA9U$uUyoX?g}a1Y;y#W5yrBvLbeGjepO!@=$lieqM$^fYghK#72&X$C0F6 zF$fc}ejkg}G}BOSjk#}7WUIp}Y?O?hLnQ>tk^@WnhG;9I4SQ2hx_T-I_34091Rkt? zU?Y0Xb0sj;Qi*X+jz*HVtOVg1t|6Y1h1S>|h-I_nbj+IXJxRRX+0YDE-~1+j16nF? zEi`M$5St6p6^VJ?bGnSipm-(9riGZkK%ya+hyW)i5}^S5F$3X&6iWoAB{zZuFv;V^ zH5eCgMEYH*_B!zZt+}9T#o9VQs;wS50Hz=?Y8k0${?K7ey3$RyHkO`R5~@|Zcm$1* zIxn9dM;Rv?hG{YT?!)DQHtg z;aH0VVlzqt15+lyS#p$CH7Hdm%;a^W8aNLu`%YU>31#(RmbO za=He`U|3o7)ixHZqS(Pt1brOCrKgthiPTJ=p(S;SR73US9rWaxHhB-5GdnNC7C)Pv zWr{gUF{(7inMMw%h+r}JIfF~)={==lk@GH7mrp23ph_5W(=-vh6O9CrW$}5}BCsh4 z9{_Wzo8&nN60-oR^{l^Q8*Z|JoqMn{rV1wLU-I_B-++Gl4=?=<=vS|vKRw+Z_ni0b zYtl~F|Jk$OUtfQ8fBzkS_xXQ3zWDk5?N{udK-^Yo`=*F)cjoLFb_5nqkhQR_k-A+_ z*^mOn#fZB;BB#&mVETc~)9!G-G8xJG5$0ls56Bhq)X)b|rwa$Uv zx+tpc%u7qSb-dNYEQG#E>E>xO0j;++#`xyq_2skAuCG76y!ztu@~g{>yQimr^ZNB) zy?!nFef$4zAK%<$OaHg$&%gWl{G*4v-`(Ew?>OcP(Rr@yBHD*h&Z1)6 z7EwDSBLh+`*OC=podIKp{ULE^c?H$e_^boJ?FFwQ*@9z9h2y-pxO{qJ;alSN;HN);i6}Jew0jjgLG{_Kv zQ1)7PJ`Ufej-5!h+ys$9#Ygv|yBmDdb5yNhZ?OdGrb81l#__CHjO|SHWs@vE4Qx$L z>lDYF#=9>qgP_m|%xnD9>SZ{d%0lLgi}u)NIz!myn+}Kcj`VA$ta|8r)x=P0IfoDv z<4K(mW2ZE1v0}t0DE-+@2zTTLx2bY$(%#lxT%!SS}1ttOm<=ES-{R5Bb%CMp|U6!?^vRvD;QCy&?OX**5`+LslP0q={C&k zxk$U?mL)PRhI%_^o6d8wVTcK6C0a>56k}o>q3-3n$MS_gWu65@396&O&?jKpB})P}3RQGA-%Yho8hNqThz7>4S3d}{i>#p#w5NGVw)*3SVK|#mOwM6b zH#HMkB|zy!asXg$T-U<%hNXoux<1n!9le*r1H3 zdxTY5u|_S<##s-ugcv(tZB2~Rdh+1q7>(XXs@^A3hMK6oKCNi9Ebpr8U`rq-;GgyqQmE%1YBB zWjcJ(IZ-@o`unodYazg~7x1`I)4a~?9Ak1|?^TP?`D#2Si6DPQF*Re1G?Sj3#t%Rz z(wRqEsdqflP1i=jEZ9=$n`zXdBWIWF0B}dpT^xg+$j(|I$!Qsq!*7JQz`ieZ!V5X@ z?V%AL@DuUTn}F6^C;|gG6hsw?p*yY!z3~y|q68F{R$?aq{^3B&VUSCN;jBVg2(KBG zq~*cq$v^?d8Rd$vUW`FXnrh=1Z*>%UjaHclJFiesxZYem@FzH4{>PVJ{JXEddVKx* z{TI+*m*z!1wU_>Zf)5@ZzWw6)e}DeMKTvS}=Ghfjdw0~)?KRfmDO;Z=?hQ5>QK&%_ zG|tgDp?d>O5H>_sY+4m8wE{p8|7DFJJT9;bgjf zdb+9GHWBaJ*P;2pr_OIc|LE@S4DL>NrutL^>UcSoEm|T#2}!ug>z6sCt(*6EvGjcOtvCc zYeO50!_p{=KFJDrdDc}t-K;c3O);lC!s&e|3leig@FdN^jnXEJrHhfm%Hropfm1mBWKgV7Ll=A;@>tH? zkG;|@y_=x$=9}m;4+yP<_%QJ}t32v-8NBHFY$P#=81WeDA$H4SB6PtL;NF8jUVTj9;&OiuLY88ww4lz{< zJOXV;KpF%r9V@`ZT@f8QCn4~e^?I7YCW9Mh^cAqoSBX`J2NNpGjzzzxNsxP`EEEGS z?M4qHE_EOf^(u%@O}dK(WlVi|=Ng)aMPfu;1WMB!jYNa5SO9Mavm+aUh2OQ}aRrgqq|V2p6Gfgf4E0i`KcCmV5SppRPCXzQ&T)w6bc`MZBJTGDu?$sf13 zWDTejaBhp-*T3ybTqJgGqgumEUhrcI=dmqO4rL|fGg7_o_*`e z3zHkKpjx2=kA=BAYA5voT;b!nTQ4NVXlX<jzu!2#vR^rMQcOSQa1JC#+LT+v)rZ2k|3dMjm8`SV-l<#m#0^=UPl0 zfjXnr*Rcbf#EvO?9hjojQ&ksr)z*5(C!g?d^A-^VRb|zIgGy$H$LvZ=d>ipR)yeyPC~G*QWte_vCKA?u&}<9Utx*w@>i} z_^dCqJFJLx8`3A&?5Gl8Hn48@a&a;A`C)P8o46U2T2*bDx5KX-E^Z*Aa?Uj76Q-%b zwJ{&RG{e~AXk&GuPC+V1<3%#V$-+bqF`>QbPlk&(FE6jYxZwZz`qLG^4gLJ}>;Ltu zuY56j-~J!k>u1kCczF0fzwzR~J%91t=Z`mQ6M@X{gRK2ADV{Nwt$?HR-VVu zly{7@BZP5GIp-N~8XgU;Odv>JL{} zUtM0lBEv;qO8(8;zu;e{_wC(pxq@F@@Qdv~zPta?{q5(M{;i-~ztf(~$(=Kjmvx8P z4hZh68oIr5bl2hG3p?2s(oWSqhOi!lSR}3?ZhGvDd*q}nM}4uW)ierXGFPtN1^{bK zGGJOa9%o|ur7Gq`X29Dwbgk_x*5B3eBvk|+GWp7(M-eARD(g_W48nFkwKUeJITiO# zB@R?xlbnZiv8zm|xIF60m`L(f?=2E*qI3hwXq6BdOcvlZn^T#wtzZN@F*O^kF(cnH zAks0+9ao4#y4d=euC7AU5IEUte(Pq;tVsq+<9?W-3Xc(#$iOj$#z7~IPLT#cK3h^l zL@+d9156`$`dHzJ2&Xy#lE`FG9>EZb4YOVu4FZA#SOJ1G5f&jsrhjC)%nO|#D0xU= zViOoWVeQf1vSnWIEI=1x5osinn)bF#Fv7+&wd1{4oCVtB6_--;@@?@jNoQ4Vjv(oL zI?1hDZzDRp2?h z*#JZOEnNxO0lKOm1I++Oh<#=sZm6@z=MNi{R=?@=VYda5BrTb-u}*MjfHPj^7n4h4 z&0Zenq$ZL__eoGO8;$1i79u)v#+H^|MI%=O5FVdwFxD-;;mT2R)bc{yk;WAe7`jP^GdYevv1DYDf2*%R|u7RP`a}Jc=W#(SSz9z-=7AC%W zIGQ@$EK#+{p!sPSE+}ze7tlb%FmGG~SCt!^AWPy=u40JQ79#E}=;_R|@Qmb=3?oeJ z=<3pN9?fdsJbUt~5ueiz>;!^~%Y_AMgdD8SG^WpQftF2FPE$vMlA-t-5#<7h!F@uo zRywy5i=iEx5L8KArdoOhaYY;umNL|PbVpeNC&ufr_*m-L0-{FLWuq8zHLNx22^PXh zpsfX^brKtMCbW&y%&QM1!)TI$n4u1I$0OPr>aL^nKoOF8yS&0$Z*jx3MCq4OLZ%`- z=2MDJF!BHiu|ORMk0B`0*0^q|ym|uUWCq0m+A6rbly$nUIu?T_fZygH6)WktWeM;7uGmnOsp$)`DiXf`WSpu#&GRtFub2q_>zJRe|L~*Tz zkmJoIwbK-eewJ9(@J~VIBr-n60paObKdhgMd!w z0UF)n<{rdP1CNmQg)tFAjGhu&AZGLEjA6{a6R*1|&ss}(^X1j`=T{eBTwXn0JbQe4 z`o_~!o_ANv`o6VgJ-@uT_{H7b|9JlV@1MW;{=>sBZtp(5zG0cpS4!E1MVNM68TZ7GGH~D-<+6c41^V(@rwEx$PsuU@7&|B<+@t zJVvXm1Jlsyg{I@!c9jm;TLMqbS&JBP=q$Yszkcv#srQCK^Zab2Yh&g3Xof}6J*$LF z-+z}W1TrF6D#ATCO2E)+t=n8F(Jb{~-YMlwB$ch4Q-Dq-DRdpWP`!eQ8Z2S)5uzx|bbYx`aEs8q`v*3p zWp-(VjP3k#q>~>EW`$8|WuQ~?gcPZ3J_h-kuwiHK`rFO7G;K)LD)lj;ZJC2}AIo?L zmWS!G4&u&=2fR@@v7ojfTT~n_A4^s1sZB7a?k?f!BrVz38MxY#``rE^AA&C6Wpd$Ciqv z^*5lgbrI@}(x0_-LG@T_L9RfQqN&N}Q?7Gtl9fpYfa+%40YMCnr56NcPG|kG&8}(oSg4zjC~>irB{5oL4LMk`+NZdI)pn{_ z`xwE(3;TFUeVU+O(XA)rRMe_#vN-|9_$$Hz6sn@V92sRC7nWX~2(fyvpX3A;h^Ag& zOVmnapAd09MLjSD(u4^B=yNnG2o_ccDpE?}8L*8x5L3e1Br5^3Ju}1aY=-W{f_21f z7XRgu`V=)=m`gAvOGXEmKPpQDNmy64&0M-mCCwJYG>f#X0y-lD;F*cVK5IcdGp5MW zXojcrUjFWLWo$;&;)+Hul>B{Y1b8BxXbhT0+IU<( zP{Y&5S=E7daq;--=|8@F`JY~X{_nox3+U@{yl?Ml zn@hUw<;BI%@9uu^`1q|CFTVBs`HvqSe|LTT#2d|^+};t`@B|Ud7V2hj=dhEd>|QN( z#Us;U7uAh6!SFL+DJ;i4=Z}4kyy95t?A!FtlW_t~V_;6U{7H8btq!$^%`~H~4Z#Xo zl&VGLNiKeVL*&4ZmfOY16R2`53TA7Z=5n>lHr~KFnY7f;BkoeXy14xEg8uch%P%ka zTO5y1Pxszh{?)d#m8JdR>gxLs582ZH_~QBZA0Ixxz5R^;OY>i#&Kl;r&UOLs`WsSL zJqJm9hHgH!dg)`~0_s7JKdgS^eHJ3)Nf9Ixccm1H+n9kk#Gra`o`C})n8T>lZ)Vi{{#&^%jG z9dCu3QAnhz2DQxXZ9IzsO%&)b@y?cs!b4Be`Ih7$LY6B-ixS3I&FLh;<;9yPsZ3kQ z(kOE1z}Vnuuo=-3mYe|(BiL5G-ZYI0P%ee7+)Tv#DHgi+f$h%W^w_a&N@H^xFF?*D<&gvL>eIEcm2h@fMbg;%u4hZk5iXUn~$h4bZGm2OcmfFfa#PCejWm zYtYQG;bnWU6$nbFXq+Kfp*SWqCovi7KyY05^k6o5PSkydPISL80a1+ZH90h|zK0znVw&WRTcC7f+7w&X;W9n(yBhVMGrD39Lg116jLFoYT9B?0#Jfu{${yg|IdKPKwdNN#n z^P7LGQB)iL%7a%WhnJLsg|}eG;hf@AN1q}%8&ezq-&OjD@YGksp)F>b?>6ZiIM^Lp3%+mQ#bbz}6<89^gLv#nk7xANIq zh(9n#3IKiVN59?d>Lspw+EGp(gQMt-!;-EG;wt5wa1rZBZ&ddVh8cbO6ch*}`7m;g z;E{y^Ug>M05CKH5B-X#FS5kLkDMwpa`Is5>eVO#0h6LFz^z49q?vx09oa%X&Fq}i)=%fT%lZqX4l54(wUXj7^iO` zS)YMmoq1+-s&;rrXs9dZ2&8VjP!&hE0;8yQ4+Rm3NxFe3py4Qy8jC@Pp*G^i4N)SG z4v4&&kkFH{dZ;wCx>YJ^rBW^j0dJl?e|qyDKmY7MzkK=EUww7==FR&rpx^P<%XqZk z+}?io@$tXEc=7G$&wu#v@Y~y)CpQgOpZ$8l(?TtezKa07KtsQAqx7ARTfyCi>EPDG zF58d3fz(=z8X>2>tb78;oCEiuMa%}qXe66aZTpP4T?!TiR+YxaOyUcU$ z;@`b~^}oD&1?XREbA?Xrr+0V%^&8*#zh1of!TrN8Z*M=p;;)&XP}vgmR%$PJFBVt9 z_e<+Izg{t8E!zQ&ZVfQmDi{A3sbd4|?SrDGx6w2>GTPu*J$i=~`SSB;K0!FkR7)6I1* z)>qH?$4Xbvo_*u>>u>D;NtpIOQ@(HSTFWcQSN;a{M|XE0-rs$8b)Cz*JAIp5l5Ned z>P}!gxy@_g?N*Gu(17x3!R~eFCNf5bg0WOK2E|QHgs@8DG#Qc(;Jn}Kvp_oy42B4N zTnD&T2kdNK?Hk!ABW*v8V7HLsqzs>MJ#}V0Tjj$B`(T3QMi#ctg;_`9#R&2# zF^4>r(@`!%&I48k3R@b?Dj-qUgat4TM95D@C&_#eF>Prgu&Nl8lTMt908OaK3Wt+! zYJ;X`M#SOpwzr<8&c)5c)EsIBlr-nDUWgzkwG>9~jIe=i2?fx&P*I5FpL@jEC`^pt z{dF`gC$CPWI9b08bQ`uvTyU@gJn$1`q%y|QFd#rxD=Gp7SLz^{^f}=R8w1pEAg3ma6rOoKne} zm-tvT7iBciWu-!bW%lUWHiP0R9%Lkz+8~ytgCd?d)7ko0@A?KRgp@$ zKddlT&V1xRAC?HQDzHuwQ4y=Q^4hUPG^Nu#))Ns}#o_^%VkANpb9m`AEC-~OIHzIA z<20{r(KKmIcf5lUjRkgeK?}=q_04bo`|(9Z^3>$aoZvABN?iiSv6dXXG_y>k)-m2p zv&$9;Q{Lb%K&fk(oQxE}piosZY3NZ4MS%xTq=+Ka;R8_9(Tv0<%bdiLk`G7)9lkce z8f!9Z6hThiJ&Y~Kj3IPNb%xt%9Ny5ftJN0OmL#lb|0 zBPfgOXN%FGwgn+CV-1>yng-XPILKf&LOKS(`x%H0qj%ip0e6{~vAvDcFi#(}Q_2a+ zVc!--y*009Yf>trp44@GnKX^!g4ZOf?|^0jCQdxE5%tbmZaWrElOBJKGzGL-MrMc~ zk==DL4Tpp=>j%A*ye)6;b>XgpN9!}uguSQcfAFk}aB?_;Jb6~@CM5`C_0W&JtT zIqA3xWuV273hdehn~F=NY)dp5jdGrXj+>1 z9$L3`5*Q|lL$_ko0`+DF*!;)BfE;;#6aj`(l`#0!Q-x<%Y*TN4Xr?VxHPB*v#V5{} z{{UF#srO7Qs1gdDvZj@Rr8M=>0{7;Rg*a?<4^L13`SZ{J z)90W6yH~Gn-@Nh9J-%;WgZAp#vwyz6{_y_c+s~hW=kf6;_xHcP<3DH1Zt)W}8!kIw zc7Z3Gs?UYk72HL$8P31AbNj{DU4@rhY2@B@1K69F(Bj)>zdFh^16W*O-tyC*9^!d4 zE{25=r*$y0LDGj@j05OUM|)me=GN2(xW;P5C1HR2soeFnn1a$;(#+Yu&S@=qo3LR= zbba#RJLrpxFE9D8^B$j`?w+3V1ibDG;J@Pb`Q_y=Zf>%r|KY`p@83Uue0%fR_52mu zzSUtF=UUrb*a~*|Y&t|1&1EUHTeR2Ft&M_du6wUMQN8n`YRh(U06)OCj7GU9QN_(B z=fRGoHKh@X%YfMOj3B<&DN2*BB5@38jV2EoobZN;EWn=5>RgG{;Q1l?d#C^0{Y_O4_DV8-Q9oi@bKA{Pr6=nuWz3h zUiWzw!H!9{P`6_Dh-I08pS5P>H?39KMK3{!uz^62x=)UG0?Df$Meo^48v$FJ2(>Z_qo(wJlBaJL&VwMKim%gD0-pe@(@^a-`owG3n zp<~vH8G~oJ0ON+U52+casuHRB=mpeqvMZ;dM%Ox?CM98Y+l(>n+K;U?m;IM$+8Q)J zjB!OhV#Z$Q3>}>&x*j4XDvprM73vNQ+E-Zpm=UuY=?;d2a4dW8nRFDwkeEJA!1QWF zhKXoYVkDt=^}Ua+&wwx-9LNTR1fwZ^jG8bI9MdrnH!yWNCxLdxj8; zq71SkVG>G*EP)MFgX_iAo^YiRdTo55O4blQIM@}oFuA5vI;f1vyK~tN$O^Z0KM8+Wj&e#M7jXi-!`?SRCrD zO|wMMVNjaHK1O4sqpdN0x48NY1|uJhR0q555E9H?DTDQtk&fRiBkQO%y%dtpzJY1I z|9dAmTBnd!&c62vu(rlsT)eux{NjQ?!SVVT-$Y-%dGl{xy*mCC0%eW(m)y>$`SRl8 z!-t1|eDVBS&!2zy@!=PDx1aU@K%=?*z?ErU1^*>v^dYIdD6W=asx!J_o3>ZAM3p+o z&`rSBnb^R%*v;o4im{hF@kYdl6;m+=uyK4CFe>WS0CNq|M21N^UG^?W3TwJ(Yj@oV&u-ISfiU*hcCPmc){R6L_EaE&^V(khP)I(4D+*nMIl@FXc-*_e4wj%p#j2}~V1 z=ycC30=ku+7Ob<;QveL(VeAb<3@5*dg9ihgXOVrWwv!_d>&aO^PP|0)DeqmW9X3)q z=XNTX2`;_vMVd$(;7J{ItPXgq+Umm6+sWwgJMHi;Db1leStAs>jq<7rN;m2u`0mD_ zU@yH6enMqn8jL-Urcz)4ybp-ja*>#%OMz#XTIrO7u5D* zD1%Fczs5MFLySeax@RnA)l^iaEtNVHCRiuJELk%0y|AI64or2YcG@Ai#%@-o&Xby<*?wA{p7>b-YaT!n`?waVD1T%;uFoGzi;{$?Prc=eo~KZZ3^TrQ^YYvTHJw$9YsTfM@(}OVo2J4<97PmjZwil$FBnm z(-r`i48?(*`ymeP3|$S7#IgL5*bc_fkAskwL?VL^3?e7CGeB(gDB{dwKruFkVWO?G zP}CVh4Iy!8ny~_9VbnE8*H{FNP*e`3am~P$id}BRoDfx=r-sUY`7>Dyt!(e|;^>-z zTgFcwEi>v2_?YW;=jJSw6dM&08dg*v=a^MK62@h=NC8d`+jwRp6_(6@5NYCjvf zIrDyQI3!jYl;PDfr&Q&=d=k1!R%#NBcD328GFh;JDb^4%oZ7q7lnshm{$^Obuz(Be zb>)s{Nu0vlszR`et!Bk>6gTFkQLdOgmREP@PDjMdo<1japlYJWIKH~;5ToBwDDC0( z>%aT_^Z)$$=YQ>QKy%UFw|BmM;veYz?(^sW`*wC{#;V7d1_r~g5D*NCXN&I^Knaq(g6WB#OltLL9aK!S=U1*^VFmhqTjSGsPN1c{FU zwRAw`U%P*K!Jp&!;_~AD&71$_t5@H6>VM?6$iMV-2UV9=l|{b^Y1@C{_OVl z(`)`d$skS(XUFK^&yr`yX;o0d+3S$z>U$*?%O>kZkC0)YoLqCxxn}!(;ZhQaG`kwr zz|wYOXx%-LQ%MW2OIk5nbT&~C?rZ|`i&1$%XI@yYeo zhkOD3@Y&TBSC);>X5*ssHmM?KuR0grH%D?XF#I{G8tAf zAvOkdJ$)W4V(Onz0O~6kd29{t91f#QnvP!Jnwx!K<5z2e5@qoHE`hF$U-|G{Kb1?w zppCFp!*IlZa05k~=%#Vaw|E>{X}$-P4G33f$hLArFb*9n?21up2xB{ZgRAB-XUiLQ zW|pB*IUE=xa0e_Vcq8qn!g(1)_Q*N?UU!@S2+4H<7SA}AI8!^YHWV&)SE+_WIs->^ z$q#y#0B67wQ^QuW@l?RTJDWaABC4I+a9~epPSen&<>i(y7qc8DK5HjcNp>Z`C!Q8~xVjA0 zXkA2&NZ?MN0bRTZ-64Lm?$vR5u{El3vIV&?ye|9ryb|fXM)-9|1YJ5W<|s^DKx_kr z!)^dtIJ7K`hxX9EdB#5n8Y0gA1U&|WV(5|Ry*Z#36FE~Z??KvjfFlH1hFOc87C2mU zYGP&8ZADo`YH_D`uP3l5T&1ClMv7UwnT5|1z!P7vfjo>8n8#cWB#9ERZbM2?YjZ^c z$WUZ`2=qR*^aMkWiURz6LL{GeW5O3x6didaec#Agrl!t7RYN^>42*X5H-GcZ>6`@mI8?Kw~z z0KCrxBxGQX;f`phwkqAV;c^92HAY zMuiUNogSH4m{EFT5prrQ)zRR8+dp%GqI2#-pW^Cko)PJJ#`D5Mtr)W?JR$e_(fdHm zUZgx`l^TjGp8eBfF)mzJ3kju8tzB7i#%OS1fPi54x)jsk###9hK3WGLqU}a(Jj&s4 z(3nnEj2+^zn5snkZdJz&?xwc4t_E!!VPFXw51s3q2Zrh(xw%bU)0AhT1>x4=n9k&7 zTg%BKwdr*udqk(ltz*0_nHan+MPPB#GI)&|a~!Bm$mCQTFkSIfGY&^Ai`&u0-ZLPU zR^3Pf+D#LtJxbqi@E98Ao#y;iBtmY0SJT;ON-G8wd+dlQVY*|I4j5@s3kES5%nSm( zuYk$n_S7~V0HcXh%^88ou$&r1Svyda9mlC%70xMv7#B-_L8FbkT0?gkBIDAY;rZ*= zfA{k9zx(XvU%!5J{r(?Hzw_oONMv}ctwEqaHu_Wvce7NOoX+sQyzkV& zJUslv^B3QJeEj+S?VqmsyU#BEUdXgvOIsJetGc$f=zD2un9HW>t}|@BAggr00nrg$ z_pb({dZ||{Y9OOV^qJEK40(uBn+>@}BjDY~Qs)3KWwe-8PA356H8F@&>e&Qlpe?hq zV&9P=H}R%)r*8%gR?{9q%#kilTi;~#%*OumlK*D&`pugculWKx8ym;__71mPz}FWS zpIlvkaDV^7!~LgMx$XPlQui9WTXsk84YB4}y6YplSwLb`iz)~*VCvm5+vARXxMn{M zCJnN>)Q_St&iyo|zKF++s)41dmoOK{Q=!6@3446CjEqUUQJ@MrFP$eQA< zb${VD!Z;8;>E${WQ$J514e7LNB*5SL71yhSZ%XbOZDI|KAXFVjtGffX^^KfvD~RB4 zIF8wV-ZGr&NDNS-C~7RC288sdMV~-kOx1T?oy3%^IQ=xKQx}Xn`tC#73doT+2}PWi z5CF*OUFF7T@?u5?$u+FgHw|HGphsWe$;V+nlXK*p*MA6n;HAUMvcyJ422Vrc6cvkp z!D9umQEh0EEqCmM)H5g{4Vlw)`e;!WI>?sgJIX=#F`z{=k4k@D_)J=(&O$wZsoGtAS>F-;x-4zAae;Y1#FG=iy# z-yau69Kh1dyhd~78-)cHFggWJaZZjTh+Q31QjIbXy%r_it}--^1QZMlCJ=cPLVOw& z9q|Yp7n0C)u{XFXo7i-E7*6J@{av1M6*SpZY1G$ZaO$apFQEVSn@(6vTf(V7blV+)vkXWk8-FLGe2?jCMR|p_Xyk6?6?|7{{w_ISx2&S~?TfZ_PAh znD`@jXLdxBYJ-gtEk{c9an%1-BM-MMrT(kQ1Cg0m&I;5_!O(>3OFjJbQfo`hWlY^S}H2^MCv5)y@0gfPS}IF6bYwu6}WQ`~8Q9|Les!zV+h8$G5ky zuln~Qay@;HvX63y(U8aPdu}>j^W7@DJutXOdKcnDA2GPJN0jZ}%2Bs{&wOI(!xXYkAPfN^Lwn3ka+OZ%@yj z{o?NKyAKcl{rU48AKl;mk>7pxWouR;7r{>kuGp+`SF;;uOSixzXvB8XS zrmQ095yVl_Sg>h^uJ1XmML$l2T1`JDTg$f;VCIvQ2j*h}xO;+0rhM9Je%X*Rf z)u&fCvAyuGoaZIxem}&#Z|`Qywahc^lWV?!{=xkNe*=dt%{6CNc4?J%KWJd`jsPGcWRwT?&2urd}gepHFIMHQ%^N<=rb$l9=W zN{D5%!j^c1>R>Q|Ryx@Z+7mf1%q0+uQMGpoBplUTRNtd&g%{x9(pjF1EjmK_!pBIA zzG~%;ReCKnh^s)DWAF!RlUI;hEMBB)gF$sphbG=7u#)bCHO^2e{tfAv&OikEHKuw198bj}0t*1dL7_PAXx!Ev^r0h*O! z0Bhltk|@(PtFu~tl$M&(u>=x@VR|^hoS~N0XVw-#h=ji6j7qI-1`<)z0AMj_Cj|iS zu^{6^O5gHiZXrMzaaoP4&_@T3w1{Mbp-qEMp*UMWof+I~Dt2U)@W^Zz$5a1a^ZM2c zp@M357bEZKlLzkES4~J=7Sc&-v`GL=a6b}=HRFUJF`^zH8A}7UNrbR;yg3;7IjHMQ zv+mT{`mv*0RKwN-=g2yg9PNBU>5RkGt33G?QZ1XoBtDO98A4uIIn(Rb_0v zP^}7siEtX~-*vAc_EWDvguYg(wMnK4qmGIYQJa&hDn>AHaiE6|P9r;-*_fclHsk%5Ro zja5o1v}4<+D`0z!3RnO%YJ78PqA+1H)6jhOh#|(5*y++YiOvp=#PA_-K7CCA^`{*L zO|#gRt+f%zgRD=y9Rq^?7R>As)F*dA?z{0?eh;58`dsID) z)F~_vX>-Oj+nESP+Qv5d7;9Cq?He#AF&tE_NZ=Zekcb|X$*RNl{{}0gK@y7qpc6ax|!-Ic(VN7mn|J4-8 z#mt6_QDKlgfC<>xHcYisKm>awGXl1znomw8)0D9%0StB12tRuoV2&w0!eOGI+NL{N ztsI0I;n*3ZBS9ozMx3v93de1S5v0Ah@LHQR_Af6lUyF_Y{ORf6yn1zC)W3rE$<@^d z_xIm^{``9n4?lXi|L2?AEQG7u^z;;v4YtsYtd-EC_e7p<3MA2kuYz{tJ`)oF?( zbD+okcCg*xl9ethe7$QUWZ-O;@e&F1U^HYDR<8oJEx-yQV|J)HrUS@G1!9Xiy-KDC z%Se%V20&t6gy?T!$in2P-ln9zjFAzdaxqTN$<0$eV-EizMUKz-3E7+br>DHW+&}R< z{x|<>Hul%F<$diR@|1jd`2NFvzktr=-5ubz%fTM1iR;V`QLo$0$WEiNbumk$clI6r zK5q?)Ps$PJGToN*nZ{r{R7}J6DZ}O1)$4kiIEZ*Xa!6aLv-F z65EmXXrrZkV|v@4H2f0D0Gb^K-^m_gq^?K;l+##Izvu16s4;M(=oJ(tC_<8FkJalbB#2(Rwk!|%lNJsX16O^KTQ6N12{cq?z55cm1jmxWwPq8%TF!ZM% zlh97{t7ai|{b$oA*NqH$YfH~NG~+Z(iG%G*VHAO@dlWbZk=AKlYvR&G^Xj1O3IM<_ zam{EtbcThLB}q5w;fg?(J#iXA3X;@N8|wLZGOrNTdsoCvs_B?{IEX+zW)YXd$L)_)$A(yKp zvp!^x1`|~1s0H*vT>`5Knr;eD)78U{Qgp0zAZgz7WAC+I;}m89T^(G5Y>Ihabtc`p z)doh7ty7HPCC-K*!_0t=9}gI79}hh`2W(rCCncGWI$U!fcpC&B6zIae3OO`yt9u`^ zHBArrb#;3SA&X8=A{J7?7#o7K#dfm7p{{>j!^|H*Z&dwllbn$Sw}LbZHgBio_DHCO z>@-;(#sc0}kRVOpwZs#3o`ovZM`;t-`5+>c9-pYOMbZQ@79F#I26SC%_`psvmO?TE zu^i#ZP8@)ycC8SWjcG%{#G~x+^3Ue`&XJ^)v`Ep%ub*}CIBipdh3Oq5KX3(#-3qlc zqgnQ~V6b>peUS<%8O0%WIx`vGC-wH|<~`whM*&~(ICPcXV)5gq165O(l8z`lSg0B+ zi1p9HiQ=Rmtf;pEc#1lq+j}xot*Sf!)fUVZX7b7&XmC>UNEs zpL?msStB)fh{>PR4u%J9`q;&^O+@ZE z5S5$-!@hxOhfGf$AV-Tc?kqP%-VJc+F)(-Q1(T!p)|tCgUtjXT?IIk>zPh~l;_~XN zi_7~rum9>bzcG6Km;R3uUOnSa%KX#q?YEvk|N9r;_|e0|Z*Q(y>{-EHAlBQo0n{#Ph71G4edSOFoUTlCyAnD04La7I5l*y;1bVcb?s}92TCY|BQ5Z061TA;OM(d2z^P8 z4T2MpV4#Yp4TFgfgGjBun1)&dNO--XnnW96v9_S+<*#ny$~DbNI|>G*AZSlpy&N+; zjV#;9<*|q}rkb{%g=ReEX@y$ko50H;%bv86>mK|m7m{fl#Q~ucYcE1A59O1Ow?;Dw3}dfy zNaM*lrdbVEw-)j$gYat%{FzxpBG0~0Baq9Rkly@xuQv^}HoS2_oAOwqJV@+(V-_ho zU5o;NW5GcPN}L339j^9Raf_!OC*;g59WEZAXEVZJUI5J@pN42yO4W7I$~6^8Jrz3Z zZ4@^ya+NV3*-9J&Ot6~*dQ=1G3_o}d6%U;)QIjAKgOYKo>NDSyg{Evobg&4nC-zw0H| zlu2dXTq7&yFsAWtQ&$IzEdf@LC^HG{EKJZq$#~SzXu~`S>+e-EyXFOu<%TA&=}U7RjY5R^ptkC-V{*=*FtJ>*Trc9viIGkqZWK~i9CGGxV#p=e zH`fh><%@Xp)CHTOs7f=d&ID`6=5z_mI$IKPO}ohe#6*5HmN^Dl7LTw>;_MfNm^a2~oNxN`tv1VQVTpYG4X^9Qzkq6RkT#@EPi_1hROaPw~#rM_aJVb-wwjfS^N zzIX9l22>-p$Z7oL6V^I8YK}j7P;pHKeISzHZ9;IC;ZEK9=y}!%lm->l#HABjaZ+Y% zxR#8C0!o8S5`{_${=NJAk8f{tEBmU8_rAT8EpG;L-{FhrTqQSwJCM%(5D|yF zS({Hxwkb|s8N@I)gxw>!2zxO^>5HaXd(I^ts||TMrx+VR_I+@Deo0El+c~{h?Z}1= z@G_?#d%E478e*>QK`tY@@}%s_+AeDpwoIavtJ{G!zaxX)GrB~i-R89F;F zM8c)KcFs=rh~XuvRNjch&~^bpl}VeXv=xdmOOH0owUL2TB#3>@X;#G}vYjegc@L86Nr!?ZjrmxHnzEyzQIXPF z>Y6KI(~KPPm<0VGar+ptA+8P}YM&XvGyq2(YqEzvrmEA768M&x1{{N7j6>&)Jn)bP zO9npCBIEI%elWJT83}_wOeA^?9VjIq#<=VV>}ext@%9~5W&--Xh&13YE(VytoPD12v+R2$@qWDD-p$VIC`Z zxW(~hM?1JmKz3`>Lfyv02382*2%F^bYR6(DT zbM=jmOa`JTkLhSfRufSODVnncZGJ_dg(@OiTY&jIz{MzV0!D)!TAp1Tv9`n_BBoR= zE+oO&DMGXpwqrET(uSRZGSa8>@6}BOU@Xr3jR^m&oVYfEgE5ORLx33xLVhU~0AQ-6 z5{$l;%mEdV0m(^Hv*gSy_scb0=gkdAJ7aGk%#P{$70txfDX^9pw55e@qX|Qrj}eVP z=bv~G=q1oW-}2I!f<;Y5A;FzTt`$CUa(VTPGU))zJf)>`CFF{yB@qTpmJ$NB93Uk~ zXE+!|91JFEpydn^-a>M3DTd%qypyJaoCPwUhDi`jpoDFff5?=_)y}n%>lU{>v(HQg44TEs zZPtj6%pfM0(t-xi`Q|>^og8tgwCFUzpwa7)E6o7JVOS(6W-xKx@KVpwZs0|Q0eKX@ zmjr5d1i6gdqG$~QQYJkSiKt$Lt#)ov1?L@%!cgPm;PTEh)J_6Zl*>0OxEibte`@FM z(jUUN5PI9NruS7qyKysL2IhddKmD@ukbH{RSo61L7JV2r7i*80Y`lvpI|Akf>=l^;0Yw|BOEa&`5S`}-d}K7RCY z_wmi$EB?9U*;VxLvN~1|;Ixt4xb71XyRE8sukdd$^Dz$3otTBIVHx z?yA|hnlxjL=wINPm}1r?EjJn_?n}nZNA#j7EY+}imc=*lD z&0p|Wb(vZw_`&`CcOD*oa)0;Ro72C+VCguRd|q<7Tv?>PLC?A|;%CS9LN=y5q;k9U zrthozfVv%Ehas&nEN${Pf26awy?jArH3A5p7+}b%EsJQh1dj_#bL$}7-H#R-112T5 z#2}k}>B+Upjm*h_3{9wB`5I4rpqt<5*tBzHd=q5V>SRW zRd-JvyTCnaXb7ZS58G+>AA1@%)=2unMzjpb9pV9q-v&ki^5*o}Mg@JzjK^50(TtL0 z0tJ{{d&{YFhS~TAapbNvvAkP0xPdihtYnT^k2O>xV$cEiT_m5-n11loO&mXGjX|VI zOalsKrrZP+$IS5q%L70y;Rmae1B@tP2A1dW7%p=^`-}oI%J>1RRIp)hV@`Up8Qlae zy1)$Vqy{yEE%o%m@}WUBy)zhW+L9zJ7;9w6DP+2_I*joErsq;JX@BvhMfz~*;70^# zP+B)+o2wi5d6w#Fi7wr!j-69@$u*;X+=YNZ(c{D+qPnUT$L@<1fn8Xe{_IRCr??o> zJ5g9CTq_8_6`&sMoS@TUXmAx5HrIvMRaUN6I(xpD4&L`aA%oxg)5u7M#WyVyolQgE zZBhq{M>MC9bC%&0gka%g?{XE4i}^=hV;l1n>qu#NZaxwn;HZ0`4Lh<_vKn0+ivDSr%L^58Z&oF~$m$P(dBjrH_wn zN@IkrO+J?aRx^{U3JKegkn!164a^9kqLs8jDI0FO*`!7BM|sHvF0C*c$fj^w$k4%? zAvnSrCNx}3u4zroWDzJ{JEnd;2e$l02=HOpu+5$@6D1)lFvJUGN8>Y$$3P6EHAt`S%v2N|X8e9_I9l>;Fyqf7=)s9PRr z8Jr-_v{D;TmloE)7fw2vIiS$yrQxlI(6X5jT*eIhmZkswBd(T7vka{LV8has145(I z#=x+?@v~Kuz26h0Ns`lnwVOO=^8|Bt^;tf#9CgQ4aFFvJK))p8HAbt>;$tNtv(sR9 zvb8!xeOfh9O_3WnTIU>f+#{})Th;}c!0bwR*J!x^}(a(8{hcx-NTnxUbea9^eXO=D&1$t74EL!N8%G0e_n2Q&(LS_;Gewr+uT41 zxXUuAbkQ^qI(>NTl^jK5%ZV>%_Or(lGxXUq8vC4%Cx|h6#7dl=rndn+2V$wVz6{)X zv_Vy4<~ytzcxu6pE{sgZZ^|@Hk@=m5Q7h4St`VP=9E&tL>0EH9(*h=!!8=SA8n*_F z-T;aC+3oE=K7WzppYHBH<>vx_neDU7%gpp2UOfN%7cYKxcgH`*==x=~5W7;W9JrGjSCraDMl7uS>AYo`$fS9i69srh>;pwn-Je5 zsK7g^)3`c(&Wc4V^OI3$hmwL(j`z!ps~1XzsD{@+=DykrrX{EzRU|DxGSCaq>phBx6*}IqqS`7|YBX!mik& zTC|}dAfTSucnw3Sn;TwT(cmIbD!N%#4cs_mY|)a=DtGn_xG)DkrXbO}>TTg6pbP-9 z?SjEZkhqNyS7R?Y%uE>*9cN1^187CL2uIXHq}8CN2_Qg~&X-C|ppcuYty~I?IJtn) z^UX;UN15M|Q*R7#Y#X+LM`QOeF2rhMZrx*Co;lFJiVLT1+mj2AnnztDpU6mg5;HH1XXs|TGJac2hF zVQK`&n8Xn?Ymh_}Hj`}%S~JqRq9fq|NM!BWSUzMAu3%gSp?3LdBLsJ!1R@IWyOq|0 z1K+{F_Q0abg_Z|iB+2G;$FdsTnI0Fm+Q7C$KYQC3wJEuGe^6h-wVlyz@B1-rUooM(1gxrhCc@ z1{UW$0dXIZG$(5s!)eyPrYJmW?+T3`6(!>l;?&HbAT(W0HC)<)W0hPYj+m(LtPp_E zG4B{;kSoaf`vT0X@t}jzY7!!*f}HxZygZHYXi5_i8!+I0tXsw645wOTgK_&tR|bu- zxy=b;^hD%vYCx(sfBgbGg?!AL;?qjfSVE#2xADY~HcY`Z&c%f1P?UfRD`f~^=(>|= zn%~|~M`QwwvSlC`WG$@(11AToGwD#RQ;ozOIWLcY$&!0{AbdSgTpAOBkDsu^lGYwq zx~KVv!0k`I2sK%NlR7vnYDv1tc?oHqMn$R;f*4>yP|=ab=2gMm4PxarqFJ#t3}kn*R!YDyL^KapI*l|q7T_EVZYor@!DYmn@tlFymr|3dcuV42 z)&@I}My8@>#u#PGHYN^3lW&66qVqC6VFJ@yFUQ7YL>wGB&CV50^H!P1RFQ!zPZI?X zLMVWPh`uFo32?^P_^#8$cje`&spvddouTH;fk})eEokmzqp@eK73wZegX+jsysx}( z?`Zqe<<-w_Z~w>R2%;84F-B+%U}f>(hCCSfO29aK z9Y-_Gj3RRJi2_};_APwwu1eRK1t%ga{<|E07{=hMr}-`w0}g5Q07{K4bHCs+LX zvy0Fbir8gjrPva4K|Nhj?p)n9s=X+A1UmQCvxcrQ=dtOt>V)p!fHJ0>F`n4~N5wc7 zHFf|Qj@^;AF1*onQB8V5uu-A~OkmvX_s%E{j4%n>35kU#3~?9zccq(XD48_%6+@)o z!xwoFyZ57^Y6szLtP>l2nxRDiubmbwHS=A~K#k^YKbfOVD$NYDXipbUzq-ErcJ5=3 zk3YM+`_ollrQWx9v9Wnh_pxr@{S^wWURy7?d(1roiXAa3o4~Iy7-dK^SOrvf3d%s_ z#4%2GV6$t)LOnUygcFmlrJ|r>m7xH=)S=R!&nMVNfAa?*gTt35Mwr>SXMe`X)5WHzq6R`}5kliVnn7qxC=?4aN zBa3fukU%^`GH7kWFs2DJKU@)Wst_0ig2sp`#B}L+Ur#vA5Th7bGvS0q3a~-Vq7Nu? zV=?Y|BCSL?E;7>#Tl7W`zu(?PjxlEbd<0@DYA!n_fpbVEi+Hfo)bE*wN-whrGjPT< zUbJSxGeKr3@04JhG)i<(7b|EZT`v=^?mnm-47@$G;b_IcZ5mBc9G z&dDp2Ri79#23Z6?!(bYSV405y3{Ko6^M}r`0AWa*IdFk899cvaQD{6FTpenV3Y5pO z7W%C~g6b-LVJ-239Ec;>WLVe_Ia+=6Lloh}%fYWh)KPNct!JYlGbRF^Z&8$j_geHy zc`Ng+{djW$Tn;PmZ$`Jy*~a=#VrN3x8j@7NI!S~eC8fiP$>J5xvJoUGJa`c^8i896 zfzFhfm)&5@XDs-l7*>!A(hdXUKGf306J=PCRXa!i6ze&AYQSl!i}3=GtfrZ5~gU$O479G%J>LH(o_yN28EGj zsl_n=$WX>y>rkq>aFfk{eSl7a#i(ld4p23$U8uy?`9#V*` zAEC+8N2{Z~6#ZHVk%IL!s3cC`Wd8X@slr3L*k}eZT`C)bqyBhno>uDC+1tO%atfEA zIWQ`ZiPrpH>#Y!*qeI+;)goGDB$5`}uYb{cU8u(B91BjvL zQvD%<&OvtrZ+W^7x4+&Mm!-As*rvV6T>>kqm=5+FETJ~eRseGsgG+!N7YAD)c;`_M zW?0)6QGZP>vU<)^l5@jq$OA*HNuUpVMyqh1)i`4#a#ZdOZEzs#rBhAUu%_kv_9G-sy?6#R z|G#B*G^2A(xCkvkhpF__^-0zU@f$uZax>R#XoZ<6G(=?pM|eQG=oMw1-9;d9)b#^( z<+{qu7;p{-kX=_x=hlIBXf01Juo;{uj`V00%8BlPmrHLDaRpaNR}f0Jb$@=BgWqv2 zQisW+zW*lR-E6NeE`ED`{o%v?NB0juySw}C-ObCZi`V?$gWU_g#%|tRSDqr>MLxN4 z_U8@7vC-U-@%gmb&D>o<6t*}goRNJ1+(0A0s>|0h;uDk66A+@TG?8gql@AB9(G_+1 z1d+%nlon=K&U&3JDMaCl!YmijdTpIjthGo!edBYM3sSAr!jOYAA~&E(od++CZTLVF zJ1ty3d5FYl{Vgo*6e^oGnng}4m+Q=W3kvwv<+ESiUVrfL@SVrUpWfZQ{LA`1=u9V* z`tHNy5APp-eSPz4?m(=b>pLq%*9+ZyY8r0r>AF`ydPQA7WUOyBo$2@o?5npcle?wH z?oTfK5F;0gO(Y0p-2n*XK^Q486SJ&ZWb30gn3TmfLZQr zLyL1cB~OQFE<(lX$X3lW+_0R-o1*r`kSIeZ8uI@687m}L9{J=k7qUfHY69BT%j?VB z(0+D%`{CW)2lo%ZxxRk?$DrTMcGW9u%bU(_Ar&RHxw5(EvK?j*xT{WZ>=_Qzu^Uz( zOQabBT(eu#7X%W(LJ~ZrXbc^j4;)TGtjM%FY2^5yDn6X5%`jZs9b1iPxqyXkpVDC^9 zWQ9`?c#d>94Az1ZfdN0rigF9(p_s>!y}ab6-*mv`njTl(n5H6P2+L_k(b47PPQ^aq zz<@1nq*xUgTLQnq0@+wvt<4&{=lo)C%{fJNp|pz8nxR!T7$*VRJa)z)gO3dqvidpu5O9I zA4}2F_r1`9+pS2YNErXbCPLp9MP;S?9OZ0Ug{#l&XfmFsZxp4a5S zH}X`K3)vT8s=<-gsI+A3FV9&_F4nHD7jK$#8#yp%lNzm-U=0B5U4((Rc3t#qC2fY< zou}z->XqnB;CQkyjW!mDIXc&XT`e)Kys{O1w=FV58%pTTXmZ zx;!U{@$EWrGDM-)#F@{q+G9;Gb;9wH8Nx{+(9R(7C0|#-rGr-xmwf`wL(}LQa3$(<3)mmV4d9ADYMIn#~gqPmoZUimuXPoI+#E+4- z>8OX9<$*Vo!yXo>RR)V`7}A-eW8KrD6PK$vR!8dbAqDfADbQfSM8zUndRl4&R~-?Y z*P)2L&RSxUKpK&Qm9fd9H|yOj%spL;S|-mA@GpTyH5ytvlP`&gR;*q zm}rPQ-H7pcE!K+$#!k2=Jpk;Q5aNbo6QpB31~i13J7T%WVqXwiI-uZ;#A*W9mk#x-v(_lq{1ja!6Jj5??Q-`(82;;;G~PmnY&9Q%n~;)U&yo)#zn z_6UW5c|zG%1i~mL9mkO75zRsVZW2uj(VN#HmhgA0#-yFP=sMJ?S?a0=^yULoWDlEx zGfWUFhl#5?f4qk8Q`*}p7T|gNA-LHH5gQ#CH)K^3l!pA|II=0EC;MsTi7L{zL zOTl`1F9YUp1y18_=cev7M!)#{^7@Al5C8QW-}v6+;~)RB{x`x;udcrT@bF*0@gft9 zJd5b$vra4n@`#K`UakP6A@*W+U%Jk4jHbuF0po0*s2p{lu}YhvSHU7Mf(XB%oZHVm zH5efdnAteZq;u+sTMByd)6f8kYm%7QAL)VVW;f0=umom~yiv8)P8Q38tlJn%IutRj z%vX&vDVKm_06LAQn9(efS}3Xh0QJl{BofI;%e$q#19-Z4b$Rj6*Vo^Fy#M>>&p)`o z{|o*LGhgqP?ci47()X%n1EA^ME4%xreY!^BO-$dbECQ?j#Nu}l4SPd$G?NNU;5lN( z5}6UV6g8#^&_ZX7jqeAJ=LD_KChM`Oc>>{&D{5XkK06oe*EgLVDq>{Nyf{Zk!<9J@ zQ66y3ZIu3srFCANlpzX^^Yq}2@8Rh!VDA>#nA#|?z1r1L#0Waje#YSCtY@2cp@bf6#xy{Xiv02{6o(I zm^_jcIdjZgp&pV^vVgR~!8K*=iNbd8#S5m($LcXmsmicAYalF6!_hg6L#wU+6(hwl zk+SFw0T|~(&W#DG@KF|k(gyw99IUH>)HsX+B_!VoCQGa^%A1K}wMNiE79vB|HP8aL z43LFTf{lV}?K0|a4e3m4$|gyJY&cq)rcoK2OiSCaEf1L(J>qm4Lj)EBl?;GsdP$mA z(^712Tz$&=o7EAA0G%h>VHS&N58i}KVbdtyR0(k+aqV8&q|lVAE%yafd{mO>0fGMD zgcKI8Nk4+@dh<|>Lf@PFUiOm;}zM zLpdp{pqyjjpu-ZW0v7UO>BlS(fCoqspp@%H^p|RDj6aDXN>^0FLsu>B@TB)^F^DK` zY;Ew2()$g(w4p-LLV>+XvAd?Jr?ZIST6qtPV{#M0N&t`oD-$-~za+17NF4qGoYYjMfCeY0~rv?Ax)c3V-Ut+O7fT?$5E@W(`$gfj8ud&f&2N{ z01+*fD0GoDYU0wW|~o@O9ZUt;VPlNapT{&`ko0kwl6Ik4+}WDS+!nF5W6qBwurrDk42pT#_19jFE){ zE8ZydW(9Qwv-pt;ZOP;TOl=d-=8~BD>e`NM)64;nU{==Y~z!z6Du>@0z;|v=u*Q4ReSJ03e*cI9Rv3Ce*ZrJ3 z?hqj1gA!?f_9B19_wC(k|9pM@(f$2*9v^>nfA`6?udiOmJV*K$n|gZ1*&Kcz;8k`n zC*yigEmk&A_e8cswhB9^&I35RF93I&Ve2Iq+s{3wEn~e^8!KhH*?nnj&og-WSULqPyUc4F|2qx(+>kj08rNHQ--EXu(3Ri>f@BrOpds4kXf#y`7Azf zPyB6!My6%(m&-SVTbm*Xj2_mltz!mXtKPi4y!_eC?e`xZvIl;1bF-f-zV{PoSv zhYxq(dwlrW-7UY|-Ko2Dhpgf?X0?#!Djv&-f|DMYE3C&gEvAk|(sm)c^j?TkbAcy{ zd>y@1-OHz)grk45ns+jg*J>Ig;bMEsxk>t@!BYrixZH?s6_b%?ibyicN%~Dxdr*PK z(jzjyV5iXLYQDJOhva*iT37SL>a?9dy3YAOR|p-H6N#J+{T_`}uJ zk8k;(C$q70i^KiCy<;seB*(^c;NjKgg6E9jrR~*i=gkgLOYVNeAQkHNchimCgI&=W ztnH*imXMB3S8jJwB>TKX=DcT>j=81ez2 z!J(%%GVV3Pcu9b4|tqfPpGmA>T4n zw0WVG7!0VHLD$51AteKa-(dJeQ;c&ku8+9D<8RRs_~Ia~U+9~G0|r10(QV>jt6hA1 zk8yLR$4)azjy$9e^LQJ@O9FH?6i=BGr;GZfCb^#?APIacgEtYK1N5XS3K#}@HX}x= zFo7s|(v$9pDzU{ziVskY<)ZV{LI*2D&~+M{W4#?KPW7bnLJ&Dgg&!K}mUFY0c`c7l9Uur$+M^9Ea1vhul*5gP5gE zBfOeGtMIZA!P>Dr3y@^yg~gRBn2QxfOIkYam=gm{Un z(U8kT4b~rv9QG2)YamG2>B?X~T`iFESVO{UJXg{D@U5YUYK}3$p97cclMyR}B>*Br zvuMe{SU#7X)$`5)Q|yKn4+5d3hV!frrn$3*$l10A&vY@ur&KRBE1KdXN*} zhIO=NE>TAA{rw$5>;&@a24F0lPszj%r>h46`t!TCt&OgKo>m0eQkBq@Mw_{_qnt?6l0*7T$yt~>-{BhqtT zFs(sK6fC6l+h0vX<+sD$ej^v!o@nUAPESImQqz1RjE3)UYNUWDOa&9^V(y=fz@P?A z>@6xL=MBJbgqWyg3vHrFCX}PHnhKGS3Nx%bj4Lmu@vTBVQgL&_paW&UhS$+LI5JuR zWtLqOW+S`J#7(}omQ^E>s@wy{7NhhNP)BA%x+qkD9Z=x~WH8{(d`XeK3!IWL%8S`; zqO}BsBT%#@)5S%Fz$Tquf^;j0DwXtbIIGjVsRg^bJpCS**4hXtScD zH+!G7(&;sDpRT3WLn~G7iRe|P3YW`pTEWrr3!MeaAZ&4+26SLYvz4E4P+II=gIS35 zY2K;9RAm@XNQtJt6EUTZM2nl)E&|U4og}c$q$F%udh1N!@gA4=oaH6s-UT~Q&;9{T zra6n&lA)nGE!2tNqQi$#nw;2F$IJF;Z2t)ke4XgHI|UrZ#VCcfW0sPVx*n6Q24Lio z2|}%T0I_~=+5{zdLo1nM66BN%N_^eAxb?qp?@-HC`NP%K$G5j1-Q9oi@bK}?-4~a9 z9%V;(y?9IH4qzP7E4zR;-n_&fM-c8u5HX1elzrrH1jLRt!Sc!I9wSss1Bi$ht)@EJ zQlPgGVvmO~&DKVaQqfCB4vgLBMnyBADDAx`RE89u0w*tXq(hq`vFG{G*<&4BB94hf zNdeTtR|A-`))k~7l-}~N+0FP?BZf5(+8a;qKlxmR6=+V1!?FKXkJ+lzkl@sI@zv$U zA8)RIeRKQMyW5ZM?|*!E_oqw##oE`mrT9-TFMo1(_oMs!pWfboe0}rD)%E;de`nve zE6=GotLA0rPt57@cBsC-rv)8cPdlF?jo~HMNC27D_6qEw)`htGB>e!HiJmr0ZdX*bFkV;4zEG zA+RMlkU*+v#M?kKG;$_I1MYX+a=BQ-vJg{FgG^JCp_{{1mIitXbibX$AFUB`XcS8r$$Xlj+gwr-4dpm9MD;}ik4HK#ge*na)7Yv_AW}M& z#+82lK(oy%oS(?Rcvx=6Z0aHnD5BKDQ)lBF0=>haJbl`jAbmetHyTPp(G$~>LI5B# z%LR^L%WDOApjDZZ^ce`uSx$6WfXFhT@DAMo=7vP<7>a{JThIao#pAY$*u9jdj&l_W zjR+UhGb!&bD(atWNF2I15Vs31`0>2`Ivdkup@FGlB|RFi&%|kwk1+bqU0~a|8%LT@(CCDh9{os1$y!0bmIhGncV~P{7rxb4rDCM)MsG>>Jo3>u)861`z4^ zIFBhIHfa-yv4k{AA|Pe@wo*zl#6d<30i^VH&WslcA`d$h7F@HZ8biFpf|Xe7OwTC` zzzNc9Gl-Go4EQKHelCI}#d3KTQWiY)hPOp&0t}SUa;00aH)kRMAGm@9@D)S}c@i{h zkP&)?^nqp;HE;}r=&p)v)u-z<-%?acI9Y&5AJK83-9M}h4-;t-86a3(QXw!9xj%W3 z5dh-E5rs8+P}uAf9)QA$Iyx8^YU0vi4p?-tQVW8Ip+u?O-*9eIZkYoo#~!^R zTgLosFe0yQ*k*n`sBCJGW)n>vsH~Mkj5-N%)Tzc#vAp?Gt?+H*60}%{RZ+@-{?}vEq=*ANVA6){i(bGmL#pPgz zO(Qe8#~Rzd2ad^TtB4aXmyT-nK^U3*Afa!NmW`5Znyx(rWQ>pzj%XAnmj#ks;!42* zalQG@Q9>Ws-nVzBz1nY|e|mTK^EVZMoW9(f(%-UhA55Ov`nT z(XEk4c7i^I16YkdYL&DI$ZW|th=mNBTgcz)J&=V<$bL;3W}eEVb>ES1VA3+$tSu0F z;2}X4It@AYxMeWOSh0X>C78hX_26iuZNlg?wI;X0pN?AbM%D_7ya%^d1fDe7)G+$a zv-3-w(#QV(vmtF(h}XF_C&sJX5LPY^JPuv~ecbkDJERu@hkmYgTSr;C zDZpYy<~R~a1-w=?8oP&xX3F4Z0)>5WDjlSwLFwMB;&bI7_T`Du)0!HKi*BDrK4lV~ zTLNBo9r^^4VvCEUPi?FdOox;PF%ABDaq*^9l;fl}rq^Prx6OMbV-GB=c=74w)j#Ft zc6awrx3|Bzy~*wFFYLeB{JJ-!ZVULoFLFJGV(<2l#R8X z1RYV>sHfa`J>WGcgV$R(LsP&Jw<~1z+8ZOU479@*S|LP%h~&1xse$&C+eBUnrv_QP zon+BsFt?*zA`V;zdJDn2xwHZaNQxtZiV*zjqhndpT5Tz$%u(l9>0+`NH0M%jS1cA{ zVRbr;rZ8@5(J!}IB(}tf#D+Lh>VgGkUlAmTmk4YirC$%OCITG?!>-PWeZW?(B`v)^ z7QdFX2}Dw$iw(^D2Q!)qYEqG;JpIhvkHR!QRh~62b`M#%lnDJirS*9AVNF;o&ci1d z87pl|0$#e-GeOZDG*7`?{6@3jM7C!Vh$sN*v{`{PXGs~{3o&K@LT0Rq#O?>r0mq$D z5_Oj8j{sqIKO8bK{9N<96Qwr6El|tU4OE-O4Rz8Wu%$p z%m;&PSPE<*&RAbj)(j|apW}zc;Ib+;R!boJ-lx#c;h3r;YiFRy!0O9TRPD4xLn<@H z0{3F195Gmx(uH={i0-&Jony#&3V#`n0A&sTZjc z1%XKVxdi~feDV*RuSc^k!m%avlu$N$>(|=|3dbQ8Ee52*T1k$C4cVS>vfL0UNH{BL zJ{hK~5CEUW!Dwq3gJW9DqcW4#|hK8zilu0E$euzCHrH2)c z0rBK-^f-V*#PcPF&A>V!K@^uo>WRs4zlcCj$drybmZgZbcORoLHbHa1#sV}=$Hl}D zU-@8e>N+qrMx!=jPF}+>oLXvVwa7xkTO{k>;ytBlT?6;ZPz>&(Tt$$mY@5z>S~Kg8 zN4(0Ib71R{cReg#4m68lGk~m}(J+U4WQv6vV$%exBVnWF88-O6=5aUZ)Z%MyQW$C4 z37kkua?~caUhDz420Us6Ps(^2%Q6*W3n;+^3L`1XgcKfSm}wFawmog=!g0K)`Ku@_ zEWJ#%s_aZi8V5O^@^;20FEA33wgn+Ju-SR%s@dy~KyfkqQXFd{VNB=F@f9M^7^l#g zMu3NULgBL17$F*KV{}z05t;_Kc;N&ENr7WsBV}k#U=ws~bvRm(Attr=0R%o{39}&V z#c+~?b(XWt7~^EZhV#!z<1!A0rZ4J!dxzSq%gf)~+o~V}wX=ny*f)FdhygGP;{FLKVL5 zG-{jY5S8sqNTF;lZhOp($@#iBJm$ws2K ziZmBzP?K1Z47si4=JwJ3-G_JgzrMM7|GyEwQ>~ZUd*5D{*`%{u;_jG>df3vTDzYWK z;m0LruKu*>yb8FV(TYqf0ev8-Y#egyNQb2{2vVi<{@kpCjiGojGIBSVgfurK)U6P! zPjXH_EydhaZ0uMy=4>O7_>)CLz!WZL>B=2Lt1AT4-oI)Obe}60ze{hKX;TQ2v^9Iv z7~T<#ZDE~a%*Sfbf@&s>wWJ0*ERED4gEkW}1p^Slh^u#vIT$;4Eu4j9RbY#o!ExkPqWdRkUPjxLdWnur(*bnDU6OJWGM zW1~k1An<-wS|VUc4MXt!LrPp!3FLHK-IZu)xisqopGsb^rbRr*jM?G-jt~G<9d|oNr zkmENQeI1^lx~4&5Iy?p-;ohLy)1u*sfs}5)hG}UPt%|q+Cd_`fD>@{=rm4Wqjv#|c z(uSSa=s{URBs?+g?Ijk(&dHk&VL1Aa+GBm1?>M;}6hOv-^+(XQKp-{0@fk>q;AwEW zaoPZ-=l0B^2{9Q|b>jA#m--l;5rc&z1!M~4*f`3rELkDm1jJ#AEeAHZtr4qBVk`@* zX26lJJ=aez?4XE3<77MLOf{G;*`I*q6p+)4O^7rS%u7O)O@>vaKZzqbs%DJT`XaDo81Zo`$m6};5b)`SCpzC#K&yG)9J9xlFwVw7+DIO*koLX_(lRxW$TSW(Q=zdO*_39Ls?M0xC-h z-%p9m&(#fr*-U}NfGsEUhRO*WVl*E5$?7A`#cMp=cz9kq1)v1Lcg!g!(!xn5Lp2I8 z!rZchDFGspa-5)8NKZ_&BQ{naIx?nF4olgjE3&2F&$E1Q{X0?a@kG3D?@IgX;^Jp_ zci(#P{QJ)z{`vYk*M`f?rr^SP_oI2o<}&wsqRln!wT_%2VDMv~h<&O6%gASWBPVZ30{nSmR)CKMdUAAY0;l@ic9FXvcWnv&UOY8F9$N4leh zFa|5a%&P3a71Mjd8F-i{-8XLmxB!<>M(GzUB3c_ZKBi3o%#)&Oe^#(cnrdUWMm6kF`kCx-b8$4J zvfp1{fA9YB+mFxx>Gt-k_x~H=yVSagxPHg=>OQi|#jKkPYCjty*53M5z1HH7Q>g43 zh!2GAh$XaSECUosgvh35+<1>7b7(am&jJBxhKZ*o!1uJoHn-k>P%hI%mTN8ZQ&)X6 zBC%sj#w4P&ik{eejOOb#oLEj|>HQ~+8Rte(Oar^NltG>Kq~T;@u#u}bSXT{;?iz1>~Vk0aF(pk5{@jFcr z5(&lU{P2sdyIfjg(lRy!S+g#Y7Q_Y&iNtQ-dKDa04lo~=ti}c5{a?md(ocyMl?@CX z0v2MY22+Lw_Hw83mkPM325X4MXjW=qRIrtsq7ckkp`CBSx=>{4lgdRdfh#o;=ApP* zJ`A&Fs%@>+{B${upa?o5z%*g80~}&>hKXe?1Nk}QkCCU6aRTyg4n|Da{bqyEW(ZAG zO-vD<2B!5FY4u`l5MB(lW6!l8Vj>n?FSmhlVbF_EZ(UgBJ*L)rcwG|YKp8ZQfqC4DqJ$08{3S;2Ant%IqU7aRry z6+`5t=yjVBtVS#ej}eh5gf4x=HP64nIaOt7ROOVC3Y19E&;TZrs&4lxM!uiYWP+|s zkPUz^qp4Ij`m8NuUo-P;;m7p}!fy4@r12?e-uoPlm&t||=Ua=(3lsk>L~3bXcaq9NGqZSP%;4 zy(hZLjMl~>7`}j9Uki#|v{0TS#S=m3ylG*#l}!f?BRD%inFV1qRn8J^Dl+5V1!kcd z934*`o`wnYC8z=QHk)j5jR$cRc${RF&6lpfHsS9k>!GEeWOO-6psjSo!+<9i>C zL4gjRl(E6i%>kO~wA5O$Cc6i_3jmGzMcX!`?c=++{9+a9&>Y)0ZErzbQeHO|2r zR7eF*!o`5;^=y! z&%)9u5O?ona(0>`5G)CgAI4N;7EU45zHL~6VKqujr(vP0y^JxORr2iFmls#Ryu10) z!~H+q-Tn6Z`j3~FuSxQ?X(=^#?>}8${`ThPr}wuX-rxW7_U?8TZC4Hp-mU0nX?{`NbM582qc>1lo6 z-igM=%C+hi$~C3;z&Ca_9@{i6+a*_>y_=Chw+cRBJP$ImEmVlGiCS8kqyPwQK;?`_ zoIyB2kH_sBB+5Mf%Z<}F^D<#%6fpndB+YMW08&sa-d^w^LtAZ($5Uld0IUQLMq0SE zjlqwbrco`lq-Lej?-?Xse#Z+9bhMaYAkcDqa6-`oh;swX-Jxk_YE!BreSLJ{!8kA^ z)?{45YK}3y3?K`~OktRT6WuV6!L*@e;d6{LFQDBNk-e{j z9el6rmB5}8?Nkm0U@(Z(g+-d$`;$1^tn*Pvhe_wu8>u6weo}Gv^F)K0qijZFsn~!( zNTgC8CtytZ*)QLF6Q`dEBMQo9mXh2m%t1E!ER z3rZLKRHwu>MZqYs6tk$-#Jw7ac^E+}0u^qHC0tJ;7d%I_Ivocxn8L8wdi73* z5^PoDAq=O6nO7Xt=J%RB0uV~|!H2fFY|J$QL(_h@O))@ms||;wX}S!`nRTH+zK+J; zAEyyv?0}BcU8;P5>kqy$f~o{KIzJ*l)Y|%ahM6)=B3fHTz)gU+bJxbud^+iz0&}M= zXcj~C@}M)9T__XMjIMv;k0=~66CVu70SN%&j7BSF$Re#3bzr=v){yOoHN6E@%;|cw zF#bfqm-?8jS#!f6OW(J9dDxZ$(bt{IN0-wAgo*eOHou!E^va<-D zWvNBO`}PjBSI?e(a&`5K+uL8<-F_q&_xulSyhE6Gnjxae#FgkDs7wKxjV%dt=kkp&ZwjvOK(F> zK_e(m0;BnSfR2|uquHS;VDVT4fVMiMJwRD4igjqkoYK^{jCxoCKKeZ4w_)3OPts3I zzQ1(hmRowiuz-PozNbBHs+Si3KuNZ#=t%7)$T5-7!!!2_xbs#&e9Mga;`djVzr4Nq z@jYKd|MBWdwy#G^sVVs9{>|V1r*llx33SF?3?}TVVQ}i{dKFmP`e?3}eMxL3f&v+P z`epH5YAyB6T{4~CoC!P&R>6pbyD&r~1#~nDr!TY7jK@%ZXP3bsHlk5(W30O#zzi%l z$&HAL5p9Ujur)abBAg)B>97IF*u=z%!*fg$pO6}DNq&YwVil8qewZJTy+6;BVkoZ) znUE80Me^6o>eu<@)y40wZ$7@c`T6b5&u?yie|7a0?)UA_-?)5xA-&8h?Rw^_at#^4 z`$uo~>i6=gy}@5LyU!L45}JDP7~VdktCwIE!6`XNX|T<;bcuF7Mp}nVrWyKtA&K} z1W5`^@GO2g0vtKA~;3UO#FQ#w}$y1`2n#jpQB_GJKT)PTUv4AQEY*042aZ98@t zVWVsWHO2UXfli|6U6AEm3JqMllff&CFl#%(F(GZO$OKk2z{(5MtR$0NMVrBLg~Lh* zZMmInsQgIAit^yLX=s5qOE*LgM|_DXGa*()Z5c)R!WyA+8baUcxDaHkpCLMBNQG6? z@EjZzf=qM~3|&bmrPbpD19eG*#60lNY_^kv*wh7sB{Et;j!B;;7gHP{%}j8>#CKY7 z>c=8YwZ?$Yb|Z!OW+I(v5vazib$cZAK@Ss^G#6f}-=e zE<;yirfP_O1_HRqe(#-DaCsSwiBAf{ZJ-gWGZ_MLeuaCqMQcllH_RD~~lA#?+RV#bKb63zT6% zV<&~rM2hQ#cnlKqF%V75Aip4MtWnK!Ovz-AAkoMRBQ-VFt*K^0+Ni36y*}p56a(IGg_;i$Effi(9tp=x4b1ES z2N$2^SW+~U7MXwmIZ6N-1935Clz&`(yePb_hxiElp!9Y+dyqD{LTM7p;txr+pX0X-awf3~#5xufGxyF61*Ja;*l*9KB?z?F71Zy{Y zp)jS!Maro!gVEdMD?JTZb|buQRF48U%j>gZtrO|9fjE`)2!Z5)aWWdRD}cAya>1#f zdqO%DNxZpl0r>R#bN8X=xl!&i(&p!f3z=p*EX3w)S%ri$b<8_p8cbrjEDQw_hHI0W zFxM^yrZ%?}px`gwyt=sj`1ba@50Bq}eE8kX4Iy8j_Pgur?>{_z_wn&pH#e`c0M3k^ z!qB-#b_qQ_XmdGTx~%kE!S)qxZk@R(Irj=x_qa|ud7Y0*V zd8_M{$8c~rC{C}WX0Yt5c7mZ3rU_tAFtxg0cdtZD8;>^aqDPRTO>3Xn2L>j81Ow|%P-~7YfSTjn zIr5RwqT0a3$A1>uFo+DDOmr-iWYIS;5Xyiw9~q+)^(lYzaF_L2_{wV9*?! z)7;BpGcQ2TfFTA20S-wevn|kSX@j*n1nxRmg^}Zy&|lp>;KXvof{ps3Yh%8csKXi{ zlsTQ*K}4KTEgYJQL1Vo*qZ8Ru}IM6{vTK<9HU6*eG{Ua}~0 zBt0{=f>=%5`Jf+dZd09(e00rZq#;VwoebkVc+#zGGL_&chTuGa#qI^=4Ye3tk>qEL zly%|t6}$*qU$OO52!BM3dh|mTpK1LAw#3=@4HRy8O)W<(wUd~75RIU#-!$V2Mla?F zJDK|Ivt4G%DTh)7Ye@RvQxzz4(`ABLE0-}!E4S{hrrp|xa{#n? zm&p4>0yb|3n20>z3PgEWsCJ4Uxz*34%QWm(9ogb+C}eVa=**#3rm3w6X*)AVt(du@ z_|qo99%FPDCuc;!q%qU57s^295(MdNc5#LXO@FPyNg=~@x~q|>&=k3|!z@?dWN0YT z3L|WSP7fxnLgN&BN@F5rf5Y8I$olPZwsp41SNDa9U^2DFe1>11i7s0WJhR^9+%Ewt z(fhpant8Ya$OQ8JvrH36lv{+>u;Tlw8Cd_VpRr;J1p<1Ow^%SxAMTt6jZVO62$=ab zm=>ea>hgBF=(VMIt5K~Ng`_hb1z82v=mmgXdBf|A9JfIloGO0g=8EbS!XNiI+kmjb zh`4l8M0)N-=<`d5L~9?k^x{l$`qmHef#sa*Uz5)^Y!(Q?(aYJ{~r9@|9jADM$1)atFWm~moeKY z!oCecto68}-A8Fb654;L;j;rHTaQCgf&fp^86gfF9t3D6OGDgX1yWusV&A)u#k#}E zF0>KIM5`Y0<{{F2F&J-VD6z2$ntkF#001SaQ9;K$a$ClyOMlQSgXV-KPT1yQB)D!` z4D+6;LAG&K1m7tiDO;A_9uwNvmnpV(pnZ}bmG$kV2zYs?W(HQ^ch@%`xIKPzbMs}w zz9ucD{`!W>bNu$^#^2b>oU&rfi%%?>po^b*&p)hFt^k^~<+s*YPdq*Fa_~t_Re%95 z^#Hn3wIQ&a_VoxOirA64nB*j#rTME~qX(F-MbVfBvWFfAy+rHn<^p17QwtQ921dVP zPDylhEhIYfOVSo8Iw|S_N;U~(l&>$fM_>!$UC}iDACZZ$=TYo1`IQ4|o=rGBTb%Px zE5KTIo^e?N2C3|1%!K0q{NnOg*Eb*D-T&zB{&&~cxedN=fA+?u>n2Fg`=uJZei6N# z8E~WZdd@Z&Xc{}x{lSKCcb)~L$(EooHw(;;#lW#~w)nzzMkgNW3{)z%DRj)EtXnZv z@foM|SaR|V3F%k>aAG0c4Qa0D0oVf2EYiK zGHIPDaV!GOp|+{3aT4d4d_;6R(Tl;(%yA3rRtz&?U|l84jYzri#xkG{#=tJZQ#(F%=P(U?$`wJ#iMqSA!Grj49( z#k_UZ;|yB;VFilWdE?fRv9CT_T7uPdLQJ^uLeiXzHG~yYgw4c7VPUN&QB#a|J%(FF zoZ$FvDV*9GSO85qZz0Cexv_jv1BDG4r=Vz9a?)dFRH7bq(0)QdCn06j4=&b*q*;v8 zIXO*YL?RBea?vtn6j@s4b8>m?zZtu{+IcjjTKQky4At2XGJct&c5l|CN*J#)f7;%SWQI=y_0BsG4U4k@MBI60v!Z(f=45<`Kd zbYbuf{n-tC;(Tl-3$dWh7-{-+^s`EMLkg-h$4*%kbjoU-$^cuevk?r_Rv7^6Bw}B5qPv3Wx*|cSfW%WMqyBG=Vuy!eALnW#A}*5N@0B z2m^u542tQ^LI=jE!A>)pen6Z7V!%cPrpsd@k3ypLRTkDhBlaSdjGsWxJSElxTTacq zxWb8r@F`o98RkqY11A!7)mmdKo2sKm*dMFwvf(vwJ`dN8tr+$4qYGEBw)4l<&|dWk zi+)HNy|RQ92M^1rTZmRn+Fb$MWcR-fByM!HL&C_|i;J!Q-a@oZU?MQM$*AUplW&+DpIjT*;p^ZK!}g66{HZk zYMOV8Qs8upX@?G@$_QY@mAOrN&f~2lACv|W6T4fG==QvS!L=Ok+n=+&y14l5&CQ1o z_a8hwM0hp-6G@uar#poo3-ZR(3(58Y(7n@L-%DCi$3$xvZHrMp4UzOi+J~WScmO0; zNHp;@_I51LYDi~_*a5jq$%`kCmUF1BCdQayhda%{5#SC=OaKuxHgKZph z-)}V1nj`UY4-TnUh zIxk%=#4Njasa>Ni^jJH8#||1_chhbnmgqAgZFP`p5pb3W-3?>dK@VP7Ocu>c#3Y{&w3U*LxWMB=n!s|Zn zEQT)v%i$4tJhe`paQM>>!lviZRW&0S8b$TMK~jJES^Y(N2xAs~TA@-xe3G|{wA}Z8 zb9TMhc;WH&CQLBV?r3~b*#bl$ zu3OC}R}-X*h98@(#$y}rye`NybVqC(`;A8R2Le7wS`d5mpr2qTo73O>X?1ql#zk`P zrVV2(xyj4Y@{gbT&|>cQa8NPd%04)FrhrGd9)(N(VdI^UC zN-?g$eA}qoN-QgSLoN91H6iH^G62X(F@}hDRa&IT(Gq!^)kMs6h=I9mmBbqR_FNKT z(N&olC6zZL9f!pm1w9ru!q!y>GmINO4s|I=AxmI}8$`Y|BYfwQMoa}_wV`jd=rK|y z-^s`lxoJ;pM)Nhg2i=gB;?%4QcZBgB2*_oikMpdY)}uu)ow=E$o;hjb0K%g+vhtIn zdGWyk&+~akpd7h zAbC)3SJSBDtFcKwo|dSY)e0^7lv&jQ5d<)}iS_F+eWD1|FCw8X2P8cILF!AOUbHkT zN62c%z+`X;!cm{1_Xi&bKsNOv6=a8 z!5ztk!vK4O@w4wRbPWwjBZ^uiN$+7aIGV0BhWcR!A%J{#>^Uxjj5CA&0D(Y$zlI4Z zRWX;_c*13EL6OUgj1}XaG0snkBf(QMZE_`*o?Rd`f)p@koFUyjf+g#g8IjpDE(HL> zHcqp~h6PQEXj=zoisuLfM^21HLOd5CE{L_@Z`(XN#A6~0MmdU^MhdwQfuzk46E$@q z?Xk=N0JC$Y$PDP4BaO`#Z`tC;aj}1ITN}@-L5t)c0;IWw8S0xzUqh#|wo>Rj6d`@J z-J%?j{J1v+wKNE=9|g-r=gP;%(yBB@Az9!(kPJfD4w@s!Y)~3yDfa)m*CN#x2(~n} z&h2wL*9h~F(38Q%oBGlq5s*H_7)9!0S+y*rfynd<>6oEIN^layjD$@&`$rLJ%x(X}JToAGV98isii!B+yw5>ob*19zrP45Zw*xhTaZOdRh z&_)n)2sAwc(@4`ujYtv0J|csN@uiiFJ{(@d&5pJIGGG>_HOC6OB3`b|>Nc^-ZfK;E zaXXEy)sw0uqC;wcb0CIeSg1cEODVue#9Ae(i?!zB3^?IYJUM3SV=uR0ys@AQNk~g| z;)VHG5UCy>EZs_VViLH#fH*-9P;J?%|)WZ;p%Prc#{WN$H*G z$$Qzl*tOzM;O$3pTPw<3Ubaj|_KZHBnAq5MzMUn?5d1lOS4SLx#=td{u~@Byx_u4^ z&p9X*sMx(z^g4%;-n`uWI_?JC1qsPW%5Un7hham{Ob1hXLonM zyuJPH&CMS#FJFmyXIskq^zxFw^Lu;y^OSva^TlO9&ae(MMY^s+8Y?x1EG3{WC@atv z^-KRQSegc~tdr{uYF@iJ3|BhDew;uibRh7M!38f*mp<45pwv;FG8n7k^|~Sj(+ZL6 zPup~0a$2!bvyUjoyY*SoLXCrU=7K)2s zYk?MTOv33Sj(=g~>NAE{Nvz8F(#+sWE5TGL8+i zO;FAvWAJ^6V5sRDR|AJ>2(nZRXk+F(wCKvrk?+}2Cddpqqh*`<4^J_bg_!{p??Y~m zRx%z?Zs(}3kIOVJ)}ykhmfzJL5<%z^5l@YEHkRh}&;nsKn<${zvQ$>&No-ubW(01q zySBk3!ehFK%@YudP6DE)mNE;>F+73f~+|bf!ygUaTu7PRoU3fCxt6M`C7ro zsaZTJqfJZ;Yy@b0beaOHZ_tsjL~(sM>eu3;z>HCjnWB(2HN+ZP6JD3h1Km z3T^oN3JSb z74u*R%lhhWA6KZsiuX#EeqxBSg0}N{78ip?14tyUK(o8gBtE$52q-aO(lhVG+V~SIp(wHcAUcrmI>@Q2z>&dz?qRLc1X4tLTiNR4N zeVn<}%nyh0?TG3ciX?`g-5X-f^g%?A8QY|2N{+~^d}%XsNLHT=!>yQ~*$Ox8Mvt^6(o19kAAcvGDk{~je+yG{T3ixy=_?y!n8M33@9NHNivq$Vx(kMV=(FwMuSFWf?W|?DlS%;gSc^L z>R zo+_}>m0vOn+*RxP!#M!*_NPAj_kF+nq&5a(80d7cbB(sm8<&7|+rMvr&i4A*v(GOt ze|vrXtJ|AjUEluZ_U5ZAHj7u%OV`V}e?FM~7oC^#=-m!MxeL;2Q=HZ-T@mfr0kg~d zPwFwmLqbp7HCuxW!6VDV-hpnkv~DG=37>rxI0rs=ySOP8C6dpQW|48WJQKpunGGg= zP%MO)KDRiAf|GR9n1-rufzU$%bHD!@H$E$i&0q4-$}M7o8E5+GP^QxLrx_=P!gd9L z@dAN2*1B8fkqFb;P#TRW1j)tt=j*Fq+}!@m@1XfU_Z@C2?>9F$KfAmA#qI6yuCKXz z%&H5(joOik&6w6{3yad5mGkcEp>YJ##q0@(S2RGEZP!je!E`|?PM9!9bX`rsJ$5+I z8!*=`g4P^-N^B6-2p_swWOBP!GJZ(`7F?#G<4oTTX_S+Ug&YvjgTv-yfPE^;pw&^} zJa4AcMJvqmOVd+a*(|b@2#t-{f!-h+SoXJ-3z#Qnvoo~9*lO3xyUy4zbG!T9?ajxz z``z4RSLbH<{tt2dd0Q?Kn~kR*&xMHHD+s;Z-QlAK5{V(2FiB#A1qVG?!Lt=$1`P}V zjXq}y2DrP0J{GcgM$8$fTPl|39VYJMI;?6)Xx*oVsnD-Z!myO|yfsC-{xXoXN2_ZR zkhTAaHEoEgD}EjwvaoY&10*s%ZxE4}taxll7}gFSl|m2!leVU6S_IB^_D0)SIOYUW zQcjGjK3S3HfypprfTm~?yedO)q}RHq?2B!AHZn0x8YnUQWn7y~4YVs)w~>vHP|;c) z$FS3Lvczy}ld8B-@S;9M<8L{fA=ah=$->i9&nN(gq4A8As5!Z;2^)C@Ym5*Sidfto z3GN~Hq#%aHM3rZjIwr#kOb48IaiopRkJhpENJxLb4ACK-$^1D;Hjt!P`wKYH1c_0Y zQ9XzABfh#3pe)N!8kl+(Y6CJxT6%9ZZ3rs$4yF}3S0;u6vp|!ln*gGBRVUgKhQToB z#T@3`Bmh|o7*R1!CMZD89H>(^He5^z`AiO5EHsO0Xsj!LhvR6^J3Y zFiC_N(Pc`0D=d-Ya;O4dk%lt?tyPv7&?s=AAWg;Cp>2?6=`xhXgtp6EtSCd=EKvg; zK4r=04Gkiv$Ov6eCbv5@ErxRiEcr2G2~s;b)u)G{b;g1lOF#{B{c`%W5HUHLK-S{d z%(5v?T17%7ZIDz+x=tG`L;|6pfF-X5o9C`q<(@S7$l>&&usGWJp5*`%Yl1vCylYU1 z@3;{IG1xNAzONz}W*8o(nPQV6g}kFDe1C zc2QzwPas0#fYoAkEsLqJKwRMe&EB6jNt#_(dT54wMC4F~i2$k!g@GhMkOW9JTdi*0 zOUYz*&1Cw~>M!~M(g&HOW{V=lp#c&T0Tik-Ga@{uyPjw5^ST%N+Wnwb4HEN&zvt{> z?X~wA{B;kH$Vlb`k=N0-Ida(=!AZ0z(pYZ3iXnUmwgN`8;8+;St}!pbiJcG9nvO!{ zF#b*ZEZg;t|6lGuJbT9fJ$SKaL%PBKT3Mbw)!kWI41Fr|H1Pz0Q$??pz4a*M@(*c6 zX#BF$XW)={*xI0phtU8IBfga#m0G}PtOOOOu4fcPN|*yiLCBm(vf=ZALa^{fhCumI z$I7ty0p|inT`OiJ6A~>T4XiR`<`{66Dh+^pOpD7OI`SZ-FjyOy-pg2- zqawR+c;cEt-V8i7a9s!p5Q(j$f4&nD`dmy&2aog;Bk!AZCmGj7ZZ zXoE7djx0C;G;!>LUR@jUckJ~mE&J-Gam8{MBi zB48On+Qf&s&nzB2?MCZD=-aFNWt{?3AD23#a-$}FD+X%GxzRY9NjFYQrzC9hrkK*8v0GgVB4e-w!gHlsssgo= zNhV52iGt*Y1}_yD>`JkW8#PLyw4#;^I0%h*U#Fi#JY@Z#% zD+~Z4&KQ7+B?)&rEW+^x)b%h15Smg$>|>Eg7i&GMXto}C+Xz6XmQ{4uaS@g=-qwo{ z9R(Ff8W?Bc0vVP@PYn)BrL*5arK*}vSQYr{C&DoK)+{nH(LBn`!9qZ!KV1qIp5Tbt zWsuP@FwE}s^-flLu(5|k3`F)7fz%Ghz)68n)mDmOaaII1CrtPTL(IhWFT>1?u*I2y z#WxZPIiyg7zYhqz8k->lTIXdZhV?SNpb^$53fQ@ZW_0ms$xRBrCoEkS1v?OhF4CNt^gdcMZWxWHLuc^mA$t7k zNONRQm@^oUkpg+xB-<1NWq`R&2}elEmu)a2ogdS?Qn-ktWm5TA=uHZD-cPO1s8L7u+_uw+6*@g^oA3uK4WzFuy_&D zZm!0$-_axGMSwzxGqz~~ML+?aL7z9HG(iHvArxh}cIvXp#QotIny+p`Y~mIZpouph z9Ux?Qpg=ub5d@7d&=xSsoM~H`I^pUFLQ1AhZ5FoDguAC{8G2W7rgSp1bjW}}#MSJLj7es&<)If*by$Mwg0=%~ zv~!^h&^jGq%|Pz9Dbs`*<8m4;ZgFB}%~0YInt?=99N0dG8j3fA@$|j7 z4Vad2@M0>V2#BW(q_bRja8@qXzVT6-rgg+pKAprxSuL9O#zKR0VOkbxP{86w7cGc1 zr8g(euF29dITeH2NooWZXfot2MUx3(GqECdt<5GNp5EcN*S5BTE3iZ@SqL={jN8H~ ztl+7R#!PBs2{owa3O*HyWLcp1g$13Co^&yq%Xo*F6w@m^hlXTumnKUKDxD;hYYf@p z)=h3}5m_!{1^|@XiF_#XiZeDCMvsSaYslDBjhMBa<+Y6251Xuh6Lu`?P5Z3ctNs4R zmzRJ2?AgyQFZ~zjZb-U0W$s?jb9IgnVvdlgIUY)Rczz5Cj;GlJpFk5bhX>Cv<-(f5 zP*Awldp5+Vq(43zfN^>d(hng}!WN5kJlSfKryimgN~NrehinAk!iFMQa7#3TyT5ks z_K^1x31SnQmZ49iQgO2q;S&uEA(Ne^fl<@4gCxRr2~#nXtL;DjH^Sp)0b|z-xzc1Y zJ3|2Yo1J?EMk=2LdAxf(`{~8yUp;^JAD%vadH%WnOVBU(`xNe_YfRS4$-xTA8 zBWJ9&b_ZjRRSKEP)hy}y^qZXUO%BEB+niG;)?xtxrmkr4m=wf(TB~CkMbR4>Xmmyh zwm;e?1F|HHiglz}9Y2C{Dh5Egf-=MxVfUE)^$isYOkD{%$C~B~E7T|1{r3iu^~6rO ztPh9Q;Lxld5*v>B#CoFUflHnnNY1p`oDw{x*#!m$t3o=7D?U5y%o^W6e9 z3koZ>I&Z%$(|}~icuKX6K~WD)Hi{vzfQBSmHO(zZUpg{PzG<6)L#RmZ*Kkg${%f&* zK(7uh0NDtG6c37F+SV*q5)r)HDpV~#v1*K+oNTUiNa{^%Ie>UT8($ugcx_Qk`ZQnY zAi>giX+~iJqNPN+ELBtdIK=~lQ!Q5>L~ke&rEnPDqQT?tXek`npt>XCHwNt>GN54{ zAE2ecn@ETvYiZlw2Tx+M83NpRH?K)3-Ko*GBxyiYF&jW7KncT)7r*$S=2I*E1cb>% z>Y9Ub; zX>^*&5FOsG6zs_aaC4KHjmVcwkNo;znw4SZyQqncOigA^r?u&MeryR08XX+qfz6l} zL5{8BT7aUZbD|VQu~Zx>mVpBhOGPaI7zKcjCDJriSxA}z62u0?WA=Ii;I*^#3qqYONl^{QCaa7VUZ2U5bN0I+qP)-B%=7!W<*-7IQZdh zL&gw%Y1Pe4I)>)Rl@e6}fCH|pcDV|of+8@t4O%P_07$L%W1iL9NGUS1gg4Xfb1SeC&;ghR0b;GC@U=iGo3R z)L;ULs^3{SrDz#_l_eB?^|PieQ87U#!xTd@Q-#tNo(9$)FlV;D$uZN00g

ZiiSj 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�dPc8;i8Isv`;9!puD+%mq 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≦_D7`CT+c;5nQzdQnD8VYnX)=QTlwgmib0~;d^Ug372 zM(Kv?JEqd$>wy&&CMP}<9v-hcO1HrTr3SoPHA>B;-A60jf3%~+<;2;|wN%3*7=%*5& zus*P?Y9!94R;)m=Rk94l1TDxwf$B0qQU|mwjc=Dozi*5R7OwzCPWp5%YnqpE6n58L!L*BixuWJI${*@m*@{~X1H41@?XoolrpX4d_o+9>x z1|I1o&9AOnB%b9mFVZu$xXy+RII*;CFmnKb^pewD_lM_sqO0LzR7A8%TJApSDs8l8 z6^Mb=^`OpgX_^r4kvO14jM91L<6SUFu!i;F7DphO(2(+3Kqe;;TQ(sV;~H$VAsxSM zSeXe@`&ir*LmlHAHZqLQEz2!d@-9sRH=!JSiSvzO>b8g=Wv$+K0V01su6}eAJbH4qc{s!Gl)=(wJ&V7zp9GNww-fQx zBZ4xS$DoA~&3&Pxw)z6%=olV}gHa{oXTG@MBk*`XM3{4My59q3!V(6fd@LUnsd~sE zA!qj}rcYTA(V5DVGZM$O%YY+FsT%4kSfqvr_8#rlW+2F~de%*3?xkl&>qNKt+JwpX zr6~XSYa%@OOrjJ$SA$H4NBm4?G|!SI^2-MEW4M}B2*h0a>%~lnn3)iyuXaMLEjDC< zM2*qCPUyOnA;@*FES+ACP^vj z`ejvf2V-PhFb8*MEDFt^IWJmfoD^IH2RU^tN9Q&B+(mdYY!kfaD(;H|d;5vrB*kYZ zkwDl~iU{V)atRE6SE6m;LdQ`*bZ1qmHzRxrTM{f(Z|z0lj63(R1v!y^(7}ku)Hd#4 zC$~#p7hSWeW-&y(VG)ZM|D?vspv4s;`a>nT7%L-77j8zh`Dh`imW}vZD!ue(e|4a` zlOi`WFgFMkr|}Dnh(oq}E6gXxDRLC7O>86#feY#O9Xu0Vgi7Wd23QU%1WSy`o_x~Z z1xJ<}>4@3Qyz=Dxd$TC|Q2otPo?oPAcyuS&=RvpO4K|jnkwIs~Ms?DI=%eWt5CWC< zjE@!%*CkTh*(;T-ZWj9ycXQ%`meJG%~X z=Q_hEr%v2LGjO_~tqRgz;z$#~I?@R%(QYN1h(@Z8kVD1 zmlNeICKL1GA8H^^j78O3y1kpwCAexfvrLZ@l5#tmq;S=mRW^N`#rb6#omN<>OP7~$ zlhAN_14LUsmw;|!LF_I8##0FC#gpk?$4;2VxSnV&08Ry%1o4wa?w%IxsSw28N{ADG zGZi9O(C1?19)%@dpA6l=w__sd@`Oa__}UV~YYFVVeFT?~c?y^)EH2^z$7Q!J*uEWb zun%hBX%|I15iPQ5k183BVHz|FS4rlS>ek1}^kS)aC=oW8$H7sU!AAKbZ^KJy^~nC{ zX_i#hiQ5B*Jay6|51f~AlsEK3`(Yi$T~sX6881L1(f;Zm4tQ-5wsnO6{nsv(X(;Fk zlU&O9uLjyvtI~O+vsPud*5u9hu-tmedKDbb@V>sNVR` zmTi?YP&w*i>me#d^bT2oSS8=`sa->8600ZGA0%SSEK7bE&@^LYQC!hlAfN6GkT1sATPTaC($Ohsd+`tR&s}SwP>kj z^7~^mu1tYR)!laIIUG8J-Ni+_kLj3w-5QFwYwWmFkk=v=_Z2rPKrHo$S~0Ps(XFak zu;b+H*8x{L2%M#HzA71CuO;z`kqGq5kQ5h)BdO_xDJ3uO%vLEh1G|*4W5ssV%oY!! z1(KJyDqjwJmHE^+qt!`;o@5|vJREF1bC=!{x>_MxtjaU7*-N|+e=4H z@_I4rY+%`7*BMWN`t*oI{Cl1B;$wQk*DM_!sD!dzHD`%e z6bR38opi}a^+-xtxexk6Qcumbe?Z(hzEe1;!J0^N3n>itN0oH>CMsOBov z!LT_%I`Gk&SPZ{$_8Sha{jQzqETU-o0lHdt75^9v`9ljrE?Ha;5edlA2u+rORNvx zO7C_fA!B12GafIQEQUK4STE6KQ!pbr?i+jya?7fNr%~7m8^03<8gMo$L-4PRL!vk) zwvot~>ER2K!9JGFqWT_8Q*Vx>i<8|(qVtUu<-%>r5nl87M;**yWl@~;k!6R#1S9|< zuGPW72kGFuI?KVYK`Jj2*?$>~5?uG?Cy}HE{qg@k%pixw2zrs!kLrs0+^^e^)ZTRl zVs$Ce=?5pAfzUhHKoUi_66EGiT0W0EQY3<__iJt+G2!D;MZ+zv(iX&b0tSg2mn@nu z%={?UVq|gu`uBhS^S9s(dN@2YX1d)sYieN8T+N(Pjq-Us$iwbnqVqtWTMyQY zu-Mm!|7?hc?Fq%SK4j0s&9S^KUPIf%n`Zh}GfWrRovd{Beu{pV1sRE3WxmVquq6-~ z8VEOvlcs6q{b^C07zuL2W?aw+9g`;j%-j!BIonwTnU-=Rb6)>(_k-~h%UB}%Lh;@A zZJcDgRed`AnO_+E4UHov-A8LHt^(9N%>KiuH5jdfwlL$r{QW3AoS@|Cmbmp@+gC1w zTQAQ9# zPF@iqsJ-DXvw7y2tW(=BMB4S;c8@%o-|#ZCX{4f3uu44KE_#YHg4Gnj;BZPCXH~l< z$fd=kV2hC^Cz=>118VPW$xD@BsQ)1*B%O4krOu2N?T30p&>-hYI^F-(jWwjGc0rMC zB0Q@eKd*KuPHY2BD{jLa9lB)vcxWKj-1{&#P|B`>Fv#w5!Snar_K#V1W?uU4F{b-h zHaK)ej4MKMaNM`R1!lWDMRf=epEc5ue2=yR`Z7>H{x;eKvxDXSqH$C8P5{29WVHp{KKrPzbsC7nb zCoo8`fpJbpUNw*g$&J6gM(W)nDX@DQggv6qnAy~CFO1Uo_3dv0)b)h53HSCX z0d2)mRvs-x$Y9{lgMsPx%Ros4(Mi71`81${o7Vop=df;X5t~X}Za4vmQqa-Dyk^>|FL2GsW zUnT2`R@Vo*zSw`>z5UV-;4fy-7St8fv>X{;d%7)23#}sf3%#WoC2nI7_HiG#aWKgL@}@s(uA6GGD+URYvBnr0B%*50P{PZJQc5NvP*An zuX1|-P4D)N|1ybaSaJ{oK1m*|o?%B7-JFmR5gv@X@iw&#b2NSi5XhoaiD|xz+glK3 z6Fq#=ak%#vT9F*j%20mq?tppoi%@QvG~SCH>1vi3O^%9{D4OYf5fC_A4U-=cW8NFi zw?Jw{7(nt&LhMJI5K%nC@gj{$t3yd;$Kha(F_qGRM|8fp2|uEzN-mul5oQ_kajemdO))|UUR*sbPdd9J!Rqb7qYz+tB?2dA%|=hD z7HMxn&Y8&?&2adZWO#=jGwZIu)zqV<%K)J%nlt8yQDI&U4)-RL--Keh<)-LpQipm; zDy&mV1+8& zuy=bG(wH9*5SkiU6~rEzQ0INyjVB40_?N$0pc`|?ot!SA*0ojB(USe-Z3Xu(iWkVZ z+bQ_pfB)xiB33ln(}{uPz%3||I80s40>jqdKT00^Evd*H3?(Wfx)vctO|Li*Q6mr3 zo)Ajmb{(8wXg|j?+GaI2FjvGrv_+D?_8P;LiUy(kHhrT4o&ajj)1ISR#rA=cJldetQNX zwv%Tu8b)gD-LF4$MpQpO&_#0A)<+7SQb^?M%vI8fHY&pu~nMP`#&h{?UH8j4QBib(N;i-(OYxJltCoB&K{Zis zdse3>qyTY?u`l)2!vz4!+ClJNwb%N*#pJjT4<&7!b)MHG@<+gPE2#;%40gt{s91=# zStnZ|2KBI7#W$otk$AObwq#MPyB-0h=_JBO5U*V(-3=c{PlFIig;9%>1q@?(3pjhe zP$g<(bTxlrPa2oxVm7PxKQbp+)eqENiyI=`4$LgUWz=)KGYTWDq$W{gXuV7I>*2Kv ztnIzRbPCB}FiCb(M?fS(tR9ul^bE`LW3;chgl%bDEYM<_i84SGlbHEdQyo(e*%pdr zp}+p(K4`-210uxUB~O>9--r{lCeB;Hb8Z)?o`62 zuuWCB8H}?4N0pAcnr(}8J+RsaYGbi24vjm*C{s@{d6gcIa9nt&dQ&z0JKq0=H)^I_ z7x4?88X-=VaE8@lIgQ(s1^80r{YkL~N745kI@_?&Ckr(TXp1c9HncHH(k^=Z>*Dx; z(1&#KlBm(^qvlLbv%#0XMnhwiYX*!A0A_x(Eg5yXfL6Q7sNhD`SwFp@E$mbdW##+6 z=UMRAKi<0J(#z;x1?3(Sk;U&GYCAgno;7-}=)BXT7Ka0qhGeeW9mjj14nq*6hnA1Fc;uiiuJM}=qt7}DVYT?aJTv|0(VeAl^wpeA?!Pvw!%1T+BHkn$bNWr>AW9f}m!O1?Y^fGs}^Q$l4VV zN?)04lL63FOdu+n%_^c*l8Uu;z~JR!v#tFDl?}ua)vu$>-1Ej7+0dVBUwC+}T zq3Cv{hvg9*$TUm&CR+^q!q}G*Z5!5|(dt%I+I+h@U(+%d-A>>PM}oKC;F{gIQFn6w z0alYRk*hwHM{EUy0FfK(K<3OuOQLh;BeDqCjA}JnNEfczFn-dwW7$H3yE_GRz!$Bn zz~!K6G>0o=&e^CAly^HstRE&H*>uTG*I=QH?hh#8Ynj*lJ^%Z!fB*fT|EP;_F*S3t z1SVVp)INF(tdo}7T4l_nN($P!E+(+_ zaS+HrOzE7l>qOa@m)}wFCL4EQZD(ja+gzf7bWN+(mT`>1{ha(#WPbk0Gr?bgIp@*8 z_9EQPdUMC}M=$I-(srm!4K9s=%-2e06<@II%8=E{_!#g`(*k;0L6E73s1ExV*f5@u zFoWwsZ;jR2u^<48G=$@KM(tm1M%#(l3)DKTu4Avq>QBpeU2JCU(XzkK|E0u|i)d{W zP`veI5Lz7C?UF9{X0H?qL8C*Ip#dDULTKobtvd5uuBN2;6gYent&dZNnD^ne1)VP^ z^~XNvwfKY-T~t4^Z)$th?S}b))FOiE1G)C4e{^8*c3YxBRF6K_HdYXs%*?IE0wfCtv&2ZL+Uzs|CIO(CYD`7i2dJvXN@*pMhQRrj?65f}N#e|9HZ6vx zdnaSgP3zXmWXF`;<}d@`6Wi?@>&>b_})b~?=*5a=k;!^tGmkKd~ z>Dtd>*ZUxSZD?&Z60Lq+?eH zK@_;y&mSx-05W-*0P4Ak8R+Sb^?gp24%D;5IW|+5L5mhIsDopUhPMspj1*P*ywOL? z61|yS82ar*&74|@dM>@lw>Ts@E8aD1qun%@)`c50aKH@LblrB;KRo8XUpH?1@Ezh1 za|WhBW^YJn)#0#0UJMf81%(bE6NP#9)*Fdp!jc%q!$D318EH=?33R0y#Z}Hs%x98=zYbnk8n&)YLRwI9jFgT1i1Y_#aJDKDTn##@ zS;ATMN+oAaH`}_ec2L&U@U+P*U@XiblW%J!qTH7DRW3n8Y) z>plx4ZQ2ZSFByMlLR!|QxdE1sJ$F~Zx%YLWan&!bcE4b|Iwo8VM1FEDU?Wemi@C3t zg1kSFdE^clT?1>1vQ8Sww9E0**qAd#X6T`jnd|7^zrbqqt-BzAvoOM=u(e zN&bv&bK=j?(->@dUvVS}epxe@t7}dPYvUgcg>X~aSN(z~y{x9tA=3V8f%ih_Vsr3C zWi|)FrI%OTYWM4b95m4xRz)ITzK`^9Ow>Y?^T)QlWow_p^nmk3WY=+<9I+R!ME9q^ z#}2W*trZ~~@4r^;{i(N;%gJaibOvm>H7u|thXd%jEyb)4XMOw6X(WmBNqvK7Xof?5 zFM38p_Ou|38bo*I@B+`Vl~|i}UR!|2Qh7l{n{jAycT{yi54>%kC*$q5F?56yuduP= z8*Eqj`7+~^L1PP{go-|Jp{orPRU!0slS)@#px$xCXdn2!?*%1=L|Z+~N{eu>Tf7GY zbu!-)3Zh)G(7{5isWMWxuAVrJO49Sj%JoFz{Fv2Hf81?Uukw?_?7O`#${VsizKs4; zPEp+lq&rmUj!407U#?3ut2`0rtHdt7&+yp2wc01R0LoQXrgG`TzIPL$Ru@`?k zZ0KSJWFB14C)0~gdeJ|vF!yZ8*-#|PS~?g52YVGO32gQGKi-I*fohwA^rmwe40P7b z4*~~DB5W>Mt&WG;HnD@ti4Uh)dhuY2#2FQLGcf_ao1+ez7XY$DJk@8DQfQ1U`;iVU zEF(8B8c}IE#QBlqXrX+MSk@8MVDScbkmBDpFa0|(%BaS}I8e9kleN!JsbLmGQ#(aMZ^JNA3Q`0|X&H_}oTt7D;p=vxy$YV9i}!$~5@GGlR&M#&;G& z9Bd1&FnfWTv^s!DS)DCSmpcA_m)9F-uW|;K+l}Qqz+aNJU_o>M{;M-M@H22-5{oys z$Pzvf9}2eLi2-fY;KeReO|&cS%hD!*azPRp!=)iYBsd-QwBsOqqXIV)4__5E@#q5D z5|}v`>I(q&hJ}BWY4A8K@W;0jM;;az1-~W0|B|D|2`Cm17NBfsdPHM`4ob_sZn@;J z4q?Q)B#hEXE}wmb*161N4Ug3=GFL(Y^#buiiGZybt@<|AMLD|C$Z42-h+ODp^k$c= zOCetFjt(4GvksL8^wb5)6(Iw%lRk5(;?tWWsNbt)Qij_~o%&3Ox5DE~(l&zwY1jDN zzTnxdkBEBl|A0+2NqVq&?3vUOce1%k3*cF>$`g~tGtO}vb)rR}PE73n+asBiNk@n6 zR1`_sxRHDHs+|MSZ~o-F^wfxaQFq)O0Ort}LenQp)Z~-0o@u_@Z)2pejf{0LsAjH@ z4s@&-wB-kCM!l$H7|PJ}f^>_>)XoaPb8lY3z;Yx=A5CF&`4VHB%NHWNjMm4er<+BY zhD9YO+hbF0=|v`9lZ$s&D&eC3BX+8uTfZ3 z{~&TJ6y`H*NJGaPJ|zQ37i&LKZjp3Wpnl@#Xrib;1uEnVsNY{h)4NdrCJ5T<+W@TN zB|*;;R1!m(YjZC_8}!~U>Y+Un>r)K+0lGaXM-b$}MHzQhbJZ$@VH6nyA49#dn<^ z@xt31Fk3MwFy?)e1BU;!G5}h_bm04Dzb}XF^SgfCplSrrsxlh2Xgoi_reBrT;oew$ z4ckKl*ORA{i7_U&Cy+Fx*!{V?!I%OblKi=7m=OcSDD z4tp_fBw{vD2K_txu(cJo(}gv2`8~@*FV}6aR)Z?$o@>c50 z#Vd_cjAJv%2D4}d8odK^IGhBH@%hSQ9oyB{!Ae|0L4BfRx$*3@?~@{VjL~^UE8z^EDQb+$YAW`_O1?%k{ z2s>_u1UYZxeB3bQRLp9UUs1vPLPEp8fFFYItz5W#ScTvHzo!`_-U0bu01_BGyN!|& z$QbxHp#SD?Kocjo{2*Qt2UIjc&UJf67p_&b|5A8+;b`$58DOq1RJ%RubuIGmJrifz>n|fq!JHLxOX=_| zLEFfY+08^LqC>;Ma?BJ;qRGVL>it<27tOAtRsv=uw*S16mAIqr1ulDXAB43R(@dSe zT48jB-E8ChIy-=CE_3{Bka1iSRZ*T-&P<7CzOfq3oCW>W)_ecG1Txq3w(Y88@Mx%8 zG5oG6V~v7y0?v#9(ZxLrI(T;n+q;4Ya6v~+GVrZ*IU1OhYqz3-_Bx4)DZ<2?p=>nR zN5e2G-*l%6YcN`S%P{~HWA-32Rz$yQK!9_6*eMi0CtH#L0RQw!L_t*Fcjdb5?}-F> zE!2x)QX9k^4vzy9C%BfY5&7OY{f?i9cDBp?@+U#tlmaafVQv|qz#vSZRX}HENCp>a zI|b#uL47Mi(`tc*D4#hwRy#<};5e<#pa5`Q6#sT63Qq3;*Rzz9CY)9Z@Kux6$3lIs z1Z!j4unIj?_dR5*qUwCD{^q-FXD?$FjP#_$7q^Y*7KPgn)@bZdx#lL$xqDZCtL z#;aS4xGC7a3;RN$wQZoMV5U+bkmZPNfN=M65l>{hipF}>HXdvMRe1al;)Zcd?mADb zogWo7{S;YiuR@koz5e1vxI8>YIF_0K8=gRy&VY8V0Nnc}ns!w{=~fHkHq>g}IvIgj zh2h*Sh?SI<&tWE}q#&BpC?>CY^r;n~>uYY!?DQqA4yX6M{l{~9<#MN1O4QJ=pI+St zo36y*7QP&p=DQcPp_VYj#O|4XcI6Ib(9&b^LDpNBJTp5T}y-kxD z69v~0jO!<}?k~)Vmr%4m@?;;dN`2i!{<11LxQmpb4qwHUw9h*_c8dgX+cpEtn!#iT zhR@({2xQrQns7xiCJS_UQ55FG(oM4o@pCUL+2_QzOi7mA)|!{D+CIisxc8K<4te(Y zmcYGGavn8^%?#AQ76wRc*NWnXFbp$?(m8`3Gw9mc#Sl+yt!7xRC^HIf5M9|6(bZnO zVlR@!vL@(i8Uur<#7o!W$@5-y<@@n`CaRy?w=-&@yUpcIoLFpKV-(N${GL1fKd5w<<%| zECcfnLJec^RGOI);%8-khmM@KVWD4X$ctxO7t0!BgzNg!tm&0l!1FFk#28dD%jlOg znF}kFII;VT!P)vC@H=Ayb7HtohOT)}otMGfRlEe<46zHV=%_T-6v^l9p*5F89A)fc zW=~d4Rb7kLsK4%aI!(!j`0uTpC(HQ#utlApx~4u;y5nSzzbNkRiZ+?O2rdDW*>mAxNdniM zXm%!Nq-kXkRGkE(d(q3Koc~j=79%yx*)pl`iZlgUNRLY_5{QE7w;bE}L$*4f3NP5{ zL!#xBK%Jo^Wp*eHe%D^@loA(% z%}6_3TSA(h3$T!?{vaKhR2S6e_^HQy5Qgc!L8V4A^~VYW9oi({tqUHwhx!oA~RX+#f?%~qt)HM=gkRuzjU@smJtrAC%&qP5> z)jb^N+g=NNzNQ&P2Gl9|!#x#!OPCJnAr#k8>-Viv>@QIB_Da(+CySe2)dLsg-ZFeG zP4s6UGuBLwoknn2K?oFh`t5LTO!26KGW!VXN(3orX5uhoTkp%uqXLH}aFd~kZ|O1#Rv+eR_Th+ z*;y?L2n}K8u~W z9pk6zXujNJH`Cmgts3#>z~O1QYNr-eAoHSddMTXXFZ98yx=a&HHqhZ>Q*nc01)nmg z<*IXdI-SA7QP`op(hv7zx6h|U6!ig=O?yJkDj>D9=`Gr92JZ{Hip`Se-(9+Ll$F5X z{I*wzv-m#0X6TP^ z{T&BK;Y^rjHGLT3CVYDDB$WGMAPy&ZR2ucP9FT6dnAT`QPA2bnQXmi!#a}_F-?b6I z6DBDJZ-dCk+!;~zsG!7G{Qm^vZ%b57`Ncj+HDJYH=cHj`-%mRR8t<^V#N)v#g1u}G z=3s9^W8utIqF#jJJTmwQ8LwM=i2HE}ub!6;jApXw7YTz~v+c}Ksz^O`m=gc%YLQy+ z){O!fuYQ3p{%vhxoZ`6l|I>gBFg2aOGzJ73+&qy>Q!o-G-^tYi`)+jjp}>zb`_>y* zp{h+ZMj(~GF!1-I(eDyIA)6;gLm389Lnr;C&q^p-WA4g%zw)$&g@Xdd(+?|L#lTB4 zuc~`jv>N-W-FOjo$l=yv7=p?`i`1Mx!G2Z^A%y|)19AZ?PJr5B;>mHqX+i^6CF#0jtp1C*54Em1YqP8{?|Fk&VO zNih|_CqUew@aHhCQBM%MtMv_dXQzx%gHf;>S1w$Rxc}nE;ie62$4t-33O%)*o>^p4 z9C%;!-2@2}4m!<+V2iM0b_e8GIZO@j4O;}CY&L!o@(hEI3a@BWAE1Xa+Kp8|#ljcQZU}AzpF7=Ei4T(aY&x5=jy< z>kR*rYGuq^+Kj-xoCQlRvX02bfE%lOQRhah2~f+Ba+52pyy}c8Kv^2x=YaxG8QvIs zG-Ps`IB^GA`8hYW=QiH=R6;anv2mKa#{dZ-YrgxFpAWY%okvzfmZK_>{8&p-P=|4o z?LlWoC(0vOkdJ6d+6XsO{H(>+ff=GzP=%q{#*^QE>b^DMAoq+;EOCb3qCcti{mG@x z9;W(5h{@kz#%@(9kAS%-HGqZelIZ3a&<4mUrN(13z`4y}Y6bik)p-uhKpU!yMo69c zJIWTF4(->0BZK1#BJX<%*IcF1UdC`q$9z2An)|meHRB~;qs0~fsEZ75Ysz3X&QUU{ znGG0fgxP>b#$;0&5(5Y`ezg(t0^jR_vjegCT@It~uz0~a`7ystK|XNE3C1(=RR4Z9R~MeWwXI3Ia-bQqbKFk7%=KRG~h z>7(|NG0MzDDo!c5Wk@4LzMSXNCY7VkoVd;c{Iheh#MF_IFZLKKE$66Eb_UVcIXlx; z_BoEG>CSBAnbFdL58gk#bxMt!@%9SUgbe&2Bg1Hef4?x?Ea<1YY7=JZyNV;LsGWVgT7D%en-#E!nBtZXQ%shF_a04=e@8XOLxa*;} zqg~XRa=OX&mdX9m#=SnUQ(y&2;kg&Qf!1Di^EMimPut9*-TK{H2JTIviL#m#LzlfUX!Hwvj3EnoNHG6b(xDpseQcjrXQi$LLY z5i_g>GWB0B;q;&@9U3hPn|t&x{_ct2cT5$d&1TNgnzQw(HW~qb2As?+<3rr4ty?WA zTX=I>)p^ol$~UARXBZ9Xa={(RrI_5IFwmXIt0`o1LZuCuJWM^3MgU+D*`DKdT)Gw< z=g;{3YstmGvgnJ4fq?V7j;0Kiqn+rhQ@WVQUacEt8(bcLp| zifG)a2q0I`rPeD3hb9Ye5b8N`=M_J10lW<&&{>=pzSE3r=yi@1OVNTkGGN#VMh?8e zHFz%C?&Iy(sS4Q;OInx?j9d+gEq;>i6ururP zPzuXJ!WdtRptGSs^>`)Jp)fEcmg)XEFnO4iA_aJTdP$L_97N0WSk|yHQ%$7x%p5vO zPXbQZnAFctniD_sRF8;K>73Cb#WXpZ3Z6bl_(|lpjtl{K^nX&l&ITBu(n#JOOar3G zD_b-ce;2#ukiNgzp!#J4ijbGld;)i?x7;imm7%E(V-oGvFsC5%nK{Xz$GMNE8|3aT zq&PKP&m4bx%(f*Rp>Sxax~bvz60H1GTILx>Xeg%Xu`;fPyUMJSGs7-~P-~A}mz|Q+ z%=+6t7fHt@W!$yW@7utN=R)JL%2klXI2s?~k@k%`YIN1W$~1}pEL32mePSc!ixAHt zPlQXQQoGFk|M9ca-5%f!x#!U6VxBgMku%?>6uyI*$Xl?pHVSmnl{o`N07#Iz|D1HC%eF$eh$RQW^ z$f9hi4w{PwiBe`mSnA3FGt}dj>S%fWwwAiluIUCzN6Fwkg{{wIH@LYHC~_ye1x%1E zPWMU=lJpGwQpbPmB2`c%H^r;lqU9pPnICYizK66)nI^I4>cDDZwKh$JVn8d5f2Bt zVDm{lSZjmHsp~*_OPnga8Ii78H!$fiEXAxRyUrr@yziQ}$o`^%qQOawy!v1pJQpZjJ;6MBhi@QbQyuz4UhhHFvVKx!UK=Jn8xv_C>D!W+1|O3-!5BWg_ULuN~WuF)}) zI16eOHWL$U3&}Sdgn16Qda(?VwPbJ`LM(WgemgPL3L$j#on$fXYV~)Dcg+#RB3_ET zu=0|Vw~hc=fbr1=5|=%hgbtHjLMWr-h%7S_1=5+!TnXb6>{}6|ef- zx8nwKPDpyjHK1T)Difhw}_J_PWo(|EUN~BqG}6vl?f@p=UiA z<1MxFxa=ED_k4?LH}MK1s|&={%H#|SKTDXebnHZTUp6y|v8y;S&7ilu-upoB-T_0K zo`JEPV@Biqoz>&a%-13VED;~+JBvyPs|2;MHE`B!_!C-`f`G6L)T+mZ4>hEqbXv6i z^m?~smIIci zA&@X-qe4_3lpCaqmw!aFq$f5&5RAdB(pF3K3cEaMd^A{0$;`jbscM7;62z5+Uis{7 zINe|4a?GG7(E^eU&W|qp^pd8&d4HiTtX{EdSg^tC7}eV88J#SB#B|8mJA$P6Ru&0 zxY0&Afcpd+mT^?`Sa>`$r{t8wTe~;@N#9U7IO-Sa{qGZdUc7g5>gMWmG@ z_C~FLV8Vz_b6DtgLTneXiV0y9g5=O+J5g*F+$7jP;t@L+6AlAAd;35^%zAh+bVahT z*c53q!nth}N+; zDgCPmzjuoWJn-d^Y&4mA@GAZ@IVR~yj-hvycHujJQ#e(8FdffAWIQKOH~#sF*OQo8LK~DTyb7#xU6K&#^9omGWR9jWY02?z3~|9D7+Y{np`~FnzXr^xu7XDLH1<9O{F41sHkEbM;8E0G)YqJdV zFU3`w8dGIp^JT_&qbzNmhvw{{^LR6#qtqxmGT;$P(7BUWuh%AQZIznC8jl}vPMqKg zL?_D%!87M%Brl^Bx^d+|&_yW_G}xrs!)8t|xhMJZBsrxK?~N9~!+|kZrg6w;pl+cx z6YBMgGq<9r9`rDGdRgf3v>wgFTGp@++OG5s{p5M-5vyaa;mgKQy0DNE(}GKh!p z!X`AWW#XjI=s1mduEJsK#iTkbLCw;)N?<)PTaB3wHOFX%B;V2i=EUfyFU-~v8V-|m zZ6#tZql7cb7u+*~8~$ibV2%Tys|b)}vcz5<#u_z%$&e2s@X<}e<0Q!5H!3UzsxBe= z(Q$UuTyD-*1g1j-Vg`K1mP7Hu3>hnHPg8O-*`VMC6KxPDpR0zt4mz}~h|{ZKI@(Doak@3=Z#3;me~2?`@Z zVCl>>ju*=-!(3*QTxf&cPcwpO39#NmgkxGHnom#ixH%JVg0X^U)LD^=3aV5pDx&rw zn@M?8DlmWGnj5MV-z0XJ=G~|QSQXw_<;h8>lScH#dZkg0swaapcr+H9mp{;T8w@83 zImy_ao4Z;qh_Q0w(c0IDg!Is^(`yY+q5g(9CrAzPSEksWK^eDJoLEJ5xBV2)#1LnKFB+zwO09+^am_34W z&Zi4X64aXbXc3bhUYc;Qjb*9(ikA*D__xes>4{dP3r>S#zc+BA7Sx|w+%O=H^o;i?oL?2^OaK_beVJPa}_QE z5O7&Ul2nFDbNaaBctK&g%}WnSq4C%ns=5G<3(!CY!_q|Oqe^Ti_mx0GsZ8r6{M;4f zL}UF%=$k~^aP!2GDsY700}KELUhax*S)Zj#FMt-pE~fi?p|>T~Lwkped$K2jSo3 zh-J5&PzKcfkOpKd{iWF4*BsTw%T;?4orsRHGzhoFC>KE6SaGSjPU643L}L)cVV=K@>OpJgZWp) zZn#&Rz()?5nVSrij($M!;cj48R_CJH{F0RCN6oCAs~)Q$ z_6!sy+7qf^>bU0hb3MFt909CuGIJ&vcOkt^^D-SV^uB~t;8@4L$Sfmk`bjM(v^in@ zuv8I&pyEU|3-)TZ7%9{oqG-XSg{5Z3-w)|X6d(ea_(>Zr3W1^=+fxZji;&S=j=~po z8S}VdHqg!W7G#IVP;h$zho?MfX67tts?>ro0L(5%073lZ2_=*TYV)5Vvp4;>ubV-= z>+SHPR25*fgh0r~nOPMKm-jmKAkzB-t}LW$GQBYanGEC65TRKEu=fu2t%Mp#K>foW zEq(oq6>~LI&pG!0syCOdZk5w6j{lUp}x0TE~Tz@ zVI352hBgx84OwhflWVp#fr|BjPX^F^iD2 ziS3&ijRcp;8JGbu`9ew(CZb*{%eM(DHR>0Lem-ege_~bOXGZ!{-l6)pT>76#!JBj@1YVbYFU7k9du zEm6Ud#TAnncXBx%SE7Dcu$v3TYDE~4kn+KzUa=Ol&*hWcoUw8wJqbLpqKEr>U)Aoo zx<7|W^~wX5!U!gtM`|4I#dhSevBOb?5Yt0-*TG4#o1=)+k)>_tVWo1?xIbE>s20K_ z=3`>@Gtkxp3o*eu&N+0trP>iN=Id=5w;R6D%mo^_zktBZIgS)-Ibn!&-H_$xG~FF9 z1dSX`@%1t%!&G>Q)n&nE&dvS6{Md-wwvvf`)P%53_fI}d=e`x?B#9fIT75gL?j%~w z#G)_BfA3A(bE8=q!5HtwWE()xMCQBhj5em%!bFWlEXl|%fh|mcnAOr%#y-6ei?=OF zY@>lv)xy|8AafGGuiI=%kxTD{3neEGe*9VQ<`02~Covcr)zO3;I@eXYYYf8}t6~B1 zH+ffw0ehUCME{bnuzHt-tFU}Gn#_R!owGv@{-OLh#xJwsv=RQj(O2o z9rxR#A@X93umpSJWO{z-2~Swg6yE`j=`#tQxVmh-^Z#7U;@N{#!|4e)ZN;}^eMiA9 z;czf(5Mf_pk{(4C{JmdR0P!*HdscG%+ZXwCY{=ArDzDNoFRa61X;Wmll zy49AHqv)PI>3YGDpD@~Wg!K`aJ89npmiZ^n^_}qJuteT#8%lD zI!6;AV(dP|-EC_gPD_!1P_TI$;4=_)8@_XKf^R+=gto31%uUl{C7N38@a zX^iSXyUtR1l}XW-_wXRw`AmbN3hl4T&FKn6$8-x(lkc7YF)mLRUb!?AL+9dgI>Rzw zjM6Yfg~wzdm{}IJs2Njdpr+uy03Df>sRteFKFP zP5%A}$J)37Z;~4>aT0J;%UGupuF0lHmvm z5;e0*1U=`93Bkn|5nl8Mg$-JTO%VLCxc=c93(B~^j!6PKL0GieourYZSVn4$%=ws% z-Wsq*#&0RnG?{rMc&QvCiq$t$R#F=~scx$O&3miacy)+it%Kc_h&(IW15*&L$~?35 z%S}RIdGzON+uRRfG`)sdHfk7pp{^v}C3bzJp?c(X5~U zI?OUdqa_3Y=}09K8@HA6Up$#HVp*YN29E1pASc@p>TAA zK!RJelA_*tz{?0*t)i&yfAnI*bjkksvY_|#6khF66KGn(7YWcFV7kE6!FbVT zpuh~Ef;*?AO0x<8jv2&QenptLmXBOdw_0RB9>7@E{ipfOU|yfKb1^|Knia)ND0EG( z8#wP*Et1?UIf&JhSst%W>9JM_12s4fDX_@rsT9liBIr#SGs6lXia+EXx1qAc2*~W% z1remo*j#2)^I3L}aAG?9G(qJK0`vp$k0AZs&Fu<$t!NJX;j=Ch;k!meUWQgqvyg)r zX4d6jZ+;k@QKgQDgt2{+u)YpUDIV4Hz3q&YB7t3j7DSJ;$T<%e27l;EtjI{)YKFyD zjbo|!FUxSO#6)YU42uxC4v85mbXwtzF&K;@E(E2XB@Bsx#y+L)QVgt02~9fP%r#yAtP&(x5_X ziR&TRx_Ku-Yf`k_{B7ftlGI0+g`cMywM>I&a*iMD#c)N)tIut!L+MHh(kNcB%^dQ` zfEat1iNxMt!@$!J*k0Dg1HNr%|8e7ElV3J?Mh4Pz1gF_bgcUBUJJgDR^X><}u4uqW zrgjRA3DNAbk~rRga1qZ_%XgjqiLLrrK4e@Of+Z9OGfP%lHXjb$D$UbD0}RakNlrr) zWG*uXwolNep>&AZ!642x=vuUiiZO@_*y9{V~Jcc?`UN{J_xp%0Od?6E>VuJ&z~voDqOd9E_lQFTEL{=Q+)a9{)dlDBG-yh~U?!eJOEks?ivDS`pCT zjSrcVXy~K#vfe&ezo;8UtsbCI|I|S;#aAVZ%t)hkbQPf7K#?QE!_|PT4ut*M!re+q zVQZSF2;9);fPo@^baZo>IOAv*0u)c;k^vLNA9NAA%@F4wr$ouHkDP#Vv_tG%F~N)( zcyIcQlR)vOw`shv8Wz!NGo(bV53y8CJnWjjZtX&Gs+HLV!oW$;c<`KbADfq%IXH>F z3)Eywk%@=5Y)h9>7kTw^DRAo7lR&RbBI3JysZKf5CBPGfI`w@V~ zO{bo7cA7v$hU1g3R-*ZZg5h7dc=PO2V6$!K^9OFFz{^~lpT&PX}tBtf@w7y z6C#BTSMgl&#QD3>aIrIxCw;^nOCnzy$}6=SEhmZ>K?UA3f{NDr(0UN7tX&2O(UB;1 zAynxP9I>g1(n?#|rNWMa72l)4wA&gMjX^fvm>I8i#kFITD)v%>m5j0`TPN2c%d5Zf zVo$;mSMG;iaT1^#hMV@0y|C zhA2)M0&rP6De+Ul3S&y11OZ@BE@;`}=+B-&ag_sEHIq3k5TJloq9uH$Xpx}Z5@b2} zu|R7=IEk!;AoP5_w0%-DwU^q!wm=N+=y|{4)(~ash7r9Db0{wuNtv2adVDzMv_Kl30;V94=--4Acf1utj6n zY_{c^4Vr|W7GMStO_glteiO0`SySSP<}(y1b)r*3=H}wzox`gw^~fMn$%LwVu7j~6 zT&5;?;cIcCQ9?TMGT1`07_796l4ul@Dw9QY@o3)uxbzw7`)9_AUlePnBW_C9927u0wi2b za)h-BRqYI;PA6FmX?a=dhnrImjpL?X-53$K1_xR1AZlKelJ6wgShrD9NDOC`SS|9ya#1PLv08>$*4rzD!#* zQZK3crAn}bBE)WHEW|~{p{q45m!OG9RQvxDVou$g{6J)hHjBW}^o;z`%P~?^bXUhJ zR)GIth-%#|3mDl-fnzZ&UikT34E>ZogY6Va`9ydxHL-}L)=vl+el=!c?n`v56=YMs zHOp=V)Hqv=vs7gK#X}s_Tmi|32|)ICM>Tkv`7)8vd=t_2Eft+q`OqGgNB)_m_DzEN z-Yb*>(cE5>dg{z_XcMDzLQ*nE!zaF=h)O<>GPfT9Xpq527J03m)EZ<-j0R^wQ5kLC zbOQB>j0Y!qBhcT5m|BooZsbTD&9*Z#mb!1Xn116Sx9LN9MzDnG!D(f@o$^A{=vWoM zTQnk&`z0oWJHu>Uc?*OrskrL!8JaWxmyH1Enwu}Wv+l&78nA zGq7RCG3X#h<5>oZZ0x1i%Q$K@3Nl8#$Rd~>jW>r&fAMBKM4Oa^mhSQ^V;!8MIZ;GK*>vHJzF7|bD8$*Cl?OEQh>CAq-dYt-8rvnPw z!H)y|V_-6+@Yr(wAluYhX%z-ybV67^%Igh`t?LiH{DFZK6jK_9=Dnk>_{KPBm+^>?$i zZVTgSP2!$%ZQ_VC$-qtFDd#Q?w>vy~vpz&L2)PYxt-(vipPTHL&3QM+Rg@P9jr7iO zjpUUPiG~pmUk@yc0IOygvW}hnE)kUmAZr_{k!EZ*NM3Uhs;tSJ^9Dm-UP!kbrJ)G6 zzBx0rF5?2@BqWtLClbSM1FS>1e5^}ohBU|o9g%Ks;x?r#AztRGwE%N}`Rjcv2{(9e z{Fssy+;Rts{}tWu_!$yr;5P|2gT9$n(X?8?RSho{BZXM21%XE zQh)1ln2K-Lnf<=hWRSqZ1>k$Nk$5;7V5EDu6PrxS#DD~_@!=Bhiufe!6$%aqTpm&K z;*iHehDzbzl3h{8>XB8OPuH9`Nyf9@_3ej{yX7|cb;re%v-FN~OWZk5N$kM# zp8(l+#|3(odIRF=hX_5W+^o1Sle$B~tYJ-8pJNX{qn_w&Bg-QoG4Sd_*oG*jvdbh$^v~$zy!9g}Yt}dP7E`m;$nZbz}RO-d%D!2tm zkh!`^2nE~w-+YEH=emN8ind85e(B&m2phw9QPULQ^kGmI9llWgs)QnP0M7IgPe+@= zP4{R?X0NClivmJTL~OL>np!8`ZJ_n(VvQ348-qlvKt~x8I=8B_i9mpO*IxL&nx4Q& z7eoqsrPoq9YKX__6QDq!73zTbE429v6j$69jZ?}Z5EfWT*soA5wipr`@cV8QR^NBh zr8x^_AS0f(y$0i7!$8-wN5it=bPl>SI#kxNty3SHO%uU@U%rUzIZN!VkSuA?w+|?B zd-<5-$V|IV&8pqnP~tB5bd}ek*Mk(?y|2Lq?*{4!3_#vV_jsaBdt(d{UmO{PP%(vrg?X3bTqzbKGHxzGJeLSg z@b;%YAhqMjoB?x)Josq6B$`=tNeU&#MQj2-T!%p3TZ7KlHGB!%so{|#u}23Ql>d2K zP;zrLK%PU*a}?#9s(Da^6o2=bld9p#2*_F581mO=c6|_wWx$CZuX)S@F5`e*EEi(? z7AVF&v?h6PSuF#Z*Ve|Q!e9-(xH^8o_JGFPbsJeh$j@a{Xn5$J&1%oK*?>94%eO^E z>)qK9Dq-H5^f;$eSvj={!2a3sBq^K0c3`#Cq040j3R^Mb&0B1HY@QeyGUY7mpB9Tv)r4D#-yrE#Kj~^rF~dW3cziy zH5)C3iPp*~2^E*C=(K`N2{*ZE{SsQ$UNd4K6vCCXjNcQgQ5DuCFI9eMv3Lb?U z{yypfF|sFqkN`+jYBQYlq5sS)6FVwDC=rNS1>r~CZWn#X8Ggyxf|``Zv2;Z!>#Ajn z7AFM(lz>(1MQ2C#5oJk#N(lu?GTmw&=_g}Rfy07(-#~~XbH6YCTftL~f6T}S_aA$Ecx^#R_e&&|U3KB^2E)AooX0ajwy1gy8RG28y|8WH>Lfa&&=+3W8O{B!Oaw$iH86_VkzMV zt0aQA$gU{QpUiQ@gs)4G8jPuUA(lP5d5~ennOuA&)CVZatxmU0c>ewOfBs{$fn+Rz za$upbAso%RQbiKSmx2E{v3?`(q;?+(LGbM@Y(NKYeDECae!bsG)1*(*nu(xfra4r? zz9f!F;Etj4GjpK=NkO7x%S8eZL}zL70M5Xx_n9f{F00O@rER{Q zl&oDOr29%RIN97CK_JzwG~Jm~-qJ(GXBxFHW@IdNO+R_&|M)*KtBROE21`6gOqU!b zo4`A&droLuuy{^2^Sk8$6K>QsZ~+MQo-&~2&L(YGcZ}p(dYqQ7VL3Ds7c5EBW_%K&q6ZjpDjHZT)RFHVMp zTU)BQT8(n0Twz~I7t!Fxlsh;rM|;OuUo^_aQtPiiJTBgi7d{v0s_b$k?U?eCTnmCh z^TO!DXxl}kjEdvjDen)n0}o+It*AGZ7zCka8(}C%&PmkvO@J+L0&r2?FMaMWkb31$Bxz*vjHg-y(!?QT9>V{`G=`dA{6u}r;b9~9(f;)TxD)TcLv`*1P)jCuYH$I7Ngr3Lc%b{89;ds~nP2!cP( zdm*x*qu_ccL$tg+qIeg;Z-{7@CztD_{tJ^s(4D!zGP**ay(cyBFcOC%rx zhr3D>n{@B}Cm?4HFt}(xrg+})aOgMJY-Tp&bO$S~>x|1M3YQaZ(=JzH1M<}^a)xc_ zgNfDUhWaN>HCmWD<_$Z~k6i2zpU?ZOo{12A_8p5FQCfIuc-DZIdjBZxrnXU_6UL6w~-_)DrHaY68E2nwg<(;}=wp^3zC!%X}T^SZ&(9uTi zC8uw6^$HNfGdzC^IO1j7M)D{!-d&;f;DG#q&2{@pdBu|x>;p%Ju#fU(0LjPzo3jx1 zG5I!{y92%fK;A^Ot1BZ#&3*5fq!BSb;F!-T!_Xxz*E@2?&1gi*!lA<5C=MGD#lYMt z$UNvltwf;x5n%u;pa`^&&)0Cin$f>#_uj6o4Wx2__OzY3N~;Om$K|k>b%-j_4r3w^{p9k*sdXa?r?;#|LwKV-Vt;`Y z$)x7zN!IMsX{iW5$QC(y8WQ{G~#2|p$W_DeO`c}<1Ym+yjrAA5!4VOWUlp-*Vm^B2R?-fKTsV<`xDPF$5S#4@L@CdaCNRL zYGX>A_)YzIqFpRayDwOJ71vw7{(wMk(V(DY6gM(wlF-d;08>w~m#WGvUt^HZ-QOcgm zIK)L#$ap`o7C}oi>o-0h%vp|42OMTBP5zc<#07y#W{bK$?1XK=rG4|3m3ExZ6==k} z$9g4Bb71aCG&dNx->)#QLxP&o)~c7++=pPnkg{|@bRfMz|k>P(zbDxb&>jM7rcG4Np;o94*5Dx|$9yYKDC}_6# z2^&Ztp2o9H`Fw3z3 z`3$SYO7GGc>cW0-?w)+4!^Cwm7{P7U$UDajEjSWxuk_DT&5XtB>imP}L3I9~!BoQ0 zd+3d)Yr;`LMgc-Fb3fzE7`X2N)@T)31*4JEz0u%M!}}K9ZFj>>*cgyu(bD#MsriqJ z=3pn9X_d~sk(o7snX3*hG*V^61%q%IEbP*#3<@}|+J*EXwz{7plvK>Tc>j}iLNZLm zQ01d~|H6}PnI9!P+y2QQdCw7^!l>Q(?xgCbZ3tpAoYD1VSWGXA-~dPe6b_>$wT7G7 zft|7+jQcwmb}EO)^9Y9)77*|;7PD?W%S}&Q!9vC8CddFME+ZuoFH~6YD9KFYW2u) z4+u@S434@!iC08s_VucFe@W^V2s*`H6DUNygV`>ip}I%)KXnOYupvq6t9!_hL3IdC z)fxUh5IuBsFy!`D-zh(0f)iwyZKT6WA5Y|94=zQY5giU!F8fBa>AvlD6?Ds!iR4nb z*>0aW-bdSfV8u}f_$Wv)Qs;9sX;i;f(3jMW7g8lG#V}e||C^XshCvvASigHRR@(v& zw0}wG*on5wuB4tA+`0vHXow&Pzy8>a=)8;<-CiWbv-aB@C$$0(2Rzp4BH3J6JK>N- zJim&Ak8l!N8woGi94$}cU1{YCS z@k3lrTx@RUZ5y7cWS^3R<5YeK6L}$k@WYw#e$3%fvP7pn5&m?Aqjl$Yrc;%!Eb>uR z+4qz|U3gs?Shq1+D(JFh^9SAG8YDs^A~cGFHV$?UMYNnaHK~tMchtQriy`hD%MTiE zwAu{g@kqMS7#6TAX4YZ@>al5X>4b$S<$=hEzt6=WgK_mQ6-QQYn z0#ltK7}4n4OJ0U_X&Q)tcu@>~8Y@x^{YAAvMpj=2bLwuBCJ1sOs>`ts4uKTHVd!hu zz}#(SGcYuX>;fM3Jh29Z$VHg=g{k!Y^k|fXF-ic-h6B2zc%yR{Q!}lyZ@m>h zw-f21`F)#I<69BaaCE{`%eO#1Jz1Z7NMrHnW*|U!F=*iM<{X8(u^>$Voq;em#^Lc{ z6n4}JH5$Eu4BZqH^L!v$wnzh)bf&@@&yx`=r`r%BVBJHlD$)u0EwV+WGG1T+$7Ka4Tbdl@4x@^pT7-U9`T#B zL2biI%1|Ov(G%_Sw`m1hJhK$%XLwHs{_hT4kB z#~;NGuxx~A#%St5#r2Yb)g}n;86?MugH$x4G=v8olnN+QgfK`UxBfqaBoX_mJ|R$u z77mh@gKpPsPC*@&Q&*w}6^6Fr4&`Qf$%{vFwfVZ657U0I>Nda|iyXIR;<8>V!8S<~ zl3Qgzp9PN&;@t#*d`W?CFiY)H)Nn9M>@oZZf!4R)cBS5hG`HXr4f>nUiR30Ae$Swv zBUL`UUuEoDDSfN-s7UBS9-ynp=o1IOX@u$L>xl*h6TNHpenrQ6h3W+0q!n8Ba=g4k zMhoSu={W;RdL#%@$s}A4eJvZklU`nA=!{WfB@@>2aQ-Oh@<n8;%&S**Y~PLjAgh5*&)91n$$ko zq+hooE1GgDTkkpGnvR^%+vFg&6&<%!-oaqyA%hF zeh3!3zq;rerCHfoC15Vf77voruX7|x49}#kY1jai;L}Xin~qVXu{Ry;R8>-i~wmr z$7e7WQz8N0lvc4B+|A01=D-(-a^bdY^9F2EV4?W%;KWatzV;sA&JZ<(VR6C`+A`Eg zolS&bINeQ)B*H!4RvT)bz^W!stFVk%wq`OI0(EGEswJe$)&K^cyA~y$zVU1a*lV5x zmcB0&C1x^X5gE{NQDISlU1V!}WfCBvkVeP{i>AA-#-Gy{<{TZMM}MI@mW6AGBV=?J z#Q0~swXAh!S2mpEC#;EVX!fij%@0=Ae9xa{K8U6}!4v#1zxnjDAlg3q+i*K1 z#W#f6ASz;VBq@V*PFM|E#JEkP*(^?)YL>}07gI@qX#E?{6P02Yv5qP{rY!i}PD-KF z5kdL7Gofi`Gh1LXkXmwn3$Bs!^vJ1K1cH5qB7C*4&P5wV95;s?br8oD$VlhxxOXW2 z({R>Be)NpHIjh5k(YKyg7T2(<@mzw$bza%>Dvd;%;Fp~4a*4BKejQFo9KveeUX5b#MEJO2{ zX`(p8!sCph<6r;&`|tm_`evI~5QA0D1TKi{G@!9y*kLvN#(ppfVCKPE>(F1d0u+{3 zEkeI}t9RMtF|`~`&rP;-gp3PDrHZvx5|ZARmzt_rX8K3WWR+kSN0<=zll2#a4XpQ! zA^vtZL23o!`?4fBOk$elbvkNM+u4Tc;_23?q5Hy7?1u{EigKD>Rrzr$cGn(I>wHo9 z@wTV;vFNggs(-gNFIsUwdevALK-`uh)vMux0(V)#K;eHPD9=S3GaskHVBxW;p1Ztc zFhkj`pk(6Mm|{%?+>5i=+X2Z6#W4mC5rfI3zcJ5A)Yo}?X|#zMOcZi+1%E*0rISqL zba(1k-WX|%d;;^zC z54ZgmSP;QC9dV9Ta(nB<^W<7^;QXVEY3Iq_mec@EG14J-8zz>`7Mf$`Xy8=uRWL@P)2_9}j97&>~i zJ_=76?6Tdqi0LXcjInRkxT9+1rAHW-bn!+BFYA~)(L5wtp25{e&0?C^mDSk$cO1NW zt0~lQ@^q6_K^%ssc696ZIEjZhH%p-Bh8TV@h^{VE<6#ljupMoW35AhoMSqZ>`6z2C zG*AWly=%%A=-zwFIw9{X?=rv|cm%vxv^iavz9Y33G=ep_L2=ceqO1&-cWZ(c5_Pi>b}5L+ee zaovgnr9*S;Fp-(}slHOViv8JSivx--HzxuafOm>3uuZnz<`9p_7NMGEY3q5Lu2XQ) zP7l?&#?N2##C#cDDAi)(Rao5aVH7Pi7hE;pJY#qh_9c&N8CPPGIOUyeT4L{K>@K`x z5bQ*fkmbTx*GBg7Hc9jJ^351gy=_@PXL#EuI!bSN&fr2goZ%|Q&}UY`Q}2(ONkTi& z^$o~F0lOGYZ!|`h zc=iD!QU9^4-h~2~tMd#$(`#_|a?bC`=9E`-yUpuuvx$$nIs^ZMH`m$N{xgaX`NyVg zCBF3AOT%$QAMGq--Jc{E8HjaJIvxNP&lZ|}${li}Mj-0X;6UDOy35dfZEsywrY2h( zU;Rwp@22&=lsSHE0UQk7mU%;T8o3$T$^gx7E&Lga4id0edW8Bh0qNFR{~|hafADL7 zLS!jdjv+rVaF&S*9Idl%0dVxfe1P7AMK%@;{`(EY)e?ajMaN!|cvxByh4Jc^xYgtg z%-~7Fd%j@;H_fH-E9R5yiV|nFiUQ{;)p`v z8>3v|FPxB=SEB>DjA;ke6l_nD^Dd9kg$((^t!ISEguWN0nSWzdwUY)fjy#2%*h*C1 z<{-fT$IKMZJ*r5mW~^rSh26Lx2;KO;kT#7;d=EaBH#Y~EUa|NA5CUaC1TixH`#*pG zCz?^c_}SkY1{ffW$8=8)eAj>?+<>2dt)jatSnHU!qUh7jaEg^Q$W`YC*^_*@Pp;Hq zabaZ>2^1~oA~sWOUfkhr&&wdwwb0xXn$YL&P8GsXbOKr4s=3m>MZ_1KbMLcFw5a9dpg#Hy1v+UOE})lCVV z_SNQapUPmEW@?dSH)zI8Ah>Oar-d*nb31Le9;-6<=|jyI_AIS_aZI1oT&x-uWo@1Q zce`xXvUAoCz~T=JrUcAQG2qD!MU(WM)B6iy8A2fY5&hQ+0;*bq5GS@b=ed#BjD%Z5c!(lmc{}O7m37btLi6r?-N`}IX1;eGdMu#F;>&p?6Ng9kNO`jM_ysE zwd>DLLEr$kpP>|EtRW{iVC=#C(d+x*k-BU~olA6|q8g5d{B&IxCdQ|ED5q;GA=Mak zt#iqx*Cs)Xq#DuI)X2T|4|2L^#ukC3VY3$Gb%4?^6^)z!=@F18Cj69ZZ0J%styaTE z(3u1AeQ#>x3Edj!Cfw()jBQHn{X${{@R6>G1Cz~ZkCHh$OWQQPE_3Ij-BOcwK0muD)gCZD}r(w|)E{lA@ zM3JHcNsxdNzZlc)E}4ZFSF0d~0=mG)jWj9%Tc_?nI>5(hdnZK>yT(rRoFb<31Im)V zw*;6AWo$M+;+*TFwK&M%+p5)n~PO|lpF_kR&&~y|3ovT8f<2(quEA`h(-(Q!5 zuXtD$E4UM2x-z7d!ROT^M&E*^h}isz-8F{yh!xdrh;v%?WAy5EZ2M}rE+2+S z0#t0(rG4At>#BE`v>;+7di^~ow7BEA@?)NE4QBo$JUMa@?5@%jG%J9UMr_tPG)1hr z&FN$>IuSjF(9Ox%+Qh81BHC#B$*aut9JZ1*Q`y<8+qE;mKbm&i@zQ{ z8MCzacYI{8#53#L?Lr2wt8kY8as^f4u*=!~7+Sis)JAG*R2o?b_5g4~ejP9s2khyD zfyG0TijdxwZ%;EFsUw5^?hnA`t)s3;lHG+uFoMDR!eWB#B=)H zSdtFR9vYCHUI$I!-e+M3_B@pFx(iXn7S_fTtsL$Y0R}MJpSP ze0LuN=tMw7jlPvOL~JaKqd^+lUIQnVG2gYcsMMSZvCc+BLq{expuszg8`y|Zy~L7d z@&+Q(84O4ooUq(l8+;>5BJ8KWxu#Cga;6a&w)cKfM9oAcJ~Vr+z0&I?L55nfd*3JC z(0+;g9KoT9CF_ggT?36D0BWve+Ukvdg}dq^EjDhNlqoQ?0nP}tFH~2975ox%7%WNQ z9D9p3SWm{Z#p9x$V{LD0o|~#u=IqYb#0XpVOL(!_2C92@c)2k%uvms(brG(tXqOV% zZsr^-A6(@)!crNfFEyXAA*m`Z-I4UE0&4ae{?FGD^9o@912P;{33V!QX8TDw>zH@Ej)Qw04?h1c~zxh2$Jb zb0JU>QoV8jt!Nepk!m#6UYzF7?6S}A<`NdbFO z^$3bj|0o<(LgD|Kis2{7YDGyC=Ida`neHS=WQ}Hw{qUgauXeXu6_=VrR4SK)`NIk1 zeZW9K-svqVVcOL7OeR3#NC^niJR<50<0}(Y_ah_R-3?H6O~^*0%HFI z{WkqYVbxCu@GwMsI6DK`1zC2Tlg8YCe%mn~(;k4vj9eZhC?z_eMXH~$%ir&1xh~O0 zri*miBbUn>t9)g0OVmm1S6S(569?{wcuvq_O<*k9aT(N8?Z)0WH%pMO5k~jJ0G>i}5{Cj7C`}5qdnaIN z4^G0RRHVj7QuCx>BOrWoC~4crLTQ0?F6Xua5Eg;?6v zf(C%-`o^c5_quK3Bhiy&9MPLFI-CA)|5xHsWa_n%0GwQn>P-^+psNpS-zKt?)PSy@ z969%I=kCJNDozLeWW=&Kqquqn$sd0{bM+|{6qS)X*A)b$zZsO!WxxW)WWiNl{24;w zlvq&Gf-_+7KlVFw2Kl*R6;#)`O=?RfB8V?!KJ|?tX?pQRiotm^d97iG3rNyMHn+=`%X=hd2~;y+ zk}YI7p(V7>EyTS}3zyyAsAqbgmc!X^r?_OpGP3-lGs20VUi8XlBsQfv3Ji`UD9RUw zK`;}M?24hq%a}yo2ZE!Qvb(2KoPF9vQ!NA^c)w8jc5vnH#V)bn`!PJnF}eye5US(j z-^ov0MALIbL5CEDJ|2fzrB!eH=0y04SwF1np)Kj}kEmJ@x)XqfwFIFSz(`ym*|s^> zVMz?Mw6aL3p#>e~>#2jueD=0P0ksi^QdTF#z;lXgMrOXDVG&$dG`Gjt;zZ~7iv(jE zi7&tfO~GWfiKa~+uLOnznHlkWBv`hA%#61k3@U0+VUy4s_A|*fE308aV<*4~Lw-6n z#fEv{hk>FZA(8})h)3^$Qy-!Dygy~&8HW$M5Km;wm`xvSh@j_{Zky4YLvh0l(i8i& zGB`LerSexPm=14S8q9GGXNu_>RgVy=crXjdX-Z5h#o1`im!zMoU#OU#bUIY71WGWm zSbGx@z{;7zGTQe~9YT#{WLFHB)uok*t2vV~r|mT=d+&?h0|HuIE;YkPL@U?LOx}*rS!z@y1c=f6psdINeVnF@#QCCzK`5W6e`(+ ziEW8dZb3L`K2|@tO^?OF$_mn^4iBd1d(s@<>OQG4`{0fm|5}YTmg|x3aN3QublVVy zy+`%BaIrG@CsJ50VnhVmIRztOcKI`p))SFaqJxMsJ;Ux^)vz<_VEm1PKVo{h4WiX6e2kIOKS})%}Grf%k0kylQv;E=kyNJtHE*@8l zzcZ$M1Xh|0>=LUATQ+S)*aWVALly=B#%+XMkO1rgH%SMp#NTS=dZ9l+M(d&x z93k&~mQriChthxGxi?2MoQP95GdXm-e)Sa^76U+{XTV4#KTG2w;JA#<^2k>ui1I-a z4m9f|CQG|*a%=%-_Xv}{B&kfKTiRrg41A#URabXOjCfJ}`xj<`qlYYpF=$G|T6)R^ zhQV{E4pZQ3m7-fh2M`J;oMLEZp()OFlS*T4?ee6w1m4ldv$&h4+hsVD>v5vn1Gw3| zgbKH2qQ``qmtB%#J*6v)WTJGbGsKbCDhbe6y;@i-Ysg5HK04eUbgkuiA&TC#4dgwE zMST_FN_&aa$CJsjKdo3J#MFdW#Il6BMJ0w%K{J6CZd6hYG zBDRu}bc26oNB*P*8BD&*Ew9(eM&LjNpL;lieyE7VpINa(7<`)NYaqc0u&CRC5c05y z(@;AqpDLFuMlK3+oc+c& zDPXoX*?0+9o@7jBCrFIcl(tkjAUlQ+#Bw<(WXR}+xc3@^NjD?RQM<*)auy^)uL(Fr zE%FcDG@dgT*T3+FOPMZ)MK&TqB$qdF|NI7!1>MQ)x*Q`3bv}7wp;M zHHhZ8hY;T=3n5u20KU5Tlk%lk`34rsuP*|u<>u23uRv5Xt8$V?dn%dfx^G+@MYT5+ z(P#$9n`Q1a3Rop+?6{HPen23?xHuB^V1mKq?>jv$8F7Q=3k~@Fy@s6dvJS&i83)R+p5~*xP#258ski{;CCZ8@#C#i& zVZ;DEhG{BO1nlO+p>38rM$idvN$^%ftYbs#gSo*1G!t4(cet*nc0!qpF`bdQ`3!>U z+<^D5eIru3YBfeq8@AY->S}yIbkU7xS-5FWyKCpv#_V+ii2JBLTj&5p%>~D|-=b6+ z_+Sw)3=Dl-a|^WS;w^Ozvd$`K{&2bteUxBz(EJ(L!7Wa)^^~0$I^NdEkj$d%iLgj_ zSs|{=6mHxc6)r!)cubVQ zEUd`QQ2=;`*N@gwnaIk;W=O4tkfk-)n`h#^H{o_~vpeDo6vR+Xj2@Em5F5}{pwWcL zXF1n34ElQu@F=EV@=ChlCGmB|#VC0Ri-@REdK>>t2Wk@*F76%NU(OP)>J+aJBtN4@CIhhl& z>3(9OW#V&I1YF=jS=5gVG523uC|!^sK7%aBs#i+ZEjNNc5b-AfEP9}_a7O9WZ&LNM zMM6`Cu1lGyyA8NS<7@&iDhxQi8d>d^D6eI<1~AoT$J9eCzOD-rDnun>bx_3;2mm|= zFly;xAN>RSttnA1Mw69q=Wq(kJvj$OPp3DZTJRo0y5VyHRWx%aAwc~j0OLwiadnM` z7HcN6?kJritUp7>B{H)daxnqXp-XtEOlT6)InHKYn6$V_*C^9fQfFAMZ7`YvZQ%^g zbYKza(daMP*`>eu&#Q6z6bQ`2sFZ}TDB2q zu`@pqqZ0C@ci@cPxtxH<6XAkbO}dto07bf= z*bq>~g8o=fN}n9kz{r>r0{oRScGII%P8Lsjiy(&82TTb%B5|3wWG|WK9wfE3!UlFGs5VaF1okQLkK59%0IgEV zXL|YSL(kxpkp?{X>(@HrPKq}Y9w+t|r-7Jh&2`uzj;@?CWomL9u9EI-BC7-mm>kf( z_LP&$Wf=`rnxgcL_Wxt+Z?GgsdL&tN=Id>|?)|T_`l*P(=uvYnoHWCaLIE?<=^l|; zS&{0ZH^`am<+fe>@2p94d51!|EwsbT>j zERFA1;MSiPg5}N;zyzbdJ1PPe^}Pce$N>Pqec%Fv1IVU23J9G!Y`S@Q=~liB zCO|~N_nUk0a-!)cX+|t0WsvK^^7XO~vxClA7c+?L*8M(ko)fP&iFjJVwAuyJI&o_Y zw5hgA$aP-ho`*kS`+12srcM#CQ&}{A&sg?iImYIoo(V*mIS9Y1Qi8%Ygck27Wz)QH z_J`S1=jp1EtDBzo+I^4O^ zb{x~e-Br^hm&tjf%DGR5UwN=`g!U0C{};^PheYuo~M% zq4c_JBE#NrImA{DjOhtoioJ&3L~@TVEwgh{lJ{FkP{?L^PQ zp(wQSMA=#_Ssphe!l*`eq2oYUf{F;$=ipmNo>MuZ#B$WFn z^1QyxBRCDxX|5H&<6Juj%I@zR4)&haq{%VUUw$4Vi!c21;pQpN`);1{mnXxS&)FV* zz0^BW;0;{|ZzD^VWd3xztqX+p*+MR{8^Cz2XCMC605Ve3HvQgA1>Uttmc-_nG3_xP zwzBNS4zL@&)+h_|sb?;`LsPiuhj4JJxve>6P@}LjQbV&{iRF1N+72Ej@z=x4=8PBU zX^dxL=pc0&WEonIG=<263qzONi-Kn9`5nA1j-2g(^b?3Rj-86eZZynJEbBH(0vR(P zLF17>Ab->p4Duglr(Zj1fZnbLwKmY-*l#jqD;(6iU!H*_8NJZJlrhR>o$vz__MPnC zk$%1gD@+@q8-r1o{<=h7-7pZ?NrvzBPS(MT>9K8`40_K59F&+f<|}$T7~&|Xq$<)R z=6sYV&kQ?G0f$X+4q7DicFCwDaO5(jQ?^SzJb7N|&GN2}l7Fbc}gq~u&XR3X5PZ#e#VuTV*!ID|nE_#Rwuk z@!nuN@WZA;W#mbcVohjd+;-3 zLvk%_WEG684JKL`tWL&K+r-G3&O)a8_T?V9Z7@128gQ+IECN)8WYLB6DQ|CNp;s6g zQ0Kk$GUW+U)dgp4yd+5s(hQ1Y*>d8WFq1q*n3?vkpC3~A7L7GmlpYgv8TL_*iVt~L zs11bpQYt3TFz%5~9I;y#FVe3_E3k9-gDDT+b+0%fk>+3=C`1#aa^2?m~EvXj~eq~^^K5OxDS&(9hrD&cG1ffM%h-3I`uPPqlE$aguT@Kz}9Fue)>2_8UF?bh?1| z;DHU@_aHN`KY(k7o|C8fODpWx%Rl`1XB+}3=+(a@G0#FeNyL05eK*={`>O_kI&*}x zXYq7pH&wScDQp9M{vGVcW@EOZ&`gP&h^x@fgtAfF2NZ-wq7l59KpK_ph)hv^c`PbR zA@O#rI;AZzuO#o|bsFBCmG;sc+UZar80<#1l7b#4eIxaah_oOp$+ogx0oZ_pb?*5o z$mJVhH+Kjm1=odk4%eT~ea7T}|L^_s-gaOVonE%4UBD>k`Ay-Rll#T+oSW@!27I8Y zNGAC^JQKp)jG+7nU;Ocy1RRNlpdj-3CBBgyDo~g=&zvyyDHNF{_r$pU($)us?D=QU zef7_`?xuw{Y|f^0x>i6{cI~*`W{6!Lw{v)|A4=?B=QAS_G@B1aIx}==I^!gXwm&CX zTCX(=LuEFu$A$`v15g!Fb)M|z#w=2_l_b0u9^Q2lf1T_AS}zWCfk{G5BxJn$IN3g- zMKCcWIBhh{z|IZQG!y9t4ONH+R9H2dIG^Eul(9M%^U&pHqIj#$S>Uy{;~#Hbzkk>t z@mr?Pc%XD?6(%Vxxv`D*-t`|>ox{k%tPG5G;92w_@|x|`hXrDOeQ4}v;>TU`^-DQX z;Rnne=9tMh5&!f;ex20gk2qExw0#g@tV;L0l#tf(>=@j-36)6Ghy9LcI*y)`j-i6( zyV=Zt833uJXqp9L(n4d#WAnBJ#4-k~B2xL-Cq71O#S`Y=U;i}5b-R*G^G%nYJYd+Q zOJ?NzP$4f(HdZ&2Z%))S&Q2iyv3wO`c1Jx~RSN*4@3rJ*^@>HK2O}4!^zvsM zI_eT1_CG$UEkO~#X7u;Rzzy!5_-uAv!uaInFdH6b0nO*&_i1!TE_biKEwJrBTY(w2 znyc9CdpkU~m+}&s7X!Uli`gK>g*>evE{zGr(7BHrQrt`~`~?7pzMGWec*P$kfhkFo(?|2$}}#i+KmBhm_T?+k)L6 zJr9|^lWT<9g`=o=3e)+b`fQ2_S;#4lK%u;G`)L$BulPw}s84_=tS71WhBj0+73Ik++#r78HNH>Ar$4}(AJT8gf{5~vGM|291E7z4l;!@ z&5{V^RW%%ghUBSuXF(_j6SU6BY;IopLQu0`08#3)H!g#LM0(NttP>YQ+xXu5a8K(8 zz75W=4ypIEbsk%IS=bwRQF&q!D%`%FF$!LqEW^McVmNgZut`AmjPA4LHGv#hH&=k0vsSe#u#;q2xg-6R z!R^{ZN-BhOeJC3lHo9`0hex=f{EACeN<8$t8JEV524QJtQ;Pn1t&O0x^+Aks0B=(; z=DsQ~H1alwan7>WDh$Mo;@s^i1!Kty@T)+y>}jYoNXL2cMO7xn$yUJ)kWE2-FoOC& z8XIyfzKnhS>bp1wK@;R`aGuj*;O@=lUot8b)nd1HD((7=N(TWMn3d}F`FRC_t6J$U zX@M6mMs_nA*Z6h=dk3fV$S*_}8>Gd%i;u?^Rj>iyug!>DXzGN8kv-g;xD#$a{zhIv zP-ra8ew1q-j~$)AuRiaxxw(k{^~^%YrFV}S$D`E`)f8ZWQE$B)LV`160ChEn`?-J} ziO?v%2jB`t^C|rJ3cEv}O{M#XD9rd5dv0<7|Qoo=6}1HwZVBgJ%+P?lvxmhIOQ`b>TK zep^)M4KQBX&4Ewbk#|IWSqh&y6`J9qr?q%0u<^Xel1D(ny~vo`oO*J9L2KcKt)c{E zR5dY!zNB_>`fON(qJeAJuB2Cp?1PI$J3m$i|CE80mZu+xKE8(OYq6fVxRVfyFzsB5 ztJsTdNB5Z+LnF|Gpx_g(qO0`*p|9`#1+EL{hwX9Ru4xWO#yn)yaZaUPFOfw-$Ay^n zL2eUfOG67Vpkvk)#O}&(x|Ko*Bp*;mO_72gt)3-0SYOH3h&kU9o`y=_wwXTK_?jJV|h=J9Qs4mPcJHO)SX$u#j#2R*eS^i0d| zQ&U>NJ0NT^=v?8H4W?j1Lq>(;M8V#PYREtcUR#ouh1L9~+vP#V$_1*}cS$EKA9duq z+{69oJDuD^rPSzj74+fIj>D8Gj<>_pRcKzIHXe7&hV>ppqiGo{aFHYEis=F4+@U3S zxT9^;d#DcqZV^JXI4A0Lgr+5%EDAbXzU4By`t}?GUE5nXZW=Yny#^jaRe5SiF5f(F zEvRj;PNb10r6=oAxP!b50|auRX+>#nG%Y(Zk)EMvtXnX@3aIm``rOusZhQfXPGiTS zTY{`RYnLo^PYCM5q$tY9S`A_W1q*z|sC1q`ns;DtiF;dE8I}qVW!PBSO?37{+v_IY zlMiQx&k`MQabRwM-Pylp(hFRKEKIQpO0jK1K+zEpGiJefD9`zci1dQ6Nu@D;TwJj( zY@584VLiz%&*%8rL~h0f85K9!3TSUWn zvugXvb)VMskJ)&&T+#wX5K!jQlljY3IX~owQ>=z#B-7w4Vdq|Urb-w*x!{j;ArTVF z$sK#eAX_KFUYl4)MP&$kPbgMZF$@9@`caqC>NXJzX+@QD4}C#OkvIP5jGKBpbEO+c8NBo;-P@6xGX#L zmXlblIq@x}B7m-j8zVtcTsC&z8(_l!&_!foX*QploKJOF;PK4IF=NkxAQxOwS~xt~ zR&vRF*V1@oAIq=EjwKbGHwxa9x8#lC*&s@ZU{@bNJfpa;%mhsCWEwo8j2sMQNBrK3 zFYoOgK^66Nx13OfHWHl&BJ8^qwZ%vQeK68|-&97s`JM>&Yu-x(JMD{0I1r;a+GTc_ z)R*TU*9IuM-#`CSX=OlN68h1AIcJV%xchF8YGq_ZKrKm>y)}IFh{3xQSG;1)by&_g-WGb={eUe9(e||~b zDmCt0Y&S8wRD8(bXKGPlh~k6F?1{hJ#W&MWCuQ(KZb!6#(}p@yUIuX5oosBmuDFFS zKuU4c*jdOXi|YnJqE}ZA*iBLe7r)7Xm5v;4r4WJxj@082P%sFI% zUZzDvi6yC@)jkf}H$r!i2 zL<8wa+K5v*9^$0F9Iu%m`8}j~Ii`O;yx($3${15mde2+sP9}klqe8!lY#=)$nO02n zg~DAMo;oz=$2XiCNCXobO$tVN@NkJ$&L;esufH>zv(I9_IGws+8$&tFU&f{fnd!c4 zAj04>9?bPPGqEMYT%`e*TNE+sO{-4xIejWrbiVc=SA(*Cnf*nziTQXEWKudx$v}JQJaume+Dob zaaoK514bl&Sj~q>`E$VZ+~C`QVtEXtKd)uj0|#7J92HKdMWz$E$#LSxh|I5h&Z+(a zX)ksMApX;=Sej;X`|u0v1Vz2ueH@QPa*+Y%yc>Z$A88m~INvRoLG-J&6vFtTWB7%c zk!*Ty6|rR_L#~ZINacbBO0^evOtn-gFR(Gr^PJmS^d#SLBFF3f?w=74!d4sr2YlW0 z_^j2Zy30fT(*PzqNp9k;z|k$77SGh9gEU4eg>gl?2h{ zk%2ptHN`3RZ^9oJ+Fxs@%|ds71+$OoFwFPI24||7chx}E+#q=mlq*gBQu)(26aYH}ltpcj*8p( z9)o75S5Oi^CDEkqw!jl3^TmO46G7)Aru_pFs%6SV`!NfTDH@0vFX_d+zaCp&$3X;Z zUY9SF3l00u>mL>Rt>+Q^9T`~5`IuRsAMne#+vuSlC}p07&kYV$-DN+EP}dUA`hYVw z!}Yl5QBfyrUY1VhulIP&nDr|ftxBwRn<4YelM7|G4J_{ z;ezB}7Xbnd9fSB2K{6#Sb+Kt<@fVRsj~5ul?$#r4LK=^C35TKL5fU;JkgY1~@lR8a zj56njW4kxi-8A;^y6IXVh+D=sYt?+>Df%Tdt>lAo{8tL zF&XyHj!$Cr_mu%J2&)s%ys4BeSUI<>BsXP>8K;NAWcKf=9|*JYE(ZhWfdBiy?~BAv zOxq{1rgzFW@mV#&1;Q&-Vr*~cLeL3=wy{kEwaNJmfkK@1Sbu(4qp^_d{XU`&YwuyZ z1!tO}YJ%A8Kgz17ZiKPTt&;wUo=;wcmHJ=)wU`exPcIF158a&eyoUVV*U zdkyNbVVc7^(~kr+hCblguqw6)-n_a4HSV+@En=K6YL+D<7x?A*MlcMlm*p7s!|n8P z7!~Y*7m(k#bC^r}xt_zhZi13Jh2_oh(gXQTU&-ync2^nel5t48*}X(39{VPgjrRdf zxV-NQ^-2yt?eiZ?iXliejLx-{nrMqmFKV2l{O~Ly?V8Czo8#W%fu6IDvfYNsw#a7w zP@ijh9T?{JDFcB-#_9hGOfILViVZwZE3OM1XHyMJffnriVesMBF^We*d15tw{ToEH zNM7e+WSISb{_DU0=YM$X0qqSF4j5^nz7zw+Hs$?)B;UtG>EW_BOmRcL&H6hkkF@?# zh@SF%qk#h4iQd40R?0gGzi)u8vs-!7Lo z3$PGMo5gr5IfX;SorrS|dG~Vthp`G8&h4fCFA(cbOtR-G%)ZYb>bJ9R=TXVn`SJV| zgQQ>pyPQbg9#qCnD)$QRC3=2scgKCtd2x{w-ZNiQ3iYLdpN{uBle$r1O-YZo)TbWg zT4K}}?3A4{e%j{q-xyxERGXg8AEy~;;Cx+#eVgq@9fDX81TV<*eewB9z3>J@JI^m6 z3U(WKk@Ya5m*ESJ+X;f=D5hTsJdAG3n@WY=}n#<iZ zGV_`_VMp2x&A+3uG-q$mKi?Bk_EW5|pzk}>EYy-3e{8o#f8@fxh6o#Ki`|BwZcU~V zoV-Zp!b~4WbXUzw76FGGuf2*>&hJ4O$>~=Ac@rIDIJae8<#$~0PNp{|D^0~0aNB42 z8y~W1*w{MtR-deAyi)h+T)^hDOlDbo)q1TJ?cMo5gVrPKt= z3r^Kob-i*D!*(7<&{74{ynWFD!SBF(p1poP4qNXuTJF5v$ReF>Q2^t0@ow!&;d=N| z}g#ho3O5SgI%mQLmMD%1NrWa;EVxaKByDII1mAl~R zD+jh@(+sG$_C2C!i@$EAQO*F-;B3MfUf{apzqQk6-5_>eFHdsws_lLh7=~-n?~V^& zQqLBI|LxnR#}=lV{1cqioH&hecLi&7ois`8KrwD`bLL{?WSnV+A%i9ffe2@lj?6`4 z)W(8`oP5%<7y7ROZIYTpMxu*!jbwe?gZtHyHI&>v=GV}?) zt3wl`xw?xzbrQAa^NDw$;dauG{61B;tI!9k4JiN6kl1N>*-iqvV;jd?BOD~{MXd`% z-JKuL*C+8Ih{X#`E_if-nggEEo zFJ}w65cxPV|1~%p+o0x&t>f?d3Zv_k{+&v@@lHJV=&Tc^8|mI2|MG*}%emX?qlv)= zu)Qt$WM@8Dg{J8-iw&5ccZ@X?6TD1BDf4~PyrQJ{s;qu<_1-nN$ZgYd{-Fvs6$AsC zE~ol;3}B<75T37#xDw4~&X;uBgr&0ZAH;4xI-B6YV{v5`yq0*r$*KCJhw zrLy;--7P82y`}PM)4k4uSs)$^0M)`j&#?&gqmOF>32Sk!v!)N%pBR|;_saECIBrW7}fdL zrB74}dk^XKuPoD>FO_)h*Mf55Hsdg4!cQ&Bg2}1!HVs-l7NKSXn`%_BB5NzuJ0QF| zC-@+P*ZTle`)>z`&G41wt_!8ki{}7IK(@bHk|@Rabj(&T%yY(#*Dja~AgvtGD|Nkr zpIL^M-b)1?Vl#cu|C&MY(gJ=)uLOSc=ZLn(ZEoo-&-vH>IP34k-rj%P`wP*{REe)M zw{BcsCt%gApmS|(tXGiy9wEe>yw}vjYjOl`%dW9?mnm_c$slH7RL-X|#sYybTmRc>bZwssT-`2T%6tTDFmAok%-hRIqiqlh0#|{%uUk0({hWqFh4D<> zA{xLi$^ZH<>Cagb1H#(L6RuL**YJK)n-g|3yl3udFt{rqPHg(4f@1`LrDYdsWTLx3 zY*aQz5gY@6XVS_$`KGGcDaD49TJhU9Y~j<;2U255MHfEJOL$Q{kj2iLn4$u5HmVArb*z2n90o{cbi+N|IpuBCWn(W< zxEF=U;;v_am=tte3|+y}49b#bc%@A(X$s%$qLw!j55V*J70U!f`1IRGTP%~)*XI5< zH5o0?rPY_V;@_>tMShWLAZ*bN9D|VL-Lw8M_ft)MA3s}qxtpd-_ZEd$KDKuQx|Fz@ zE96*-MaZ9>%1A^9LK}ERv6)dtq{c0AP75qz11gV;I0`&aUCkB z*y`MSS%ebbz5pNi`w($*W6+_&BZ#CN)Frl%g|yT`>5^gAAS8!JHsRoRD!d3GT54tv z#sqR_0Y*uV!b^d|;ZHvx_HCbS;2Q7p-3>f3+Y$^P4+^`5GCy(7jiK1WJ}BUKaSg#% zSma`k#*~0AiVLHEal_|^VO`F^)~caTxB=(E2Vc%*V}zF|zd~R3=Asz2U~$5iQWLa| z$SH*3D_K91Zuqe+*EVjK6lWp4dw|U&Kj#~j&smN@8Zc{-UN4=>t4J@EH~CgBY=@db zZ9Hb6vC_4R0*=91@^*mr=m2UEFBHx><1Z$*`}q97j9|#88$@>vv!?T#Mcdjai|Qh5 zg*x;CXP99!J8!QqiR||W2_h+yvGjr6fM5KipMN`$O?wSwk(qpS7*VJ)0Sp@`OATb2 zER|rys*#7^^c8S!Rqnt8i!Us6=&G(TyI>&YIC!u-dRN)e( zxcW-aGlYnuk9-p}(}Z;;m~rFumPX3C@A`)2Dz6`5h!yWy;WDA}3_iD%yDmcKeJSDt zv9#rS?2MU+djQ1);*-(c)(4L%-83W|oOB*!s`J#|GnpkM)nVXQ0Jc*UnJ=|(f33uo zvyc+;_$>rjrxlHp7Xu?*qZ6vIObFD(Ugw*-5Z9jKmOmyogpQWN$H%qP2mju(`=B8x z7C*zKzM8B~&t|~OqaQoQv>GrH>KJ?-JtVnvuvt|JET=&H(!a87Ao=b9Ag7o{LyuCvF%e zID2gFn>gAz{RBrOb5nVpScHFqLqu%9JhZE-7v03_7T~#V@K+4pUp0bg8>aeCCh;5f zt$MaZ`w5IYlNFI5_@`eF)U>Wq#y_GJY@y-Im%{J5hA9X`e==!(whk1texumaiL;=hT*_wxQ0Brr%^FRV@0m$+F{aCw!hYI^9BZb)OA*8>q{QJ9S}%If$8D z3NHs{q$c9TcaA8h>cwS7x~9>qe=$P~VAtpLV?ADC{^M_?i?0imzk*3%SIS5jR|xx| zKkY3~;AXvrXz@kpp+muc!IsGn6c^d)@G(9xnK=F>PF zC^2p6PS}x`%qXe_-<+yn^#&V3^>&Ai6IZdfG9bG#nX^P#62udFxx3Ez6CGTcd@L#D zPv{k%T$GBZu)rVfUCP<)61z$;*Fvziecyi&!tT5RvWMJj2i2G4Mn?vKgder(q?mC| zPC`#DRFKsC9GwMwvSceEZ`$xu8Q~I`@c`>~Yh$50f$BHj7i|+0=h;;T4^h)Ao)3pM z!3h4i=Bph^QVFA_fT6uhz6<3BFV~<@Zh(ieg96Ma1m%TqZUb0hBw@P1_hQ&sx~$d@ z^sm9`Of#2b?zFYqW2Hm6`8>}%Wk`R|z>9yj(t_N&1Y92*?~h?yz!fvKYNXxD_;dZ@ zMl?JH+*`}gYE;t$;jEFlHcEh1Eh&WxCjXFQmqEdb4z1YU* z{=943>0R6z@evJx0{QFQyN)f)R3TfN@nn%MnpHM?yuC$?eI?*{cj}JwzWy>k83(Nk zB=*p$Z11!&&q=+RpA>nNjhnHj9#L-~JshC4AkV#lb(IQ#S5(la0anoe>~y}0ib2n< zXAC|2&ZTdo8pyzuO~7uN`(KMiJL|T6nf`AAKY#Pc@97K$r*#N|{;((+Mt>=6jX{-Z z?jR07HMUY5R|AuQbq+iIuvskKY%e1^-kx@nNq;+3T4eCz7mW^SB67(hPw+u6$D&v| zA8gt)n3Wv(&OkMcO9!%Sx4E7__c^q?47a`fzMqgT7to@d>#JPhE+Li^)FeUUE=7v!Ri>kL9KSzdH(OYSlBPDi7w_op_dd*rWhk6>ig)D%QZ!i9c z(eSw}eQAfAkN4>8!!Z3llW82^GAZ_cf3Nce6FIdv_@NE`t;;V znB2`t`%&3OsfWkPXK=PYiAmMf#`^EXzKjhwZ!-M3`i}snx^1{_|K=}!rdwN^GhUws zyQVYVn`UuaG^DVw<)FBf+S}>>y70bQihU(Paqkjv)1bsDlm4L1L_*nO6tDX9A}rvr_HfJ) z0HFnf!S?b}imsrGxKVEvF3sJ!U6$8BEWy@U2KeVZiR2e`oRjW!O#U6yjryFM&n)9t zDCzy;opz0QelT^H=r}8c{u9#HQW2mbMU;FPG;7=@8FE7ezuD1*Y-SpSt2R0ZA%-C( z&c0PFp`*X-h`bvZ&@9962D)zeF2bcb=%O3_t4aJQD@Zn@8!XA>$}n*5HS6|OmnU+G zrIosQ<_CDrpjx+rIml$yz~>9Rb~OpTYHx4Ivj9HSO^b2R^%7yP$w(aUN>*1ZJ}cv; zk(`nkYP*V6!{;?-xzD+D`G0@6-kmp+b6M>7=_`E77DcqW2 zh$h#Nrr}xri=g(wz(OB`hb;gukxkHXOF3$L=46*JcFaMPq<3hsBtOnrzDXM*;!@EG z-qX!;1Sd5Fxk)&@UR@+fj@MK#Q5lsqVI7{OdTx&0(xu|6KU>49Ac=%zi?&qM(s7zunr{$FJ&DX~ZVdg_@%_(95=xuja#J{KkQ&^i4D%&C3D}4HClJ zmRWdXGf}q?m0af8#&Fq^>t(6orm{i)YMcuBQWo0#2NP`dNs<+tNwL}4S%Y_zLxtk`Z!Rfx0Y!~z~7;cPx6!r=LZel(;Q>Ov)==~E z5W0M0AR)Q$xTrgP+#Ts7n(#0g;^zSk>qw_QG-|_azdXR*BI=VcTtuEjJwU)f9}kO! z;&1>+_MU9NNzkB2`8&nvPRb85}5 z3cKYuN1zLyet5fI;JY3P=(>N`2vOpE>cgfzq4~ zIbpnzur-9pF$o90-%e>xoxN3$Wv}^M{96!Be6-$IASSox1hR$NFXttZB~2RWkB#YZ zIl*B}NCr)C?EC}I(xamY2a!5luDw9V@|M;=T6~#185LRH42?QH;31{eBL1(_&2-BQ zSrr%J^6kV75FMBHc(kk}&*vSR-Ez=9yR%@|&Ns=D%8&yUqRMWnLuRoh1ZF+Ln7;CDgz1N;ow`F(^X>~b@_L^AkEQI+aF z^4PqKGanc>OzYZRF-}WaZcY@@r!@Vos`FG*oVPGsTq}`tdr)MKy>7QraY174Z4)AH zj(O&L@X6V!r^Jph%C1QtMzWx1*U>$#Kgh$+@qJ{`_Y!=n95~29O>t5Ex2NRlcG!iR zB62uqitxgPL4_H{Hu732zM)`mV3zHZVet=U;et64oDDVC^2xY)DX6y%R|6M*Sf7iv zY*xAi7L;~EzNid(NOwFEVL@m8iQ`ETGWBk-l2ifxflX|VR~A_)+~_LTjTh8b_x7x~cgkSG~m~`h>OawP-_e;e9IGNDa z9MfsfnVrxEFAhweEb5gxH*!^}AH}s-gCF*SIW%Zbbh{Rq&4I~$+nV#*YdJ2a=52C& z1jKep@f|l*oB<}7-$+#Ng#M3k+F_SCGF&@FjA#>8a2hpnk6Zttkjo{hiiem^7kJ2< z53i|WfRyXQInazA;EIj`#bH` z{1Yk2QF|vCCk+-)THxzP_Y$YUIqp|63v6T!SL0Z132~cl@7NduuOS()Zd&P-h;YPK zz6AS5^i5*IDzn*ccdYC*Jhvv4{)<{oJ^{_Y+{s+MC-k97#VWXz#1`46#}{eSJVeo^ zb6>S?*N=A|5S+(f;RWsUPx4fC22dGQeIo~n?l8WiwA&#x{qAV6kcjw1T$U7yA1%-K z=Q8$@mto)vc}vf2#$j2h9qh$_0TjOQS7_obWG$wmhQ}!%erG0$473hbRueRXm+bJ~ zC74>$EJ`1k-50I_3vc;U0fFtt&JtseSeyfxBpf0U2#k2AqRF+nt3VUHjP4Y+KyAzy zkHu+`_+pItZ-8rYiRH7Cf%Fe!G58F0?2Id*U7IocyyEH<`!rW-cvD*SkYR|@hyDH` zDS;6u2G5uR98&Zz@L-L>7;IOOe&+-shbAC4M+8;ZJ)rd@$K3Nf!L?zdiSm-q1VhYT zz-c?6-a&WABLOHK6v$iTrxSojg^B?!wTqOufj|B9sv1Vdr^s_W!VA0A>8bruuIw+C!~rv2Q`Q;{qvl%`Te*FcfEAD3R33=O%(3O0c7{~ zCVmqt;vJLOZo)%vGkdq82ie>j`NH6T29C>MNI2-!ox$s{kmfBrdoNPREv$Pjs)S}=6;^#>`C_c^Nftd9sEVdu=TQPOwxdQz80m(ymB?!tKV0Z^Cly zynF^+Or~5CDOxWN|6n4g(@ZYvE%p3IJ&`hRnCSp#^QCb?V&Lr;ot>Jq6c5_K1ahdv zd(&i>$J6?1;}4jVv-Nw7YA&OF(EfGLN;)D0S!&-&nF2aXEU63e3X;{*q0c^60H z)CX$w+}vu0XZrIs(VZJeWLbFJCb)V{!7)NJ^+d#$0NsSwTLg#h4FiF1C9osxHu3Lb zh%#yYRqKE{ZR(s=r$tyqY}h1=4~^$ho0#5fhS@LidRjBZjwP&x5#K}B2SEfNP;UVc zJEz1w!y|)+m#}`>1z;B;q{kh6>yjS?6PE;>7MgKzXr43Tb3=r4@b)-Jb4^S+?vNWL zmw?2l2v$Y8%{B*OIyVf*cm9$|;zHcrFiWq%-$Vjz26sL;8atk&%a+(XRTMji!>!xN z({B!cehy&*%9#M!Gp4B2xjMI)TN!@3zm;R_lO(zg&Ff>}!%z>m9aT#$tlQpdlK8J7`Yr2t zxO2M+vf-mh+c~JdTCk9ovFTh^i#+UgVqb~c2A|t+&o{tDCaI1r8sjdHPnMkq2=$Rr zg|{%rd%Kz{G*%$5!rpmv0Aqpx=mn8kcwLY#_W!c~_y7La|NQs=Tr#gZED=&rr%&|} zWAsk@t21rPi-bArn~B)mb56dvN}V@>3iyNM%ak+I{6-e2d1`3EYH@muS