UPNP server integrated into rqbit.
How to use: https://github.com/ikatson/rqbit/pull/208
This commit is contained in:
parent
e8bd7ca7e5
commit
9e7b656f0b
34 changed files with 2420 additions and 234 deletions
|
|
@ -26,6 +26,7 @@ postgres = ["librqbit/postgres"]
|
|||
librqbit = { path = "../librqbit", default-features = false, features = [
|
||||
"http-api",
|
||||
"tracing-subscriber-utils",
|
||||
"upnp-serve-adapter",
|
||||
], version = "7.0.0-beta.2" }
|
||||
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
||||
console-subscriber = { version = "0.2", optional = true }
|
||||
|
|
@ -43,6 +44,7 @@ serde_json = "1"
|
|||
size_format = "1"
|
||||
bytes = "1.5.0"
|
||||
openssl = { version = "0.10", features = ["vendored"], optional = true }
|
||||
upnp-serve = { path = "../upnp-serve" }
|
||||
|
||||
[dev-dependencies]
|
||||
futures = { version = "0.3" }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::{io, net::SocketAddr, path::PathBuf, sync::Arc, time::Duration};
|
||||
|
||||
use anyhow::Context;
|
||||
use anyhow::{bail, Context};
|
||||
use clap::{CommandFactory, Parser, ValueEnum};
|
||||
use clap_complete::Shell;
|
||||
use librqbit::{
|
||||
|
|
@ -16,6 +16,7 @@ use librqbit::{
|
|||
PeerConnectionOptions, Session, SessionOptions, SessionPersistenceConfig, TorrentStatsState,
|
||||
};
|
||||
use size_format::SizeFormatterBinary as SF;
|
||||
use tokio::net::TcpListener;
|
||||
use tracing::{error, error_span, info, trace_span, warn};
|
||||
|
||||
#[derive(Debug, Clone, Copy, ValueEnum)]
|
||||
|
|
@ -95,6 +96,15 @@ struct Opts {
|
|||
#[arg(long = "disable-upnp")]
|
||||
disable_upnp: bool,
|
||||
|
||||
/// If set, will run a UPNP Media server and stream all the torrents through it.
|
||||
/// Should be set to your hostname/IP as seen by your LAN neighbors.
|
||||
#[arg(long = "upnp-server-hostname")]
|
||||
upnp_server_hostname: Option<String>,
|
||||
|
||||
/// UPNP server name that would be displayed on devices in your network.
|
||||
#[arg(long = "upnp-server-friendly-name")]
|
||||
upnp_server_friendly_name: Option<String>,
|
||||
|
||||
#[command(subcommand)]
|
||||
subcommand: SubCommand,
|
||||
|
||||
|
|
@ -437,6 +447,28 @@ async fn async_main(opts: Opts) -> anyhow::Result<()> {
|
|||
trace_span!("stats_printer"),
|
||||
stats_printer(session.clone()),
|
||||
);
|
||||
|
||||
let mut upnp_server = {
|
||||
match opts.upnp_server_hostname {
|
||||
Some(hn) => {
|
||||
if opts.http_api_listen_addr.ip().is_loopback() {
|
||||
bail!("cannot enable UPNP server as HTTP API listen addr is localhost. Change --http-api-listen-addr to start with 0.0.0.0");
|
||||
}
|
||||
let server = session
|
||||
.make_upnp_adapter(
|
||||
opts.upnp_server_friendly_name
|
||||
.unwrap_or_else(|| format!("rqbit at {hn}")),
|
||||
hn,
|
||||
opts.http_api_listen_addr.port(),
|
||||
)
|
||||
.await
|
||||
.context("error starting UPNP server")?;
|
||||
Some(server)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
};
|
||||
|
||||
let api = Api::new(
|
||||
session,
|
||||
Some(log_config.rust_log_reload_tx),
|
||||
|
|
@ -444,10 +476,31 @@ async fn async_main(opts: Opts) -> anyhow::Result<()> {
|
|||
);
|
||||
let http_api = HttpApi::new(api, Some(HttpApiOptions { read_only: false }));
|
||||
let http_api_listen_addr = opts.http_api_listen_addr;
|
||||
http_api
|
||||
.make_http_api_and_run(http_api_listen_addr)
|
||||
|
||||
info!("starting HTTP API at http://{http_api_listen_addr}");
|
||||
let tcp_listener = TcpListener::bind(http_api_listen_addr)
|
||||
.await
|
||||
.context("error running HTTP API")
|
||||
.with_context(|| format!("error binding to {http_api_listen_addr}"))?;
|
||||
|
||||
let upnp_router = upnp_server.as_mut().and_then(|s| s.take_router().ok());
|
||||
let http_api_fut = http_api.make_http_api_and_run(tcp_listener, upnp_router);
|
||||
|
||||
let res = match upnp_server {
|
||||
Some(srv) => {
|
||||
let upnp_fut = srv.run_ssdp_forever();
|
||||
|
||||
tokio::pin!(http_api_fut);
|
||||
tokio::pin!(upnp_fut);
|
||||
|
||||
tokio::select! {
|
||||
r = &mut http_api_fut => r,
|
||||
r = &mut upnp_fut => r
|
||||
}
|
||||
}
|
||||
None => http_api_fut.await,
|
||||
};
|
||||
|
||||
res.context("error running rqbit server")
|
||||
}
|
||||
},
|
||||
SubCommand::Download(download_opts) => {
|
||||
|
|
@ -534,10 +587,16 @@ async fn async_main(opts: Opts) -> anyhow::Result<()> {
|
|||
);
|
||||
let http_api = HttpApi::new(api, Some(HttpApiOptions { read_only: true }));
|
||||
let http_api_listen_addr = opts.http_api_listen_addr;
|
||||
|
||||
info!("starting HTTP API at http://{http_api_listen_addr}");
|
||||
let listener = tokio::net::TcpListener::bind(opts.http_api_listen_addr)
|
||||
.await
|
||||
.with_context(|| format!("error binding to {http_api_listen_addr}"))?;
|
||||
|
||||
librqbit_spawn(
|
||||
"http_api",
|
||||
error_span!("http_api"),
|
||||
http_api.make_http_api_and_run(http_api_listen_addr),
|
||||
http_api.make_http_api_and_run(listener, None),
|
||||
);
|
||||
|
||||
let mut added = false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue