import React, { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { Play, Star, ExternalLink, X, ChevronLeft, Download, ListPlus, Info } from 'lucide-react'; import { SubsonicSong, buildCoverArtUrl } from '../api/subsonic'; import CachedImage from './CachedImage'; import CoverLightbox from './CoverLightbox'; import { useTranslation } from 'react-i18next'; function formatDuration(seconds: number): string { const m = Math.floor(seconds / 60); const s = seconds % 60; return `${m}:${s.toString().padStart(2, '0')}`; } function formatSize(bytes?: number): string { if (!bytes) return ''; return `${(bytes / 1024 / 1024).toFixed(1)} MB`; } function sanitizeHtml(html: string): string { const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); doc.querySelectorAll('script, style, iframe, object, embed, form, input, button, select, base, meta, link').forEach(el => el.remove()); doc.querySelectorAll('*').forEach(el => { Array.from(el.attributes).forEach(attr => { const name = attr.name.toLowerCase(); const val = attr.value.toLowerCase().trim(); if ( name.startsWith('on') || (name === 'href' && (val.startsWith('javascript:') || val.startsWith('data:'))) || (name === 'src' && (val.startsWith('javascript:') || val.startsWith('data:'))) ) { el.removeAttribute(attr.name); } }); }); return doc.body.innerHTML; } function BioModal({ bio, onClose }: { bio: string; onClose: () => void }) { const { t } = useTranslation(); return (
e.stopPropagation()}>

{t('albumDetail.bioModal')}

); } interface AlbumInfo { id: string; name: string; artist: string; artistId: string; year?: number; genre?: string; coverArt?: string; recordLabel?: string; } interface AlbumHeaderProps { info: AlbumInfo; songs: SubsonicSong[]; coverUrl: string; coverKey: string; resolvedCoverUrl: string | null; isStarred: boolean; downloadProgress: number | null; bio: string | null; bioOpen: boolean; onToggleStar: () => void; onDownload: () => void; onPlayAll: () => void; onEnqueueAll: () => void; onBio: () => void; onCloseBio: () => void; } export default function AlbumHeader({ info, songs, coverUrl, coverKey, resolvedCoverUrl, isStarred, downloadProgress, bio, bioOpen, onToggleStar, onDownload, onPlayAll, onEnqueueAll, onBio, onCloseBio, }: AlbumHeaderProps) { const { t } = useTranslation(); const navigate = useNavigate(); const [lightboxOpen, setLightboxOpen] = useState(false); const totalDuration = songs.reduce((acc, s) => acc + s.duration, 0); const totalSize = songs.reduce((acc, s) => acc + (s.size ?? 0), 0); const formatLabel = [...new Set(songs.map(s => s.suffix).filter((f): f is string => !!f))].map(f => f.toUpperCase()).join(' / '); return ( <> {bioOpen && bio && } {lightboxOpen && info.coverArt && ( setLightboxOpen(false)} /> )}
{resolvedCoverUrl && (