Leading with `get` was the wrong trade. It ranks on free slot, bitrate and peer speed, which are facts about the transfer and say nothing about whether the file is the track that was asked for. On a setlist of remixes, dubs and white labels it does not miss, it succeeds with the wrong one: exit 0, a file on disk, and nobody finds out until it plays. A miss can be retried; a confident wrong answer cannot. The previous revision optimised the measurable cost and paid for it in the unmeasurable one. The token problem it was solving is real, but the answer is to narrow before reading rather than not to look. A search writes its records to a file; search flags and jq cut three hundred down to eight without any of them entering context; the eight are read and judged. `get` keeps a scoped mention for when the user has named nothing in particular. Adds what "the right one" means, since that is now the load-bearing step: fold both sides, require every distinctive word, reject live/radio edit/snippet/dj mix, prefer lossless, and above all treat the version as the thing you match on rather than the thing you dropped. The ladder drops "(Kalabrese Remix)" to find anything at all, so the match must then require "kalabrese" or it takes the original and reports success. Checked against agents given only this file on a remix-heavy setlist: 2/2 refuse `get`, narrow before reading, require the remixer's name in the chosen path, and report a track as unavailable rather than silently taking the original when only that exists.
soulseek-rs
A Soulseek client for the terminal: a full-screen TUI, and a one-shot command surface built for scripts and agents.
Website: https://re-invention.nl/soulseek-rs/
Installation
brew install michel/tap/soulseek-rs # macOS and Linux, prebuilt
cargo install soulseek-rs # anywhere with a Rust toolchain
Usage
Run it with no subcommand for the interactive TUI:
soulseek-rs
Anything else is a one-shot command that runs headless. stdout carries records only, stderr carries progress, and the exit code carries the verdict:
soulseek-rs search <QUERY> # print matching files
soulseek-rs get <QUERY> # search, pick the best, download
soulseek-rs download <USER> <PATH> # fetch one known file
soulseek-rs download --stdin # fetch files listed on stdin
soulseek-rs browse <USER> # list a user's shared files
soulseek-rs serve [--follow] # stay online sharing, stream uploads
soulseek-rs whoami # confirm credentials and connection
soulseek-rs user <NAME> # a peer's status and share counts
soulseek-rs room list|say|users|listen
soulseek-rs message send|read
soulseek-rs shares list|add|remove|status|reindex
soulseek-rs config path|list|get|set
soulseek-rs portmap # test automatic port mapping
soulseek-rs completions install # tab completion for bash/zsh/fish
Add --json for newline-delimited JSON instead of tab-separated text, so
commands compose:
soulseek-rs search "rjd2" --json \
| jq -c 'select(.size > 5000000 and .free_slot)' \
| soulseek-rs download --stdin
Exit codes: 0 success, 2 usage or config, 3 connect or login, 4 nothing
found, 5 timed out, 6 transfer failed, 7 session lost mid-command, 1
unexpected error.
The full README documents every
command, record shape, flag, environment variable and config.toml key.
Building from Source
git clone https://github.com/michel/soulseek-rs.git
cd soulseek-rs
cargo build --release
Library
To build your own Soulseek client, use the soulseek-rs-lib crate: the protocol implementation this client is built on.