Well, it doesnt crash at least
This commit is contained in:
parent
276a1e175e
commit
efcffdd072
6 changed files with 101 additions and 21 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{net::SocketAddr, sync::Arc};
|
use std::{collections::HashSet, net::SocketAddr, sync::Arc};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use buffers::ByteBufOwned;
|
use buffers::ByteBufOwned;
|
||||||
|
|
@ -122,6 +122,18 @@ impl Api {
|
||||||
Ok(Default::default())
|
Ok(Default::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn api_torrent_action_update_only_files(
|
||||||
|
&self,
|
||||||
|
idx: TorrentId,
|
||||||
|
only_files: &HashSet<usize>,
|
||||||
|
) -> Result<EmptyJsonResponse> {
|
||||||
|
let handle = self.mgr_handle(idx)?;
|
||||||
|
self.session
|
||||||
|
.update_only_files(&handle, only_files)
|
||||||
|
.context("error updating only_files")?;
|
||||||
|
Ok(Default::default())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn api_set_rust_log(&self, new_value: String) -> Result<EmptyJsonResponse> {
|
pub fn api_set_rust_log(&self, new_value: String) -> Result<EmptyJsonResponse> {
|
||||||
let tx = self
|
let tx = self
|
||||||
.rust_log_reload_tx
|
.rust_log_reload_tx
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use buffers::ByteBufOwned;
|
use librqbit_core::lengths::{ChunkInfo, Lengths, ValidPieceIndex};
|
||||||
use librqbit_core::{
|
|
||||||
lengths::{ChunkInfo, Lengths, ValidPieceIndex},
|
|
||||||
torrent_metainfo::{TorrentMetaV1Info, TorrentMetaV1Owned},
|
|
||||||
};
|
|
||||||
use peer_binary_protocol::Piece;
|
use peer_binary_protocol::Piece;
|
||||||
use tracing::{debug, trace};
|
use tracing::{debug, trace};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,21 @@ impl HttpApi {
|
||||||
state.api_torrent_action_delete(idx).map(axum::Json)
|
state.api_torrent_action_delete(idx).map(axum::Json)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct UpdateOnlyFilesRequest {
|
||||||
|
only_files: Vec<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn torrent_action_update_only_files(
|
||||||
|
State(state): State<ApiState>,
|
||||||
|
Path(idx): Path<usize>,
|
||||||
|
axum::Json(req): axum::Json<UpdateOnlyFilesRequest>,
|
||||||
|
) -> Result<impl IntoResponse> {
|
||||||
|
state
|
||||||
|
.api_torrent_action_update_only_files(idx, &req.only_files.into_iter().collect())
|
||||||
|
.map(axum::Json)
|
||||||
|
}
|
||||||
|
|
||||||
async fn set_rust_log(
|
async fn set_rust_log(
|
||||||
State(state): State<ApiState>,
|
State(state): State<ApiState>,
|
||||||
new_value: String,
|
new_value: String,
|
||||||
|
|
@ -215,7 +230,11 @@ impl HttpApi {
|
||||||
.route("/torrents/:id/pause", post(torrent_action_pause))
|
.route("/torrents/:id/pause", post(torrent_action_pause))
|
||||||
.route("/torrents/:id/start", post(torrent_action_start))
|
.route("/torrents/:id/start", post(torrent_action_start))
|
||||||
.route("/torrents/:id/forget", post(torrent_action_forget))
|
.route("/torrents/:id/forget", post(torrent_action_forget))
|
||||||
.route("/torrents/:id/delete", post(torrent_action_delete));
|
.route("/torrents/:id/delete", post(torrent_action_delete))
|
||||||
|
.route(
|
||||||
|
"/torrents/:id/update_only_files",
|
||||||
|
post(torrent_action_update_only_files),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "webui")]
|
#[cfg(feature = "webui")]
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ impl SessionDatabase {
|
||||||
.collect(),
|
.collect(),
|
||||||
info_hash: torrent.info_hash().as_string(),
|
info_hash: torrent.info_hash().as_string(),
|
||||||
info: torrent.info().info.clone(),
|
info: torrent.info().info.clone(),
|
||||||
only_files: torrent.only_files.clone(),
|
only_files: torrent.only_files().clone(),
|
||||||
is_paused: torrent
|
is_paused: torrent
|
||||||
.with_state(|s| matches!(s, ManagedTorrentState::Paused(_))),
|
.with_state(|s| matches!(s, ManagedTorrentState::Paused(_))),
|
||||||
output_folder: torrent.info().out_dir.clone(),
|
output_folder: torrent.info().out_dir.clone(),
|
||||||
|
|
@ -1137,6 +1137,18 @@ impl Session {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn update_only_files(
|
||||||
|
self: &Arc<Self>,
|
||||||
|
handle: &ManagedTorrentHandle,
|
||||||
|
only_files: &HashSet<usize>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let need_to_unpause = handle.update_only_files(only_files)?;
|
||||||
|
if need_to_unpause {
|
||||||
|
self.unpause(handle)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn tcp_listen_port(&self) -> Option<u16> {
|
pub fn tcp_listen_port(&self) -> Option<u16> {
|
||||||
self.tcp_listen_port
|
self.tcp_listen_port
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ impl ManagedTorrentState {
|
||||||
|
|
||||||
pub(crate) struct ManagedTorrentLocked {
|
pub(crate) struct ManagedTorrentLocked {
|
||||||
pub state: ManagedTorrentState,
|
pub state: ManagedTorrentState,
|
||||||
|
pub(crate) only_files: Option<Vec<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
@ -91,7 +92,6 @@ pub struct ManagedTorrentInfo {
|
||||||
|
|
||||||
pub struct ManagedTorrent {
|
pub struct ManagedTorrent {
|
||||||
pub info: Arc<ManagedTorrentInfo>,
|
pub info: Arc<ManagedTorrentInfo>,
|
||||||
pub(crate) only_files: Option<Vec<usize>>,
|
|
||||||
locked: RwLock<ManagedTorrentLocked>,
|
locked: RwLock<ManagedTorrentLocked>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,7 +109,7 @@ impl ManagedTorrent {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn only_files(&self) -> Option<Vec<usize>> {
|
pub fn only_files(&self) -> Option<Vec<usize>> {
|
||||||
self.only_files.clone()
|
self.locked.read().only_files.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_state<R>(&self, f: impl FnOnce(&ManagedTorrentState) -> R) -> R {
|
pub fn with_state<R>(&self, f: impl FnOnce(&ManagedTorrentState) -> R) -> R {
|
||||||
|
|
@ -298,7 +298,7 @@ impl ManagedTorrent {
|
||||||
ManagedTorrentState::Error(_) => {
|
ManagedTorrentState::Error(_) => {
|
||||||
let initializing = Arc::new(TorrentStateInitializing::new(
|
let initializing = Arc::new(TorrentStateInitializing::new(
|
||||||
self.info.clone(),
|
self.info.clone(),
|
||||||
self.only_files.clone(),
|
g.only_files.clone(),
|
||||||
));
|
));
|
||||||
g.state = ManagedTorrentState::Initializing(initializing.clone());
|
g.state = ManagedTorrentState::Initializing(initializing.clone());
|
||||||
drop(g);
|
drop(g);
|
||||||
|
|
@ -407,6 +407,45 @@ impl ManagedTorrent {
|
||||||
}
|
}
|
||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns true if needed to unpause torrent.
|
||||||
|
// This is just implementation detail - it's easier to pause/unpause than to tinker with internals.
|
||||||
|
pub(crate) fn update_only_files(&self, only_files: &HashSet<usize>) -> anyhow::Result<bool> {
|
||||||
|
if only_files.is_empty() {
|
||||||
|
anyhow::bail!("you need to select at least one file");
|
||||||
|
}
|
||||||
|
let file_count = self.info().info.iter_file_lengths()?.count();
|
||||||
|
for f in only_files.iter().copied() {
|
||||||
|
if f >= file_count {
|
||||||
|
anyhow::bail!("only_files contains invalid value {f}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if live, need to update chunk tracker
|
||||||
|
// - if already finished: need to pause, then unpause (to reopen files etc)
|
||||||
|
// if paused, need to update chunk tracker
|
||||||
|
|
||||||
|
let mut g = self.locked.write();
|
||||||
|
let need_to_unpause = match &mut g.state {
|
||||||
|
ManagedTorrentState::Initializing(_) => bail!("can't update initializing torrent"),
|
||||||
|
ManagedTorrentState::Error(_) => false,
|
||||||
|
ManagedTorrentState::None => false,
|
||||||
|
ManagedTorrentState::Paused(p) => {
|
||||||
|
p.update_only_files(only_files)?;
|
||||||
|
false
|
||||||
|
}
|
||||||
|
ManagedTorrentState::Live(l) => {
|
||||||
|
let mut p = l.pause()?;
|
||||||
|
let e = p.update_only_files(only_files);
|
||||||
|
g.state = ManagedTorrentState::Paused(p);
|
||||||
|
e?;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
g.only_files = Some(only_files.iter().copied().collect());
|
||||||
|
Ok(need_to_unpause)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ManagedTorrentBuilder {
|
pub struct ManagedTorrentBuilder {
|
||||||
|
|
@ -507,9 +546,9 @@ impl ManagedTorrentBuilder {
|
||||||
self.only_files.clone(),
|
self.only_files.clone(),
|
||||||
));
|
));
|
||||||
Ok(Arc::new(ManagedTorrent {
|
Ok(Arc::new(ManagedTorrent {
|
||||||
only_files: self.only_files,
|
|
||||||
locked: RwLock::new(ManagedTorrentLocked {
|
locked: RwLock::new(ManagedTorrentLocked {
|
||||||
state: ManagedTorrentState::Initializing(initializing),
|
state: ManagedTorrentState::Initializing(initializing),
|
||||||
|
only_files: self.only_files,
|
||||||
}),
|
}),
|
||||||
info,
|
info,
|
||||||
}))
|
}))
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{fs::File, path::PathBuf, sync::Arc};
|
use std::{collections::HashSet, fs::File, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
|
@ -15,11 +15,13 @@ pub struct TorrentStatePaused {
|
||||||
pub(crate) needed_bytes: u64,
|
pub(crate) needed_bytes: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
// impl TorrentStatePaused {
|
impl TorrentStatePaused {
|
||||||
// pub fn get_have_bytes(&self) -> u64 {
|
pub(crate) fn update_only_files(&mut self, only_files: &HashSet<usize>) -> anyhow::Result<()> {
|
||||||
// self.have_bytes
|
let hn = self
|
||||||
// }
|
.chunk_tracker
|
||||||
// pub fn get_needed_bytes(&self) -> u64 {
|
.update_only_files(self.info.info.iter_file_lengths()?, only_files)?;
|
||||||
// self.needed_bytes
|
self.have_bytes = hn.have_bytes;
|
||||||
// }
|
self.needed_bytes = hn.needed_bytes;
|
||||||
// }
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue