refactor: split modules
This commit is contained in:
2
packages/downloader/src/bittorrent/defs.rs
Normal file
2
packages/downloader/src/bittorrent/defs.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub const BITTORRENT_MIME_TYPE: &str = "application/x-bittorrent";
|
||||
pub const MAGNET_SCHEMA: &str = "magnet";
|
||||
74
packages/downloader/src/bittorrent/downloader.rs
Normal file
74
packages/downloader/src/bittorrent/downloader.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
DownloaderError,
|
||||
bittorrent::task::{
|
||||
TorrentCreationTrait, TorrentHashTrait, TorrentStateTrait, TorrentTaskTrait,
|
||||
},
|
||||
core::{DownloadIdSelectorTrait, DownloadSelectorTrait, DownloadTaskTrait, DownloaderTrait},
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
pub trait TorrentDownloaderTrait: DownloaderTrait
|
||||
where
|
||||
Self::State: TorrentStateTrait,
|
||||
Self::Id: TorrentHashTrait,
|
||||
Self::Task: TorrentTaskTrait<State = Self::State, Id = Self::Id>,
|
||||
Self::Creation: TorrentCreationTrait<Task = Self::Task>,
|
||||
Self::Selector: DownloadSelectorTrait<Task = Self::Task, Id = Self::Id>,
|
||||
{
|
||||
type IdSelector: DownloadIdSelectorTrait<Task = Self::Task, Id = Self::Id>;
|
||||
|
||||
async fn pause_downloads(
|
||||
&self,
|
||||
selector: <Self as DownloaderTrait>::Selector,
|
||||
) -> Result<Self::IdSelector, DownloaderError> {
|
||||
let hashes = <Self as TorrentDownloaderTrait>::query_torrent_hashes(self, selector).await?;
|
||||
self.pause_torrents(hashes).await
|
||||
}
|
||||
|
||||
async fn resume_downloads(
|
||||
&self,
|
||||
selector: <Self as DownloaderTrait>::Selector,
|
||||
) -> Result<Self::IdSelector, DownloaderError> {
|
||||
let hashes = <Self as TorrentDownloaderTrait>::query_torrent_hashes(self, selector).await?;
|
||||
self.resume_torrents(hashes).await
|
||||
}
|
||||
async fn remove_downloads(
|
||||
&self,
|
||||
selector: <Self as DownloaderTrait>::Selector,
|
||||
) -> Result<Self::IdSelector, DownloaderError> {
|
||||
let hashes = <Self as TorrentDownloaderTrait>::query_torrent_hashes(self, selector).await?;
|
||||
self.remove_torrents(hashes).await
|
||||
}
|
||||
|
||||
async fn query_torrent_hashes(
|
||||
&self,
|
||||
selector: <Self as DownloaderTrait>::Selector,
|
||||
) -> Result<Self::IdSelector, DownloaderError> {
|
||||
let hashes = match selector.try_into_ids_only() {
|
||||
Ok(hashes) => Self::IdSelector::from_iter(hashes),
|
||||
Err(selector) => {
|
||||
let tasks = self.query_downloads(selector).await?;
|
||||
|
||||
Self::IdSelector::from_iter(tasks.into_iter().map(|s| s.into_id()))
|
||||
}
|
||||
};
|
||||
Ok(hashes)
|
||||
}
|
||||
|
||||
async fn pause_torrents(
|
||||
&self,
|
||||
hashes: Self::IdSelector,
|
||||
) -> Result<Self::IdSelector, DownloaderError>;
|
||||
|
||||
async fn resume_torrents(
|
||||
&self,
|
||||
hashes: Self::IdSelector,
|
||||
) -> Result<Self::IdSelector, DownloaderError>;
|
||||
|
||||
async fn remove_torrents(
|
||||
&self,
|
||||
hashes: Self::IdSelector,
|
||||
) -> Result<Self::IdSelector, DownloaderError>;
|
||||
}
|
||||
6
packages/downloader/src/bittorrent/mod.rs
Normal file
6
packages/downloader/src/bittorrent/mod.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
pub mod defs;
|
||||
pub mod downloader;
|
||||
pub mod source;
|
||||
pub mod task;
|
||||
|
||||
pub use defs::{BITTORRENT_MIME_TYPE, MAGNET_SCHEMA};
|
||||
224
packages/downloader/src/bittorrent/source.rs
Normal file
224
packages/downloader/src/bittorrent/source.rs
Normal file
@@ -0,0 +1,224 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
fmt::{Debug, Formatter},
|
||||
};
|
||||
|
||||
use bytes::Bytes;
|
||||
use fetch::{bytes::fetch_bytes, client::core::HttpClientTrait};
|
||||
use librqbit_core::{magnet::Magnet, torrent_metainfo, torrent_metainfo::TorrentMetaV1Owned};
|
||||
use snafu::ResultExt;
|
||||
use url::Url;
|
||||
use util::errors::AnyhowResultExt;
|
||||
|
||||
use super::defs::MAGNET_SCHEMA;
|
||||
use crate::errors::{DownloadFetchSnafu, DownloaderError, MagnetFormatSnafu, TorrentMetaSnafu};
|
||||
|
||||
pub trait HashTorrentSourceTrait: Sized {
|
||||
fn hash_info(&self) -> Cow<'_, str>;
|
||||
}
|
||||
|
||||
pub struct MagnetUrlSource {
|
||||
pub magnet: Magnet,
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
impl MagnetUrlSource {
|
||||
pub fn from_url(url: String) -> Result<Self, DownloaderError> {
|
||||
let magnet = Magnet::parse(&url)
|
||||
.to_dyn_boxed()
|
||||
.context(MagnetFormatSnafu {
|
||||
message: url.clone(),
|
||||
})?;
|
||||
|
||||
Ok(Self { magnet, url })
|
||||
}
|
||||
}
|
||||
|
||||
impl HashTorrentSourceTrait for MagnetUrlSource {
|
||||
fn hash_info(&self) -> Cow<'_, str> {
|
||||
let hash_info = self
|
||||
.magnet
|
||||
.as_id32()
|
||||
.map(|s| s.as_string())
|
||||
.or_else(|| self.magnet.as_id20().map(|s| s.as_string()))
|
||||
.unwrap_or_else(|| unreachable!("hash of magnet must existed"));
|
||||
hash_info.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for MagnetUrlSource {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("MagnetUrlSource")
|
||||
.field("url", &self.url)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for MagnetUrlSource {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
magnet: Magnet::parse(&self.url).unwrap(),
|
||||
url: self.url.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for MagnetUrlSource {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.url == other.url
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for MagnetUrlSource {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct TorrentUrlSource {
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
impl TorrentUrlSource {
|
||||
pub fn from_url(url: String) -> Result<Self, DownloaderError> {
|
||||
Ok(Self { url })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TorrentFileSource {
|
||||
pub url: Option<String>,
|
||||
pub payload: Bytes,
|
||||
pub meta: Box<TorrentMetaV1Owned>,
|
||||
pub filename: String,
|
||||
}
|
||||
|
||||
impl TorrentFileSource {
|
||||
pub fn from_bytes(
|
||||
filename: String,
|
||||
bytes: Bytes,
|
||||
url: Option<String>,
|
||||
) -> Result<Self, DownloaderError> {
|
||||
let meta = torrent_metainfo::torrent_from_bytes(bytes.as_ref())
|
||||
.to_dyn_boxed()
|
||||
.with_context(|_| TorrentMetaSnafu {
|
||||
message: format!(
|
||||
"filename = {}, url = {}",
|
||||
filename,
|
||||
url.as_deref().unwrap_or_default()
|
||||
),
|
||||
})?
|
||||
.to_owned();
|
||||
|
||||
Ok(TorrentFileSource {
|
||||
url,
|
||||
payload: bytes,
|
||||
meta: Box::new(meta),
|
||||
filename,
|
||||
})
|
||||
}
|
||||
pub async fn from_url_and_http_client(
|
||||
client: &impl HttpClientTrait,
|
||||
url: String,
|
||||
) -> Result<TorrentFileSource, DownloaderError> {
|
||||
let payload = fetch_bytes(client, &url)
|
||||
.await
|
||||
.boxed()
|
||||
.with_context(|_| DownloadFetchSnafu { url: url.clone() })?;
|
||||
|
||||
let filename = Url::parse(&url)
|
||||
.boxed()
|
||||
.and_then(|s| {
|
||||
s.path_segments()
|
||||
.and_then(|mut p| p.next_back())
|
||||
.map(String::from)
|
||||
.ok_or_else(|| anyhow::anyhow!("invalid url"))
|
||||
.to_dyn_boxed()
|
||||
})
|
||||
.with_context(|_| DownloadFetchSnafu { url: url.clone() })?;
|
||||
|
||||
Self::from_bytes(filename, payload, Some(url))
|
||||
}
|
||||
}
|
||||
|
||||
impl HashTorrentSourceTrait for TorrentFileSource {
|
||||
fn hash_info(&self) -> Cow<'_, str> {
|
||||
self.meta.info_hash.as_string().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for TorrentFileSource {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("TorrentFileSource")
|
||||
.field("hash", &self.meta.info_hash.as_string())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum UrlTorrentSource {
|
||||
MagnetUrl(MagnetUrlSource),
|
||||
TorrentUrl(TorrentUrlSource),
|
||||
}
|
||||
|
||||
impl UrlTorrentSource {
|
||||
pub fn from_url(url: String) -> Result<Self, DownloaderError> {
|
||||
let url_ = Url::parse(&url)?;
|
||||
let source = if url_.scheme() == MAGNET_SCHEMA {
|
||||
Self::from_magnet_url(url)?
|
||||
} else {
|
||||
Self::from_torrent_url(url)?
|
||||
};
|
||||
Ok(source)
|
||||
}
|
||||
|
||||
pub fn from_magnet_url(url: String) -> Result<Self, DownloaderError> {
|
||||
let magnet_source = MagnetUrlSource::from_url(url)?;
|
||||
Ok(Self::MagnetUrl(magnet_source))
|
||||
}
|
||||
|
||||
pub fn from_torrent_url(url: String) -> Result<Self, DownloaderError> {
|
||||
let torrent_source = TorrentUrlSource::from_url(url)?;
|
||||
Ok(Self::TorrentUrl(torrent_source))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum HashTorrentSource {
|
||||
MagnetUrl(MagnetUrlSource),
|
||||
TorrentFile(TorrentFileSource),
|
||||
}
|
||||
|
||||
impl HashTorrentSource {
|
||||
pub async fn from_url_and_http_client(
|
||||
client: &impl HttpClientTrait,
|
||||
url: String,
|
||||
) -> Result<Self, DownloaderError> {
|
||||
let url_ = Url::parse(&url)?;
|
||||
let source = if url_.scheme() == MAGNET_SCHEMA {
|
||||
Self::from_magnet_url(url)?
|
||||
} else {
|
||||
Self::from_torrent_url_and_http_client(client, url).await?
|
||||
};
|
||||
Ok(source)
|
||||
}
|
||||
|
||||
pub fn from_magnet_url(url: String) -> Result<Self, DownloaderError> {
|
||||
let magnet_source = MagnetUrlSource::from_url(url)?;
|
||||
Ok(Self::MagnetUrl(magnet_source))
|
||||
}
|
||||
|
||||
pub async fn from_torrent_url_and_http_client(
|
||||
client: &impl HttpClientTrait,
|
||||
url: String,
|
||||
) -> Result<Self, DownloaderError> {
|
||||
let torrent_source = TorrentFileSource::from_url_and_http_client(client, url).await?;
|
||||
Ok(Self::TorrentFile(torrent_source))
|
||||
}
|
||||
}
|
||||
|
||||
impl HashTorrentSourceTrait for HashTorrentSource {
|
||||
fn hash_info(&self) -> Cow<'_, str> {
|
||||
match self {
|
||||
HashTorrentSource::MagnetUrl(m) => m.hash_info(),
|
||||
HashTorrentSource::TorrentFile(t) => t.hash_info(),
|
||||
}
|
||||
}
|
||||
}
|
||||
43
packages/downloader/src/bittorrent/task.rs
Normal file
43
packages/downloader/src/bittorrent/task.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use std::{borrow::Cow, hash::Hash};
|
||||
|
||||
use quirks_path::{Path, PathBuf};
|
||||
|
||||
use crate::{
|
||||
bittorrent::source::HashTorrentSource,
|
||||
core::{DownloadCreationTrait, DownloadIdTrait, DownloadStateTrait, DownloadTaskTrait},
|
||||
};
|
||||
|
||||
pub const TORRENT_TAG_NAME: &str = "konobangu";
|
||||
|
||||
pub trait TorrentHashTrait: DownloadIdTrait + Send + Hash {}
|
||||
|
||||
pub type SimpleTorrentHash = String;
|
||||
|
||||
impl DownloadIdTrait for SimpleTorrentHash {}
|
||||
|
||||
impl TorrentHashTrait for SimpleTorrentHash {}
|
||||
|
||||
pub trait TorrentStateTrait: DownloadStateTrait {}
|
||||
|
||||
pub trait TorrentTaskTrait: DownloadTaskTrait
|
||||
where
|
||||
Self::State: TorrentStateTrait,
|
||||
Self::Id: TorrentHashTrait,
|
||||
{
|
||||
fn hash_info(&self) -> &str;
|
||||
fn name(&self) -> Cow<'_, str> {
|
||||
Cow::Borrowed(self.hash_info())
|
||||
}
|
||||
|
||||
fn tags(&self) -> impl Iterator<Item = Cow<'_, str>>;
|
||||
|
||||
fn category(&self) -> Option<Cow<'_, str>>;
|
||||
}
|
||||
|
||||
pub trait TorrentCreationTrait: DownloadCreationTrait {
|
||||
fn save_path(&self) -> &Path;
|
||||
|
||||
fn save_path_mut(&mut self) -> &mut PathBuf;
|
||||
|
||||
fn sources_mut(&mut self) -> &mut Vec<HashTorrentSource>;
|
||||
}
|
||||
Reference in New Issue
Block a user