Update dependencies
This commit is contained in:
		
							parent
							
								
									56898120d9
								
							
						
					
					
						commit
						f12edb405e
					
				
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
[package]
 | 
			
		||||
name = "hentaihavenrs"
 | 
			
		||||
version = "0.1.1"
 | 
			
		||||
version = "0.1.2"
 | 
			
		||||
authors = ["blank X <theblankx@protonmail.com>"]
 | 
			
		||||
edition = "2018"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -10,9 +10,9 @@ edition = "2018"
 | 
			
		|||
lto = true
 | 
			
		||||
 | 
			
		||||
[dependencies]
 | 
			
		||||
tokio = { version = "1.1", features = ["rt"] }
 | 
			
		||||
tokio = { version = "1.18", features = ["rt"] }
 | 
			
		||||
reqwest = { version = "0.11", features = ["multipart", "rustls-tls"] }
 | 
			
		||||
serde = { version = "1.0", features = ["derive"] }
 | 
			
		||||
serde_json = "1.0"
 | 
			
		||||
quick-xml = "0.20"
 | 
			
		||||
clap = { version = "2.33", default-features = false }
 | 
			
		||||
quick-xml = "0.22"
 | 
			
		||||
clap = { version = "3.1", features = ["std"], default-features = false }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,14 +1,14 @@
 | 
			
		|||
use crate::utils;
 | 
			
		||||
 | 
			
		||||
use clap::ArgMatches;
 | 
			
		||||
use std::fs::{create_dir, rename};
 | 
			
		||||
use std::io;
 | 
			
		||||
use std::fs::{rename, create_dir};
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use std::process::{exit, Command};
 | 
			
		||||
use clap::ArgMatches;
 | 
			
		||||
 | 
			
		||||
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 id = arg_m.value_of("id").unwrap();
 | 
			
		||||
    let client = utils::create_client();
 | 
			
		||||
