From 52bcc81068712dc22d505608e622766fe8465f5f Mon Sep 17 00:00:00 2001 From: angel Date: Thu, 19 Jun 2025 20:34:15 +0000 Subject: [PATCH 1/9] 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. --- app/artist/[artist]/page.tsx | 55 +++- app/components/AudioPlayer.tsx | 17 +- app/components/FullScreenPlayer.tsx | 372 ++++++++++++++++++++++ app/components/NavidromeConfigContext.tsx | 101 ++++++ app/components/sidebar.tsx | 22 ++ app/layout.tsx | 21 +- app/radio/page.tsx | 256 +++++++++++++++ app/settings/page.tsx | 187 ++++++++++- lib/lrclib.ts | 108 +++++++ lib/navidrome.ts | 92 +++++- 10 files changed, 1204 insertions(+), 27 deletions(-) create mode 100644 app/components/FullScreenPlayer.tsx create mode 100644 app/components/NavidromeConfigContext.tsx create mode 100644 app/radio/page.tsx create mode 100644 lib/lrclib.ts diff --git a/app/artist/[artist]/page.tsx b/app/artist/[artist]/page.tsx index ff1556c..1bca727 100644 --- a/app/artist/[artist]/page.tsx +++ b/app/artist/[artist]/page.tsx @@ -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([]); const [loading, setLoading] = useState(true); const [artist, setArtist] = useState(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 ; } @@ -90,10 +125,20 @@ export default function ArtistPage() {

{artist.name}

{artist.albumCount} albums

- +
+ + +
diff --git a/app/components/AudioPlayer.tsx b/app/components/AudioPlayer.tsx index 14e2e55..72b762e 100644 --- a/app/components/AudioPlayer.tsx +++ b/app/components/AudioPlayer.tsx @@ -2,7 +2,8 @@ import React, { useEffect, useRef, useState } from 'react'; import Image from 'next/image'; import { useAudioPlayer } from '@/app/components/AudioPlayerContext'; -import { FaPlay, FaPause, FaVolumeHigh, FaForward, FaBackward, FaCompress, FaVolumeXmark } from "react-icons/fa6"; +import { FullScreenPlayer } from '@/app/components/FullScreenPlayer'; +import { FaPlay, FaPause, FaVolumeHigh, FaForward, FaBackward, FaCompress, FaVolumeXmark, FaExpand } from "react-icons/fa6"; import ColorThief from '@neutrixs/colorthief'; import { Progress } from '@/components/ui/progress'; import { useToast } from '@/hooks/use-toast'; @@ -16,6 +17,7 @@ export const AudioPlayer: React.FC = () => { const [volume, setVolume] = useState(1); const [isClient, setIsClient] = useState(false); const [isMinimized, setIsMinimized] = useState(false); + const [isFullScreen, setIsFullScreen] = useState(false); const audioCurrent = audioRef.current; const { toast } = useToast(); @@ -229,6 +231,13 @@ export const AudioPlayer: React.FC = () => {
+