Commit Graph
316 Commits
Author SHA1 Message Date
HoneyandClaude Sonnet 4.6 f7012dab49 fix: login flash, album name casing, lrclib duration mismatch
- Android: show nothing while DataStore loads credentials (null initial
  value) to prevent login screen flash on startup when already logged in
- Web: dispAlbumName now only uses sortName as romaji for Japanese titles;
  English albums with lowercase ALBUMSORT tags now display correct casing
- lrclib: add no-duration fallback exact match to find songs where local
  file duration differs from lrclib entry (e.g. Sanctuary Opening 5:58 vs 4:24)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 22:50:11 +02:00
HoneyandClaude Sonnet 4.6 0272b9e99f chore: remove NetEase lyrics fallback from Android and web
NetEase returned wrong/unsynced results. lrclib with exact + fuzzy
search is sufficient; no third-party Chinese platform needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 21:48:46 +02:00
HoneyandClaude Sonnet 4.6 eb5b0ea360 fix: lrclib rate limit — exact first, then 2 parallel searches; check HTTP 200
3 simultaneous requests were hitting lrclib rate limiter.
Now: exact match alone → if no content, 2 parallel searches.
Also check response.status === 200 before parsing to avoid treating
404/429 error bodies as valid empty lyrics responses.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 21:46:09 +02:00
HoneyandClaude Sonnet 4.6 d7f8b301ab perf: parallel lrclib requests + in-memory cache for lyrics
- Android + web: fire exact/artist-search/title-search in parallel so
  total wait = slowest single request instead of sum of all three
- Android: HashMap cache keyed by artist|title, cleared on app restart
- Web: Map cache (session lifetime), cache hit is instant re-open
- Web: extract _showLyricsResult helper, safeJson wrapper

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 21:26:07 +02:00
HoneyandClaude Sonnet 4.6 d0df01a76d fix: lyrics faster + title-only search fallback for metadata mismatches
- Android: add 6s HttpTimeout to LrcLibClient, wrap each step in withTimeoutOrNull
- Both: add title-only lrclib search (step 3) so "Sanctuary (Opening)" is found
  even when artist tag differs ("Utada" vs "Hikaru Utada")
- Web: reduce all timeouts 8s → 5s, extract bestMatch helper

Search order: lrclib exact → lrclib artist+title → lrclib title-only → NetEase

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 21:20:47 +02:00
HoneyandClaude Sonnet 4.6 71667d1a03 feat: add NetEase as lyrics fallback after lrclib (Android + web)
Search order: lrclib exact → lrclib fuzzy search → NetEase.
NetEase has strong coverage for Asian/Japanese music that lrclib misses.
Duration-matched candidate selection on all three steps.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 20:50:16 +02:00
HoneyandClaude Sonnet 4.6 8b9bfd3098 fix: restore direct lrclib lyrics search with fuzzy fallback (both platforms)
Navidrome lyrics plugin does not implement getLyricsBySongId for synced
lyrics — removed all plugin calls. Both Android and web now query
lrclib.net/api/get (exact match) with /api/search fallback for metadata
mismatches (e.g. artist tagged differently in Navidrome vs lrclib).
Also fixes Android lyrics scroll tween<Int> type inference error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 20:45:32 +02:00
HoneyandClaude Sonnet 4.6 7e20062995 feat: Android Auto, radio mode, lyrics overhaul + web lyrics cleanup
Android:
- fix: Android Auto credentials never set on MusicService → artwork/stream null
- feat: Android Auto tabs reduced to Zuletzt gespielt, Favoriten, Playlists
- feat: radio mode (getSimilarSongs2 → getRandomSongs fallback, <5 songs refill)
- fix: lyrics now use Navidrome getLyricsBySongId plugin exclusively
- fix: lyrics scroll rewritten with Column+ScrollState absolute animateScrollTo
- fix: lyrics recomposition lag — extracted LyricsLineItem + key(idx) per line
- chore: remove dead LrcLibClient (direct lrclib bypassed Navidrome plugin)

