fix(queue): complete album_id and artist_id propagation for track navigation

Ensure album_id and artist_id are passed through all queue track creation paths:
- trackActions.ts build functions (Qobuz, album, playlist, local tracks)
- Session persistence (both frontend and Rust backend)
- buildAlbumQueueTrack wrapper function
- Session restoration
This commit is contained in:
vicrodh committed 2026-01-23 03:56:14 -06:00
1 parent 2ea767f37c
commit 1684df7752
4 files changed
+79 -16

No files matched your search

+32 -3
View File
@@ -16,6 +16,10 @@ pub struct PersistedQueueTrack {
pub hires: bool, pub hires: bool,
pub bit_depth: Option<u32>, pub bit_depth: Option<u32>,
pub sample_rate: Option<f64>, pub sample_rate: Option<f64>,
#[serde(default)]
pub is_local: bool,
pub album_id: Option<String>,
pub artist_id: Option<u64>,
} }
/// Represents the full persisted session state /// Represents the full persisted session state
@@ -120,6 +124,25 @@ impl SessionStore {
); );
} }
// Add is_local, album_id, artist_id columns if they don't exist
let has_is_local: bool = conn
.query_row(
"SELECT COUNT(*) FROM pragma_table_info('queue_tracks') WHERE name = 'is_local'",
[],
|row| row.get(0),
)
.unwrap_or(0) > 0;
if !has_is_local {
let _ = conn.execute_batch(
"
ALTER TABLE queue_tracks ADD COLUMN is_local INTEGER NOT NULL DEFAULT 0;
ALTER TABLE queue_tracks ADD COLUMN album_id TEXT;
ALTER TABLE queue_tracks ADD COLUMN artist_id INTEGER;
"
);
}
Ok(Self { conn }) Ok(Self { conn })
} }
@@ -143,8 +166,8 @@ impl SessionStore {
// Insert queue tracks // Insert queue tracks
for (pos, track) in session.queue_tracks.iter().enumerate() { for (pos, track) in session.queue_tracks.iter().enumerate() {
if let Err(e) = self.conn.execute( if let Err(e) = self.conn.execute(
"INSERT INTO queue_tracks (position, track_id, title, artist, album, duration_secs, artwork_url, hires, bit_depth, sample_rate) "INSERT INTO queue_tracks (position, track_id, title, artist, album, duration_secs, artwork_url, hires, bit_depth, sample_rate, is_local, album_id, artist_id)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)", VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13)",
params![ params![
pos as i64, pos as i64,
track.id as i64, track.id as i64,
@@ -156,6 +179,9 @@ impl SessionStore {
track.hires as i64, track.hires as i64,
track.bit_depth.map(|v| v as i64), track.bit_depth.map(|v| v as i64),
track.sample_rate, track.sample_rate,
track.is_local as i64,
track.album_id,
track.artist_id.map(|v| v as i64),
], ],
) { ) {
let _ = self.conn.execute("ROLLBACK", []); let _ = self.conn.execute("ROLLBACK", []);
@@ -217,7 +243,7 @@ impl SessionStore {
// Load queue tracks // Load queue tracks
let mut stmt = self.conn let mut stmt = self.conn
.prepare("SELECT track_id, title, artist, album, duration_secs, artwork_url, hires, bit_depth, sample_rate FROM queue_tracks ORDER BY position") .prepare("SELECT track_id, title, artist, album, duration_secs, artwork_url, hires, bit_depth, sample_rate, is_local, album_id, artist_id FROM queue_tracks ORDER BY position")
.map_err(|e| format!("Failed to prepare queue query: {}", e))?; .map_err(|e| format!("Failed to prepare queue query: {}", e))?;
let tracks: Vec<PersistedQueueTrack> = stmt let tracks: Vec<PersistedQueueTrack> = stmt
@@ -232,6 +258,9 @@ impl SessionStore {
hires: row.get::<_, i64>(6).unwrap_or(0) != 0, hires: row.get::<_, i64>(6).unwrap_or(0) != 0,
bit_depth: row.get::<_, Option<i64>>(7)?.map(|v| v as u32), bit_depth: row.get::<_, Option<i64>>(7)?.map(|v| v as u32),
sample_rate: row.get(8)?, sample_rate: row.get(8)?,
is_local: row.get::<_, i64>(9).unwrap_or(0) != 0,
album_id: row.get(10)?,
artist_id: row.get::<_, Option<i64>>(11)?.map(|v| v as u64),
}) })
}) })
.map_err(|e| format!("Failed to query queue tracks: {}", e))? .map_err(|e| format!("Failed to query queue tracks: {}", e))?
+3
View File
@@ -17,6 +17,9 @@ export interface PersistedQueueTrack {
hires?: boolean; hires?: boolean;
bit_depth?: number | null; bit_depth?: number | null;
sample_rate?: number | null; sample_rate?: number | null;
is_local?: boolean;
album_id?: string | null;
artist_id?: number | null;
} }
export interface PersistedSession { export interface PersistedSession {
+20 -5
View File
@@ -37,11 +37,20 @@ export function buildQueueTrackFromQobuz(track: QobuzTrack): BackendQueueTrack {
hires: track.hires_streamable ?? false, hires: track.hires_streamable ?? false,
bit_depth: track.maximum_bit_depth ?? null, bit_depth: track.maximum_bit_depth ?? null,
sample_rate: track.maximum_sampling_rate ?? null, sample_rate: track.maximum_sampling_rate ?? null,
is_local: false is_local: false,
album_id: track.album?.id || null,
artist_id: track.performer?.id ?? null
}; };
} }
export function buildQueueTrackFromAlbumTrack(track: Track, albumArtwork: string, albumArtist: string, albumTitle: string): BackendQueueTrack { export function buildQueueTrackFromAlbumTrack(
track: Track,
albumArtwork: string,
albumArtist: string,
albumTitle: string,
albumId?: string,
artistId?: number
): BackendQueueTrack {
return { return {
id: track.id, id: track.id,
title: track.title, title: track.title,
@@ -52,7 +61,9 @@ export function buildQueueTrackFromAlbumTrack(track: Track, albumArtwork: string
hires: track.hires ?? false, hires: track.hires ?? false,
bit_depth: track.bitDepth ?? null, bit_depth: track.bitDepth ?? null,
sample_rate: track.samplingRate ?? null, sample_rate: track.samplingRate ?? null,
is_local: false is_local: false,
album_id: track.albumId || albumId || null,
artist_id: track.artistId ?? artistId ?? null
}; };
} }
@@ -67,7 +78,9 @@ export function buildQueueTrackFromPlaylistTrack(track: PlaylistTrack): BackendQ
hires: track.hires ?? false, hires: track.hires ?? false,
bit_depth: track.bitDepth ?? null, bit_depth: track.bitDepth ?? null,
sample_rate: track.samplingRate ?? null, sample_rate: track.samplingRate ?? null,
is_local: false is_local: false,
album_id: track.albumId || null,
artist_id: track.artistId ?? null
}; };
} }
@@ -85,7 +98,9 @@ export function buildQueueTrackFromLocalTrack(track: LocalLibraryTrack): Backend
hires: isHires, hires: isHires,
bit_depth: track.bit_depth ?? null, bit_depth: track.bit_depth ?? null,
sample_rate: track.sample_rate ?? null, sample_rate: track.sample_rate ?? null,
is_local: true is_local: true,
album_id: null, // Local tracks don't have Qobuz album IDs
artist_id: null // Local tracks don't have Qobuz artist IDs
}; };
} }
+24 -8
View File
@@ -518,7 +518,9 @@
track, track,
selectedAlbum?.artwork || '', selectedAlbum?.artwork || '',
selectedAlbum?.artist || 'Unknown Artist', selectedAlbum?.artist || 'Unknown Artist',
selectedAlbum?.title || '' selectedAlbum?.title || '',
selectedAlbum?.id,
selectedAlbum?.artistId
); );
} }
@@ -548,7 +550,9 @@
hires: t.hires ?? false, hires: t.hires ?? false,
bit_depth: t.bitDepth ?? null, bit_depth: t.bitDepth ?? null,
sample_rate: t.samplingRate ?? null, sample_rate: t.samplingRate ?? null,
is_local: false is_local: false,
album_id: album.id,
artist_id: t.artistId ?? album.artistId
})); }));
await setQueue(queueTracks, 0, true); await setQueue(queueTracks, 0, true);
@@ -591,7 +595,9 @@
hires: t.hires ?? false, hires: t.hires ?? false,
bit_depth: t.bitDepth ?? null, bit_depth: t.bitDepth ?? null,
sample_rate: t.samplingRate ?? null, sample_rate: t.samplingRate ?? null,
is_local: false is_local: false,
album_id: album.id,
artist_id: t.artistId ?? album.artistId
}); });
} }
showToast(`Playing ${album.tracks.length} tracks next`, 'success'); showToast(`Playing ${album.tracks.length} tracks next`, 'success');
@@ -612,7 +618,9 @@
hires: t.hires ?? false, hires: t.hires ?? false,
bit_depth: t.bitDepth ?? null, bit_depth: t.bitDepth ?? null,
sample_rate: t.samplingRate ?? null, sample_rate: t.samplingRate ?? null,
is_local: false is_local: false,
album_id: album.id,
artist_id: t.artistId ?? album.artistId
})); }));
const success = await addTracksToQueue(queueTracks); const success = await addTracksToQueue(queueTracks);
@@ -743,7 +751,9 @@
hires: t.hires ?? false, hires: t.hires ?? false,
bit_depth: t.bitDepth ?? null, bit_depth: t.bitDepth ?? null,
sample_rate: t.samplingRate ?? null, sample_rate: t.samplingRate ?? null,
is_local: false is_local: false,
album_id: selectedAlbum?.id,
artist_id: t.artistId ?? selectedAlbum?.artistId
})); }));
console.log('[Album Queue] Mapped to', queueTracks.length, 'queue tracks, startIndex:', trackIndex); console.log('[Album Queue] Mapped to', queueTracks.length, 'queue tracks, startIndex:', trackIndex);
@@ -1080,7 +1090,9 @@
hires: t.hires ?? false, hires: t.hires ?? false,
bit_depth: t.bitDepth ?? null, bit_depth: t.bitDepth ?? null,
sample_rate: t.samplingRate ?? null, sample_rate: t.samplingRate ?? null,
is_local: false is_local: false,
album_id: selectedAlbum?.id,
artist_id: t.artistId ?? selectedAlbum?.artistId
}); });
} }
showToast(`Playing ${selectedAlbum.tracks.length} tracks next`, 'success'); showToast(`Playing ${selectedAlbum.tracks.length} tracks next`, 'success');
@@ -1101,7 +1113,9 @@
hires: t.hires ?? false, hires: t.hires ?? false,
bit_depth: t.bitDepth ?? null, bit_depth: t.bitDepth ?? null,
sample_rate: t.samplingRate ?? null, sample_rate: t.samplingRate ?? null,
is_local: false is_local: false,
album_id: selectedAlbum?.id,
artist_id: t.artistId ?? selectedAlbum?.artistId
})); }));
const success = await addTracksToQueue(queueTracks); const success = await addTracksToQueue(queueTracks);
@@ -1529,7 +1543,9 @@
hires: t.hires ?? false, hires: t.hires ?? false,
bit_depth: t.bit_depth ?? null, bit_depth: t.bit_depth ?? null,
sample_rate: t.sample_rate ?? null, sample_rate: t.sample_rate ?? null,
is_local: t.is_local ?? false is_local: t.is_local ?? false,
album_id: t.album_id ?? null,
artist_id: t.artist_id ?? null
})); }));
await setQueue(tracks, session.current_index ?? 0, true); await setQueue(tracks, session.current_index ?? 0, true);