fix(playback): honor autoplay and seed queues

cx
This commit is contained in:
vicrodh committed 2026-01-19 12:46:28 -06:00
1 parent 7c4fb6d483
commit c59ab226b9
6 files changed
+163 -77

No files matched your search

@@ -309,7 +309,21 @@
void togglePlay();
}
function handleTrackPlay(track: Track, trackIndex?: number) {
function buildTopTracksQueue(tracks: Track[]) {
return tracks.map(t => ({
id: t.id,
title: t.title,
artist: t.performer?.name || artist.name,
album: t.album?.title || '',
duration_secs: t.duration,
artwork_url: t.album?.image?.large || t.album?.image?.thumbnail || '',
hires: t.hires_streamable ?? false,
bit_depth: t.maximum_bit_depth ?? null,
sample_rate: t.maximum_sampling_rate ?? null,
}));
}
async function handleTrackPlay(track: Track, trackIndex?: number) {
// Create artist top tracks context
if (topTracks.length > 0) {
const trackIds = topTracks.map(t => t.id);
@@ -325,6 +339,12 @@
index
);
console.log(`[Artist] Context created: "${artist.name}" top tracks, ${trackIds.length} tracks, starting at ${index}`);
try {
const queueTracks = buildTopTracksQueue(topTracks);
await invoke('set_queue', { tracks: queueTracks, startIndex: index });
} catch (err) {
console.error('Failed to set queue:', err);
}
}
}
@@ -351,21 +371,8 @@
async function handlePlayAllTracks() {
if (topTracks.length === 0 || !onTrackPlay) return;
const queueTracks = topTracks.map(t => ({
id: t.id,
title: t.title,
artist: t.performer?.name || artist.name,
album: t.album?.title || '',
duration_secs: t.duration,
artwork_url: t.album?.image?.large || t.album?.image?.thumbnail || '',
hires: t.hires_streamable ?? false,
bit_depth: t.maximum_bit_depth ?? null,
sample_rate: t.maximum_sampling_rate ?? null,
}));
try {
await invoke('set_queue', { tracks: queueTracks, startIndex: 0 });
handleTrackPlay(topTracks[0]);
await handleTrackPlay(topTracks[0], 0);
} catch (err) {
console.error('Failed to set queue:', err);
}
+42 -27
View File
@@ -720,31 +720,8 @@
};
}
function handleTrackClick(track: FavoriteTrack, index: number) {
// Create favorites context
if (filteredTracks.length > 0) {
const trackIds = filteredTracks.map(t => t.id);
setPlaybackContext(
'favorites',
'favorites',
'Favorites',
'qobuz',
trackIds,
index
);
}
// Play track
if (onTrackPlay) {
onTrackPlay(buildDisplayTrack(track, index));
}
}
async function handlePlayAllTracks() {
if (filteredTracks.length === 0 || !onTrackPlay) return;
const queueTracks = filteredTracks.map(t => ({
function buildFavoritesQueueTracks(tracks: FavoriteTrack[]) {
return tracks.map(t => ({
id: t.id,
title: t.title,
artist: t.performer?.name || 'Unknown Artist',
@@ -755,10 +732,48 @@
bit_depth: t.maximum_bit_depth ?? null,
sample_rate: t.maximum_sampling_rate ?? null,
}));
}
function setFavoritesContext(trackIds: number[], index: number) {
if (trackIds.length === 0) return;
setPlaybackContext(
'favorites',
'favorites',
'Favorites',
'qobuz',
trackIds,
index
);
}
async function setFavoritesQueue(startIndex: number) {
if (filteredTracks.length === 0) return;
const queueTracks = buildFavoritesQueueTracks(filteredTracks);
await invoke('set_queue', { tracks: queueTracks, startIndex });
}
async function handleTrackClick(track: FavoriteTrack, index: number) {
const trackIds = filteredTracks.map(t => t.id);
setFavoritesContext(trackIds, index);
try {
await invoke('set_queue', { tracks: queueTracks, startIndex: 0 });
handleTrackClick(filteredTracks[0], 0);
await setFavoritesQueue(index);
} catch (err) {
console.error('Failed to set queue:', err);
}
if (onTrackPlay) {
onTrackPlay(buildDisplayTrack(track, index));
}
}
async function handlePlayAllTracks() {
if (filteredTracks.length === 0 || !onTrackPlay) return;
try {
await setFavoritesQueue(0);
setFavoritesContext(filteredTracks.map(t => t.id), 0);
onTrackPlay(buildDisplayTrack(filteredTracks[0], 0));
} catch (err) {
console.error('Failed to set queue:', err);
}
+24 -1
View File
@@ -335,7 +335,21 @@
return formatQuality(track.hires, track.bitDepth, track.samplingRate);
}
function handleContinueTrackPlay(track: DisplayTrack, trackIndex: number) {
function buildContinueQueueTracks(tracks: DisplayTrack[]) {
return tracks.map(t => ({
id: t.id,
title: t.title,
artist: t.artist || 'Unknown Artist',
album: t.album || '',
duration_secs: t.durationSeconds,
artwork_url: t.albumArt || '',
hires: t.hires ?? false,
bit_depth: t.bitDepth ?? null,
sample_rate: t.samplingRate ?? null,
}));
}
async function handleContinueTrackPlay(track: DisplayTrack, trackIndex: number) {
// Create continue listening context
if (continueTracks.length > 0) {
const trackIds = continueTracks.map(t => t.id);
@@ -351,6 +365,15 @@
console.log(`[Home] Context created: Continue Listening, ${trackIds.length} tracks, starting at ${trackIndex}`);
}
if (continueTracks.length > 0) {
try {
const queueTracks = buildContinueQueueTracks(continueTracks);
await invoke('set_queue', { tracks: queueTracks, startIndex: trackIndex });
} catch (err) {
console.error('Failed to set queue:', err);
}
}
// Play track
if (onTrackPlay) {
onTrackPlay(track);
@@ -544,29 +544,66 @@
return '';
}
function handleTrackClick(track: DisplayTrack) {
function buildQueueTracks(tracks: DisplayTrack[]) {
const queueTracks = tracks.map(t => ({
id: t.isLocal ? Math.abs(t.id) : t.id,
title: t.title,
artist: t.artist || 'Unknown Artist',
album: t.album || playlist?.name || 'Playlist',
duration_secs: t.durationSeconds,
artwork_url: t.albumArt || getPlaylistImage(),
hires: t.hires ?? false,
bit_depth: t.bitDepth ?? null,
sample_rate: t.samplingRate != null ? (t.isLocal ? t.samplingRate * 1000 : t.samplingRate) : null,
is_local: t.isLocal ?? false,
}));
const localIds = tracks
.filter(t => t.isLocal)
.map(t => Math.abs(t.id));
return { queueTracks, localIds };
}
async function setPlaylistQueue(startIndex: number) {
const allTracks = displayTracks;
if (allTracks.length === 0) return;
const { queueTracks, localIds } = buildQueueTracks(allTracks);
await invoke('set_queue', { tracks: queueTracks, startIndex });
if (localIds.length > 0) {
onSetLocalQueue?.(localIds);
}
}
async function handleTrackClick(track: DisplayTrack, trackIndex: number) {
// Create playlist context before playing
if (playlist) {
const trackIds = displayTracks
.filter(t => !t.isLocal) // Only Qobuz tracks in context
.map(t => t.id);
const trackIndex = trackIds.indexOf(track.id);
const contextIndex = trackIds.indexOf(track.id);
if (trackIndex >= 0 && trackIds.length > 0) {
if (contextIndex >= 0 && trackIds.length > 0) {
setPlaybackContext(
'playlist',
playlist.id.toString(),
playlist.name,
'qobuz',
trackIds,
trackIndex
contextIndex
);
console.log(`[Playlist] Context created: "${playlist.name}", ${trackIds.length} tracks, starting at ${trackIndex}`);
console.log(`[Playlist] Context created: "${playlist.name}", ${trackIds.length} tracks, starting at ${contextIndex}`);
}
}
// Handle playback
try {
await setPlaylistQueue(trackIndex);
} catch (err) {
console.error('Failed to set queue:', err);
}
if (track.isLocal && track.localTrackId) {
// Handle local track play
const localTrack = localTracksMap.get(track.localTrackId);
@@ -628,32 +665,8 @@
const allTracks = displayTracks;
if (allTracks.length === 0) return;
// Build queue tracks, using absolute ID for local tracks
const queueTracks = allTracks.map(t => ({
id: t.isLocal ? Math.abs(t.id) : t.id,
title: t.title,
artist: t.artist || 'Unknown Artist',
album: t.album || playlist?.name || 'Playlist',
duration_secs: t.durationSeconds,
artwork_url: t.albumArt || getPlaylistImage(),
hires: t.hires ?? false,
bit_depth: t.bitDepth ?? null,
sample_rate: t.samplingRate != null ? (t.isLocal ? t.samplingRate * 1000 : t.samplingRate) : null,
is_local: t.isLocal ?? false,
}));
// Collect local track IDs (original positive IDs)
const localIds = allTracks
.filter(t => t.isLocal)
.map(t => Math.abs(t.id));
try {
await invoke('set_queue', { tracks: queueTracks, startIndex: 0 });
// Tell parent about local tracks in queue
if (localIds.length > 0) {
onSetLocalQueue?.(localIds);
}
await setPlaylistQueue(0);
// Play first track (handle local vs Qobuz)
const firstTrack = allTracks[0];
@@ -991,11 +1004,11 @@
hideDownload={track.isLocal}
downloadStatus={downloadInfo.status}
downloadProgress={downloadInfo.progress}
onPlay={available ? () => handleTrackClick(track) : undefined}
onPlay={available ? () => handleTrackClick(track, idx) : undefined}
onDownload={available && !track.isLocal && onTrackDownload ? () => onTrackDownload(track) : undefined}
onRemoveDownload={available && !track.isLocal && onTrackRemoveDownload ? () => onTrackRemoveDownload(track.id) : undefined}
menuActions={available ? {
onPlayNow: () => handleTrackClick(track),
onPlayNow: () => handleTrackClick(track, idx),
onPlayNext: track.isLocal ? () => handleTrackPlayNext(track) : (onTrackPlayNext ? () => onTrackPlayNext(track) : undefined),
onPlayLater: track.isLocal ? () => handleTrackPlayLater(track) : (onTrackPlayLater ? () => onTrackPlayLater(track) : undefined),
onAddToPlaylist: !track.isLocal && onTrackAddToPlaylist ? () => onTrackAddToPlaylist(track.id) : undefined,
+24 -1
View File
@@ -381,7 +381,21 @@
return track.album?.image?.large || track.album?.image?.thumbnail || track.album?.image?.small || '';
}
function handleSearchTrackPlay(track: Track, trackIndex: number) {
function buildSearchQueueTracks(tracks: Track[]) {
return tracks.map(t => ({
id: t.id,
title: t.title,
artist: t.performer?.name || 'Unknown Artist',
album: t.album?.title || '',
duration_secs: t.duration,
artwork_url: t.album?.image?.large || t.album?.image?.thumbnail || t.album?.image?.small || '',
hires: t.hires_streamable ?? false,
bit_depth: t.maximum_bit_depth ?? null,
sample_rate: t.maximum_sampling_rate ?? null,
}));
}
async function handleSearchTrackPlay(track: Track, trackIndex: number) {
// Create search results context
if (trackResults && trackResults.items.length > 0) {
const trackIds = trackResults.items.map(t => t.id);
@@ -397,6 +411,15 @@
console.log(`[Search] Context created: "${currentQuery}", ${trackIds.length} tracks, starting at ${trackIndex}`);
}
if (trackResults && trackResults.items.length > 0) {
try {
const queueTracks = buildSearchQueueTracks(trackResults.items);
await invoke('set_queue', { tracks: queueTracks, startIndex: trackIndex });
} catch (err) {
console.error('Failed to set queue:', err);
}
}
// Play track
if (onTrackPlay) {
onTrackPlay(track);
+6 -1
View File
@@ -35,7 +35,8 @@
} from '$lib/stores/playbackContextStore';
import {
initPlaybackPreferences,
getCachedPreferences
getCachedPreferences,
isAutoplayEnabled
} from '$lib/stores/playbackPreferencesStore';
// UI state management
@@ -1853,6 +1854,10 @@
// Set up track ended callback for auto-advance
setOnTrackEnded(async () => {
if (!isAutoplayEnabled()) {
setQueueEnded(true);
return;
}
const nextTrackResult = await nextTrack();
if (nextTrackResult) {
await playQueueTrack(nextTrackResult);