Web:
- fix: lyrics now use Navidrome getLyricsBySongId only, removed direct lrclib fallback

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 20:30:20 +02:00
HoneyandClaude Sonnet 4.6 55eef2f831 feat: randomized home greetings (web PWA)
Same pool of music-themed phrases as Android — picks randomly each load.
With username: "Ready for some beats, Marc" — without: "Pick something great"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 00:38:21 +02:00
HoneyandClaude Sonnet 4.6 afae5d94df feat: partial album split view + artist page full-screen + Bibliothek iTunes integration
- Partial album detail: tapping incomplete albums now shows downloaded songs
  (normal playback) above and missing iTunes tracks (30s preview + download)
  below — on both Android and web PWA
- Android: ItunesArtistScreen now renders full-screen above the tab/search bar
- Android: ArtistDetailScreen (Bibliothek) now loads iTunes data in parallel —
  adds Weltweit populär section and In Library/Unvollständig/Nicht heruntergeladen
  album sections identical to the Erkunden artist page
- Fix: artist no longer appears in its own Similar Artists row
- Fix: duplicate album entries deduplicated by collectionId in categorizeAlbums

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 20:54:32 +02:00
HoneyandClaude Sonnet 4.6 59adee60d5 fix: invalidate sort caches after metadata save, remove sort-debug logs
After saving sort tags via the metadata editor, invalidate _artistSortMap
and the apiCached getArtists entry so the updated Navidrome sortName values
are picked up on the next page visit without a hard refresh.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 23:34:12 +02:00
Honey 85f8d758a5 debug: full JSON dump of song/album + artist sort map diagnostics 2026-06-08 23:23:41 +02:00
HoneyandClaude Sonnet 4.6 9e6e81bc22 fix: auto-populate artist sort map on album page — no Artists page visit needed
ensureArtistSortMap() fetches getArtists once per session (cached) and
populates _artistSortMap automatically. Called at start of loadAlbumDetail
when showRomaji is on, so artist sort names are always available.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 23:07:02 +02:00
Honey 6851c37e15 fix: remove non-existent sortArtist/artistSort checks from dispArtist — Navidrome only exposes sortName on artist objects 2026-06-08 23:04:46 +02:00
HoneyandClaude Sonnet 4.6 b4ece2db85 fix: use r.album.sortName for album name — Navidrome only exposes sortName on album objects
Song objects carry sortName for title only, no album/artist sort fields.
Album sort is only available on the album object itself (r.album.sortName).
Updated loadAlbumDetail to use the fresh API response album, not the nav album.
Simplified dispAlbum to not check non-existent song sort fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 23:00:16 +02:00
Honey 228f58d6d5 debug: log all sort keys from Navidrome song and album response 2026-06-08 22:55:03 +02:00
Honey 44ef685922 debug: log sort fields from Navidrome album/song response 2026-06-08 22:50:41 +02:00
HoneyandClaude Sonnet 4.6 e3987a6376 feat: album names use Navidrome sortName tag when showRomaji is on
dispAlbumName now takes the full album object and reads album.sortName
directly — no kuromoji fallback, no backend translation.
dispAlbum (song context) also drops the kuromoji fallback, keeping only
the sortAlbum/albumSort file tag.

Override list → Navidrome sort tag → raw Japanese name.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 22:42:53 +02:00
HoneyandClaude Sonnet 4.6 4c3ec5726c fix: dispArtistById prefers Navidrome sortName over kuromoji translation
Added _artistSortMap populated from getArtists (artist listing) and
getArtist (artist detail) responses. dispArtistById now checks the
Navidrome sort tag first, falls back to kuromoji only when missing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 22:35:40 +02:00
HoneyandClaude Sonnet 4.6 da68afe0e0 fix: remove romaji album name from player bar and now-playing overlay
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 22:30:25 +02:00
HoneyandClaude Sonnet 4.6 8f7f1e2635 fix: artist hero appears instantly; album names skip romaji conversion
Hero now only waits for getArtist (fast local Navidrome) before revealing.
getArtistImage (Last.fm lookup) was the 2-4s bottleneck when uncached —
it now resolves async and patches in the artist photo after the hero is
already visible.

Album card, album detail header, and home hero now display the raw album
name from Navidrome instead of converting to romaji when showRomaji is on.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 22:23:35 +02:00
HoneyandClaude Sonnet 4.6 2c9d3a9dde perf: cache Ticketmaster event results for 30 min per artist
Avoids burning API quota on repeat visits to the same artist page.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 20:52:50 +02:00
HoneyandClaude Sonnet 4.6 adcbb2e883 feat: replace Bandsintown with Ticketmaster for concert events
Backend: swap /api/events/:artist to use Ticketmaster Discovery API
(startDateTime=now, classificationName=music, size=20, sort by date).
Maps TM response to the same {datetime, venue, offers} shape the modal
expects. Returns [] if TICKETMASTER_API_KEY is not set.

