Files
Psychotoxical-psysonic/src-tauri/crates/psysonic-library/src/library_readiness.rs
T
cucadmuh b24a7fc5cb 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
2026-05-28 10:49:31 +03:00

65 lines
1.9 KiB
Rust

//! When the local library index is safe for background analysis backfill.
use crate::dto::local_tracks_max_updated_ms;
use crate::repos::{sync_state::SyncStateRepository, TrackRepository};
use crate::store::LibraryStore;
/// Mirrors frontend `libraryStatusIsReady` / `libraryIsReady` (spec §9.3).
pub fn library_server_is_ready(store: &LibraryStore, server_id: &str) -> Result<bool, String> {
let repo = SyncStateRepository::new(store);
let phase = repo
.get_sync_phase(server_id, "")
.map_err(|e| e.to_string())?;
let Some(phase) = phase else {
return Ok(has_any_local_tracks(store, server_id));
};
if phase == "ready" {
return Ok(true);
}
if phase == "initial_sync" {
let local = repo
.get_local_track_count(server_id, "")
.map_err(|e| e.to_string())?
.unwrap_or(0);
let server = repo
.get_server_track_count(server_id, "")
.map_err(|e| e.to_string())?
.unwrap_or(0);
if server > 0 && (local as f64 / server as f64) >= 0.95 {
return Ok(true);
}
return Ok(false);
}
if phase == "idle" {
if has_any_local_tracks(store, server_id) {
return Ok(true);
}
if repo
.has_last_full_sync_at(server_id, "")
.map_err(|e| e.to_string())?
{
return Ok(true);
}
if local_tracks_max_updated_ms(store, server_id)?
.is_some_and(|ms| ms > 0)
{
return Ok(true);
}
let local = repo
.get_local_track_count(server_id, "")
.map_err(|e| e.to_string())?
.unwrap_or(0);
return Ok(local > 0);
}
Ok(false)
}
fn has_any_local_tracks(store: &LibraryStore, server_id: &str) -> bool {
TrackRepository::new(store)
.count_live_tracks(server_id)
.map(|n| n > 0)
.unwrap_or(false)
}