Commit Graph
100 Commits
Author SHA1 Message Date
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 8c8f03503b fix: multi-artist click in NowPlaying — individual composables per artist
Replace AnnotatedString/ClickableText (offset-based click was unreliable)
with a horizontally-scrollable Row of individual Text composables. Each
artist has its own explicit clickable modifier bound directly to its ID.

ID de-duplication: if Navidrome copies the primary artistId to all entries
in the artists array, only the first keeps the ID; others render as
non-navigable text (dimmed NsAccent) instead of silently going to wrong page.

Fallback: split song.artist by ';' when artists array is absent/single,
matching the web app's parseArtists() logic.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 00:44:59 +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 f15c005a3c feat: multi-artist support in NowPlaying (OpenSubsonic artists array)
- Song model: add SongArtist{id,name} + artists: List<SongArtist>
  Navidrome sends this via OpenSubsonic extension — previously ignored
- NowPlaying: if song.artists has >1 entry, render each name as an
  inline AnnotatedString with ClickableText, separated by ' · ';
  every artist navigates to their own page with their real ID
  Falls back to single artist/artistId entry when array is absent
- AppNavigation: onArtistClick now receives artistId: String instead
  of a fixed closure, enabling per-artist navigation from the sheet

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 00:37:08 +02:00
HoneyandClaude Sonnet 4.6 0e651e8be3 feat: randomized home greetings (music-themed, EN)
Replace static time-of-day greetings (Guten Morgen/Tag/Abend) with a random
pick from a pool of 10-12 music-themed phrases each time HomeScreen is composed.
With username: "Ready for some beats, Marc" — without: "What's on the playlist today?"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 00:28:44 +02:00
HoneyandClaude Sonnet 4.6 3855207530 feat: artist cards with image in search + fix action bar overflow
Search: replace SmallArtistChip (text pill) with ArtistCard showing
circular profile image (72dp) + name below; works for both Navidrome
artists (uses coverArt) and iTunes artists (uses artworkUrl100 if present)

ArtistDetailScreen: convert action bar Row → LazyRow so Play / Shuffle /
Konzerte pills sit comfortably with horizontal scroll instead of cramming

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 00:24:18 +02:00
HoneyandClaude Sonnet 4.6 13e9ec9d9a feat: Konzerte button on artist page (Android)
- EventModels: ConcertEvent / ConcertVenue / ConcertOffer (Kotlinx Serializable)
- EventsClient: Ktor client fetching backendUrl/api/events/{artist}
- ArtistViewModel: fetches events in parallel with iTunes data on load;
  openEventsSheet / dismissEventsSheet
- ConcertsSheet: ModalBottomSheet with event rows (date, venue, city,
  Tickets → button that opens browser, Ausverkauft state)
- ArtistDetailScreen: Konzerte pill in action bar showing event count,
  tapping opens ConcertsSheet

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 00:17:55 +02:00
HoneyandClaude Sonnet 4.6 b5aa484c20 fix: push NowPlaying cover and controls down by 28dp
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 23:35:59 +02:00
HoneyandClaude Sonnet 4.6 42c12cbd3a fix: genre loading spinner + NowPlaying back button priority
GenreViewModel: GenreUiState(genres = it) left isLoading=true (default),
so genres loaded but spinner never cleared. Added isLoading=false explicitly.

AppNavigation: replace BackHandler + navController.enableOnBackPressed with a
DisposableEffect that re-adds an OnBackPressedCallback each time NowPlaying
opens. Since callbacks are LIFO, a freshly-added callback always beats the
BackHandler NavHost registers internally, regardless of composition order.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 22:49:40 +02:00
HoneyandClaude Sonnet 4.6 f01fc71f54 feat: back navigation, NowPlaying fix, home refresh, genre top-6
- ExploreScreen: add BackHandlers for state-based overlays (partialAlbum →
  albumTracks → artistDetail hierarchy) so system back navigates correctly
- AppNavigation: disable NavController's OnBackPressedCallback while NowPlaying
  is open so our BackHandler always intercepts first (fixes tab-switch-then-back bug)
- Home tab: tapping Home while already on Home triggers vm.load() refresh via refreshKey
- HomeViewModel: load all genres sorted by songCount desc (removed take(12) cap)
- HomeScreen: show only top-6 genres + 'Alle anzeigen' button with total count
- New GenreListScreen + GenreViewModel: full scrollable genre list at 'genres' route

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 22:40:48 +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 b25e162806 fix: safe area and UI polish pass
- Enable dark navigation headers in Tab navigator (fixes status bar overlap
  on all 5 tab screens automatically)
