Update dependencies

This commit is contained in:
blank X 2022-04-30 20:07:56 +07:00
parent 56898120d9
commit f12edb405e
Signed by: blankie
GPG Key ID: CC15FC822C7F61F5
9 changed files with 466 additions and 522 deletions

576
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package] [package]
name = "hentaihavenrs" name = "hentaihavenrs"
version = "0.1.1" version = "0.1.2"
authors = ["blank X <theblankx@protonmail.com>"] authors = ["blank X <theblankx@protonmail.com>"]
edition = "2018" edition = "2018"
@ -10,9 +10,9 @@ edition = "2018"
lto = true lto = true
[dependencies] [dependencies]
tokio = { version = "1.1", features = ["rt"] } tokio = { version = "1.18", features = ["rt"] }
reqwest = { version = "0.11", features = ["multipart", "rustls-tls"] } reqwest = { version = "0.11", features = ["multipart", "rustls-tls"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
quick-xml = "0.20" quick-xml = "0.22"
clap = { version = "2.33", default-features = false } clap = { version = "3.1", features = ["std"], default-features = false }

View File

@ -1,14 +1,14 @@
use crate::utils; use crate::utils;
use clap::ArgMatches;
use std::fs::{create_dir, rename};
use std::io; use std::io;
use std::fs::{rename, create_dir};
use std::path::PathBuf; use std::path::PathBuf;
use std::process::{exit, Command}; use std::process::{exit, Command};
use clap::ArgMatches;
const MAX_DOWNLOAD_ATTEMPTS: i32 = 5; const MAX_DOWNLOAD_ATTEMPTS: i32 = 5;
pub async fn download(arg_m: &ArgMatches<'_>) { pub async fn download(arg_m: &ArgMatches) {
let print_only = arg_m.is_present("print"); let print_only = arg_m.is_present("print");
let id = arg_m.value_of("id").unwrap(); let id = arg_m.value_of("id").unwrap();
let client = utils::create_client(); let client = utils::create_client();
@ -26,17 +26,21 @@ pub async fn download(arg_m: &ArgMatches<'_>) {
if filename.exists() { if filename.exists() {
continue; continue;
} }
let download_url = match utils::get_url(tcloned_client.clone(), &episode_url).await { let download_url =
Ok(Some(i)) => i, match utils::get_url(tcloned_client.clone(), &episode_url).await {
Ok(None) => { Ok(Some(i)) => i,
eprintln!("Failed to get {}: get_url returned None", filename.display()); Ok(None) => {
continue; eprintln!(
}, "Failed to get {}: get_url returned None",
Err(err) => { filename.display()
eprintln!("Failed to get {}: {}", filename.display(), err); );
continue; continue;
} }
}; Err(err) => {
eprintln!("Failed to get {}: {}", filename.display(), err);
continue;
}
};
if print_only { if print_only {
println!("{}", download_url); println!("{}", download_url);
continue; continue;
@ -56,12 +60,17 @@ pub async fn download(arg_m: &ArgMatches<'_>) {
for i in 0..MAX_DOWNLOAD_ATTEMPTS { for i in 0..MAX_DOWNLOAD_ATTEMPTS {
eprintln!("Downloading {} (attempt {})", filename.display(), i); eprintln!("Downloading {} (attempt {})", filename.display(), i);
let mut command = Command::new("ffmpeg"); let mut command = Command::new("ffmpeg");
let command = command.args(&["-v", "warning", "-stats", "-nostdin", "-y", "-i"]); let command =
command.args(&["-v", "warning", "-stats", "-nostdin", "-y", "-i"]);
let mut command = command.arg(&download_url.video); let mut command = command.arg(&download_url.video);
if let Some(ref captions) = download_url.captions { if let Some(ref captions) = download_url.captions {
command = command.args(&["-i", &captions]); command = command.args(&["-i", &captions]);
} }
match command.args(&["-c", "copy", "-f", "matroska"]).arg(&tmp_filename).spawn() { match command
.args(&["-c", "copy", "-f", "matroska"])
.arg(&tmp_filename)
.spawn()
{
Ok(mut child) => { Ok(mut child) => {
match child.wait() { match child.wait() {
Ok(exit_status) => { Ok(exit_status) => {
@ -69,16 +78,29 @@ pub async fn download(arg_m: &ArgMatches<'_>) {
fail_dl = false; fail_dl = false;
match rename(&tmp_filename, &filename) { match rename(&tmp_filename, &filename) {
Ok(_) => (), Ok(_) => (),
Err(err) => eprintln!("Failed to rename {} to {} due to {}", tmp_filename.display(), filename.display(), err) Err(err) => eprintln!(
"Failed to rename {} to {} due to {}",
tmp_filename.display(),
filename.display(),
err
),
}; };
break; break;
} }
eprintln!("ffmpeg exited with {:?}", exit_status.code()); eprintln!(
}, "ffmpeg exited with {:?}",
Err(err) => eprintln!("Failed to wait on ffmpeg process due to {}", err) exit_status.code()
);
}
Err(err) => eprintln!(
"Failed to wait on ffmpeg process due to {}",
err
),
}; };
}, }
Err(err) => eprintln!("Failed to spawn ffmpeg process due to {}", err) Err(err) => {
eprintln!("Failed to spawn ffmpeg process due to {}", err)
}
}; };
} }
if fail_dl { if fail_dl {
@ -86,13 +108,13 @@ pub async fn download(arg_m: &ArgMatches<'_>) {
return_fail = true; return_fail = true;
} }
} }
}, }
None => { None => {
eprintln!("Failed to get {}: does not exist", id); eprintln!("Failed to get {}: does not exist", id);
return_fail = true; return_fail = true;
} }
}; };
}, }
Err(err) => { Err(err) => {
eprintln!("Failed to get {}: {}", id, err); eprintln!("Failed to get {}: {}", id, err);
return_fail = true; return_fail = true;

View File

@ -1,6 +1,6 @@
mod view;
mod search;
mod download; mod download;
pub use view::view; mod search;
pub use search::search; mod view;
pub use download::download; pub use download::download;
pub use search::search;
pub use view::view;

View File

@ -1,10 +1,14 @@
use crate::utils; use crate::utils;
use std::process::exit;
use clap::ArgMatches; use clap::ArgMatches;
use std::process::exit;
pub async fn search(arg_m: &ArgMatches<'_>) { pub async fn search(arg_m: &ArgMatches) {
let query = arg_m.values_of("query").unwrap_or_default().collect::<Vec<_>>().join(" "); let query = arg_m
.values_of("query")
.unwrap_or_default()
.collect::<Vec<_>>()
.join(" ");
let query = query.trim(); let query = query.trim();
let results = utils::search(utils::create_client(), query).await.unwrap(); let results = utils::search(utils::create_client(), query).await.unwrap();
if results.is_empty() { if results.is_empty() {

View File

@ -1,19 +1,24 @@
use crate::utils; use crate::utils;
use std::process::exit;
use clap::ArgMatches; use clap::ArgMatches;
use std::process::exit;
extern crate tokio; extern crate tokio;
pub async fn view(arg_m: &ArgMatches<'_>) { pub async fn view(arg_m: &ArgMatches) {
let client = utils::create_client(); let client = utils::create_client();
let handles = arg_m.values_of("id").unwrap().map(|id| { let handles = arg_m
let cloned_client = client.clone(); .values_of("id")
let id = id.to_string(); .unwrap()
let cid = id.clone(); .map(|id| {
(tokio::spawn(async move { let cloned_client = client.clone();
utils::get_hentai(cloned_client, &cid).await let id = id.to_string();
}), id) let cid = id.clone();
}).collect::<Vec<_>>(); (
tokio::spawn(async move { utils::get_hentai(cloned_client, &cid).await }),
id,
)
})
.collect::<Vec<_>>();
let mut fail = false; let mut fail = false;
let mut one_done = false; let mut one_done = false;
for handle in handles { for handle in handles {
@ -38,7 +43,7 @@ pub async fn view(arg_m: &ArgMatches<'_>) {
println!(""); println!("");
} }
println!("{}", &hentai); println!("{}", &hentai);
}, }
None => { None => {
if one_done { if one_done {
eprintln!(""); eprintln!("");
@ -47,7 +52,7 @@ pub async fn view(arg_m: &ArgMatches<'_>) {
fail = true; fail = true;
} }
}; };
}, }
Err(err) => { Err(err) => {
if one_done { if one_done {
eprintln!(""); eprintln!("");

View File

@ -1,46 +1,40 @@
mod commands; mod commands;
mod structs; mod structs;
mod utils; mod utils;
use clap::{App, AppSettings, Arg, SubCommand}; use clap::{Arg, Command};
extern crate tokio; extern crate tokio;
fn main() { fn main() {
let matches = App::new("hentaihavenrs") let matches = Command::new("hentaihavenrs")
.about("hentaihaven.tv downloader in rust") .about("hentaihaven.tv downloader in rust")
.version(env!("CARGO_PKG_VERSION")) .version(env!("CARGO_PKG_VERSION"))
.setting(AppSettings::SubcommandRequiredElseHelp) .subcommand_required(true)
.subcommand( .subcommand(
SubCommand::with_name("search") Command::new("search").arg(
.arg( Arg::new("query")
Arg::with_name("query")
.takes_value(true) .takes_value(true)
.multiple(true) .multiple_values(true)
.help("Search query") .help("Search query"),
) ),
) )
.subcommand( .subcommand(
SubCommand::with_name("view") Command::new("view").visible_aliases(&["info", "show"]).arg(
.aliases(&["info", "show"]) Arg::new("id")
.arg(
Arg::with_name("id")
.takes_value(true) .takes_value(true)
.multiple(true) .multiple_values(true)
.required(true) .required(true),
) ),
) )
.subcommand( .subcommand(
SubCommand::with_name("download") Command::new("download")
.alias("dl") .visible_alias("dl")
.arg( .arg(
Arg::with_name("print") Arg::new("print")
.long("print") .long("print")
.short("p") .short('p')
.help("Print the URL to download only") .help("Print the URL to download only"),
).arg( )
Arg::with_name("id") .arg(Arg::new("id").takes_value(true).required(true)),
.takes_value(true)
.required(true)
)
) )
.get_matches(); .get_matches();
@ -49,9 +43,9 @@ fn main() {
.build() .build()
.unwrap(); .unwrap();
match matches.subcommand() { match matches.subcommand() {
("search", Some(sub_m)) => runtime.block_on(commands::search(sub_m)), Some(("search", sub_m)) => runtime.block_on(commands::search(sub_m)),
("view", Some(sub_m)) => runtime.block_on(commands::view(sub_m)), Some(("view", sub_m)) => runtime.block_on(commands::view(sub_m)),
("download", Some(sub_m)) => runtime.block_on(commands::download(sub_m)), Some(("download", sub_m)) => runtime.block_on(commands::download(sub_m)),
_ => panic!("AppSettings::SubcommandRequiredElseHelp do your job please") _ => unreachable!("subcommand_required do your job please"),
}; };
} }

View File

@ -1,18 +1,18 @@
use std::fmt;
use serde::Deserialize; use serde::Deserialize;
extern crate reqwest; use std::fmt;
extern crate quick_xml; extern crate quick_xml;
extern crate reqwest;
extern crate serde_json; extern crate serde_json;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct SearchResult { pub struct SearchResult {
pub id: i32, pub id: i32,
pub title: RenderedTitle pub title: RenderedTitle,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct RenderedTitle { pub struct RenderedTitle {
pub rendered: String pub rendered: String,
} }
impl fmt::Display for SearchResult { impl fmt::Display for SearchResult {
@ -30,18 +30,20 @@ pub struct HentaiInfo {
pub genres: Vec<String>, pub genres: Vec<String>,
pub censored: bool, pub censored: bool,
pub episode_urls: Vec<String>, pub episode_urls: Vec<String>,
pub summary: String pub summary: String,
} }
impl fmt::Display for HentaiInfo { impl fmt::Display for HentaiInfo {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "ID: {}\nTitle: {}\nViews: {}\nCensored: {}\nGenres: {}\nEpisodes: {}\nSummary:\n{}", write!(
formatter,
"ID: {}\nTitle: {}\nViews: {}\nCensored: {}\nGenres: {}\nEpisodes: {}\nSummary:\n{}",
&self.id, &self.id,
&self.title, &self.title,
self.views, self.views,
match self.censored { match self.censored {
true => "Yes", true => "Yes",
false => "No" false => "No",
}, },
&self.genres.join(", "), &self.genres.join(", "),
self.episode_urls.len(), self.episode_urls.len(),
@ -53,7 +55,7 @@ impl fmt::Display for HentaiInfo {
#[derive(Debug)] #[derive(Debug)]
pub struct HentaiVideo { pub struct HentaiVideo {
pub captions: Option<String>, pub captions: Option<String>,
pub video: String pub video: String,
} }
impl fmt::Display for HentaiVideo { impl fmt::Display for HentaiVideo {
@ -69,18 +71,18 @@ impl fmt::Display for HentaiVideo {
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct RawHentaiVideo { pub struct RawHentaiVideo {
pub data: RawHentaiVideoData pub data: RawHentaiVideoData,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct RawHentaiVideoData { pub struct RawHentaiVideoData {
pub captions: RawHentaiVideoSrc, pub captions: RawHentaiVideoSrc,
pub sources: Vec<RawHentaiVideoSrc> pub sources: Vec<RawHentaiVideoSrc>,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct RawHentaiVideoSrc { pub struct RawHentaiVideoSrc {
pub src: String pub src: String,
} }
#[derive(Debug)] #[derive(Debug)]
@ -113,12 +115,10 @@ impl From<serde_json::Error> for Error {
impl fmt::Display for Error { impl fmt::Display for Error {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str( formatter.write_str(&match self {
&match self { Error::Reqwest(err) => format!("reqwest error: {}", err),
Error::Reqwest(err) => format!("reqwest error: {}", err), Error::QuickXML(err) => format!("quick-xml error: {}", err),
Error::QuickXML(err) => format!("quick-xml error: {}", err), Error::SerdeJSON(err) => format!("serde_json error: {}", err),
Error::SerdeJSON(err) => format!("serde_json error: {}", err), })
}
)
} }
} }

View File

@ -1,34 +1,46 @@
use crate::structs; use crate::structs;
use quick_xml::Reader;
use quick_xml::events::Event; use quick_xml::events::Event;
use quick_xml::Reader;
extern crate reqwest; extern crate reqwest;
extern crate serde_json; extern crate serde_json;
pub async fn search(client: reqwest::Client, query: &str) -> Result<Vec<structs::SearchResult>, structs::Error> { pub async fn search(
let text = &client.get("https://hentaihaven.xxx/wp-json/wp/v2/wp-manga") client: reqwest::Client,
.query(&[("search", &query)]) query: &str,
.send() ) -> Result<Vec<structs::SearchResult>, structs::Error> {
.await? let text = &client
.text() .get("https://hentaihaven.xxx/wp-json/wp/v2/wp-manga")
.await?; .query(&[("search", &query)])
.send()
.await?
.text()
.await?;
let text = text.trim_start_matches("\u{feff}"); let text = text.trim_start_matches("\u{feff}");
Ok(serde_json::from_str(&text)?) Ok(serde_json::from_str(&text)?)
} }
pub async fn get_hentai(client: reqwest::Client, id: &str) -> Result<Option<structs::HentaiInfo>, structs::Error> { pub async fn get_hentai(
client: reqwest::Client,
id: &str,
) -> Result<Option<structs::HentaiInfo>, structs::Error> {
let url = match id.contains(|c: char| !c.is_digit(10)) { let url = match id.contains(|c: char| !c.is_digit(10)) {
true => format!("https://hentaihaven.xxx/watch/{}", &id), true => format!("https://hentaihaven.xxx/watch/{}", &id),
false => format!("https://hentaihaven.xxx/?p={}", &id) false => format!("https://hentaihaven.xxx/?p={}", &id),
}; };
let resp = client.get(&url) let resp = client.get(&url).send().await?;
.send()
.await?;
if resp.status() != 200 { if resp.status() != 200 {
return Ok(None); return Ok(None);
} }
let mut id = String::new(); let mut id = String::new();
let slug = resp.url().path().trim_end_matches('/').rsplitn(2, '/').nth(0).unwrap().to_string(); let slug = resp
.url()
.path()
.trim_end_matches('/')
.rsplitn(2, '/')
.nth(0)
.unwrap()
.to_string();
let text = resp.text().await?.replace("&nbsp;", " "); let text = resp.text().await?.replace("&nbsp;", " ");
let mut reader = Reader::from_str(&text); let mut reader = Reader::from_str(&text);
reader.check_end_names(false); reader.check_end_names(false);
@ -52,13 +64,10 @@ pub async fn get_hentai(client: reqwest::Client, id: &str) -> Result<Option<stru
match reader.read_event(&mut buf) { match reader.read_event(&mut buf) {
Ok(Event::Start(ref e)) => { Ok(Event::Start(ref e)) => {
if e.name() == b"div" { if e.name() == b"div" {
let class = e.attributes() let class = e.attributes().find(|i| match i.as_ref() {
.find(|i| { Ok(i) => i.key == b"class",
match i.as_ref() { Err(_) => false,
Ok(i) => i.key == b"class", });
Err(_) => false
}
});
if let Some(class) = class { if let Some(class) = class {
match class.unwrap().unescape_and_decode_value(&reader) { match class.unwrap().unescape_and_decode_value(&reader) {
Ok(class_name) => { Ok(class_name) => {
@ -69,80 +78,75 @@ pub async fn get_hentai(client: reqwest::Client, id: &str) -> Result<Option<stru
"nav-links" => is_inside_nav_links = true, "nav-links" => is_inside_nav_links = true,
"listing-chapters_wrap" => is_inside_chapter_list = true, "listing-chapters_wrap" => is_inside_chapter_list = true,
"summary__content show-more" => is_inside_summary = true, "summary__content show-more" => is_inside_summary = true,
_ => () _ => (),
}; };
}, }
Err(_) => () Err(_) => (),
}; };
} }
} else if e.name() == b"a" { } else if e.name() == b"a" {
is_inside_a = true; is_inside_a = true;
if is_inside_nav_links { if is_inside_nav_links {
let class = e.attributes() let class = e.attributes().find(|i| match i.as_ref() {
.find(|i| { Ok(i) => i.key == b"class",
match i.as_ref() { Err(_) => false,
Ok(i) => i.key == b"class", });
Err(_) => false
}
});
if let Some(class) = class { if let Some(class) = class {
match class.unwrap().unescape_and_decode_value(&reader) { match class.unwrap().unescape_and_decode_value(&reader) {
Ok(class_name) => { Ok(class_name) => {
if class_name.to_lowercase().split_whitespace().any(|i| i == "uncensored") { if class_name
.to_lowercase()
.split_whitespace()
.any(|i| i == "uncensored")
{
censored = false; censored = false;
is_inside_nav_links = false; is_inside_nav_links = false;
} }
}, }
Err(_) => () Err(_) => (),
}; };
} }
} else if is_inside_chapter_list { } else if is_inside_chapter_list {
let href = e.attributes() let href = e.attributes().find(|i| match i.as_ref() {
.find(|i| { Ok(i) => i.key == b"href",
match i.as_ref() { Err(_) => false,
Ok(i) => i.key == b"href", });
Err(_) => false
}
});
if let Some(href) = href { if let Some(href) = href {
match href.unwrap().unescape_and_decode_value(&reader) { match href.unwrap().unescape_and_decode_value(&reader) {
Ok(href) => episode_urls.push(href), Ok(href) => episode_urls.push(href),
Err(_) => () Err(_) => (),
}; };
} }
} else if id.is_empty() { } else if id.is_empty() {
let data_post = e.attributes() let data_post = e.attributes().find(|i| match i.as_ref() {
.find(|i| { Ok(i) => i.key == b"data-post",
match i.as_ref() { Err(_) => false,
Ok(i) => i.key == b"data-post", });
Err(_) => false
}
});
if let Some(data_post) = data_post { if let Some(data_post) = data_post {
match data_post.unwrap().unescape_and_decode_value(&reader) { match data_post.unwrap().unescape_and_decode_value(&reader) {
Ok(data_post) => id = data_post, Ok(data_post) => id = data_post,
Err(_) => () Err(_) => (),
}; };
} }
} }
} }
}, }
Ok(Event::Text(e)) => { Ok(Event::Text(e)) => {
let text = match e.unescape_and_decode(&reader) { let text = match e.unescape_and_decode(&reader) {
Ok(text) => text, Ok(text) => text,
Err(_) => continue Err(_) => continue,
}; };
if is_inside_summary_heading { if is_inside_summary_heading {
match text.trim() { match text.trim() {
"Rank" => to_read_rank = true, "Rank" => to_read_rank = true,
"Genre(s)" => to_read_genres = true, "Genre(s)" => to_read_genres = true,
_ => () _ => (),
}; };
} else if is_inside_summary_content { } else if is_inside_summary_content {
if to_read_rank { if to_read_rank {
match text.trim().splitn(2, " ").nth(0).unwrap().parse::<usize>() { match text.trim().splitn(2, " ").nth(0).unwrap().parse::<usize>() {
Ok(i) => rank = i, Ok(i) => rank = i,
Err(_) => () Err(_) => (),
}; };
to_read_rank = false; to_read_rank = false;
} else if to_read_genres && is_inside_a { } else if to_read_genres && is_inside_a {
@ -153,7 +157,7 @@ pub async fn get_hentai(client: reqwest::Client, id: &str) -> Result<Option<stru
} else if is_inside_summary { } else if is_inside_summary {
summary.push_str(&text); summary.push_str(&text);
} }
}, }
Ok(Event::End(ref e)) => { Ok(Event::End(ref e)) => {
if e.name() == b"div" { if e.name() == b"div" {
if is_inside_summary_heading { if is_inside_summary_heading {
@ -174,10 +178,10 @@ pub async fn get_hentai(client: reqwest::Client, id: &str) -> Result<Option<stru
} else if e.name() == b"a" { } else if e.name() == b"a" {
is_inside_a = false; is_inside_a = false;
} }
}, }
Err(err) => panic!("Error at position {}: {:?}", reader.buffer_position(), err), Err(err) => panic!("Error at position {}: {:?}", reader.buffer_position(), err),
Ok(Event::Eof) => break, Ok(Event::Eof) => break,
_ => () _ => (),
}; };
buf.clear(); buf.clear();
} }
@ -194,14 +198,15 @@ pub async fn get_hentai(client: reqwest::Client, id: &str) -> Result<Option<stru
genres: genres, genres: genres,
censored: censored, censored: censored,
episode_urls: episode_urls, episode_urls: episode_urls,
summary: summary summary: summary,
})) }))
} }
pub async fn get_url(client: reqwest::Client, url: &str) -> Result<Option<structs::HentaiVideo>, structs::Error> { pub async fn get_url(
let resp = client.get(url) client: reqwest::Client,
.send() url: &str,
.await?; ) -> Result<Option<structs::HentaiVideo>, structs::Error> {
let resp = client.get(url).send().await?;
if resp.status() != 200 { if resp.status() != 200 {
return Ok(None); return Ok(None);
} }
@ -213,40 +218,38 @@ pub async fn get_url(client: reqwest::Client, url: &str) -> Result<Option<struct
loop { loop {
match reader.read_event(&mut buf) { match reader.read_event(&mut buf) {
Ok(Event::Start(ref e)) if e.name() == b"iframe" => { Ok(Event::Start(ref e)) if e.name() == b"iframe" => {
let src = e.attributes() let src = e.attributes().find(|i| match i.as_ref() {
.find(|i| { Ok(i) => i.key == b"src",
match i.as_ref() { Err(_) => false,
Ok(i) => i.key == b"src", });
Err(_) => false
}
});
if let Some(src) = src { if let Some(src) = src {
match src.unwrap().unescape_and_decode_value(&reader) { match src.unwrap().unescape_and_decode_value(&reader) {
Ok(src) => { Ok(src) => {
iframe_url = Some(src); iframe_url = Some(src);
break break;
}, }
Err(_) => () Err(_) => (),
}; };
} }
}, }
Err(err) => panic!("Error at position {}: {:?}", reader.buffer_position(), err), Err(err) => panic!("Error at position {}: {:?}", reader.buffer_position(), err),
Ok(Event::Eof) => break, Ok(Event::Eof) => break,
_ => () _ => (),
}; };
buf.clear(); buf.clear();
} }
let iframe_url = match iframe_url { let iframe_url = match iframe_url {
Some(tmp) => tmp, Some(tmp) => tmp,
None => return Ok(None) None => return Ok(None),
}; };
parse_iframe(client, &iframe_url).await parse_iframe(client, &iframe_url).await
} }
async fn parse_iframe(client: reqwest::Client, url: &str) -> Result<Option<structs::HentaiVideo>, structs::Error> { async fn parse_iframe(
let resp = client.get(url) client: reqwest::Client,
.send() url: &str,
.await?; ) -> Result<Option<structs::HentaiVideo>, structs::Error> {
let resp = client.get(url).send().await?;
if resp.status() != 200 { if resp.status() != 200 {
return Ok(None); return Ok(None);
} }
@ -261,36 +264,40 @@ async fn parse_iframe(client: reqwest::Client, url: &str) -> Result<Option<struc
Ok(Event::Text(e)) => { Ok(Event::Text(e)) => {
let text = match reader.decode(e.escaped()) { let text = match reader.decode(e.escaped()) {
Ok(text) => text, Ok(text) => text,
Err(_) => continue Err(_) => continue,
}; };
for i in text.split('\n') { for i in text.split('\n') {
let i = i.trim(); let i = i.trim();
if !i.starts_with("data.append('") { if !i.starts_with("data.append('") {
continue; continue;
} }
let mut i = i.trim_start_matches("data.append('").trim_end_matches("');").splitn(2, "', '"); let mut i = i
.trim_start_matches("data.append('")
.trim_end_matches("');")
.splitn(2, "', '");
let key = match i.next() { let key = match i.next() {
Some(i) => i, Some(i) => i,
None => continue None => continue,
}; };
let value = match i.next() { let value = match i.next() {
Some(i) => i, Some(i) => i,
None => continue None => continue,
}; };
form = form.text(key.to_string(), value.to_string()); form = form.text(key.to_string(), value.to_string());
form_modified = true; form_modified = true;
} }
}, }
Err(err) => panic!("Error at position {}: {:?}", reader.buffer_position(), err), Err(err) => panic!("Error at position {}: {:?}", reader.buffer_position(), err),
Ok(Event::Eof) => break, Ok(Event::Eof) => break,
_ => () _ => (),
}; };
buf.clear(); buf.clear();
} }
if !form_modified { if !form_modified {
return Ok(None); return Ok(None);
} }
let text = client.post("https://hentaihaven.xxx/wp-admin/admin-ajax.php") let text = client
.post("https://hentaihaven.xxx/wp-admin/admin-ajax.php")
.multipart(form) .multipart(form)
.send() .send()
.await? .await?
@ -299,17 +306,23 @@ async fn parse_iframe(client: reqwest::Client, url: &str) -> Result<Option<struc
let text = text.trim_start_matches("\u{feff}"); let text = text.trim_start_matches("\u{feff}");
let raw_data: structs::RawHentaiVideo = serde_json::from_str(&text)?; let raw_data: structs::RawHentaiVideo = serde_json::from_str(&text)?;
let raw_data = raw_data.data; let raw_data = raw_data.data;
let captions = match client.get(&raw_data.captions.src).send().await?.status().as_u16() { let captions = match client
.get(&raw_data.captions.src)
.send()
.await?
.status()
.as_u16()
{
200 => Some(raw_data.captions.src), 200 => Some(raw_data.captions.src),
_ => None _ => None,
}; };
let video_url = match raw_data.sources.get(0) { let video_url = match raw_data.sources.get(0) {
Some(i) => i.src.clone(), Some(i) => i.src.clone(),
None => return Ok(None) None => return Ok(None),
}; };
Ok(Some(structs::HentaiVideo { Ok(Some(structs::HentaiVideo {
captions: captions, captions: captions,
video: video_url video: video_url,
})) }))
} }