forked from Keelrleek/SoulStream
import: the whole feature, and releases put back together
Everything the import grew over the last week, landing in one go — the window, the picker, the audition pen, apply, and the review that stands between them. The fold is the new part. A release filed as a container of parts arrived as N albums, because the scan groups audio by its immediate parent and the container holds none of its own: Analord's eleven volumes were eleven records, and six more releases split the same way. Records now fold into the release above them, and apply is handed an explicit part list — never a recursive walk, which is what let one stray file swallow 288 descendants and duplicate 9 GB. Where releases live is read off the collection rather than assumed, so a genre-nested library moves the answer instead of breaking; below a dominant majority at one depth the pass refuses entirely, because no folding beats wrong folding. Three vetoes independent of depth catch a misread level. Every fold is listed in the review with a way out. research/import/fold-audit.mjs imports the shipping module, so the audited code is the running code: 9 folds, 0 swallows across four library shapes, and none of the 148 artist folders touched. It was that harness that killed the tempting version of this rule — matching a part against its container folded 40 artist folders, Mac Demarco's 8 albums into one, because album folders carry the artist's name. The regression test found a live bug on the way: parts number from 01 each, so flattening two discs collided, and "same name, same size" read the second file as already-imported and dropped it while still counting it. That reuse now also requires that this run has not already written the path. Also here: the picker's accent crossfades between records instead of cutting through the app's blue (ownership moved to the window, which survives the per-record re-key), the fullscreen viewer wears the album's accent instead of the one it left behind in the portal, Skip defers a record without deciding it, and Keep mine is gone — with the cards picked field by field it was Next under another name.
This commit is contained in:
1 parent
4b30c5e573
commit
e7d0971de2
23 files changed
+6498
-258
No files matched your search
@@ -665,6 +665,80 @@ pub async fn connect_and_login(
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if crate::import::is_audition(update.id) {
|
||||
// Audition downloads are candidate versions under
|
||||
// review in the (Cache) pen — they stay OUT of the
|
||||
// library pipeline; the import window watches them.
|
||||
use soulseek_client::DownloadState as DS;
|
||||
// WHICH record and WHICH track, straight from the
|
||||
// registry — looked up before audition_done retires
|
||||
// the id. The UI matches on this, never on the id:
|
||||
// the first events of a transfer are emitted before
|
||||
// import_audition has returned its ids at all.
|
||||
let (au_rel, au_idx) = crate::import::audition_of(update.id)
|
||||
.map(|(r, i)| (r, i as i64))
|
||||
.unwrap_or_else(|| (String::new(), -1));
|
||||
match &update.state {
|
||||
DS::Complete { local_path } => {
|
||||
let _ = event_app.emit(
|
||||
"import:audition",
|
||||
serde_json::json!({
|
||||
"id": update.id,
|
||||
"rel": au_rel,
|
||||
"idx": au_idx,
|
||||
"localPath": local_path,
|
||||
"remote": update.filename,
|
||||
"done": true,
|
||||
}),
|
||||
);
|
||||
crate::import::audition_done(update.id);
|
||||
}
|
||||
DS::Failed { reason } => {
|
||||
let _ = event_app.emit(
|
||||
"import:audition",
|
||||
serde_json::json!({
|
||||
"id": update.id,
|
||||
"rel": au_rel,
|
||||
"idx": au_idx,
|
||||
"remote": update.filename,
|
||||
"failed": reason,
|
||||
}),
|
||||
);
|
||||
crate::import::audition_done(update.id);
|
||||
}
|
||||
DS::Cancelled => {
|
||||
crate::import::audition_done(update.id);
|
||||
}
|
||||
// In flight. The picker's row can only say what it
|
||||
// is told, and "arriving…" was all it had: the
|
||||
// phase, the QUEUE PLACE and the speed were all
|
||||
// known here and thrown away. A place that ticks
|
||||
// down is the difference between waiting and
|
||||
// wondering whether anything is happening.
|
||||
rest => {
|
||||
let (phase, place) = match rest {
|
||||
DS::Connecting => ("connecting", None),
|
||||
DS::Requested => ("requested", None),
|
||||
DS::Queued { place } => ("queued", *place),
|
||||
DS::Transferring => ("transferring", None),
|
||||
_ => ("working", None),
|
||||
};
|
||||
let _ = event_app.emit(
|
||||
"import:audition",
|
||||
serde_json::json!({
|
||||
"id": update.id,
|
||||
"rel": au_rel,
|
||||
"idx": au_idx,
|
||||
"remote": update.filename,
|
||||
"received": update.received,
|
||||
"total": update.total,
|
||||
"speedBps": update.speed_bps,
|
||||
"phase": phase,
|
||||
"place": place,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let _ = event_app.emit("soulseek:download", download_payload(&update));
|
||||
// Completed real downloads go through the tag/art
|
||||
|
||||
Reference in new issue
Block a user