docs: record 1.1 hard-won knowledge in CLAUDE.md; refresh README
CLAUDE.md: - Download pipeline: leading aesthetic characters in artist names must be trimmed before the iTunes lookup (a missed match cascades into a wrong-duration YouTube grab), and an album tag is always written (defaulting to Singles) so Navidrome and the metadata editor agree. - New Android section covering the traps this release cost the most time on: a BackHandler inside a NavHost destination loses back priority (overlay back must be registered at the app root), overlay state must live in the ViewModel rather than remember/rememberSaveable, bottomBar and ModalBottomSheet inset handling, reusing the Ktor client, global optimistic StarState, and iTunes artists having no artwork. README: offline downloads also cover single songs and work without a server connection; StarState added to the data/ listing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
a80ac3deb0
commit
c5649ac22f
2 files changed
+37
-2
No files matched your search
@@ -56,6 +56,15 @@ Backend runs in Docker (see `docker-compose.yml`); on the deploy host it lives a
|
||||
pulled same-titled covers by other uploaders, and wrong Piano Echoes for Japanese tracks.
|
||||
- **Size cap:** `--max-filesize 30M` on every yt-dlp call; Mittelweg steps also cap at 2×
|
||||
expected duration. Prevents saving a 28-min video (52 MB) when the wrong result slips through.
|
||||
- **Leading aesthetic characters:** artists often style their name with leading punctuation
|
||||
(`.diedlonely`, `†…`). `metadata.js` strips leading non-alphanumerics (`stripLeadSym`) before the
|
||||
iTunes lookup and in `firstWord`. Without it the lookup misses, falls through to a title-only
|
||||
match, and returns an unrelated same-titled track — whose wrong duration then *also* misleads the
|
||||
YouTube duration filter (observed: a 96 s track resolved to a 176 s slowed version). The same trim
|
||||
runs client-side in Android's `ExploreViewModel.search()`.
|
||||
- **Always write an `album` tag.** `ytdlp.js` defaults it to `Singles` when no album is known.
|
||||
Previously no tag was written at all, so Navidrome showed its own "Singles" fallback while the
|
||||
metadata editor showed "[Unknown Album]" — the two never matched and file resolution failed.
|
||||
- **Duplicate detection** (`metadata.js findExistingTrack`): searches case-insensitively across
|
||||
all album folders under the matching artist folder; compares normalized artist+title ignoring
|
||||
track prefix and release/variant suffix. Handles DJ OKAWARI vs DJ Okawari and "- Single" drift.
|
||||
@@ -79,6 +88,32 @@ Backend runs in Docker (see `docker-compose.yml`); on the deploy host it lives a
|
||||
- **Playback:** `MusicRepository.streamUrl()` checks `offlineRepo.localAudioPath(id)` first; if the file exists it returns a `file://` URI. This applies to every play path — album, playlist, Offline tab — no special offline mode needed.
|
||||
- **Offline tab:** Library → tab 2 (OFFLINE). Driven by `OfflineViewModel`. Play button on a completed group calls `offlineVm.songsForGroup()` (JOIN query → `DownloadedTrackEntity.toSong()`) then `player.playSongs()`.
|
||||
|
||||
## Android navigation, state & insets (hard-won)
|
||||
|
||||
- **A `BackHandler` inside a NavHost destination loses back priority to the NavHost.** The Erkunden
|
||||
(Apple) artist/album are in-screen *overlays* on the `erkunden` destination, not routes — their
|
||||
`BackHandler` never fired, so one back press popped the whole tab to Home. Fix: the overlay reports
|
||||
its close action upward via `onOverlayBackChange`, and `AppNavigation` registers a **root-level**
|
||||
`OnBackPressedCallback` for it (the same mechanism the NowPlaying overlay always used successfully).
|
||||
Real nav routes (`album/{id}`, `artist/{id}`) are unaffected — `popBackStack()` is correct there.
|
||||
- **Overlay state belongs in the ViewModel, not `remember`.** Anything an overlay shows (e.g. the
|
||||
artist's "all albums" section) is destroyed when the user drills into an album, because the overlay
|
||||
leaves composition. `rememberSaveable` does **not** save you — it only survives for real nav
|
||||
destinations. Hoist it into the ViewModel (see `ExploreViewModel.artistAllAlbums`).
|
||||
- **`bottomBar` insets:** the tab `NavigationBar` absorbs the system-navigation inset. Detail screens
|
||||
have no tab bar, so the MiniPlayer needs `navigationBarsPadding()` itself — otherwise it sits behind
|
||||
the phone's gesture/navigation bar.
|
||||
- **`ModalBottomSheet`:** use `rememberModalBottomSheetState(skipPartiallyExpanded = true)` *and*
|
||||
`navigationBarsPadding()` on the content, or the bottom-most action (e.g. "Herunterladen") opens
|
||||
hidden behind the navigation bar and needs an extra drag-up.
|
||||
- **Reuse the Ktor client.** `MusicRepository.coverArtUrl()/streamUrl()` must go through
|
||||
`ClientHolder.get(creds)`. Constructing a `SubsonicClient` per call spins up (and never closes) an
|
||||
`HttpClient` for *every cover art request* — this was the cause of visible scroll/tab-switch lag.
|
||||
- **Star/favorite is optimistic and global** via `data/StarState.kt`. Toggle through it so every icon
|
||||
(song rows, NowPlaying, context sheet) flips instantly and rolls back if the API call fails.
|
||||
- **iTunes artists have no artwork.** The Search API returns no `artworkUrl100` for artist entities;
|
||||
borrow the cover of one of their albums (`entity=album&limit=1`) — otherwise you get grey circles.
|
||||
|
||||
## UI / references
|
||||
|
||||
When referencing files in chat, use clickable markdown links, not backticks:
|
||||
|
||||
Reference in new issue
Block a user