Add support for download fanbox creator's info

This commit is contained in:
2022-07-17 11:04:12 +00:00
committed by GitHub
parent bc02cad593
commit 3126bf5b2b
9 changed files with 258 additions and 23 deletions

View File

@@ -1,6 +1,6 @@
#[cfg(feature = "exif")]
use super::exif::ExifDataSource;
use crate::fanbox::post::FanboxPost;
use crate::ext::json::ToJson2;
use crate::opt::author_name_filter::AuthorFiler;
use crate::opthelper::get_helper;
use crate::pixiv_link::PixivID;
@@ -18,11 +18,11 @@ pub struct FanboxData {
}
impl FanboxData {
pub fn new<T: ToPixivID>(id: T, post: &FanboxPost) -> Option<Self> {
pub fn new<T: ToPixivID, D: ToJson2>(id: T, data: D) -> Option<Self> {
match id.to_pixiv_id() {
Some(id) => Some(Self {
id,
raw: post.get_json().clone(),
raw: data.to_json2(),
#[cfg(feature = "exif")]
exif_data: None,
}),

View File

@@ -17,6 +17,8 @@ use crate::ext::any::AsAny;
use crate::ext::try_err::TryErr;
use crate::fanbox::article::block::FanboxArticleBlock;
use crate::fanbox::check::CheckUnknown;
use crate::fanbox::creator::FanboxCreator;
use crate::fanbox::creator::FanboxProfileItem;
use crate::fanbox::post::FanboxPost;
use crate::fanbox_api::FanboxClient;
use crate::gettext;
@@ -34,6 +36,7 @@ use indicatif::MultiProgress;
use json::JsonValue;
use reqwest::IntoUrl;
use std::fs::create_dir_all;
use std::ops::Deref;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::task::JoinHandle;
@@ -87,6 +90,31 @@ impl Main {
tasks.join().await;
}
}
PixivID::FanboxCreator(id) => {
if !fc.is_inited() {
let helper = get_helper();
if !fc.init(helper.cookies()) {
println!("{}", gettext("Failed to initialize fanbox api client."));
return 1;
}
if !fc.check_login().await {
return 1;
}
if !fc.logined() {
println!("{}", gettext("Warning: Fanbox client is not logged in."));
}
}
tasks
.add_task(download_fanbox_creator_info(
Arc::clone(&fc),
id.to_owned(),
None,
))
.await;
if !download_multiple_posts {
tasks.join().await;
}
}
}
}
let mut re = 0;
@@ -679,3 +707,121 @@ pub async fn download_fanbox_post(
}
re
}
pub async fn download_fanbox_creator_info(
fc: Arc<FanboxClient>,
id: String,
data: Option<FanboxCreator>,
) -> Result<(), PixivDownloaderError> {
let data = match data {
Some(data) => {
let cid = data
.creator_id()
.try_err(gettext("Failed to get creator's id."))?;
if id == cid {
Some(data)
} else {
None
}
}
None => None,
};
let data = match data {
Some(data) => data,
None => fc
.get_creator(&id)
.await
.try_err(gettext("Failed to get creator's information."))?,
};
let data = Arc::new(data);
let helper = get_helper();
if helper.verbose() {
println!("{:#?}", data);
}
match data.check_unknown() {
Ok(_) => {}
Err(e) => {
println!(
"{} {}",
gettext("Warning: Creator's info contains unknown data:"),
e
);
return Ok(());
}
}
let mut fdata = FanboxData::new(PixivID::FanboxCreator(id.clone()), &*data)
.try_err("Failed to create data file.")?;
let base = Arc::new(PathBuf::from(".").join(&id));
let json_file = base.join("creator.json");
let data_file = JSONDataFile::from(&fdata);
data_file
.save(&json_file)
.try_err(gettext("Failed to save post data to file."))?;
let tasks = TaskManager::default();
fdata.exif_data.replace(Box::new(Arc::clone(&data)));
let fdata = Arc::new(fdata);
let download_multiple_files = helper.download_multiple_files();
let mut np = 0u16;
{
match data.download_cover_image_url()? {
Some(dh) => {
tasks
.add_task(download_fanbox_image(
dh,
np,
Some(get_progress_bar()),
Arc::clone(&fdata),
Arc::clone(&base),
))
.await;
if !download_multiple_files {
tasks.join().await;
}
np += 1;
}
None => {}
}
}
for i in data.profile_items()?.deref() {
match i {
FanboxProfileItem::Image(img) => {
let dh = img
.download_image_url()?
.try_err(gettext("Can not get image url."))?;
tasks
.add_task(download_fanbox_image(
dh,
np,
Some(get_progress_bar()),
Arc::clone(&fdata),
Arc::clone(&base),
))
.await;
if !download_multiple_files {
tasks.join().await;
}
np += 1;
}
FanboxProfileItem::Unknown(_) => {
return Err(PixivDownloaderError::from(gettext(
"Unrecognized profile item type.",
)));
}
}
}
tasks.join().await;
let mut re = Ok(());
let tasks = tasks.take_finished_tasks();
for mut task in tasks {
let task = task.as_any_mut();
if let Some(task) = task.downcast_mut::<JoinHandle<Result<(), PixivDownloaderError>>>() {
let r = task.await;
let r = match r {
Ok(r) => r,
Err(e) => Err(PixivDownloaderError::from(e)),
};
concat_pixiv_downloader_error!(re, r);
}
}
re
}

