fix(analysis): native library backfill coordinator for advanced strategy (#881)

* feat(analysis): native library backfill coordinator for advanced strategy

Move advanced analytics scheduling from the webview loop into a Rust
background worker (configure + spawn_blocking batch/enqueue), matching
cover backfill. Scan hash+BPM gap tracks instead of the full library;
suppress low-priority analysis UI events; limit loudness refresh IPC to
the playback window.

* fix(analysis): flat backfill configure IPC and restore probe track-perf

Use flattened Tauri args like cover backfill so release/prod invoke works;
keep emitting analysis:track-perf for library low-priority work so Performance
Probe tpm/last-track stats update while waveform/enrichment UI events stay suppressed.

* chore(analysis): fix clippy and drop duplicate TS backfill policy

Allow too_many_arguments on library_analysis_backfill_configure for CI;
remove unused frontend batch API and TS watermark helpers now owned in Rust;
clarify analysis_emits_ui_events comment for low-priority track-perf.

* docs: CHANGELOG and credits for PR #881 native analysis backfill
This commit is contained in:
cucadmuh
2026-05-28 10:49:31 +03:00
committed by GitHub
parent df3533bb5a
commit b24a7fc5cb
21 changed files with 888 additions and 387 deletions
@@ -15,6 +15,7 @@ import {
hasStableLoudness,
setCachedLoudnessGain,
} from '../loudnessGainCache';
import { isTrackInsideLoudnessBackfillWindow } from '../loudnessBackfillWindow';
import { refreshLoudnessForTrack } from '../loudnessRefresh';
import { emitNormalizationDebug } from '../normalizationDebug';
import {
@@ -96,8 +97,19 @@ export function setupAudioEngineListeners(): () => void {
emitNormalizationDebug('backfill:applied', { trackId: currentId });
return;
}
// Backfill finished for another id (e.g. next in queue): refresh loudness cache only
// so the cached gain is ready before `audio_play` / gapless chain.
// Library aggressive backfill completes thousands of tracks — only warm loudness
// for the playback window (current + next N). Skip IPC for the rest.
const live = usePlayerStore.getState();
if (
!isTrackInsideLoudnessBackfillWindow(
payloadTrackId,
live.queueItems,
live.queueIndex,
live.currentTrack,
)
) {
return;
}
void refreshLoudnessForTrack(payloadTrackId, { syncPlayingEngine: false });
emitNormalizationDebug('backfill:applied', { trackId: payloadTrackId });
}),