Well, it doesnt crash at least

This commit is contained in:
Igor Katson 2024-03-30 17:55:43 +00:00
parent 276a1e175e
commit efcffdd072
6 changed files with 101 additions and 21 deletions

View file

@ -1,4 +1,4 @@
use std::{net::SocketAddr, sync::Arc};
use std::{collections::HashSet, net::SocketAddr, sync::Arc};
use anyhow::Context;
use buffers::ByteBufOwned;
@ -122,6 +122,18 @@ impl Api {
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> {
let tx = self
.rust_log_reload_tx

View file

@ -1,11 +1,7 @@
use std::collections::HashSet;
use anyhow::Context;
use buffers::ByteBufOwned;
use librqbit_core::{
lengths::{ChunkInfo, Lengths, ValidPieceIndex},
torrent_metainfo::{TorrentMetaV1Info, TorrentMetaV1Owned},
};
use librqbit_core::lengths::{ChunkInfo, Lengths, ValidPieceIndex};
use peer_binary_protocol::Piece;
use tracing::{debug, trace};

View file

@ -181,6 +181,21 @@ impl HttpApi {
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(
State(state): State<ApiState>,
new_value: String,
@ -215,7 +230,11 @@ impl HttpApi {
.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("/torrents/:id/delete", post(torrent_action_delete))
.route(
"/torrents/:id/update_only_files",
post(torrent_action_update_only_files),
);
}
#[cfg(feature = "webui")]

View file

@ -107,7 +107,7 @@ impl SessionDatabase {
.collect(),
info_hash: torrent.info_hash().as_string(),
info: torrent.info().info.clone(),
only_files: torrent.only_files.clone(),
only_files: torrent.only_files().clone(),
is_paused: torrent
.with_state(|s| matches!(s, ManagedTorrentState::Paused(_))),
output_folder: torrent.info().out_dir.clone(),
@ -1137,6 +1137,18 @@ impl Session {
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> {
self.tcp_listen_port
}

View file

@ -67,6 +67,7 @@ impl ManagedTorrentState {
pub(crate) struct ManagedTorrentLocked {
pub state: ManagedTorrentState,
pub(crate) only_files: Option<Vec<usize>>,
}
#[derive(Default)]
@ -91,7 +92,6 @@ pub struct ManagedTorrentInfo {
pub struct ManagedTorrent {
pub info: Arc<ManagedTorrentInfo>,
pub(crate) only_files: Option<Vec<usize>>,
locked: RwLock<ManagedTorrentLocked>,
}
@ -109,7 +109,7 @@ impl ManagedTorrent {
}
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 {
@ -298,7 +298,7 @@ impl ManagedTorrent {
ManagedTorrentState::Error(_) => {
let initializing = Arc::new(TorrentStateInitializing::new(
self.info.clone(),
self.only_files.clone(),
g.only_files.clone(),
));
g.state = ManagedTorrentState::Initializing(initializing.clone());
drop(g);
@ -407,6 +407,45 @@ impl ManagedTorrent {
}
.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 {
@ -507,9 +546,9 @@ impl ManagedTorrentBuilder {
self.only_files.clone(),
));
Ok(Arc::new(ManagedTorrent {
only_files: self.only_files,
locked: RwLock::new(ManagedTorrentLocked {
state: ManagedTorrentState::Initializing(initializing),
only_files: self.only_files,
}),
info,
}))

View file

@ -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;
@ -15,11 +15,13 @@ pub struct TorrentStatePaused {
pub(crate) needed_bytes: u64,
}
// impl TorrentStatePaused {
// pub fn get_have_bytes(&self) -> u64 {
// self.have_bytes
// }
// pub fn get_needed_bytes(&self) -> u64 {
// self.needed_bytes
// }
// }
impl TorrentStatePaused {
pub(crate) fn update_only_files(&mut self, only_files: &HashSet<usize>) -> anyhow::Result<()> {
let hn = self
.chunk_tracker
.update_only_files(self.info.info.iter_file_lengths()?, only_files)?;
self.have_bytes = hn.have_bytes;
self.needed_bytes = hn.needed_bytes;
Ok(())
}
}