feat: seperate into lib and cli packages
This commit is contained in:
Generated
+7
@@ -5,3 +5,10 @@ version = 4
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "soulseek-rs"
|
name = "soulseek-rs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"soulseek-rs-lib",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "soulseek-rs-lib"
|
||||||
|
version = "0.1.0"
|
||||||
|
|||||||
+9
-4
@@ -1,5 +1,10 @@
|
|||||||
[package]
|
[workspace]
|
||||||
name = "soulseek-rs"
|
members = ["soulseek-rs-lib", "soulseek-rs"]
|
||||||
version = "0.1.0"
|
resolver = "2"
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
|
[workspace.package]
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Michel de Graaf"]
|
||||||
|
edition = "2021"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
repository = "https://github.com/yourusername/soulseek-rs"
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
## ⚠️WARNING THIS IS UNDER DEVELOPMENT AND NOT READY FOR USE ⚠️
|
## ⚠️WARNING THIS IS UNDER DEVELOPMENT AND NOT READY FOR USE ⚠️
|
||||||
|
|
||||||
Souleek-rs is an experimental Soulseek client built in Rust. It's under
|
Souleek-rs is an experimental Soulseek library and client built in Rust. It's
|
||||||
development and not yet ready for use. Soulseek is a closed-source P2P
|
under development and not yet ready for use. Soulseek is a closed-source P2P
|
||||||
file-sharing network from the 2000s. It's still used by music enthusiasts
|
file-sharing network from the 2000s. It's still used by music enthusiasts
|
||||||
around the world to share niche music.
|
around the world to share niche music.
|
||||||
|
|
||||||
@@ -17,9 +17,12 @@ about asynchronous and concurrent network programming and reverse engineering
|
|||||||
It's not intended to be a production-ready client (yet).
|
It's not intended to be a production-ready client (yet).
|
||||||
|
|
||||||
Since it's a learning project, I have a self-imposed restriction not to use
|
Since it's a learning project, I have a self-imposed restriction not to use
|
||||||
external dependencies. This means I can't use any external crates that are not
|
external dependencies in the library. This means I can't use any external
|
||||||
part of the Rust standard library. This is a good challenge to learn how to
|
crates that are not part of the Rust standard library. This is a good challenge
|
||||||
build complex systems with only the standard library.
|
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.
|
||||||
|
|
||||||
## Planned Features
|
## Planned Features
|
||||||
|
|
||||||
@@ -33,18 +36,30 @@ build complex systems with only the standard library.
|
|||||||
- [ ] Private messaging
|
- [ ] Private messaging
|
||||||
- [ ] Headless mode daemon mode with remote control
|
- [ ] Headless mode daemon mode with remote control
|
||||||
|
|
||||||
## Project structure
|
## Project Structure
|
||||||
|
|
||||||
Soulseek-rs is planned to be structured as a generic reusable library
|
This project is organized as a Cargo workspace with two crates:
|
||||||
(soulseek-rs-lib) with a CLI client (soulseek-rs).
|
|
||||||
|
|
||||||
The CLI client will have a simple command-line interface to interact with the
|
- **soulseek-rs-lib** - The core library implementing the Soulseek protocol
|
||||||
library. A rich TUI mode will be added in the future.
|
- **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 via `cargo install soulseek-rs`
|
||||||
|
- Clean separation of concerns between protocol implementation and user interface
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
To install Soulseek-rs, you'll need to have Rust installed on your system. Then,
|
### For Users
|
||||||
you can clone the repository and build it with cargo:
|
|
||||||
|
```bash
|
||||||
|
cargo install soulseek-rs
|
||||||
|
```
|
||||||
|
|
||||||
|
### For Developers
|
||||||
|
|
||||||
|
Clone and build from source:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone git@github.com:michel/soulseek-rs.git
|
git clone git@github.com:michel/soulseek-rs.git
|
||||||
@@ -52,12 +67,21 @@ cd soulseek-rs
|
|||||||
cargo build --release
|
cargo build --release
|
||||||
```
|
```
|
||||||
|
|
||||||
The binary will be available at `target/release/soulseek-rs`.
|
The binary will be available at `target/release/soulseek`.
|
||||||
|
|
||||||
|
### For Library Users
|
||||||
|
|
||||||
|
To build your own Soulseek client, add to your `Cargo.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
soulseek-rs-lib = "0.1.0"
|
||||||
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./target/release/soulseek-rs "the weeknd Blinding Lights"
|
./target/release/soulseek "the weeknd Blinding Lights"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|||||||
+122
@@ -0,0 +1,122 @@
|
|||||||
|
# Release Guide
|
||||||
|
|
||||||
|
This guide explains how to publish the `soulseek-rs-lib` and `soulseek-rs` crates to crates.io.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
1. Create an account on [crates.io](https://crates.io/)
|
||||||
|
2. Generate an API token: https://crates.io/me
|
||||||
|
3. Login to cargo: `cargo login <your-token>`
|
||||||
|
|
||||||
|
## Publishing Process
|
||||||
|
|
||||||
|
### Step 1: Publish the Library First
|
||||||
|
|
||||||
|
The client depends on the library, so the library must be published first.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd soulseek-rs-lib
|
||||||
|
cargo publish --dry-run # Test the publish
|
||||||
|
cargo publish # Actually publish
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 2: Wait for crates.io to Index
|
||||||
|
|
||||||
|
After publishing the library, wait 1-2 minutes for crates.io to index it before publishing the client.
|
||||||
|
|
||||||
|
### Step 3: Update Client Dependency (for releases)
|
||||||
|
|
||||||
|
When publishing to crates.io, the client's `Cargo.toml` should reference the published library version, not the local path.
|
||||||
|
|
||||||
|
Edit `soulseek-rs/Cargo.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
# For local development:
|
||||||
|
soulseek-rs-lib = { version = "0.1.0", path = "../soulseek-rs-lib" }
|
||||||
|
|
||||||
|
# For publishing (comment out path):
|
||||||
|
# soulseek-rs-lib = "0.1.0"
|
||||||
|
```
|
||||||
|
|
||||||
|
Or use both with optional features:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
soulseek-rs-lib = "0.1.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
soulseek-rs-lib = { version = "0.1.0", path = "../soulseek-rs-lib" }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4: Publish the Client
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ../soulseek-rs
|
||||||
|
cargo publish --dry-run # Test the publish
|
||||||
|
cargo publish # Actually publish
|
||||||
|
```
|
||||||
|
|
||||||
|
## Version Management
|
||||||
|
|
||||||
|
When bumping versions:
|
||||||
|
|
||||||
|
1. Update the version in root `Cargo.toml` under `[workspace.package]`
|
||||||
|
2. Both crates will inherit this version automatically
|
||||||
|
3. Publish library first, then client
|
||||||
|
|
||||||
|
## Automated Releases (Optional)
|
||||||
|
|
||||||
|
You can automate this with GitHub Actions. Create `.github/workflows/release.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: stable
|
||||||
|
|
||||||
|
- name: Publish library
|
||||||
|
run: |
|
||||||
|
cd soulseek-rs-lib
|
||||||
|
cargo publish --token ${{ secrets.CARGO_TOKEN }}
|
||||||
|
|
||||||
|
- name: Wait for crates.io
|
||||||
|
run: sleep 60
|
||||||
|
|
||||||
|
- name: Publish client
|
||||||
|
run: |
|
||||||
|
cd soulseek-rs
|
||||||
|
cargo publish --token ${{ secrets.CARGO_TOKEN }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Installation
|
||||||
|
|
||||||
|
After publishing, test the installation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo install soulseek-rs
|
||||||
|
soulseek --help # Should work
|
||||||
|
```
|
||||||
|
|
||||||
|
## For Developers Building Custom Clients
|
||||||
|
|
||||||
|
Developers can use the library by adding to their `Cargo.toml`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
soulseek-rs-lib = "0.1.0"
|
||||||
|
```
|
||||||
|
|
||||||
|
They'll get access to all the types and functionality exported in `lib.rs`.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
[package]
|
||||||
|
name = "soulseek-rs-lib"
|
||||||
|
version.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
description = "Library for Soulseek protocol implementation in Rust"
|
||||||
|
keywords = ["soulseek", "p2p", "protocol", "file-sharing"]
|
||||||
|
categories = ["network-programming"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "soulseek_rs"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# Add your dependencies here as needed
|
||||||
|
# For example:
|
||||||
|
# tokio = { version = "1", features = ["full"] }
|
||||||
|
# serde = { version = "1", features = ["derive"] }
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# 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`:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
soulseek-rs-lib = "0.1.0"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```rust
|
||||||
|
use soulseek_rs::{Client, PeerAddress};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
// Create and connect to Soulseek server
|
||||||
|
let mut client = Client::new(
|
||||||
|
PeerAddress::new("server.slsknet.org".to_string(), 2242),
|
||||||
|
"username".to_string(),
|
||||||
|
"password".to_string(),
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
[package]
|
||||||
|
name = "soulseek-rs"
|
||||||
|
version.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
repository.workspace = true
|
||||||
|
description = "Command-line client for Soulseek protocol"
|
||||||
|
keywords = ["soulseek", "p2p", "client", "file-sharing"]
|
||||||
|
categories = ["command-line-utilities"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "soulseek"
|
||||||
|
path = "src/main.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# Depend on your library
|
||||||
|
soulseek-rs-lib = { version = "0.1.0", path = "../soulseek-rs-lib" }
|
||||||
|
|
||||||
|
# Add client-specific dependencies here
|
||||||
|
# For example:
|
||||||
|
# clap = { version = "4", features = ["derive"] }
|
||||||
|
# anyhow = "1"
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# soulseek-rs
|
||||||
|
|
||||||
|
A command-line client for the Soulseek peer-to-peer network.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
`bash cargo install soulseek-rs `
|
||||||
|
|
||||||
|
This will install the `soulseek` binary to your system.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
The client provides a simple interface to search and download files from the
|
||||||
|
Soulseek network.
|
||||||
|
|
||||||
|
## Building from Source
|
||||||
|
|
||||||
|
````bash git clone https://github.com/yourusername/soulseek-rs cd soulseek-rs
|
||||||
|
cargo build --release ```
|
||||||
|
|
||||||
|
## Library
|
||||||
|
|
||||||
|
If you want to build your own Soulseek client, check out the
|
||||||
|
[soulseek-rs-lib](https://crates.io/crates/soulseek-rs-lib) library crate.
|
||||||
|
|
||||||
|
````
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
use soulseek_rs::{Client, PeerAddress};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
// Create and connect to Soulseek server
|
||||||
|
let mut client = Client::new(
|
||||||
|
PeerAddress::new("server.slsknet.org".to_string(), 2242),
|
||||||
|
"username".to_string(),
|
||||||
|
"password".to_string(),
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
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(())
|
||||||
|
}
|
||||||
-52
@@ -1,52 +0,0 @@
|
|||||||
fn main() {
|
|
||||||
use soulseek_rs::{Client, PeerAddress};
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
let mut client = Client::new(
|
|
||||||
PeerAddress::new(String::from("server.slsknet.org"), 2242),
|
|
||||||
String::from("xxxxxx"),
|
|
||||||
String::from("xxxxxx"),
|
|
||||||
false,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
client.connect();
|
|
||||||
match client.login() {
|
|
||||||
Ok(_) => {
|
|
||||||
match client
|
|
||||||
.search("Eternity Alex Kassian", Duration::from_secs(30))
|
|
||||||
{
|
|
||||||
Ok(results) => {
|
|
||||||
if let Some(result) =
|
|
||||||
results.iter().find(|r| !r.files.is_empty())
|
|
||||||
{
|
|
||||||
let file = result.files[0].clone();
|
|
||||||
match client.download(
|
|
||||||
file.name,
|
|
||||||
file.username,
|
|
||||||
file.size,
|
|
||||||
"~/Downloads".to_string(),
|
|
||||||
) {
|
|
||||||
Ok(download_result) => {
|
|
||||||
println!(
|
|
||||||
"Download result: {download_result:?}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Failed to download: {e}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eprint!("No results");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Failed to search: {e}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("Failed to login: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user