2021-02-02 16:52:15 +00:00
|
|
|
use crate::utils;
|
|
|
|
|
2022-04-30 11:30:36 +00:00
|
|
|
use clap::ArgMatches;
|
|
|
|
use reqwest::redirect::Policy;
|
|
|
|
use reqwest::Client;
|
|
|
|
use std::collections::HashMap;
|
2021-02-02 16:52:15 +00:00
|
|
|
use std::fs::rename;
|
|
|
|
use std::path::Path;
|
|
|
|
use std::process::{exit, Command};
|
|
|
|
|
|
|
|
const MAX_DOWNLOAD_ATTEMPTS: i32 = 5;
|
|
|
|
|
2022-04-30 11:30:36 +00:00
|
|
|
pub async fn download(arg_m: &ArgMatches) {
|
2021-02-02 16:52:15 +00:00
|
|
|
let print_only = arg_m.is_present("print");
|
|
|
|
let resolution = arg_m.value_of("resolution");
|
|
|
|
let ids = arg_m.values_of("id").unwrap().collect::<Vec<_>>();
|
|
|
|
let policy = Policy::custom(|attempt| {
|
|
|
|
if attempt.previous().len() > 10 {
|
|
|
|
attempt.error("too many redirects")
|
|
|
|
} else if attempt.url().path() == "/404" {
|
|
|
|
attempt.stop()
|
|
|
|
} else {
|
|
|
|
attempt.follow()
|
|
|
|
}
|
|
|
|
});
|
|
|
|
let client = Client::builder().redirect(policy).build().unwrap();
|
|
|
|
let mut return_fail = false;
|
|
|
|
for id in ids {
|
|
|
|
let hentai_info = utils::get_hentai(client.clone(), id).await;
|
|
|
|
match hentai_info {
|
|
|
|
Ok(hentai_info) => {
|
|
|
|
match hentai_info {
|
|
|
|
Some(hentai_info) => {
|
|
|
|
let slug = hentai_info.state.data.video.hentai_video.slug;
|
|
|
|
let filename = format!("{}.mkv", &slug);
|
|
|
|
if !print_only && Path::new(&filename).exists() {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let mut download_url = None;
|
2021-02-04 05:59:11 +00:00
|
|
|
let mut perm_urls: HashMap<String, (String, i32)> = HashMap::new();
|
|
|
|
let mut temp_urls: HashMap<String, (String, i32)> = HashMap::new();
|
2021-02-02 16:52:15 +00:00
|
|
|
for server in hentai_info.state.data.video.videos_manifest.servers {
|
|
|
|
let mut to_hashmap = match server.is_permanent {
|
|
|
|
true => perm_urls.clone(),
|
2022-04-30 11:30:36 +00:00
|
|
|
false => temp_urls.clone(),
|
2021-02-02 16:52:15 +00:00
|
|
|
};
|
|
|
|
for stream in server.streams {
|
|
|
|
if stream.url.is_empty() {
|
|
|
|
continue;
|
|
|
|
}
|
2022-04-30 11:30:36 +00:00
|
|
|
if server.is_permanent && Some(stream.height.as_str()) == resolution
|
|
|
|
{
|
2021-02-04 05:59:11 +00:00
|
|
|
download_url = Some((stream.url, stream.filesize_mbs));
|
2021-02-02 16:52:15 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if !to_hashmap.contains_key(&stream.height) {
|
2022-04-30 11:30:36 +00:00
|
|
|
to_hashmap
|
|
|
|
.insert(stream.height, (stream.url, stream.filesize_mbs));
|
2021-02-02 16:52:15 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
if download_url.is_some() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
match server.is_permanent {
|
|
|
|
true => perm_urls.extend(to_hashmap),
|
2022-04-30 11:30:36 +00:00
|
|
|
false => temp_urls.extend(to_hashmap),
|
2021-02-02 16:52:15 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
if download_url.is_none() {
|
|
|
|
if resolution.is_some() {
|
2022-04-30 11:30:36 +00:00
|
|
|
download_url = temp_urls
|
|
|
|
.get(resolution.unwrap())
|
|
|
|
.map(|i| (i.0.to_string(), i.1));
|
2021-02-02 16:52:15 +00:00
|
|
|
}
|
|
|
|
if download_url.is_none() {
|
2022-04-30 11:30:36 +00:00
|
|
|
download_url =
|
|
|
|
magic_thing(perm_urls).or_else(|| magic_thing(temp_urls));
|
2021-02-02 16:52:15 +00:00
|
|
|
if download_url.is_none() {
|
|
|
|
eprintln!("Failed to get {}: cannot get download url", id);
|
|
|
|
return_fail = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-02-04 05:59:11 +00:00
|
|
|
let (download_url, filesize_mbs) = download_url.unwrap();
|
2021-02-02 16:52:15 +00:00
|
|
|
if print_only {
|
|
|
|
println!("{}", download_url);
|
|
|
|
} else {
|
|
|
|
let mut fail_dl = true;
|
|
|
|
let tmp_filename = format!("{}.tmp", &slug);
|
|
|
|
for i in 0..MAX_DOWNLOAD_ATTEMPTS {
|
2022-04-30 11:30:36 +00:00
|
|
|
eprintln!(
|
|
|
|
"Downloading {} ({}MB, attempt {})",
|
|
|
|
&filename, filesize_mbs, i
|
|
|
|
);
|
|
|
|
match Command::new("ffmpeg")
|
|
|
|
.args(&[
|
|
|
|
"-v",
|
|
|
|
"warning",
|
|
|
|
"-stats",
|
|
|
|
"-nostdin",
|
|
|
|
"-y",
|
|
|
|
"-i",
|
|
|
|
&download_url,
|
|
|
|
"-c",
|
|
|
|
"copy",
|
|
|
|
"-f",
|
|
|
|
"matroska",
|
|
|
|
&tmp_filename,
|
|
|
|
])
|
|
|
|
.spawn()
|
|
|
|
{
|
2021-02-02 16:52:15 +00:00
|
|
|
Ok(mut child) => {
|
|
|
|
match child.wait() {
|
|
|
|
Ok(exit_status) => {
|
|
|
|
if exit_status.success() {
|
|
|
|
fail_dl = false;
|
|
|
|
match rename(&tmp_filename, &filename) {
|
|
|
|
Ok(_) => (),
|
2022-04-30 11:30:36 +00:00
|
|
|
Err(err) => eprintln!(
|
|
|
|
"Failed to rename {} to {} due to {}",
|
|
|
|
&tmp_filename, &filename, err
|
|
|
|
),
|
2021-02-02 16:52:15 +00:00
|
|
|
};
|
|
|
|
break;
|
|
|
|
}
|
2022-04-30 11:30:36 +00:00
|
|
|
eprintln!(
|
|
|
|
"ffmpeg exited with {:?}",
|
|
|
|
exit_status.code()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
Err(err) => eprintln!(
|
|
|
|
"Failed to wait on ffmpeg process due to {}",
|
|
|
|
err
|
|
|
|
),
|
2021-02-02 16:52:15 +00:00
|
|
|
};
|
2022-04-30 11:30:36 +00:00
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
eprintln!("Failed to spawn ffmpeg process due to {}", err)
|
|
|
|
}
|
2021-02-02 16:52:15 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
if fail_dl {
|
|
|
|
eprintln!("Failed to download {}", &filename);
|
|
|
|
return_fail = true;
|
|
|
|
}
|
|
|
|
}
|
2022-04-30 11:30:36 +00:00
|
|
|
}
|
2021-02-02 16:52:15 +00:00
|
|
|
None => {
|
|
|
|
eprintln!("Failed to get {}: does not exist", id);
|
|
|
|
return_fail = true;
|
|
|
|
}
|
|
|
|
};
|
2022-04-30 11:30:36 +00:00
|
|
|
}
|
2021-02-02 16:52:15 +00:00
|
|
|
Err(err) => {
|
|
|
|
eprintln!("Failed to get {}: {}", id, err);
|
|
|
|
return_fail = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
if return_fail {
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-04 05:59:11 +00:00
|
|
|
fn magic_thing(map: HashMap<String, (String, i32)>) -> Option<(String, i32)> {
|
2021-02-02 16:52:15 +00:00
|
|
|
let mut keys = Vec::new();
|
|
|
|
for i in map.keys() {
|
|
|
|
match i.parse::<i32>() {
|
|
|
|
Ok(i) => keys.push(i),
|
|
|
|
Err(_) => {
|
|
|
|
keys.clear();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
keys.sort();
|
|
|
|
match keys.pop() {
|
2021-02-04 05:59:11 +00:00
|
|
|
Some(key) => map.get(&key.to_string()).map(|i| i.clone()),
|
2021-02-02 16:52:15 +00:00
|
|
|
None => {
|
|
|
|
let mut keys: Vec<_> = map.keys().collect();
|
|
|
|
keys.sort();
|
|
|
|
match keys.pop() {
|
2021-02-04 05:59:11 +00:00
|
|
|
Some(key) => map.get(key.as_str()).map(|i| i.clone()),
|
2022-04-30 11:30:36 +00:00
|
|
|
None => None,
|
2021-02-02 16:52:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|