Update docs, cleanup for 4.0.0 release

This commit is contained in:
Igor Katson 2023-12-03 12:14:50 +00:00
parent 3160f06f65
commit 006d83d6a7
No known key found for this signature in database
GPG key ID: B4EC22B66D61A3F5
29 changed files with 206 additions and 116 deletions

25
Cargo.lock generated
View file

@ -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"

View file

@ -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

View file

@ -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]

View file

@ -209,6 +209,7 @@ impl PeerStore {
Vec::new()
}
#[allow(dead_code)]
pub fn garbage_collect_peers(&self) {
todo!()
}

View file

@ -1,6 +1,6 @@
[package]
name = "librqbit"
version = "4.0.0-beta.3"
version = "4.0.0"
authors = ["Igor Katson <igor.katson@gmail.com>"]
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"

View file

@ -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).
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/)

View file

@ -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")?;

View file

@ -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<T> = std::result::Result<T, ApiError>;
// 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<Session>,
rust_log_reload_tx: Option<UnboundedSender<String>>,

View file

@ -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();

View file

@ -24,7 +24,7 @@ type ApiState = Arc<Api>;
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<usize>);
pub struct InitialPeers(pub Vec<SocketAddr>);
pub(crate) struct OnlyFiles(Vec<usize>);
pub(crate) struct InitialPeers(pub Vec<SocketAddr>);
#[derive(Serialize, Deserialize, Default)]
pub struct TorrentAddQueryParams {
pub(crate) struct TorrentAddQueryParams {
pub overwrite: Option<bool>,
pub output_folder: Option<String>,
pub sub_folder: Option<String>,

View file

@ -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;

View file

@ -119,9 +119,7 @@ impl<H: PeerConnectionHandler> PeerConnection<H> {
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<WriterRequest>,

View file

@ -183,24 +183,41 @@ fn compute_only_files<ByteBuf: AsRef<[u8]>>(
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<String>,
/// An explicit list of file IDs to download.
/// To see the file indices, run with "list_only".
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)]
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<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>,
/// Peer connection options, timeouts etc. If not set, session's defaults will be used.
pub peer_opts: Option<PeerConnectionOptions>,
/// Force a refresh interval for polling trackers.
#[serde_as(as = "Option<serde_with::DurationSeconds>")]
pub force_tracker_interval: Option<Duration>,
/// Initial peers to start of with.
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)]
pub preferred_id: Option<usize>,
}
@ -220,6 +237,16 @@ pub enum AddTorrentResponse {
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>> {
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,
pub persistence: bool,
pub persistence_filename: Option<PathBuf>,
/// Pass in to configure DHT persistence filename. This can be used to run multiple
/// librqbit instances at a time.
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>,
/// Configure default peer connection options. Can be overriden per torrent.
pub peer_opts: Option<PeerConnectionOptions>,
}
impl Session {
pub async fn new(
output_folder: PathBuf,
spawner: BlockingSpawner,
) -> anyhow::Result<Arc<Self>> {
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<Arc<Self>> {
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<Arc<Self>> {
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<R>(
&self,
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)))
}
/// Add a torrent to the session.
pub async fn add_torrent(
&self,
add: AddTorrent<'_>,

View file

@ -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,
}

View file

@ -81,7 +81,7 @@ pub struct ManagedTorrentInfo {
pub info: TorrentMetaV1Info<ByteString>,
pub info_hash: Id20,
pub out_dir: PathBuf,
pub spawner: BlockingSpawner,
pub(crate) spawner: BlockingSpawner,
pub trackers: HashSet<Url>,
pub peer_id: Id20,
pub lengths: Lengths,
@ -120,7 +120,10 @@ impl ManagedTorrent {
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();
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<Arc<TorrentStateLive>> {
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<Self>,
initial_peers: Vec<SocketAddr>,
peer_rx: Option<RequestPeersStream>,
@ -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(),

View file

@ -13,7 +13,9 @@ use librqbit_core::id20::Id20;
#[derive(Clone, Copy)]
pub enum TrackerRequestEvent {
Started,
#[allow(dead_code)]
Stopped,
#[allow(dead_code)]
Completed,
}

View file

@ -6,7 +6,7 @@ import { API } from "./http-api";
ReactDOM.createRoot(document.getElementById('app') as HTMLInputElement).render(
<StrictMode>
<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>
</StrictMode>
);

View file

@ -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"

View file

@ -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]);

View file

@ -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<String>,
}
impl Magnet {
/// Parse a magnet link.
pub fn parse(url: &str) -> anyhow::Result<Magnet> {
let url = url::Url::parse(url).context("magnet link must be a valid URL")?;
if url.scheme() != "magnet" {

View file

@ -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<Output = anyhow::Result<()>> + Send + 'static,

View file

@ -12,6 +12,7 @@ struct ProgressSnapshot {
instant: Instant,
}
/// Estimates download speed in a sliding time window.
pub struct SpeedEstimator {
latest_per_second_snapshots: Mutex<VecDeque<ProgressSnapshot>>,
download_bytes_per_second: AtomicU64,

View file

@ -12,6 +12,7 @@ use crate::id20::Id20;
pub type TorrentMetaV1Borrowed<'a> = TorrentMetaV1<ByteBuf<'a>>;
pub type TorrentMetaV1Owned = TorrentMetaV1<ByteString>;
/// Parse torrent metainfo from bytes.
pub fn torrent_from_bytes<'de, ByteBuf: Deserialize<'de>>(
buf: &'de [u8],
) -> anyhow::Result<TorrentMetaV1<ByteBuf>> {
@ -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<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)]
pub struct TorrentMetaV1Info<BufType> {
#[serde(skip_serializing_if = "Option::is_none")]

View file

@ -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"

View file

@ -1,6 +1,6 @@
[package]
name = "rqbit"
version = "4.0.0-beta.3"
version = "4.0.0"
authors = ["Igor Katson <igor.katson@gmail.com>"]
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"

View file

@ -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<String> {
}
let (reload_tx, mut reload_rx) = tokio::sync::mpsc::unbounded_channel::<String>();
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

View file

@ -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",

View file

@ -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<ApiAddTorrentResponse, ApiError> {
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 })

View file

@ -6,7 +6,7 @@ import { API } from "./api";
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<StrictMode>
<APIContext.Provider value={API}>
<RqbitWebUI title="Rqbit Desktop v4.0.0-beta.3" />
<RqbitWebUI title="Rqbit Desktop v4.0.0" />
</APIContext.Provider>
</StrictMode>
);