From 8cdde2fd658ac71e237a12037c2b35a8e14a066d Mon Sep 17 00:00:00 2001 From: lifegpc Date: Fri, 15 Jul 2022 09:16:31 +0000 Subject: [PATCH] Support download fanbox image post --- proc_macros/proc_macros.rs | 58 +++++++++++++++++++++++-- src/data/exif.rs | 5 ++- src/data/fanbox.rs | 10 +++++ src/data/json.rs | 11 +++++ src/download.rs | 89 +++++++++++++++++++++++++++++++++++--- src/downloader/helper.rs | 30 ++++++++----- src/fanbox/comment_list.rs | 2 +- src/fanbox/post.rs | 36 +++++++++++++++ 8 files changed, 217 insertions(+), 24 deletions(-) diff --git a/proc_macros/proc_macros.rs b/proc_macros/proc_macros.rs index d42007f..c6da9e1 100644 --- a/proc_macros/proc_macros.rs +++ b/proc_macros/proc_macros.rs @@ -706,31 +706,81 @@ pub fn derive_check_unknown(item: TokenStream) -> TokenStream { } struct CreateFanboxDownloadHelper { + pub file_name: Option, pub fn_name: Ident, + pub headers: Option, } impl Parse for CreateFanboxDownloadHelper { fn parse(input: syn::parse::ParseStream) -> syn::Result { let fn_name = input.parse()?; - Ok(Self { fn_name }) + let mut file_name = None; + let mut headers = None; + loop { + if input.cursor().eof() { + break; + } + token::Comma::parse(input)?; + if input.cursor().eof() { + break; + } + let name: Ident = input.parse()?; + let nname = name.to_string(); + match nname.as_str() { + "file_path" => { + token::Eq::parse(input)?; + file_name.replace(Expr::parse(input)?); + } + "headers" => { + token::Eq::parse(input)?; + headers.replace(Expr::parse(input)?); + } + _ => { + return Err(syn::Error::new(name.span(), "Unsupported name.")); + } + } + } + Ok(Self { + file_name, + fn_name, + headers, + }) } } #[proc_macro] pub fn create_fanbox_download_helper(item: TokenStream) -> TokenStream { - let CreateFanboxDownloadHelper { fn_name } = - parse_macro_input!(item as CreateFanboxDownloadHelper); + let CreateFanboxDownloadHelper { + file_name, + fn_name, + headers, + } = parse_macro_input!(item as CreateFanboxDownloadHelper); let defn_name = Ident::new( format!("download_{}", fn_name.to_string()).as_str(), fn_name.span(), ); + let headers = match headers { + Some(headers) => { + quote!(.headers(#headers)) + } + None => quote!(), + }; + let file_name = match file_name { + Some(file_name) => { + quote!(.file_name(#file_name)) + } + None => quote!(), + }; let stream = quote!( #[inline] pub fn #defn_name(&self) -> Result, crate::downloader::DownloaderError> { match self.#fn_name() { Some(url) => { let client: &std::sync::Arc = self.client.as_ref().as_ref(); - Ok(Some(crate::downloader::DownloaderHelper::builder(url)?.client(client).build())) + Ok(Some(crate::downloader::DownloaderHelper::builder(url)?.client(client) + #file_name + #headers + .build())) } None => Ok(None), } diff --git a/src/data/exif.rs b/src/data/exif.rs index 448f7a4..da082f4 100644 --- a/src/data/exif.rs +++ b/src/data/exif.rs @@ -18,7 +18,10 @@ pub trait ExifDataSource { } impl ExifDataSource for std::sync::Arc { - call_parent_data_source_fun!("src/data/exif_data_source.json", *self,); + call_parent_data_source_fun!( + "src/data/exif_data_source.json", + std::ops::Deref::deref(self), + ); } impl ExifDataSource for Option { diff --git a/src/data/fanbox.rs b/src/data/fanbox.rs index 5240869..61d8958 100644 --- a/src/data/fanbox.rs +++ b/src/data/fanbox.rs @@ -29,6 +29,16 @@ impl FanboxData { } } +impl Clone for FanboxData { + fn clone(&self) -> Self { + Self { + id: self.id.clone(), + raw: self.raw.clone(), + exif_data: None, + } + } +} + #[cfg(feature = "exif")] impl ExifDataSource for FanboxData { call_parent_data_source_fun!( diff --git a/src/data/json.rs b/src/data/json.rs index ab05407..ff26146 100644 --- a/src/data/json.rs +++ b/src/data/json.rs @@ -120,6 +120,17 @@ impl From for JSONDataFile { } } +impl From<&FanboxData> for JSONDataFile { + fn from(d: &FanboxData) -> Self { + let mut f = Self { + id: d.id.clone(), + maps: HashMap::new(), + }; + f.add("raw", d.raw.clone()).unwrap(); + f + } +} + impl ToJson for JSONDataFile { fn to_json(&self) -> Option { let mut value = json::object! {}; diff --git a/src/download.rs b/src/download.rs index e97c35f..fb14627 100644 --- a/src/download.rs +++ b/src/download.rs @@ -9,6 +9,7 @@ use crate::data::json::JSONDataFile; #[cfg(feature = "ugoira")] use crate::data::video::get_video_metadata; use crate::downloader::Downloader; +use crate::downloader::DownloaderHelper; use crate::downloader::DownloaderResult; use crate::downloader::LocalFile; use crate::error::PixivDownloaderError; @@ -28,7 +29,7 @@ use crate::Main; use indicatif::MultiProgress; use json::JsonValue; use reqwest::IntoUrl; -use std::fs::create_dir; +use std::fs::create_dir_all; use std::path::PathBuf; use std::sync::Arc; @@ -372,6 +373,65 @@ impl Main { Ok(()) } + /// Download a fanbox image link + /// * `dh` - Link and other informations + /// * `np` - Number of page + /// * `progress_bars` - Multiple progress bars + /// * `datas` - The artwork's data + /// * `base` - The directory of the target + pub async fn download_fanbox_image( + &self, + dh: DownloaderHelper, + np: u16, + progress_bars: Option>, + datas: Arc, + base: Arc, + ) -> Result<(), PixivDownloaderError> { + let helper = get_helper(); + let file_name = dh + .get_local_file_path(&*base) + .try_err(gettext("Failed to get file name from url."))?; + match dh.download_local(helper.overwrite(), &*base)? { + DownloaderResult::Ok(d) => { + d.handle_options(&helper, progress_bars); + d.download(); + d.join().await?; + if d.is_downloaded() { + #[cfg(feature = "exif")] + { + if add_exifdata_to_image(&file_name, &datas, np).is_err() { + println!( + "{} {}", + gettext("Failed to add exif data to image:"), + file_name.to_str().unwrap_or("(null)") + ); + } + } + } else if d.is_panic() { + return Err(PixivDownloaderError::from( + d.get_panic() + .try_err(gettext("Failed to get error message."))?, + )); + } + } + DownloaderResult::Canceled => { + #[cfg(feature = "exif")] + { + if helper.update_exif() && file_name.exists() { + if add_exifdata_to_image(&file_name, &datas, np).is_err() { + println!( + "{} {}", + gettext("Failed to add exif data to image:"), + file_name.to_str().unwrap_or("(null)") + ); + } + } + } + } + } + Ok(()) + } + pub async fn download_fanbox_post( &self, fc: Arc, @@ -406,9 +466,9 @@ impl Main { let base = Arc::new(PathBuf::from(format!("./{}/{}", id.creator_id, id.post_id))); let json_file = base.join("data.json"); let data = FanboxData::new(id, &post).try_err("Failed to create data file.")?; - let data_file = JSONDataFile::from(data); + let data_file = JSONDataFile::from(&data); if !base.exists() { - match create_dir(&*base) { + match create_dir_all(&*base) { Ok(_) => {} Err(e) => { if !base.exists() { @@ -423,15 +483,32 @@ impl Main { match post { FanboxPost::Article(article) => {} FanboxPost::Image(img) => { + let img = Arc::new(img); let body = img .body() .try_err(gettext("Failed to get the body of image post."))?; - let text = body - .text() - .try_err(gettext("Failed to get the text of the image post."))?; let images = body .images() .try_err(gettext("Failed to get images from the image post."))?; + let mut np = 0; + let mut datas = data.clone(); + datas.exif_data.replace(Box::new(Arc::clone(&img))); + let datas = Arc::new(datas); + for img in images.iter() { + let dh = img + .download_original_url()? + .try_err(gettext("Can not get original url for image"))?; + Self::download_fanbox_image( + &self, + dh, + np, + None, + Arc::clone(&datas), + Arc::clone(&base), + ) + .await?; + np += 1; + } } FanboxPost::Unknown(_) => { return Err(PixivDownloaderError::from(gettext( diff --git a/src/downloader/helper.rs b/src/downloader/helper.rs index 0ee9f5f..bc5e4e9 100644 --- a/src/downloader/helper.rs +++ b/src/downloader/helper.rs @@ -3,6 +3,7 @@ use super::enums::DownloaderResult; use super::error::DownloaderError; use super::local_file::LocalFile; use crate::ext::replace::ReplaceWith; +use crate::ext::try_err::TryErr; use crate::gettext; use crate::webclient::ToHeaders; use crate::webclient::WebClient; @@ -48,18 +49,9 @@ impl DownloaderHelper { overwrite: Option, base: &P, ) -> Result>, DownloaderError> { - let base = base.as_ref(); - let file = match &self.file_name { - Some(file_name) => base.join(file_name.as_ref()), - None => match crate::utils::get_file_name_from_url(self.url.clone()) { - Some(file_name) => base.join(file_name), - None => { - return Err(DownloaderError::from(gettext( - "Failed to get file name from url.", - ))); - } - }, - }; + let file = self + .get_local_file_path(base) + .try_err(gettext("Failed to get file name from url."))?; let headers = match &self.headers { Some(headers) => headers.to_headers(), None => None, @@ -76,6 +68,20 @@ impl DownloaderHelper { } } + pub fn get_local_file_path + ?Sized>( + &self, + base: &P, + ) -> Option { + let base = base.as_ref(); + match &self.file_name { + Some(file_name) => Some(base.join(file_name.as_ref())), + None => match crate::utils::get_file_name_from_url(self.url.clone()) { + Some(file_name) => Some(base.join(file_name)), + None => None, + }, + } + } + pub fn set_client(&mut self, client: &Arc) { self.client.replace(Arc::clone(client)); } diff --git a/src/fanbox/comment_list.rs b/src/fanbox/comment_list.rs index 4b41594..7bee8b6 100644 --- a/src/fanbox/comment_list.rs +++ b/src/fanbox/comment_list.rs @@ -100,7 +100,7 @@ fanbox_api_test!(test_comment_list, { ); match data { FanboxPost::Article(a) => { - println!("{:?}", a); + println!("{:#?}", a); match a.check_unknown() { Ok(_) => {} Err(e) => { diff --git a/src/fanbox/post.rs b/src/fanbox/post.rs index 89f584d..512f1a4 100644 --- a/src/fanbox/post.rs +++ b/src/fanbox/post.rs @@ -3,6 +3,8 @@ use super::article::image::FanboxArticleImage; use super::check::CheckUnknown; use super::comment_list::FanboxCommentList; use super::error::FanboxAPIError; +#[cfg(feature = "exif")] +use crate::data::exif::ExifDataSource; use crate::fanbox_api::FanboxClientInternal; use crate::parser::json::parse_u64; use json::JsonValue; @@ -313,6 +315,16 @@ impl Debug for FanboxImageBody { } } +#[cfg(feature = "exif")] +impl ExifDataSource for FanboxImageBody { + fn image_comment(&self) -> Option { + match self.text() { + Some(text) => Some(text.to_owned()), + None => None, + } + } +} + /// Fanbox image post pub struct FanboxPostImage { /// Raw data @@ -558,6 +570,30 @@ impl Debug for FanboxPostImage { } } +#[cfg(feature = "exif")] +impl ExifDataSource for FanboxPostImage { + fn image_author(&self) -> Option { + match self.user_name() { + Some(u) => Some(u.to_owned()), + None => None, + } + } + + fn image_comment(&self) -> Option { + match self.body() { + Some(body) => body.image_comment(), + None => None, + } + } + + fn image_title(&self) -> Option { + match self.title() { + Some(t) => Some(t.to_owned()), + None => None, + } + } +} + /// A reference to another post pub struct FanboxPostRef { /// Raw data