Add implementer for qbittorrent

This commit is contained in:
seanomik 2022-06-20 15:49:38 -04:00
parent 34999380db
commit 207f8b1c94
4 changed files with 167 additions and 56 deletions

@ -1 +1 @@
Subproject commit 4c907b940e6a4eaeee0c92581b62173d918b054a Subproject commit d0e087af2b378025416a91085934f396886f3456

View File

@ -1,21 +1,27 @@
pub mod qbittorrent;
use async_trait::async_trait; use async_trait::async_trait;
use crate::{torrent::{TorrentInfo, TorrentTracker}, error::ClientError}; use crate::{torrent::{TorrentInfo, TorrentTracker, TorrentUpload}, error::ClientError};
pub type ClientResult<T> = Result<T, ClientError>; pub type ClientResult<T> = Result<T, ClientError>;
#[async_trait] #[async_trait]
pub trait TorrentClient { pub trait TorrentClient<'a> {
async fn login(&self, url: &str, username: &str, password: &str) -> ClientResult<()>; async fn login(&mut self, url: &'a str, username: &'a str, password: &'a str) -> ClientResult<()>;
async fn get_torrent_list(&self) -> ClientResult<Vec<TorrentInfo>>; async fn get_torrent_list(&self) -> 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 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<()>;
async fn add_torrent(&self, torrent: &TorrentUpload) -> ClientResult<()>;
async fn add_torrent(&self, torrent: &TorrentInfo) -> ClientResult<()>;
async fn remove_torrent(&self, torrent: &TorrentInfo, delete_files: bool) -> ClientResult<()>; async fn remove_torrent(&self, torrent: &TorrentInfo, delete_files: bool) -> ClientResult<()>;
async fn remove_torrents(&self, torrents: Vec<TorrentInfo>, delete_files: bool) -> ClientResult<()>;
async fn get_tags(&self) -> ClientResult<Vec<String>>;
async fn create_tag(&self, tag: &str) -> ClientResult<()>;
async fn delete_tag(&self, tag: &str) -> ClientResult<()>;
} }

144
src/client/qbittorrent.rs Normal file
View File

@ -0,0 +1,144 @@
use crate::torrent::{TorrentInfo, TorrentTracker, TorrentUpload};
use super::{TorrentClient, ClientResult};
use qbittorrent::QBittorrentClient;
use async_trait::async_trait;
impl From<&qbittorrent::TorrentInfo> for TorrentInfo {
fn from(torrent: &qbittorrent::TorrentInfo) -> Self {
TorrentInfo {
name: torrent.name.clone(),
trackers: vec![torrent.tracker.clone()], // NOTE: qBittorrent only gives us one tracker.
category: torrent.category.clone(),
tags: torrent.tags.clone(),
hash: torrent.hash.clone(),
}
}
}
// For some reason I have to implement this twice smh
impl Into<qbittorrent::TorrentInfo> for &TorrentInfo {
fn into(self) -> qbittorrent::TorrentInfo {
let mut t = qbittorrent::TorrentInfo::default();
t.name = self.name.clone();
t.tracker = self.trackers.first().unwrap_or(&String::new()).clone();
t.category = self.category.clone();
t.tags = self.tags.clone();
t.hash = self.hash.clone();
t
}
}
// For some reason I have to implement this twice smh
impl Into<qbittorrent::TorrentInfo> for TorrentInfo {
fn into(self) -> qbittorrent::TorrentInfo {
let mut t = qbittorrent::TorrentInfo::default();
t.name = self.name.clone();
t.tracker = self.trackers.first().unwrap_or(&String::new()).clone();
t.category = self.category.clone();
t.tags = self.tags.clone();
t.hash = self.hash.clone();
t
}
}
impl From<qbittorrent::TorrentUpload> for TorrentUpload {
fn from(upload: qbittorrent::TorrentUpload) -> Self {
TorrentUpload {
urls: upload.urls,
torrents: upload.torrents,
tags: upload.tags,
category: upload.category,
paused: upload.paused,
}
}
}
impl Into<qbittorrent::TorrentUpload> for &TorrentUpload {
fn into(self) -> qbittorrent::TorrentUpload {
let mut t = qbittorrent::TorrentUpload::default();
t.urls = self.urls.clone();
t.torrents = self.torrents.clone();
t.tags = self.tags.clone();
t.category = self.category.clone();
t.paused = self.paused;
t
}
}
impl From<&qbittorrent::TorrentTracker> for TorrentTracker {
fn from(tracker: &qbittorrent::TorrentTracker) -> Self {
TorrentTracker {
url: tracker.url.clone(),
status: tracker.status.clone().into(),
message: Some(tracker.message.clone()),
}
}
}
impl From<qbittorrent::TrackerStatus> for crate::torrent::TrackerStatus {
fn from(status: qbittorrent::TrackerStatus) -> Self {
match status {
qbittorrent::TrackerStatus::Disabled => crate::torrent::TrackerStatus::Disabled,
qbittorrent::TrackerStatus::NotContacted => crate::torrent::TrackerStatus::NotContacted,
qbittorrent::TrackerStatus::Working => crate::torrent::TrackerStatus::Working,
qbittorrent::TrackerStatus::Updating => crate::torrent::TrackerStatus::Updating,
qbittorrent::TrackerStatus::NotWorking => crate::torrent::TrackerStatus::NotWorking,
}
}
}
#[async_trait]
impl<'a> TorrentClient<'a> for QBittorrentClient<'a> {
async fn login(&mut self, url: &'a str, username: &'a str, password: &'a str) -> ClientResult<()> {
Ok(Self::login(&mut self, url, username, password).await?)
}
async fn get_torrent_list(&self) -> ClientResult<Vec<TorrentInfo>> {
Ok(Self::get_torrent_list(self).await?.iter().map(|t| t.into()).collect())
}
async fn get_torrent_trackers(&self, torrent: &TorrentInfo) -> ClientResult<Vec<TorrentTracker>> {
Ok(Self::get_torrent_trackers(self, &torrent.into()).await?.iter().map(|t| t.into()).collect())
}
async fn add_torrent_tracker(&self, torrent: &TorrentInfo, tracker_url: String) -> ClientResult<()> {
Ok(Self::add_torrent_tracker(self, &torrent.into(), tracker_url).await?)
}
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?)
}
async fn remove_torrent_tracker(&self, torrent: &TorrentInfo, tracker_url: String) -> ClientResult<()> {
Ok(Self::remove_torrent_tracker(self, &torrent.into(), tracker_url).await?)
}
async fn add_torrent(&self, torrent: &TorrentUpload) -> ClientResult<()> {
Ok(Self::add_torrent(self, &torrent.into()).await?)
}
async fn remove_torrent(&self, torrent: &TorrentInfo, delete_files: bool) -> ClientResult<()> {
Ok(Self::remove_torrent(self, &torrent.into(), delete_files).await?)
}
async fn remove_torrents(&self, torrents: Vec<TorrentInfo>, delete_files: bool) -> ClientResult<()> {
let torrents: Vec<qbittorrent::TorrentInfo> = torrents.iter().map(|t| t.into()).collect();
Ok(Self::remove_torrents(self, torrents, delete_files).await?)
}
async fn get_tags(&self) -> ClientResult<Vec<String>> {
Ok(Self::get_tags(self).await?)
}
async fn create_tag(&self, tag: &str) -> ClientResult<()> {
Ok(Self::create_tag(self, tag).await?)
}
async fn delete_tag(&self, tag: &str) -> ClientResult<()> {
Ok(Self::delete_tag(self, tag).await?)
}
}

