diff --git a/Cargo.lock b/Cargo.lock index 08253fa..a9b6dd2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -330,13 +330,14 @@ dependencies = [ [[package]] name = "mangafetchi" -version = "0.1.6" +version = "0.1.7" dependencies = [ "quick-xml", "reqwest", "serde", "serde_json", "tokio", + "url", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 5365a6a..844c4ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mangafetchi" -version = "0.1.6" +version = "0.1.7" authors = ["blank X "] edition = "2018" @@ -12,6 +12,7 @@ lto = true [dependencies] serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" +url = "2.2" reqwest = "0.11" quick-xml = "0.20" tokio = { version = "1.0", features = ["rt-multi-thread", "sync", "time"] } diff --git a/src/commands/download.rs b/src/commands/download.rs index 7127680..f5491db 100644 --- a/src/commands/download.rs +++ b/src/commands/download.rs @@ -5,6 +5,7 @@ use std::env; use std::sync::Arc; use std::process::exit; use std::path::{Path, PathBuf}; +use url::Url; use tokio::sync::Mutex; use tokio::task::JoinHandle; use tokio::time::{sleep, Duration}; @@ -14,6 +15,7 @@ extern crate reqwest; const DOWNLOAD_WORKERS: usize = 5; const NON_IMAGE_WAIT_TIME: u64 = 5000; const NO_ITEM_WAIT_TIME: u64 = 1000; +const GET_MANGA_FAIL_WAIT_TIME: u64 = 30000; pub async fn run(mut args: env::Args) { let manga_id = match args.next() { @@ -23,7 +25,7 @@ pub async fn run(mut args: env::Args) { exit(1); } }; - let mut chapter_numbers: Vec<_> = args.collect(); + let mut chapter_numbers: Vec<_> = args.map(|i| i.trim().to_string()).collect(); chapter_numbers.sort(); chapter_numbers.dedup(); let mut chapters: Vec = Vec::new(); @@ -34,7 +36,7 @@ pub async fn run(mut args: env::Args) { chapters = manga_info.chapters; } else { for chapter_number in chapter_numbers { - let tmp = manga_info.chapters.iter().enumerate().find(|(_, chapter)| chapter_number.trim() == chapter.chapter_number.as_str()); + let tmp = manga_info.chapters.iter().enumerate().find(|(_, chapter)| chapter_number == chapter.chapter_number); if tmp.is_some() { let (i, _) = tmp.unwrap(); chapters.push(manga_info.chapters.remove(i)); @@ -46,7 +48,7 @@ pub async fn run(mut args: env::Args) { } }, structs::MangaOption::Redirect(_) => panic!("Nested redirect"), - structs::MangaOption::None => { + structs::MangaOption::DoesNotExist => { eprintln!("ID: {}\nError: does not exist", &manga_id); exit(1); } @@ -54,18 +56,28 @@ pub async fn run(mut args: env::Args) { let mutex = Arc::new(Mutex::new(DownloadData { data: Vec::new(), is_done: false })); let handles: Vec> = summon_handles(client.clone(), Arc::clone(&mutex)).await; for chapter in chapters { - let cloned_mutex = Arc::clone(&mutex); - let chapter_pages = utils::get_pages(client.clone(), &chapter, &manga_id).await.unwrap(); - let mut to_extend: Vec<(String, PathBuf, String)> = Vec::new(); - for url in chapter_pages { - let mut file_name = PathBuf::from(&chapter.chapter_number); - file_name.push(Path::new(reqwest::Url::parse(&url).unwrap().path()).file_name().unwrap()); - if !file_name.exists() { - to_extend.push((url, file_name, chapter.domain.clone())); - } - } - if !to_extend.is_empty() { - cloned_mutex.lock().await.data.extend(to_extend); + loop { + match utils::get_pages(client.clone(), &chapter, &manga_id).await { + Ok(chapter_pages) => { + if chapter_pages.is_empty() { + sleep(Duration::from_millis(GET_MANGA_FAIL_WAIT_TIME)).await; + continue; + } + let mut to_extend: Vec<(String, PathBuf, String)> = Vec::new(); + for url in chapter_pages { + let mut file_name = PathBuf::from(&chapter.chapter_number); + file_name.push(Path::new(Url::parse(&url).unwrap().path()).file_name().unwrap()); + if !file_name.exists() { + to_extend.push((url, file_name, chapter.domain.clone())); + } + } + if !to_extend.is_empty() { + Arc::clone(&mutex).lock().await.data.extend(to_extend); + } + break; + }, + Err(_) => sleep(Duration::from_millis(GET_MANGA_FAIL_WAIT_TIME)).await + }; } } { diff --git a/src/commands/feed.rs b/src/commands/feed.rs index 455c50e..a9ebd2f 100644 --- a/src/commands/feed.rs +++ b/src/commands/feed.rs @@ -23,7 +23,7 @@ pub async fn run(mut args: env::Args) { let mut manga_info = match utils::get_manga(reqwest::Client::new(), &manga_id).await.unwrap() { structs::MangaOption::Manga(manga_info) => manga_info, structs::MangaOption::Redirect(_) => panic!("Nested redirect"), - structs::MangaOption::None => { + structs::MangaOption::DoesNotExist => { eprintln!("ID: {}\nError: does not exist", &manga_id); exit(1); } diff --git a/src/commands/view.rs b/src/commands/view.rs index 1f44898..5602a77 100644 --- a/src/commands/view.rs +++ b/src/commands/view.rs @@ -33,7 +33,7 @@ pub async fn run(args: env::Args) { println!("{}", &manga_info); }, structs::MangaOption::Redirect(_) => panic!("Nested redirect"), - structs::MangaOption::None => { + structs::MangaOption::DoesNotExist => { if one_done { eprintln!(""); } diff --git a/src/structs.rs b/src/structs.rs index 0b94e67..f827179 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -1,6 +1,8 @@ use std::fmt; use serde::Deserialize; +extern crate url; extern crate serde; +extern crate serde_json; #[derive(Deserialize, Debug)] pub struct SearchResult { @@ -36,8 +38,8 @@ pub struct Manga { pub id: String, pub name: String, pub authors: Vec, - pub status: String, - pub last_updated: String, + pub status: Option, + pub last_updated: Option, pub genres: Vec, pub summary: Option, pub chapters: Vec @@ -45,13 +47,21 @@ pub struct Manga { impl fmt::Display for Manga { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut text = format!("ID: {}\nName: {}\nStatus: {}\nLast Updated: {}\nGenres: {}\nAuthors: {}", + let mut text = format!("ID: {}\nName: {}", self.id, - self.name, - self.status, - self.last_updated, - self.genres.join(", "), - self.authors.join(", ")); + self.name); + if self.status.is_some() { + text.push_str(&format!("\nStatus: {}", self.status.as_ref().unwrap())); + } + if self.last_updated.is_some() { + text.push_str(&format!("\nLast Updated: {}", self.last_updated.as_ref().unwrap())); + } + if !self.genres.is_empty() { + text.push_str(&format!("\nGenres: {}", self.genres.join(", "))); + } + if !self.authors.is_empty() { + text.push_str(&format!("\nAuthors: {}", self.authors.join(", "))); + } if self.summary.is_some() { text.push_str(&format!("\nSummary:\n{}", self.summary.as_ref().unwrap())); } @@ -70,5 +80,33 @@ pub struct Redirect { pub enum MangaOption { Manga(Manga), Redirect(Redirect), - None + DoesNotExist +} + +#[derive(Debug)] +pub enum Error { + Reqwest(reqwest::Error), + URL(url::ParseError), + SerdeJSON(serde_json::Error), +} + +impl From for Error { + #[inline] + fn from(error: reqwest::Error) -> Error { + Error::Reqwest(error) + } +} + +impl From for Error { + #[inline] + fn from(error: url::ParseError) -> Error { + Error::URL(error) + } +} + +impl From for Error { + #[inline] + fn from(error: serde_json::Error) -> Error { + Error::SerdeJSON(error) + } } diff --git a/src/utils.rs b/src/utils.rs index a806df3..5cc990b 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -3,6 +3,7 @@ use crate::structs; use std::io::Write; use std::path::PathBuf; use std::fs::{create_dir, File}; +use url::Url; use quick_xml::Reader; use quick_xml::events::Event; extern crate reqwest; @@ -41,7 +42,7 @@ fn remove_html(text: &str) -> String { removed } -pub async fn search(client: reqwest::Client, query: &str) -> Result, reqwest::Error> { +pub async fn search(client: reqwest::Client, query: &str) -> Result, structs::Error> { let text = client.post("https://mangakakalot.com/home_json_search") .form(&[("searchword", &generate_slug(&query))]) .send() @@ -51,7 +52,7 @@ pub async fn search(client: reqwest::Client, query: &str) -> Result = serde_json::from_str(&text).unwrap(); + let mut results: Vec = serde_json::from_str(&text)?; for i in 0..results.len() { let old_result = &results[i]; results[i] = structs::SearchResult { @@ -67,7 +68,7 @@ pub async fn search(client: reqwest::Client, query: &str) -> Result Result { +pub async fn get_manga(client: reqwest::Client, manga_id: &str) -> Result { let text = client.get(&format!("https://mangakakalot.com/manga/{}", &manga_id)) .send() .await? @@ -82,7 +83,7 @@ pub async fn get_manga(client: reqwest::Client, manga_id: &str) -> Result parse_mangakakalot_manga(&text, &manga_id), "manganelo.com" => parse_manganelo_manga(&text, &manga_id), _ => panic!("Unknown URL: {}", &redirect.url) @@ -90,10 +91,10 @@ pub async fn get_manga(client: reqwest::Client, manga_id: &str) -> Result resp, structs::MangaOption::Redirect(_) => panic!("Nested redirect"), - structs::MangaOption::None => structs::MangaOption::None + structs::MangaOption::DoesNotExist => structs::MangaOption::DoesNotExist } }, - structs::MangaOption::None => structs::MangaOption::None + structs::MangaOption::DoesNotExist => structs::MangaOption::DoesNotExist }) } @@ -130,12 +131,19 @@ fn parse_manganelo_manga(text: &str, manga_id: &str) -> structs::MangaOption { b"a" => { is_inside_a = true; if is_inside_ul { - tmp_chapter_link = Some(e.attributes() - .find(|attribute| attribute.as_ref().unwrap().key == b"href") - .unwrap() - .unwrap() - .unescape_and_decode_value(&reader) - .unwrap()); + let href = e.attributes() + .find(|attribute| { + match attribute.as_ref() { + Ok(attribute) => attribute.key == b"href", + Err(_) => false + } + }); + if href.is_some() { + match href.unwrap().unwrap().unescape_and_decode_value(&reader) { + Ok(text) => tmp_chapter_link = Some(text), + Err(_) => () + }; + } } }, b"ul" => is_inside_ul = true, @@ -144,9 +152,16 @@ fn parse_manganelo_manga(text: &str, manga_id: &str) -> structs::MangaOption { b"td" => { let is_table_value = e.attributes() .find(|attribute| { - let attribute = attribute.as_ref().unwrap(); - attribute.key == b"class" && - attribute.unescape_and_decode_value(&reader).unwrap().as_str() == "table-value" + match attribute.as_ref() { + Ok(attribute) => { + attribute.key == b"class" && + match attribute.unescape_and_decode_value(&reader) { + Ok(text) => text.as_str() == "table-value", + Err(_) => false + } + }, + Err(_) => false + } }).is_some(); if is_table_value { is_inside_td = true; @@ -154,26 +169,39 @@ fn parse_manganelo_manga(text: &str, manga_id: &str) -> structs::MangaOption { }, b"i" => { let class = e.attributes() - .find(|attribute| attribute.as_ref().unwrap().key == b"class"); + .find(|attribute| { + match attribute.as_ref() { + Ok(attribute) => attribute.key == b"class", + Err(_) => false + } + }); if class.is_some() { - let class_name = class.unwrap() - .unwrap() - .unescape_and_decode_value(&reader) - .unwrap(); - match class_name.as_str() { - "info-author" => is_inside_authors = true, - "info-status" => is_inside_status = true, - "info-genres" => is_inside_genres = true, - _ => () - } + match class.unwrap().unwrap().unescape_and_decode_value(&reader) { + Ok(class_name) => { + match class_name.as_str() { + "info-author" => is_inside_authors = true, + "info-status" => is_inside_status = true, + "info-genres" => is_inside_genres = true, + _ => () + }; + }, + Err(_) => () + }; } }, b"span" => { let is_stre_value = e.attributes() .find(|attribute| { - let attribute = attribute.as_ref().unwrap(); - attribute.key == b"class" && - attribute.unescape_and_decode_value(&reader).unwrap().as_str() == "stre-value" + match attribute.as_ref() { + Ok(attribute) => { + attribute.key == b"class" && + match attribute.unescape_and_decode_value(&reader) { + Ok(text) => text.as_str() == "stre-value", + Err(_) => false + } + }, + Err(_) => false + } }).is_some(); if is_stre_value { is_inside_stre_value = true; @@ -182,9 +210,16 @@ fn parse_manganelo_manga(text: &str, manga_id: &str) -> structs::MangaOption { b"div" => { let is_description = e.attributes() .find(|attribute| { - let attribute = attribute.as_ref().unwrap(); - attribute.key == b"class" && - attribute.unescape_and_decode_value(&reader).unwrap().as_str() == "panel-story-info-description" + match attribute.as_ref() { + Ok(attribute) => { + attribute.key == b"class" && + match attribute.unescape_and_decode_value(&reader) { + Ok(text) => text.as_str() == "panel-story-info-description", + Err(_) => false + } + }, + Err(_) => false + } }).is_some(); if is_description { is_inside_description = true; @@ -223,11 +258,16 @@ fn parse_manganelo_manga(text: &str, manga_id: &str) -> structs::MangaOption { Some(text) => Some(text.trim().to_string()), None => None }; - chapters.push(structs::Chapter { - chapter_number: tmp_chapter_link.unwrap().rsplitn(2, '_').nth(0).unwrap().to_string(), - chapter_name: chapter_name, - domain: "manganelo.com".to_string() - }); + match tmp_chapter_link.unwrap().rsplitn(2, '_').nth(0) { + Some(chapter_number) => { + chapters.push(structs::Chapter { + chapter_number: chapter_number.to_string(), + chapter_name: chapter_name, + domain: "manganelo.com".to_string() + }); + }, + None => () + }; tmp_chapter_link = None; } else if text.starts_with("REDIRECT : ") { return structs::MangaOption::Redirect(structs::Redirect { url: text.splitn(2, ':').nth(1).unwrap().trim().to_string() }); @@ -254,7 +294,7 @@ fn parse_manganelo_manga(text: &str, manga_id: &str) -> structs::MangaOption { Err(err) => panic!("Error at position {}: {}", reader.buffer_position(), err), Ok(Event::Eof) => break, _ => () - } + }; buf.clear(); } chapters.reverse(); @@ -262,8 +302,8 @@ fn parse_manganelo_manga(text: &str, manga_id: &str) -> structs::MangaOption { id: manga_id.to_string(), name: name.unwrap(), authors: authors, - status: status.unwrap(), - last_updated: last_updated.unwrap(), + status: status, + last_updated: last_updated, genres: genres, summary: summary, chapters: chapters @@ -305,9 +345,16 @@ fn parse_mangakakalot_manga(text: &str, manga_id: &str) -> structs::MangaOption b"ul" => { let is_manga_info_text = e.attributes() .find(|attribute| { - let attribute = attribute.as_ref().unwrap(); - attribute.key == b"class" && - attribute.unescape_and_decode_value(&reader).unwrap().as_str() == "manga-info-text" + match attribute.as_ref() { + Ok(attribute) => { + attribute.key == b"class" && + match attribute.unescape_and_decode_value(&reader) { + Ok(text) => text.as_str() == "manga-info-text", + Err(_) => false + } + }, + Err(_) => false + } }).is_some(); if is_manga_info_text { is_inside_manga_info = true; @@ -315,29 +362,39 @@ fn parse_mangakakalot_manga(text: &str, manga_id: &str) -> structs::MangaOption }, b"div" => { let class = e.attributes() - .find(|attribute| attribute.as_ref().unwrap().key == b"class"); - if class.is_some() { - let class_name = class.unwrap() - .unwrap() - .unescape_and_decode_value(&reader) - .unwrap(); - match class_name.as_str() { - "chapter-list" => is_inside_chapter_list = true, - "row" => is_inside_row = true, - _ => () - }; - } - let id = e.attributes() .find(|attribute| { match attribute.as_ref() { - Ok(attribute) => attribute.key == b"id", + Ok(attribute) => attribute.key == b"class", Err(_) => false } }); - if id.is_some() { - if id.unwrap().unwrap().unescape_and_decode_value(&reader).unwrap().as_str() == "noidungm" { - is_inside_noidungm = true; - } + if class.is_some() { + match class.unwrap().unwrap().unescape_and_decode_value(&reader) { + Ok(class_name) => { + match class_name.as_str() { + "chapter-list" => is_inside_chapter_list = true, + "row" => is_inside_row = true, + _ => () + }; + }, + Err(_) => () + }; + } + let inside_noidungm = e.attributes() + .find(|attribute| { + match attribute.as_ref() { + Ok(attribute) => { + attribute.key == b"id" && + match attribute.unescape_and_decode_value(&reader) { + Ok(text) => text.as_str() == "noidungm", + Err(_) => false + } + }, + Err(_) => false + } + }).is_some(); + if inside_noidungm { + is_inside_noidungm = true; } }, b"h1" => is_inside_h1 = true, @@ -345,12 +402,19 @@ fn parse_mangakakalot_manga(text: &str, manga_id: &str) -> structs::MangaOption b"a" => { is_inside_a = true; if is_inside_chapter_list { - tmp_chapter_link = Some(e.attributes() - .find(|attribute| attribute.as_ref().unwrap().key == b"href") - .unwrap() - .unwrap() - .unescape_and_decode_value(&reader) - .unwrap()); + let href = e.attributes() + .find(|attribute| { + match attribute.as_ref() { + Ok(attribute) => attribute.key == b"href", + Err(_) => false + } + }); + if href.is_some() { + match href.unwrap().unwrap().unescape_and_decode_value(&reader) { + Ok(text) => tmp_chapter_link = Some(text), + Err(_) => () + }; + } } }, b"title" => is_inside_title = true, @@ -376,10 +440,18 @@ fn parse_mangakakalot_manga(text: &str, manga_id: &str) -> structs::MangaOption } else { match text.splitn(2, ' ').nth(0).unwrap() { "Author(s)" => is_inside_authors = true, - "Status" => status = Some(text.splitn(3, ' ').nth(2).unwrap().to_string()), + "Status" => { + match text.splitn(3, ' ').nth(2) { + Some(text) => status = Some(text.to_string()), + None => () + }; + }, "Last" => { if text.starts_with("Last updated : ") { - last_updated = Some(text.splitn(4, ' ').nth(3).unwrap().to_string()); + match text.splitn(4, ' ').nth(3) { + Some(text) => last_updated = Some(text.to_string()), + None => () + }; } }, "Genres" => is_inside_genres = true, @@ -397,11 +469,16 @@ fn parse_mangakakalot_manga(text: &str, manga_id: &str) -> structs::MangaOption Some(text) => Some(text.trim().to_string()), None => None }; - chapters.push(structs::Chapter { - chapter_number: tmp_chapter_link.unwrap().rsplitn(2, '_').nth(0).unwrap().to_string(), - chapter_name: chapter_name, - domain: "mangakakalot.com".to_string() - }); + match tmp_chapter_link.unwrap().rsplitn(2, '_').nth(0) { + Some(chapter_number) => { + chapters.push(structs::Chapter { + chapter_number: chapter_number.to_string(), + chapter_name: chapter_name, + domain: "mangakakalot.com".to_string() + }); + }, + None => () + }; tmp_chapter_link = None; } else if is_inside_title { is_title_real = !text.is_empty(); @@ -435,7 +512,7 @@ fn parse_mangakakalot_manga(text: &str, manga_id: &str) -> structs::MangaOption b"a" => is_inside_a = false, b"title" => { if !is_title_real { - return structs::MangaOption::None; + return structs::MangaOption::DoesNotExist; } }, _ => () @@ -452,8 +529,8 @@ fn parse_mangakakalot_manga(text: &str, manga_id: &str) -> structs::MangaOption id: manga_id.to_string(), name: name.unwrap(), authors: authors, - status: status.unwrap(), - last_updated: last_updated.unwrap(), + status: status, + last_updated: last_updated, genres: genres, summary: summary, chapters: chapters @@ -478,28 +555,75 @@ fn parse_mangakakalot_pages(text: &str) -> Vec { let screaming_doctype = split[0].to_uppercase(); split[0] = &screaming_doctype; let text = split.join("\n"); + let mut is_inside_pages = false; + let mut is_inside_ads = false; let mut pages = Vec::new(); let mut reader = Reader::from_str(&text); reader.check_end_names(false); let mut buf = Vec::new(); loop { match reader.read_event(&mut buf) { + Ok(Event::Start(ref e)) => { + if e.name() == b"div" { + if is_inside_pages { + is_inside_ads = true; + } else { + let inside_pages = e.attributes() + .find(|attribute| { + match attribute.as_ref() { + Ok(attribute) => { + attribute.key == b"id" && + match attribute.unescape_and_decode_value(&reader) { + Ok(text) => text.as_str() == "vungdoc", + Err(_) => false + } + }, + Err(_) => false + } + }).is_some(); + if inside_pages { + is_inside_pages = true; + } + } + } + }, Ok(Event::Empty(ref e)) => { if e.name() == b"img" { let mut src: Option = None; let mut alt: Option = None; for attribute in e.attributes() { - let attribute = attribute.unwrap(); - match attribute.key { - b"src" => src = Some(attribute.unescape_and_decode_value(&reader).unwrap()), - b"alt" => alt = Some(attribute.unescape_and_decode_value(&reader).unwrap()), - _ => () + match attribute { + Ok(attribute) => { + match attribute.key { + b"src" => { + match attribute.unescape_and_decode_value(&reader) { + Ok(src_text) => src = Some(src_text), + Err(_) => () + }; + }, + b"alt" => { + match attribute.unescape_and_decode_value(&reader) { + Ok(alt_text) => alt = Some(alt_text), + Err(_) => () + }; + }, + _ => () + }; + }, + Err(_) => () }; } if src.is_some() && alt.is_some() { - if alt.unwrap().ends_with(" - Mangakakalot.com") { - pages.push(src.unwrap()); - } + pages.push(src.unwrap()); + } + } + }, + Ok(Event::End(e)) => { + if e.name() == b"div" { + if is_inside_ads { + is_inside_ads = false; + } else if is_inside_pages { + is_inside_pages = false; } } }, @@ -517,28 +641,75 @@ fn parse_manganelo_pages(text: &str) -> Vec { let screaming_doctype = split[0].to_uppercase(); split[0] = &screaming_doctype; let text = split.join("\n"); + let mut is_inside_pages = false; + let mut is_inside_ads = false; let mut pages = Vec::new(); let mut reader = Reader::from_str(&text); reader.check_end_names(false); let mut buf = Vec::new(); loop { match reader.read_event(&mut buf) { + Ok(Event::Start(ref e)) => { + if e.name() == b"div" { + if is_inside_pages { + is_inside_ads = true; + } else { + let inside_pages = e.attributes() + .find(|attribute| { + match attribute.as_ref() { + Ok(attribute) => { + attribute.key == b"class" && + match attribute.unescape_and_decode_value(&reader) { + Ok(text) => text.as_str() == "container-chapter-reader", + Err(_) => false + } + }, + Err(_) => false + } + }).is_some(); + if inside_pages { + is_inside_pages = true; + } + } + } + }, Ok(Event::Empty(ref e)) => { - if e.name() == b"img" { + if is_inside_pages && !is_inside_ads && e.name() == b"img" { let mut src: Option = None; let mut alt: Option = None; for attribute in e.attributes() { - let attribute = attribute.unwrap(); - match attribute.key { - b"src" => src = Some(attribute.unescape_and_decode_value(&reader).unwrap()), - b"alt" => alt = Some(attribute.unescape_and_decode_value(&reader).unwrap()), - _ => () + match attribute { + Ok(attribute) => { + match attribute.key { + b"src" => { + match attribute.unescape_and_decode_value(&reader) { + Ok(src_text) => src = Some(src_text), + Err(_) => () + }; + }, + b"alt" => { + match attribute.unescape_and_decode_value(&reader) { + Ok(alt_text) => alt = Some(alt_text), + Err(_) => () + }; + }, + _ => () + }; + }, + Err(_) => () }; } if src.is_some() && alt.is_some() { - if alt.unwrap().ends_with(" - MangaNelo.com") { - pages.push(src.unwrap()); - } + pages.push(src.unwrap()); + } + } + }, + Ok(Event::End(e)) => { + if e.name() == b"div" { + if is_inside_ads { + is_inside_ads = false; + } else if is_inside_pages { + is_inside_pages = false; } } },