Add storage example
This commit is contained in:
parent
3e37b4698f
commit
6c3dfbc52f
7 changed files with 103 additions and 36 deletions
70
crates/librqbit/examples/storage.rs
Normal file
70
crates/librqbit/examples/storage.rs
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
use librqbit::{
|
||||||
|
storage::{StorageFactory, TorrentStorage},
|
||||||
|
ManagedTorrentInfo, SessionOptions,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DummyStorage {}
|
||||||
|
|
||||||
|
impl StorageFactory for DummyStorage {
|
||||||
|
fn init_storage(
|
||||||
|
&self,
|
||||||
|
info: &ManagedTorrentInfo,
|
||||||
|
) -> anyhow::Result<Box<dyn librqbit::storage::TorrentStorage>> {
|
||||||
|
Ok(Box::new(DummyStorage {}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TorrentStorage for DummyStorage {
|
||||||
|
fn pread_exact(&self, file_id: usize, offset: u64, buf: &mut [u8]) -> anyhow::Result<()> {
|
||||||
|
anyhow::bail!("pread_exact")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pwrite_all(&self, file_id: usize, offset: u64, buf: &[u8]) -> anyhow::Result<()> {
|
||||||
|
anyhow::bail!("pwrite_all")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_file(&self, file_id: usize, filename: &std::path::Path) -> anyhow::Result<()> {
|
||||||
|
anyhow::bail!("remove_file")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ensure_file_length(&self, file_id: usize, length: u64) -> anyhow::Result<()> {
|
||||||
|
anyhow::bail!("ensure_file_length")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take(&self) -> anyhow::Result<Box<dyn TorrentStorage>> {
|
||||||
|
Ok(Box::new(Self {}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
// Output logs to console.
|
||||||
|
match std::env::var("RUST_LOG") {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(_) => std::env::set_var("RUST_LOG", "info"),
|
||||||
|
}
|
||||||
|
tracing_subscriber::fmt::init();
|
||||||
|
let s = librqbit::Session::new_with_opts(
|
||||||
|
"/does-not-matter".into(),
|
||||||
|
SessionOptions {
|
||||||
|
disable_dht: true,
|
||||||
|
persistence: false,
|
||||||
|
listen_port_range: None,
|
||||||
|
enable_upnp_port_forwarding: false,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let handle = s
|
||||||
|
.add_torrent(
|
||||||
|
librqbit::AddTorrent::TorrentFileBytes(
|
||||||
|
include_bytes!("../resources/ubuntu-21.04-live-server-amd64.iso.torrent").into(),
|
||||||
|
),
|
||||||
|
Some(librqbit::AddTorrentOptions {
|
||||||
|
storage_factory: Some(Box::new(DummyStorage {})),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
@ -171,23 +171,6 @@ impl ChunkTracker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_empty(lengths: Lengths, file_infos: &FileInfos) -> anyhow::Result<Self> {
|
|
||||||
let have = BF::from_boxed_slice(vec![0; lengths.piece_bitfield_bytes()].into_boxed_slice());
|
|
||||||
let selected = have.clone();
|
|
||||||
let chunk_status =
|
|
||||||
BF::from_boxed_slice(vec![0; lengths.chunk_bitfield_bytes()].into_boxed_slice());
|
|
||||||
let queued = have.clone();
|
|
||||||
Ok(Self {
|
|
||||||
queue_pieces: queued,
|
|
||||||
chunk_status,
|
|
||||||
have,
|
|
||||||
selected,
|
|
||||||
lengths,
|
|
||||||
per_file_bytes: vec![0; file_infos.len()],
|
|
||||||
hns: Default::default(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_lengths(&self) -> &Lengths {
|
pub fn get_lengths(&self) -> &Lengths {
|
||||||
&self.lengths
|
&self.lengths
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ mod peer_info_reader;
|
||||||
mod read_buf;
|
mod read_buf;
|
||||||
mod session;
|
mod session;
|
||||||
mod spawn_utils;
|
mod spawn_utils;
|
||||||
mod storage;
|
pub mod storage;
|
||||||
mod torrent_state;
|
mod torrent_state;
|
||||||
pub mod tracing_subscriber_config_utils;
|
pub mod tracing_subscriber_config_utils;
|
||||||
mod type_aliases;
|
mod type_aliases;
|
||||||
|
|
@ -56,7 +56,9 @@ pub use session::{
|
||||||
SUPPORTED_SCHEMES,
|
SUPPORTED_SCHEMES,
|
||||||
};
|
};
|
||||||
pub use spawn_utils::spawn as librqbit_spawn;
|
pub use spawn_utils::spawn as librqbit_spawn;
|
||||||
pub use torrent_state::{ManagedTorrent, ManagedTorrentState, TorrentStats, TorrentStatsState};
|
pub use torrent_state::{
|
||||||
|
ManagedTorrent, ManagedTorrentInfo, ManagedTorrentState, TorrentStats, TorrentStatsState,
|
||||||
|
};
|
||||||
|
|
||||||
pub use buffers::*;
|
pub use buffers::*;
|
||||||
pub use clone_to_owned::CloneToOwned;
|
pub use clone_to_owned::CloneToOwned;
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ use crate::{
|
||||||
peer_connection::PeerConnectionOptions,
|
peer_connection::PeerConnectionOptions,
|
||||||
read_buf::ReadBuf,
|
read_buf::ReadBuf,
|
||||||
spawn_utils::BlockingSpawner,
|
spawn_utils::BlockingSpawner,
|
||||||
|
storage::StorageFactory,
|
||||||
torrent_state::{
|
torrent_state::{
|
||||||
ManagedTorrentBuilder, ManagedTorrentHandle, ManagedTorrentState, TorrentStateLive,
|
ManagedTorrentBuilder, ManagedTorrentHandle, ManagedTorrentState, TorrentStateLive,
|
||||||
},
|
},
|
||||||
|
|
@ -43,7 +44,6 @@ use librqbit_core::{
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use peer_binary_protocol::Handshake;
|
use peer_binary_protocol::Handshake;
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use serde_with::serde_as;
|
|
||||||
use tokio::net::{TcpListener, TcpStream};
|
use tokio::net::{TcpListener, TcpStream};
|
||||||
use tokio_stream::StreamExt;
|
use tokio_stream::StreamExt;
|
||||||
use tokio_util::sync::{CancellationToken, DropGuard};
|
use tokio_util::sync::{CancellationToken, DropGuard};
|
||||||
|
|
@ -270,9 +270,7 @@ fn merge_two_optional_streams<T>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Options for adding new torrents to the session.
|
/// Options for adding new torrents to the session.
|
||||||
#[serde_as]
|
#[derive(Default)]
|
||||||
#[derive(Default, Clone, Serialize, Deserialize)]
|
|
||||||
#[serde(default)]
|
|
||||||
pub struct AddTorrentOptions {
|
pub struct AddTorrentOptions {
|
||||||
/// Start in paused state.
|
/// Start in paused state.
|
||||||
pub paused: bool,
|
pub paused: bool,
|
||||||
|
|
@ -295,7 +293,6 @@ pub struct AddTorrentOptions {
|
||||||
pub peer_opts: Option<PeerConnectionOptions>,
|
pub peer_opts: Option<PeerConnectionOptions>,
|
||||||
|
|
||||||
/// Force a refresh interval for polling trackers.
|
/// Force a refresh interval for polling trackers.
|
||||||
#[serde_as(as = "Option<serde_with::DurationSeconds>")]
|
|
||||||
pub force_tracker_interval: Option<Duration>,
|
pub force_tracker_interval: Option<Duration>,
|
||||||
|
|
||||||
pub disable_trackers: bool,
|
pub disable_trackers: bool,
|
||||||
|
|
@ -304,8 +301,9 @@ pub struct AddTorrentOptions {
|
||||||
pub initial_peers: Option<Vec<SocketAddr>>,
|
pub initial_peers: Option<Vec<SocketAddr>>,
|
||||||
|
|
||||||
/// This is used to restore the session from serialized state.
|
/// This is used to restore the session from serialized state.
|
||||||
#[serde(skip)]
|
|
||||||
pub preferred_id: Option<usize>,
|
pub preferred_id: Option<usize>,
|
||||||
|
|
||||||
|
pub storage_factory: Option<Box<dyn StorageFactory>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ListOnlyResponse {
|
pub struct ListOnlyResponse {
|
||||||
|
|
@ -976,7 +974,7 @@ impl Session {
|
||||||
trackers: Vec<String>,
|
trackers: Vec<String>,
|
||||||
peer_rx: Option<PeerStream>,
|
peer_rx: Option<PeerStream>,
|
||||||
initial_peers: Vec<SocketAddr>,
|
initial_peers: Vec<SocketAddr>,
|
||||||
opts: AddTorrentOptions,
|
mut opts: AddTorrentOptions,
|
||||||
) -> anyhow::Result<AddTorrentResponse> {
|
) -> anyhow::Result<AddTorrentResponse> {
|
||||||
debug!("Torrent info: {:#?}", &info);
|
debug!("Torrent info: {:#?}", &info);
|
||||||
|
|
||||||
|
|
@ -1031,6 +1029,10 @@ impl Session {
|
||||||
builder.peer_read_write_timeout(t);
|
builder.peer_read_write_timeout(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(storage_factory) = opts.storage_factory.take() {
|
||||||
|
builder.storage_factory(storage_factory);
|
||||||
|
}
|
||||||
|
|
||||||
let (managed_torrent, id) = {
|
let (managed_torrent, id) = {
|
||||||
let mut g = self.db.write();
|
let mut g = self.db.write();
|
||||||
if let Some((id, handle)) = g.torrents.iter().find(|(_, t)| t.info_hash() == info_hash)
|
if let Some((id, handle)) = g.torrents.iter().find(|(_, t)| t.info_hash() == info_hash)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use parking_lot::RwLock;
|
||||||
|
|
||||||
use crate::type_aliases::FileInfos;
|
use crate::type_aliases::FileInfos;
|
||||||
|
|
||||||
use super::TorrentStorage;
|
use super::{StorageFactory, TorrentStorage};
|
||||||
|
|
||||||
struct InMemoryPiece {
|
struct InMemoryPiece {
|
||||||
bytes: Box<[u8]>,
|
bytes: Box<[u8]>,
|
||||||
|
|
@ -19,7 +19,21 @@ impl InMemoryPiece {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InMemoryExampleStorage {
|
pub struct InMemoryExampleStorageFactory {}
|
||||||
|
|
||||||
|
impl StorageFactory for InMemoryExampleStorageFactory {
|
||||||
|
fn init_storage(
|
||||||
|
&self,
|
||||||
|
info: &crate::torrent_state::ManagedTorrentInfo,
|
||||||
|
) -> anyhow::Result<Box<dyn TorrentStorage>> {
|
||||||
|
Ok(Box::new(InMemoryExampleStorage::new(
|
||||||
|
info.lengths,
|
||||||
|
info.file_infos.clone(),
|
||||||
|
)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InMemoryExampleStorage {
|
||||||
lengths: Lengths,
|
lengths: Lengths,
|
||||||
file_infos: FileInfos,
|
file_infos: FileInfos,
|
||||||
map: RwLock<HashMap<ValidPieceIndex, InMemoryPiece>>,
|
map: RwLock<HashMap<ValidPieceIndex, InMemoryPiece>>,
|
||||||
|
|
@ -28,7 +42,7 @@ pub struct InMemoryExampleStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InMemoryExampleStorage {
|
impl InMemoryExampleStorage {
|
||||||
pub fn new(lengths: Lengths, file_infos: FileInfos) -> anyhow::Result<Self> {
|
fn new(lengths: Lengths, file_infos: FileInfos) -> anyhow::Result<Self> {
|
||||||
// Max memory 128MiB. Make it tunable
|
// Max memory 128MiB. Make it tunable
|
||||||
let max_pieces = 128 * 1024 * 1024 / lengths.default_piece_length();
|
let max_pieces = 128 * 1024 * 1024 / lengths.default_piece_length();
|
||||||
if max_pieces == 0 {
|
if max_pieces == 0 {
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,6 @@ use crate::{
|
||||||
PeerConnection, PeerConnectionHandler, PeerConnectionOptions, WriterRequest,
|
PeerConnection, PeerConnectionHandler, PeerConnectionOptions, WriterRequest,
|
||||||
},
|
},
|
||||||
session::CheckedIncomingConnection,
|
session::CheckedIncomingConnection,
|
||||||
storage::TorrentStorage,
|
|
||||||
torrent_state::{peer::Peer, utils::atomic_inc},
|
torrent_state::{peer::Peer, utils::atomic_inc},
|
||||||
type_aliases::{FilePriorities, FileStorage, PeerHandle, BF},
|
type_aliases::{FilePriorities, FileStorage, PeerHandle, BF},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -367,7 +367,7 @@ async fn async_main(opts: Opts) -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
let http_api_url = format!("http://{}", opts.http_api_listen_addr);
|
let http_api_url = format!("http://{}", opts.http_api_listen_addr);
|
||||||
let client = http_api_client::HttpApiClient::new(&http_api_url)?;
|
let client = http_api_client::HttpApiClient::new(&http_api_url)?;
|
||||||
let torrent_opts = AddTorrentOptions {
|
let torrent_opts = || AddTorrentOptions {
|
||||||
only_files_regex: download_opts.only_files_matching_regex.clone(),
|
only_files_regex: download_opts.only_files_matching_regex.clone(),
|
||||||
overwrite: download_opts.overwrite,
|
overwrite: download_opts.overwrite,
|
||||||
list_only: download_opts.list,
|
list_only: download_opts.list,
|
||||||
|
|
@ -393,7 +393,7 @@ async fn async_main(opts: Opts) -> anyhow::Result<()> {
|
||||||
match client
|
match client
|
||||||
.add_torrent(
|
.add_torrent(
|
||||||
AddTorrent::from_cli_argument(torrent_url)?,
|
AddTorrent::from_cli_argument(torrent_url)?,
|
||||||
Some(torrent_opts.clone()),
|
Some(torrent_opts()),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
|
@ -452,10 +452,7 @@ async fn async_main(opts: Opts) -> anyhow::Result<()> {
|
||||||
|
|
||||||
for path in &download_opts.torrent_path {
|
for path in &download_opts.torrent_path {
|
||||||
let handle = match session
|
let handle = match session
|
||||||
.add_torrent(
|
.add_torrent(AddTorrent::from_cli_argument(path)?, Some(torrent_opts()))
|
||||||
AddTorrent::from_cli_argument(path)?,
|
|
||||||
Some(torrent_opts.clone()),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(v) => match v {
|
Ok(v) => match v {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue