# MeshBay — Music application (plan) > **Superseded by `MESHBAY_DESIGN.md`.** This was the Music application design; its design > content now lives in §9.8. > > It is kept because code comments, tests and other documents cite its > sections and its labels, and because it records reasoning a synthesis > compresses. **Where it disagrees with `MESHBAY_DESIGN.md`, the design > document is right; where either disagrees with the code, the code is.** > `MESHBAY_DESIGN.md` §16 maps every section reference here onto its > replacement, and §13 defines every label. > Status: **built** — `music-app.js`, `music-app-settings.js` and the node-side > enrichment all shipped; this header said "not implemented" long after they did. > Follows the plug-in mechanism in > `docs/apps.md` — read that first, and `docs/mediacenter.md` second: Music > reuses its node-side pattern (metadata fetched and cached by the node, > never by a client) wherever the same shape applies, and this document only > states where Music differs and why. > > Follows the project convention: every claim names the adversary it holds > against (§7). --- ## 0. What was asked, in one paragraph A group "application" with exactly the same principles as Videos — a view over the existing file index, no catalogue, node-side metadata enrichment, enable/disable per group on the same signed-op mechanism — but for audio, styled after a mainstream streaming-music player rather than a poster grid: album art browsing, a persistent transport (play/pause, previous/next within the current album or queue, shuffle, repeat), and no cast/DLNA surface (out of scope, unlike a possible future addition to Videos). --- ## 1. What this design does not reopen Everything Videos already established stands, and this plan fits inside it: - **Views over the index, never a catalogue** (`desktop-client-v1.md` §6.10, draft-v6 §2.7, `mediacenter.md` §1). A file stays tied to its filesystem representation; nothing here adds a second identity for a file, and nothing reaches the hub. - **The apps plug-in mechanism** (`apps.md`): a new `music-app.js`, one registry entry, one node-side `ALLOWED_APPS` entry, i18n keys, the asset list, the two file-set tests. Enablement is a per-group, operator-signed setting, same shape as `member_upload`/`apps_enabled`. - **Group-related server state lives on the node** (E9). Nothing in this design puts a row on the hub. - **Node-side derived-data caching, never in a shared root** (`mediacenter.md` §2, §9). Same reasoning applies unchanged: a cache belongs in the node's own `data_dir`, never beside the audio files. - **The node already classifies audio** — `indexer.py`'s `MEDIA_EXTENSIONS` already has an `"audio"` set (`.mp3`, `.flac`, `.ogg`, `.wav`, `.aac`, `.m4a`, `.opus`), and `IndexEntry.duration`/`.thumb_hash`/`.display_title` are already generic fields, not video-specific ones, per their own docstrings. No MNP change is needed to know a file is a track. ## 2. Where Music genuinely differs from Videos ### 2.1 Metadata mostly already exists in the files themselves A video file's name is usually all there is to go on; an MP3 collection — especially one assembled over many years — routinely carries **ID3 tags** (artist, album, title, track/disc number, year, genre) and often an **embedded cover image** (the `APIC` frame). Unlike Videos, where `guessit` had to do all the work, here the filename parser is the *fallback*, not the primary source. Order of trust, each one filling gaps the previous left: 1. **Embedded tags** (ID3v2 for MP3, Vorbis comments for OGG/FLAC, etc.), read with `mutagen` (pure Python, MIT, no native extension — same licensing shape as `guessit`, trivially addable to `packages/meshbay-node/pyproject.toml`). Also the source of an embedded cover image when present. 2. **Filename/folder parsing** for whatever tags don't supply — the existing `title_parse.py` pattern extended with a small audio-specific ruleset (`Track - Title.mp3`, a leading `NN - `/`NN. ` track-number prefix, the parent folder as a fallback album name, the grandparent as a fallback artist name for an `Artist/Album/track.mp3` layout). 3. **MusicBrainz**, node-side, fetched and cached exactly like TMDB — for canonical spelling, a missing field, or cover art when the file has none embedded. Read the whole of §3 before assuming this needs the same credential story as TMDB: it does not. A quick, honest read of a real personal MP3 library (several thousand files, classic P2P-era rips, folder-per-artist/album) during planning showed embedded tags present and broadly usable on the large majority of files, with gaps concentrated in older VBR rips and remix/compilation folders — exactly where the folder-name fallback already has something to say (the folder is named after the artist or the release). The library itself is not committed anywhere in this repo, the same discipline `mediacenter.md` §3.1 already follows for its own test corpus; a real accuracy figure belongs in an acceptance step (§8), not asserted here from a first look. **Amendment (2026-08-24):** an early version of this plan skipped a Videos- style scoping root, on the reasoning that tag reads are free/local (unlike ffprobe+ffmpeg thumbnailing) so the cost a root protects against was smaller. Real use showed the actual problem a root solves isn't cost, it's *mixing* — a shared tree with more than one kind of thing under it (a real messy library rarely holds only music) puts everything into one undifferentiated Music view with no way to scope it down. Music now has `audio_root`, symmetric to `video_root` in every respect including gating enrichment on it — see §4.3b. ### 2.2 No playback protocol change is needed at all — the big simplification Videos needed MSE, live ffmpeg remuxing, seek-by-restart, and a whole flow control mechanism (`video-player.js`) because a film is gigabytes and has to start playing before it is fully downloaded. A track is a few megabytes. **Music does not need any of that.** Playback reuses the download/decrypt pipeline `Files` and `Chat` attachments already use — `pipelinedDownload`/`downloadEntry` in `file-utils.js` — fetching and decrypting a whole track into a blob, then handing a `blob:` URL to an `