Add `TorrentState` enum, update qbittorrent, bugfixes

This commit is contained in:
seanomik 2022-06-22 22:05:52 -04:00
parent edf45342fb
commit 2b4cd84d7f
7 changed files with 118 additions and 36 deletions

1
Cargo.lock generated
View File

@ -599,7 +599,6 @@ dependencies = [
"serde_json", "serde_json",
"serde_repr", "serde_repr",
"serde_with", "serde_with",
"tokio",
] ]
[[package]] [[package]]

@ -1 +1 @@
Subproject commit e3e9e26be49aeb34b3985dafbfd043ba6fcc51e2 Subproject commit c3395926894fda44c6a203f8293beac609cabb5e

View File

@ -1,4 +1,7 @@
pub mod qbittorrent; mod qbittorrent_client;
pub use qbittorrent_client::*;
use crate::common;
use async_trait::async_trait; use async_trait::async_trait;
@ -10,10 +13,11 @@ pub type ClientResult<T> = Result<T, ClientError>;
pub trait TorrentClient { pub trait TorrentClient {
async fn login(&mut self, url: &str, username: &str, password: &str) -> ClientResult<()>; async fn login(&mut self, url: &str, username: &str, password: &str) -> ClientResult<()>;
async fn get_torrent_list(&self) -> ClientResult<Vec<TorrentInfo>>; async fn get_torrent_list(&self, params: Option<common::GetTorrentListParams>) -> ClientResult<Vec<TorrentInfo>>;
async fn get_torrent_trackers(&self, torrent: &TorrentInfo) -> ClientResult<Vec<TorrentTracker>>; async fn get_torrent_trackers(&self, torrent: &TorrentInfo) -> ClientResult<Vec<TorrentTracker>>;
async fn add_torrent_tracker(&self, torrent: &TorrentInfo, tracker_url: String) -> ClientResult<()>; async fn add_torrent_tracker(&self, torrent: &TorrentInfo, tracker_url: String) -> ClientResult<()>;
async fn add_torrent_trackers(&self, torrent: &TorrentInfo, trackers: Vec<String>) -> ClientResult<()>;
async fn replace_torrent_tracker(&self, torrent: &TorrentInfo, old_url: String, new_url: String) -> ClientResult<()>; async fn replace_torrent_tracker(&self, torrent: &TorrentInfo, old_url: String, new_url: String) -> ClientResult<()>;
async fn remove_torrent_tracker(&self, torrent: &TorrentInfo, tracker_url: String) -> ClientResult<()>; async fn remove_torrent_tracker(&self, torrent: &TorrentInfo, tracker_url: String) -> ClientResult<()>;

View File

@ -1,25 +1,29 @@
use crate::torrent::{TorrentInfo, TorrentTracker, TorrentUpload}; use crate::torrent::{TorrentInfo, TorrentTracker, TorrentUpload};
use super::{TorrentClient, ClientResult}; use super::{TorrentClient, ClientResult};
use qbittorrent::QBittorrentClient;
pub use qbittorrent;
use qbittorrent::{client::QBittorrentClient, common::GetTorrentListParams};
use async_trait::async_trait; use async_trait::async_trait;
impl From<&qbittorrent::TorrentInfo> for TorrentInfo { impl From<&qbittorrent::torrent::TorrentInfo> for TorrentInfo {
fn from(torrent: &qbittorrent::TorrentInfo) -> Self { fn from(torrent: &qbittorrent::torrent::TorrentInfo) -> Self {
TorrentInfo { TorrentInfo {
name: torrent.name.clone(), name: torrent.name.clone(),
trackers: vec![torrent.tracker.clone()], // NOTE: qBittorrent only gives us one tracker. trackers: vec![torrent.tracker.clone()], // NOTE: qBittorrent only gives us one tracker.
category: torrent.category.clone(), category: torrent.category.clone(),
tags: torrent.tags.clone(), tags: torrent.tags.clone(),
hash: torrent.hash.clone(), hash: torrent.hash.clone(),
state: torrent.state.clone().into(),
} }
} }
} }
// For some reason I have to implement this twice smh // For some reason I have to implement this twice smh
impl Into<qbittorrent::TorrentInfo> for &TorrentInfo { impl Into<qbittorrent::torrent::TorrentInfo> for &TorrentInfo {
fn into(self) -> qbittorrent::TorrentInfo { fn into(self) -> qbittorrent::torrent::TorrentInfo {
let mut t = qbittorrent::TorrentInfo::default(); let mut t = qbittorrent::torrent::TorrentInfo::default();
t.name = self.name.clone(); t.name = self.name.clone();
t.tracker = self.trackers.first().unwrap_or(&String::new()).clone(); t.tracker = self.trackers.first().unwrap_or(&String::new()).clone();
t.category = self.category.clone(); t.category = self.category.clone();
@ -31,9 +35,9 @@ impl Into<qbittorrent::TorrentInfo> for &TorrentInfo {
} }
// For some reason I have to implement this twice smh // For some reason I have to implement this twice smh
impl Into<qbittorrent::TorrentInfo> for TorrentInfo { impl Into<qbittorrent::torrent::TorrentInfo> for TorrentInfo {
fn into(self) -> qbittorrent::TorrentInfo { fn into(self) -> qbittorrent::torrent::TorrentInfo {
let mut t = qbittorrent::TorrentInfo::default(); let mut t = qbittorrent::torrent::TorrentInfo::default();
t.name = self.name.clone(); t.name = self.name.clone();
t.tracker = self.trackers.first().unwrap_or(&String::new()).clone(); t.tracker = self.trackers.first().unwrap_or(&String::new()).clone();
t.category = self.category.clone(); t.category = self.category.clone();
@ -44,8 +48,8 @@ impl Into<qbittorrent::TorrentInfo> for TorrentInfo {
} }
} }
impl From<qbittorrent::TorrentUpload> for TorrentUpload { impl From<qbittorrent::torrent::TorrentUpload> for TorrentUpload {
fn from(upload: qbittorrent::TorrentUpload) -> Self { fn from(upload: qbittorrent::torrent::TorrentUpload) -> Self {
TorrentUpload { TorrentUpload {
urls: upload.urls, urls: upload.urls,
torrents: upload.torrents, torrents: upload.torrents,
@ -56,9 +60,9 @@ impl From<qbittorrent::TorrentUpload> for TorrentUpload {
} }
} }
impl Into<qbittorrent::TorrentUpload> for &TorrentUpload { impl Into<qbittorrent::torrent::TorrentUpload> for &TorrentUpload {
fn into(self) -> qbittorrent::TorrentUpload { fn into(self) -> qbittorrent::torrent::TorrentUpload {
let mut t = qbittorrent::TorrentUpload::default(); let mut t = qbittorrent::torrent::TorrentUpload::default();
t.urls = self.urls.clone(); t.urls = self.urls.clone();
t.torrents = self.torrents.clone(); t.torrents = self.torrents.clone();
t.tags = self.tags.clone(); t.tags = self.tags.clone();
@ -69,8 +73,8 @@ impl Into<qbittorrent::TorrentUpload> for &TorrentUpload {
} }
} }
impl From<&qbittorrent::TorrentTracker> for TorrentTracker { impl From<&qbittorrent::torrent::TorrentTracker> for TorrentTracker {
fn from(tracker: &qbittorrent::TorrentTracker) -> Self { fn from(tracker: &qbittorrent::torrent::TorrentTracker) -> Self {
TorrentTracker { TorrentTracker {
url: tracker.url.clone(), url: tracker.url.clone(),
status: tracker.status.clone().into(), status: tracker.status.clone().into(),
@ -79,14 +83,36 @@ impl From<&qbittorrent::TorrentTracker> for TorrentTracker {
} }
} }
impl From<qbittorrent::TrackerStatus> for crate::torrent::TrackerStatus { impl From<qbittorrent::torrent::TrackerStatus> for crate::torrent::TrackerStatus {
fn from(status: qbittorrent::TrackerStatus) -> Self { fn from(status: qbittorrent::torrent::TrackerStatus) -> Self {
use qbittorrent::torrent::TrackerStatus as QBStatus;
match status { match status {
qbittorrent::TrackerStatus::Disabled => crate::torrent::TrackerStatus::Disabled, QBStatus::Disabled => Self::Disabled,
qbittorrent::TrackerStatus::NotContacted => crate::torrent::TrackerStatus::NotContacted, QBStatus::NotContacted => Self::NotContacted,
qbittorrent::TrackerStatus::Working => crate::torrent::TrackerStatus::Working, QBStatus::Working => Self::Working,
qbittorrent::TrackerStatus::Updating => crate::torrent::TrackerStatus::Updating, QBStatus::Updating => Self::Updating,
qbittorrent::TrackerStatus::NotWorking => crate::torrent::TrackerStatus::NotWorking, QBStatus::NotWorking => Self::NotWorking,
}
}
}
impl From<qbittorrent::torrent::TorrentState> for crate::torrent::TorrentState {
fn from(state: qbittorrent::torrent::TorrentState) -> Self {
use qbittorrent::torrent::TorrentState as QBState;
match state {
QBState::Error => Self::Error,
QBState::MissingFiles => Self::MissingFiles,
QBState::Uploading | QBState::StalledUP | QBState::ForcedUP | QBState::CheckingUP => Self::Uploading,
QBState::PausedUP => Self::PausedUploading,
QBState::QueuedUP => Self::QueuedUploading,
QBState::Downloading | QBState::StalledDL | QBState::ForcedDL |
QBState::Allocating | QBState::MetaDownloading | QBState::CheckingDL |
QBState::CheckingResumeData | QBState::Moving => Self::Downloading,
QBState::PausedDL => Self::PausedDownloading,
QBState::QueuedDL => Self::QueuedDownloading,
QBState::Unknown => Self::Unknown,
} }
} }
} }
@ -97,8 +123,8 @@ impl TorrentClient for QBittorrentClient {
Ok(Self::login(&mut self, url, username, password).await?) Ok(Self::login(&mut self, url, username, password).await?)
} }
async fn get_torrent_list(&self) -> ClientResult<Vec<TorrentInfo>> { async fn get_torrent_list(&self, params: Option<GetTorrentListParams>) -> ClientResult<Vec<TorrentInfo>> {
Ok(Self::get_torrent_list(self).await?.iter().map(|t| t.into()).collect()) Ok(Self::get_torrent_list(self, params).await?.iter().map(|t| t.into()).collect())
} }
async fn get_torrent_trackers(&self, torrent: &TorrentInfo) -> ClientResult<Vec<TorrentTracker>> { async fn get_torrent_trackers(&self, torrent: &TorrentInfo) -> ClientResult<Vec<TorrentTracker>> {
@ -109,6 +135,10 @@ impl TorrentClient for QBittorrentClient {
Ok(Self::add_torrent_tracker(self, &torrent.into(), tracker_url).await?) Ok(Self::add_torrent_tracker(self, &torrent.into(), tracker_url).await?)
} }
async fn add_torrent_trackers(&self, torrent: &TorrentInfo, trackers: Vec<String>) -> ClientResult<()> {
Ok(Self::add_torrent_trackers(self, &torrent.into(), trackers).await?)
}
async fn replace_torrent_tracker(&self, torrent: &TorrentInfo, old_url: String, new_url: String) -> ClientResult<()> { async fn replace_torrent_tracker(&self, torrent: &TorrentInfo, old_url: String, new_url: String) -> ClientResult<()> {
Ok(Self::replace_torrent_tracker(self, &torrent.into(), old_url, new_url).await?) Ok(Self::replace_torrent_tracker(self, &torrent.into(), old_url, new_url).await?)
} }
@ -126,7 +156,7 @@ impl TorrentClient for QBittorrentClient {
} }
async fn remove_torrents(&self, torrents: Vec<TorrentInfo>, delete_files: bool) -> ClientResult<()> { async fn remove_torrents(&self, torrents: Vec<TorrentInfo>, delete_files: bool) -> ClientResult<()> {
let torrents: Vec<qbittorrent::TorrentInfo> = torrents.iter().map(|t| t.into()).collect(); let torrents: Vec<qbittorrent::torrent::TorrentInfo> = torrents.iter().map(|t| t.into()).collect();
Ok(Self::remove_torrents(self, torrents, delete_files).await?) Ok(Self::remove_torrents(self, torrents, delete_files).await?)
} }

View File

@ -5,13 +5,13 @@ use qbittorrent::error::ClientError as QClientError;
#[derive(Debug)] #[derive(Debug)]
pub enum ClientError { pub enum ClientError {
/// Http error /// Http error
Http(Box<dyn Error>), Http(Box<dyn Error + Send + Sync>),
/// Authorization error /// Authorization error
Authorization, Authorization,
/// Parsing error (json for qBittorrent) /// Parsing error (json for qBittorrent)
Parsing(Box<dyn Error>), Parsing(Box<dyn Error + Send + Sync>),
} }
impl From<QClientError> for ClientError { impl From<QClientError> for ClientError {

View File

@ -5,3 +5,5 @@ pub mod client;
//pub use client::*; //pub use client::*;
pub mod error; pub mod error;
pub use qbittorrent::common;

View File

@ -1,10 +1,56 @@
#[derive(Debug, Default)] #[derive(Debug, Default, Clone)]
pub struct TorrentInfo { pub struct TorrentInfo {
pub name: String, pub name: String,
pub trackers: Vec<String>, pub trackers: Vec<String>,
pub category: String, pub category: String,
pub tags: Vec<String>, pub tags: Vec<String>,
pub hash: String, pub hash: String,
pub state: TorrentState,
}
impl TorrentInfo {
pub fn from_hash(hash: String) -> TorrentInfo {
let mut def = TorrentInfo::default();
def.hash = hash;
def
}
}
#[derive(Debug, Clone)]
pub enum TorrentState {
/// Some error occurred
Error,
/// Torrent data files is missing
MissingFiles,
/// Torrent is being seeded and data is being transferred
Uploading,
/// Torrent is paused but in a seeding state.
PausedUploading,
/// Torrent is queued but in a seeding state.
QueuedUploading,
/// Torrent is being downloaded and data is being transferred
Downloading,
/// Torrent is paused but in a downloading state.
PausedDownloading,
/// Torrent is queued but in a downloading state.
QueuedDownloading,
/// The state is unknown
Unknown,
}
impl Default for TorrentState {
fn default() -> Self {
Self::Unknown
}
} }
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
@ -81,8 +127,9 @@ impl TorrentUploadBuilder {
self self
} }
pub fn paused(&mut self, paused: bool) -> &mut Self { /// Set the upload to be paused by default.
self.params.paused = Some(paused); pub fn paused(&mut self) -> &mut Self {
self.params.paused = Some(true);
self self
} }