Die less and other stuff

This commit is contained in:
blank X 2021-01-11 22:38:08 +07:00
parent 9495d93fff
commit e00fb4b652
7 changed files with 345 additions and 122 deletions

3
Cargo.lock generated
View File

@ -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]]

View File

@ -1,6 +1,6 @@
[package]
name = "mangafetchi"
version = "0.1.6"
version = "0.1.7"
authors = ["blank X <theblankx@protonmail.com>"]
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"] }

View File

@ -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<structs::Chapter> = 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<JoinHandle<()>> = 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
};
}
}
{

View File

@ -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);
}

View File

@ -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!("");
}

View File

@ -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<String>,
pub status: String,
pub last_updated: String,
pub status: Option<String>,
pub last_updated: Option<String>,
pub genres: Vec<String>,
pub summary: Option<String>,
pub chapters: Vec<Chapter>
@ -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<reqwest::Error> for Error {
#[inline]
fn from(error: reqwest::Error) -> Error {
Error::Reqwest(error)
}
}
impl From<url::ParseError> for Error {
#[inline]
fn from(error: url::ParseError) -> Error {
Error::URL(error)
}
}
impl From<serde_json::Error> for Error {
#[inline]
fn from(error: serde_json::Error) -> Error {
Error::SerdeJSON(error)
}
}

View File

@ -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<Vec<structs::SearchResult>, reqwest::Error> {
pub async fn search(client: reqwest::Client, query: &str) -> Result<Vec<structs::SearchResult>, 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<Vec<structs:
if text.is_empty() {
return Ok(Vec::new());
}
let mut results: Vec<structs::SearchResult> = serde_json::from_str(&text).unwrap();
let mut results: Vec<structs::SearchResult> = 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<Vec<structs:
Ok(results)
}
pub async fn get_manga(client: reqwest::Client, manga_id: &str) -> Result<structs::MangaOption, reqwest::Error> {
pub async fn get_manga(client: reqwest::Client, manga_id: &str) -> Result<structs::MangaOption, structs::Error> {
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<struct
.await?
.text()
.await?;
let resp = match reqwest::Url::parse(&redirect.url).unwrap().host_str().unwrap() {
let resp = match Url::parse(&redirect.url)?.host_str().unwrap() {
"mangakakalot.com" => 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<struct
match resp {
structs::MangaOption::Manga(_) => 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<String> {
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<String> = None;
let mut alt: Option<String> = 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<String> {
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<String> = None;
let mut alt: Option<String> = 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;
}
}
},