- Use useSafeAreaInsets for AlbumDetail/ArtistDetail back buttons
- Use useSafeAreaInsets for NowPlaying header and bottom padding
- Remove redundant paddingTop from Home heading and Login content
- Cleaner tab bar (height 60, better label spacing)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 14:22:02 +02:00
HoneyandClaude Sonnet 4.6 98394bab75 fix: spread DarkTheme to include fonts in NavigationContainer theme
React Navigation v7 requires theme.fonts (medium, regular, bold, heavy).
Providing a partial theme without fonts caused BottomTabItem to crash
with "Cannot read property 'medium' of undefined".

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 13:45:27 +02:00
HoneyandClaude Sonnet 4.6 4027827312 fix: add babel-preset-expo devDependency and babel.config.js
Without this, Metro fails to bundle because the preset is not found.
Verified: npx expo export --platform android now bundles 1093 modules clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 13:04:35 +02:00
HoneyandClaude Sonnet 4.6 db80f41018 fix: upgrade to react-native-track-player 5.0.0-alpha0 (Media3/New Arch)
- RNTP 4.1.2 used ExoPlayer 2.x which is incompatible with RN 0.85.3
- RNTP 5.0.0-alpha0 uses androidx.media3 and supports New Architecture
- Remove newArchEnabled:false from app.json (New Arch is default)
- Remove Kotlin null safety patch (no longer needed)
- Remove patch-package (no more patches required)
- Fix getState() -> getPlaybackState() API rename in player.js
- Add babel.config.js with babel-preset-expo

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 12:53:02 +02:00
HoneyandClaude Sonnet 4.6 1856452d8f fix: remove react-native-reanimated and react-native-worklets
These packages require New Architecture (Fabric), which conflicts with
react-native-track-player requiring Old Architecture (newArchEnabled: false).
Neither of these packages was imported anywhere in our code.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 11:30:42 +02:00
Honey a48093d6d8 fix: disable New Architecture for react-native-track-player compatibility 2026-06-10 10:19:29 +02:00
Honey a65a3eeb5b fix: add error boundary, harden player setup, catch init errors 2026-06-10 09:51:42 +02:00
Honey 2327778136 fix: move gesture-handler and TrackPlayer init to index.js, fix Dimensions usage 2026-06-10 09:21:27 +02:00
Honey 45ec25d257 fix: patch react-native-track-player for Kotlin 2.x null safety 2026-06-10 01:23:20 +02:00
Honey fac3ab670d fix: align dependencies to Expo SDK 56 2026-06-10 01:16:22 +02:00
Honey 68912522f8 fix: correct eas.json buildType value 2026-06-10 01:10:51 +02:00
Honey 3c9602f861 chore: add eas.json for cloud builds 2026-06-10 01:08:16 +02:00
Honey 131097550a fix: remove invalid track-player config plugin, fix adaptive icon path 2026-06-10 00:28:48 +02:00
HoneyandClaude Sonnet 4.6 1b96706a1c feat: add Expo mobile app (mobile/)
Full React Native app using react-native-track-player for native
audio — fixes background freeze, autoplay stopping, stuck play
button, and enables Android Auto in future. Includes:

- Navidrome API layer (ported from src/api.js)
- Zustand global store with SecureStore config persistence
- react-native-track-player with background playback service
- Bottom tab navigation: Home, Artists, Albums, Search, Settings
- Screens: Home, Library (Artists/Albums), ArtistDetail, AlbumDetail,
  Search, NowPlaying, Login/Settings
