diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-24 14:33:38 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-24 14:33:38 +0200 |
| commit | 317f09328ed8bf20148b707470c9b0fe82e59575 (patch) | |
| tree | ba8daf5dcf050d44b7b0e766babbfda8fadac59f /packages/meshbay-node/src/meshbay_node/ops.py | |
| parent | b6e2dcea65124673da047f9b3c92bc5e25980d63 (diff) | |
| parent | 0b0da86f1f9d6f0b1a27b5e1e1658c42de9f356a (diff) | |
| download | meshbay-317f09328ed8bf20148b707470c9b0fe82e59575.tar.gz | |
Merge branch 'docs/mediacenter-videos-app': Videos group app
Poster grid / flat list browsing, TMDB metadata enrichment, thumbnail
generation and caching, season-specific overviews, manual match
correction, and the create-group wizard's app-selection step.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LAmyXtc6dAADsH23ydXQpY
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/ops.py')
| -rw-r--r-- | packages/meshbay-node/src/meshbay_node/ops.py | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/ops.py b/packages/meshbay-node/src/meshbay_node/ops.py index 154813d..6848e3a 100644 --- a/packages/meshbay-node/src/meshbay_node/ops.py +++ b/packages/meshbay-node/src/meshbay_node/ops.py @@ -731,6 +731,68 @@ async def set_enabled_apps(state: dict, group_id: str, apps: list[str]) -> dict: return {"apps": apps, "group_id": group_id} +# ── TMDB config (Videos app) ───────────────────────────────────────────────── + +async def set_tmdb_config(state: dict, enabled: bool, token: str | None = None, + language: str | None = None) -> dict: + """ + Whether the node calls TMDB at all, whether it uses a custom API token + instead of the shipped default, and in what language it queries TMDB + (docs/mediacenter.md §5.5). + + Node-wide (roster.py group_settings, group_id="") rather than per-group + like set_member_upload/set_enabled_apps: TMDB is one operator's budget, + one credential and one shared cache, not a per-group or per-viewer + concern. `token=""` explicitly clears a previously-set custom token + (reverts to the shipped default); `token=None` leaves whatever was + there unchanged. Same discipline for `language`. + """ + roster = _roster(state) + await roster.set_tmdb_config(enabled, token, language, set_by=state.get("node_user_id", "")) + state["tmdb_enabled"] = enabled + # `token=None` means "leave whatever was there" (§ set_tmdb_config's own + # docstring) — so the customized flag only changes when a value (a real + # token, or "" to clear one) was actually given. + if token is not None: + state["tmdb_token_customized"] = bool(token) + if language is not None: + state["tmdb_language"] = language + log.info("TMDB config: enabled=%s custom_token=%s language=%s", + enabled, bool(token), language or state.get("tmdb_language", "")) + return { + "enabled": enabled, + "token_customized": state.get("tmdb_token_customized", False), + "language": state.get("tmdb_language", ""), + } + + +async def set_video_root(state: dict, group_id: str, path: str) -> dict: + """ + Which folder (possibly a subfolder of a shared root) is the Videos app's + entry point for this group. Same shape as set_enabled_apps: lives on the + node (roster.db), takes effect without a restart, signed by the operator. + `path=""` clears it — the Videos tab then asks for one to be chosen + before anything (including TMDB enrichment, docs/mediacenter.md §5.2) + runs, rather than defaulting to the whole shared index. + + A non-empty path fires (never awaits) a sweep of whatever that folder + already contains: the ordinary per-change enrichment path only ever + looks at files new since the last broadcast, so anything already sitting + in a folder before it became the video_root would otherwise never be + picked up. + """ + roster = _roster(state) + ctx = _group_ctx(state, group_id) + await roster.set_video_root(group_id, path, set_by=state.get("node_user_id", "")) + ctx["video_root"] = path + log.info("Videos root for group %s: %r", group_id[:8], path) + if path: + enrich_fn = state.get("enrich_video_root_fn") + if enrich_fn: + asyncio.ensure_future(enrich_fn(group_id)) + return {"path": path, "group_id": group_id} + + # ── Scan settings ──────────────────────────────────────────────────────────── async def set_scan_settings(state: dict, group_id: str, reconcile_interval_secs: float, |