From c5585beab3d6adefaa2ef9444946dd3816960a7c Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Mon, 24 Aug 2026 15:57:41 +0200 Subject: fix(node,hub): HEVC transcode fallback, live-add progress, per-group TMDB toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three bugs found live testing the Videos app against a real HEVC/EAC3 show, plus a design change requested afterward: - Streaming always did "-c:v copy", which faithfully reports a source's real hev1 codec string but is unplayable in a browser with no HEVC decoder (most Chrome/Linux builds). The node now transcodes to H264 whenever the probed codec is browser-incompatible (media_probe.py's new BROWSER_INCOMPATIBLE_VIDEO_CODECS), with a `transcode_incompatible_video` node.toml opt-out for operators who know their viewers already decode it. - Dropping a whole season into an already-watched folder gave no scanning indicator and no progress bar: IndexProgress was only ever updated by the two bulk scan paths, never by the real-time per-file watchdog path (_schedule_update/_debounce/_update_entry). That path now accounts a "burst" the same way, without double-counting a file rewritten mid-debounce. - A stray literal "0" rendered in the video detail modal when there was no TMDB match (`meta.confidence` is 0, and `0 && x` renders "0" in JSX/htm, not nothing) — `confident` is now a real boolean. - Whether TMDB is used at all moves from a node-wide setting to per-group (OP_TMDB_ENABLED/tmdb_enabled/tmdb_enabled_ack, scoped like OP_VIDEO_ROOT): an operator running a real media-library group alongside test/demo groups on one node wants outbound TMDB traffic for the one that needs it, not all of them. The custom API token and query language stay node-wide, one shared credential/cache (tmdb_config/OP_TMDB_CONFIG, unchanged reasoning). MNP_VERSION 0.6 -> 0.7, additive. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01LAmyXtc6dAADsH23ydXQpY --- docs/mediacenter.md | 60 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 51 insertions(+), 9 deletions(-) (limited to 'docs/mediacenter.md') diff --git a/docs/mediacenter.md b/docs/mediacenter.md index 66232f8..0df115b 100644 --- a/docs/mediacenter.md +++ b/docs/mediacenter.md @@ -362,6 +362,25 @@ seen; the file appears in the index immediately with size/hash only, and an `index_sync` delta fills in the technical/parsed fields once ready — no scan is blocked waiting for it. +**Bug found live, 2026-08-24**: an operator moved a whole show's season into +an already-watched shared folder and saw no scanning indicator and no +progress bar at all — files just appeared one at a time, exactly as if +nothing were tracking the add. Root cause: `IndexProgress` (`scanning`/ +`scanned_bytes`/`total_bytes`) was only ever touched by the two *bulk* scan +paths — the initial walk and the periodic reconcile backstop, both of which +know their file list upfront. The real-time watchdog path +(`_schedule_update`/`_debounce`/`_update_entry`, one file at a time, each +independently debounced) never touched it at all — an omission, not a race: +dropping in ten files this way always gave zero feedback, indexing or not. +Fixed by accounting a "burst" the same real-time path creates: a file's size +is added to `total_bytes` the moment its debounce timer is first scheduled +(not on every re-trigger of the same path — a cancelled-and-rescheduled +timer must not double-count), and to `scanned_bytes` once its hash actually +finishes; `scanning` flips back off only once no debounce timers are pending +*and* no hash is still running, not just when the last timer fires — the +in-flight hash of a large file is the entire reason to show progress in the +first place. + ### 5.3 Thumbnail delivery — reuse the chunk path Since `thumb_hash` is a blake3 exactly like a file's own `id`, the cleanest @@ -471,20 +490,22 @@ made a revisit's fetch fast enough to *be* synchronous. ### 5.5 Node-wide config, not per-group -TMDB is one operator's budget and one credential, not a per-group concern, -so it does not belong in `group_settings` keyed by a real `group_id`. The -existing precedent for node-wide operator state stored in that same table is -already established (`desktop-client-v1.md` §6.3: *"the precedent exists: +The TMDB credential and query language are one operator's budget and one +shared cache, not a per-group concern, so they do not belong in +`group_settings` keyed by a real `group_id`. The existing precedent for +node-wide operator state stored in that same table is already established +(`desktop-client-v1.md` §6.3: *"the precedent exists: `roster.get_member("", user_id)` already authorizes the operator -node-wide"*) — so `tmdb_enabled` and an optional `tmdb_api_token` land in +node-wide"*) — so an optional `tmdb_api_token` and `tmdb_language` land in `group_settings` under the sentinel `group_id=""`, changed by a signed operator op (`OP_TMDB_CONFIG`, same shape as `OP_MEMBER_UPLOAD`/ `OP_APPS_ENABLED`), and broadcast in `node_status` (the node-wide channel, as opposed to the per-group `handshake_ack`) so every connected client, -across every group, sees whether TMDB is on without reconnecting. Absent -means the shipped default token, present with a token means "use this one -instead" — the same "absent means the old behaviour" discipline -`member_upload`/`apps_enabled` already follow. +across every group, sees the current token/language state without +reconnecting. Absent means the shipped default token, present with a token +means "use this one instead" — the same "absent means the old behaviour" +discipline `member_upload`/`apps_enabled` already follow. (Whether TMDB is +used *at all* used to live here too — moved per-group, below.) **Added 2026-08-24**: a third field, `tmdb_language` (e.g. `"fr-FR"`), travels the same way — one node-wide setting, not a per-viewer request. @@ -521,6 +542,27 @@ same — a second, English-forced details fetch only when a checked field (`overview`, `poster_path`, `genres`) comes back empty, merged field by field so a good localized field is never overwritten by the fallback. +**Whether TMDB is used at all, moved per-group, 2026-08-24**: an operator +running one real media-library group alongside several test/demo groups on +the same node found every group sharing one on/off switch meant there was +no way to spend TMDB quota and make outbound requests for the group that +actually needed it without doing the same for groups that didn't. Split +into its own signed op, `OP_TMDB_ENABLED`/`tmdb_enabled`/`tmdb_enabled_ack`, +scoped to `self._group_id` exactly like `OP_VIDEO_ROOT` — stored in +`group_settings` under the real `group_id` this time, not the `""` +sentinel, and surfaced in the per-group `handshake_ack` (`tmdb_enabled`, +already there — it simply reads a per-group value now) rather than +`node_status`. `tmdb_config`/`OP_TMDB_CONFIG` keep their name and shape for +the token/language, which stay node-wide for the reasoning above; only the +on/off switch moved. `TmdbClient` itself lost its own notion of "enabled" +entirely — every call site in `webrtc_server.py` (`_do_media_meta_request`, +`_do_season_meta_request`, `_do_tmdb_search_request`) now checks +`self._group_ctx().get("tmdb_enabled", True)` before ever calling it, and a +group with it off degrades exactly like "no client configured" already +did — zero confidence, empty search results, never an error, since a +member's Videos tab already has to handle "no TMDB match" as the ordinary +case (§4.1). + ### 5.6 `ALLOWED_APPS` `webrtc_server.py:1629` — add `"video"` to the frozenset. `DEFAULT_APPS` -- cgit v1.2.3