import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Download, FolderOpen, ImageDown, FileText, BadgeCheck } from "lucide-react"; import { Spinner } from "@/components/ui/spinner"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { SearchAndSort } from "./SearchAndSort"; import { TrackList } from "./TrackList"; import { DownloadProgress } from "./DownloadProgress"; import type { TrackMetadata, TrackAvailability } from "@/types/api"; import { downloadHeader, downloadGalleryImage, downloadAvatar } from "@/lib/api"; import { getSettings } from "@/lib/settings"; import { toastWithSound as toast } from "@/lib/toast-with-sound"; import { useState } from "react"; interface ArtistInfoProps { artistInfo: { name: string; images: string; header?: string; gallery?: string[]; followers: number; genres: string[]; biography?: string; verified?: boolean; listeners?: number; rank?: number; }; albumList: Array<{ id: string; name: string; images: string; release_date: string; album_type: string; external_urls: string; }>; trackList: TrackMetadata[]; searchQuery: string; sortBy: string; selectedTracks: string[]; downloadedTracks: Set; failedTracks: Set; skippedTracks: Set; downloadingTrack: string | null; isDownloading: boolean; bulkDownloadType: "all" | "selected" | null; downloadProgress: number; currentDownloadInfo: { name: string; artists: string; } | null; currentPage: number; itemsPerPage: number; downloadedLyrics?: Set; failedLyrics?: Set; skippedLyrics?: Set; downloadingLyricsTrack?: string | null; checkingAvailabilityTrack?: string | null; availabilityMap?: Map; downloadedCovers?: Set; failedCovers?: Set; skippedCovers?: Set; downloadingCoverTrack?: string | null; isBulkDownloadingCovers?: boolean; isBulkDownloadingLyrics?: boolean; onSearchChange: (value: string) => void; onSortChange: (value: string) => void; onToggleTrack: (isrc: string) => void; onToggleSelectAll: (tracks: TrackMetadata[]) => void; onDownloadTrack: (isrc: string, name: string, artists: string, albumName: string, spotifyId?: string, folderName?: string, durationMs?: number, position?: number, albumArtist?: string, releaseDate?: string, coverUrl?: string, spotifyTrackNumber?: number, spotifyDiscNumber?: number, spotifyTotalTracks?: number, spotifyTotalDiscs?: number, copyright?: string, publisher?: string) => void; onDownloadLyrics?: (spotifyId: string, name: string, artists: string, albumName: string, folderName?: string, isArtistDiscography?: boolean, position?: number, albumArtist?: string, releaseDate?: string, discNumber?: number) => void; onDownloadCover?: (coverUrl: string, trackName: string, artistName: string, albumName: string, folderName?: string, isArtistDiscography?: boolean, position?: number, trackId?: string, albumArtist?: string, releaseDate?: string, discNumber?: number) => void; onCheckAvailability?: (spotifyId: string) => void; onDownloadAllLyrics?: () => void; onDownloadAllCovers?: () => void; onDownloadAll: () => void; onDownloadSelected: () => void; onStopDownload: () => void; onOpenFolder: () => void; onAlbumClick: (album: { id: string; name: string; external_urls: string; }) => void; onArtistClick: (artist: { id: string; name: string; external_urls: string; }) => void; onPageChange: (page: number) => void; onTrackClick?: (track: TrackMetadata) => void; } export function ArtistInfo({ artistInfo, albumList, trackList, searchQuery, sortBy, selectedTracks, downloadedTracks, failedTracks, skippedTracks, downloadingTrack, isDownloading, bulkDownloadType, downloadProgress, currentDownloadInfo, currentPage, itemsPerPage, downloadedLyrics, failedLyrics, skippedLyrics, downloadingLyricsTrack, checkingAvailabilityTrack, availabilityMap, downloadedCovers, failedCovers, skippedCovers, downloadingCoverTrack, isBulkDownloadingCovers, isBulkDownloadingLyrics, onSearchChange, onSortChange, onToggleTrack, onToggleSelectAll, onDownloadTrack, onDownloadLyrics, onDownloadCover, onCheckAvailability, onDownloadAllLyrics, onDownloadAllCovers, onDownloadAll, onDownloadSelected, onStopDownload, onOpenFolder, onAlbumClick, onArtistClick, onPageChange, onTrackClick, }: ArtistInfoProps) { const [downloadingHeader, setDownloadingHeader] = useState(false); const [downloadingAvatar, setDownloadingAvatar] = useState(false); const [downloadingGalleryIndex, setDownloadingGalleryIndex] = useState(null); const [downloadingAllGallery, setDownloadingAllGallery] = useState(false); const handleDownloadHeader = async () => { if (!artistInfo.header) return; setDownloadingHeader(true); try { const settings = getSettings(); const response = await downloadHeader({ header_url: artistInfo.header, artist_name: artistInfo.name, output_dir: settings.downloadPath, }); if (response.success) { if (response.already_exists) { toast.info("Header already exists"); } else { toast.success("Header downloaded successfully"); } } else { toast.error(response.error || "Failed to download header"); } } catch (error) { toast.error(`Error downloading header: ${error}`); } finally { setDownloadingHeader(false); } }; const handleDownloadAvatar = async () => { if (!artistInfo.images) return; setDownloadingAvatar(true); try { const settings = getSettings(); const response = await downloadAvatar({ avatar_url: artistInfo.images, artist_name: artistInfo.name, output_dir: settings.downloadPath, }); if (response.success) { if (response.already_exists) { toast.info("Avatar already exists"); } else { toast.success("Avatar downloaded successfully"); } } else { toast.error(response.error || "Failed to download avatar"); } } catch (error) { toast.error(`Error downloading avatar: ${error}`); } finally { setDownloadingAvatar(false); } }; const handleDownloadGalleryImage = async (imageUrl: string, index: number) => { setDownloadingGalleryIndex(index); try { const settings = getSettings(); const response = await downloadGalleryImage({ image_url: imageUrl, artist_name: artistInfo.name, image_index: index, output_dir: settings.downloadPath, }); if (response.success) { if (response.already_exists) { toast.info(`Gallery image ${index + 1} already exists`); } else { toast.success(`Gallery image ${index + 1} downloaded successfully`); } } else { toast.error(response.error || `Failed to download gallery image ${index + 1}`); } } catch (error) { toast.error(`Error downloading gallery image ${index + 1}: ${error}`); } finally { setDownloadingGalleryIndex(null); } }; const handleDownloadAllGallery = async () => { if (!artistInfo.gallery || artistInfo.gallery.length === 0) return; setDownloadingAllGallery(true); try { const settings = getSettings(); let successCount = 0; let existsCount = 0; let failCount = 0; for (let index = 0; index < artistInfo.gallery.length; index++) { const imageUrl = artistInfo.gallery[index]; try { const response = await downloadGalleryImage({ image_url: imageUrl, artist_name: artistInfo.name, image_index: index, output_dir: settings.downloadPath, }); if (response.success) { if (response.already_exists) { existsCount++; } else { successCount++; } } else { failCount++; } } catch (error) { failCount++; } } if (failCount === 0) { if (existsCount > 0 && successCount > 0) { toast.success(`${successCount} images downloaded, ${existsCount} already existed`); } else if (existsCount > 0) { toast.info(`All ${existsCount} images already exist`); } else { toast.success(`All ${successCount} gallery images downloaded successfully`); } } else { toast.error(`${failCount} images failed to download`); } } catch (error) { toast.error(`Error downloading gallery images: ${error}`); } finally { setDownloadingAllGallery(false); } }; return (
{artistInfo.header ? (<>

Download Header

{artistInfo.images && (
{artistInfo.name}

Download Avatar

)}

Artist

{artistInfo.name}

{artistInfo.verified && ()}
{artistInfo.biography && (

{artistInfo.biography}

)}
{artistInfo.followers.toLocaleString()} followers {artistInfo.listeners && (<> {artistInfo.listeners.toLocaleString()} listeners )} {artistInfo.rank && (<> #{artistInfo.rank} rank )} {albumList.length} {albumList.length === 1 ? "album" : "albums"} {trackList.length} {trackList.length === 1 ? "track" : "tracks"} {artistInfo.genres.length > 0 && (<> {artistInfo.genres.join(", ")} )}
) : (
{artistInfo.images && (
{artistInfo.name}

Download Avatar

)}

Artist

{artistInfo.name}

{artistInfo.verified && ()}
{artistInfo.biography && (

{artistInfo.biography}

)}
{artistInfo.followers.toLocaleString()} followers {artistInfo.listeners && (<> {artistInfo.listeners.toLocaleString()} listeners )} {artistInfo.rank && (<> #{artistInfo.rank} rank )} {albumList.length} albums {trackList.length} tracks {artistInfo.genres.length > 0 && (<> {artistInfo.genres.join(", ")} )}
)} {artistInfo.gallery && artistInfo.gallery.length > 0 && (

Gallery ({artistInfo.gallery.length})

Download All Gallery

{artistInfo.gallery.map((imageUrl, index) => (
{`${artistInfo.name}

Download Image {index + 1}

))}
)} {albumList.length > 0 && (

Discography

{albumList.map((album) => (
onAlbumClick({ id: album.id, name: album.name, external_urls: album.external_urls, })}>
{album.images && ({album.name})}

{album.name}

{album.release_date?.split("-")[0]}

))}
)} {trackList.length > 0 && (

All Tracks

{selectedTracks.length > 0 && ()} {onDownloadAllLyrics && (

Download All Lyrics

)} {onDownloadAllCovers && (

Download All Covers

)} {downloadedTracks.size > 0 && ()}
{isDownloading && ()}
)}
); }