use std::fmt::Write as _;
pub async fn cmd_clean(
global: &crate::Global,
dry_run: bool,
) -> anyhow::Result<()> {
#[derive(Debug,serde::Deserialize)]
struct Sync {
server_state: ServerState,
}
#[derive(Debug,serde::Deserialize)]
struct ServerState {
free_space_on_disk: u64,
}
let system = global.http.get(global.qbt.join("/api/v2/sync/maindata")?)
.send().await?
.error_for_status()?
.json::<Sync>().await?;
let Some(to_free) = global.config.target_free.checked_sub(system.server_state.free_space_on_disk)
else {
eprintln!(
"Have {} free which satisfies target_free of {}, nothing to do.",
crate::FmtBytes(system.server_state.free_space_on_disk),
crate::FmtBytes(global.config.target_free));
return Ok(())
};
eprintln!(
"Going to free {} to hit target of {} free space.",
crate::FmtBytes(to_free),
crate::FmtBytes(global.config.target_free));
let now = std::time::SystemTime::now();
let mut groups = global.group(None).await?
.filter(|g| g.pinned.is_none())
.collect::<Vec<_>>();
groups.sort_unstable_by_key(|g| {
std::cmp::Reverse(crate::F64Ord(g.rank(&global)))
});
let mut groups = groups.into_iter();
let mut removed_bytes = 0u64;
let mut removed_groups = 0u64;
let mut removed_torrents = 0u64;
let mut hashes = String::new();
for g in &mut groups {
removed_bytes += g.content_size();
removed_groups += 1;
eprintln!(
"--- group {}, torrents: {} ---",
crate::FmtBytes(g.group_id()),
g.torrents.len());
for (i, t) in g.torrents.into_iter().enumerate() {
let last_active = now.duration_since(t.torrent.last_activity)
.unwrap_or(std::time::Duration::ZERO);
eprintln!(
"DELETE {} {:?} category: {}, tracker: {:?}, seeders: {}, active: {} ratio: {:.02}",
t.torrent.hash,
t.torrent.name,
t.torrent.category,
t.torrent.tracker_host().as_deref().unwrap_or(&t.torrent.tracker),
t.torrent.seeders,
crate::FmtDuration(last_active),
t.torrent.ratio);
if i == 0 {
hashes.clear();
} else {
hashes.push('|');
}
write!(&mut hashes, "{}", t.torrent.hash)?;
removed_torrents += 1;
}
if !dry_run {
global.http.post(global.qbt.join("/api/v2/torrents/delete")?)
.form(&[
("deleteFiles", "true"),
("hashes", &hashes),
])
.send().await?
.error_for_status()?;
}
if removed_bytes >= to_free { break }
}
eprintln!(
"Deleted {} torrents in {} groups. Aprox {} freed.",
removed_torrents,
removed_groups,
crate::FmtBytes(removed_bytes));
if to_free > removed_bytes {
eprintln!("No more eligible torrents to reach free space target.");
} else {
let remaining = groups.map(|g| g.content_size()).sum();
eprintln!("Free space target met. {} still available to clean.", crate::FmtBytes(remaining));
}
Ok(())
}