aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/music-player.js
Commit message (Collapse)AuthorAgeFilesLines
* fix(hub): show what's actually playing in the queue panelChristophe Besson2026-08-241-0/+24
| | | | | | | | The panel's header said "Playing now" but only the panel itself was named that — the current track was just a highlighted row you had to spot in the list, easy to miss on a long queue and often scrolled out of view entirely on open. Now shows the track's own title/artist right under the header and scrolls the highlighted row into view when the panel opens.
* feat(music): transcode WMA/Musepack to AAC so they actually playChristophe Besson2026-08-241-8/+37
| | | | | | | | | | | | | | | | | | | | | Tagging and covers for these two formats landed already, but neither one decodes in any mainstream browser's <audio> element at all — a real library scan turned up 273 such files that would show up correctly in the Music app and then simply fail on click. This closes that gap: the node transcodes to AAC/M4A on request (a one-shot whole-file conversion, not live-piped like video's fMP4 segments — an audio file is small enough that streaming it buys nothing), caches the result under its own content hash the same way a TMDB poster or a MusicBrainz cover is cached, and serves it back through the ordinary file_req/chunk path. That path used to assume anything in the media cache was thumbnail-sized (single chunk, always); generalized it to slice a cached blob the same way a real file on disk gets sliced, since a transcoded track can be several MB. New MNP pair (`audio_transcode_req`/`_resp`, version bump to 0.9), shares its concurrency cap with video's transcode pool rather than getting its own — both are real ffmpeg processes on the same node. Every other audio format is untouched: this only fires for .wma/.mpc, the two extensions that need it.
* fix(hub): auto-skip a track that fails to load instead of stalling the queueChristophe Besson2026-08-241-1/+35
| | | | | | | | | | A genuinely corrupt source file (found via a real "1 track, fails to play" report) would stop a "play all" queue dead with no way forward except manually picking the next track. The player now counts consecutive load failures and advances past them on its own, capped so a pathological queue (everything broken) doesn't spin forever; the existing Promise-rejection catch and a new <audio> onerror handler both feed the same counter, since a decode failure can surface either way depending on the browser.
* feat(hub): consolidate loose tracks, "&"/"and" fold, player close/queueChristophe Besson2026-08-241-1/+65
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Album-grid readability, part two: - groupMusicEntries (music-app.js): an album bucket left with exactly one track - a real album tag, but only one song from it, not the whole release - clutters the grid the same way an untagged loose track does. Both kinds now fold into one "<artist> - Various" tile per artist, unless there is only one leftover track overall, where relabeling buys nothing and the track keeps its own name (or the generic placeholder, if it never had one). - foldKey also normalizes "&" vs "and" ("Artist & The Band" / "Artist and The Band" is one act, tagged both ways across different rips of the same catalogue) alongside the existing case/whitespace fold. - music-player.js: a close button pauses and tears the player down; an unmount cleanup effect (pause, revoke every cached blob URL) fires either way, whether that's the close button or the shell tearing the bar down on its own. A "current queue" button opens an overlay listing the whole playing queue with the current track highlighted, click any to jump to it - works identically regardless of how the queue was built (an album, the consolidated misc bucket, a single standalone track), since it only ever reads the player's own live tracks/order/pos. - group-page.js: this component is not remounted when switching to a *different* group on the same /group/:id route (only the groupId prop changes) - so without an explicit reset, music from one group would carry into the next one opened. Resets musicQueue to null on groupId change; a tab switch inside one group still leaves it alone. - Scrubbed real artist/band names that had leaked into code comments and test fixtures (enrich_audio.py's docstrings, several test_enrich_audio.py assertions, a music-app.js comment) - replaced with generic placeholders, no behavioural change. - i18n: music.various, music.player_close, music.player_queue, music.queue_title added across all ten locales. Client-side only except none of this touches the node at all. npm run sync-ui re-run. Full suite: 1129 passed, no regressions. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBi7ALLGfwcjBXt57yNMcy
* feat(hub): Music app client — album grid, flat list, persistent playerChristophe Besson2026-08-241-0/+320
Implements the client half of docs/musicbay.md against MNP 0.8: - music-app.js: album grid (grouped by artist -> album, from index-time artist/album fields) or flat folder view, per-group localStorage toggle like Videos. MusicBrainz (music_meta_req) is only looked up when a track has no embedded cover at all — well-tagged files never trigger a network call, unlike Videos where TMDB is unconditional. Reuses video-app.js's MediaThumb/LazyTile (now exported) rather than duplicating the chunk-path thumbnail decode + virtualization. - music-player.js: the persistent player bar — queue, shuffle (Fisher- Yates, keeps the current track in place when toggled), repeat (off/ all/one), volume (localStorage), prev/next, a one-track prefetch cache. No MSE, no node-side streaming: a track is downloaded and decrypted once via file-utils.js's pipelinedDownload, same chunk pipeline Files already uses, then played from a blob URL. - group-page.js: owns musicQueue/musicbrainzConfig state and renders MusicPlayerBar outside the tab-switched area — deliberately, so playback survives navigating to Chat/Files, the same reasoning the video/preview modals are shell-owned rather than app-owned. - apps.js: registers "music". transport.js: fetchMusicMeta (keyed by path, same reordering-hazard fix as fetchMediaMeta), setMusicbrainzConfig/setMusicbrainzEnabled (signed ops, mirroring TMDB's), and the three new ack handlers. group-settings.js: a MusicBrainz settings section (contact string, per-group toggle) — the existing Applications checklist already picks up "music" for free, per apps.md's own claim. - icon.js: music/pause/skip-next/skip-prev/shuffle/repeat/volume, drawn in the same stroked style as the existing set. - i18n: group.tab_music, the music.* and settings_node.musicbrainz_* keys, translated (not just copied) across all ten locales, Polish carrying full one/few/many/other plural forms for music.n_tracks. - webapp.py's _ASSETS, test_hook_ordering.py's STATIC_FILES and test_transport_contracts.py's SPLIT_FILES gain the two new files. Full suite (common + hub + node): 1116 passed, no regressions. `npm run sync-ui` in meshbay-client confirmed both files copied. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBi7ALLGfwcjBXt57yNMcy