The server now doesnt read files by itself, client must do it
This commit is contained in:
parent
7d6ed06166
commit
707d4be631
5 changed files with 70 additions and 40 deletions
|
|
@ -6,7 +6,7 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use librqbit::session::{AddTorrentOptions, AddTorrentResponse, Session};
|
use librqbit::session::{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
|
||||||
|
|
@ -30,7 +30,7 @@ async fn main() -> Result<(), anyhow::Error> {
|
||||||
// Add the torrent to the session
|
// Add the torrent to the session
|
||||||
let handle = match session
|
let handle = match session
|
||||||
.add_torrent(
|
.add_torrent(
|
||||||
MAGNET_LINK,
|
AddTorrent::from_url(MAGNET_LINK),
|
||||||
Some(AddTorrentOptions {
|
Some(AddTorrentOptions {
|
||||||
// Set this to true to allow writing on top of existing files.
|
// Set this to true to allow writing on top of existing files.
|
||||||
// If the file is partially downloaded, librqbit will only download the
|
// If the file is partially downloaded, librqbit will only download the
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,8 @@ impl HttpApi {
|
||||||
) -> Result<impl IntoResponse> {
|
) -> Result<impl IntoResponse> {
|
||||||
let opts = params.into_add_torrent_options();
|
let opts = params.into_add_torrent_options();
|
||||||
let add = match String::from_utf8(data.to_vec()) {
|
let add = match String::from_utf8(data.to_vec()) {
|
||||||
Ok(s) => AddTorrent::from(s),
|
Ok(s) => AddTorrent::Url(s.into()),
|
||||||
Err(e) => AddTorrent::from(e.into_bytes()),
|
Err(e) => AddTorrent::TorrentFileBytes(e.into_bytes().into()),
|
||||||
};
|
};
|
||||||
state.api_add_torrent(add, Some(opts)).await.map(axum::Json)
|
state.api_add_torrent(add, Some(opts)).await.map(axum::Json)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{http_api::TorrentAddQueryParams, session::AddTorrentOptions};
|
use crate::{
|
||||||
|
http_api::TorrentAddQueryParams,
|
||||||
|
session::{AddTorrent, AddTorrentOptions},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct HttpApiClient {
|
pub struct HttpApiClient {
|
||||||
|
|
@ -77,7 +80,7 @@ impl HttpApiClient {
|
||||||
|
|
||||||
pub async fn add_torrent(
|
pub async fn add_torrent(
|
||||||
&self,
|
&self,
|
||||||
torrent: &str,
|
torrent: AddTorrent<'_>,
|
||||||
opts: Option<AddTorrentOptions>,
|
opts: Option<AddTorrentOptions>,
|
||||||
) -> anyhow::Result<crate::http_api::ApiAddTorrentResponse> {
|
) -> anyhow::Result<crate::http_api::ApiAddTorrentResponse> {
|
||||||
let opts = opts.unwrap_or_default();
|
let opts = opts.unwrap_or_default();
|
||||||
|
|
@ -94,7 +97,7 @@ impl HttpApiClient {
|
||||||
let response = check_response(
|
let response = check_response(
|
||||||
self.client
|
self.client
|
||||||
.post(&url)
|
.post(&url)
|
||||||
.body(torrent.to_owned())
|
.body(torrent.into_bytes())
|
||||||
.send()
|
.send()
|
||||||
.await?,
|
.await?,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{borrow::Cow, fs::File, io::Read, net::SocketAddr, path::PathBuf, time::Duration};
|
use std::{borrow::Cow, io::Read, net::SocketAddr, path::PathBuf, time::Duration};
|
||||||
|
|
||||||
use anyhow::{bail, Context};
|
use anyhow::{bail, Context};
|
||||||
use buffers::ByteString;
|
use buffers::ByteString;
|
||||||
|
|
@ -20,6 +20,8 @@ use crate::{
|
||||||
torrent_manager::{TorrentManagerBuilder, TorrentManagerHandle},
|
torrent_manager::{TorrentManagerBuilder, TorrentManagerHandle},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const SUPPORTED_SCHEMES: [&str; 3] = ["http:", "https:", "magnet:"];
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum ManagedTorrentState {
|
pub enum ManagedTorrentState {
|
||||||
Initializing,
|
Initializing,
|
||||||
|
|
@ -83,21 +85,6 @@ async fn torrent_from_url(url: &str) -> anyhow::Result<TorrentMetaV1Owned> {
|
||||||
torrent_from_bytes(&b).context("error decoding torrent")
|
torrent_from_bytes(&b).context("error decoding torrent")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn torrent_from_file(filename: &str) -> anyhow::Result<TorrentMetaV1Owned> {
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
if filename == "-" {
|
|
||||||
std::io::stdin()
|
|
||||||
.read_to_end(&mut buf)
|
|
||||||
.context("error reading stdin")?;
|
|
||||||
} else {
|
|
||||||
File::open(filename)
|
|
||||||
.with_context(|| format!("error opening {filename}"))?
|
|
||||||
.read_to_end(&mut buf)
|
|
||||||
.with_context(|| format!("error reading {filename}"))?;
|
|
||||||
}
|
|
||||||
torrent_from_bytes(&buf).context("error decoding torrent")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compute_only_files<ByteBuf: AsRef<[u8]>>(
|
fn compute_only_files<ByteBuf: AsRef<[u8]>>(
|
||||||
torrent: &TorrentMetaV1Info<ByteBuf>,
|
torrent: &TorrentMetaV1Info<ByteBuf>,
|
||||||
filename_re: &str,
|
filename_re: &str,
|
||||||
|
|
@ -142,26 +129,55 @@ pub enum AddTorrentResponse {
|
||||||
Added(TorrentManagerHandle),
|
Added(TorrentManagerHandle),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_local_file_including_stdin(filename: &str) -> anyhow::Result<Vec<u8>> {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
if filename == "-" {
|
||||||
|
std::io::stdin()
|
||||||
|
.read_to_end(&mut buf)
|
||||||
|
.context("error reading stdin")?;
|
||||||
|
} else {
|
||||||
|
std::fs::File::open(filename)
|
||||||
|
.context("error opening")?
|
||||||
|
.read_to_end(&mut buf)
|
||||||
|
.context("error reading")?;
|
||||||
|
}
|
||||||
|
Ok(buf)
|
||||||
|
}
|
||||||
|
|
||||||
pub enum AddTorrent<'a> {
|
pub enum AddTorrent<'a> {
|
||||||
Url(Cow<'a, str>),
|
Url(Cow<'a, str>),
|
||||||
TorrentFileBytes(Vec<u8>),
|
TorrentFileBytes(Cow<'a, [u8]>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a str> for AddTorrent<'a> {
|
impl<'a> AddTorrent<'a> {
|
||||||
fn from(s: &'a str) -> Self {
|
// Don't call this from HTTP API.
|
||||||
Self::Url(Cow::Borrowed(s))
|
pub fn from_cli_argument(path: &'a str) -> anyhow::Result<Self> {
|
||||||
|
if SUPPORTED_SCHEMES.iter().any(|s| path.starts_with(s)) {
|
||||||
|
return Ok(Self::Url(Cow::Borrowed(path)));
|
||||||
|
}
|
||||||
|
Self::from_local_filename(path)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<String> for AddTorrent<'a> {
|
pub fn from_url(url: impl Into<Cow<'a, str>>) -> Self {
|
||||||
fn from(s: String) -> Self {
|
Self::Url(url.into())
|
||||||
Self::Url(Cow::Owned(s))
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<Vec<u8>> for AddTorrent<'a> {
|
pub fn from_bytes(bytes: impl Into<Cow<'a, [u8]>>) -> Self {
|
||||||
fn from(b: Vec<u8>) -> Self {
|
Self::TorrentFileBytes(bytes.into())
|
||||||
Self::TorrentFileBytes(b)
|
}
|
||||||
|
|
||||||
|
// Don't call this from HTTP API.
|
||||||
|
pub fn from_local_filename(filename: &str) -> anyhow::Result<Self> {
|
||||||
|
let file = read_local_file_including_stdin(filename)
|
||||||
|
.with_context(|| format!("error reading local file {filename:?}"))?;
|
||||||
|
Ok(Self::TorrentFileBytes(Cow::Owned(file)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_bytes(self) -> Vec<u8> {
|
||||||
|
match self {
|
||||||
|
Self::Url(s) => s.into_owned().into_bytes(),
|
||||||
|
Self::TorrentFileBytes(b) => b.into_owned(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,7 +286,12 @@ impl Session {
|
||||||
{
|
{
|
||||||
torrent_from_url(&url).await?
|
torrent_from_url(&url).await?
|
||||||
}
|
}
|
||||||
AddTorrent::Url(filename) => torrent_from_file(&filename)?,
|
AddTorrent::Url(url) => {
|
||||||
|
bail!(
|
||||||
|
"unsupported URL {:?}. Supporting magnet:, http:, and https",
|
||||||
|
url
|
||||||
|
)
|
||||||
|
}
|
||||||
AddTorrent::TorrentFileBytes(bytes) => {
|
AddTorrent::TorrentFileBytes(bytes) => {
|
||||||
torrent_from_bytes(&bytes).context("error decoding torrent")?
|
torrent_from_bytes(&bytes).context("error decoding torrent")?
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ use librqbit::{
|
||||||
http_api_client,
|
http_api_client,
|
||||||
peer_connection::PeerConnectionOptions,
|
peer_connection::PeerConnectionOptions,
|
||||||
session::{
|
session::{
|
||||||
AddTorrentOptions, AddTorrentResponse, ListOnlyResponse, ManagedTorrentState, Session,
|
AddTorrent, AddTorrentOptions, AddTorrentResponse, ListOnlyResponse, ManagedTorrentState,
|
||||||
SessionOptions,
|
Session, SessionOptions,
|
||||||
},
|
},
|
||||||
spawn_utils::{spawn, BlockingSpawner},
|
spawn_utils::{spawn, BlockingSpawner},
|
||||||
torrent_state::timeit,
|
torrent_state::timeit,
|
||||||
|
|
@ -329,7 +329,10 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()>
|
||||||
if connect_to_existing {
|
if connect_to_existing {
|
||||||
for torrent_url in &download_opts.torrent_path {
|
for torrent_url in &download_opts.torrent_path {
|
||||||
match client
|
match client
|
||||||
.add_torrent(torrent_url, Some(torrent_opts.clone()))
|
.add_torrent(
|
||||||
|
AddTorrent::from_cli_argument(torrent_url)?,
|
||||||
|
Some(torrent_opts.clone()),
|
||||||
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(ApiAddTorrentResponse { id, details }) => {
|
Ok(ApiAddTorrentResponse { id, details }) => {
|
||||||
|
|
@ -382,7 +385,10 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> 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(path.as_str(), Some(torrent_opts.clone()))
|
.add_torrent(
|
||||||
|
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