Fix a couple bugs
This commit is contained in:
parent
1bea1f9235
commit
1c53aeba2f
6 changed files with 57 additions and 30 deletions
|
|
@ -36,12 +36,8 @@ async fn main() -> Result<(), anyhow::Error> {
|
||||||
.add_torrent(
|
.add_torrent(
|
||||||
AddTorrent::from_url(MAGNET_LINK),
|
AddTorrent::from_url(MAGNET_LINK),
|
||||||
Some(AddTorrentOptions {
|
Some(AddTorrentOptions {
|
||||||
// Set this to true to allow writing on top of existing files.
|
// Allow writing on top of existing files.
|
||||||
// If the file is partially downloaded, librqbit will only download the
|
overwrite: true,
|
||||||
// missing pieces.
|
|
||||||
//
|
|
||||||
// Otherwise it will throw an error that the file exists.
|
|
||||||
overwrite: false,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,11 @@ impl HttpApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn make_http_api_and_run(self, addr: SocketAddr) -> anyhow::Result<()> {
|
pub async fn make_http_api_and_run(
|
||||||
|
self,
|
||||||
|
addr: SocketAddr,
|
||||||
|
read_only: bool,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
let state = self.inner;
|
let state = self.inner;
|
||||||
|
|
||||||
async fn api_root() -> impl IntoResponse {
|
async fn api_root() -> impl IntoResponse {
|
||||||
|
|
@ -160,22 +164,26 @@ impl HttpApi {
|
||||||
state.api_set_rust_log(new_value).map(axum::Json)
|
state.api_set_rust_log(new_value).map(axum::Json)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_mut)]
|
|
||||||
let mut app = Router::new()
|
let mut app = Router::new()
|
||||||
.route("/", get(api_root))
|
.route("/", get(api_root))
|
||||||
|
.route("/rust_log", post(set_rust_log))
|
||||||
.route("/dht/stats", get(dht_stats))
|
.route("/dht/stats", get(dht_stats))
|
||||||
.route("/dht/table", get(dht_table))
|
.route("/dht/table", get(dht_table))
|
||||||
.route("/torrents", get(torrents_list).post(torrents_post))
|
.route("/torrents", get(torrents_list))
|
||||||
.route("/torrents/:id", get(torrent_details))
|
.route("/torrents/:id", get(torrent_details))
|
||||||
.route("/torrents/:id/haves", get(torrent_haves))
|
.route("/torrents/:id/haves", get(torrent_haves))
|
||||||
.route("/torrents/:id/stats", get(torrent_stats_v0))
|
.route("/torrents/:id/stats", get(torrent_stats_v0))
|
||||||
.route("/torrents/:id/stats/v1", get(torrent_stats_v1))
|
.route("/torrents/:id/stats/v1", get(torrent_stats_v1))
|
||||||
.route("/torrents/:id/peer_stats", get(peer_stats))
|
.route("/torrents/:id/peer_stats", get(peer_stats));
|
||||||
.route("/torrents/:id/pause", post(torrent_action_pause))
|
|
||||||
.route("/torrents/:id/start", post(torrent_action_start))
|
if !read_only {
|
||||||
.route("/torrents/:id/forget", post(torrent_action_forget))
|
app = app
|
||||||
.route("/torrents/:id/delete", post(torrent_action_delete))
|
.route("/torrents", post(torrents_post))
|
||||||
.route("/rust_log", post(set_rust_log));
|
.route("/torrents/:id/pause", post(torrent_action_pause))
|
||||||
|
.route("/torrents/:id/start", post(torrent_action_start))
|
||||||
|
.route("/torrents/:id/forget", post(torrent_action_forget))
|
||||||
|
.route("/torrents/:id/delete", post(torrent_action_delete));
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "webui")]
|
#[cfg(feature = "webui")]
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,8 @@ pub struct SessionOptions {
|
||||||
pub disable_dht: bool,
|
pub disable_dht: bool,
|
||||||
pub disable_dht_persistence: bool,
|
pub disable_dht_persistence: bool,
|
||||||
pub persistence: bool,
|
pub persistence: bool,
|
||||||
|
// Will default to output_folder/.rqbit-session.json
|
||||||
|
pub persistence_filename: Option<PathBuf>,
|
||||||
pub dht_config: Option<PersistentDhtConfig>,
|
pub dht_config: Option<PersistentDhtConfig>,
|
||||||
pub peer_id: Option<Id20>,
|
pub peer_id: Option<Id20>,
|
||||||
pub peer_opts: Option<PeerConnectionOptions>,
|
pub peer_opts: Option<PeerConnectionOptions>,
|
||||||
|
|
@ -240,7 +242,9 @@ impl Session {
|
||||||
Some(dht)
|
Some(dht)
|
||||||
};
|
};
|
||||||
let peer_opts = opts.peer_opts.unwrap_or_default();
|
let peer_opts = opts.peer_opts.unwrap_or_default();
|
||||||
let session_filename = output_folder.join(".rqbit-session.json");
|
let session_filename = opts
|
||||||
|
.persistence_filename
|
||||||
|
.unwrap_or_else(|| output_folder.join(".rqbit-session.json"));
|
||||||
let session = Arc::new(Self {
|
let session = Arc::new(Self {
|
||||||
persistence_filename: session_filename,
|
persistence_filename: session_filename,
|
||||||
peer_id,
|
peer_id,
|
||||||
|
|
|
||||||
|
|
@ -191,7 +191,7 @@ impl Serialize for Speed {
|
||||||
}
|
}
|
||||||
Tmp {
|
Tmp {
|
||||||
mbps: self.mbps,
|
mbps: self.mbps,
|
||||||
human_readable: format!("{:?}", self.mbps),
|
human_readable: format!("{}", self),
|
||||||
}
|
}
|
||||||
.serialize(serializer)
|
.serialize(serializer)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { MouseEventHandler, StrictMode, createContext, useContext, useEffect, useRef, useState } from 'react';
|
import { MouseEventHandler, StrictMode, createContext, useContext, useEffect, useRef, useState } from 'react';
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import { ProgressBar, Button, Container, Row, Col, Alert, Modal, Form, Spinner, Table } from 'react-bootstrap';
|
import { ProgressBar, Button, Container, Row, Col, Alert, Modal, Form, Spinner, Table } from 'react-bootstrap';
|
||||||
import { AddTorrentResponse, TorrentDetails, TorrentFile, TorrentId, TorrentStats, ErrorDetails, API, STATE_INITIALIZING, STATE_LIVE, STATE_PAUSED } from './api';
|
import { AddTorrentResponse, TorrentDetails, TorrentFile, TorrentId, TorrentStats, ErrorDetails, API, STATE_INITIALIZING, STATE_LIVE, STATE_PAUSED, STATE_ERROR } from './api';
|
||||||
|
|
||||||
interface Error {
|
interface Error {
|
||||||
text: string,
|
text: string,
|
||||||
|
|
@ -171,13 +171,16 @@ const TorrentRow: React.FC<{
|
||||||
return `${peer_stats.live} / ${peer_stats.seen}`;
|
return `${peer_stats.live} / ${peer_stats.seen}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatDownloadSped = () => {
|
const formatDownloadSpeed = () => {
|
||||||
if (finished) {
|
if (finished) {
|
||||||
return 'Completed';
|
return 'Completed';
|
||||||
}
|
}
|
||||||
if (state == STATE_INITIALIZING) {
|
switch (state) {
|
||||||
return 'Checking files';
|
case STATE_PAUSED: return 'Paused';
|
||||||
|
case STATE_INITIALIZING: return 'Checking files';
|
||||||
|
case STATE_ERROR: return 'Error';
|
||||||
}
|
}
|
||||||
|
|
||||||
return statsResponse.live?.download_speed.human_readable ?? "N/A";
|
return statsResponse.live?.download_speed.human_readable ?? "N/A";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -206,14 +209,14 @@ const TorrentRow: React.FC<{
|
||||||
{statsResponse ?
|
{statsResponse ?
|
||||||
<>
|
<>
|
||||||
<Column label="Size">{`${formatBytes(totalBytes)} `}</Column>
|
<Column label="Size">{`${formatBytes(totalBytes)} `}</Column>
|
||||||
<Column size={2} label={state == STATE_PAUSED ? 'Progress (PAUSED)' : 'Progress'}>
|
<Column size={2} label={state == STATE_PAUSED ? 'Progress' : 'Progress'}>
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
now={progressPercentage}
|
now={progressPercentage}
|
||||||
label={progressLabel}
|
label={progressLabel}
|
||||||
animated={isAnimated}
|
animated={isAnimated}
|
||||||
variant={progressBarVariant} />
|
variant={progressBarVariant} />
|
||||||
</Column>
|
</Column>
|
||||||
<Column size={2} label="Down Speed">{formatDownloadSped()}</Column>
|
<Column size={2} label="Down Speed">{formatDownloadSpeed()}</Column>
|
||||||
<Column label="ETA">{getCompletionETA(statsResponse)}</Column>
|
<Column label="ETA">{getCompletionETA(statsResponse)}</Column>
|
||||||
<Column size={2} label="Peers">{formatPeersString()}</Column >
|
<Column size={2} label="Peers">{formatPeersString()}</Column >
|
||||||
<Column label="Actions">
|
<Column label="Actions">
|
||||||
|
|
@ -393,7 +396,7 @@ const UploadButton = ({ buttonText, onClick, data, resetData, variant }) => {
|
||||||
const response = await API.uploadTorrent(data, { listOnly: true });
|
const response = await API.uploadTorrent(data, { listOnly: true });
|
||||||
setFileList(response.details.files);
|
setFileList(response.details.files);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setFileListError({ text: 'Error listing torrent', details: e });
|
setFileListError({ text: 'Error uploading torrent', details: e });
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,13 @@ struct Opts {
|
||||||
struct ServerStartOptions {
|
struct ServerStartOptions {
|
||||||
/// The output folder to write to. If not exists, it will be created.
|
/// The output folder to write to. If not exists, it will be created.
|
||||||
output_folder: String,
|
output_folder: String,
|
||||||
|
#[arg(
|
||||||
|
long = "disable-persistence",
|
||||||
|
help = "Disable server persistence. It will not read or write its state to disk."
|
||||||
|
)]
|
||||||
|
disable_persistence: bool,
|
||||||
|
#[arg(long = "persistence-filename")]
|
||||||
|
persistence_filename: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
|
|
@ -275,11 +282,12 @@ fn main() -> anyhow::Result<()> {
|
||||||
async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()> {
|
async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()> {
|
||||||
let logging_reload_tx = init_logging(&opts);
|
let logging_reload_tx = init_logging(&opts);
|
||||||
|
|
||||||
let sopts = SessionOptions {
|
let mut sopts = SessionOptions {
|
||||||
disable_dht: opts.disable_dht,
|
disable_dht: opts.disable_dht,
|
||||||
disable_dht_persistence: opts.disable_dht_persistence,
|
disable_dht_persistence: opts.disable_dht_persistence,
|
||||||
dht_config: None,
|
dht_config: None,
|
||||||
persistence: true,
|
persistence: false,
|
||||||
|
persistence_filename: None,
|
||||||
peer_id: None,
|
peer_id: None,
|
||||||
peer_opts: Some(PeerConnectionOptions {
|
peer_opts: Some(PeerConnectionOptions {
|
||||||
connect_timeout: Some(opts.peer_connect_timeout),
|
connect_timeout: Some(opts.peer_connect_timeout),
|
||||||
|
|
@ -343,6 +351,9 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()>
|
||||||
match &opts.subcommand {
|
match &opts.subcommand {
|
||||||
SubCommand::Server(server_opts) => match &server_opts.subcommand {
|
SubCommand::Server(server_opts) => match &server_opts.subcommand {
|
||||||
ServerSubcommand::Start(start_opts) => {
|
ServerSubcommand::Start(start_opts) => {
|
||||||
|
sopts.persistence = !start_opts.disable_persistence;
|
||||||
|
sopts.persistence_filename =
|
||||||
|
start_opts.persistence_filename.clone().map(PathBuf::from);
|
||||||
let session = Session::new_with_opts(
|
let session = Session::new_with_opts(
|
||||||
PathBuf::from(&start_opts.output_folder),
|
PathBuf::from(&start_opts.output_folder),
|
||||||
spawner,
|
spawner,
|
||||||
|
|
@ -358,7 +369,7 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()>
|
||||||
let http_api = HttpApi::new(session, Some(logging_reload_tx));
|
let http_api = HttpApi::new(session, Some(logging_reload_tx));
|
||||||
let http_api_listen_addr = opts.http_api_listen_addr;
|
let http_api_listen_addr = opts.http_api_listen_addr;
|
||||||
http_api
|
http_api
|
||||||
.make_http_api_and_run(http_api_listen_addr)
|
.make_http_api_and_run(http_api_listen_addr, false)
|
||||||
.await
|
.await
|
||||||
.context("error starting HTTP API")
|
.context("error starting HTTP API")
|
||||||
}
|
}
|
||||||
|
|
@ -438,7 +449,9 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()>
|
||||||
spawn(
|
spawn(
|
||||||
"http_api",
|
"http_api",
|
||||||
error_span!("http_api"),
|
error_span!("http_api"),
|
||||||
http_api.clone().make_http_api_and_run(http_api_listen_addr),
|
http_api
|
||||||
|
.clone()
|
||||||
|
.make_http_api_and_run(http_api_listen_addr, true),
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut added = false;
|
let mut added = false;
|
||||||
|
|
@ -504,8 +517,11 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()>
|
||||||
if download_opts.exit_on_finish {
|
if download_opts.exit_on_finish {
|
||||||
let results = futures::future::join_all(
|
let results = futures::future::join_all(
|
||||||
handles.iter().map(|h| h.wait_until_completed()),
|
handles.iter().map(|h| h.wait_until_completed()),
|
||||||
);
|
)
|
||||||
results.await;
|
.await;
|
||||||
|
if results.iter().any(|r| r.is_err()) {
|
||||||
|
anyhow::bail!("some downloads failed")
|
||||||
|
}
|
||||||
info!("All downloads completed, exiting");
|
info!("All downloads completed, exiting");
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue