docs(skill): add the search ladder, and one budget both batch routes obey
Soulseek matches a query against the filenames peers happen to have, and the name a user gives is usually more specific than any of them: a mix suffix that is not in the file, a diacritic the uploader typed as ASCII, an ampersand where the file has a space. Searched verbatim that is exit 4 for a track dozens of people share. Adds a four-rung ladder (as written, without the version suffix, folded to ASCII without featured artists, then the rarest two or three words), stopping at the first rung that returns anything, and then matching results back against what was actually asked for, since widening finds candidates but does not choose. Reconciled with the rate-limit rule: a wider query is a different query, repeating one is not. Also fixes an inconsistency the concurrency numbers had grown: the subagent route capped transfers per peer but never overall, while the batch route said -c 5, so the same skill gave two answers. There is now one table covering subagents, searches on the wire, transfers, transfers per peer, and rungs per track. Five searches against ten subagents is deliberate, because the server rate-limits searches harder than peers rate-limit uploads. Checked against agents given only this file: 2/2 climb the ladder rather than reporting failure on the first empty search, stop at four rungs, and verify the result is the track that was asked for.
This commit is contained in:
@@ -222,6 +222,51 @@ soulseek-rs user someuser --json
|
||||
`download --stdin` reads whole records, so filtering with `jq` and piping back
|
||||
is the normal way to be selective.
|
||||
|
||||
## Searching: widen by rungs, then match back
|
||||
|
||||
Soulseek matches your query against the filenames peers happen to have on
|
||||
disk. Nobody agrees on how to write a track, so the name the user gives you is
|
||||
usually *more* specific than any filename: it carries a mix suffix that is not
|
||||
in the file, a diacritic the uploader typed as ASCII, an ampersand where the
|
||||
file has a space. Search that verbatim and you get exit 4 for a track dozens of
|
||||
people are sharing.
|
||||
|
||||
So climb down a ladder, one rung at a time, and **stop at the first rung that
|
||||
returns anything usable**:
|
||||
|
||||
1. **As written.** `bicep glue original mix`
|
||||
2. **Without the version suffix**, meaning anything in trailing brackets or
|
||||
after a dash: `(Original Mix)`, `[Radio Edit]`, `- Remastered 2011`, `(feat. X)`.
|
||||
`bicep glue`
|
||||
3. **Without featured artists and punctuation**, and with diacritics folded to
|
||||
ASCII: `sigur rós hoppípolla` becomes `sigur ros hoppipolla`, `Tyler, The
|
||||
Creator - EARFQUAKE` becomes `tyler the creator earfquake`.
|
||||
4. **The two or three rarest words**, usually a surname and one distinctive
|
||||
title word: `hoppipolla`.
|
||||
|
||||
Each rung is a *different, wider* query, which is why this does not break the
|
||||
rule against re-running a search: never repeat a rung, and do not go past the
|
||||
fourth. Four searches is the most any one track should ever cost.
|
||||
|
||||
**Then match back.** Widening finds candidates; it does not choose. Compare each
|
||||
result against what the user actually asked for, folded the same way (lowercase,
|
||||
diacritics stripped, punctuation to spaces), and require the distinctive words
|
||||
to appear in the path. A search for `hoppipolla` will also return live versions,
|
||||
covers, and a DJ set that mentions it.
|
||||
|
||||
Where the user named a specific version, prefer a filename that carries it and
|
||||
treat one that does not as second best rather than a match:
|
||||
|
||||
```bash
|
||||
# "Bicep - Glue (Original Mix)" -> rung 2 finds it
|
||||
soulseek-rs search 'bicep glue' --json --min-bitrate 320
|
||||
# then, among the results, prefer paths containing "glue" and not "live",
|
||||
# "remix" or "edit" unless that is what was asked for.
|
||||
```
|
||||
|
||||
A track that survives all four rungs with nothing plausible is genuinely not
|
||||
there. Say so and move on; do not keep inventing spellings.
|
||||
|
||||
## Downloading a list of tracks
|
||||
|
||||
Soulseek is people's home computers, and they can see what you are doing. The
|
||||
@@ -258,10 +303,28 @@ their queue is *for* when you let it feed them to you a couple at a time,
|
||||
which is the opposite of opening ten transfers at once. Fall back to
|
||||
per-track searching for whatever that peer does not have.
|
||||
|
||||
### How much to have in flight
|
||||
|
||||
One table, because the two routes below must not disagree:
|
||||
|
||||
| | At once |
|
||||
|---|---|
|
||||
| Subagents | 10 |
|
||||
| Searches on the wire | 5 |
|
||||
| Transfers running | 5 |
|
||||
| Transfers from any one peer | 2 |
|
||||
| Search rungs spent on one track | 4 |
|
||||
|
||||
Ten subagents but five searches is deliberate. A subagent spends most of its
|
||||
life waiting on a transfer, not searching, and the server rate-limits searches
|
||||
harder than peers rate-limit uploads. You are already holding subagents at the
|
||||
pick, so hold them before the search the same way: hand out a search slot, take
|
||||
it back when results land.
|
||||
|
||||
### Search in parallel, download with a plan
|
||||
|
||||
Give each track its own subagent, **at most ten at a time**. A subagent
|
||||
searches for its track and judges the candidates, because that is a judgement
|
||||
Give each track its own subagent, **at most ten at a time**. A subagent works
|
||||
the search ladder for its track and judges the candidates, because that is a judgement
|
||||
call every time: bitrate against file size, the right pressing against a live
|
||||
bootleg of the same name, a peer with a free slot against a faster one. A shell
|
||||
loop cannot make that call, so it takes the first hit or needs rules you would
|
||||
@@ -279,7 +342,8 @@ resolve:
|
||||
- **The same file chosen twice**, which wastes a transfer slot on a duplicate.
|
||||
|
||||
Cleared subagents download their own track, report where it landed, and are
|
||||
done. Hold the rest until a slot frees up.
|
||||
done. Hold the rest until a slot frees up: five transfers at once across the
|
||||
batch, and never more than two from one peer.
|
||||
|
||||
**Report as you go.** Each subagent says when its search has found candidates
|
||||
and again when its download finishes. A batch that only reports at the end is
|
||||
@@ -297,13 +361,15 @@ soulseek-rs download --stdin --json -c 5 < picks.ndjson
|
||||
It runs `-c` at a time from the list in order, so **order the lines to
|
||||
alternate between peers**. Consecutive lines naming the same user are what put
|
||||
several simultaneous transfers on one person; interleaving them spreads the
|
||||
load without slowing anything down. Keep `-c` at 5 or so.
|
||||
load without slowing anything down. `-c 5` matches the budget above; the
|
||||
tool's own default is 20, which is too many strangers at once for a batch you
|
||||
did not hand-pick.
|
||||
|
||||
### What not to do
|
||||
|
||||
- **Do not search the same query twice.** The server rate-limits searches, which
|
||||
is why the wishlist has an interval, and a re-run returns what you already
|
||||
have. Keep the results from the first search and pick again from
|
||||
have. Widening it is a different query and is fine; repeating it is not. Keep the results from the first search and pick again from
|
||||
them.
|
||||
- **Do not retry a failed transfer against the same peer.** Exit 6 or 5 means
|
||||
they went offline, their queue is long, or they are not serving you. Take the
|
||||
@@ -334,8 +400,9 @@ send you the file.
|
||||
foreground will hang you until the timeout.
|
||||
- **Every wait is bounded.** `--timeout` per command, `--search-timeout` for
|
||||
searches. There is no unbounded form.
|
||||
- **Exit 4 is not a failure.** A search that finds nothing means the query was
|
||||
too narrow or misspelled. Widen it; use `whoami` to rule out the connection.
|
||||
- **Exit 4 is not a failure.** A search that finds nothing usually means the
|
||||
query was more specific than anyone's filename. Take the next rung of the
|
||||
search ladder; use `whoami` to rule out the connection.
|
||||
- **Exit 7 is not an answer.** The session ended mid-command, so nothing it saw
|
||||
counts. Something else logged in as you, almost always a second run of your
|
||||
own. Start a daemon and route through it rather than hunting for
|
||||
|
||||
Reference in New Issue
Block a user