forked from Keelrleek/SoulStream
library: the page lit by its own records, and portraits that prove out
The expander reserves once on the page root instead of standing min-heights on the grid; shelf heads carry artist portraits keyed by path; every record tints its own card, expander and glow from its cover; the artwork opens the shared viewer; the banner wash reads the playing record and actually paints (isolation, the artist page's own fix); descriptions animate on both pages via partAnim. Portrait resolution rejects placeholder rows and never stores a silhouette (pick_artist_portrait, 3 tests); repair re-resolves the missing.
This commit is contained in:
1 parent
741c4ae6dc
commit
185c29fc33
5 files changed
+1068
-49
No files matched your search
@@ -280,9 +280,56 @@ async fn scan_downloads(app: &tauri::AppHandle) {
|
||||
process_file(app, &path, None).await;
|
||||
}
|
||||
}
|
||||
repair_artist_portraits(app).await;
|
||||
let _ = app.emit("soulseek:library-changed", ());
|
||||
}
|
||||
|
||||
/// Artist portraits are written ONCE, by `process_file` at import. This scan
|
||||
/// skips every file it already knows, so a portrait that later goes missing
|
||||
/// from disk — a cleared art directory, a half-copied profile — leaves the
|
||||
/// database pointing at a file that will never come back, and the shelf head
|
||||
/// falls back to its icon forever.
|
||||
///
|
||||
/// Repair exactly that: an artist whose stored path is NULL or no longer on
|
||||
/// disk. Untouched portraits cost nothing, and an affected artist costs one
|
||||
/// paced Deezer read (shared budget, same as every other read here).
|
||||
///
|
||||
/// Deliberately NOT a re-resolve of portraits that DO exist. That would repair
|
||||
/// the other half of this problem — entries stored before `fetch_artist_art`
|
||||
/// learned to rank candidates and reject Deezer's empty-md5 placeholder — but
|
||||
/// it is a request per artist on every scan, and it would overwrite a portrait
|
||||
/// the user is already looking at. Dropping the `needs` condition below is all
|
||||
/// it would take, if that is ever wanted.
|
||||
async fn repair_artist_portraits(app: &tauri::AppHandle) {
|
||||
let state = app.state::<LibraryState>();
|
||||
let stored = {
|
||||
let lib = state.library.clone();
|
||||
match tauri::async_runtime::spawn_blocking(move || lib.artist_portraits()).await {
|
||||
Ok(Ok(rows)) => rows,
|
||||
_ => return,
|
||||
}
|
||||
};
|
||||
for (artist, path) in stored {
|
||||
let needs = match path.as_deref() {
|
||||
None => true,
|
||||
Some(p) => !Path::new(p).exists(),
|
||||
};
|
||||
if !needs {
|
||||
continue;
|
||||
}
|
||||
if let Some(fresh) = fetch_artist_art_cached(&state, &artist).await {
|
||||
let lib = state.library.clone();
|
||||
let name = artist.clone();
|
||||
let updated =
|
||||
tauri::async_runtime::spawn_blocking(move || lib.set_artist_art(&name, &fresh))
|
||||
.await;
|
||||
if let Ok(Ok(n)) = updated {
|
||||
log::info!("artist portrait repaired for {artist} ({n} albums)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_audio_files(dir: &Path, out: &mut Vec<PathBuf>, depth: u8) {
|
||||
if depth > 6 {
|
||||
return;
|
||||
|
||||
Reference in new issue
Block a user