Files
lidify/frontend/features/audiobook/hooks/useAudiobookData.ts
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

71 lines
1.9 KiB
TypeScript

"use client";
import { useEffect } from "react";
import { useParams } from "next/navigation";
import { useAudiobookQuery } from "@/hooks/useQueries";
import { api } from "@/lib/api";
import { subscribeQueryEvent } from "@/lib/query-events";
export function useAudiobookData() {
const params = useParams();
const audiobookId = params.id as string;
const { data: audiobook, isLoading, refetch } = useAudiobookQuery(audiobookId);
// Listen for audiobook-progress-updated event (fired when playback starts/updates)
useEffect(() => {
const unsubscribe = subscribeQueryEvent("audiobook-progress-updated", () => {
refetch();
});
return unsubscribe;
}, [refetch]);
// Calculate hero image for color extraction
const heroImage = audiobook?.coverUrl
? api.getCoverArtUrl(audiobook.coverUrl, 1200)
: null;
// Extract metadata from audioFiles
const getMetadata = () => {
if (!audiobook) return null;
if (!audiobook.audioFiles?.[0]?.metaTags) {
return {
narrator: audiobook.narrator || null,
genre: null,
publishedYear: null,
description: audiobook.description || null,
};
}
const metaTags = audiobook.audioFiles[0].metaTags;
// Extract narrator from description or tagComment
let narrator = audiobook.narrator;
if (!narrator || narrator.trim() === "") {
const desc = audiobook.description || metaTags.tagComment || "";
const narratorMatch = desc.match(/(?:Read by|Narrated by):\s*(.+)/i);
if (narratorMatch) {
narrator = narratorMatch[1].trim();
}
}
return {
narrator: narrator || null,
genre: metaTags.tagGenre || null,
publishedYear: metaTags.tagDate || null,
description: audiobook.description || metaTags.tagComment || null,
};
};
return {
audiobookId,
audiobook,
isLoading,
refetch,
heroImage,
metadata: getMetadata(),
};
}