-
-
-
-
{playlist.name}
-
- {playlist.songCount} songs
-
- {playlist.comment && (
-
- {playlist.comment}
+ {playlists.map((playlist) => {
+ const playlistCoverUrl = playlist.coverArt
+ ? api.getCoverArtUrl(playlist.coverArt, 200)
+ : '/default-user.jpg';
+
+ return (
+
+
+
+
+
+
+
+
{playlist.name}
+
+ {playlist.songCount} songs
- )}
+
+ {playlist.comment && (
+
+ {playlist.comment}
+
+ )}
+
+
-
-
- ))}
+
+ );
+ })}
diff --git a/app/playlist/[id]/page.tsx b/app/playlist/[id]/page.tsx
index a41ecc9..1990c4f 100644
--- a/app/playlist/[id]/page.tsx
+++ b/app/playlist/[id]/page.tsx
@@ -3,13 +3,16 @@
import { useEffect, useState } from 'react';
import { useParams } from 'next/navigation';
import Image from 'next/image';
+import Link from 'next/link';
import { Playlist, Song } from '@/lib/navidrome';
import { useNavidrome } from '@/app/components/NavidromeContext';
import { useAudioPlayer } from '@/app/components/AudioPlayerContext';
-import { Play, Heart, Plus } from 'lucide-react';
+import { getNavidromeAPI } from '@/lib/navidrome';
+import { Play, Heart, Plus, Clock, User, Disc } from 'lucide-react';
import { Button } from '@/components/ui/button';
import Loading from "@/app/components/loading";
import { Separator } from '@/components/ui/separator';
+import { ScrollArea } from '@/components/ui/scroll-area';
export default function PlaylistPage() {
const { id } = useParams();
@@ -17,7 +20,8 @@ export default function PlaylistPage() {
const [tracklist, setTracklist] = useState
([]);
const [loading, setLoading] = useState(true);
const { getPlaylist } = useNavidrome();
- const { playTrack, addToQueue } = useAudioPlayer();
+ const { playTrack, addToQueue, currentTrack } = useAudioPlayer();
+ const api = getNavidromeAPI();
useEffect(() => {
const fetchPlaylist = async () => {
@@ -45,11 +49,11 @@ export default function PlaylistPage() {
const track = {
id: song.id,
name: song.title,
- url: '', // Will be set by the context
+ url: api.getStreamUrl(song.id),
artist: song.artist,
album: song.album,
duration: song.duration,
- coverArt: song.coverArt,
+ coverArt: song.coverArt ? api.getCoverArtUrl(song.coverArt, 300) : undefined,
albumId: song.albumId,
artistId: song.artistId
};
@@ -60,21 +64,51 @@ export default function PlaylistPage() {
const track = {
id: song.id,
name: song.title,
- url: '', // Will be set by the context
+ url: api.getStreamUrl(song.id),
artist: song.artist,
album: song.album,
duration: song.duration,
- coverArt: song.coverArt,
+ coverArt: song.coverArt ? api.getCoverArtUrl(song.coverArt, 300) : undefined,
albumId: song.albumId,
artistId: song.artistId
};
addToQueue(track);
};
- const formatDuration = (seconds: number) => {
- const minutes = Math.floor(seconds / 60);
- const secs = Math.floor(seconds % 60).toString().padStart(2, '0');
- return `${minutes}:${secs}`;
+ const handlePlayPlaylist = () => {
+ if (tracklist.length === 0) return;
+
+ // Convert all songs to tracks
+ const tracks = tracklist.map(song => ({
+ id: song.id,
+ name: song.title,
+ url: api.getStreamUrl(song.id),
+ artist: song.artist,
+ album: song.album,
+ duration: song.duration,
+ coverArt: song.coverArt ? api.getCoverArtUrl(song.coverArt, 300) : undefined,
+ albumId: song.albumId,
+ artistId: song.artistId
+ }));
+
+ // Play the first track and add the rest to queue
+ if (tracks.length > 0) {
+ playTrack(tracks[0], true); // Enable autoplay
+ if (tracks.length > 1) {
+ // Add remaining tracks to queue
+ tracks.slice(1).forEach(track => addToQueue(track));
+ }
+ }
+ };
+
+ const isCurrentlyPlaying = (song: Song): boolean => {
+ return currentTrack?.id === song.id;
+ };
+
+ const formatDuration = (duration: number): string => {
+ const minutes = Math.floor(duration / 60);
+ const seconds = duration % 60;
+ return `${minutes}:${seconds.toString().padStart(2, '0')}`;
};
if (loading) {
@@ -92,64 +126,146 @@ export default function PlaylistPage() {
);
}
+ // Get playlist cover art URL with fallback
+ const playlistCoverUrl = playlist.coverArt
+ ? api.getCoverArtUrl(playlist.coverArt, 300)
+ : '/default-user.jpg';
+
return (
-
-
+
+
{playlist.comment && (
-
{playlist.comment}
+
{playlist.comment}
)}
+
-
{playlist.songCount} songs • {formatDuration(playlist.duration || 0)}
+
{playlist.songCount} songs • Duration: {formatDuration(playlist.duration || 0)}
{playlist.public !== undefined && (
{playlist.public ? 'Public' : 'Private'} playlist
)}
-
+
- {tracklist.length > 0 ? (
- tracklist.map((song, index) => (
-
handlePlayClick(song)}>
-
-
{index + 1}
-
-
- {song.title}
-
-
- {song.artist}
-
-
-
-
-
{formatDuration(song.duration)}
-
-
+
+ {tracklist.length === 0 ? (
+
+
This playlist is empty.
- ))
- ) : (
-
-
This playlist is empty
-
- )}
+ ) : (
+
+ {tracklist.map((song, index) => (
+
handlePlayClick(song)}
+ >
+ {/* Track Number / Play Indicator */}
+
+ {isCurrentlyPlaying(song) ? (
+
+ ) : (
+ <>
+
{index + 1}
+
+ >
+ )}
+
+
+ {/* Album Art */}
+
+
+
+
+ {/* Song Info */}
+
+
+
+
+
+ e.stopPropagation()}
+ >
+ {song.artist}
+
+
+ {song.album && (
+
+
+ e.stopPropagation()}
+ >
+ {song.album}
+
+
+ )}
+
+
+
+ {/* Duration */}
+
+
+ {formatDuration(song.duration)}
+
+
+ {/* Actions */}
+
+
+
+
+ ))}
+
+ )}
+