View File

@@ -29,6 +29,7 @@ pub struct DownloaderHelperBuilder {
helper: DownloaderHelper,
}
#[allow(dead_code)]
impl DownloaderHelper {
pub fn builder<U: IntoUrl>(url: U) -> Result<DownloaderHelperBuilder, DownloaderError> {
Ok(DownloaderHelperBuilder {
@@ -110,6 +111,7 @@ impl DownloaderHelper {
}
}
#[allow(dead_code)]
impl DownloaderHelperBuilder {
pub fn build(self) -> DownloaderHelper {
self.helper

View File

@@ -14,6 +14,7 @@ pub enum PixivDownloaderError {
Hyper(hyper::Error),
HTTP(http::Error),
IOError(std::io::Error),
Fanbox(crate::fanbox::error::FanboxAPIError),
}
impl From<&str> for PixivDownloaderError {

View File

@@ -7,30 +7,58 @@ pub trait ToJson {
fn to_json(&self) -> Option<JsonValue>;
}
pub trait ToJson2 {
fn to_json2(&self) -> JsonValue;
}
impl ToJson for &str {
fn to_json(&self) -> Option<JsonValue> {
Some(JsonValue::String(String::from(*self)))
}
}
impl ToJson2 for &str {
fn to_json2(&self) -> JsonValue {
JsonValue::String(String::from(*self))
}
}
impl ToJson for String {
fn to_json(&self) -> Option<JsonValue> {
Some(JsonValue::String(self.to_string()))
}
}
impl ToJson2 for String {
fn to_json2(&self) -> JsonValue {
JsonValue::String(self.to_string())
}
}
impl ToJson for JsonValue {
fn to_json(&self) -> Option<JsonValue> {
Some(self.clone())
}
}
impl ToJson2 for JsonValue {
fn to_json2(&self) -> JsonValue {
self.clone()
}
}
impl<T: ToJson> ToJson for &T {
fn to_json(&self) -> Option<JsonValue> {
(*self).to_json()
}
}
impl<T: ToJson2> ToJson2 for &T {
fn to_json2(&self) -> JsonValue {
(*self).to_json2()
}
}
impl<T: ToJson> ToJson for Option<T> {
fn to_json(&self) -> Option<JsonValue> {
match self {
@@ -46,12 +74,24 @@ impl<T: ToJson> ToJson for RwLockReadGuard<'_, T> {
}
}
impl<T: ToJson2> ToJson2 for RwLockReadGuard<'_, T> {
fn to_json2(&self) -> JsonValue {
self.deref().to_json2()
}
}
impl<T: ToJson> ToJson for RwLockWriteGuard<'_, T> {
fn to_json(&self) -> Option<JsonValue> {
self.deref().to_json()
}
}
impl<T: ToJson2> ToJson2 for RwLockWriteGuard<'_, T> {
fn to_json2(&self) -> JsonValue {
self.deref().to_json2()
}
}
pub trait FromJson
where
Self: Sized,

View File

@@ -1,9 +1,13 @@
use super::check::CheckUnknown;
use super::error::FanboxAPIError;
#[cfg(feature = "exif")]
use crate::data::exif::ExifDataSource;
use crate::ext::json::ToJson2;
use crate::fanbox_api::FanboxClientInternal;
use crate::parser::json::parse_u64;
use json::JsonValue;
use proc_macros::check_json_keys;
use proc_macros::create_fanbox_download_helper;
use std::convert::From;
use std::fmt::Debug;
use std::ops::Deref;
@@ -29,6 +33,8 @@ impl FanboxProfileImage {
self.data["imageUrl"].as_str()
}
create_fanbox_download_helper!(image_url);
#[inline]
/// Create a new instance
pub fn new(data: &JsonValue, client: Arc<FanboxClientInternal>) -> Self {
@@ -42,6 +48,8 @@ impl FanboxProfileImage {
pub fn thumbnail_url(&self) -> Option<&str> {
self.data["thumbnailUrl"].as_str()
}
create_fanbox_download_helper!(thumbnail_url);
}
impl CheckUnknown for FanboxProfileImage {
@@ -161,6 +169,8 @@ impl FanboxCreator {
self.data["coverImageUrl"].as_str()
}
create_fanbox_download_helper!(cover_image_url);
#[inline]
pub fn description(&self) -> Option<&str> {
self.data["description"].as_str()
@@ -292,3 +302,19 @@ impl Debug for FanboxCreator {
.finish_non_exhaustive()
}
}
impl ExifDataSource for FanboxCreator {
fn image_author(&self) -> Option<String> {
self.user_name().map(|s| s.to_owned())
}
fn image_comment(&self) -> Option<String> {
self.description().map(|s| s.to_owned())
}
}
impl ToJson2 for FanboxCreator {
fn to_json2(&self) -> JsonValue {
self.data.clone()
}
}

View File

@@ -5,6 +5,7 @@ use super::comment_list::FanboxCommentList;
use super::error::FanboxAPIError;
#[cfg(feature = "exif")]
use crate::data::exif::ExifDataSource;
use crate::ext::json::ToJson2;
use crate::fanbox_api::FanboxClientInternal;
use crate::parser::json::parse_u64;
use json::JsonValue;
@@ -1311,6 +1312,12 @@ impl FanboxPost {
}
}
impl ToJson2 for FanboxPost {
fn to_json2(&self) -> JsonValue {
self.get_json().clone()
}
}
fanbox_api_test!(test_get_post_info, {
match client.get_post_info(4070200).await {
Some(data) => {

View File

@@ -11,6 +11,10 @@ lazy_static! {
static ref RE2: Regex = Regex::new("^(https?://)?(www\\.)?fanbox\\.cc/@(?P<creator>[^/]+)/posts/(?P<id>\\d+)").unwrap();
#[doc(hidden)]
static ref RE3: Regex = Regex::new("^(https?://)?(?P<creator>[^./]+)\\.fanbox\\.cc/posts/(?P<id>\\d+)").unwrap();
#[doc(hidden)]
static ref RE4: Regex = Regex::new("^(https?://)?(?P<creator>[^./]+)\\.fanbox\\.cc(/(\\?.*)?)?$").unwrap();
#[doc(hidden)]
static ref RE5: Regex = Regex::new("^(https?://)?(www\\.)?fanbox\\.cc/@(?P<creator>[^/?]+)(/(\\?.*)?)?$").unwrap();
}
#[derive(Clone, Debug)]
@@ -41,6 +45,8 @@ pub enum PixivID {
Artwork(u64),
/// Fanbox post
FanboxPost(FanboxPostID),
/// Fanbox creator
FanboxCreator(String),
}
pub trait ToPixivID {
@@ -94,6 +100,25 @@ impl PixivID {
},
None => {}
}
match RE4.captures(s) {
Some(re) => match re.name("creator") {
Some(creator) => match creator.as_str() {
"www" => {}
_ => return Some(Self::FanboxCreator(String::from(creator.as_str()))),
},
None => {}
},
None => {}
}
match RE5.captures(s) {
Some(re) => match re.name("creator") {
Some(creator) => {
return Some(Self::FanboxCreator(String::from(creator.as_str())));
}
None => {}
},
None => {}
}
None
}
@@ -108,6 +133,9 @@ impl PixivID {
id.creator_id, id.post_id
)
}
Self::FanboxCreator(id) => {
format!("https://www.fanbox.cc/@{}", id)
}
}
}
}
@@ -121,6 +149,9 @@ impl ToJson for PixivID {
&PixivID::FanboxPost(id) => Some(
json::value!({"type": "fanbox_post", "post_id": id.post_id.clone(), "creator_id": id.creator_id.clone(), "link": self.to_link()}),
),
&PixivID::FanboxCreator(id) => Some(
json::value!({"type": "fanbox_creator", "creator_id": id.clone(), "link": self.to_link()}),
),
}
}
}
@@ -167,6 +198,7 @@ impl TryInto<u64> for PixivID {
match self {
Self::Artwork(id) => Ok(id),
Self::FanboxPost(id) => Ok(id.post_id),
Self::FanboxCreator(_) => Err(()),
}
}
}
@@ -177,6 +209,7 @@ impl TryInto<u64> for &PixivID {
match self {
PixivID::Artwork(id) => Ok(id.clone()),
PixivID::FanboxPost(id) => Ok(id.post_id.clone()),
PixivID::FanboxCreator(_) => Err(()),
}
}
}

View File

@@ -5,7 +5,6 @@ use crate::opthelper::get_helper;
use crate::parser::metadata::MetaDataParser;
use crate::webclient::WebClient;
use json::JsonValue;
use reqwest::IntoUrl;
use reqwest::Response;
use std::sync::atomic::AtomicBool;
use std::sync::RwLock;
@@ -161,25 +160,6 @@ impl PixivWebClient {
Some(body.clone())
}
pub async fn download_image<U: IntoUrl + Clone>(&self, url: U) -> Option<Response> {
self.auto_init();
let r = self
.client
.get(url, json::object! {"referer": "https://www.pixiv.net/"})
.await;
if r.is_none() {
return None;
}
let r = r.unwrap();
let status = r.status();
let code = status.as_u16();
if code >= 400 {
println!("{} {}", gettext("Failed to download image:"), status);
return None;
}
Some(r)
}
pub async fn get_artwork_ajax(&self, id: u64) -> Option<JsonValue> {
self.auto_init();
let r = self