Fix a couple bugs

This commit is contained in:
Igor Katson 2023-11-25 11:21:45 +00:00
parent 1bea1f9235
commit 1c53aeba2f
No known key found for this signature in database
GPG key ID: B4EC22B66D61A3F5
6 changed files with 57 additions and 30 deletions

View file

@ -36,12 +36,8 @@ async fn main() -> Result<(), anyhow::Error> {
.add_torrent(
AddTorrent::from_url(MAGNET_LINK),
Some(AddTorrentOptions {
// Set this to true to allow writing on top of existing files.
// If the file is partially downloaded, librqbit will only download the
// missing pieces.
//
// Otherwise it will throw an error that the file exists.
overwrite: false,
// Allow writing on top of existing files.
overwrite: true,
..Default::default()
}),
)

View file

@ -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;
async fn api_root() -> impl IntoResponse {
@ -160,22 +164,26 @@ impl HttpApi {
state.api_set_rust_log(new_value).map(axum::Json)
}
#[allow(unused_mut)]
let mut app = Router::new()
.route("/", get(api_root))
.route("/rust_log", post(set_rust_log))
.route("/dht/stats", get(dht_stats))
.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/haves", get(torrent_haves))
.route("/torrents/:id/stats", get(torrent_stats_v0))
.route("/torrents/:id/stats/v1", get(torrent_stats_v1))
.route("/torrents/:id/peer_stats", get(peer_stats))
.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))
.route("/rust_log", post(set_rust_log));
.route("/torrents/:id/peer_stats", get(peer_stats));
if !read_only {
app = app
.route("/torrents", post(torrents_post))
.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")]
{

View file

@ -210,6 +210,8 @@ pub struct SessionOptions {
pub disable_dht: bool,
pub disable_dht_persistence: bool,
pub persistence: bool,
// Will default to output_folder/.rqbit-session.json
pub persistence_filename: Option<PathBuf>,
pub dht_config: Option<PersistentDhtConfig>,
pub peer_id: Option<Id20>,
pub peer_opts: Option<PeerConnectionOptions>,
@ -240,7 +242,9 @@ impl Session {
Some(dht)
};
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 {
persistence_filename: session_filename,
peer_id,

View file

@ -191,7 +191,7 @@ impl Serialize for Speed {
}
Tmp {
mbps: self.mbps,
human_readable: format!("{:?}", self.mbps),
human_readable: format!("{}", self),
}
.serialize(serializer)
}

View file

@ -1,7 +1,7 @@
import { MouseEventHandler, StrictMode, createContext, useContext, useEffect, useRef, useState } from 'react';
import ReactDOM from 'react-dom/client';
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 {
text: string,
@ -171,13 +171,16 @@ const TorrentRow: React.FC<{
return `${peer_stats.live} / ${peer_stats.seen}`;
}
const formatDownloadSped = () => {
const formatDownloadSpeed = () => {
if (finished) {
return 'Completed';
}
if (state == STATE_INITIALIZING) {
return 'Checking files';
switch (state) {
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";
}
@ -206,14 +209,14 @@ const TorrentRow: React.FC<{
{statsResponse ?
<>
<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
now={progressPercentage}
label={progressLabel}
animated={isAnimated}
variant={progressBarVariant} />
</Column>
<Column size={2} label="Down Speed">{formatDownloadSped()}</Column>
<Column size={2} label="Down Speed">{formatDownloadSpeed()}</Column>
<Column label="ETA">{getCompletionETA(statsResponse)}</Column>
<Column size={2} label="Peers">{formatPeersString()}</Column >
<Column label="Actions">
@ -393,7 +396,7 @@ const UploadButton = ({ buttonText, onClick, data, resetData, variant }) => {
const response = await API.uploadTorrent(data, { listOnly: true });
setFileList(response.details.files);
} catch (e) {
setFileListError({ text: 'Error listing torrent', details: e });
setFileListError({ text: 'Error uploading torrent', details: e });
} finally {
setLoading(false);
}

View file

@ -77,6 +77,13 @@ struct Opts {
struct ServerStartOptions {
/// The output folder to write to. If not exists, it will be created.
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)]
@ -275,11 +282,12 @@ fn main() -> anyhow::Result<()> {
async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()> {
let logging_reload_tx = init_logging(&opts);
let sopts = SessionOptions {
let mut sopts = SessionOptions {
disable_dht: opts.disable_dht,
disable_dht_persistence: opts.disable_dht_persistence,
dht_config: None,
persistence: true,
persistence: false,
persistence_filename: None,
peer_id: None,
peer_opts: Some(PeerConnectionOptions {
connect_timeout: Some(opts.peer_connect_timeout),
@ -343,6 +351,9 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()>
match &opts.subcommand {
SubCommand::Server(server_opts) => match &server_opts.subcommand {
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(
PathBuf::from(&start_opts.output_folder),
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_listen_addr = opts.http_api_listen_addr;
http_api
.make_http_api_and_run(http_api_listen_addr)
.make_http_api_and_run(http_api_listen_addr, false)
.await
.context("error starting HTTP API")
}
@ -438,7 +449,9 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()>
spawn(
"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;
@ -504,8 +517,11 @@ async fn async_main(opts: Opts, spawner: BlockingSpawner) -> anyhow::Result<()>
if download_opts.exit_on_finish {
let results = futures::future::join_all(
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");
Ok(())
} else {