Update docs, cleanup for 4.0.0 release
This commit is contained in:
parent
3160f06f65
commit
006d83d6a7
29 changed files with 206 additions and 116 deletions
25
Cargo.lock
generated
25
Cargo.lock
generated
|
|
@ -1242,7 +1242,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "librqbit"
|
name = "librqbit"
|
||||||
version = "4.0.0-beta.3"
|
version = "4.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"axum 0.7.1",
|
"axum 0.7.1",
|
||||||
|
|
@ -1277,6 +1277,7 @@ dependencies = [
|
||||||
"size_format",
|
"size_format",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
|
"tokio-test",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
|
@ -1310,7 +1311,7 @@ version = "2.2.1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "librqbit-core"
|
name = "librqbit-core"
|
||||||
version = "3.2.0"
|
version = "3.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"directories",
|
"directories",
|
||||||
|
|
@ -1330,7 +1331,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "librqbit-dht"
|
name = "librqbit-dht"
|
||||||
version = "4.0.0-beta.3"
|
version = "4.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"backoff",
|
"backoff",
|
||||||
|
|
@ -1355,7 +1356,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "librqbit-peer-protocol"
|
name = "librqbit-peer-protocol"
|
||||||
version = "3.2.0"
|
version = "3.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
|
|
@ -1960,14 +1961,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rqbit"
|
name = "rqbit"
|
||||||
version = "4.0.0-beta.3"
|
version = "4.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
"console-subscriber",
|
"console-subscriber",
|
||||||
"futures",
|
"futures",
|
||||||
"librqbit",
|
"librqbit",
|
||||||
"librqbit-dht",
|
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"parse_duration",
|
"parse_duration",
|
||||||
"regex",
|
"regex",
|
||||||
|
|
@ -2461,6 +2461,19 @@ dependencies = [
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-test"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e89b3cbabd3ae862100094ae433e1def582cf86451b4e9bf83aa7ac1d8a7d719"
|
||||||
|
dependencies = [
|
||||||
|
"async-stream",
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"tokio",
|
||||||
|
"tokio-stream",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.10"
|
version = "0.7.10"
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ Access with http://localhost:3030/web/
|
||||||
## Desktop app
|
## Desktop app
|
||||||
The desktop app is a [thin wrapper](https://github.com/ikatson/rqbit/blob/main/desktop/src-tauri/src/main.rs) on top of the Web UI frontend.
|
The desktop app is a [thin wrapper](https://github.com/ikatson/rqbit/blob/main/desktop/src-tauri/src/main.rs) on top of the Web UI frontend.
|
||||||
|
|
||||||
Download it in [Releases](https://github.com/ikatson/rqbit/releases) starting from [4.0.0-beta.3](https://github.com/ikatson/rqbit/releases/tag/v4.0.0-beta.3).
|
Download it in [Releases](https://github.com/ikatson/rqbit/releases).
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "librqbit-dht"
|
name = "librqbit-dht"
|
||||||
version = "4.0.0-beta.3"
|
version = "4.0.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "DHT implementation, used in rqbit torrent client."
|
description = "DHT implementation, used in rqbit torrent client."
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
@ -34,7 +34,7 @@ indexmap = "2"
|
||||||
dashmap = {version = "5.5.3", features = ["serde"]}
|
dashmap = {version = "5.5.3", features = ["serde"]}
|
||||||
|
|
||||||
clone_to_owned = {path="../clone_to_owned", package="librqbit-clone-to-owned", version = "2.2.1"}
|
clone_to_owned = {path="../clone_to_owned", package="librqbit-clone-to-owned", version = "2.2.1"}
|
||||||
librqbit-core = {path="../librqbit_core", version = "3.2.0"}
|
librqbit-core = {path="../librqbit_core", version = "3.2.1"}
|
||||||
chrono = {version = "0.4.31", features = ["serde"]}
|
chrono = {version = "0.4.31", features = ["serde"]}
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
||||||
|
|
@ -209,6 +209,7 @@ impl PeerStore {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn garbage_collect_peers(&self) {
|
pub fn garbage_collect_peers(&self) {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "librqbit"
|
name = "librqbit"
|
||||||
version = "4.0.0-beta.3"
|
version = "4.0.0"
|
||||||
authors = ["Igor Katson <igor.katson@gmail.com>"]
|
authors = ["Igor Katson <igor.katson@gmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "The main library used by rqbit torrent client. The binary is just a small wrapper on top of it."
|
description = "The main library used by rqbit torrent client. The binary is just a small wrapper on top of it."
|
||||||
|
|
@ -24,11 +24,11 @@ rust-tls = ["reqwest/rustls-tls"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bencode = {path = "../bencode", default-features=false, package="librqbit-bencode", version="2.2.1"}
|
bencode = {path = "../bencode", default-features=false, package="librqbit-bencode", version="2.2.1"}
|
||||||
buffers = {path = "../buffers", package="librqbit-buffers", version = "2.2.1"}
|
buffers = {path = "../buffers", package="librqbit-buffers", version = "2.2.1"}
|
||||||
librqbit-core = {path = "../librqbit_core", version = "3.2.0"}
|
librqbit-core = {path = "../librqbit_core", version = "3.2.1"}
|
||||||
clone_to_owned = {path = "../clone_to_owned", package="librqbit-clone-to-owned", version = "2.2.1"}
|
clone_to_owned = {path = "../clone_to_owned", package="librqbit-clone-to-owned", version = "2.2.1"}
|
||||||
peer_binary_protocol = {path = "../peer_binary_protocol", package="librqbit-peer-protocol", version = "3.2.0"}
|
peer_binary_protocol = {path = "../peer_binary_protocol", package="librqbit-peer-protocol", version = "3.2.1"}
|
||||||
sha1w = {path = "../sha1w", default-features=false, package="librqbit-sha1-wrapper", version="2.2.1"}
|
sha1w = {path = "../sha1w", default-features=false, package="librqbit-sha1-wrapper", version="2.2.1"}
|
||||||
dht = {path = "../dht", package="librqbit-dht", version="4.0.0-beta.3"}
|
dht = {path = "../dht", package="librqbit-dht", version="4.0.0"}
|
||||||
|
|
||||||
tokio = {version = "1", features = ["macros", "rt-multi-thread"]}
|
tokio = {version = "1", features = ["macros", "rt-multi-thread"]}
|
||||||
axum = {version = "0.7"}
|
axum = {version = "0.7"}
|
||||||
|
|
@ -67,3 +67,4 @@ serde_with = "3.4.0"
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
futures = {version = "0.3"}
|
futures = {version = "0.3"}
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
|
tokio-test = "0.4"
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
# librqbit
|
# librqbit
|
||||||
|
|
||||||
A fully featured, easy to use torrent downloading library used as a backbone of [rqbit](https://github.com/ikatson/rqbit) CLI.
|
A fully featured, easy to use torrent downloading library used as a backbone of [rqbit](https://github.com/ikatson/rqbit).
|
||||||
|
|
||||||
## Basic example
|
## Basic example
|
||||||
See [examples on GitHub](https://github.com/ikatson/rqbit/tree/main/crates/librqbit/examples).
|
See [examples on GitHub](https://github.com/ikatson/rqbit/tree/main/crates/librqbit/examples).
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
[librqbit at docs.rs](https://docs.rs/librqbit/latest/librqbit/)
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use librqbit::session::{AddTorrent, AddTorrentOptions, AddTorrentResponse, Session};
|
use librqbit::{AddTorrent, AddTorrentOptions, AddTorrentResponse, Session};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
// This is ubuntu-21.04-live-server-amd64.iso.torrent
|
// This is ubuntu-21.04-live-server-amd64.iso.torrent
|
||||||
|
|
@ -27,7 +27,7 @@ async fn main() -> Result<(), anyhow::Error> {
|
||||||
.expect("the first argument should be the output directory");
|
.expect("the first argument should be the output directory");
|
||||||
|
|
||||||
// Create the session
|
// Create the session
|
||||||
let session = Session::new(output_dir.into(), Default::default())
|
let session = Session::new(output_dir.into())
|
||||||
.await
|
.await
|
||||||
.context("error creating session")?;
|
.context("error creating session")?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,16 @@ use crate::{
|
||||||
},
|
},
|
||||||
torrent_state::{
|
torrent_state::{
|
||||||
peer::stats::snapshot::{PeerStatsFilter, PeerStatsSnapshot},
|
peer::stats::snapshot::{PeerStatsFilter, PeerStatsSnapshot},
|
||||||
stats::{LiveStats, TorrentStats},
|
|
||||||
ManagedTorrentHandle,
|
ManagedTorrentHandle,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub use crate::torrent_state::stats::{LiveStats, TorrentStats};
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, ApiError>;
|
pub type Result<T> = std::result::Result<T, ApiError>;
|
||||||
|
|
||||||
// Library API for use in different web frameworks.
|
/// Library API for use in different web frameworks.
|
||||||
// Contains all methods you might want to expose with (de)serializable inputs/outputs.
|
/// Contains all methods you might want to expose with (de)serializable inputs/outputs.
|
||||||
pub struct Api {
|
pub struct Api {
|
||||||
session: Arc<Session>,
|
session: Arc<Session>,
|
||||||
rust_log_reload_tx: Option<UnboundedSender<String>>,
|
rust_log_reload_tx: Option<UnboundedSender<String>>,
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ mod tests {
|
||||||
async fn read_metainfo_from_dht() {
|
async fn read_metainfo_from_dht() {
|
||||||
init_logging();
|
init_logging();
|
||||||
|
|
||||||
let info_hash = Id20::from_str("cf3ea75e2ebbd30e0da6e6e215e2226bf35f2e33").unwrap();
|
let info_hash = Id20::from_str("cab507494d02ebb1178b38f2e9d7be299c86b862").unwrap();
|
||||||
let dht = DhtBuilder::new().await.unwrap();
|
let dht = DhtBuilder::new().await.unwrap();
|
||||||
let peer_rx = dht.get_peers(info_hash).unwrap();
|
let peer_rx = dht.get_peers(info_hash).unwrap();
|
||||||
let peer_id = generate_peer_id();
|
let peer_id = generate_peer_id();
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ type ApiState = Arc<Api>;
|
||||||
|
|
||||||
use crate::api::Result;
|
use crate::api::Result;
|
||||||
|
|
||||||
// Public API
|
/// An HTTP server for the API.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct HttpApi {
|
pub struct HttpApi {
|
||||||
inner: ApiState,
|
inner: ApiState,
|
||||||
|
|
@ -37,6 +37,8 @@ impl HttpApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Run the HTTP server forever on the given address.
|
||||||
|
/// If read_only is passed, no state-modifying methods will be exposed.
|
||||||
pub async fn make_http_api_and_run(
|
pub async fn make_http_api_and_run(
|
||||||
self,
|
self,
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
|
|
@ -267,11 +269,11 @@ impl HttpApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OnlyFiles(Vec<usize>);
|
pub(crate) struct OnlyFiles(Vec<usize>);
|
||||||
pub struct InitialPeers(pub Vec<SocketAddr>);
|
pub(crate) struct InitialPeers(pub Vec<SocketAddr>);
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default)]
|
#[derive(Serialize, Deserialize, Default)]
|
||||||
pub struct TorrentAddQueryParams {
|
pub(crate) struct TorrentAddQueryParams {
|
||||||
pub overwrite: Option<bool>,
|
pub overwrite: Option<bool>,
|
||||||
pub output_folder: Option<String>,
|
pub output_folder: Option<String>,
|
||||||
pub sub_folder: Option<String>,
|
pub sub_folder: Option<String>,
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,52 @@
|
||||||
|
//!
|
||||||
|
//! This crate provides everything necessary to download [torrents](https://en.wikipedia.org/wiki/BitTorrent).
|
||||||
|
//!
|
||||||
|
//! # Quick usage example
|
||||||
|
//!
|
||||||
|
//! ```no_run
|
||||||
|
//! use librqbit::*;
|
||||||
|
//!
|
||||||
|
//! tokio_test::block_on(async {
|
||||||
|
//! let session = Session::new("/tmp/where-to-download".into()).await.unwrap();
|
||||||
|
//! let managed_torrent_handle = session.add_torrent(
|
||||||
|
//! AddTorrent::from_url("magnet:?xt=urn:btih:cab507494d02ebb1178b38f2e9d7be299c86b862"),
|
||||||
|
//! None // options
|
||||||
|
//! ).await.unwrap().into_handle().unwrap();
|
||||||
|
//! managed_torrent_handle.wait_until_completed().await.unwrap();
|
||||||
|
//! })
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! # Overview
|
||||||
|
//! The main type to start off with is [`Session`].
|
||||||
|
//!
|
||||||
|
//! It also proved useful to use the [`Api`] when building the rqbit desktop app, as it provides
|
||||||
|
//! a facade that works with simple serializable types.
|
||||||
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
pub mod api_error;
|
mod api_error;
|
||||||
pub mod chunk_tracker;
|
mod chunk_tracker;
|
||||||
pub mod dht_utils;
|
mod dht_utils;
|
||||||
pub mod file_ops;
|
mod file_ops;
|
||||||
pub mod http_api;
|
pub mod http_api;
|
||||||
pub mod http_api_client;
|
pub mod http_api_client;
|
||||||
pub mod peer_connection;
|
mod peer_connection;
|
||||||
pub mod peer_info_reader;
|
mod peer_info_reader;
|
||||||
pub mod session;
|
mod session;
|
||||||
pub mod spawn_utils;
|
mod spawn_utils;
|
||||||
pub mod torrent_state;
|
mod torrent_state;
|
||||||
pub mod tracker_comms;
|
mod tracker_comms;
|
||||||
pub mod type_aliases;
|
mod type_aliases;
|
||||||
|
|
||||||
|
pub use api::Api;
|
||||||
|
pub use api_error::ApiError;
|
||||||
|
pub use dht;
|
||||||
|
pub use peer_connection::PeerConnectionOptions;
|
||||||
|
pub use session::{
|
||||||
|
AddTorrent, AddTorrentOptions, AddTorrentResponse, ListOnlyResponse, Session, SessionOptions,
|
||||||
|
SUPPORTED_SCHEMES,
|
||||||
|
};
|
||||||
|
pub use spawn_utils::spawn as librqbit_spawn;
|
||||||
|
pub use torrent_state::{ManagedTorrent, ManagedTorrentState};
|
||||||
|
|
||||||
pub use buffers::*;
|
pub use buffers::*;
|
||||||
pub use clone_to_owned::CloneToOwned;
|
pub use clone_to_owned::CloneToOwned;
|
||||||
|
|
|
||||||
|
|
@ -119,9 +119,7 @@ impl<H: PeerConnectionHandler> PeerConnection<H> {
|
||||||
options: options.unwrap_or_default(),
|
options: options.unwrap_or_default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn into_handler(self) -> H {
|
|
||||||
self.handler
|
|
||||||
}
|
|
||||||
pub async fn manage_peer(
|
pub async fn manage_peer(
|
||||||
&self,
|
&self,
|
||||||
mut outgoing_chan: tokio::sync::mpsc::UnboundedReceiver<WriterRequest>,
|
mut outgoing_chan: tokio::sync::mpsc::UnboundedReceiver<WriterRequest>,
|
||||||
|
|
|
||||||
|
|
@ -183,24 +183,41 @@ fn compute_only_files<ByteBuf: AsRef<[u8]>>(
|
||||||
Ok(only_files)
|
Ok(only_files)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Options for adding new torrents to the session.
|
||||||
#[serde_as]
|
#[serde_as]
|
||||||
#[derive(Default, Clone, Serialize, Deserialize)]
|
#[derive(Default, Clone, Serialize, Deserialize)]
|
||||||
pub struct AddTorrentOptions {
|
pub struct AddTorrentOptions {
|
||||||
|
/// Start in paused state.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub paused: bool,
|
pub paused: bool,
|
||||||
|
/// A regex to only download files matching it.
|
||||||
pub only_files_regex: Option<String>,
|
pub only_files_regex: Option<String>,
|
||||||
|
/// An explicit list of file IDs to download.
|
||||||
|
/// To see the file indices, run with "list_only".
|
||||||
pub only_files: Option<Vec<usize>>,
|
pub only_files: Option<Vec<usize>>,
|
||||||
|
/// Allow writing on top of existing files, including when resuming a torrent.
|
||||||
|
/// You probably want to set it, however for safety it's not default.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub overwrite: bool,
|
pub overwrite: bool,
|
||||||
|
/// Only list the files in the torrent without starting it.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub list_only: bool,
|
pub list_only: bool,
|
||||||
|
/// The output folder for the torrent. If not set, the session's default one will be used.
|
||||||
pub output_folder: Option<String>,
|
pub output_folder: Option<String>,
|
||||||
|
/// Sub-folder within session's default output folder. Will error if "output_folder" if also set.
|
||||||
|
/// By default, multi-torrent files are downloaded to a sub-folder.
|
||||||
pub sub_folder: Option<String>,
|
pub sub_folder: Option<String>,
|
||||||
|
/// Peer connection options, timeouts etc. If not set, session's defaults will be used.
|
||||||
pub peer_opts: Option<PeerConnectionOptions>,
|
pub peer_opts: Option<PeerConnectionOptions>,
|
||||||
|
|
||||||
|
/// Force a refresh interval for polling trackers.
|
||||||
#[serde_as(as = "Option<serde_with::DurationSeconds>")]
|
#[serde_as(as = "Option<serde_with::DurationSeconds>")]
|
||||||
pub force_tracker_interval: Option<Duration>,
|
pub force_tracker_interval: Option<Duration>,
|
||||||
|
|
||||||
|
/// Initial peers to start of with.
|
||||||
pub initial_peers: Option<Vec<SocketAddr>>,
|
pub initial_peers: Option<Vec<SocketAddr>>,
|
||||||
// This is used to restore the session.
|
|
||||||
|
/// This is used to restore the session from serialized state.
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub preferred_id: Option<usize>,
|
pub preferred_id: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
@ -220,6 +237,16 @@ pub enum AddTorrentResponse {
|
||||||
Added(TorrentId, ManagedTorrentHandle),
|
Added(TorrentId, ManagedTorrentHandle),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AddTorrentResponse {
|
||||||
|
pub fn into_handle(self) -> Option<ManagedTorrentHandle> {
|
||||||
|
match self {
|
||||||
|
Self::AlreadyManaged(_, handle) => Some(handle),
|
||||||
|
Self::ListOnly(_) => None,
|
||||||
|
Self::Added(_, handle) => Some(handle),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_local_file_including_stdin(filename: &str) -> anyhow::Result<Vec<u8>> {
|
pub fn read_local_file_including_stdin(filename: &str) -> anyhow::Result<Vec<u8>> {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
if filename == "-" {
|
if filename == "-" {
|
||||||
|
|
@ -276,25 +303,36 @@ impl<'a> AddTorrent<'a> {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SessionOptions {
|
pub struct SessionOptions {
|
||||||
|
/// Turn on to disable DHT.
|
||||||
pub disable_dht: bool,
|
pub disable_dht: bool,
|
||||||
|
/// Turn on to disable DHT persistence. By default it will re-use stored DHT
|
||||||
|
/// configuration, including the port it listens on.
|
||||||
pub disable_dht_persistence: bool,
|
pub disable_dht_persistence: bool,
|
||||||
pub persistence: bool,
|
/// Pass in to configure DHT persistence filename. This can be used to run multiple
|
||||||
pub persistence_filename: Option<PathBuf>,
|
/// librqbit instances at a time.
|
||||||
pub dht_config: Option<PersistentDhtConfig>,
|
pub dht_config: Option<PersistentDhtConfig>,
|
||||||
|
|
||||||
|
/// Turn on to dump session contents into a file periodically, so that on next start
|
||||||
|
/// all remembered torrents will continue where they left off.
|
||||||
|
pub persistence: bool,
|
||||||
|
/// The filename for persistence. By default uses an OS-specific folder.
|
||||||
|
pub persistence_filename: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// The peer ID to use. If not specified, a random one will be generated.
|
||||||
pub peer_id: Option<Id20>,
|
pub peer_id: Option<Id20>,
|
||||||
|
/// Configure default peer connection options. Can be overriden per torrent.
|
||||||
pub peer_opts: Option<PeerConnectionOptions>,
|
pub peer_opts: Option<PeerConnectionOptions>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Session {
|
impl Session {
|
||||||
pub async fn new(
|
/// Create a new session. The passed in folder will be used as a default unless overriden per torrent.
|
||||||
output_folder: PathBuf,
|
pub async fn new(output_folder: PathBuf) -> anyhow::Result<Arc<Self>> {
|
||||||
spawner: BlockingSpawner,
|
Self::new_with_opts(output_folder, SessionOptions::default()).await
|
||||||
) -> anyhow::Result<Arc<Self>> {
|
|
||||||
Self::new_with_opts(output_folder, spawner, SessionOptions::default()).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new session with options.
|
||||||
pub async fn new_with_opts(
|
pub async fn new_with_opts(
|
||||||
output_folder: PathBuf,
|
output_folder: PathBuf,
|
||||||
spawner: BlockingSpawner,
|
|
||||||
opts: SessionOptions,
|
opts: SessionOptions,
|
||||||
) -> anyhow::Result<Arc<Self>> {
|
) -> anyhow::Result<Arc<Self>> {
|
||||||
let peer_id = opts.peer_id.unwrap_or_else(generate_peer_id);
|
let peer_id = opts.peer_id.unwrap_or_else(generate_peer_id);
|
||||||
|
|
@ -316,6 +354,7 @@ impl Session {
|
||||||
.data_dir()
|
.data_dir()
|
||||||
.join("session.json"),
|
.join("session.json"),
|
||||||
};
|
};
|
||||||
|
let spawner = BlockingSpawner::default();
|
||||||
let session = Arc::new(Self {
|
let session = Arc::new(Self {
|
||||||
persistence_filename,
|
persistence_filename,
|
||||||
peer_id,
|
peer_id,
|
||||||
|
|
@ -474,6 +513,7 @@ impl Session {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Run a callback given the currently managed torrents.
|
||||||
pub fn with_torrents<R>(
|
pub fn with_torrents<R>(
|
||||||
&self,
|
&self,
|
||||||
callback: impl Fn(&mut dyn Iterator<Item = (TorrentId, &ManagedTorrentHandle)>) -> R,
|
callback: impl Fn(&mut dyn Iterator<Item = (TorrentId, &ManagedTorrentHandle)>) -> R,
|
||||||
|
|
@ -481,6 +521,7 @@ impl Session {
|
||||||
callback(&mut self.db.read().torrents.iter().map(|(id, t)| (*id, t)))
|
callback(&mut self.db.read().torrents.iter().map(|(id, t)| (*id, t)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a torrent to the session.
|
||||||
pub async fn add_torrent(
|
pub async fn add_torrent(
|
||||||
&self,
|
&self,
|
||||||
add: AddTorrent<'_>,
|
add: AddTorrent<'_>,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
/// Spawn a future inside a tracing span, while logging it's start,
|
||||||
|
/// finish and periodically logging if it's still alive.
|
||||||
pub fn spawn(
|
pub fn spawn(
|
||||||
_name: &str,
|
_name: &str,
|
||||||
span: tracing::Span,
|
span: tracing::Span,
|
||||||
|
|
@ -7,7 +9,7 @@ pub fn spawn(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct BlockingSpawner {
|
pub(crate) struct BlockingSpawner {
|
||||||
allow_tokio_block_in_place: bool,
|
allow_tokio_block_in_place: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ pub struct ManagedTorrentInfo {
|
||||||
pub info: TorrentMetaV1Info<ByteString>,
|
pub info: TorrentMetaV1Info<ByteString>,
|
||||||
pub info_hash: Id20,
|
pub info_hash: Id20,
|
||||||
pub out_dir: PathBuf,
|
pub out_dir: PathBuf,
|
||||||
pub spawner: BlockingSpawner,
|
pub(crate) spawner: BlockingSpawner,
|
||||||
pub trackers: HashSet<Url>,
|
pub trackers: HashSet<Url>,
|
||||||
pub peer_id: Id20,
|
pub peer_id: Id20,
|
||||||
pub lengths: Lengths,
|
pub lengths: Lengths,
|
||||||
|
|
@ -120,7 +120,10 @@ impl ManagedTorrent {
|
||||||
f(&mut self.locked.write().state)
|
f(&mut self.locked.write().state)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_chunk_tracker<R>(&self, f: impl FnOnce(&ChunkTracker) -> R) -> anyhow::Result<R> {
|
pub(crate) fn with_chunk_tracker<R>(
|
||||||
|
&self,
|
||||||
|
f: impl FnOnce(&ChunkTracker) -> R,
|
||||||
|
) -> anyhow::Result<R> {
|
||||||
let g = self.locked.read();
|
let g = self.locked.read();
|
||||||
match &g.state {
|
match &g.state {
|
||||||
ManagedTorrentState::Paused(p) => Ok(f(&p.chunk_tracker)),
|
ManagedTorrentState::Paused(p) => Ok(f(&p.chunk_tracker)),
|
||||||
|
|
@ -132,6 +135,7 @@ impl ManagedTorrent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the live state if the torrent is live.
|
||||||
pub fn live(&self) -> Option<Arc<TorrentStateLive>> {
|
pub fn live(&self) -> Option<Arc<TorrentStateLive>> {
|
||||||
let g = self.locked.read();
|
let g = self.locked.read();
|
||||||
match &g.state {
|
match &g.state {
|
||||||
|
|
@ -164,7 +168,7 @@ impl ManagedTorrent {
|
||||||
g.state = ManagedTorrentState::Error(error)
|
g.state = ManagedTorrentState::Error(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start(
|
pub(crate) fn start(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
initial_peers: Vec<SocketAddr>,
|
initial_peers: Vec<SocketAddr>,
|
||||||
peer_rx: Option<RequestPeersStream>,
|
peer_rx: Option<RequestPeersStream>,
|
||||||
|
|
@ -309,6 +313,7 @@ impl ManagedTorrent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Pause the torrent if it's live.
|
||||||
pub fn pause(&self) -> anyhow::Result<()> {
|
pub fn pause(&self) -> anyhow::Result<()> {
|
||||||
let mut g = self.locked.write();
|
let mut g = self.locked.write();
|
||||||
match &g.state {
|
match &g.state {
|
||||||
|
|
@ -330,6 +335,7 @@ impl ManagedTorrent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get stats.
|
||||||
pub fn stats(&self) -> TorrentStats {
|
pub fn stats(&self) -> TorrentStats {
|
||||||
let mut resp = TorrentStats {
|
let mut resp = TorrentStats {
|
||||||
total_bytes: self.info().lengths.total_length(),
|
total_bytes: self.info().lengths.total_length(),
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,9 @@ use librqbit_core::id20::Id20;
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum TrackerRequestEvent {
|
pub enum TrackerRequestEvent {
|
||||||
Started,
|
Started,
|
||||||
|
#[allow(dead_code)]
|
||||||
Stopped,
|
Stopped,
|
||||||
|
#[allow(dead_code)]
|
||||||
Completed,
|
Completed,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { API } from "./http-api";
|
||||||
ReactDOM.createRoot(document.getElementById('app') as HTMLInputElement).render(
|
ReactDOM.createRoot(document.getElementById('app') as HTMLInputElement).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<APIContext.Provider value={API}>
|
<APIContext.Provider value={API}>
|
||||||
<RqbitWebUI title="rqbit web UI - version 4.0.0-beta.3" />
|
<RqbitWebUI title="rqbit web UI - version 4.0.0" />
|
||||||
</APIContext.Provider>
|
</APIContext.Provider>
|
||||||
</StrictMode>
|
</StrictMode>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "librqbit-core"
|
name = "librqbit-core"
|
||||||
version = "3.2.0"
|
version = "3.2.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Important utilities used throughout librqbit useful for working with torrents."
|
description = "Important utilities used throughout librqbit useful for working with torrents."
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use std::{cmp::Ordering, str::FromStr};
|
||||||
|
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
|
|
||||||
|
/// A 20-byte hash used throughout librqbit, for torrent info hashes, peer ids etc.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||||
pub struct Id20(pub [u8; 20]);
|
pub struct Id20(pub [u8; 20]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,14 @@ use anyhow::Context;
|
||||||
|
|
||||||
use crate::id20::Id20;
|
use crate::id20::Id20;
|
||||||
|
|
||||||
|
/// A parsed magnet link.
|
||||||
pub struct Magnet {
|
pub struct Magnet {
|
||||||
pub info_hash: Id20,
|
pub info_hash: Id20,
|
||||||
pub trackers: Vec<String>,
|
pub trackers: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Magnet {
|
impl Magnet {
|
||||||
|
/// Parse a magnet link.
|
||||||
pub fn parse(url: &str) -> anyhow::Result<Magnet> {
|
pub fn parse(url: &str) -> anyhow::Result<Magnet> {
|
||||||
let url = url::Url::parse(url).context("magnet link must be a valid URL")?;
|
let url = url::Url::parse(url).context("magnet link must be a valid URL")?;
|
||||||
if url.scheme() != "magnet" {
|
if url.scheme() != "magnet" {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use tracing::{error, trace, Instrument};
|
use tracing::{error, trace, Instrument};
|
||||||
|
|
||||||
|
/// Spawns a future with tracing instrumentation.
|
||||||
pub fn spawn(
|
pub fn spawn(
|
||||||
span: tracing::Span,
|
span: tracing::Span,
|
||||||
fut: impl std::future::Future<Output = anyhow::Result<()>> + Send + 'static,
|
fut: impl std::future::Future<Output = anyhow::Result<()>> + Send + 'static,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ struct ProgressSnapshot {
|
||||||
instant: Instant,
|
instant: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Estimates download speed in a sliding time window.
|
||||||
pub struct SpeedEstimator {
|
pub struct SpeedEstimator {
|
||||||
latest_per_second_snapshots: Mutex<VecDeque<ProgressSnapshot>>,
|
latest_per_second_snapshots: Mutex<VecDeque<ProgressSnapshot>>,
|
||||||
download_bytes_per_second: AtomicU64,
|
download_bytes_per_second: AtomicU64,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ use crate::id20::Id20;
|
||||||
pub type TorrentMetaV1Borrowed<'a> = TorrentMetaV1<ByteBuf<'a>>;
|
pub type TorrentMetaV1Borrowed<'a> = TorrentMetaV1<ByteBuf<'a>>;
|
||||||
pub type TorrentMetaV1Owned = TorrentMetaV1<ByteString>;
|
pub type TorrentMetaV1Owned = TorrentMetaV1<ByteString>;
|
||||||
|
|
||||||
|
/// Parse torrent metainfo from bytes.
|
||||||
pub fn torrent_from_bytes<'de, ByteBuf: Deserialize<'de>>(
|
pub fn torrent_from_bytes<'de, ByteBuf: Deserialize<'de>>(
|
||||||
buf: &'de [u8],
|
buf: &'de [u8],
|
||||||
) -> anyhow::Result<TorrentMetaV1<ByteBuf>> {
|
) -> anyhow::Result<TorrentMetaV1<ByteBuf>> {
|
||||||
|
|
@ -25,6 +26,7 @@ pub fn torrent_from_bytes<'de, ByteBuf: Deserialize<'de>>(
|
||||||
Ok(t)
|
Ok(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A parsed .torrent file.
|
||||||
#[derive(Deserialize, Debug, Clone)]
|
#[derive(Deserialize, Debug, Clone)]
|
||||||
pub struct TorrentMetaV1<BufType> {
|
pub struct TorrentMetaV1<BufType> {
|
||||||
pub announce: BufType,
|
pub announce: BufType,
|
||||||
|
|
@ -51,6 +53,7 @@ impl<BufType> TorrentMetaV1<BufType> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Main torrent information, shared by .torrent files and magnet link contents.
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct TorrentMetaV1Info<BufType> {
|
pub struct TorrentMetaV1Info<BufType> {
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "librqbit-peer-protocol"
|
name = "librqbit-peer-protocol"
|
||||||
version = "3.2.0"
|
version = "3.2.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Protocol for working with torrent peers. Used in rqbit torrent client."
|
description = "Protocol for working with torrent peers. Used in rqbit torrent client."
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
|
|
@ -23,6 +23,6 @@ byteorder = "1"
|
||||||
buffers = {path="../buffers", package="librqbit-buffers", version = "2.2.1"}
|
buffers = {path="../buffers", package="librqbit-buffers", version = "2.2.1"}
|
||||||
bencode = {path = "../bencode", default-features=false, package="librqbit-bencode", version="2.2.1"}
|
bencode = {path = "../bencode", default-features=false, package="librqbit-bencode", version="2.2.1"}
|
||||||
clone_to_owned = {path="../clone_to_owned", package="librqbit-clone-to-owned", version = "2.2.1"}
|
clone_to_owned = {path="../clone_to_owned", package="librqbit-clone-to-owned", version = "2.2.1"}
|
||||||
librqbit-core = {path="../librqbit_core", version = "3.2.0"}
|
librqbit-core = {path="../librqbit_core", version = "3.2.1"}
|
||||||
bitvec = "1"
|
bitvec = "1"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "rqbit"
|
name = "rqbit"
|
||||||
version = "4.0.0-beta.3"
|
version = "4.0.0"
|
||||||
authors = ["Igor Katson <igor.katson@gmail.com>"]
|
authors = ["Igor Katson <igor.katson@gmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "A bittorrent command line client and server."
|
description = "A bittorrent command line client and server."
|
||||||
|
|
@ -23,8 +23,7 @@ default-tls = ["librqbit/default-tls"]
|
||||||
rust-tls = ["librqbit/rust-tls"]
|
rust-tls = ["librqbit/rust-tls"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
librqbit = {path="../librqbit", default-features=false, version = "4.0.0-beta.3"}
|
librqbit = {path="../librqbit", default-features=false, version = "4.0.0"}
|
||||||
dht = {path="../dht", package="librqbit-dht", version="4.0.0-beta.3"}
|
|
||||||
tokio = {version = "1", features = ["macros", "rt-multi-thread"]}
|
tokio = {version = "1", features = ["macros", "rt-multi-thread"]}
|
||||||
console-subscriber = {version = "0.2", optional = true}
|
console-subscriber = {version = "0.2", optional = true}
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,9 @@ use std::{io::LineWriter, net::SocketAddr, path::PathBuf, sync::Arc, time::Durat
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::{Parser, ValueEnum};
|
use clap::{Parser, ValueEnum};
|
||||||
use librqbit::{
|
use librqbit::{
|
||||||
api::ApiAddTorrentResponse,
|
api::ApiAddTorrentResponse, http_api::HttpApi, http_api_client, librqbit_spawn, AddTorrent,
|
||||||
http_api::HttpApi,
|
AddTorrentOptions, AddTorrentResponse, ListOnlyResponse, ManagedTorrentState,
|
||||||
http_api_client,
|
PeerConnectionOptions, Session, SessionOptions,
|
||||||
peer_connection::PeerConnectionOptions,
|
|
||||||
session::{
|
|
||||||
AddTorrent, AddTorrentOptions, AddTorrentResponse, ListOnlyResponse, Session,
|
|
||||||
SessionOptions,
|
|
||||||
},
|
|
||||||
spawn_utils::{spawn, BlockingSpawner},
|
|
||||||
torrent_state::ManagedTorrentState,
|
|
||||||
};
|
};
|
||||||
use size_format::SizeFormatterBinary as SF;
|
use size_format::SizeFormatterBinary as SF;
|
||||||
use tracing::{error, error_span, info, trace_span, warn};
|
use tracing::{error, error_span, info, trace_span, warn};
|
||||||
|
|
@ -230,7 +223,7 @@ fn init_logging(opts: &Opts) -> tokio::sync::mpsc::UnboundedSender<String> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let (reload_tx, mut reload_rx) = tokio::sync::mpsc::unbounded_channel::<String>();
|
let (reload_tx, mut reload_rx) = tokio::sync::mpsc::unbounded_channel::<String>();
|
||||||
spawn(
|
librqbit_spawn(
|
||||||
"fmt_filter_reloader",
|
"fmt_filter_reloader",
|
||||||
error_span!("fmt_filter_reloader"),
|
error_span!("fmt_filter_reloader"),
|
||||||
async move {
|
async move {
|
||||||
|
|
@ -278,21 +271,15 @@ fn _start_deadlock_detector_thread() {
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
let opts = Opts::parse();
|
let opts = Opts::parse();
|
||||||
|
|
||||||
let (mut rt_builder, spawner) = match opts.single_thread_runtime {
|
let mut rt_builder = match opts.single_thread_runtime {
|
||||||
true => (
|
true => tokio::runtime::Builder::new_current_thread(),
|
||||||
tokio::runtime::Builder::new_current_thread(),
|
false => {
|
||||||
BlockingSpawner::new(false),
|
let mut b = tokio::runtime::Builder::new_multi_thread();
|
||||||
),
|
if let Some(e) = opts.worker_threads {
|
||||||
false => (
|
b.worker_threads(e);
|
||||||
{
|
}
|
||||||
let mut b = tokio::runtime::Builder::new_multi_thread();
|
b
|
||||||
if let Some(e) = opts.worker_threads {
|
}
|
||||||
b.worker_threads(e);
|
|
||||||
}
|
|
||||||
b
|
|
||||||
},
|
|
||||||
BlockingSpawner::new(true),
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let rt = rt_builder
|
let rt = rt_builder
|
||||||
|
|
@ -306,10 +293,10 @@ fn main() -> anyhow::Result<()> {
|
||||||
.max_blocking_threads(8)
|
.max_blocking_threads(8)
|
||||||
.build()?;
|
.build()?;
|
||||||
|
|
||||||
rt.block_on(async_main(opts, spawner))
|
rt.block_on(async_main(opts))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()> {
|
async fn async_main(opts: Opts) -> anyhow::Result<()> {
|
||||||
let logging_reload_tx = init_logging(&opts);
|
let logging_reload_tx = init_logging(&opts);
|
||||||
|
|
||||||
let mut sopts = SessionOptions {
|
let mut sopts = SessionOptions {
|
||||||
|
|
@ -384,14 +371,11 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()>
|
||||||
sopts.persistence = !start_opts.disable_persistence;
|
sopts.persistence = !start_opts.disable_persistence;
|
||||||
sopts.persistence_filename =
|
sopts.persistence_filename =
|
||||||
start_opts.persistence_filename.clone().map(PathBuf::from);
|
start_opts.persistence_filename.clone().map(PathBuf::from);
|
||||||
let session = Session::new_with_opts(
|
let session =
|
||||||
PathBuf::from(&start_opts.output_folder),
|
Session::new_with_opts(PathBuf::from(&start_opts.output_folder), sopts)
|
||||||
spawner,
|
.await
|
||||||
sopts,
|
.context("error initializing rqbit session")?;
|
||||||
)
|
librqbit_spawn(
|
||||||
.await
|
|
||||||
.context("error initializing rqbit session")?;
|
|
||||||
spawn(
|
|
||||||
"stats_printer",
|
"stats_printer",
|
||||||
trace_span!("stats_printer"),
|
trace_span!("stats_printer"),
|
||||||
stats_printer(session.clone()),
|
stats_printer(session.clone()),
|
||||||
|
|
@ -464,19 +448,18 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()>
|
||||||
.context(
|
.context(
|
||||||
"output_folder is required if can't connect to an existing server",
|
"output_folder is required if can't connect to an existing server",
|
||||||
)?,
|
)?,
|
||||||
spawner,
|
|
||||||
sopts,
|
sopts,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.context("error initializing rqbit session")?;
|
.context("error initializing rqbit session")?;
|
||||||
spawn(
|
librqbit_spawn(
|
||||||
"stats_printer",
|
"stats_printer",
|
||||||
trace_span!("stats_printer"),
|
trace_span!("stats_printer"),
|
||||||
stats_printer(session.clone()),
|
stats_printer(session.clone()),
|
||||||
);
|
);
|
||||||
let http_api = HttpApi::new(session.clone(), Some(logging_reload_tx));
|
let http_api = HttpApi::new(session.clone(), Some(logging_reload_tx));
|
||||||
let http_api_listen_addr = opts.http_api_listen_addr;
|
let http_api_listen_addr = opts.http_api_listen_addr;
|
||||||
spawn(
|
librqbit_spawn(
|
||||||
"http_api",
|
"http_api",
|
||||||
error_span!("http_api"),
|
error_span!("http_api"),
|
||||||
http_api
|
http_api
|
||||||
|
|
|
||||||
8
desktop/src-tauri/Cargo.lock
generated
8
desktop/src-tauri/Cargo.lock
generated
|
|
@ -1856,7 +1856,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "librqbit"
|
name = "librqbit"
|
||||||
version = "4.0.0-beta.3"
|
version = "4.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"axum",
|
"axum",
|
||||||
|
|
@ -1920,7 +1920,7 @@ version = "2.2.1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "librqbit-core"
|
name = "librqbit-core"
|
||||||
version = "3.2.0"
|
version = "3.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"directories",
|
"directories",
|
||||||
|
|
@ -1939,7 +1939,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "librqbit-dht"
|
name = "librqbit-dht"
|
||||||
version = "4.0.0-beta.3"
|
version = "4.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"backoff",
|
"backoff",
|
||||||
|
|
@ -1963,7 +1963,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "librqbit-peer-protocol"
|
name = "librqbit-peer-protocol"
|
||||||
version = "3.2.0"
|
version = "3.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bincode",
|
"bincode",
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,10 @@ use anyhow::Context;
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
use librqbit::{
|
use librqbit::{
|
||||||
api::{
|
api::{
|
||||||
Api, ApiAddTorrentResponse, EmptyJsonResponse, TorrentDetailsResponse, TorrentListResponse,
|
ApiAddTorrentResponse, EmptyJsonResponse, TorrentDetailsResponse, TorrentListResponse,
|
||||||
|
TorrentStats,
|
||||||
},
|
},
|
||||||
api_error::ApiError,
|
AddTorrent, AddTorrentOptions, Api, ApiError, Session, SessionOptions,
|
||||||
session::AddTorrentOptions,
|
|
||||||
torrent_state::stats::TorrentStats,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
|
|
@ -29,7 +28,7 @@ async fn torrent_create_from_url(
|
||||||
) -> Result<ApiAddTorrentResponse, ApiError> {
|
) -> Result<ApiAddTorrentResponse, ApiError> {
|
||||||
state
|
state
|
||||||
.api
|
.api
|
||||||
.api_add_torrent(librqbit::session::AddTorrent::Url(url.into()), opts)
|
.api_add_torrent(AddTorrent::Url(url.into()), opts)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,10 +45,7 @@ async fn torrent_create_from_base64_file(
|
||||||
.map_err(|e| ApiError::new_from_anyhow(StatusCode::BAD_REQUEST, e))?;
|
.map_err(|e| ApiError::new_from_anyhow(StatusCode::BAD_REQUEST, e))?;
|
||||||
state
|
state
|
||||||
.api
|
.api
|
||||||
.api_add_torrent(
|
.api_add_torrent(AddTorrent::TorrentFileBytes(bytes.into()), opts)
|
||||||
librqbit::session::AddTorrent::TorrentFileBytes(bytes.into()),
|
|
||||||
opts,
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,10 +106,9 @@ async fn start_session() {
|
||||||
.expect("download_dir()")
|
.expect("download_dir()")
|
||||||
.to_path_buf();
|
.to_path_buf();
|
||||||
|
|
||||||
let s = librqbit::session::Session::new_with_opts(
|
let session = Session::new_with_opts(
|
||||||
download_folder,
|
download_folder,
|
||||||
Default::default(),
|
SessionOptions {
|
||||||
librqbit::session::SessionOptions {
|
|
||||||
disable_dht: false,
|
disable_dht: false,
|
||||||
disable_dht_persistence: false,
|
disable_dht_persistence: false,
|
||||||
persistence: true,
|
persistence: true,
|
||||||
|
|
@ -123,7 +118,7 @@ async fn start_session() {
|
||||||
.await
|
.await
|
||||||
.expect("couldn't set up librqbit session");
|
.expect("couldn't set up librqbit session");
|
||||||
|
|
||||||
let api = Api::new(s, None);
|
let api = Api::new(session, None);
|
||||||
|
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.manage(State { api })
|
.manage(State { api })
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { API } from "./api";
|
||||||
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<APIContext.Provider value={API}>
|
<APIContext.Provider value={API}>
|
||||||
<RqbitWebUI title="Rqbit Desktop v4.0.0-beta.3" />
|
<RqbitWebUI title="Rqbit Desktop v4.0.0" />
|
||||||
</APIContext.Provider>
|
</APIContext.Provider>
|
||||||
</StrictMode>
|
</StrictMode>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue