diff --git a/Cargo.lock b/Cargo.lock index 99635733..2df7daaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1242,7 +1242,7 @@ dependencies = [ [[package]] name = "librqbit" -version = "4.0.0-beta.3" +version = "4.0.0" dependencies = [ "anyhow", "axum 0.7.1", @@ -1277,6 +1277,7 @@ dependencies = [ "size_format", "tokio", "tokio-stream", + "tokio-test", "tower-http", "tracing", "tracing-subscriber", @@ -1310,7 +1311,7 @@ version = "2.2.1" [[package]] name = "librqbit-core" -version = "3.2.0" +version = "3.2.1" dependencies = [ "anyhow", "directories", @@ -1330,7 +1331,7 @@ dependencies = [ [[package]] name = "librqbit-dht" -version = "4.0.0-beta.3" +version = "4.0.0" dependencies = [ "anyhow", "backoff", @@ -1355,7 +1356,7 @@ dependencies = [ [[package]] name = "librqbit-peer-protocol" -version = "3.2.0" +version = "3.2.1" dependencies = [ "anyhow", "bincode", @@ -1960,14 +1961,13 @@ dependencies = [ [[package]] name = "rqbit" -version = "4.0.0-beta.3" +version = "4.0.0" dependencies = [ "anyhow", "clap", "console-subscriber", "futures", "librqbit", - "librqbit-dht", "parking_lot", "parse_duration", "regex", @@ -2461,6 +2461,19 @@ dependencies = [ "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]] name = "tokio-util" version = "0.7.10" diff --git a/README.md b/README.md index 5c018dc6..da08621c 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ Access with http://localhost:3030/web/ ## 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. -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 diff --git a/crates/dht/Cargo.toml b/crates/dht/Cargo.toml index d3c09fc4..41418d10 100644 --- a/crates/dht/Cargo.toml +++ b/crates/dht/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librqbit-dht" -version = "4.0.0-beta.3" +version = "4.0.0" edition = "2021" description = "DHT implementation, used in rqbit torrent client." license = "Apache-2.0" @@ -34,7 +34,7 @@ indexmap = "2" dashmap = {version = "5.5.3", features = ["serde"]} 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"]} [dev-dependencies] diff --git a/crates/dht/src/peer_store.rs b/crates/dht/src/peer_store.rs index 259dc232..410b5b42 100644 --- a/crates/dht/src/peer_store.rs +++ b/crates/dht/src/peer_store.rs @@ -209,6 +209,7 @@ impl PeerStore { Vec::new() } + #[allow(dead_code)] pub fn garbage_collect_peers(&self) { todo!() } diff --git a/crates/librqbit/Cargo.toml b/crates/librqbit/Cargo.toml index 76500db5..25fbe257 100644 --- a/crates/librqbit/Cargo.toml +++ b/crates/librqbit/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librqbit" -version = "4.0.0-beta.3" +version = "4.0.0" authors = ["Igor Katson "] edition = "2021" 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] bencode = {path = "../bencode", default-features=false, package="librqbit-bencode", 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"} -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"} -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"]} axum = {version = "0.7"} @@ -67,3 +67,4 @@ serde_with = "3.4.0" [dev-dependencies] futures = {version = "0.3"} tracing-subscriber = "0.3" +tokio-test = "0.4" \ No newline at end of file diff --git a/crates/librqbit/README.md b/crates/librqbit/README.md index f08c1014..45d21caa 100644 --- a/crates/librqbit/README.md +++ b/crates/librqbit/README.md @@ -1,6 +1,9 @@ # 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 -See [examples on GitHub](https://github.com/ikatson/rqbit/tree/main/crates/librqbit/examples). \ No newline at end of file +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/) \ No newline at end of file diff --git a/crates/librqbit/examples/ubuntu.rs b/crates/librqbit/examples/ubuntu.rs index f93d27c9..6c7f4143 100644 --- a/crates/librqbit/examples/ubuntu.rs +++ b/crates/librqbit/examples/ubuntu.rs @@ -6,7 +6,7 @@ use std::time::Duration; use anyhow::Context; -use librqbit::session::{AddTorrent, AddTorrentOptions, AddTorrentResponse, Session}; +use librqbit::{AddTorrent, AddTorrentOptions, AddTorrentResponse, Session}; use tracing::info; // 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"); // Create the session - let session = Session::new(output_dir.into(), Default::default()) + let session = Session::new(output_dir.into()) .await .context("error creating session")?; diff --git a/crates/librqbit/src/api.rs b/crates/librqbit/src/api.rs index ecfd394d..bdfb41bd 100644 --- a/crates/librqbit/src/api.rs +++ b/crates/librqbit/src/api.rs @@ -16,15 +16,16 @@ use crate::{ }, torrent_state::{ peer::stats::snapshot::{PeerStatsFilter, PeerStatsSnapshot}, - stats::{LiveStats, TorrentStats}, ManagedTorrentHandle, }, }; +pub use crate::torrent_state::stats::{LiveStats, TorrentStats}; + pub type Result = std::result::Result; -// Library API for use in different web frameworks. -// Contains all methods you might want to expose with (de)serializable inputs/outputs. +/// Library API for use in different web frameworks. +/// Contains all methods you might want to expose with (de)serializable inputs/outputs. pub struct Api { session: Arc, rust_log_reload_tx: Option>, diff --git a/crates/librqbit/src/dht_utils.rs b/crates/librqbit/src/dht_utils.rs index 44e8fda4..455407d1 100644 --- a/crates/librqbit/src/dht_utils.rs +++ b/crates/librqbit/src/dht_utils.rs @@ -106,7 +106,7 @@ mod tests { async fn read_metainfo_from_dht() { 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 peer_rx = dht.get_peers(info_hash).unwrap(); let peer_id = generate_peer_id(); diff --git a/crates/librqbit/src/http_api.rs b/crates/librqbit/src/http_api.rs index 5a682f72..a6c8c41a 100644 --- a/crates/librqbit/src/http_api.rs +++ b/crates/librqbit/src/http_api.rs @@ -24,7 +24,7 @@ type ApiState = Arc; use crate::api::Result; -// Public API +/// An HTTP server for the API. #[derive(Clone)] pub struct HttpApi { 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( self, addr: SocketAddr, @@ -267,11 +269,11 @@ impl HttpApi { } } -pub struct OnlyFiles(Vec); -pub struct InitialPeers(pub Vec); +pub(crate) struct OnlyFiles(Vec); +pub(crate) struct InitialPeers(pub Vec); #[derive(Serialize, Deserialize, Default)] -pub struct TorrentAddQueryParams { +pub(crate) struct TorrentAddQueryParams { pub overwrite: Option, pub output_folder: Option, pub sub_folder: Option, diff --git a/crates/librqbit/src/lib.rs b/crates/librqbit/src/lib.rs index cb7934ec..ba90652d 100644 --- a/crates/librqbit/src/lib.rs +++ b/crates/librqbit/src/lib.rs @@ -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_error; -pub mod chunk_tracker; -pub mod dht_utils; -pub mod file_ops; +mod api_error; +mod chunk_tracker; +mod dht_utils; +mod file_ops; pub mod http_api; pub mod http_api_client; -pub mod peer_connection; -pub mod peer_info_reader; -pub mod session; -pub mod spawn_utils; -pub mod torrent_state; -pub mod tracker_comms; -pub mod type_aliases; +mod peer_connection; +mod peer_info_reader; +mod session; +mod spawn_utils; +mod torrent_state; +mod tracker_comms; +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 clone_to_owned::CloneToOwned; diff --git a/crates/librqbit/src/peer_connection.rs b/crates/librqbit/src/peer_connection.rs index 6c65b88b..9c7b83a8 100644 --- a/crates/librqbit/src/peer_connection.rs +++ b/crates/librqbit/src/peer_connection.rs @@ -119,9 +119,7 @@ impl PeerConnection { options: options.unwrap_or_default(), } } - pub fn into_handler(self) -> H { - self.handler - } + pub async fn manage_peer( &self, mut outgoing_chan: tokio::sync::mpsc::UnboundedReceiver, diff --git a/crates/librqbit/src/session.rs b/crates/librqbit/src/session.rs index bcb75e2e..f72b03b1 100644 --- a/crates/librqbit/src/session.rs +++ b/crates/librqbit/src/session.rs @@ -183,24 +183,41 @@ fn compute_only_files>( Ok(only_files) } +/// Options for adding new torrents to the session. #[serde_as] #[derive(Default, Clone, Serialize, Deserialize)] pub struct AddTorrentOptions { + /// Start in paused state. #[serde(default)] pub paused: bool, + /// A regex to only download files matching it. pub only_files_regex: Option, + /// An explicit list of file IDs to download. + /// To see the file indices, run with "list_only". pub only_files: Option>, + /// 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)] pub overwrite: bool, + /// Only list the files in the torrent without starting it. #[serde(default)] 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, + /// 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, + /// Peer connection options, timeouts etc. If not set, session's defaults will be used. pub peer_opts: Option, + + /// Force a refresh interval for polling trackers. #[serde_as(as = "Option")] pub force_tracker_interval: Option, + + /// Initial peers to start of with. pub initial_peers: Option>, - // This is used to restore the session. + + /// This is used to restore the session from serialized state. #[serde(skip)] pub preferred_id: Option, } @@ -220,6 +237,16 @@ pub enum AddTorrentResponse { Added(TorrentId, ManagedTorrentHandle), } +impl AddTorrentResponse { + pub fn into_handle(self) -> Option { + 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> { let mut buf = Vec::new(); if filename == "-" { @@ -276,25 +303,36 @@ impl<'a> AddTorrent<'a> { #[derive(Default)] pub struct SessionOptions { + /// Turn on to disable DHT. 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, + /// Pass in to configure DHT persistence filename. This can be used to run multiple + /// librqbit instances at a time. + pub dht_config: Option, + + /// 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, - pub dht_config: Option, + + /// The peer ID to use. If not specified, a random one will be generated. pub peer_id: Option, + /// Configure default peer connection options. Can be overriden per torrent. pub peer_opts: Option, } impl Session { - pub async fn new( - output_folder: PathBuf, - spawner: BlockingSpawner, - ) -> anyhow::Result> { - Self::new_with_opts(output_folder, spawner, SessionOptions::default()).await + /// Create a new session. The passed in folder will be used as a default unless overriden per torrent. + pub async fn new(output_folder: PathBuf) -> anyhow::Result> { + Self::new_with_opts(output_folder, SessionOptions::default()).await } + + /// Create a new session with options. pub async fn new_with_opts( output_folder: PathBuf, - spawner: BlockingSpawner, opts: SessionOptions, ) -> anyhow::Result> { let peer_id = opts.peer_id.unwrap_or_else(generate_peer_id); @@ -316,6 +354,7 @@ impl Session { .data_dir() .join("session.json"), }; + let spawner = BlockingSpawner::default(); let session = Arc::new(Self { persistence_filename, peer_id, @@ -474,6 +513,7 @@ impl Session { Ok(()) } + /// Run a callback given the currently managed torrents. pub fn with_torrents( &self, callback: impl Fn(&mut dyn Iterator) -> R, @@ -481,6 +521,7 @@ impl Session { callback(&mut self.db.read().torrents.iter().map(|(id, t)| (*id, t))) } + /// Add a torrent to the session. pub async fn add_torrent( &self, add: AddTorrent<'_>, diff --git a/crates/librqbit/src/spawn_utils.rs b/crates/librqbit/src/spawn_utils.rs index 3cb4aff4..34d4aec5 100644 --- a/crates/librqbit/src/spawn_utils.rs +++ b/crates/librqbit/src/spawn_utils.rs @@ -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( _name: &str, span: tracing::Span, @@ -7,7 +9,7 @@ pub fn spawn( } #[derive(Clone, Copy, Debug)] -pub struct BlockingSpawner { +pub(crate) struct BlockingSpawner { allow_tokio_block_in_place: bool, } diff --git a/crates/librqbit/src/torrent_state/mod.rs b/crates/librqbit/src/torrent_state/mod.rs index f85e3f91..b6b35431 100644 --- a/crates/librqbit/src/torrent_state/mod.rs +++ b/crates/librqbit/src/torrent_state/mod.rs @@ -81,7 +81,7 @@ pub struct ManagedTorrentInfo { pub info: TorrentMetaV1Info, pub info_hash: Id20, pub out_dir: PathBuf, - pub spawner: BlockingSpawner, + pub(crate) spawner: BlockingSpawner, pub trackers: HashSet, pub peer_id: Id20, pub lengths: Lengths, @@ -120,7 +120,10 @@ impl ManagedTorrent { f(&mut self.locked.write().state) } - pub fn with_chunk_tracker(&self, f: impl FnOnce(&ChunkTracker) -> R) -> anyhow::Result { + pub(crate) fn with_chunk_tracker( + &self, + f: impl FnOnce(&ChunkTracker) -> R, + ) -> anyhow::Result { let g = self.locked.read(); match &g.state { 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> { let g = self.locked.read(); match &g.state { @@ -164,7 +168,7 @@ impl ManagedTorrent { g.state = ManagedTorrentState::Error(error) } - pub fn start( + pub(crate) fn start( self: &Arc, initial_peers: Vec, peer_rx: Option, @@ -309,6 +313,7 @@ impl ManagedTorrent { } } + /// Pause the torrent if it's live. pub fn pause(&self) -> anyhow::Result<()> { let mut g = self.locked.write(); match &g.state { @@ -330,6 +335,7 @@ impl ManagedTorrent { } } + /// Get stats. pub fn stats(&self) -> TorrentStats { let mut resp = TorrentStats { total_bytes: self.info().lengths.total_length(), diff --git a/crates/librqbit/src/tracker_comms.rs b/crates/librqbit/src/tracker_comms.rs index 208211c6..54292f04 100644 --- a/crates/librqbit/src/tracker_comms.rs +++ b/crates/librqbit/src/tracker_comms.rs @@ -13,7 +13,9 @@ use librqbit_core::id20::Id20; #[derive(Clone, Copy)] pub enum TrackerRequestEvent { Started, + #[allow(dead_code)] Stopped, + #[allow(dead_code)] Completed, } diff --git a/crates/librqbit/webui/src/main.tsx b/crates/librqbit/webui/src/main.tsx index 3fc435f8..c217307f 100644 --- a/crates/librqbit/webui/src/main.tsx +++ b/crates/librqbit/webui/src/main.tsx @@ -6,7 +6,7 @@ import { API } from "./http-api"; ReactDOM.createRoot(document.getElementById('app') as HTMLInputElement).render( - + ); diff --git a/crates/librqbit_core/Cargo.toml b/crates/librqbit_core/Cargo.toml index ce9f0bcc..9d0d85e7 100644 --- a/crates/librqbit_core/Cargo.toml +++ b/crates/librqbit_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librqbit-core" -version = "3.2.0" +version = "3.2.1" edition = "2021" description = "Important utilities used throughout librqbit useful for working with torrents." license = "Apache-2.0" diff --git a/crates/librqbit_core/src/id20.rs b/crates/librqbit_core/src/id20.rs index f5f02224..18c66d6d 100644 --- a/crates/librqbit_core/src/id20.rs +++ b/crates/librqbit_core/src/id20.rs @@ -2,6 +2,7 @@ use std::{cmp::Ordering, str::FromStr}; 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)] pub struct Id20(pub [u8; 20]); diff --git a/crates/librqbit_core/src/magnet.rs b/crates/librqbit_core/src/magnet.rs index 12e09d98..4ab8c5f5 100644 --- a/crates/librqbit_core/src/magnet.rs +++ b/crates/librqbit_core/src/magnet.rs @@ -4,12 +4,14 @@ use anyhow::Context; use crate::id20::Id20; +/// A parsed magnet link. pub struct Magnet { pub info_hash: Id20, pub trackers: Vec, } impl Magnet { + /// Parse a magnet link. pub fn parse(url: &str) -> anyhow::Result { let url = url::Url::parse(url).context("magnet link must be a valid URL")?; if url.scheme() != "magnet" { diff --git a/crates/librqbit_core/src/spawn_utils.rs b/crates/librqbit_core/src/spawn_utils.rs index 20c1bb22..ac0dd658 100644 --- a/crates/librqbit_core/src/spawn_utils.rs +++ b/crates/librqbit_core/src/spawn_utils.rs @@ -1,5 +1,6 @@ use tracing::{error, trace, Instrument}; +/// Spawns a future with tracing instrumentation. pub fn spawn( span: tracing::Span, fut: impl std::future::Future> + Send + 'static, diff --git a/crates/librqbit_core/src/speed_estimator.rs b/crates/librqbit_core/src/speed_estimator.rs index da94e21d..bfd5ed91 100644 --- a/crates/librqbit_core/src/speed_estimator.rs +++ b/crates/librqbit_core/src/speed_estimator.rs @@ -12,6 +12,7 @@ struct ProgressSnapshot { instant: Instant, } +/// Estimates download speed in a sliding time window. pub struct SpeedEstimator { latest_per_second_snapshots: Mutex>, download_bytes_per_second: AtomicU64, diff --git a/crates/librqbit_core/src/torrent_metainfo.rs b/crates/librqbit_core/src/torrent_metainfo.rs index c51ff066..883df1f7 100644 --- a/crates/librqbit_core/src/torrent_metainfo.rs +++ b/crates/librqbit_core/src/torrent_metainfo.rs @@ -12,6 +12,7 @@ use crate::id20::Id20; pub type TorrentMetaV1Borrowed<'a> = TorrentMetaV1>; pub type TorrentMetaV1Owned = TorrentMetaV1; +/// Parse torrent metainfo from bytes. pub fn torrent_from_bytes<'de, ByteBuf: Deserialize<'de>>( buf: &'de [u8], ) -> anyhow::Result> { @@ -25,6 +26,7 @@ pub fn torrent_from_bytes<'de, ByteBuf: Deserialize<'de>>( Ok(t) } +/// A parsed .torrent file. #[derive(Deserialize, Debug, Clone)] pub struct TorrentMetaV1 { pub announce: BufType, @@ -51,6 +53,7 @@ impl TorrentMetaV1 { } } +/// Main torrent information, shared by .torrent files and magnet link contents. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct TorrentMetaV1Info { #[serde(skip_serializing_if = "Option::is_none")] diff --git a/crates/peer_binary_protocol/Cargo.toml b/crates/peer_binary_protocol/Cargo.toml index e4fe6877..03dc948b 100644 --- a/crates/peer_binary_protocol/Cargo.toml +++ b/crates/peer_binary_protocol/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "librqbit-peer-protocol" -version = "3.2.0" +version = "3.2.1" edition = "2021" description = "Protocol for working with torrent peers. Used in rqbit torrent client." license = "Apache-2.0" @@ -23,6 +23,6 @@ byteorder = "1" buffers = {path="../buffers", package="librqbit-buffers", 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"} -librqbit-core = {path="../librqbit_core", version = "3.2.0"} +librqbit-core = {path="../librqbit_core", version = "3.2.1"} bitvec = "1" anyhow = "1" \ No newline at end of file diff --git a/crates/rqbit/Cargo.toml b/crates/rqbit/Cargo.toml index efbd3ac6..1a684579 100644 --- a/crates/rqbit/Cargo.toml +++ b/crates/rqbit/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rqbit" -version = "4.0.0-beta.3" +version = "4.0.0" authors = ["Igor Katson "] edition = "2021" description = "A bittorrent command line client and server." @@ -23,8 +23,7 @@ default-tls = ["librqbit/default-tls"] rust-tls = ["librqbit/rust-tls"] [dependencies] -librqbit = {path="../librqbit", default-features=false, version = "4.0.0-beta.3"} -dht = {path="../dht", package="librqbit-dht", version="4.0.0-beta.3"} +librqbit = {path="../librqbit", default-features=false, version = "4.0.0"} tokio = {version = "1", features = ["macros", "rt-multi-thread"]} console-subscriber = {version = "0.2", optional = true} anyhow = "1" diff --git a/crates/rqbit/src/main.rs b/crates/rqbit/src/main.rs index e2fa5b7a..056ccd6e 100644 --- a/crates/rqbit/src/main.rs +++ b/crates/rqbit/src/main.rs @@ -3,16 +3,9 @@ use std::{io::LineWriter, net::SocketAddr, path::PathBuf, sync::Arc, time::Durat use anyhow::Context; use clap::{Parser, ValueEnum}; use librqbit::{ - api::ApiAddTorrentResponse, - http_api::HttpApi, - http_api_client, - peer_connection::PeerConnectionOptions, - session::{ - AddTorrent, AddTorrentOptions, AddTorrentResponse, ListOnlyResponse, Session, - SessionOptions, - }, - spawn_utils::{spawn, BlockingSpawner}, - torrent_state::ManagedTorrentState, + api::ApiAddTorrentResponse, http_api::HttpApi, http_api_client, librqbit_spawn, AddTorrent, + AddTorrentOptions, AddTorrentResponse, ListOnlyResponse, ManagedTorrentState, + PeerConnectionOptions, Session, SessionOptions, }; use size_format::SizeFormatterBinary as SF; use tracing::{error, error_span, info, trace_span, warn}; @@ -230,7 +223,7 @@ fn init_logging(opts: &Opts) -> tokio::sync::mpsc::UnboundedSender { } let (reload_tx, mut reload_rx) = tokio::sync::mpsc::unbounded_channel::(); - spawn( + librqbit_spawn( "fmt_filter_reloader", error_span!("fmt_filter_reloader"), async move { @@ -278,21 +271,15 @@ fn _start_deadlock_detector_thread() { fn main() -> anyhow::Result<()> { let opts = Opts::parse(); - let (mut rt_builder, spawner) = match opts.single_thread_runtime { - true => ( - tokio::runtime::Builder::new_current_thread(), - BlockingSpawner::new(false), - ), - false => ( - { - let mut b = tokio::runtime::Builder::new_multi_thread(); - if let Some(e) = opts.worker_threads { - b.worker_threads(e); - } - b - }, - BlockingSpawner::new(true), - ), + let mut rt_builder = match opts.single_thread_runtime { + true => tokio::runtime::Builder::new_current_thread(), + false => { + let mut b = tokio::runtime::Builder::new_multi_thread(); + if let Some(e) = opts.worker_threads { + b.worker_threads(e); + } + b + } }; let rt = rt_builder @@ -306,10 +293,10 @@ fn main() -> anyhow::Result<()> { .max_blocking_threads(8) .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 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_filename = start_opts.persistence_filename.clone().map(PathBuf::from); - let session = Session::new_with_opts( - PathBuf::from(&start_opts.output_folder), - spawner, - sopts, - ) - .await - .context("error initializing rqbit session")?; - spawn( + let session = + Session::new_with_opts(PathBuf::from(&start_opts.output_folder), sopts) + .await + .context("error initializing rqbit session")?; + librqbit_spawn( "stats_printer", trace_span!("stats_printer"), stats_printer(session.clone()), @@ -464,19 +448,18 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()> .context( "output_folder is required if can't connect to an existing server", )?, - spawner, sopts, ) .await .context("error initializing rqbit session")?; - spawn( + librqbit_spawn( "stats_printer", trace_span!("stats_printer"), stats_printer(session.clone()), ); let http_api = HttpApi::new(session.clone(), Some(logging_reload_tx)); let http_api_listen_addr = opts.http_api_listen_addr; - spawn( + librqbit_spawn( "http_api", error_span!("http_api"), http_api diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index 7de6886f..31959104 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/desktop/src-tauri/Cargo.lock @@ -1856,7 +1856,7 @@ dependencies = [ [[package]] name = "librqbit" -version = "4.0.0-beta.3" +version = "4.0.0" dependencies = [ "anyhow", "axum", @@ -1920,7 +1920,7 @@ version = "2.2.1" [[package]] name = "librqbit-core" -version = "3.2.0" +version = "3.2.1" dependencies = [ "anyhow", "directories", @@ -1939,7 +1939,7 @@ dependencies = [ [[package]] name = "librqbit-dht" -version = "4.0.0-beta.3" +version = "4.0.0" dependencies = [ "anyhow", "backoff", @@ -1963,7 +1963,7 @@ dependencies = [ [[package]] name = "librqbit-peer-protocol" -version = "3.2.0" +version = "3.2.1" dependencies = [ "anyhow", "bincode", diff --git a/desktop/src-tauri/src/main.rs b/desktop/src-tauri/src/main.rs index e09aaf31..5d93a9f3 100644 --- a/desktop/src-tauri/src/main.rs +++ b/desktop/src-tauri/src/main.rs @@ -5,11 +5,10 @@ use anyhow::Context; use http::StatusCode; use librqbit::{ api::{ - Api, ApiAddTorrentResponse, EmptyJsonResponse, TorrentDetailsResponse, TorrentListResponse, + ApiAddTorrentResponse, EmptyJsonResponse, TorrentDetailsResponse, TorrentListResponse, + TorrentStats, }, - api_error::ApiError, - session::AddTorrentOptions, - torrent_state::stats::TorrentStats, + AddTorrent, AddTorrentOptions, Api, ApiError, Session, SessionOptions, }; struct State { @@ -29,7 +28,7 @@ async fn torrent_create_from_url( ) -> Result { state .api - .api_add_torrent(librqbit::session::AddTorrent::Url(url.into()), opts) + .api_add_torrent(AddTorrent::Url(url.into()), opts) .await } @@ -46,10 +45,7 @@ async fn torrent_create_from_base64_file( .map_err(|e| ApiError::new_from_anyhow(StatusCode::BAD_REQUEST, e))?; state .api - .api_add_torrent( - librqbit::session::AddTorrent::TorrentFileBytes(bytes.into()), - opts, - ) + .api_add_torrent(AddTorrent::TorrentFileBytes(bytes.into()), opts) .await } @@ -110,10 +106,9 @@ async fn start_session() { .expect("download_dir()") .to_path_buf(); - let s = librqbit::session::Session::new_with_opts( + let session = Session::new_with_opts( download_folder, - Default::default(), - librqbit::session::SessionOptions { + SessionOptions { disable_dht: false, disable_dht_persistence: false, persistence: true, @@ -123,7 +118,7 @@ async fn start_session() { .await .expect("couldn't set up librqbit session"); - let api = Api::new(s, None); + let api = Api::new(session, None); tauri::Builder::default() .manage(State { api }) diff --git a/desktop/src/main.tsx b/desktop/src/main.tsx index 6dbf2de6..d3999915 100644 --- a/desktop/src/main.tsx +++ b/desktop/src/main.tsx @@ -6,7 +6,7 @@ import { API } from "./api"; ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( - + );