View File

@ -1,6 +1,3 @@
use qbittorrent::{TorrentInfo as QTorrentInfo, TorrentUpload as QTorrentUpload,
TrackerStatus as QTrackerStatus, TorrentTracker as QTorrentTracker};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct TorrentInfo { pub struct TorrentInfo {
pub name: String, pub name: String,
@ -10,19 +7,7 @@ pub struct TorrentInfo {
pub hash: String, pub hash: String,
} }
impl From<QTorrentInfo> for TorrentInfo { #[derive(Debug, Default, Clone)]
fn from(torrent: QTorrentInfo) -> Self {
TorrentInfo {
name: torrent.name,
trackers: vec![torrent.tracker], // NOTE: qBittorrent only gives us one tracker.
category: torrent.category,
tags: torrent.tags,
hash: torrent.hash,
}
}
}
#[derive(Debug, Default)]
pub struct TorrentUpload { pub struct TorrentUpload {
/// URL(s) of the torrent files. /// URL(s) of the torrent files.
pub urls: Vec<String>, pub urls: Vec<String>,
@ -38,15 +23,9 @@ pub struct TorrentUpload {
pub paused: Option<bool>, pub paused: Option<bool>,
} }
impl From<QTorrentUpload> for TorrentUpload { impl TorrentUpload {
fn from(upload: QTorrentUpload) -> Self { pub fn builder() -> TorrentUploadBuilder {
TorrentUpload { TorrentUploadBuilder::default()
urls: upload.urls,
torrents: upload.torrents,
tags: upload.tags,
category: upload.category,
paused: upload.paused,
}
} }
} }
@ -106,6 +85,10 @@ impl TorrentUploadBuilder {
self.params.paused = Some(paused); self.params.paused = Some(paused);
self self
} }
pub fn build(&self) -> TorrentUpload {
self.params.clone()
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -138,26 +121,4 @@ pub enum TrackerStatus {
/// Tracker has been contacted, but it is not working (or doesn't send proper replies) /// Tracker has been contacted, but it is not working (or doesn't send proper replies)
NotWorking = 4 NotWorking = 4
}
impl From<QTorrentTracker> for TorrentTracker {
fn from(tracker: QTorrentTracker) -> Self {
TorrentTracker {
url: tracker.url,
status: tracker.status.into(),
message: Some(tracker.message),
}
}
}
impl From<QTrackerStatus> for TrackerStatus {
fn from(status: QTrackerStatus) -> Self {
match status {
QTrackerStatus::Disabled => TrackerStatus::Disabled,
QTrackerStatus::NotContacted => TrackerStatus::NotContacted,
QTrackerStatus::Working => TrackerStatus::Working,
QTrackerStatus::Updating => TrackerStatus::Updating,
QTrackerStatus::NotWorking => TrackerStatus::NotWorking,
}
}
} }