mirror of
https://github.com/lifegpc/pixiv_downloader.git
synced 2026-07-08 01:32:41 +08:00
Support download fanbox image post
This commit is contained in:
@@ -706,31 +706,81 @@ pub fn derive_check_unknown(item: TokenStream) -> TokenStream {
|
||||
}
|
||||
|
||||
struct CreateFanboxDownloadHelper {
|
||||
pub file_name: Option<Expr>,
|
||||
pub fn_name: Ident,
|
||||
pub headers: Option<Expr>,
|
||||
}
|
||||
|
||||
impl Parse for CreateFanboxDownloadHelper {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
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<Option<crate::downloader::DownloaderHelper>, crate::downloader::DownloaderError> {
|
||||
match self.#fn_name() {
|
||||
Some(url) => {
|
||||
let client: &std::sync::Arc<crate::webclient::WebClient> = 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),
|
||||
}
|
||||
|
||||
@@ -18,7 +18,10 @@ pub trait ExifDataSource {
|
||||
}
|
||||
|
||||
impl<T: ExifDataSource> ExifDataSource for std::sync::Arc<T> {
|
||||
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<T: ExifDataSource> ExifDataSource for Option<T> {
|
||||
|
||||
@@ -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!(
|
||||
|
||||
@@ -120,6 +120,17 @@ impl From<FanboxData> 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<JsonValue> {
|
||||
let mut value = json::object! {};
|
||||
|
||||
@@ -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<Arc<MultiProgress>>,
|
||||
datas: Arc<FanboxData>,
|
||||
base: Arc<PathBuf>,
|
||||
) -> 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<FanboxClient>,
|
||||
@@ -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(
|
||||
|
||||
@@ -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<bool>,
|
||||
base: &P,
|
||||
) -> Result<DownloaderResult<Downloader<LocalFile>>, 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<P: AsRef<Path> + ?Sized>(
|
||||
&self,
|
||||
base: &P,
|
||||
) -> Option<std::path::PathBuf> {
|
||||
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<WebClient>) {
|
||||
self.client.replace(Arc::clone(client));
|
||||
}
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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<String> {
|
||||
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<String> {
|
||||
match self.user_name() {
|
||||
Some(u) => Some(u.to_owned()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn image_comment(&self) -> Option<String> {
|
||||
match self.body() {
|
||||
Some(body) => body.image_comment(),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn image_title(&self) -> Option<String> {
|
||||
match self.title() {
|
||||
Some(t) => Some(t.to_owned()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to another post
|
||||
pub struct FanboxPostRef {
|
||||
/// Raw data
|
||||
|
||||
Reference in New Issue
Block a user