180 lines
8.3 KiB
Rust
180 lines
8.3 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use reqwest::get;
|
|
use scraper::{Html, Selector};
|
|
use tokio::io::split;
|
|
|
|
use crate::source::{Chapter, EpisodeListItem, MangaSource, SourceType};
|
|
|
|
pub struct Mangaworld {
|
|
url: String,
|
|
hostname:String,
|
|
sourcetype: SourceType
|
|
}
|
|
|
|
#[async_trait::async_trait]
|
|
impl MangaSource for Mangaworld {
|
|
fn get_hostname(&self)->String {
|
|
self.hostname.clone()
|
|
}
|
|
fn get_url(&self) -> String {
|
|
self.url.clone()
|
|
}
|
|
|
|
fn new(hostname: String, url: String) -> Self {
|
|
Self {
|
|
url,
|
|
hostname,
|
|
sourcetype: crate::source::SourceType::Manga
|
|
}
|
|
}
|
|
async fn listupdated(&self, page: usize) -> Result<Vec<crate::source::Chapter>, Box<dyn std::error::Error + Send + Sync>> {
|
|
let body = self.fetch_updated_page(page).await?;
|
|
self.extract_episode_from_updated(body)
|
|
}
|
|
//https://www.mangaworld.mx/archive?keyword=
|
|
fn extract_episode_from_search(&self, body: String) -> Result<Vec<crate::source::Chapter>, Box<dyn std::error::Error + Send + Sync>> {
|
|
let document = Html::parse_document(&body);
|
|
let sel_inner = Selector::parse("div.entry").unwrap();
|
|
// let sel_episode_number = Selector::parse("a.xanh").unwrap();
|
|
let sel_anime_name = Selector::parse("a.manga-title").unwrap();
|
|
let cards = document.select(&sel_inner)
|
|
.filter_map(|card| {
|
|
// let carde = card.select(&sel_episode_number).next()?;
|
|
// let index = carde.inner_html().trim().split(" ").last().unwrap().parse::<usize>().unwrap_or(0);
|
|
// log!("index: {}", index);
|
|
let carda = card.select(&sel_anime_name).next()?;
|
|
|
|
// log!("Card a found");
|
|
let url = carda.value().attr("href").unwrap_or("").trim().to_owned();
|
|
// log!("Card url: {}",url);
|
|
let title = carda.inner_html().trim().to_owned();
|
|
// log!("Card title: {}", title);
|
|
Some(Chapter {
|
|
title,
|
|
index: 0,
|
|
url,
|
|
section: 0
|
|
})
|
|
});
|
|
Ok(cards.collect::<Vec<Chapter>>())
|
|
}
|
|
//https://www.mangaworld.ac/
|
|
fn extract_episode_from_updated(&self, body: String) -> Result<Vec<crate::source::Chapter>, Box<dyn std::error::Error + Send + Sync>> {
|
|
let document = Html::parse_document(&body);
|
|
let sel_inner = Selector::parse("div.entry").unwrap();
|
|
let sel_episode_number = Selector::parse("a.xanh").unwrap();
|
|
let sel_anime_name = Selector::parse("a.manga-title").unwrap();
|
|
let cards = document.select(&sel_inner)
|
|
.filter_map(|card| {
|
|
let carde = card.select(&sel_episode_number).next()?;
|
|
let index = carde.inner_html().trim().split(" ").last().unwrap().parse::<usize>().unwrap_or(0);
|
|
// log!("index: {}", index);
|
|
let carda = card.select(&sel_anime_name).next()?;
|
|
|
|
// log!("Card a found");
|
|
let url = carda.value().attr("href").unwrap_or("").trim().to_owned();
|
|
// log!("Card url: {}",url);
|
|
let title = carda.inner_html().trim().to_owned();
|
|
// log!("Card title: {}", title);
|
|
Some(Chapter {
|
|
title,
|
|
index,
|
|
url,
|
|
section: 0
|
|
})
|
|
});
|
|
Ok(cards.collect::<Vec<Chapter>>())
|
|
}
|
|
// async fn episode_openable_link(&self, card: Chapter) -> Result<String, Box<dyn std::error::Error>> {
|
|
// let id= Self::id_extract(&card.url).unwrap();
|
|
// let u = format!("{}/api/episode/serverPlayerAnimeWorld?id={}", self.url, id);
|
|
// let r = get(u).await?.error_for_status()?.text().await?;
|
|
// let b = Html::parse_document(&r);
|
|
// let select_source = Selector::parse("source").unwrap();
|
|
// Ok(b.select(&select_source).next().unwrap().value().attr("src").unwrap_or("").to_owned())
|
|
// }
|
|
async fn fetch_search_page(&self, query: String, page: usize) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
|
|
// if page < 1 { page = 1};
|
|
let u = format!("{}?keyword={}&page={}",self.get_url(), query, page);
|
|
let r = reqwest::get(u).await?.error_for_status()?.text().await?;
|
|
return Ok(r);
|
|
}
|
|
|
|
// async fn search(&self, query: String, page: usize) -> Result<Vec<crate::source::Episode>, Box<dyn std::error::Error + Send + Sync>>{
|
|
// let body = self.fetch_search_page(query, page).await?;
|
|
// self.extract_episode_from_search(body)
|
|
// }
|
|
|
|
async fn fetch_updated_page(&self, page: usize)-> Result<String, Box<dyn std::error::Error + Send + Sync>> {
|
|
let p = std::cmp::max(1, page);
|
|
let u = format!("{}",self.get_url());
|
|
let a = get(u).await?.error_for_status()?.text().await?;
|
|
return Ok(a);
|
|
}
|
|
async fn lib_url_parse(&self, url: String) -> Result<HashMap<String, EpisodeListItem>, Box<dyn std::error::Error + Send + Sync>> {
|
|
let mut hs = HashMap::<String, EpisodeListItem>::new();
|
|
if let Ok(a) = get(url).await?.error_for_status()?.text().await {
|
|
let b = Html::parse_document(&a);
|
|
// let select_chapters_wrapper = Selector::parse("").unwrap();
|
|
let select_volume_element = Selector::parse(".chapters-wrapper .volume-element").unwrap();
|
|
let select_volume_name = Selector::parse(".volume .volume-name").unwrap();
|
|
let select_episodes = Selector::parse("div.chapter a.chap").unwrap();
|
|
let select_span = Selector::parse("span").unwrap();
|
|
// for ep in b.select(&select_episodes) {
|
|
// // let alink = ep.value();
|
|
// if let Some(span) = ep.select(&select_span).next() {
|
|
// hs.insert(span.inner_html(), EpisodeListItem {
|
|
// title: format!("Volume {:>4} Capitolo {:>4}",,span.inner_html()),
|
|
// url: alink.attr("href").unwrap_or("").trim().to_owned()
|
|
// });
|
|
// }
|
|
// }
|
|
for vol in b.select(&select_volume_element) {
|
|
if let Some(volname) = vol.select(&select_volume_name).next() {
|
|
let volume = volname.text().next();
|
|
for ep in vol.select(&select_episodes) {
|
|
if let Some(span) = ep.select(&select_span).next() {
|
|
let alink = ep.value();
|
|
let chapter = span.inner_html();
|
|
let x = volume
|
|
.and_then(|s| s.split_whitespace().nth(1))
|
|
.and_then(|second| second.split_once('.'));
|
|
let y = chapter.split_whitespace().nth(1)
|
|
.and_then(|second| second.split_once('.'));
|
|
|
|
let title_vol = match x {
|
|
Some((vola,volb)) => {
|
|
if volb.is_empty() {
|
|
format!("Volume {:>4}", vola)
|
|
} else {
|
|
format!("Volume {:>4}.{}", vola,volb)
|
|
}
|
|
},
|
|
_ => "".to_owned()
|
|
};
|
|
let title_cha = match y {
|
|
Some((cha,chb)) => {
|
|
if chb.is_empty() {
|
|
format!("Capitolo {:>4}", cha)
|
|
} else {
|
|
format!("Capitolo {:>4}.{}", cha,chb)
|
|
}
|
|
},
|
|
_ => "".to_owned()
|
|
};
|
|
let title_sep = if !title_vol.is_empty() && !title_cha.is_empty() {" "} else {""};
|
|
hs.insert(span.inner_html(), EpisodeListItem {
|
|
title: format!("{}{}{}", title_vol,title_sep, title_cha),
|
|
url: alink.attr("href").unwrap_or("").trim().to_owned()
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return Ok(hs);
|
|
}
|
|
|
|
}
|