Files
lidify/frontend/features/artist/components/Discography.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
2.8 KiB
TypeScript

"use client";
import { PlayableCard } from "@/components/ui/PlayableCard";
import { Disc3 } from "lucide-react";
import { api } from "@/lib/api";
import type { Album } from "../types";
interface DiscographyProps {
albums: Album[];
colors: any;
onPlayAlbum: (albumId: string, albumTitle: string) => Promise<void>;
sortBy: "year" | "dateAdded";
onSortChange: (sortBy: "year" | "dateAdded") => void;
}
export function Discography({
albums,
colors,
onPlayAlbum,
sortBy,
onSortChange,
}: DiscographyProps) {
if (!albums || albums.length === 0) {
return null;
}
return (
<section>
<div className="flex items-center justify-between mb-4">
<h2 className="text-xl font-bold">Discography</h2>
{/* Sort Dropdown */}
<select
value={sortBy}
onChange={(e) =>
onSortChange(e.target.value as "year" | "dateAdded")
}
className="px-3 py-1.5 bg-white/5 border border-white/10 rounded-full text-white text-xs focus:outline-none focus:border-white/20 [&>option]:bg-[#1a1a1a] [&>option]:text-white"
>
<option value="year">Year (Newest)</option>
<option value="dateAdded">Date Added (Recent)</option>
</select>
</div>
<div
data-tv-section="discography"
className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4"
>
{albums.map((album, index) => {
const subtitle = [
album.year,
album.trackCount && `${album.trackCount} tracks`,
]
.filter(Boolean)
.join(" • ");
return (
<PlayableCard
key={album.id}
href={`/album/${album.id}`}
coverArt={
album.coverArt
? api.getCoverArtUrl(album.coverArt, 300)
: null
}
title={album.title}
subtitle={subtitle}
placeholderIcon={
<Disc3 className="w-12 h-12 text-gray-600" />
}
badge="owned"
circular={false}
colors={colors}
onPlay={() => onPlayAlbum(album.id, album.title)}
tvCardIndex={index}
/>
);
})}
</div>
</section>
);
}