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.
This commit is contained in:
Frank Stellmacher
2026-05-07 20:44:27 +02:00
committed by GitHub
parent a41e3a624a
commit bd56177e2c
18 changed files with 568 additions and 3 deletions
+7
View File
@@ -25,6 +25,7 @@ export const zhTranslation = {
tracks: '曲目',
playlists: '播放列表',
mostPlayed: '最常播放',
losslessAlbums: '无损',
radio: '网络电台',
folderBrowser: '文件夹浏览器',
deviceSync: '设备同步',
@@ -41,6 +42,7 @@ export const zhTranslation = {
recent: '最近添加',
mostPlayed: '最常播放',
recentlyPlayed: '最近播放',
losslessAlbums: '无损专辑',
discover: '发现',
discoverSongs: '发现曲目',
loadMore: '加载更多',
@@ -1465,6 +1467,11 @@ export const zhTranslation = {
filterCompilations: '隐藏合辑艺术家(Various Artists、原声带等)',
filterCompilationsShort: '隐藏合辑',
},
losslessAlbums: {
empty: '此媒体库中还没有无损专辑。',
unsupported: '此服务器未公开查找无损专辑所需的元数据。',
slowFetchHint: '加载比其他专辑页面慢 — Psysonic 按音质遍历整个歌曲目录。',
},
radio: {
title: '网络电台',
empty: '未配置任何电台。',