use async_trait::async_trait;
use reqwest::Client;
use scraper::{Html, Selector};
use crate::{Magnet, MagneteaseError, Provider};
pub struct Nyaa;
const URL: &str = "https://nyaa.si/";
#[async_trait]
impl Provider for Nyaa {
async fn search(&self, client: &Client, phrase: &str) -> Result<Vec<Magnet>, MagneteaseError> {
let params = [("q", phrase)];
let html = client.get(URL).query(¶ms).send().await?.text().await?;
let doc = Html::parse_document(&html);
let torrent_table_selector = Selector::parse("tbody").unwrap();
let torrent_table = doc
.select(&torrent_table_selector)
.next()
.ok_or(MagneteaseError::HtmlError)?;
let anchor_selector = Selector::parse("a").unwrap();
let torrent_selector = Selector::parse("tr").unwrap();
let mut magnets = vec![];
for torrent in torrent_table.select(&torrent_selector) {
let torrent_info: Vec<_> = torrent.child_elements().collect();
let name = torrent_info
.get(1)
.ok_or(MagneteaseError::HtmlError)?
.select(&anchor_selector)
.last()
.ok_or(MagneteaseError::HtmlError)?
.inner_html();
let url = torrent_info
.get(2)
.ok_or(MagneteaseError::HtmlError)?
.select(&anchor_selector)
.last()
.ok_or(MagneteaseError::HtmlError)?
.attr("href")
.ok_or(MagneteaseError::HtmlError)?
.to_string();
let bytes = size_to_bytes(
&torrent_info
.get(3)
.ok_or(MagneteaseError::HtmlError)?
.inner_html(),
);
let seeders: u32 = torrent_info
.get(5)
.ok_or(MagneteaseError::HtmlError)?
.inner_html()
.parse()
.map_err(|_| MagneteaseError::HtmlError)?;
let magnet = NyaaMagnet {
url,
name,
seeders,
bytes,
};
magnets.push(magnet);
}
Ok(magnets
.into_iter()
.map(|nyaa_magnet| nyaa_magnet.into())
.collect())
}
}
pub struct NyaaMagnet {
url: String,
name: String,
seeders: u32,
bytes: u64,
}
impl From<NyaaMagnet> for Magnet {
fn from(value: NyaaMagnet) -> Self {
Magnet {
title: value.name,
url: value.url,
seeders: value.seeders,
bytes: value.bytes,
}
}
}
fn size_to_bytes(size: &str) -> u64 {
let kb = 1000.0;
let mb = kb * 1000.0;
let gb = mb * 1000.0;
let (size, suffix) = size.split_once(' ').unwrap();
let size: f64 = size.parse().unwrap();
if suffix == "GiB" {
(size * gb) as u64
} else if suffix == "MiB" {
(size * mb) as u64
} else if suffix == "KiB" {
(size * kb) as u64
} else {
todo!()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_search() {
let client = Client::new();
let magnets = Nyaa.search(&client, "lain").await.unwrap();
assert!(magnets.len() == 75);
assert!(!magnets[15].url.is_empty());
}
}