feat: implement playArtist function to play all albums by an artist and update QueuePage layout
This commit is contained in:
@@ -21,7 +21,7 @@ export default function ArtistPage() {
|
|||||||
const [artist, setArtist] = useState<Artist | null>(null);
|
const [artist, setArtist] = useState<Artist | null>(null);
|
||||||
const [isPlayingArtist, setIsPlayingArtist] = useState(false);
|
const [isPlayingArtist, setIsPlayingArtist] = useState(false);
|
||||||
const { getArtist, starItem, unstarItem } = useNavidrome();
|
const { getArtist, starItem, unstarItem } = useNavidrome();
|
||||||
const { addArtistToQueue, playAlbum, clearQueue } = useAudioPlayer();
|
const { playArtist } = useAudioPlayer();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const api = getNavidromeAPI();
|
const api = getNavidromeAPI();
|
||||||
|
|
||||||
@@ -65,19 +65,7 @@ export default function ArtistPage() {
|
|||||||
|
|
||||||
setIsPlayingArtist(true);
|
setIsPlayingArtist(true);
|
||||||
try {
|
try {
|
||||||
// Clear current queue and add all artist albums
|
await playArtist(artist.id);
|
||||||
clearQueue();
|
|
||||||
await addArtistToQueue(artist.id);
|
|
||||||
|
|
||||||
// Start playing the first album if we have any
|
|
||||||
if (artistAlbums.length > 0) {
|
|
||||||
await playAlbum(artistAlbums[0].id);
|
|
||||||
}
|
|
||||||
|
|
||||||
toast({
|
|
||||||
title: "Playing Artist",
|
|
||||||
description: `Now playing all albums by ${artist.name}`,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to play artist:', error);
|
console.error('Failed to play artist:', error);
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ interface AudioPlayerContextProps {
|
|||||||
shuffle: boolean;
|
shuffle: boolean;
|
||||||
toggleShuffle: () => void;
|
toggleShuffle: () => void;
|
||||||
shuffleAllAlbums: () => Promise<void>;
|
shuffleAllAlbums: () => Promise<void>;
|
||||||
|
playArtist: (artistId: string) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AudioPlayerContext = createContext<AudioPlayerContextProps | undefined>(undefined);
|
const AudioPlayerContext = createContext<AudioPlayerContextProps | undefined>(undefined);
|
||||||
@@ -420,6 +421,55 @@ export const AudioPlayerProvider: React.FC<{ children: React.ReactNode }> = ({ c
|
|||||||
}
|
}
|
||||||
}, [api, songToTrack, toast]);
|
}, [api, songToTrack, toast]);
|
||||||
|
|
||||||
|
const playArtist = useCallback(async (artistId: string) => {
|
||||||
|
setIsLoading(true);
|
||||||
|
try {
|
||||||
|
const { artist, albums } = await api.getArtist(artistId);
|
||||||
|
let allTracks: Track[] = [];
|
||||||
|
|
||||||
|
// Collect all tracks from all albums
|
||||||
|
for (const album of albums) {
|
||||||
|
const { songs } = await api.getAlbum(album.id);
|
||||||
|
const tracks = songs.map(songToTrack);
|
||||||
|
allTracks = allTracks.concat(tracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allTracks.length > 0) {
|
||||||
|
if (shuffle) {
|
||||||
|
// If shuffle is enabled, shuffle all tracks
|
||||||
|
const shuffledTracks = [...allTracks];
|
||||||
|
// Fisher-Yates shuffle algorithm
|
||||||
|
for (let i = shuffledTracks.length - 1; i > 0; i--) {
|
||||||
|
const j = Math.floor(Math.random() * (i + 1));
|
||||||
|
[shuffledTracks[i], shuffledTracks[j]] = [shuffledTracks[j], shuffledTracks[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play the first shuffled track and set the rest as queue
|
||||||
|
playTrack(shuffledTracks[0]);
|
||||||
|
setQueue(shuffledTracks.slice(1));
|
||||||
|
} else {
|
||||||
|
// Normal order: play first track and set the rest as queue
|
||||||
|
playTrack(allTracks[0]);
|
||||||
|
setQueue(allTracks.slice(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: "Playing Artist",
|
||||||
|
description: `Now playing all albums by "${artist.name}"${shuffle ? ' (shuffled)' : ''}`,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to play artist:', error);
|
||||||
|
toast({
|
||||||
|
variant: "destructive",
|
||||||
|
title: "Error",
|
||||||
|
description: "Failed to play artist albums",
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
}, [api, songToTrack, toast, shuffle, playTrack]);
|
||||||
|
|
||||||
const contextValue = useMemo(() => ({
|
const contextValue = useMemo(() => ({
|
||||||
currentTrack,
|
currentTrack,
|
||||||
playTrack,
|
playTrack,
|
||||||
@@ -437,7 +487,8 @@ export const AudioPlayerProvider: React.FC<{ children: React.ReactNode }> = ({ c
|
|||||||
skipToTrackInQueue,
|
skipToTrackInQueue,
|
||||||
shuffle,
|
shuffle,
|
||||||
toggleShuffle,
|
toggleShuffle,
|
||||||
shuffleAllAlbums
|
shuffleAllAlbums,
|
||||||
|
playArtist
|
||||||
}), [
|
}), [
|
||||||
currentTrack,
|
currentTrack,
|
||||||
queue,
|
queue,
|
||||||
@@ -455,7 +506,8 @@ export const AudioPlayerProvider: React.FC<{ children: React.ReactNode }> = ({ c
|
|||||||
skipToTrackInQueue,
|
skipToTrackInQueue,
|
||||||
shuffle,
|
shuffle,
|
||||||
toggleShuffle,
|
toggleShuffle,
|
||||||
shuffleAllAlbums
|
shuffleAllAlbums,
|
||||||
|
playArtist
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const QueuePage: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full px-4 py-6 lg:px-8">
|
<div className="h-full px-4 py-6 lg:px-8 pb-24">
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
|
|||||||
Reference in New Issue
Block a user