feat: add full screen audio player and radio station management
- Implemented FullScreenPlayer component for enhanced audio playback experience. - Added functionality to toggle full screen mode in AudioPlayer. - Introduced NavidromeConfigContext for managing Navidrome server configurations. - Created RadioStationsPage for managing internet radio stations, including adding, deleting, and playing stations. - Enhanced SettingsPage to configure Navidrome server connection with validation and feedback. - Updated NavidromeAPI to support fetching and managing radio stations. - Integrated lyrics fetching and display in FullScreenPlayer using LrcLibClient.
This commit is contained in:
@@ -3,13 +3,15 @@ import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { Album, Artist } from '@/lib/navidrome';
|
||||
import { useNavidrome } from '@/app/components/NavidromeContext';
|
||||
import { useAudioPlayer } from '@/app/components/AudioPlayerContext';
|
||||
import { AlbumArtwork } from '@/app/components/album-artwork';
|
||||
import Image from 'next/image';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Heart } from 'lucide-react';
|
||||
import { Heart, Play } from 'lucide-react';
|
||||
import { ScrollArea, ScrollBar } from '@/components/ui/scroll-area';
|
||||
import Loading from '@/app/components/loading';
|
||||
import { getNavidromeAPI } from '@/lib/navidrome';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
|
||||
export default function ArtistPage() {
|
||||
const { artist: artistId } = useParams();
|
||||
@@ -17,7 +19,10 @@ export default function ArtistPage() {
|
||||
const [artistAlbums, setArtistAlbums] = useState<Album[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [artist, setArtist] = useState<Artist | null>(null);
|
||||
const [isPlayingArtist, setIsPlayingArtist] = useState(false);
|
||||
const { getArtist, starItem, unstarItem } = useNavidrome();
|
||||
const { addArtistToQueue, playAlbum, clearQueue } = useAudioPlayer();
|
||||
const { toast } = useToast();
|
||||
const api = getNavidromeAPI();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -55,6 +60,36 @@ export default function ArtistPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const handlePlayArtist = async () => {
|
||||
if (!artist) return;
|
||||
|
||||
setIsPlayingArtist(true);
|
||||
try {
|
||||
// Clear current queue and add all artist albums
|
||||
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) {
|
||||
console.error('Failed to play artist:', error);
|
||||
toast({
|
||||
title: "Error",
|
||||
description: "Failed to play artist albums.",
|
||||
variant: "destructive"
|
||||
});
|
||||
} finally {
|
||||
setIsPlayingArtist(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
@@ -90,10 +125,20 @@ export default function ArtistPage() {
|
||||
<div className="flex-1">
|
||||
<h1 className="text-4xl font-bold text-white mb-2">{artist.name}</h1>
|
||||
<p className="text-white/80 mb-4">{artist.albumCount} albums</p>
|
||||
<Button onClick={handleStar} variant="secondary" className="mr-4">
|
||||
<Heart className={isStarred ? 'text-red-500' : 'text-gray-500'} fill={isStarred ? 'red' : 'none'}/>
|
||||
{isStarred ? 'Starred' : 'Star Artist'}
|
||||
</Button>
|
||||
<div className="flex gap-3">
|
||||
<Button
|
||||
onClick={handlePlayArtist}
|
||||
disabled={isPlayingArtist}
|
||||
className="bg-green-600 hover:bg-green-700"
|
||||
>
|
||||
<Play className="w-4 h-4 mr-2" />
|
||||
{isPlayingArtist ? 'Adding to Queue...' : 'Play Artist'}
|
||||
</Button>
|
||||
<Button onClick={handleStar} variant="secondary">
|
||||
<Heart className={isStarred ? 'text-red-500' : 'text-gray-500'} fill={isStarred ? 'red' : 'none'}/>
|
||||
{isStarred ? 'Starred' : 'Star Artist'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user