aboutsummaryrefslogtreecommitdiffstats
path: root/docs/musicbay.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/musicbay.md')
-rw-r--r--docs/musicbay.md34
1 files changed, 34 insertions, 0 deletions
diff --git a/docs/musicbay.md b/docs/musicbay.md
index 2ca8b23..d9794d2 100644
--- a/docs/musicbay.md
+++ b/docs/musicbay.md
@@ -434,6 +434,40 @@ actually implement (§3.2), not a property the protocol gives for free.
| M5 | Gapless playback, crossfade, lyrics, waveform seek preview | Nice-to-haves, no architectural prerequisite from this plan either way |
| M6 | Photos app | Out of scope of this document, per `apps.md`'s own list — unaffected by anything here |
+## 9b. One album, not two, in the cross-group Search view (2026-09-02)
+
+Reported against Videos and true here by construction: an operator hosting two
+groups that share one directory saw every track listed twice inside one album.
+Inside a group it cannot happen (`GroupIndex` is keyed by blake3); the Search
+page concatenates *N* indexes, and that is where the duplication is born.
+
+The fix is `source-merge.js`, applied identically to all three media views —
+fold on the content hash, resolve **one source per unit**. For Music the unit
+is an album, and a track loose enough to carry no artist at all is a unit of
+its own (or it would be dropped from the merge entirely, since
+`groupMusicEntries` never puts it in an album bucket).
+
+**`foldKey` is exported for this**, and the reason is worth keeping. §5.1's
+grouping folds case and `&`/`and` for the *key* while keeping the first-seen
+spelling for display — and which group is seen first is whichever index
+happened to arrive first. Keying a merge unit on the display strings would
+therefore let the chosen source change between page loads. The folded key is
+the one grouping actually uses, and is stable.
+
+The units come from `groupMusicEntries` itself, called on the un-merged list
+purely to learn them — never a second copy of its keys in the Search page.
+
+**The player needed no change.** `music-player.js` resolves a connection from
+`entry.groupId` (§2.3), and a merged entry carries exactly one. The queue built
+in `search-page.js`'s `onPreview` needed none either: it filters by `groupId`
+and is reachable only from the Files explorer, which is deliberately *not*
+merged — there each group is a folder and merging would remove a track from
+one of them.
+
+Design and the adversary it names: `docs/refactoring-search.md`.
+`test_search_media_merge.py` covers the album cases, including a differently-
+cased tag not splitting the unit.
+
## 10. Acceptance before shipping
1. Tag-coverage measurement against a real local library (not committed —