Add librosa beat/mood analysis and an Ambience background driven by it
The background worker now runs a real librosa analyzer (tempo, beat grid, per-second energy/valence curves) instead of only the null baseline, kept behind build_analyzer() so a plain checkout without the analysis extra still runs fine. The web player reads that per-track analysis and drives a new animated "Ambience" background (bubbles, colour, current) that reacts to the beat and the mood curve as the track plays, plus a debug overlay for tuning it. Also adds a one-off script to backfill podcast cover art from iTunes.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/** Every call the UI makes. Commands are fire-and-forget: the websocket reports back. */
|
||||
|
||||
import type { Album, HaConfig, HaEntityState, PlayerState, Settings } from "./types";
|
||||
import type { Album, HaConfig, HaEntityState, PlayerState, Settings, TrackDetail } from "./types";
|
||||
|
||||
async function request<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
const response = await fetch(`/api${path}`, {
|
||||
@@ -47,6 +47,14 @@ async function fetchHaStates(entityIds: string[]): Promise<Record<string, HaEnti
|
||||
return byId;
|
||||
}
|
||||
|
||||
/** `null` means "not analyzed" (the backend's expected 404 for this), not an error -
|
||||
* the ambient background just falls back to its un-analyzed baseline for that track. */
|
||||
async function fetchTrackDetail(albumId: string, trackIndex: number): Promise<TrackDetail | null> {
|
||||
const response = await fetch(`/api/tracks/${albumId}/${trackIndex}/analysis`);
|
||||
if (!response.ok) return null;
|
||||
return (await response.json()) as TrackDetail;
|
||||
}
|
||||
|
||||
export const api = {
|
||||
library: () => request<{ albums: Album[] }>("/library").then((body) => body.albums),
|
||||
state: () => request<PlayerState>("/state"),
|
||||
@@ -70,6 +78,8 @@ export const api = {
|
||||
haStates: fetchHaStates,
|
||||
haCallService: (domain: string, service: string, body: Record<string, unknown>) =>
|
||||
post(`/ha/services/${domain}/${service}`, body),
|
||||
|
||||
trackDetail: fetchTrackDetail,
|
||||
};
|
||||
|
||||
export const coverUrl = (albumId: string) => `/api/albums/${albumId}/cover`;
|
||||
|
||||
Reference in New Issue
Block a user