docs: cut the padding from the readme
Drop the "This structure allows" bullets justifying a two-crate workspace, collapse four one-line "To run X:" stubs into a single block, and lose the adverbs, the emoji heading, and the bolded emphasis. Project Goals no longer claims a hard no-external-crates rule for the library, which stopped being the stance; it says lean instead. Also fixes "Headless mode daemon mode".
This commit is contained in:
@@ -11,15 +11,14 @@ there.**
|
||||
Search the network, share your files, browse someone's collection, join a room.
|
||||
It runs over ssh on the machine where your music already lives.
|
||||
|
||||
Soulseek is a closed-source P2P file-sharing network from the 2000s, still used
|
||||
by music enthusiasts around the world to share niche music. This repository is
|
||||
that client plus [`soulseek-rs-lib`](./soulseek-rs-lib), the protocol library it
|
||||
is built on.
|
||||
Soulseek is a closed-source P2P network from the 2000s, still used by music
|
||||
enthusiasts to share niche music. This repository is that client plus
|
||||
[`soulseek-rs-lib`](./soulseek-rs-lib), the protocol library under it.
|
||||
|
||||
**[re-invention.nl/soulseek-rs](https://re-invention.nl/soulseek-rs/)**: what it
|
||||
does, how to install it, and every `config.toml` setting.
|
||||
|
||||
## 🎥 Demo
|
||||
## Demo
|
||||
|
||||
[](https://re-invention.nl/soulseek-rs/)
|
||||
|
||||
@@ -36,41 +35,31 @@ does, how to install it, and every `config.toml` setting.
|
||||
connections when a peer can't be reached directly
|
||||
- **Automatic port mapping**: opens your listen port via UPnP-IGD and
|
||||
NAT-PMP, with a `portmap` subcommand to test your router
|
||||
- **TUI and CLI**: a full terminal interface, plus a one-shot command surface
|
||||
built for scripts and agents, where every feature is reachable without a
|
||||
terminal, stdout carries records only, and the exit code says what happened
|
||||
- **TUI and CLI**: a full terminal interface, plus one-shot commands for
|
||||
scripts and agents: every feature reachable without a terminal, stdout
|
||||
carrying records, the exit code saying what happened
|
||||
|
||||
## Project Goals
|
||||
|
||||
This project is a learning exercise to explore Rust. I've been using Soulseek
|
||||
since the early 2000s, so it's close to my heart, and the Soulseek protocol is
|
||||
a closed-source network protocol that provides a great opportunity to learn
|
||||
about asynchronous and concurrent network programming and reverse engineering
|
||||
A learning exercise in Rust. I've been on Soulseek since the early 2000s, and
|
||||
its closed-source protocol is a good way to learn asynchronous network
|
||||
programming and reverse engineering.
|
||||
|
||||
Since it's a learning project, I have a self-imposed restriction not to use
|
||||
external dependencies in the library. This means I can't use any external
|
||||
crates that are not part of the Rust standard library. This is a good challenge
|
||||
to learn how to build complex systems with only the standard library.
|
||||
|
||||
In the client crate, external dependencies are allowed for building a rich
|
||||
experience. For me, this is a good balance between learning and practicality.
|
||||
The library stays lean on dependencies and has none today; the client takes
|
||||
them freely.
|
||||
|
||||
## Planned Features
|
||||
|
||||
- [ ] Headless mode daemon mode with remote control
|
||||
- [ ] Headless daemon mode with remote control
|
||||
|
||||
## Project Structure
|
||||
|
||||
This project is organized as a Cargo workspace with two crates:
|
||||
A Cargo workspace with two crates:
|
||||
|
||||
- **soulseek-rs-lib** - The core library implementing the Soulseek protocol
|
||||
- **soulseek-rs** - A CLI client built on top of the library
|
||||
|
||||
This structure allows:
|
||||
|
||||
- Other developers to build custom Soulseek clients using `soulseek-rs-lib`
|
||||
- Users to install the ready-made client with Homebrew or `cargo install`
|
||||
- Clean separation of concerns between protocol implementation and user interface
|
||||
- **soulseek-rs-lib** - the protocol implementation, for anyone building their
|
||||
own client
|
||||
- **soulseek-rs** - the client built on it, installable with Homebrew or
|
||||
`cargo install`
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -98,7 +87,7 @@ cd soulseek-rs
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
The binary will be available at `target/release/soulseek-rs`.
|
||||
The binary lands at `target/release/soulseek-rs`.
|
||||
|
||||
### For Library Users
|
||||
|
||||
@@ -168,10 +157,9 @@ first: the config commands need no account at all, and `whoami` answers
|
||||
|
||||
Eleven of these need no credentials, because they never touch the network:
|
||||
`config path|list|get|set`, `shares list|add|remove`, `portmap` and
|
||||
`skills install|uninstall|list`. Note that
|
||||
`shares status` and `shares reindex` are not among them, because they report
|
||||
what the network will actually see, which means logging in and letting the
|
||||
library scan the folders.
|
||||
`skills install|uninstall|list`. `shares status` and `shares reindex` are not
|
||||
among them: they report what the network will see, which means logging in and
|
||||
scanning the folders.
|
||||
|
||||
Every command emits records except the four that perform an action.
|
||||
`room say`, `message send`, `shares add` and `shares remove` say nothing on
|
||||
@@ -207,10 +195,9 @@ the message field empty. `serve`'s `status` is `uploading`, `completed`,
|
||||
`cancelled` or `failed`. `user`'s `status` is `online`, `away` or `offline`,
|
||||
and any field the server did not answer prints as `-`.
|
||||
|
||||
`--json` carries more than text does, because an object has room for fields a
|
||||
column layout does not: `search` adds `duration`, `slots`, `speed` and
|
||||
`free_slot`; `browse` adds `directory`; `download` adds the `user`, remote
|
||||
`path` and `size` alongside the local `file`; `whoami` adds `listening`,
|
||||
`--json` carries more than text does: `search` adds `duration`, `slots`,
|
||||
`speed` and `free_slot`; `browse` adds `directory`; `download` adds the `user`,
|
||||
remote `path` and `size` alongside the local `file`; `whoami` adds `listening`,
|
||||
`listen_port` and `download_dir`; `user` adds `privileged` and
|
||||
`shared_folders`; `shares status` adds the `directories` array; `serve` adds
|
||||
`bytes_sent`, `size`, `speed` and a `reason` on failure; `portmap` adds
|
||||
@@ -260,10 +247,10 @@ soulseek-rs user someuser --json | jq -e '.status != "offline"'
|
||||
|
||||
[](https://re-invention.nl/soulseek-rs/#agents)
|
||||
|
||||
The scriptable surface above is meant to be driven by coding agents as much as
|
||||
by shell scripts, but `--help` cannot tell an agent which JSON keys a record
|
||||
carries or what exit 4 means. That lives in a skill file shipped inside the
|
||||
binary, so a `cargo install` is all you need to hand it to your agent:
|
||||
Coding agents drive the surface above as readily as shell scripts do, but
|
||||
`--help` cannot tell an agent which JSON keys a record carries or what exit 4
|
||||
means. That lives in a skill file shipped inside the binary, so `cargo install`
|
||||
is all it takes to hand it over:
|
||||
|
||||
```bash
|
||||
soulseek-rs skills install
|
||||
@@ -301,16 +288,16 @@ seconds (an hour by default), or runs until interrupted with `--follow`.
|
||||
|
||||
```bash
|
||||
soulseek-rs shares add ~/Music # remembered in config.toml
|
||||
soulseek-rs shares status # what the network will actually see
|
||||
soulseek-rs shares status # what the network will see
|
||||
soulseek-rs shares reindex # after adding files on disk
|
||||
soulseek-rs serve --follow
|
||||
```
|
||||
|
||||
Uploads are only visible inside a running `serve`, because every other command
|
||||
is a short-lived process with nothing to serve, so there is no separate
|
||||
`uploads list`. Managing transfers across commands (pause, resume, a queue
|
||||
that outlives one invocation) needs the resident daemon that `serve` would
|
||||
grow into; it is not there yet.
|
||||
Uploads only show up inside a running `serve`; every other command is
|
||||
short-lived with nothing to serve, so there is no `uploads list`. Managing
|
||||
transfers across commands (pause, resume, a queue that outlives one
|
||||
invocation) needs the resident daemon `serve` would grow into, which is not
|
||||
there yet.
|
||||
|
||||
Downloads run under a deadline (`--timeout`, 300s by default) and
|
||||
`--max-concurrent-downloads` at a time, so an unattended run always ends.
|
||||
@@ -387,7 +374,7 @@ In the interactive TUI:
|
||||
|
||||
- press `m` to compose, then type `<recipient> <message>` and `Enter` to send;
|
||||
- press `i` to open the inbox popup listing sent and received messages
|
||||
(incoming messages arrive automatically while the TUI is open). The `i`
|
||||
(the TUI receives incoming messages while it is open). The `i`
|
||||
shortcut shows an unread counter, e.g. `i inbox (3)`.
|
||||
|
||||
### Chat rooms
|
||||
@@ -416,20 +403,20 @@ In the interactive TUI, press `c` to open the chat-rooms popup:
|
||||
|
||||
Browsing and downloading are peer-to-peer, so at least one side must accept an
|
||||
incoming connection. When the listener is enabled (the default), the client
|
||||
**automatically tries to open its listen port** on your router via **UPnP-IGD**
|
||||
and **NAT-PMP**, so firewalled peers and the server can connect back to you.
|
||||
This is best-effort: if your router has UPnP/NAT-PMP disabled it's a no-op and
|
||||
you'll see a log line suggesting you forward the port manually.
|
||||
tries to open its listen port on your router via **UPnP-IGD** and **NAT-PMP**,
|
||||
so firewalled peers and the server can connect back. This is best-effort: if
|
||||
your router has UPnP/NAT-PMP disabled it's a no-op, and a log line tells you to
|
||||
forward the port yourself.
|
||||
|
||||
- The mapped/forwarded port is your `--listener-port` (env
|
||||
`SOULSEEK_LISTENER_PORT`, default `2234`); it is renewed automatically and
|
||||
removed on exit.
|
||||
- If auto-mapping can't get that exact port, forward **TCP 2234** (or whatever
|
||||
`SOULSEEK_LISTENER_PORT`, default `2234`); the client renews it while it runs
|
||||
and removes it on exit.
|
||||
- If auto-mapping can't get that port, forward **TCP 2234** (or whatever
|
||||
`--listener-port` you chose) to this machine on your router.
|
||||
- Pass `--no-listener` to turn the listener (and port mapping) off, or
|
||||
`--listener` to force it on when the config file disables it.
|
||||
|
||||
Check whether it works on **your** network without launching the whole client:
|
||||
Check whether it works on your network without launching the whole client:
|
||||
|
||||
```bash
|
||||
soulseek-rs portmap # exits 0 when mapping works, 4 when it does not
|
||||
@@ -447,27 +434,10 @@ bug.
|
||||
|
||||
## Development
|
||||
|
||||
To run the project in development mode with debug output and trace output:
|
||||
|
||||
```bash
|
||||
RUST_LOG=trace cargo run
|
||||
```
|
||||
|
||||
To run the tests:
|
||||
|
||||
```bash
|
||||
RUST_LOG=trace cargo run # run with debug and trace output
|
||||
cargo test
|
||||
```
|
||||
|
||||
To run the linter:
|
||||
|
||||
```bash
|
||||
cargo clippy
|
||||
```
|
||||
|
||||
To run the formatter:
|
||||
|
||||
```bash
|
||||
cargo fmt
|
||||
```
|
||||
|
||||
@@ -501,8 +471,8 @@ SOULFIND_BIN=/path/to/soulfind/bin/soulfind \
|
||||
> Homebrew's SQLite instead; no patching needed.
|
||||
|
||||
Set `SOULSEEK_E2E_REQUIRED=1` to turn a missing server into a hard failure
|
||||
instead of a skip. Continuous integration sets it so the e2e suite genuinely
|
||||
runs against a freshly built soulfind rather than silently skipping.
|
||||
instead of a skip. CI sets it, so the e2e suite runs against a freshly built
|
||||
soulfind instead of skipping.
|
||||
|
||||
### Continuous integration
|
||||
|
||||
@@ -517,5 +487,4 @@ runs against a freshly built soulfind rather than silently skipping.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License. See the [LICENSE](./LICENSE)
|
||||
file for details.
|
||||
MIT. See [LICENSE](./LICENSE).
|
||||
|
||||
Reference in New Issue
Block a user