Frontend: remove all debug console.logs from events.js; drop redundant
client-side date filter (TM already filters by startDateTime server-side).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 20:51:51 +02:00
HoneyandClaude Sonnet 4.6 c8e3f5b641 perf: show artist hero after 2 fast calls instead of waiting for all 6
Split the allSettled into two phases: await imgP+albumP first (both are
fast — getArtistImage hits S.artistImgCache when navigating from the
listing, getArtist is a local Navidrome call), then reveal the hero.
The slow calls (getArtistInfo2 / Last.fm bio, iTunes search, events)
keep running in parallel and are only awaited when rendering the content
sections below the hero — so the user sees the hero card immediately.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 20:42:03 +02:00
HoneyandClaude Sonnet 4.6 1f9495f806 fix(android): persist artists array in localStorage so multi-artist splits survive cold start
The queue serialization was saving artist as a plain string but not the
artists array. On cold start the app restores from localStorage, so
parseArtists had no array to work with and fell back to splitting the
artist string by semicolons — "Maduk Veela" has none, so it returned
one combined entry. PWA always re-fetches fresh API data so the array
was always present. Adding artists:[] to both song and queue entries
fixes artist navigation after cold start on Android.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 20:31:08 +02:00
HoneyandClaude Sonnet 4.6 60e89fe3b6 fix(android): instant cold-start resume + play button icon flicker
WebBridge: skip setMediaItems/prepare when the track is already buffered
from the cold-start pre-warm, so resuming the last song is instant (~0s)
instead of re-buffering from scratch (~10s).

Player: show pause icon during buffering (isPlaying=false, isBuffering=true)
so the button doesn't flash back to "play" between tracks. Also optimistically
set _nativePlaying=true and call setPlayIcon immediately on playCurrent so the
icon updates before the native callback round-trip.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 20:08:56 +02:00
HoneyandClaude Sonnet 4.6 128437821a fix: instant OS notification update + real audio preload for gapless playback
- Call updateMediaSession immediately on track change so skip buttons and
  cover art in the OS notification bar update without waiting for audio load
- Replace link[rel=prefetch] (no-op for audio streams) with a shadow Audio
  element (preload=auto) that actually buffers the next track
- Start preloading 60s before track end instead of 30s for more buffer margin

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 19:57:52 +02:00
HoneyandClaude Sonnet 4.6 f04d87254b feat: concerts button always visible, shows 'Keine Konzerte angekündigt' when empty
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 01:26:46 +02:00
HoneyandClaude Sonnet 4.6 4bb131b219 debug: add console logs to fetchEvents to diagnose empty results
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 01:26:18 +02:00
HoneyandClaude Sonnet 4.6 98c964ecae fix: proxy Bandsintown events through backend to bypass 403, always show ticket link with sold-out badge
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 01:23:00 +02:00
HoneyandClaude Sonnet 4.6 914297651e feat: artist concerts via Bandsintown — button + modal with ticket links
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 01:09:52 +02:00
HoneyandClaude Sonnet 4.6 f808db7eae feat: remove color tint overlay from artist hero
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 00:59:44 +02:00
HoneyandClaude Sonnet 4.6 c3d0360946 fix: hero reveal — set bg inside pfp onload so cache is warm, no state machine
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 22:51:13 +02:00
HoneyandClaude Sonnet 4.6 55cfaf0392 fix: hero reveals on image load only, color tint non-blocking CSS fade
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 22:39:06 +02:00
HoneyandClaude Sonnet 4.6 28de177381 fix: hero waits for pfp + bg + color before revealing — no more incremental flicker
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 22:36:31 +02:00
HoneyandClaude Sonnet 4.6 0a3c72e2fd fix: remove expandArtistBio from main.js imports and window binding
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 22:34:28 +02:00
HoneyandClaude Sonnet 4.6 f91176da2a feat: artist bio always shows full text, remove Mehr lesen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 22:29:24 +02:00
HoneyandClaude Sonnet 4.6 b436e39f40 fix: hero non-blocking — reveals on pfp load, tint fades in via CSS
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 22:28:59 +02:00
HoneyandClaude Sonnet 4.6 42fe5d802b fix: hero fades in complete — no more incremental load flicker
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 22:19:37 +02:00
HoneyandClaude Sonnet 4.6 e2964829bd feat: artist hero — dominant color tint + artist photo as full background
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 22:15:48 +02:00
HoneyandClaude Sonnet 4.6 4131907ebd fix: hide pfp initial when photo loads, brighter album art background
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 21:43:33 +02:00
HoneyandClaude Sonnet 4.6 0ac7fc5383 fix: artist hero — large square pfp on left, brighter background
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 21:39:45 +02:00
HoneyandClaude Sonnet 4.6 64aec78a43 feat: artist hero redesign — album art banner + circular pfp on left
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 21:31:54 +02:00
HoneyandClaude Sonnet 4.6 79a4aa352f fix: artist hero desktop uses blurred backdrop + natural-proportion portrait
Single object-position can never work well for portrait photos in a ~6:1 wide
banner — we'd only see ~11% of the image height no matter the value.

