Commit Graph

4 Commits

Author SHA1 Message Date
Frank Stellmacher bd56177e2c feat(home): Lossless Albums rail + dedicated page + sidebar nav (#506)
* wip(home): Lossless rail + dedicated /lossless-albums page

Rail under Home > mostPlayed and a dedicated infinite-scroll page that
list albums whose tracks are tagged in lossless containers. Walks
Navidrome's native /api/song?_sort=bit_depth&_order=DESC, dedupes by
albumId on the way down, stops when the song stream crosses into lossy
(bitDepth==0) or the server runs out of rows. _filters has no operators
on quality columns, so a sort + walk is the only path; equality on
sample_rate / bit_depth probes returned empty (verified).

The page paginates through the song-cursor with an in-flight ref so
overlapping IntersectionObserver fires don't double-add albums, plus a
cancelled flag so React StrictMode's double-mount doesn't apply two
parallel result sets in dev.

Suffix allowlist excludes ambiguous wrappers — m4a/m4b can be ALAC
*or* AAC and Navidrome's response carries an empty codec field, so we
can't tell them apart; same story for wma. Allowlist is flac, wav,
aiff/aif, dsf/dff, ape, wv, shn, tta — containers that are only
lossless. ALAC-in-m4a setups will miss out, acceptable trade-off
without a reliable codec field.

Status: WIP. Settings home-customizer label and en.ts strings landed,
no other locales yet, no quality badge on AlbumCards across the rest of
the app, no CHANGELOG. Branch was 'feat/home-hires-rail' during the
earlier hi-res-only iteration before the lossless broadening.

* wip(home): Lossless page header parity + streaming + sidebar nav

Page header now mirrors All Albums: selection mode with three action
buttons (Enqueue Selected, Add Offline, Download ZIPs) wired to the
same handlers Albums.tsx uses, selection-counter title swap, and the
perfFlags.disableMainstageStickyHeader respect path. Filters were
intentionally skipped — the rail is sorted by bit_depth, mixing client-
side filters with server-driven pagination would produce gaps.

Loading feels noticeably faster: ndListLosslessAlbumsPage takes an
optional onProgress callback that fires once per internal fetch with
the entries discovered in that fetch, so the page can stream new
albums into state instead of waiting on the whole loadMore. Page-side
budget dropped from 5×200 to 2×100 songs per loadMore (~1 MB worst
case vs 5 MB before), since the rail's catch-em-all pass is wrong for
infinite-scroll UX. Subtitle under the page title primes the user that
this is slower than other album pages because Psysonic walks the song
catalog by quality (Navidrome ignores _fields, so per-song responses
ship with lyrics + tags + participants whether we want them or not).

Sidebar nav entry registered under 'losslessAlbums' with a Gem icon,
defaults to visible:false (matches composers / folderBrowser /
deviceSync — niche browsing modes). Existing users get the entry
appended at the end of their persisted sidebar list automatically via
the onRehydrateStorage merge that sidebarStore already runs for new
DEFAULT_SIDEBAR_ITEMS.

i18n: full coverage across all 8 locales for sidebar.losslessAlbums,
home.losslessAlbums, losslessAlbums.empty, losslessAlbums.unsupported
and the new losslessAlbums.slowFetchHint subtitle. ru/zh are
machine-translation quality, flagged for a polish pass.

* feat(home): default Lossless sidebar entry to visible

Flips the DEFAULT_SIDEBAR_ITEMS entry for `losslessAlbums` from false
to true. Existing installs keep whatever the user has in persisted
storage; fresh installs see the entry in the sidebar from the start.

Earlier wip commit defaulted it off (matched composers / folderBrowser
/ deviceSync as a niche browse mode), but the rail + page do show
something useful for any library with at least one FLAC/WAV/etc. album,
so off-by-default just hid the feature.

* docs: changelog entry for PR #506

Logs the Lossless Albums rail + page + sidebar entry in v1.46.0
"## Added".

* docs(settings): contributor entry for PR #506

Adds the Lossless Albums rail/page bullet to Psychotoxical's
contributions list.
2026-05-07 20:44:27 +02:00
Frank Stellmacher d1ff2fab51 feat(home): Because you listened recommendation rail (#489)
* feat(home): "Because you listened" recommendation rail

New Home rail (under Recently Added, default on, toggleable in Settings →
Personalisation → Home Page) that surfaces 3 albums from artists similar
to one of your top-played artists. Anchor rotates per Home mount so a
different top-artist seeds the recommendations each visit; within each
anchor, both the similar-artist subset and the chosen album per artist
are randomised, so the same anchor returns different picks on subsequent
visits.

Card layout matches the regular Album cards' surface (--bg-card with
accent-tinted border + 1px inset top highlight) and gets the same
Play / Enqueue hover overlay buttons. Cover and meta scale via CSS only
— no infinite animations, no filter/blur/transform, no compositing
layers. Grid wraps below 3-up at <400px card width instead of shrinking.

API budget: one getArtistInfo2 + 6 parallel getArtist calls per Home
mount, both reusing the existing mostPlayed payload to derive the anchor
pool (no extra API call to find top artists). All 8 locales seeded.

* fix(home): ru plurals + per-server anchor + narrower card grid

- ru: add _few / _many for becauseYouLikeTracks (CLDR Russian needs
  4 forms — 3 треков was wrong, now 3 трека).
- Anchor rotation memory is now per-server. The localStorage key
  becomes psysonic_because_anchor:<serverId>; switching servers no
  longer aliases server A's rotation onto server B's pool.
- because-card grid minmax(400px, 1fr) -> minmax(340px, 1fr) so two
  cards fit side by side at typical sidebar-expanded widths instead
  of collapsing to a single card per row.

* docs: CHANGELOG + Contributors entry for Because-you-listened rail (PR #489)

* style(home): blurred cover backdrop + centred layout for Because-cards

- Each Because-card renders the album cover as a blurred, low-opacity
  full-bleed background layer behind the existing cover thumb and text.
  Resolved through useCachedUrl so the cache layer feeds it (same key
  as the thumbnail) instead of a fresh salted URL on every render.
- Card content (cover thumb + text block) now centred horizontally and
  vertically within the card; the meta line lives in a small pill that
  sits centred under the artist row.
- Text contrast halo and meta-pill background are theme-aware via
  color-mix on var(--bg-card) / var(--text-primary), so the same rules
  read on dark and light themes (was hard-coded rgba black before and
  smudged the type on Latte / Nord Snowstorm).
2026-05-07 02:28:58 +02:00
Frank Stellmacher 807e7b4520 feat(home): add Discover Songs rail to Mainstage (#301)
Reuses the SongRail component from the Tracks hub — 18 random songs
fetched in parallel with the other Home queries. No reroll button.
Click-to-play uses enqueueAndPlay so the existing queue stays intact.

New homeStore section id 'discoverSongs' inserted after 'discover'
in DEFAULT_HOME_SECTIONS. onRehydrateStorage now appends any newly
introduced sections to a previously persisted layout — existing users
get the rail without a manual Reset. Settings → Personalisation
Home-Customizer surfaces it automatically via SECTION_LABELS.

i18n key home.discoverSongs added to all 8 locales.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 14:32:20 +02:00
Psychotoxical 55e7cb835b feat: v1.27.0 — In-App Auto-Update, Configurable Home, Icon Consistency
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 02:14:57 +02:00