Commit Graph
615 Commits
Author SHA1 Message Date
Honey 85252e24df fix: unresolved reference 'albums' after rename to albumsByNewest/albumsByName 2026-06-18 20:21:36 +02:00
HoneyandClaude Sonnet 4.6 7030f029b7 docs: finalize CHANGELOG v0.7.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 20:15:11 +02:00
HoneyandClaude Sonnet 4.6 93c5fb481d feat(android): add description field to playlist edit dialog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 20:13:19 +02:00
HoneyandClaude Sonnet 4.6 43cf11465e fix: playlist edit — cover upload in pencil dialog, remove cover-remove button
Android: camera hero button removed; cover upload moved into edit dialog.
Web: trash icon removed from cover actions in edit modal.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 20:12:27 +02:00
HoneyandClaude Sonnet 4.6 6a1bf33f31 fix(android): album Neueste now uses Navidrome type=newest (add-date), not year sort
Loads albumsByNewest (type=newest) and albumsByName (alphabetical) in parallel.
Neueste → Navidrome add-date order; A-Z → alphabetical; Älteste → client-side by year asc.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 20:09:32 +02:00
HoneyandClaude Sonnet 4.6 9ffd071c58 feat(android): artist fast-scroll index bar with letter bubble
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 20:06:44 +02:00
HoneyandClaude Sonnet 4.6 dd484df636 fix(android): album sort — scroll to top on change, remove Künstler, default Neueste
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 20:05:00 +02:00
HoneyandClaude Sonnet 4.6 6be7981b8a feat(android): playlist mgmt, album sort, home hero parity, notification fix
- Playlist: create (FAB + dialog), rename, delete, remove songs, cover upload/remove
- Library Albums tab: sort chips (Neueste/Älteste/A-Z/Künstler) matching StarredScreen
- ABS notification: shows current chapter title; updates live without re-buffering
- Home hero: pool now matches web (frequent(5) ∪ recentlyPlayed(5)); label MEISTGESPIELT / ZULETZT GEHÖRT
- Home: added Zuletzt gehört section (type=recent) matching web layout
- Notifications: fixed delivery bug — polling moved to NotificationStore app-lifetime scope so notifications arrive even after user navigates away from ImporterScreen
- NotificationStore.startPoll() extended with navType/navAlbum/navArtist/navId params
- Removed defunct AbsPlayerService.kt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 19:04:36 +02:00
HoneyandClaude Sonnet 4.6 eacce487df fix: lyrics scroll — center active line only, remove drift phase
The 'drift to next line' animation moved the text while the user was
still reading/singing the current line, making it feel out of sync.
Now matches the web behavior: smooth-scroll to center the active line
when it changes, nothing more.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 01:39:51 +02:00
HoneyandClaude Sonnet 4.6 ec9c0add9e feat: notification nav + ABS search + HTML desc fix + brighter NowPlaying bg
- Notification sheet: add 'Zum Album →' / 'Öffnen →' button; tapping searches
  Navidrome and navigates to the matching album/playlist
- AppNotification: add navType/navAlbum/navArtist/navId fields (mirrors web notify.js)
- ImporterViewModel: store resolved album/artist/type in notification after download
- ABS Android: search bar with 400ms debounce, server-side search via /search endpoint
- AbsClient: add searchLibraryItems() for library-specific search
- AbsScreen/abs.js: strip HTML tags from book descriptions (<p> tags no longer visible)
- NowPlayingScreen: vibrant palette swatch + lighter scrim (0.15) + wider gradient
  for more visible dominant color background

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 01:38:58 +02:00
HoneyandClaude Sonnet 4.6 72feac41bb fix: ABS skip library list + nav label overflow + NowPlaying top bar + dominant color bg
- ABS: new AbsBooksScreen loads books directly on tab open; library pills shown only when >1 library — no more two-step library→books navigation
- Nav: shorten "Hörbücher"→"Bücher", "Einstellungen"→"Einst.", reduce label font to 9sp with maxLines=1 to prevent label wrapping
- NowPlaying: increase top padding so the tab bar sits lower on screen (statusBarTop + 16dp, min 52dp)
- NowPlaying: replace blurred-cover background with dominant-color gradient extracted via Palette from cover art; animates on track change; falls back to dark color

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 00:23:43 +02:00
HoneyandClaude Sonnet 4.6 820a75f40b feat: ABS full playback on Android — chapters, speed, sleep timer, progress sync
- AbsClient: add session API (startSession, closeSession, getProgress, saveProgress, getItemChapters) and data classes (AbsChapter, AbsAudioTrack, AbsPlaySession, AbsProgress)
- AbsPlayerController (new): standalone ExoPlayer controller for ABS audio; tracks global time across multi-track audiobooks, syncs progress to ABS every 30 s, chapter navigation, speed control, sleep timer
- AbsScreen: book detail screen with cover/meta/progress/chapter list/play button; AbsNowPlayingScreen overlay (progress bar, prev/+30s/play/-30s/next controls, speed picker, sleep timer, chapter list with auto-scroll)
- AppNavigation: create AbsPlayerController, wire up book detail and player overlay, hardware back button handling for all ABS sub-screens
- PrefsStore + SettingsViewModel + SettingsScreen: absChapterTrackMode toggle — progress bar shows current chapter length instead of full book
- README: update ABS section to cover both web and Android features

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 00:15:30 +02:00
HoneyandClaude Sonnet 4.6 3280ffee37 docs: trim README to core features only; fix: remove duplicate save button in Android settings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 23:58:15 +02:00
HoneyandClaude Sonnet 4.6 ba16f88355 docs: update README — accurate feature list + Android native app description
Removed:
- "WebView wrapper" — Android is native Jetpack Compose, not a WebView
- "Artwork pre-warming / pre-buffer on cold start" — not implemented this way
- ABS chapters/speed/sleep/progress on Android — web only for now

Added:
- Lyrics section: lrclib.net, fallback chain, in-memory cache, synced scroll
- Android: last played song in MiniPlayer across restarts
- Android: ABS library browser tab (hidden until configured)
- Android: no login flash on startup
- Settings: connection status dots, save button top+bottom, matched web order
- Multi-artist support in NowPlaying
- Artist cards with images in search
- Genre browsing
- ABS web vs Android split into separate entries
- Accurate Android project structure

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 23:56:02 +02:00
HoneyandClaude Sonnet 4.6 613d5156fa fix: settings order now matches web — Theme → Bibliothek → ABS → Discover APIs → Navidrome
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 23:54:11 +02:00
HoneyandClaude Sonnet 4.6 a0d3c7a592 feat: ABS integration, last played song, settings reorder + conn dots
Settings:
- Reordered to match web: Navidrome → Bibliothek → Theme → Audiobookshelf → Discover APIs
- Save button now appears at top AND bottom for easy access
- Audiobookshelf section: Server URL + API Key (masked) + green/red status dot with Test button
- NovaSonic Backend section: same green/red conn dot + Test button
- Connection status uses ConnStatus sealed class (Idle/Checking/Ok/Err)

Audiobookshelf:
- New AbsClient.kt: Ktor HTTP client, proxies through NovaSonic backend or direct with Bearer token
- New AbsScreen.kt: library list → book list with covers, author, duration
- Hörbücher tab appears in bottom nav only when ABS URL + API key are both set
- absUrl + absApiKey persisted in DataStore

Last played song:
- PlayerController saves song to DataStore on every media item transition
- MiniPlayer shows last song (dimmed, single play button) when no active queue
- Tapping play fetches the song from Navidrome and resumes
- LastPlayedSong stored in DataStore (id, title, artist, coverArt)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 23:49:36 +02:00
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 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