Desktop now uses two layers:
  1. artist-hero-bg: same image, blurred + darkened, fills full banner width
  2. artist-hero-img: sharp portrait anchored to the right at natural proportions
     (height: 100%, width: auto — no cropping, full face visible)

Mobile unchanged (cover + center center was already working great).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 21:11:24 +02:00
HoneyandClaude Sonnet 4.6 aa20eec29d fix: artist hero uses <img> with responsive object-position for better crop
Switching from background-image (inline JS, single position for all sizes)
to an <img> element lets CSS media queries apply different object-position:
- Mobile: center top (portrait images show face in upper portion)
- Desktop: center 30% (wide banner, lower anchor avoids showing only hair)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 21:05:47 +02:00
HoneyandClaude Sonnet 4.6 e3c435420c fix: artist hero banner shows face/upper body on desktop
background-position: center crops to mid-body on wide desktop banners.
Shifting to center 20% anchors the crop in the upper-third of the image
where faces typically are, matching the phone experience.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 20:57:22 +02:00
HoneyandClaude Sonnet 4.6 920e385a8f fix: break static circular dep in notify.js that caused 'Failed to fetch' after ui split
notify.js statically imported navigate from views/views.js, creating a new
circular chain: navigation.js → ui/ui.js → settings.js → downloads.js →
notify.js → views/views.js → navigation.js. This chain did not exist in the
old monolithic ui.js and caused module initialisation failures in some browsers.

Fix: make navigate a lazy dynamic import inside _notifNavigate() body, which
is the only call site. Also replace unnecessary dynamic import('../utils.js')
calls with the already-statically-imported toast function.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 19:49:29 +02:00
HoneyandClaude Sonnet 4.6 6391857fda feat: configurable scrobble timing (15s / 30s / 1/3 / 2/3 / end)
Settings dropdown: 15s (default), 30s, 1/3 of duration, 2/3 of duration, song end.
Duration-based modes use s.duration from Navidrome metadata (always available).
'end' mode skips the timer and fires the scrobble in onEnded (web) / onQueueEnded (native).
Skipping a song never scrobbles in any mode — _cancelScrobble() still runs on skip/pause.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 17:53:02 +02:00
HoneyandClaude Sonnet 4.6 acb15d8c00 fix: hero card shows romaji for album name and artist
loadHeroCard() now pre-fetches the romaji index (same pattern as loadAlbumRow)
and uses dispAlbumName()/dispArtistById() instead of raw album.name/artist.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 14:05:10 +02:00
HoneyandClaude Sonnet 4.6 0663b1c923 refactor: split ui/ui.js into 5 focused modules
settings.js  — settings modal, romaji overrides, sidebar, theme, nav mode
modals.js    — context menu, playlist-add modal, confirm dialog, edit-playlist modal
metadata.js  — song info/edit modal, MusicBrainz/iTunes autofill, saveMetadata, deleteSong
notify.js    — notification panel, bell badge, _addNotif, _notifNavigate
downloads.js — download modal, Spotify download, jobs panel, startArtistSortFix
ui.js        — barrel re-export; all external imports unchanged

player.js left unsplit: shared private state (_nativePlaying, _playGen, _queueGen, etc.)
spans every section — splitting would require exporting internal state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 14:00:38 +02:00