Start creating the TorrentClient trait and other structs around it

This commit is contained in:
SeanOMik 2022-06-19 23:46:52 -04:00
parent 20e061a2e5
commit 34999380db
9 changed files with 1478 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
.vscode

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "qbittorrent-rs"]
path = qbittorrent-rs
url = git@github.com:SeanOMik/qbittorrent-rs.git

1246
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

10
Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[package]
name = "abstracttorrent"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-trait = "0.1.56"
qbittorrent = { path = "qbittorrent-rs" }

1
qbittorrent-rs Submodule

@ -0,0 +1 @@
Subproject commit 4c907b940e6a4eaeee0c92581b62173d918b054a

21
src/client/mod.rs Normal file
View File

@ -0,0 +1,21 @@
use async_trait::async_trait;
use crate::{torrent::{TorrentInfo, TorrentTracker}, error::ClientError};
pub type ClientResult<T> = Result<T, ClientError>;
#[async_trait]
pub trait TorrentClient {
async fn login(&self, url: &str, username: &str, password: &str) -> ClientResult<()>;
async fn get_torrent_list(&self) -> ClientResult<Vec<TorrentInfo>>;
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 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 add_torrent(&self, torrent: &TorrentInfo) -> ClientResult<()>;
async fn remove_torrent(&self, torrent: &TorrentInfo, delete_files: bool) -> ClientResult<()>;
}

25
src/error.rs Normal file
View File

@ -0,0 +1,25 @@
use std::error::Error;
use qbittorrent::error::ClientError as QClientError;
#[derive(Debug)]
pub enum ClientError {
/// Http error
Http(Box<dyn Error>),
/// Authorization error
Authorization,
/// Parsing error (json for qBittorrent)
Parsing(Box<dyn Error>),
}
impl From<QClientError> for ClientError {
fn from(err: QClientError) -> Self {
match err {
QClientError::Http(err) => ClientError::Http(Box::new(err)),
QClientError::Authorization => ClientError::Authorization,
QClientError::Json(err) => ClientError::Parsing(Box::new(err)),
}
}
}

7
src/lib.rs Normal file
View File

@ -0,0 +1,7 @@
pub mod torrent;
//pub use torrent::*;
pub mod client;
//pub use client::*;
pub mod error;

163
src/torrent.rs Normal file
View File

@ -0,0 +1,163 @@
use qbittorrent::{TorrentInfo as QTorrentInfo, TorrentUpload as QTorrentUpload,
TrackerStatus as QTrackerStatus, TorrentTracker as QTorrentTracker};
#[derive(Debug, Default)]
pub struct TorrentInfo {
pub name: String,
pub trackers: Vec<String>,
pub category: String,
pub tags: Vec<String>,
pub hash: String,
}
impl From<QTorrentInfo> for TorrentInfo {
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 {
/// URL(s) of the torrent files.
pub urls: Vec<String>,
/// Binary data of the torrents that are being added.
/// Torrent file data that is being added. (Name, Bytes)
pub torrents: Vec<(String, Vec<u8>)>,
pub tags: Option<Vec<String>>,
pub category: Option<String>,
pub paused: Option<bool>,
}
impl From<QTorrentUpload> for TorrentUpload {
fn from(upload: QTorrentUpload) -> Self {
TorrentUpload {
urls: upload.urls,
torrents: upload.torrents,
tags: upload.tags,
category: upload.category,
paused: upload.paused,
}
}
}
#[derive(Debug, Default)]
pub struct TorrentUploadBuilder {
params: TorrentUpload
}
impl TorrentUploadBuilder {
pub fn url(&mut self, url: &str) -> &mut Self {
self.params.urls.push(url.to_string());
self
}
pub fn torrent_file(&mut self, torrent_path: &str) -> &mut Self {
let path = std::path::Path::new(torrent_path);
self.torrent_path(path)
}
pub fn torrent_path(&mut self, torrent_path: &std::path::Path) -> &mut Self {
let torrents = &mut self.params.torrents;
torrents.push((
torrent_path.file_name().unwrap().to_str().unwrap().to_string(),
std::fs::read(torrent_path).unwrap(),
));
self
}
pub fn torrent_data(&mut self, filename: String, data: Vec<u8>) -> &mut Self {
let torrents = &mut self.params.torrents;
torrents.push((
filename,
data,
));
self
}
pub fn tags(&mut self, tags: Vec<String>) -> &mut Self {
self.params.tags = Some(tags);
self
}
pub fn tag(&mut self, tag: String) -> &mut Self {
self.params.tags.as_mut().unwrap_or(&mut vec![]).push(tag);
self
}
pub fn category(&mut self, category: String) -> &mut Self {
self.params.category = Some(category);
self
}
pub fn paused(&mut self, paused: bool) -> &mut Self {
self.params.paused = Some(paused);
self
}
}
#[derive(Debug)]
pub struct TorrentTracker {
/// Tracker URL
pub url: String,
/// Tracker status
pub status: TrackerStatus,
/// Tracker message (there is no way of knowing what this message is - it's up to tracker admins)
pub message: Option<String>,
}
/// An enum that represents the status of a tracker.
/// Some of these statuses may not be supported by other trackers.
#[derive(Debug, PartialEq)]
pub enum TrackerStatus {
/// Tracker is disabled (used for DHT, PeX, and LSD)
Disabled = 0,
/// Tracker has not been contacted yet
NotContacted = 1,
/// Tracker has been contacted and is working
Working = 2,
/// Tracker is updating
Updating = 3,
/// Tracker has been contacted, but it is not working (or doesn't send proper replies)
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,
}
}
}