nyaa 0.9.1

A tui tool for browsing and downloading torrents from nyaa.si
Documentation
use std::error::Error;

use reqwest::{Response, StatusCode};
use serde::{Deserialize, Serialize};
use urlencoding::encode;

use crate::{source::Item, util::conv::add_protocol};

use super::{multidownload, ClientConfig, DownloadClient, DownloadError, DownloadResult};

#[derive(Serialize, Deserialize, Clone)]
#[serde(default)]
pub struct RqbitConfig {
    pub base_url: String,
    pub use_magnet: Option<bool>,
    pub overwrite: Option<bool>,
    pub output_folder: Option<String>,
}

pub struct RqbitClient;

#[derive(Serialize, Deserialize, Clone)]
pub struct RqbitForm {
    pub overwrite: Option<bool>,
    pub output_folder: Option<String>,
}

impl Default for RqbitConfig {
    fn default() -> Self {
        Self {
            base_url: "http://localhost:3030".to_owned(),
            use_magnet: None,
            overwrite: None,
            output_folder: None,
        }
    }
}

async fn add_torrent(
    conf: &RqbitConfig,
    link: String,
    client: &reqwest::Client,
) -> Result<Response, Box<dyn Error + Send + Sync>> {
    let base_url = add_protocol(conf.base_url.clone(), false)?;
    let mut url = base_url.join("/torrents")?;
    let mut query: Vec<String> = vec![];
    if let Some(ow) = conf.overwrite {
        query.push(format!("overwrite={}", ow));
    }
    if let Some(out) = conf.output_folder.to_owned() {
        query.push(format!("output_folder={}", encode(&out)));
    }
    url.set_query(Some(&query.join("&")));

    match client.post(url).body(link).send().await {
        Ok(res) => Ok(res),
        Err(e) => Err(e.into()),
    }
}

pub fn load_config(cfg: &mut ClientConfig) {
    if cfg.rqbit.is_none() {
        cfg.rqbit = Some(RqbitConfig::default());
    }
}

impl DownloadClient for RqbitClient {
    async fn download(item: Item, conf: ClientConfig, client: reqwest::Client) -> DownloadResult {
        let conf = match conf.rqbit.clone() {
            Some(q) => q,
            None => {
                return DownloadResult::error(DownloadError("Failed to get rqbit config".into()));
            }
        };
        let link = match conf.use_magnet.unwrap_or(true) {
            true => item.magnet_link.to_owned(),
            false => item.torrent_link.to_owned(),
        };
        let res = match add_torrent(&conf, link, &client).await {
            Ok(r) => r,
            Err(e) => {
                return DownloadResult::error(DownloadError(format!(
                    "Failed to get response from rqbit\n{}",
                    e
                )));
            }
        };
        if res.status() != StatusCode::OK {
            return DownloadResult::error(DownloadError(format!(
                "rqbit returned status code {}",
                res.status().as_u16()
            )));
        }

        DownloadResult::new(
            "Successfully sent torrent to rqbit".to_owned(),
            vec![item.id],
            vec![],
            false,
        )
    }

    async fn batch_download(
        items: Vec<Item>,
        conf: ClientConfig,
        client: reqwest::Client,
    ) -> DownloadResult {
        multidownload::<RqbitClient, _>(
            |s| format!("Successfully sent {} torrents to rqbit", s),
            &items,
            &conf,
            &client,
        )
        .await
    }
}