import { useEffect, useState } from "react"; import { Link, useParams } from "react-router-dom"; import { ArrowLeft, Disc3, ExternalLink, LoaderCircle, Play, } from "lucide-react"; import { Button } from "../components/Button"; import { DownloadButton } from "../components/DownloadButton"; import { useSpotifyDownload } from "../helpers/useSpotifyDownload"; import { LyricsPanel } from "../components/LyricsPanel"; import { SpotifyPlayerBar } from "../components/SpotifyPlayerBar"; import { getSpotifyAlbum, type OutputType as SpotifyAlbumResult, type SpotifyAlbumTrack, } from "../endpoints/spotify-album_GET.schema"; import { getSpotifySession, type OutputType as SpotifySession, } from "../endpoints/spotify-session_GET.schema"; import { postLyrics, type OutputType as LyricsResult } from "../endpoints/lyrics_POST.schema"; import { useSpotifyPlayer } from "../helpers/useSpotifyPlayer"; import styles from "./album.$id.module.css"; import { MobileNav } from "../components/MobileNav"; function formatTime(ms: number) { const seconds = Math.max(0, Math.floor(ms / 1000)); return Math.floor(seconds / 60) + ":" + String(seconds % 60).padStart(2, "0"); } export default function AlbumPage() { const params = useParams(); const albumId = params.id || ""; const [spotify, setSpotify] = useState({ connected: false }); const [album, setAlbum] = useState(); const [loading, setLoading] = useState(true); const [error, setError] = useState(); const [lyricsOpen, setLyricsOpen] = useState(false); const [lyricsLoading, setLyricsLoading] = useState(false); const [lyrics, setLyrics] = useState(); const [lyricsError, setLyricsError] = useState(); const player = useSpotifyPlayer(spotify.accessToken); const downloads = useSpotifyDownload(); useEffect(() => { let cancelled = false; async function load() { setLoading(true); try { const session = await getSpotifySession(); if (cancelled) return; setSpotify(session); if (!session.connected) { setError("Connect Spotify to browse and play album tracks."); return; } const result = await getSpotifyAlbum(albumId); if (!cancelled) setAlbum(result); } catch (reason) { if (!cancelled) { setError(reason instanceof Error ? reason.message : "Unable to load album."); } } finally { if (!cancelled) setLoading(false); } } if (albumId) void load(); return () => { cancelled = true; }; }, [albumId]); useEffect(() => { if (!lyricsOpen || !player.track?.title || !player.track.artist) return; let cancelled = false; setLyricsLoading(true); setLyrics(undefined); setLyricsError(undefined); postLyrics({ title: player.track.title, artist: player.track.artist, album: player.track.album || album?.album.name, }) .then((result) => { if (!cancelled) setLyrics(result); }) .catch((reason) => { if (!cancelled) { setLyricsError(reason instanceof Error ? reason.message : "Unable to load lyrics."); } }) .finally(() => { if (!cancelled) setLyricsLoading(false); }); return () => { cancelled = true; }; }, [ lyricsOpen, player.track?.uri, player.track?.title, player.track?.artist, player.track?.album, album?.album.name, ]); async function play(track: SpotifyAlbumTrack) { if (!track.uri) return; try { setError(undefined); await player.playUri(track.uri); } catch (reason) { setError(reason instanceof Error ? reason.message : "Unable to start Spotify playback."); } } return (
My Spotify Spotify Album
{error ? (
{error} {!spotify.connected ? ( ) : null}
) : null} {loading ? (
Loading album…
) : null} {album ? ( <>
{album.album.imageUrl ? ( ) : ( )}
SPOTIFY ALBUM

{album.album.name}

{album.album.artist}

{album.album.releaseDate || "Release date unknown"} {album.album.totalTracks ? " · " + album.album.totalTracks + " tracks" : ""}

{album.album.externalUrl ? ( Spotify Open album ) : null}
(track.externalUrl ? [track.externalUrl] : [])), name: `${album.album.artist} - ${album.album.name}`, }} text="Download album" size="sm" variant="primary" />
#TitleArtistTime
{album.tracks.map((track) => (
play(track)} onKeyDown={(event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); void play(track); } }} > {track.discNumber && track.discNumber > 1 ? track.discNumber + "-" + String(track.trackNumber || 0).padStart(2, "0") : String(track.trackNumber || 0).padStart(2, "0")}
{track.name}
{track.artist} {formatTime(track.durationMs)}
{track.externalUrl ? ( ) : null} {track.externalUrl ? ( ) : null}
))}
) : null} {spotify.connected ? (
setLyricsOpen((open) => !open)} onTogglePlay={() => player.togglePlay()} onPrevious={() => player.previousTrack()} onNext={() => player.nextTrack()} onSeek={(position) => player.seek(position)} onVolume={(volume) => player.setVolume(volume)} />
) : null} {lyricsOpen ? ( setLyricsOpen(false)} /> ) : null}
); }