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
|
|
@ -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>>,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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<'_>,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ use librqbit_core::id20::Id20;
|
|||
#[derive(Clone, Copy)]
|
||||
pub enum TrackerRequestEvent {
|
||||
Started,
|
||||
#[allow(dead_code)]
|
||||
Stopped,
|
||||
#[allow(dead_code)]
|
||||
Completed,
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue