fix(playback): honor autoplay and seed queues
cx
This commit is contained in:
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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user