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
This commit is contained in:
Your Name
2026-01-06 20:07:33 -06:00
parent 8fe151a0d1
commit cc8d0f6969
242 changed files with 20562 additions and 7725 deletions
+9 -7
View File
@@ -60,6 +60,8 @@ A self-hosted music streaming platform with intelligent discovery, podcast suppo
**Bulk Operations** - Delete artists, albums, or tracks with confirmation. Paginated views for large libraries.
**Metadata Enrichment** - Automatic enrichment of artist bios, images, mood tags, and audio analysis from Last.fm and local audio processing. Configurable enrichment speed (Conservative to Maximum) balances processing speed with API rate limits. Comprehensive UX with pause/resume/stop controls, failure tracking, and retry capabilities.
---
## Player
@@ -74,13 +76,13 @@ A self-hosted music streaming platform with intelligent discovery, podcast suppo
## Additional Features
- Global search across artists, albums, and tracks
- Featured playlists from Deezer
- Recently added and popular artists sections
- Create and share playlists with other users
- MusicBrainz integration for accurate metadata
- Clean, responsive UI that works on desktop, tablet, and mobile
- Global search across artists, albums, and tracks
- Featured playlists from Deezer
- Recently added and popular artists sections
- Create and share playlists with other users
- MusicBrainz integration for accurate metadata
- Clean, responsive UI that works on desktop, tablet, and mobile
---
*Lidify is self-hosted, giving you full control over your music library with the discovery features of commercial streaming services.*
_Lidify is self-hosted, giving you full control over your music library with the discovery features of commercial streaming services._
-97
View File
@@ -1,97 +0,0 @@
/**
* Check if tracks have Enhanced vibe analysis data
*/
import { prisma } from "../utils/db";
async function check() {
// Get a sample of tracks with their analysis data
const tracks = await prisma.track.findMany({
take: 10,
select: {
title: true,
album: { select: { artist: { select: { name: true } } } },
analysisMode: true,
moodHappy: true,
moodSad: true,
moodRelaxed: true,
moodAggressive: true,
danceabilityMl: true,
valence: true,
arousal: true,
energy: true,
bpm: true,
moodTags: true,
},
where: {
bpm: { not: null }
}
});
console.log('Sample tracks with analysis data:');
for (const t of tracks) {
console.log(`\n${t.album?.artist?.name} - ${t.title}`);
console.log(` analysisMode: ${t.analysisMode || 'NOT SET (legacy)'}`);
console.log(` ML moods: happy=${t.moodHappy}, sad=${t.moodSad}, relaxed=${t.moodRelaxed}, aggressive=${t.moodAggressive}`);
console.log(` danceabilityMl: ${t.danceabilityMl}`);
console.log(` valence: ${t.valence}, arousal: ${t.arousal}`);
console.log(` energy: ${t.energy}, bpm: ${t.bpm}`);
console.log(` moodTags: ${t.moodTags?.join(', ') || 'none'}`);
}
// Count tracks with enhanced analysis
const enhancedCount = await prisma.track.count({ where: { analysisMode: 'enhanced' } });
const standardCount = await prisma.track.count({ where: { analysisMode: 'standard' } });
const noModeCount = await prisma.track.count({ where: { analysisMode: null, bpm: { not: null } } });
const totalAnalyzed = await prisma.track.count({ where: { bpm: { not: null } } });
// Count tracks with ML mood data
const withMoodHappy = await prisma.track.count({ where: { moodHappy: { not: null } } });
console.log(`\n--- Analysis Mode Stats ---`);
console.log(`Enhanced: ${enhancedCount}`);
console.log(`Standard: ${standardCount}`);
console.log(`No mode (legacy): ${noModeCount}`);
console.log(`Total analyzed: ${totalAnalyzed}`);
console.log(`With ML mood data: ${withMoodHappy}`);
// Check specific songs the user mentioned
console.log(`\n--- Checking specific songs ---`);
const specificSongs = await prisma.track.findMany({
where: {
OR: [
{ title: { contains: "I Love You", mode: "insensitive" } },
{ title: { contains: "Roots", mode: "insensitive" } },
{ title: { contains: "Alright", mode: "insensitive" } },
]
},
select: {
title: true,
album: { select: { artist: { select: { name: true } } } },
analysisMode: true,
moodHappy: true,
moodSad: true,
moodRelaxed: true,
moodAggressive: true,
valence: true,
arousal: true,
energy: true,
bpm: true,
danceability: true,
moodTags: true,
}
});
for (const t of specificSongs) {
console.log(`\n${t.album?.artist?.name} - ${t.title}`);
console.log(` analysisMode: ${t.analysisMode || 'NOT SET (legacy)'}`);
console.log(` ML moods: happy=${t.moodHappy}, sad=${t.moodSad}, relaxed=${t.moodRelaxed}, aggressive=${t.moodAggressive}`);
console.log(` valence: ${t.valence}, arousal: ${t.arousal}`);
console.log(` energy: ${t.energy}, bpm: ${t.bpm}, dance: ${t.danceability}`);
console.log(` moodTags: ${t.moodTags?.join(', ') || 'none'}`);
}
await prisma.$disconnect();
}
check().catch(console.error);