fix(ephemeral): replace 1 << 48 with 2 ** 48 for ephemeral track id floor

JavaScript bitwise shift operates on 32-bit signed integers, so
`1 << 48` is `1 << (48 % 32)` = `1 << 16` = 65536. Every Qobuz track
id sits above that, so the existing `id >= EPHEMERAL_ID_FLOOR` checks
were classifying every Qobuz track as ephemeral and disabling the
favorite / playlist / mixtape / collection actions for them — most
visibly in the immersive player favorite button, but also silently
stripping Qobuz tracks from the persisted session queue.

Five places shared the same broken constant, all now use `2 ** 48`
(281474976710656) — the actual ephemeral id floor defined in
src-tauri/src/ephemeral_library/mod.rs:

  - src/routes/+page.svelte (two metadataActionsDisabled checks for
    the immersive player and now-playing bar, plus the session-save
    EPHEMERAL_ID_FLOOR filter).
  - src/lib/components/QueuePanel.svelte (isEphemeralQueueTrack guard
    that hides favorite / playlist / track-info actions for queue
    rows).
  - src/lib/stores/playerStore.ts (isEphemeralTransition flag in the
    gapless carve-out and CUE virtual-track position translation).
  - src/lib/components/views/LocalLibraryView.svelte (close-folder
    cleanup check — only ephemeral folders should wipe the queue +
    now-playing slot when closed).

Each call site keeps a short comment naming the trap so future edits
don't reach for the same bitwise expression.

cc
This commit is contained in:
vicrodh committed 2026-05-13 22:22:09 -06:00
1 parent 7bb1047283
commit 5bc5122d58
4 files changed
+17 -7

No files matched your search

+4 -2
View File
@@ -29,13 +29,15 @@
parental_warning?: boolean;
}
// Mirrors EPHEMERAL_ID_FLOOR (1 << 48) in src-tauri/src/ephemeral_library.
// Mirrors EPHEMERAL_ID_FLOOR (2^48) in src-tauri/src/ephemeral_library.
// Track ids at or above this threshold come from the in-memory ephemeral
// cache, not the library DB. Favorites / playlists / track-info actions
// require a DB row and don't make sense for ephemeral tracks; the
// helper below gates the corresponding UI off so users don't get
// dead-end clicks.
const EPHEMERAL_ID_FLOOR = 1 << 48;
// 2 ** 48 (NOT 1 << 48 — JS bitwise shift is 32-bit, 1 << 48 silently
// becomes 65536 and every Qobuz track ends up "ephemeral").
const EPHEMERAL_ID_FLOOR = 2 ** 48;
function isEphemeralQueueTrack(id: string | number | null | undefined): boolean {
if (id == null) return false;
return Number(id) >= EPHEMERAL_ID_FLOOR;
@@ -1330,7 +1330,9 @@
// not a DB row. Used for "did the user just close a folder whose
// tracks are still playing?" checks so we know whether to wipe the
// queue + now-playing slot when an ephemeral session ends.
const EPHEMERAL_ID_FLOOR = 1 << 48;
// 2 ** 48 (NOT 1 << 48 — JS bitwise shift is 32-bit, 1 << 48 silently
// becomes 65536 and every Qobuz track gets mis-classified).
const EPHEMERAL_ID_FLOOR = 2 ** 48;
const EPHEMERAL_PATH_STORAGE_KEY = 'qbz-ephemeral-folder-path';
let ephemeralFolder = $state<EphemeralFolderState | null>(null);
let openingEphemeralFolder = $state(false);
+4 -1
View File
@@ -87,7 +87,10 @@ interface BackendPlaybackState {
// ephemeral cache, not DB rows; the playback logic special-cases them
// in a couple of places (gapless transition is_playing carve-out, CUE
// virtual-track position translation).
const EPHEMERAL_ID_FLOOR = 1 << 48;
// 2 ** 48 (NOT 1 << 48 — JavaScript bitwise shift is 32-bit, 1 << 48
// silently truncates to 65536 and every Qobuz track ends up classified
// as ephemeral, suppressing favorites and breaking session save).
const EPHEMERAL_ID_FLOOR = 2 ** 48;
/**
* Bit-perfect mode of the active audio stream, reported by the Rust backend.
+6 -3
View File
@@ -4474,7 +4474,10 @@
// Strip them from the persisted queue and clear the current-index
// pointer if it was sitting on one — the ephemeral pane comes back
// via folder-path persistence and the user re-clicks play.
const EPHEMERAL_ID_FLOOR = 1 << 48;
// 2 ** 48 (NOT 1 << 48 — JavaScript bitwise shift operates on
// 32-bit ints so 1 << 48 silently truncates to 65536, which
// mis-classified every Qobuz track as ephemeral).
const EPHEMERAL_ID_FLOOR = 2 ** 48;
const persistedTracks: PersistedQueueTrack[] = [];
let persistedCurrentIndex: number | null = null;
for (let i = 0; i < tracks.length; i++) {
@@ -6884,7 +6887,7 @@
{isFavorite}
onToggleFavorite={toggleFavorite}
onAddToPlaylist={openAddToPlaylistModal}
metadataActionsDisabled={currentTrack != null && currentTrack.id >= (1 << 48)}
metadataActionsDisabled={currentTrack != null && currentTrack.id >= 2 ** 48}
onOpenQueue={toggleQueue}
onOpenMiniPlayer={() => {
void enterMiniplayerMode();
@@ -6990,7 +6993,7 @@
onToggleRepeat={toggleRepeat}
{isFavorite}
onToggleFavorite={toggleFavorite}
metadataActionsDisabled={currentTrack != null && currentTrack.id >= (1 << 48)}
metadataActionsDisabled={currentTrack != null && currentTrack.id >= 2 ** 48}
lyricsLines={lyricsLines}
lyricsActiveIndex={lyricsActiveIndex}
lyricsActiveProgress={lyricsActiveProgress}