summaryrefslogtreecommitdiffstats
path: root/docs/mediacenter.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/mediacenter.md')
-rw-r--r--docs/mediacenter.md43
1 files changed, 43 insertions, 0 deletions
diff --git a/docs/mediacenter.md b/docs/mediacenter.md
index dbdf85c..d714366 100644
--- a/docs/mediacenter.md
+++ b/docs/mediacenter.md
@@ -904,6 +904,49 @@ only when there is nothing else, and the lowest *number* rather than the first
entry so it does not quietly depend on `buildSeasons` keeping its sort.
`test_video_default_season.py` — no input it takes can carry a thumbnail.
+### 10.6 The same film twice in the cross-group Search view (2026-09-02)
+
+An operator hosting two groups gave both the *same* video directory — which is
+the point of having two groups: different people are invited to different
+libraries, and one library may be in several of them. **Search files** then
+showed every film as two poster cards and every episode twice in the season
+list, one copy badged per group. Flat list too.
+
+Not a Videos bug. Inside a group it cannot happen: `GroupIndex` is keyed by
+blake3, so the same bytes at two paths are already one entry. `search-page.js`
+concatenates *N* independently keyed indexes into one list, and that is where
+the duplication is born.
+
+`source-merge.js` folds entries on the content hash and resolves **one source
+per unit** — a film, a whole show — rather than per file: a season split across
+two nodes would open two connections and two metadata lookups for one show. A
+group hosted by the reader's own node wins (read from `handshake_ack`'s
+`is_node_admin`, which the node computes from its own record of who it belongs
+to, never a hub claim); failing that the pick is `hash(unitKey + userId)`,
+stable for one reader across renders and reloads — a source that changed
+mid-stream would tear down the connection under a film that is playing — and
+spread across readers.
+
+The units come from **`groupVideoEntries` itself**, called on the un-merged
+list purely to learn them, never a second copy of its keys in the Search page.
+A copy would keep agreeing until one of them changed, and the symptom would be
+a show whose episodes stream from two different nodes.
+
+Two consequences worth knowing:
+
+- **An operator's "Fix match" and "Rematch" go to the chosen source's node.**
+ On the operator's own libraries that is their node, which is what they mean.
+ On a merged entry they do not host, the override lands on whichever group was
+ picked — and the other source keeps its own match.
+- **`PosterGrid.mergedShows` still merges two differently-parsed titles once
+ both resolve to the same TMDB id.** Those were two units when the source was
+ picked, so a merged card can hold two sources. Left alone deliberately:
+ re-picking under a card the reader is looking at is worse than a mixed one.
+
+Full design, the adversary this names, and what must not change:
+`docs/refactoring-search.md`. `test_search_source_merge.py` holds the rules,
+`test_search_media_merge.py` holds this symptom end to end.
+
## 11. Acceptance before shipping
1. Re-run the §3 validation (real TMDB calls, same corpus, same script