- Persistent PlayerBar with progress indicator
- Romaji/sort-tag display support carried over from web app

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 23:19:06 +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 28489f722a docs: rewrite README with current features, structure and env vars
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 21:56:54 +02:00
HoneyandClaude Sonnet 4.6 fc2c5de1fb fix: pass TICKETMASTER_API_KEY env var to backend container
Was missing from docker-compose environment list so the key was never
visible to the Node.js process even when set in .env.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 21:49:39 +02:00
HoneyandClaude Sonnet 4.6 d47a3404b3 debug: add /api/events-debug/:artist endpoint to inspect raw Ticketmaster response
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 21:06:10 +02:00
HoneyandClaude Sonnet 4.6 7fe755379b fix(events): fix Ticketmaster URL encoding + add error logging
Colons in startDateTime were being percent-encoded which Ticketmaster
may reject. Also log errors/status so backend logs reveal the cause
when no events appear.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 20:57:26 +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 def625a622 chore: remove unused SPOTIFY_CLIENT_ID/SECRET from .env.example
The Spotify importer resolves links without OAuth credentials — these
variables were never read by any code.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 20:47:30 +02:00
HoneyandClaude Sonnet 4.6 972c6b9722 docs: add TICKETMASTER_API_KEY to .env.example
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 20:43:03 +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 76efa393ea fix(android): keep skip buttons visible in notification
Returning RESULT_ERROR_NOT_SUPPORTED from onPlayerCommandRequest caused
Media3 to mark the skip commands as failed and hide the buttons. Now
returns RESULT_SUCCESS so the notification keeps them enabled, while
ForwardingPlayer no-ops seekToNext/Previous so ExoPlayer never tries to
advance its single-item queue — JS still owns all queue navigation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 20:06:33 +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 6bbebcc602 fix: increase mobile artist hero height to 320px
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 01:00: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 32bde3ce87 feat: mobile artist hero — full-bleed image with name, no pfp square
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 00:51:49 +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 ce0697e00a fix: restore center center on mobile artist hero (was breaking phone view)
Only override object-position on desktop (>=769px) to center 30%.
Mobile keeps center center which was already working great.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 21:07:23 +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 106fbf54bf refactor: reorder settings sections — important ones first, login last
Theme → Bibliothek (+ Romaji) → Audiobookshelf → Discover APIs → Navidrome Server
Also merged the standalone Romaji Korrekturen row into the Bibliothek section.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 20:59:10 +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
HoneyandClaude Sonnet 4.6 2a2e5ea495 refactor: split views/views.js into 6 focused modules
navigation.js  — navigate(), history, closeHeaderSearch, _navGoBack
home.js        — home page, genres, album rows, loadArtists/loadAlbums, getArtistImage
detail.js      — artist/album/iTunes detail pages, quickPlay helpers, expandArtistBio
playlists.js   — playlists, playlist detail, reorder, starred, random
search.js      — local/header/global search, iTunes helpers, s_goArtist, playerBarGoX
discover.js    — new releases, recommendations (Last.fm + iTunes fallback)
views.js       — barrel re-export; all external imports unchanged

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 13:50:36 +02:00
HoneyandClaude Sonnet 4.6 308a27d293 refactor: restructure src into player/views/ui subfolders; fix notification skip buttons
- Move player.js + nowplaying.js → src/player/
- Move views.js + spotify/soundcloud/youtube/abs → src/views/
- Move ui.js + components.js → src/ui/
- Update all import paths (static + lazy) across every file
- MusicService: wrap ExoPlayer in ForwardingPlayer that always advertises
  next/prev commands so the notification skip buttons stay visible in radio
  mode even when the JS queue has only one native track

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 13:12:27 +02:00
HoneyandClaude Sonnet 4.6 d8908c69e9 feat: add session-level API cache — skip spinners on home/albums/artists/playlists revisit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 11:45:22 +02:00
HoneyandClaude Sonnet 4.6 08ca8303f3 chore: remove unused _syncNpDirect import; add express to backend/package.json
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 01:21:41 +02:00
HoneyandClaude Sonnet 4.6 45cb62198e fix: album cards use romaji index for album name display via _albumRomajiMap
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 01:10:38 +02:00
Honey 52f5385988 fix: import dispAlbum in views.js 2026-06-06 01:03:56 +02:00
HoneyandClaude Sonnet 4.6 eb47a42f15 fix: album detail header re-renders with sort tags after songs load; search artist uses dispArtistById
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 00:56:33 +02:00
HoneyandClaude Sonnet 4.6 25d80eb559 feat: use file sort tags (sortAlbum/sortArtist/sortName) as display override when romaji is on
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 00:51:22 +02:00
HoneyandClaude Sonnet 4.6 961511b3d0 fix: apply romaji disp functions in _restoreLastPlayed and album detail header
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 00:44:28 +02:00
HoneyandClaude Sonnet 4.6 8704a17b4f fix: romaji display race on reload; iTunes search uses original Japanese name
- Await romaji overrides before restoring player bar so disp* functions
  have the cache ready on first render after reload
- dispArtistParts now carries originalName so artist clicks pass the
  Japanese name to Navidrome/iTunes search while displaying romaji

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 00:09:13 +02:00
HoneyandClaude Sonnet 4.6 ccdcda74ec fix: apply romaji overrides to album cards on home/albums/artist views
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 23:43:27 +02:00
HoneyandClaude Sonnet 4.6 01407ef3f1 feat: update sort-tag labels to reflect Romaji / English support
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 14:08:50 +02:00
HoneyandClaude Sonnet 4.6 bb927e8120 fix: continue lrclib attempts if first response has no lyrics
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 12:01:33 +02:00
Honey 570eb803c2 Kanji → korrektes Romaji für Sort-Tags -> Deleted 2026-06-05 11:50:28 +02:00
HoneyandClaude Sonnet 4.6 89241a4fe6 fix: update romaji overrides hint text — accurate description with line break
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 10:48:35 +02:00
HoneyandClaude Sonnet 4.6 36a0b38eca feat: server-side romaji overrides (shared list); preload ExoPlayer from SharedPreferences on cold start
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 10:47:25 +02:00
HoneyandClaude Sonnet 4.6 b924f01b2a fix: ignore onQueueEnded before onReady fires — prevents stale end-of-song event from advancing queue on app reopen
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 01:33:16 +02:00
HoneyandClaude Sonnet 4.6 21d55d8ab0 fix: suppress spurious onQueueEnded after reconnect; handle STATE_ENDED on reopen; add isEnded to snapshot
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 01:03:19 +02:00