| 
						 | 
				
			
			@ -26,17 +26,21 @@ pub async fn download(arg_m: &ArgMatches<'_>) {
 | 
			
		|||
                        if filename.exists() {
 | 
			
		||||
                            continue;
 | 
			
		||||
                        }
 | 
			
		||||
                        let download_url = match utils::get_url(tcloned_client.clone(), &episode_url).await {
 | 
			
		||||
                            Ok(Some(i)) => i,
 | 
			
		||||
                            Ok(None) => {
 | 
			
		||||
                                eprintln!("Failed to get {}: get_url returned None", filename.display());
 | 
			
		||||
                                continue;
 | 
			
		||||
                            },
 | 
			
		||||
                            Err(err) => {
 | 
			
		||||
                                eprintln!("Failed to get {}: {}", filename.display(), err);
 | 
			
		||||
                                continue;
 | 
			
		||||
                            }
 | 
			
		||||
                        };
 | 
			
		||||
                        let download_url =
 | 
			
		||||
                            match utils::get_url(tcloned_client.clone(), &episode_url).await {
 | 
			
		||||
                                Ok(Some(i)) => i,
 | 
			
		||||
                                Ok(None) => {
 | 
			
		||||
                                    eprintln!(
 | 
			
		||||
                                        "Failed to get {}: get_url returned None",
 | 
			
		||||
                                        filename.display()
 | 
			
		||||
                                    );
 | 
			
		||||
                                    continue;
 | 
			
		||||
                                }
 | 
			
		||||
                                Err(err) => {
 | 
			
		||||
                                    eprintln!("Failed to get {}: {}", filename.display(), err);
 | 
			
		||||
                                    continue;
 | 
			
		||||
                                }
 | 
			
		||||
                            };
 | 
			
		||||
                        if print_only {
 | 
			
		||||
                            println!("{}", download_url);
 | 
			
		||||
                            continue;
 | 
			
		||||
| 
						 | 
				
			
			@ -56,12 +60,17 @@ pub async fn download(arg_m: &ArgMatches<'_>) {
 | 
			
		|||
                        for i in 0..MAX_DOWNLOAD_ATTEMPTS {
 | 
			
		||||
                            eprintln!("Downloading {} (attempt {})", filename.display(), i);
 | 
			
		||||
                            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);
 | 
			
		||||
                            if let Some(ref captions) = download_url.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) => {
 | 
			
		||||
                                    match child.wait() {
 | 
			
		||||
                                        Ok(exit_status) => {
 | 
			
		||||
| 
						 | 
				
			
			@ -69,16 +78,29 @@ pub async fn download(arg_m: &ArgMatches<'_>) {
 | 
			
		|||
                                                fail_dl = false;
 | 
			
		||||
                                                match rename(&tmp_filename, &filename) {
 | 
			
		||||
                                                    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;
 | 
			
		||||
                                            }
 | 
			
		||||
                                            eprintln!("ffmpeg exited with {:?}", exit_status.code());
 | 
			
		||||
                                        },
 | 
			
		||||
                                        Err(err) => eprintln!("Failed to wait on ffmpeg process due to {}", err)
 | 
			
		||||
                                            eprintln!(
 | 
			
		||||
                                                "ffmpeg exited with {:?}",
 | 
			
		||||
                                                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 {
 | 
			
		||||
| 
						 | 
				
			
			@ -86,13 +108,13 @@ pub async fn download(arg_m: &ArgMatches<'_>) {
 | 
			
		|||
                            return_fail = true;
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                },
 | 
			
		||||
                }
 | 
			
		||||
                None => {
 | 
			
		||||
                    eprintln!("Failed to get {}: does not exist", id);
 | 
			
		||||
                    return_fail = true;
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
        },
 | 
			
		||||
        }
 | 
			
		||||
        Err(err) => {
 | 
			
		||||
            eprintln!("Failed to get {}: {}", id, err);
 | 
			
		||||
            return_fail = true;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
mod view;
 | 
			
		||||
mod search;
 | 
			
		||||
mod download;
 | 
			
		||||
pub use view::view;
 | 
			
		||||
pub use search::search;
 | 
			
		||||
mod search;
 | 
			
		||||
mod view;
 | 
			
		||||
pub use download::download;
 | 
			
		||||
pub use search::search;
 | 
			
		||||
pub use view::view;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,14 @@
 | 
			
		|||
use crate::utils;
 | 
			
		||||
 | 
			
		||||
use std::process::exit;
 | 
			
		||||
use clap::ArgMatches;
 | 
			
		||||
use std::process::exit;
 | 
			
		||||
 | 
			
		||||
pub async fn search(arg_m: &ArgMatches<'_>) {
 | 
			
		||||
    let query = arg_m.values_of("query").unwrap_or_default().collect::<Vec<_>>().join(" ");
 | 
			
		||||
pub async fn search(arg_m: &ArgMatches) {
 | 
			
		||||
    let query = arg_m
 | 
			
		||||
        .values_of("query")
 | 
			
		||||
        .unwrap_or_default()
 | 
			
		||||
        .collect::<Vec<_>>()
 | 
			
		||||
        .join(" ");
 | 
			
		||||
    let query = query.trim();
 | 
			
		||||
    let results = utils::search(utils::create_client(), query).await.unwrap();
 | 
			
		||||
    if results.is_empty() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,19 +1,24 @@
 | 
			
		|||
use crate::utils;
 | 
			
		||||
 | 
			
		||||
use std::process::exit;
 | 
			
		||||
use clap::ArgMatches;
 | 
			
		||||
use std::process::exit;
 | 
			
		||||
extern crate tokio;
 | 
			
		||||
 | 
			
		||||
pub async fn view(arg_m: &ArgMatches<'_>) {
 | 
			
		||||
pub async fn view(arg_m: &ArgMatches) {
 | 
			
		||||
    let client = utils::create_client();
 | 
			
		||||
    let handles = arg_m.values_of("id").unwrap().map(|id| {
 | 
			
		||||
        let cloned_client = client.clone();
 | 
			
		||||
        let id = id.to_string();
 | 
			
		||||
        let cid = id.clone();
 | 
			
		||||
        (tokio::spawn(async move {
 | 
			
		||||
            utils::get_hentai(cloned_client, &cid).await
 | 
			
		||||
        }), id)
 | 
			
		||||
    }).collect::<Vec<_>>();
 | 
			
		||||
    let handles = arg_m
 | 
			
		||||
        .values_of("id")
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .map(|id| {
 | 
			
		||||
            let cloned_client = client.clone();
 | 
			
		||||
            let id = id.to_string();
 | 
			
		||||
            let cid = id.clone();
 | 
			
		||||
            (
 | 
			
		||||
                tokio::spawn(async move { utils::get_hentai(cloned_client, &cid).await }),
 | 
			
		||||
                id,
 | 
			
		||||
            )
 | 
			
		||||
        })
 | 
			
		||||
        .collect::<Vec<_>>();
 | 
			
		||||
    let mut fail = false;
 | 
			
		||||
    let mut one_done = false;
 | 
			
		||||
    for handle in handles {
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +43,7 @@ pub async fn view(arg_m: &ArgMatches<'_>) {
 | 
			
		|||
                            println!("");
 | 
			
		||||
                        }
 | 
			
		||||
                        println!("{}", &hentai);
 | 
			
		||||
                    },
 | 
			
		||||
                    }
 | 
			
		||||
                    None => {
 | 
			
		||||
                        if one_done {
 | 
			
		||||
                            eprintln!("");
 | 
			
		||||
| 
						 | 
				
			
			@ -47,7 +52,7 @@ pub async fn view(arg_m: &ArgMatches<'_>) {
 | 
			
		|||
                        fail = true;
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
            },
 | 
			
		||||
            }
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
                if one_done {
 | 
			
		||||
                    eprintln!("");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										58
									
								
								src/main.rs
								
								
								
								
							
							
						
						
									
										58
									
								
								src/main.rs
								
								
								
								
							| 
						 | 
				
			
			@ -1,46 +1,40 @@
 | 
			
		|||
mod commands;
 | 
			
		||||
mod structs;
 | 
			
		||||
mod utils;
 | 
			
		||||
use clap::{App, AppSettings, Arg, SubCommand};
 | 
			
		||||
use clap::{Arg, Command};
 | 
			
		||||
extern crate tokio;
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
    let matches = App::new("hentaihavenrs")
 | 
			
		||||
    let matches = Command::new("hentaihavenrs")
 | 
			
		||||
        .about("hentaihaven.tv downloader in rust")
 | 
			
		||||
        .version(env!("CARGO_PKG_VERSION"))
 | 
			
		||||
        .setting(AppSettings::SubcommandRequiredElseHelp)
 | 
			
		||||
        .subcommand_required(true)
 | 
			
		||||
        .subcommand(
 | 
			
		||||
            SubCommand::with_name("search")
 | 
			
		||||
            .arg(
 | 
			
		||||
                Arg::with_name("query")
 | 
			
		||||
            Command::new("search").arg(
 | 
			
		||||
                Arg::new("query")
 | 
			
		||||
                    .takes_value(true)
 | 
			
		||||
                    .multiple(true)
 | 
			
		||||
                    .help("Search query")
 | 
			
		||||
            )
 | 
			
		||||
                    .multiple_values(true)
 | 
			
		||||
                    .help("Search query"),
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
        .subcommand(
 | 
			
		||||
            SubCommand::with_name("view")
 | 
			
		||||
            .aliases(&["info", "show"])
 | 
			
		||||
            .arg(
 | 
			
		||||
                Arg::with_name("id")
 | 
			
		||||
            Command::new("view").visible_aliases(&["info", "show"]).arg(
 | 
			
		||||
                Arg::new("id")
 | 
			
		||||
                    .takes_value(true)
 | 
			
		||||
                    .multiple(true)
 | 
			
		||||
                    .required(true)
 | 
			
		||||
            )
 | 
			
		||||
                    .multiple_values(true)
 | 
			
		||||
                    .required(true),
 | 
			
		||||
            ),
 | 
			
		||||
        )
 | 
			
		||||
        .subcommand(
 | 
			
		||||
            SubCommand::with_name("download")
 | 
			
		||||
            .alias("dl")
 | 
			
		||||
            .arg(
 | 
			
		||||
                Arg::with_name("print")
 | 
			
		||||
                    .long("print")
 | 
			
		||||
                    .short("p")
 | 
			
		||||
                    .help("Print the URL to download only")
 | 
			
		||||
            ).arg(
 | 
			
		||||
                Arg::with_name("id")
 | 
			
		||||
                    .takes_value(true)
 | 
			
		||||
                    .required(true)
 | 
			
		||||
            )
 | 
			
		||||
            Command::new("download")
 | 
			
		||||
                .visible_alias("dl")
 | 
			
		||||
                .arg(
 | 
			
		||||
                    Arg::new("print")
 | 
			
		||||
                        .long("print")
 | 
			
		||||
                        .short('p')
 | 
			
		||||
                        .help("Print the URL to download only"),
 | 
			
		||||
                )
 | 
			
		||||
                .arg(Arg::new("id").takes_value(true).required(true)),
 | 
			
		||||
        )
 | 
			
		||||
        .get_matches();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -49,9 +43,9 @@ fn main() {
 | 
			
		|||
        .build()
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    match matches.subcommand() {
 | 
			
		||||
        ("search", Some(sub_m)) => runtime.block_on(commands::search(sub_m)),
 | 
			
		||||
        ("view", Some(sub_m)) => runtime.block_on(commands::view(sub_m)),
 | 
			
		||||
        ("download", Some(sub_m)) => runtime.block_on(commands::download(sub_m)),
 | 
			
		||||
        _ => panic!("AppSettings::SubcommandRequiredElseHelp do your job please")
 | 
			
		||||
        Some(("search", sub_m)) => runtime.block_on(commands::search(sub_m)),
 | 
			
		||||
        Some(("view", sub_m)) => runtime.block_on(commands::view(sub_m)),
 | 
			
		||||
        Some(("download", sub_m)) => runtime.block_on(commands::download(sub_m)),
 | 
			
		||||
        _ => unreachable!("subcommand_required do your job please"),
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,18 +1,18 @@
 | 
			
		|||
use std::fmt;
 | 
			
		||||
use serde::Deserialize;
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
use std::fmt;
 | 
			
		||||
extern crate quick_xml;
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
extern crate serde_json;
 | 
			
		||||
 | 
			
		||||
#[derive(Deserialize, Debug)]
 | 
			
		||||
pub struct SearchResult {
 | 
			
		||||
    pub id: i32,
 | 
			
		||||
    pub title: RenderedTitle
 | 
			
		||||
    pub title: RenderedTitle,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Deserialize, Debug)]
 | 
			
		||||
pub struct RenderedTitle {
 | 
			
		||||
    pub rendered: String
 | 
			
		||||
    pub rendered: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for SearchResult {
 | 
			
		||||
| 
						 | 
				
			
			@ -30,18 +30,20 @@ pub struct HentaiInfo {
 | 
			
		|||
    pub genres: Vec<String>,
 | 
			
		||||
    pub censored: bool,
 | 
			
		||||
    pub episode_urls: Vec<String>,
 | 
			
		||||
    pub summary: String
 | 
			
		||||
    pub summary: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for HentaiInfo {
 | 
			
		||||
    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.title,
 | 
			
		||||
            self.views,
 | 
			
		||||
            match self.censored {
 | 
			
		||||
                true => "Yes",
 | 
			
		||||
                false => "No"
 | 
			
		||||
                false => "No",
 | 
			
		||||
            },
 | 
			
		||||
            &self.genres.join(", "),
 | 
			
		||||
            self.episode_urls.len(),
 | 
			
		||||
| 
						 | 
				
			
			@ -53,7 +55,7 @@ impl fmt::Display for HentaiInfo {
 | 
			
		|||
#[derive(Debug)]
 | 
			
		||||
pub struct HentaiVideo {
 | 
			
		||||
    pub captions: Option<String>,
 | 
			
		||||
    pub video: String
 | 
			
		||||
    pub video: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for HentaiVideo {
 | 
			
		||||
| 
						 | 
				
			
			@ -69,18 +71,18 @@ impl fmt::Display for HentaiVideo {
 | 
			
		|||
 | 
			
		||||
#[derive(Deserialize, Debug)]
 | 
			
		||||
pub struct RawHentaiVideo {
 | 
			
		||||
    pub data: RawHentaiVideoData
 | 
			
		||||
    pub data: RawHentaiVideoData,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Deserialize, Debug)]
 | 
			
		||||
pub struct RawHentaiVideoData {
 | 
			
		||||
    pub captions: RawHentaiVideoSrc,
 | 
			
		||||
    pub sources: Vec<RawHentaiVideoSrc>
 | 
			
		||||
    pub sources: Vec<RawHentaiVideoSrc>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Deserialize, Debug)]
 | 
			
		||||
pub struct RawHentaiVideoSrc {
 | 
			
		||||
    pub src: String
 | 
			
		||||
    pub src: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
| 
						 | 
				
			
			@ -113,12 +115,10 @@ impl From<serde_json::Error> for Error {
 | 
			
		|||
 | 
			
		||||
impl fmt::Display for Error {
 | 
			
		||||
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        formatter.write_str(
 | 
			
		||||
            &match self {
 | 
			
		||||
                Error::Reqwest(err) => format!("reqwest error: {}", err),
 | 
			
		||||
                Error::QuickXML(err) => format!("quick-xml error: {}", err),
 | 
			
		||||
                Error::SerdeJSON(err) => format!("serde_json error: {}", err),
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
        formatter.write_str(&match self {
 | 
			
		||||
            Error::Reqwest(err) => format!("reqwest error: {}", err),
 | 
			
		||||
            Error::QuickXML(err) => format!("quick-xml error: {}", err),
 | 
			
		||||
            Error::SerdeJSON(err) => format!("serde_json error: {}", err),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										193
									
								
								src/utils.rs
								
								
								
								
							
							
						
						
									
										193
									
								
								src/utils.rs
								
								
								
								
							| 
						 | 
				
			
			@ -1,34 +1,46 @@
 | 
			
		|||
use crate::structs;
 | 
			
		||||
 | 
			
		||||
use quick_xml::Reader;
 | 
			
		||||
use quick_xml::events::Event;
 | 
			
		||||
use quick_xml::Reader;
 | 
			
		||||
extern crate reqwest;
 | 
			
		||||
extern crate serde_json;
 | 
			
		||||
 | 
			
		||||
pub async fn search(client: reqwest::Client, query: &str) -> Result<Vec<structs::SearchResult>, structs::Error> {
 | 
			
		||||
    let text = &client.get("https://hentaihaven.xxx/wp-json/wp/v2/wp-manga")
 | 
			
		||||
            .query(&[("search", &query)])
 | 
			
		||||
            .send()
 | 
			
		||||
            .await?
 | 
			
		||||
            .text()
 | 
			
		||||
            .await?;
 | 
			
		||||
pub async fn search(
 | 
			
		||||
    client: reqwest::Client,
 | 
			
		||||
    query: &str,
 | 
			
		||||
) -> Result<Vec<structs::SearchResult>, structs::Error> {
 | 
			
		||||
    let text = &client
 | 
			
		||||
        .get("https://hentaihaven.xxx/wp-json/wp/v2/wp-manga")
 | 
			
		||||
        .query(&[("search", &query)])
 | 
			
		||||
        .send()
 | 
			
		||||
        .await?
 | 
			
		||||
        .text()
 | 
			
		||||
        .await?;
 | 
			
		||||
    let text = text.trim_start_matches("\u{feff}");
 | 
			
		||||
    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)) {
 | 
			
		||||
        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)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await?;
 | 
			
		||||
    let resp = client.get(&url).send().await?;
 | 
			
		||||
    if resp.status() != 200 {
 | 
			
		||||
        return Ok(None);
 | 
			
		||||
    }
 | 
			
		||||
    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(" ", " ");
 | 
			
		||||
    let mut reader = Reader::from_str(&text);
 | 
			
		||||
    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) {
 | 
			
		||||
            Ok(Event::Start(ref e)) => {
 | 
			
		||||
                if e.name() == b"div" {
 | 
			
		||||
                    let class = e.attributes()
 | 
			
		||||
                        .find(|i| {
 | 
			
		||||
                            match i.as_ref() {
 | 
			
		||||
                                Ok(i) => i.key == b"class",
 | 
			
		||||
                                Err(_) => false
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                    let class = e.attributes().find(|i| match i.as_ref() {
 | 
			
		||||
                        Ok(i) => i.key == b"class",
 | 
			
		||||
                        Err(_) => false,
 | 
			
		||||
                    });
 | 
			
		||||
                    if let Some(class) = class {
 | 
			
		||||
                        match class.unwrap().unescape_and_decode_value(&reader) {
 | 
			
		||||
                            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,
 | 
			
		||||
                                    "listing-chapters_wrap" => is_inside_chapter_list = true,
 | 
			
		||||
                                    "summary__content show-more" => is_inside_summary = true,
 | 
			
		||||
                                    _ => ()
 | 
			
		||||
                                    _ => (),
 | 
			
		||||
                                };
 | 
			
		||||
                            },
 | 
			
		||||
                            Err(_) => ()
 | 
			
		||||
                            }
 | 
			
		||||
                            Err(_) => (),
 | 
			
		||||
                        };
 | 
			
		||||
                    }
 | 
			
		||||
                } else if e.name() == b"a" {
 | 
			
		||||
                    is_inside_a = true;
 | 
			
		||||
                    if is_inside_nav_links {
 | 
			
		||||
                        let class = e.attributes()
 | 
			
		||||
                            .find(|i| {
 | 
			
		||||
                                match i.as_ref() {
 | 
			
		||||
                                    Ok(i) => i.key == b"class",
 | 
			
		||||
                                    Err(_) => false
 | 
			
		||||
                                }
 | 
			
		||||
                            });
 | 
			
		||||
                        let class = e.attributes().find(|i| match i.as_ref() {
 | 
			
		||||
                            Ok(i) => i.key == b"class",
 | 
			
		||||
                            Err(_) => false,
 | 
			
		||||
                        });
 | 
			
		||||
                        if let Some(class) = class {
 | 
			
		||||
                            match class.unwrap().unescape_and_decode_value(&reader) {
 | 
			
		||||
                                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;
 | 
			
		||||
                                        is_inside_nav_links = false;
 | 
			
		||||
                                    }
 | 
			
		||||
                                },
 | 
			
		||||
                                Err(_) => ()
 | 
			
		||||
                                }
 | 
			
		||||
                                Err(_) => (),
 | 
			
		||||
                            };
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if is_inside_chapter_list {
 | 
			
		||||
                        let href = e.attributes()
 | 
			
		||||
                            .find(|i| {
 | 
			
		||||
                                match i.as_ref() {
 | 
			
		||||
                                    Ok(i) => i.key == b"href",
 | 
			
		||||
                                    Err(_) => false
 | 
			
		||||
                                }
 | 
			
		||||
                            });
 | 
			
		||||
                        let href = e.attributes().find(|i| match i.as_ref() {
 | 
			
		||||
                            Ok(i) => i.key == b"href",
 | 
			
		||||
                            Err(_) => false,
 | 
			
		||||
                        });
 | 
			
		||||
                        if let Some(href) = href {
 | 
			
		||||
                            match href.unwrap().unescape_and_decode_value(&reader) {
 | 
			
		||||
                                Ok(href) => episode_urls.push(href),
 | 
			
		||||
                                Err(_) => ()
 | 
			
		||||
                                Err(_) => (),
 | 
			
		||||
                            };
 | 
			
		||||
                        }
 | 
			
		||||
                    } else if id.is_empty() {
 | 
			
		||||
                        let data_post = e.attributes()
 | 
			
		||||
                            .find(|i| {
 | 
			
		||||
                                match i.as_ref() {
 | 
			
		||||
                                    Ok(i) => i.key == b"data-post",
 | 
			
		||||
                                    Err(_) => false
 | 
			
		||||
                                }
 | 
			
		||||
                            });
 | 
			
		||||
                        let data_post = e.attributes().find(|i| match i.as_ref() {
 | 
			
		||||
                            Ok(i) => i.key == b"data-post",
 | 
			
		||||
                            Err(_) => false,
 | 
			
		||||
                        });
 | 
			
		||||
                        if let Some(data_post) = data_post {
 | 
			
		||||
                            match data_post.unwrap().unescape_and_decode_value(&reader) {
 | 
			
		||||
                                Ok(data_post) => id = data_post,
 | 
			
		||||
                                Err(_) => ()
 | 
			
		||||
                                Err(_) => (),
 | 
			
		||||
                            };
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            }
 | 
			
		||||
            Ok(Event::Text(e)) => {
 | 
			
		||||
                let text = match e.unescape_and_decode(&reader) {
 | 
			
		||||
                    Ok(text) => text,
 | 
			
		||||
                    Err(_) => continue
 | 
			
		||||
                    Err(_) => continue,
 | 
			
		||||
                };
 | 
			
		||||
                if is_inside_summary_heading {
 | 
			
		||||
                    match text.trim() {
 | 
			
		||||
                        "Rank" => to_read_rank = true,
 | 
			
		||||
                        "Genre(s)" => to_read_genres = true,
 | 
			
		||||
                        _ => ()
 | 
			
		||||
                        _ => (),
 | 
			
		||||
                    };
 | 
			
		||||
                } else if is_inside_summary_content {
 | 
			
		||||
                    if to_read_rank {
 | 
			
		||||
                        match text.trim().splitn(2, " ").nth(0).unwrap().parse::<usize>() {
 | 
			
		||||
                            Ok(i) => rank = i,
 | 
			
		||||
                            Err(_) => ()
 | 
			
		||||
                            Err(_) => (),
 | 
			
		||||
                        };
 | 
			
		||||
                        to_read_rank = false;
 | 
			
		||||
                    } 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 {
 | 
			
		||||
                    summary.push_str(&text);
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            }
 | 
			
		||||
            Ok(Event::End(ref e)) => {
 | 
			
		||||
                if e.name() == b"div" {
 | 
			
		||||
                    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" {
 | 
			
		||||
                    is_inside_a = false;
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            }
 | 
			
		||||
            Err(err) => panic!("Error at position {}: {:?}", reader.buffer_position(), err),
 | 
			
		||||
            Ok(Event::Eof) => break,
 | 
			
		||||
            _ => ()
 | 
			
		||||
            _ => (),
 | 
			
		||||
        };
 | 
			
		||||
        buf.clear();
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -194,14 +198,15 @@ pub async fn get_hentai(client: reqwest::Client, id: &str) -> Result<Option<stru
 | 
			
		|||
        genres: genres,
 | 
			
		||||
        censored: censored,
 | 
			
		||||
        episode_urls: episode_urls,
 | 
			
		||||
        summary: summary
 | 
			
		||||
        summary: summary,
 | 
			
		||||
    }))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub async fn get_url(client: reqwest::Client, url: &str) -> Result<Option<structs::HentaiVideo>, structs::Error> {
 | 
			
		||||
    let resp = client.get(url)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await?;
 | 
			
		||||
pub async fn get_url(
 | 
			
		||||
    client: reqwest::Client,
 | 
			
		||||
    url: &str,
 | 
			
		||||
) -> Result<Option<structs::HentaiVideo>, structs::Error> {
 | 
			
		||||
    let resp = client.get(url).send().await?;
 | 
			
		||||
    if resp.status() != 200 {
 | 
			
		||||
        return Ok(None);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -213,40 +218,38 @@ pub async fn get_url(client: reqwest::Client, url: &str) -> Result<Option<struct
 | 
			
		|||
    loop {
 | 
			
		||||
        match reader.read_event(&mut buf) {
 | 
			
		||||
            Ok(Event::Start(ref e)) if e.name() == b"iframe" => {
 | 
			
		||||
                let src = e.attributes()
 | 
			
		||||
                    .find(|i| {
 | 
			
		||||
                        match i.as_ref() {
 | 
			
		||||
                            Ok(i) => i.key == b"src",
 | 
			
		||||
                            Err(_) => false
 | 
			
		||||
                        }
 | 
			
		||||
                    });
 | 
			
		||||
                let src = e.attributes().find(|i| match i.as_ref() {
 | 
			
		||||
                    Ok(i) => i.key == b"src",
 | 
			
		||||
                    Err(_) => false,
 | 
			
		||||
                });
 | 
			
		||||
                if let Some(src) = src {
 | 
			
		||||
                    match src.unwrap().unescape_and_decode_value(&reader) {
 | 
			
		||||
                        Ok(src) => {
 | 
			
		||||
                            iframe_url = Some(src);
 | 
			
		||||
                            break
 | 
			
		||||
                        },
 | 
			
		||||
                        Err(_) => ()
 | 
			
		||||
                            break;
 | 
			
		||||
                        }
 | 
			
		||||
                        Err(_) => (),
 | 
			
		||||
                    };
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            }
 | 
			
		||||
            Err(err) => panic!("Error at position {}: {:?}", reader.buffer_position(), err),
 | 
			
		||||
            Ok(Event::Eof) => break,
 | 
			
		||||
            _ => ()
 | 
			
		||||
            _ => (),
 | 
			
		||||
        };
 | 
			
		||||
        buf.clear();
 | 
			
		||||
    }
 | 
			
		||||
    let iframe_url = match iframe_url {
 | 
			
		||||
        Some(tmp) => tmp,
 | 
			
		||||
        None => return Ok(None)
 | 
			
		||||
        None => return Ok(None),
 | 
			
		||||
    };
 | 
			
		||||
    parse_iframe(client, &iframe_url).await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn parse_iframe(client: reqwest::Client, url: &str) -> Result<Option<structs::HentaiVideo>, structs::Error> {
 | 
			
		||||
    let resp = client.get(url)
 | 
			
		||||
        .send()
 | 
			
		||||
        .await?;
 | 
			
		||||
async fn parse_iframe(
 | 
			
		||||
    client: reqwest::Client,
 | 
			
		||||
    url: &str,
 | 
			
		||||
) -> Result<Option<structs::HentaiVideo>, structs::Error> {
 | 
			
		||||
    let resp = client.get(url).send().await?;
 | 
			
		||||
    if resp.status() != 200 {
 | 
			
		||||
        return Ok(None);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -261,36 +264,40 @@ async fn parse_iframe(client: reqwest::Client, url: &str) -> Result<Option<struc
 | 
			
		|||
            Ok(Event::Text(e)) => {
 | 
			
		||||
                let text = match reader.decode(e.escaped()) {
 | 
			
		||||
                    Ok(text) => text,
 | 
			
		||||
                    Err(_) => continue
 | 
			
		||||
                    Err(_) => continue,
 | 
			
		||||
                };
 | 
			
		||||
                for i in text.split('\n') {
 | 
			
		||||
                    let i = i.trim();
 | 
			
		||||
                    if !i.starts_with("data.append('") {
 | 
			
		||||
                        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() {
 | 
			
		||||
                        Some(i) => i,
 | 
			
		||||
                        None => continue
 | 
			
		||||
                        None => continue,
 | 
			
		||||
                    };
 | 
			
		||||
                    let value = match i.next() {
 | 
			
		||||
                        Some(i) => i,
 | 
			
		||||
                        None => continue
 | 
			
		||||
                        None => continue,
 | 
			
		||||
                    };
 | 
			
		||||
                    form = form.text(key.to_string(), value.to_string());
 | 
			
		||||
                    form_modified = true;
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            }
 | 
			
		||||
            Err(err) => panic!("Error at position {}: {:?}", reader.buffer_position(), err),
 | 
			
		||||
            Ok(Event::Eof) => break,
 | 
			
		||||
            _ => ()
 | 
			
		||||
            _ => (),
 | 
			
		||||
        };
 | 
			
		||||
        buf.clear();
 | 
			
		||||
    }
 | 
			
		||||
    if !form_modified {
 | 
			
		||||
        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)
 | 
			
		||||
        .send()
 | 
			
		||||
        .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 raw_data: structs::RawHentaiVideo = serde_json::from_str(&text)?;
 | 
			
		||||
    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),
 | 
			
		||||
        _ => None
 | 
			
		||||
        _ => None,
 | 
			
		||||
    };
 | 
			
		||||
    let video_url = match raw_data.sources.get(0) {
 | 
			
		||||
        Some(i) => i.src.clone(),
 | 
			
		||||
        None => return Ok(None)
 | 
			
		||||
        None => return Ok(None),
 | 
			
		||||
    };
 | 
			
		||||
    Ok(Some(structs::HentaiVideo {
 | 
			
		||||
        captions: captions,
 | 
			
		||||
        video: video_url
 | 
			
		||||
        video: video_url,
 | 
			
		||||
    }))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue