Files
lidify/frontend/features/discover/components/DiscoverHero.tsx
Your Name cc8d0f6969 Release v1.3.0: Multi-source downloads, audio analyzer resilience, mobile improvements
Major Features:
- Multi-source download system (Soulseek/Lidarr with fallback)
- Configurable enrichment speed control (1-5x)
- Mobile touch drag support for seek sliders
- iOS PWA media controls (Control Center, Lock Screen)
- Artist name alias resolution via Last.fm
- Circuit breaker pattern for audio analysis

Critical Fixes:
- Audio analyzer stability (non-ASCII, BrokenProcessPool, OOM)
- Discovery system race conditions and import failures
- Radio decade categorization using originalYear
- LastFM API response normalization
- Mood bucket infinite loop prevention

Security:
- Bull Board admin authentication
- Lidarr webhook signature verification
- JWT token expiration and refresh
- Encryption key validation on startup

Closes #2, #6, #9, #13, #21, #26, #31, #34, #35, #37, #40, #43
2026-01-06 20:07:33 -06:00

81 lines
3.4 KiB
TypeScript

import { Music2 } from "lucide-react";
import { format } from "date-fns";
import { DiscoverPlaylist, DiscoverConfig } from "../types";
interface DiscoverHeroProps {
playlist: DiscoverPlaylist | null;
config: DiscoverConfig | null;
}
export function DiscoverHero({ playlist, config }: DiscoverHeroProps) {
// Calculate total duration
const totalDuration =
playlist?.tracks?.reduce((sum, t) => sum + (t.duration || 0), 0) || 0;
const formatTotalDuration = (seconds: number) => {
const hours = Math.floor(seconds / 3600);
const mins = Math.floor((seconds % 3600) / 60);
if (hours > 0) {
return `about ${hours} hr ${mins} min`;
}
return `${mins} min`;
};
return (
<div className="relative bg-gradient-to-b from-purple-900/40 via-[#1a1a1a] to-transparent pt-16 pb-10 px-4 md:px-8">
<div className="flex items-end gap-6">
{/* Icon */}
<div className="w-[140px] h-[140px] md:w-[192px] md:h-[192px] bg-gradient-to-br from-purple-600/30 to-yellow-600/20 rounded shadow-2xl shrink-0 flex items-center justify-center border border-white/10">
<Music2 className="w-16 h-16 md:w-20 md:h-20 text-purple-400" />
</div>
{/* Info - Bottom Aligned */}
<div className="flex-1 min-w-0 pb-1">
<p className="text-xs font-medium text-white/90 mb-1">
Playlist
</p>
<h1 className="text-2xl md:text-4xl lg:text-5xl font-bold text-white leading-tight line-clamp-2 mb-2">
Discover Weekly
</h1>
<p className="text-sm text-white/60 mb-2 line-clamp-2">
Your personalized playlist of new music, curated based
on your listening history.
</p>
<div className="flex flex-wrap items-center gap-1 text-sm text-white/70">
{playlist && (
<>
<span>
Week of{" "}
{format(
new Date(playlist.weekStart),
"MMM d, yyyy"
)}
</span>
<span className="mx-1"></span>
<span>{playlist.totalCount} songs</span>
{totalDuration > 0 && (
<span>
, {formatTotalDuration(totalDuration)}
</span>
)}
</>
)}
{config?.lastGeneratedAt && (
<>
<span className="mx-1"></span>
<span>
Updated{" "}
{format(
new Date(config.lastGeneratedAt),
"MMM d"
)}
</span>
</>
)}
</div>
</div>
</div>
</div>
);
}