Compare commits

..

No commits in common. "f1bc906d26ce0a0e17f0fca591fb0e19f8ebfa51" and "c49b5a058a6a4a04750a4f314cc85c8629749951" have entirely different histories.

4 changed files with 16 additions and 47 deletions

View file

@ -1,10 +0,0 @@
usage:
```console
cargo build --release
./target/release/lonefire https://www.pixiv.net/artworks/111503285
```
or, install from AUR
[https://aur.archlinux.org/packages/lonefire](https://aur.archlinux.org/packages/lonefire)

View file

@ -1,9 +1,7 @@
use anyhow::{Context, Result, anyhow}; use anyhow::{Context, Result, anyhow};
use config::{MAX_CONCURRENT_DOWNLOADS, PixivResponse}; use config::PixivResponse;
use futures::stream::{self, StreamExt}; use futures::stream::{self, StreamExt};
use helpers::{ use helpers::{format_filename, generate_image_urls, normalize_image_url, set_headers};
filter_valid_urls, format_filename, generate_image_urls, normalize_image_url, set_headers,
};
use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use reqwest::{ use reqwest::{
Client, Client,
@ -17,7 +15,10 @@ use std::{
}; };
use url::Url; use url::Url;
use crate::{config, helpers}; use crate::{
config::{self, MAX_CONCURRENT_DOWNLOADS},
helpers,
};
pub async fn download_pixiv_artwork(artwork_url: &str) -> Result<()> { pub async fn download_pixiv_artwork(artwork_url: &str) -> Result<()> {
let client = Client::new(); let client = Client::new();
@ -58,11 +59,11 @@ pub async fn download_pixiv_artwork(artwork_url: &str) -> Result<()> {
let page_count = illust_data.page_count as usize; let page_count = illust_data.page_count as usize;
let image_urls: Vec<String> = match &illust_data.urls.original { let image_urls: Vec<String> = match &illust_data.urls.original {
Some(original_url) if original_url.contains(&artwork_id) => (0..page_count) Some(original_url) if original_url.contains(&artwork_id) => {
.map(|seq| original_url.replace("p0", &format!("p{}", seq))) generate_image_urls(original_url, page_count)
.collect(), }
_ => { _ => {
let urls = illust_data illust_data
.user_illusts .user_illusts
.as_ref() .as_ref()
.map(|user_illusts| { .map(|user_illusts| {
@ -74,9 +75,7 @@ pub async fn download_pixiv_artwork(artwork_url: &str) -> Result<()> {
.flat_map(|normalized_url| generate_image_urls(&normalized_url, page_count)) .flat_map(|normalized_url| generate_image_urls(&normalized_url, page_count))
.collect::<Vec<String>>() .collect::<Vec<String>>()
}) })
.unwrap_or_default(); .unwrap_or_default()
filter_valid_urls(urls).await
} }
}; };
if image_urls.is_empty() { if image_urls.is_empty() {
@ -85,10 +84,10 @@ pub async fn download_pixiv_artwork(artwork_url: &str) -> Result<()> {
let m = MultiProgress::new(); let m = MultiProgress::new();
let sty = ProgressStyle::with_template( let sty = ProgressStyle::with_template(
"{spinner:.green}[{percent}%][{wide_bar:.cyan/blue}] {bytes}/{total_bytes} {msg}", "[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg}",
) )
.unwrap() .unwrap()
.progress_chars("#>-"); .progress_chars("##-");
stream::iter(image_urls.into_iter().map(|url| { stream::iter(image_urls.into_iter().map(|url| {
let client = client.clone(); let client = client.clone();
@ -114,7 +113,7 @@ async fn download_image(client: Client, url: &str, dir: &Path, pb: &ProgressBar)
let filepath = dir.join(&filename); let filepath = dir.join(&filename);
let temp_path = filepath.with_extension("part"); let temp_path = filepath.with_extension("part");
if filepath.exists() { if filepath.exists() {
pb.finish_with_message(format!("{filename} already exists")); pb.finish_with_message("Already exists");
return Ok(()); return Ok(());
} }
let existing_size = temp_path.metadata().map(|s| s.len()).unwrap_or(0); let existing_size = temp_path.metadata().map(|s| s.len()).unwrap_or(0);
@ -157,7 +156,7 @@ async fn download_image(client: Client, url: &str, dir: &Path, pb: &ProgressBar)
pb.inc(chunk.len() as u64); pb.inc(chunk.len() as u64);
} }
pb.finish_with_message(format!("{filename} 🗹")); pb.finish_with_message("Download completed.");
rename(temp_path, filepath)?; rename(temp_path, filepath)?;
Ok(()) Ok(())
} }

View file

@ -1,11 +1,8 @@
use futures::{StreamExt, stream};
use regex::Regex; use regex::Regex;
use reqwest::header::{ use reqwest::header::{
ACCEPT, ACCEPT_LANGUAGE, CONNECTION, HeaderMap, HeaderValue, REFERER, USER_AGENT, ACCEPT, ACCEPT_LANGUAGE, CONNECTION, HeaderMap, HeaderValue, REFERER, USER_AGENT,
}; };
use crate::config::MAX_CONCURRENT_DOWNLOADS;
pub fn normalize_image_url(base_url: &str) -> String { pub fn normalize_image_url(base_url: &str) -> String {
let re_custom_thumb = Regex::new(r"/c/250x250_80_a2/custom-thumb").unwrap(); let re_custom_thumb = Regex::new(r"/c/250x250_80_a2/custom-thumb").unwrap();
let re_img_master = Regex::new(r"/c/250x250_80_a2/img-master").unwrap(); let re_img_master = Regex::new(r"/c/250x250_80_a2/img-master").unwrap();
@ -32,23 +29,6 @@ pub fn generate_image_urls(base_url: &str, page_count: usize) -> Vec<String> {
.collect() .collect()
} }
async fn probe_image_url(url: String) -> Option<String> {
let probe_client = reqwest::Client::new();
let headers = set_headers();
match probe_client.head(&url).headers(headers).send().await {
Ok(response) if response.status().is_success() => Some(url),
_ => None,
}
}
pub async fn filter_valid_urls(urls: Vec<String>) -> Vec<String> {
stream::iter(urls.into_iter().map(probe_image_url))
.buffer_unordered(MAX_CONCURRENT_DOWNLOADS)
.filter_map(|url| async { url })
.collect::<Vec<String>>()
.await
}
pub fn format_filename(url: &str) -> Option<String> { pub fn format_filename(url: &str) -> Option<String> {
let re = Regex::new(r"/(\d+)_p(\d+)(\.\w+)$").unwrap(); let re = Regex::new(r"/(\d+)_p(\d+)(\.\w+)$").unwrap();
if let Some(caps) = re.captures(url) { if let Some(caps) = re.captures(url) {

View file

@ -9,7 +9,7 @@ mod helpers;
async fn main() -> Result<()> { async fn main() -> Result<()> {
let args: Vec<String> = std::env::args().collect(); let args: Vec<String> = std::env::args().collect();
if args.len() < 2 { if args.len() < 2 {
eprintln!("Usage: lonefire <URL>\nBTW, lonefire can't download GIF image"); eprintln!("Usage: pixiv_download <URL>");
std::process::exit(1); std::process::exit(1);
} }
let artwork_url = &args[1]; let artwork_url = &args[1];