mirror of
https://github.com/lifegpc/pixiv_downloader.git
synced 2026-06-06 05:49:01 +08:00
Download pixiv artwork now support app api
This commit is contained in:
@@ -5,6 +5,7 @@ use crate::opt::author_name_filter::AuthorFiler;
|
||||
use crate::opthelper::get_helper;
|
||||
use crate::pixiv_link::PixivID;
|
||||
use crate::pixiv_link::ToPixivID;
|
||||
use crate::pixivapp::illust::PixivAppIllust;
|
||||
use int_enum::IntEnum;
|
||||
use json::JsonValue;
|
||||
use xml::unescape;
|
||||
@@ -56,6 +57,27 @@ impl PixivData {
|
||||
})
|
||||
}
|
||||
|
||||
/// Read data from [PixivAppIllust].
|
||||
pub fn from_app_illust(&mut self, illust: &PixivAppIllust) {
|
||||
self.title = illust.title().map(|s| s.to_owned());
|
||||
self.author = illust.user_name().map(|s| s.to_owned());
|
||||
self.description = illust.caption().map(|s| s.to_owned());
|
||||
let mut tags = Vec::new();
|
||||
for i in illust.tags() {
|
||||
if let Some(name) = i.name() {
|
||||
tags.push((name.to_owned(), i.translated_name().map(|s| s.to_owned())));
|
||||
}
|
||||
}
|
||||
self.tags.replace(tags);
|
||||
self.ai_type = match illust.illust_ai_type() {
|
||||
Some(t) => match PixivAiType::from_int(t as u8) {
|
||||
Ok(t) => Some(t),
|
||||
Err(_) => None,
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
}
|
||||
|
||||
/// Read data from JSON object.
|
||||
/// The object is from `https://www.pixiv.net/artworks/<id>`
|
||||
/// * `value` - The JSON object
|
||||
|
||||
304
src/download.rs
304
src/download.rs
@@ -275,12 +275,237 @@ pub async fn download_artwork(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn download_artwork_ugoira(
|
||||
pw: Arc<PixivWebClient>,
|
||||
id: u64,
|
||||
base: Arc<PathBuf>,
|
||||
datas: Arc<PixivData>,
|
||||
) -> Result<(), PixivDownloaderError> {
|
||||
let helper = get_helper();
|
||||
let ugoira_data = pw
|
||||
.get_ugoira(id)
|
||||
.await
|
||||
.try_err(gettext("Failed to get ugoira's data."))?;
|
||||
let src = (&ugoira_data["originalSrc"])
|
||||
.as_str()
|
||||
.try_err(gettext("Can not find source link for ugoira."))?;
|
||||
let dh = DownloaderHelper::builder(src)?
|
||||
.headers(json::object! { "referer": "https://www.pixiv.net/" })
|
||||
.build();
|
||||
let tasks = TaskManager::default();
|
||||
tasks
|
||||
.add_task(download_file(
|
||||
dh,
|
||||
if helper.enable_multi_progress_bar() {
|
||||
Some(get_progress_bar())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
Arc::clone(&base),
|
||||
))
|
||||
.await;
|
||||
tasks.join().await;
|
||||
let mut tasks = tasks.take_finished_tasks();
|
||||
let task = tasks.get_mut(0).try_err(gettext("No finished task."))?;
|
||||
task.await??;
|
||||
#[cfg(feature = "ugoira")]
|
||||
{
|
||||
let file_name = get_file_name_from_url(src).try_err(format!(
|
||||
"{} {}",
|
||||
gettext("Failed to get file name from url:"),
|
||||
src
|
||||
))?;
|
||||
let file_name = base.join(file_name);
|
||||
let metadata = match get_video_metadata(Arc::clone(&datas).as_ref()) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
println!(
|
||||
"{} {}",
|
||||
gettext("Warning: Failed to generate video's metadata:"),
|
||||
e
|
||||
);
|
||||
AVDict::new()
|
||||
}
|
||||
};
|
||||
let mut options = AVDict::new();
|
||||
if helper.force_yuv420p() {
|
||||
options.set("force_yuv420p", "1", None)?;
|
||||
}
|
||||
let profile = helper.x264_profile();
|
||||
if !profile.is_auto() {
|
||||
options.set("profile", profile.as_str(), None)?;
|
||||
}
|
||||
match helper.x264_crf() {
|
||||
Some(crf) => {
|
||||
options.set("crf", format!("{}", crf), None)?;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
let frames = UgoiraFrames::from_json(&ugoira_data["frames"])?;
|
||||
let output_file_name = base.join(format!("{}.mp4", id));
|
||||
convert_ugoira_to_mp4(
|
||||
&file_name,
|
||||
&output_file_name,
|
||||
&frames,
|
||||
helper.ugoira_max_fps(),
|
||||
&options,
|
||||
&metadata,
|
||||
)?;
|
||||
println!(
|
||||
"{}",
|
||||
gettext("Converted <src> -> <dest>")
|
||||
.replace("<src>", file_name.to_str().unwrap_or("(null)"))
|
||||
.replace("<dest>", output_file_name.to_str().unwrap_or("(null)"))
|
||||
.as_str()
|
||||
);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub async fn download_artwork_app(
|
||||
ac: PixivAppClient,
|
||||
pw: Arc<PixivWebClient>,
|
||||
id: u64,
|
||||
) -> Result<(), PixivDownloaderError> {
|
||||
let data = ac.get_illust_details(id).await?;
|
||||
let helper = get_helper();
|
||||
if helper.verbose() {
|
||||
println!("{:#?}", data);
|
||||
}
|
||||
match crate::pixivapp::check::CheckUnknown::check_unknown(&data) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!(
|
||||
"{} {}",
|
||||
gettext("Warning: Post info contains unknown data:"),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
let base = Arc::new(PathBuf::from(helper.download_base()));
|
||||
let json_file = base.join(format!("{}.json", id));
|
||||
let mut datas = PixivData::new(id).unwrap();
|
||||
datas.from_app_illust(&data);
|
||||
let datas = Arc::new(datas);
|
||||
let json_data = JSONDataFile::from(Arc::clone(&datas));
|
||||
if !json_data.save(&json_file) {
|
||||
return Err(PixivDownloaderError::from(gettext(
|
||||
"Failed to save metadata to JSON file.",
|
||||
)));
|
||||
}
|
||||
let illust_type = data.typ();
|
||||
match illust_type {
|
||||
Some(illust_type) => match illust_type {
|
||||
"ugoira" => {
|
||||
return download_artwork_ugoira(pw, id, base, datas).await;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
None => {
|
||||
println!("{}", gettext("Warning: Failed to get illust's type."));
|
||||
}
|
||||
}
|
||||
let page_count = data
|
||||
.page_count()
|
||||
.ok_or(gettext("Failed to get page count."))?;
|
||||
if page_count > 1 && helper.download_multiple_files() {
|
||||
let mut np = 0u16;
|
||||
let tasks = TaskManager::default();
|
||||
let mut re: Result<(), PixivDownloaderError> = Ok(());
|
||||
for page in data.meta_pages() {
|
||||
let url = match page.original() {
|
||||
Some(url) => url.to_owned(),
|
||||
None => {
|
||||
concat_pixiv_downloader_error!(
|
||||
re,
|
||||
Err::<(), &str>(gettext("Failed to get original picture's link."))
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
tasks
|
||||
.add_task(download_artwork_link(
|
||||
url,
|
||||
np,
|
||||
if helper.enable_multi_progress_bar() {
|
||||
Some(get_progress_bar())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
Arc::clone(&datas),
|
||||
Arc::clone(&base),
|
||||
))
|
||||
.await;
|
||||
np += 1;
|
||||
}
|
||||
tasks.join().await;
|
||||
let tasks = tasks.take_finished_tasks();
|
||||
for task in tasks {
|
||||
let r = task.await;
|
||||
let r = match r {
|
||||
Ok(r) => r,
|
||||
Err(e) => Err(PixivDownloaderError::from(e)),
|
||||
};
|
||||
concat_pixiv_downloader_error!(re, r);
|
||||
}
|
||||
return re;
|
||||
} else if page_count > 1 {
|
||||
let mut np = 0u16;
|
||||
let tasks = TaskManager::default();
|
||||
for page in data.meta_pages() {
|
||||
let link = page
|
||||
.original()
|
||||
.ok_or(gettext("Failed to get original picture's link."))?;
|
||||
tasks
|
||||
.add_task(download_artwork_link(
|
||||
link.to_owned(),
|
||||
np,
|
||||
if helper.enable_multi_progress_bar() {
|
||||
Some(get_progress_bar())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
Arc::clone(&datas),
|
||||
Arc::clone(&base),
|
||||
))
|
||||
.await;
|
||||
tasks.join().await;
|
||||
np += 1;
|
||||
}
|
||||
let mut re = Ok(());
|
||||
let tasks = tasks.take_finished_tasks();
|
||||
for task in tasks {
|
||||
let r = task.await;
|
||||
let r = match r {
|
||||
Ok(r) => r,
|
||||
Err(e) => Err(PixivDownloaderError::from(e)),
|
||||
};
|
||||
concat_pixiv_downloader_error!(re, r);
|
||||
}
|
||||
return re;
|
||||
} else {
|
||||
let link = data
|
||||
.original_image_url()
|
||||
.ok_or(gettext("Failed to get original picture's link."))?;
|
||||
let tasks = TaskManager::default();
|
||||
tasks
|
||||
.add_task(download_artwork_link(
|
||||
link.to_owned(),
|
||||
0,
|
||||
if helper.enable_multi_progress_bar() {
|
||||
Some(get_progress_bar())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
Arc::clone(&datas),
|
||||
Arc::clone(&base),
|
||||
))
|
||||
.await;
|
||||
tasks.join().await;
|
||||
let mut tasks = tasks.take_finished_tasks();
|
||||
let task = tasks.get_mut(0).try_err(gettext("No tasks finished."))?;
|
||||
task.await??;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -355,84 +580,7 @@ pub async fn download_artwork_web(
|
||||
0 => {} // Normal illust
|
||||
1 => {} // Manga illust
|
||||
2 => {
|
||||
let ugoira_data = pw
|
||||
.get_ugoira(id)
|
||||
.await
|
||||
.try_err(gettext("Failed to get ugoira's data."))?;
|
||||
let src = (&ugoira_data["originalSrc"])
|
||||
.as_str()
|
||||
.try_err(gettext("Can not find source link for ugoira."))?;
|
||||
let dh = DownloaderHelper::builder(src)?
|
||||
.headers(json::object! { "referer": "https://www.pixiv.net/" })
|
||||
.build();
|
||||
let tasks = TaskManager::default();
|
||||
tasks
|
||||
.add_task(download_file(
|
||||
dh,
|
||||
if helper.enable_multi_progress_bar() {
|
||||
Some(get_progress_bar())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
Arc::clone(&base),
|
||||
))
|
||||
.await;
|
||||
tasks.join().await;
|
||||
let mut tasks = tasks.take_finished_tasks();
|
||||
let task = tasks.get_mut(0).try_err(gettext("No finished task."))?;
|
||||
task.await??;
|
||||
#[cfg(feature = "ugoira")]
|
||||
{
|
||||
let file_name = get_file_name_from_url(src).try_err(format!(
|
||||
"{} {}",
|
||||
gettext("Failed to get file name from url:"),
|
||||
src
|
||||
))?;
|
||||
let file_name = base.join(file_name);
|
||||
let metadata = match get_video_metadata(Arc::clone(&datas).as_ref()) {
|
||||
Ok(m) => m,
|
||||
Err(e) => {
|
||||
println!(
|
||||
"{} {}",
|
||||
gettext("Warning: Failed to generate video's metadata:"),
|
||||
e
|
||||
);
|
||||
AVDict::new()
|
||||
}
|
||||
};
|
||||
let mut options = AVDict::new();
|
||||
if helper.force_yuv420p() {
|
||||
options.set("force_yuv420p", "1", None)?;
|
||||
}
|
||||
let profile = helper.x264_profile();
|
||||
if !profile.is_auto() {
|
||||
options.set("profile", profile.as_str(), None)?;
|
||||
}
|
||||
match helper.x264_crf() {
|
||||
Some(crf) => {
|
||||
options.set("crf", format!("{}", crf), None)?;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
let frames = UgoiraFrames::from_json(&ugoira_data["frames"])?;
|
||||
let output_file_name = base.join(format!("{}.mp4", id));
|
||||
convert_ugoira_to_mp4(
|
||||
&file_name,
|
||||
&output_file_name,
|
||||
&frames,
|
||||
helper.ugoira_max_fps(),
|
||||
&options,
|
||||
&metadata,
|
||||
)?;
|
||||
println!(
|
||||
"{}",
|
||||
gettext("Converted <src> -> <dest>")
|
||||
.replace("<src>", file_name.to_str().unwrap_or("(null)"))
|
||||
.replace("<dest>", output_file_name.to_str().unwrap_or("(null)"))
|
||||
.as_str()
|
||||
);
|
||||
}
|
||||
return Ok(());
|
||||
return download_artwork_ugoira(pw, id, base, datas).await;
|
||||
}
|
||||
_ => {
|
||||
println!(
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::ext::replace::ReplaceWith2;
|
||||
use crate::ext::rw_lock::GetRwLock;
|
||||
use crate::opthelper::OptHelper;
|
||||
use crate::pixivapp::error::handle_error;
|
||||
use crate::pixivapp::illust::PixivAppIllust;
|
||||
use crate::webclient::{ReqMiddleware, WebClient};
|
||||
use crate::{get_helper, gettext};
|
||||
use chrono::{DateTime, Local, SecondsFormat, Utc};
|
||||
@@ -286,6 +287,14 @@ impl PixivAppClient {
|
||||
.add_req_middleware(Box::new(PixivAppMiddleware::new(r.internal.clone())));
|
||||
r
|
||||
}
|
||||
|
||||
pub async fn get_illust_details(
|
||||
&self,
|
||||
id: u64,
|
||||
) -> Result<PixivAppIllust, PixivDownloaderError> {
|
||||
let obj = self.internal.get_illust_details(id).await?;
|
||||
Ok(PixivAppIllust::new(obj["illust"].clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<PixivAppClientInternal> for PixivAppClient {
|
||||
|
||||
215
src/pixivapp/illust.rs
Normal file
215
src/pixivapp/illust.rs
Normal file
@@ -0,0 +1,215 @@
|
||||
use super::check::CheckUnknown;
|
||||
use super::image_urls::ImageUrls;
|
||||
use super::tag::Tag;
|
||||
use json::JsonValue;
|
||||
use proc_macros::check_json_keys;
|
||||
|
||||
pub struct PixivAppIllust {
|
||||
data: JsonValue,
|
||||
}
|
||||
|
||||
impl PixivAppIllust {
|
||||
pub fn new(data: JsonValue) -> Self {
|
||||
Self { data }
|
||||
}
|
||||
|
||||
pub fn id(&self) -> Option<u64> {
|
||||
self.data["id"].as_u64()
|
||||
}
|
||||
|
||||
pub fn title(&self) -> Option<&str> {
|
||||
self.data["title"].as_str()
|
||||
}
|
||||
|
||||
pub fn typ(&self) -> Option<&str> {
|
||||
self.data["type"].as_str()
|
||||
}
|
||||
|
||||
pub fn image_urls(&self) -> ImageUrls {
|
||||
return ImageUrls::new(self.data["image_urls"].clone());
|
||||
}
|
||||
|
||||
pub fn caption(&self) -> Option<&str> {
|
||||
self.data["caption"].as_str()
|
||||
}
|
||||
|
||||
pub fn restrict(&self) -> Option<u64> {
|
||||
self.data["restrict"].as_u64()
|
||||
}
|
||||
|
||||
pub fn user_id(&self) -> Option<u64> {
|
||||
self.data["user"]["id"].as_u64()
|
||||
}
|
||||
|
||||
pub fn user_name(&self) -> Option<&str> {
|
||||
self.data["user"]["name"].as_str()
|
||||
}
|
||||
|
||||
pub fn user_account(&self) -> Option<&str> {
|
||||
self.data["user"]["account"].as_str()
|
||||
}
|
||||
|
||||
pub fn user_profile_image_urls_medium(&self) -> Option<&str> {
|
||||
self.data["user"]["profile_image_urls"]["medium"].as_str()
|
||||
}
|
||||
|
||||
pub fn user_is_followed(&self) -> Option<bool> {
|
||||
self.data["user"]["is_followed"].as_bool()
|
||||
}
|
||||
|
||||
pub fn tags(&self) -> Vec<Tag> {
|
||||
let mut tags = Vec::new();
|
||||
for tag in self.data["tags"].members() {
|
||||
tags.push(Tag::new(tag.clone()));
|
||||
}
|
||||
tags
|
||||
}
|
||||
|
||||
pub fn create_date(&self) -> Option<&str> {
|
||||
self.data["create_date"].as_str()
|
||||
}
|
||||
|
||||
pub fn page_count(&self) -> Option<u64> {
|
||||
self.data["page_count"].as_u64()
|
||||
}
|
||||
|
||||
pub fn width(&self) -> Option<u64> {
|
||||
self.data["width"].as_u64()
|
||||
}
|
||||
|
||||
pub fn height(&self) -> Option<u64> {
|
||||
self.data["height"].as_u64()
|
||||
}
|
||||
|
||||
pub fn sanity_level(&self) -> Option<u64> {
|
||||
self.data["sanity_level"].as_u64()
|
||||
}
|
||||
|
||||
pub fn x_restrict(&self) -> Option<u64> {
|
||||
self.data["x_restrict"].as_u64()
|
||||
}
|
||||
|
||||
pub fn original_image_url(&self) -> Option<&str> {
|
||||
self.data["meta_single_page"]["original_image_url"].as_str()
|
||||
}
|
||||
|
||||
pub fn meta_pages(&self) -> Vec<ImageUrls> {
|
||||
let mut meta_pages = Vec::new();
|
||||
for meta_page in self.data["meta_pages"].members() {
|
||||
meta_pages.push(ImageUrls::new(meta_page["image_urls"].clone()));
|
||||
}
|
||||
meta_pages
|
||||
}
|
||||
|
||||
pub fn total_view(&self) -> Option<u64> {
|
||||
self.data["total_view"].as_u64()
|
||||
}
|
||||
|
||||
pub fn total_bookmarks(&self) -> Option<u64> {
|
||||
self.data["total_bookmarks"].as_u64()
|
||||
}
|
||||
|
||||
pub fn is_bookmarked(&self) -> Option<bool> {
|
||||
self.data["is_bookmarked"].as_bool()
|
||||
}
|
||||
|
||||
pub fn visible(&self) -> Option<bool> {
|
||||
self.data["visible"].as_bool()
|
||||
}
|
||||
|
||||
pub fn is_muted(&self) -> Option<bool> {
|
||||
self.data["is_muted"].as_bool()
|
||||
}
|
||||
|
||||
pub fn total_comments(&self) -> Option<u64> {
|
||||
self.data["total_comments"].as_u64()
|
||||
}
|
||||
|
||||
pub fn illust_ai_type(&self) -> Option<u64> {
|
||||
self.data["illust_ai_type"].as_u64()
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckUnknown for PixivAppIllust {
|
||||
fn check_unknown(&self) -> Result<(), String> {
|
||||
check_json_keys!(
|
||||
"id"+,
|
||||
"title"+,
|
||||
"type"+typ,
|
||||
"image_urls": ["square_medium", "medium", "large"],
|
||||
"caption"+,
|
||||
"restrict"+,
|
||||
"user": [
|
||||
"id"+,
|
||||
"name"+,
|
||||
"account"+,
|
||||
"profile_image_urls": ["medium"],
|
||||
"is_followed"+
|
||||
],
|
||||
"tags",
|
||||
"tools",
|
||||
"create_date"+,
|
||||
"page_count"+,
|
||||
"width"+,
|
||||
"height"+,
|
||||
"sanity_level"+,
|
||||
"x_restrict"+,
|
||||
"series",
|
||||
"meta_single_page": ["original_image_url"],
|
||||
"meta_pages",
|
||||
"total_view"+,
|
||||
"total_bookmarks"+,
|
||||
"is_bookmarked"+,
|
||||
"visible"+,
|
||||
"is_muted"+,
|
||||
"total_comments"+,
|
||||
"illust_ai_type"+,
|
||||
"illust_book_style",
|
||||
"comment_access_control"
|
||||
);
|
||||
for i in self.tags() {
|
||||
i.check_unknown()?;
|
||||
}
|
||||
for i in self.meta_pages() {
|
||||
i.check_unknown()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for PixivAppIllust {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("PixivAppIllust")
|
||||
.field("id", &self.id())
|
||||
.field("title", &self.title())
|
||||
.field("type", &self.typ())
|
||||
.field("image_urls", &self.image_urls())
|
||||
.field("caption", &self.caption())
|
||||
.field("restrict", &self.restrict())
|
||||
.field("user_id", &self.user_id())
|
||||
.field("user_name", &self.user_name())
|
||||
.field("user_account", &self.user_account())
|
||||
.field(
|
||||
"user_profile_image_urls_medium",
|
||||
&self.user_profile_image_urls_medium(),
|
||||
)
|
||||
.field("user_is_followed", &self.user_is_followed())
|
||||
.field("tags", &self.tags())
|
||||
.field("create_date", &self.create_date())
|
||||
.field("page_count", &self.page_count())
|
||||
.field("width", &self.width())
|
||||
.field("height", &self.height())
|
||||
.field("sanity_level", &self.sanity_level())
|
||||
.field("x_restrict", &self.x_restrict())
|
||||
.field("original_image_url", &self.original_image_url())
|
||||
.field("meta_pages", &self.meta_pages())
|
||||
.field("total_view", &self.total_view())
|
||||
.field("total_bookmarks", &self.total_bookmarks())
|
||||
.field("is_bookmarked", &self.is_bookmarked())
|
||||
.field("visible", &self.visible())
|
||||
.field("is_muted", &self.is_muted())
|
||||
.field("total_comments", &self.total_comments())
|
||||
.field("illust_ai_type", &self.illust_ai_type())
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
47
src/pixivapp/image_urls.rs
Normal file
47
src/pixivapp/image_urls.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use super::check::CheckUnknown;
|
||||
use json::JsonValue;
|
||||
use proc_macros::check_json_keys;
|
||||
|
||||
pub struct ImageUrls {
|
||||
data: JsonValue,
|
||||
}
|
||||
|
||||
impl ImageUrls {
|
||||
pub fn new(data: JsonValue) -> Self {
|
||||
Self { data }
|
||||
}
|
||||
|
||||
pub fn square_medium(&self) -> Option<&str> {
|
||||
self.data["square_medium"].as_str()
|
||||
}
|
||||
|
||||
pub fn medium(&self) -> Option<&str> {
|
||||
self.data["medium"].as_str()
|
||||
}
|
||||
|
||||
pub fn large(&self) -> Option<&str> {
|
||||
self.data["large"].as_str()
|
||||
}
|
||||
|
||||
pub fn original(&self) -> Option<&str> {
|
||||
self.data["original"].as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckUnknown for ImageUrls {
|
||||
fn check_unknown(&self) -> Result<(), String> {
|
||||
check_json_keys!("square_medium"+, "medium"+, "large"+, "original"+);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for ImageUrls {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("ImageUrls")
|
||||
.field("square_medium", &self.square_medium())
|
||||
.field("medium", &self.medium())
|
||||
.field("large", &self.large())
|
||||
.field("original", &self.original())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
pub mod check;
|
||||
/// Error handling for pixiv app api
|
||||
pub mod error;
|
||||
pub mod illust;
|
||||
pub mod image_urls;
|
||||
pub mod tag;
|
||||
|
||||
37
src/pixivapp/tag.rs
Normal file
37
src/pixivapp/tag.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use super::check::CheckUnknown;
|
||||
use json::JsonValue;
|
||||
use proc_macros::check_json_keys;
|
||||
|
||||
pub struct Tag {
|
||||
data: JsonValue,
|
||||
}
|
||||
|
||||
impl Tag {
|
||||
pub fn new(data: JsonValue) -> Self {
|
||||
Self { data }
|
||||
}
|
||||
|
||||
pub fn name(&self) -> Option<&str> {
|
||||
self.data["name"].as_str()
|
||||
}
|
||||
|
||||
pub fn translated_name(&self) -> Option<&str> {
|
||||
self.data["translated_name"].as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl CheckUnknown for Tag {
|
||||
fn check_unknown(&self) -> Result<(), String> {
|
||||
check_json_keys!("name"+, "translated_name");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Tag {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Tag")
|
||||
.field("name", &self.name())
|
||||
.field("translated_name", &self.translated_name())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user