Implement the DIRECT connection-initiation mode so a download can target a peer the client is not already connected to: - Fix the outbound peer handshake. on_connection_established sent build_watch_user() (a server WatchUser message, wrongly framed with get_data()) for outbound connections; it now sends a proper length-prefixed PeerInit (peer code 1) with our own username. own_username is plumbed in via PeerRegistry -> PeerActor; inbound peers (outbound=false) are unchanged. - download() now auto-connects: if there is no control connection to the peer it sends GetPeerAddress and defers the QueueUpload until the outbound stream handshakes, signalled by a new ClientOperation::PeerConnected. A never- connected client still resolves the download to Failed (unit test). - Add Client::connect_peer(username) to open/pre-warm a control connection. Verified end to end against soulfind: a_file_downloads_from_a_peer_via_direct_connection logs a mock peer into the server, advertises its port via SetWaitPort, and the client resolves the address, dials it, and downloads the file — bytes match. The F-serve leg is shared with the passive-download mock.
soulseek-rs-lib
A Rust library for implementing the Soulseek peer-to-peer protocol.
About
This library provides the core functionality for interacting with the Soulseek network. It can be used to build custom Soulseek clients or bots.
Usage
Add this to your Cargo.toml:
[dependencies]
soulseek-rs-lib = "0.1.0"
Example
Simple Usage
use soulseek_rs::Client;
use std::time::Duration;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create and connect to Soulseek server
let mut client = Client::new("username", "password");
client.connect();
client.login()?;
// Search for files
let results = client.search("Alex Kassian lifestream", Duration::from_secs(10))?;
// Download first available file
if let Some(result) = results.iter().find(|r| !r.files.is_empty()) {
let file = &result.files[0];
client.download(
file.name.clone(),
file.username.clone(),
file.size,
"~/Downloads".to_string(),
)?;
println!("Downloaded: {}", file.name);
}
Ok(())
}
Advanced Configuration
use soulseek_rs::{Client, ClientSettings, PeerAddress};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create client with custom settings
let settings = ClientSettings {
server_address: PeerAddress::new("server.slsknet.org".to_string(), 2242),
enable_listen: true,
listen_port: 3000,
..ClientSettings::new("username", "password")
};
let mut client = Client::with_settings(settings);
client.connect();
client.login()?;
Ok(())
}