mirror of
https://github.com/lifegpc/pixiv_downloader.git
synced 2026-07-08 01:32:41 +08:00
pixiv push now support web api
This commit is contained in:
@@ -3,17 +3,53 @@ use crate::push::every_push::EveryPushTextType;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum PixivMode {
|
||||
/// All artworks
|
||||
All,
|
||||
/// R18 artworks
|
||||
R18,
|
||||
}
|
||||
|
||||
impl PixivMode {
|
||||
pub fn is_r18(&self) -> bool {
|
||||
matches!(self, Self::R18)
|
||||
}
|
||||
}
|
||||
|
||||
fn default_restrict() -> PixivRestrictType {
|
||||
PixivRestrictType::All
|
||||
}
|
||||
|
||||
fn default_mode() -> PixivMode {
|
||||
PixivMode::All
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
pub enum PushTaskPixivAction {
|
||||
Follow {
|
||||
#[serde(default = "default_restrict")]
|
||||
/// Follower's type
|
||||
///
|
||||
/// Only supported when using Pixiv APP API.
|
||||
restrict: PixivRestrictType,
|
||||
#[serde(default = "default_mode")]
|
||||
/// Illust's type
|
||||
///
|
||||
/// Only supported when using Pixiv Web API.
|
||||
mode: PixivMode,
|
||||
},
|
||||
Bookmarks {
|
||||
#[serde(default = "default_restrict")]
|
||||
/// Bookmarks' type
|
||||
restrict: PixivRestrictType,
|
||||
/// User ID
|
||||
uid: u64,
|
||||
},
|
||||
Illusts {
|
||||
/// User ID
|
||||
uid: u64,
|
||||
},
|
||||
}
|
||||
@@ -71,6 +107,7 @@ pub struct EveryPushConfig {
|
||||
pub topic_id: Option<String>,
|
||||
#[serde(default = "defualt_author_locations")]
|
||||
/// Author locations
|
||||
///
|
||||
/// If type is `Image`, this field only support [AuthorLocation::Title].
|
||||
pub author_locations: Vec<AuthorLocation>,
|
||||
#[serde(default = "default_true")]
|
||||
@@ -104,6 +141,7 @@ pub struct PushDeerConfig {
|
||||
pub typ: EveryPushTextType,
|
||||
#[serde(default = "defualt_author_locations")]
|
||||
/// Author locations
|
||||
///
|
||||
/// Not supported when type is `Image`.
|
||||
pub author_locations: Vec<AuthorLocation>,
|
||||
#[serde(default = "default_true")]
|
||||
@@ -114,6 +152,7 @@ pub struct PushDeerConfig {
|
||||
pub add_link: bool,
|
||||
#[serde(default = "default_true")]
|
||||
/// Whether to add artwork link to title
|
||||
///
|
||||
/// Supported when type is `Text`.
|
||||
pub add_link_to_title: bool,
|
||||
#[serde(default = "default_true")]
|
||||
@@ -130,6 +169,7 @@ pub struct PushDeerConfig {
|
||||
pub add_translated_tag: bool,
|
||||
#[serde(default = "default_true")]
|
||||
/// Whether to add image link
|
||||
///
|
||||
/// Supported when type is `Text`.
|
||||
pub add_image_link: bool,
|
||||
}
|
||||
|
||||
@@ -290,6 +290,35 @@ impl PixivWebClient {
|
||||
v
|
||||
}
|
||||
|
||||
pub async fn get_follow(&self, page: u64, r18: bool) -> Option<JsonValue> {
|
||||
self.auto_init();
|
||||
let mut params = self.get_params().unwrap_or_else(|| json::object! {});
|
||||
params["p"] = page.into();
|
||||
params["mode"] = if r18 { "r18" } else { "all" }.into();
|
||||
let r = self
|
||||
.client
|
||||
.get_with_param(
|
||||
"https://www.pixiv.net/ajax/follow_latest/illust",
|
||||
Some(params),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
if r.is_none() {
|
||||
return None;
|
||||
}
|
||||
let r = r.unwrap();
|
||||
let v = self.deal_json(r).await;
|
||||
if v.is_some() {
|
||||
log::debug!(
|
||||
target: "pixiv_web",
|
||||
"{} {}",
|
||||
gettext("Follower's new illusts: "),
|
||||
v.as_ref().unwrap().pretty(2)
|
||||
);
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
pub fn logined(&self) -> bool {
|
||||
let data = self.data.get_ref();
|
||||
if data.is_none() {
|
||||
|
||||
@@ -57,8 +57,8 @@ pub async fn run_push_task(
|
||||
) -> Result<(), PixivDownloaderError> {
|
||||
match &task.config {
|
||||
PushTaskConfig::Pixiv(config) => match &config.act {
|
||||
PushTaskPixivAction::Follow { restrict } => {
|
||||
pixiv_follow::run_push_task(ctx, task, config, restrict, send_mode).await
|
||||
PushTaskPixivAction::Follow { restrict, mode } => {
|
||||
pixiv_follow::run_push_task(ctx, task, config, restrict, mode, send_mode).await
|
||||
}
|
||||
_ => Ok(()),
|
||||
},
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
use super::super::super::preclude::*;
|
||||
use super::pixiv_send_message::send_message;
|
||||
use super::TestSendMode;
|
||||
use crate::db::push_task::PushTaskPixivConfig;
|
||||
use crate::db::push_task::{PixivMode, PushTaskPixivConfig};
|
||||
use crate::db::PushTask;
|
||||
use crate::ext::rw_lock::GetRwLock;
|
||||
use crate::get_helper;
|
||||
use crate::pixiv_app::PixivRestrictType;
|
||||
use crate::pixivapp::illust::PixivAppIllust;
|
||||
use crate::utils::parse_pixiv_id;
|
||||
use json::JsonValue;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::RwLock;
|
||||
|
||||
struct PixivFollowData {
|
||||
web_list: RwLock<Option<JsonValue>>,
|
||||
web_data: RwLock<HashMap<u64, JsonValue>>,
|
||||
}
|
||||
|
||||
impl PixivFollowData {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
web_list: RwLock::new(None),
|
||||
web_data: RwLock::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
@@ -38,6 +37,7 @@ struct RunContext<'a> {
|
||||
task: &'a PushTask,
|
||||
config: &'a PushTaskPixivConfig,
|
||||
restrict: &'a PixivRestrictType,
|
||||
mode: &'a PixivMode,
|
||||
send_mode: Option<&'a TestSendMode>,
|
||||
data: Arc<PixivFollowData>,
|
||||
use_app_api: bool,
|
||||
@@ -51,6 +51,7 @@ impl<'a> RunContext<'a> {
|
||||
task: &'a PushTask,
|
||||
config: &'a PushTaskPixivConfig,
|
||||
restrict: &'a PixivRestrictType,
|
||||
mode: &'a PixivMode,
|
||||
send_mode: Option<&'a TestSendMode>,
|
||||
) -> Self {
|
||||
let helper = get_helper();
|
||||
@@ -59,6 +60,7 @@ impl<'a> RunContext<'a> {
|
||||
task,
|
||||
config,
|
||||
restrict,
|
||||
mode,
|
||||
send_mode,
|
||||
data: Arc::new(PixivFollowData::new()),
|
||||
use_app_api: config.use_app_api.unwrap_or(helper.use_app_api()),
|
||||
@@ -72,6 +74,77 @@ impl<'a> RunContext<'a> {
|
||||
pub async fn run(&self) -> Result<(), PixivDownloaderError> {
|
||||
if self.use_app_api {
|
||||
self.app_run().await?;
|
||||
} else {
|
||||
self.web_run().await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn web_run(&self) -> Result<(), PixivDownloaderError> {
|
||||
let pw = self.ctx.pixiv_web_client().await;
|
||||
let data = pw
|
||||
.get_follow(1, self.mode.is_r18())
|
||||
.await
|
||||
.ok_or("Failed to get follow.")?;
|
||||
let illusts = &data["thumbnails"]["illust"];
|
||||
match self.send_mode {
|
||||
Some(m) => {
|
||||
if m.is_all() {
|
||||
for i in illusts.members() {
|
||||
self.web_illust(i, &data).await?;
|
||||
}
|
||||
} else {
|
||||
let index = m.to_index(illusts.len());
|
||||
if let Some(index) = index {
|
||||
self.web_illust(&illusts[index], &data).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn web_illust(
|
||||
&self,
|
||||
illust: &JsonValue,
|
||||
data: &JsonValue,
|
||||
) -> Result<(), PixivDownloaderError> {
|
||||
let id = parse_pixiv_id(&illust["id"]).ok_or("illust id is none")?;
|
||||
let wdata = match self.data.get_web_data(id) {
|
||||
Some(d) => d,
|
||||
None => {
|
||||
let pw = self.ctx.pixiv_web_client().await;
|
||||
let wdata = pw
|
||||
.get_artwork_ajax(id)
|
||||
.await
|
||||
.ok_or("Failed to get artwork ajax.")?;
|
||||
self.data.set_web_data(id, wdata.clone());
|
||||
wdata
|
||||
}
|
||||
};
|
||||
let len = illust["pageCount"].as_u64().unwrap_or(1);
|
||||
let pdata = if len != 1 {
|
||||
let pw = self.ctx.pixiv_web_client().await;
|
||||
let pdata = pw
|
||||
.get_illust_pages(id)
|
||||
.await
|
||||
.ok_or("Failed to get illust pages.")?;
|
||||
Some(pdata)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
for i in self.task.push_configs.iter() {
|
||||
send_message(
|
||||
self.ctx.clone(),
|
||||
None,
|
||||
Some(&wdata),
|
||||
pdata.as_ref(),
|
||||
Some(illust),
|
||||
Some(&data["tagTranslation"]),
|
||||
i,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -117,7 +190,16 @@ impl<'a> RunContext<'a> {
|
||||
}
|
||||
};
|
||||
for i in self.task.push_configs.iter() {
|
||||
send_message(self.ctx.clone(), Some(&illust), data.as_ref(), i).await?;
|
||||
send_message(
|
||||
self.ctx.clone(),
|
||||
Some(&illust),
|
||||
data.as_ref(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
i,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -128,8 +210,9 @@ pub async fn run_push_task(
|
||||
task: &PushTask,
|
||||
config: &PushTaskPixivConfig,
|
||||
restrict: &PixivRestrictType,
|
||||
mode: &PixivMode,
|
||||
send_mode: Option<&TestSendMode>,
|
||||
) -> Result<(), PixivDownloaderError> {
|
||||
let ctx = RunContext::new(ctx, task, config, restrict, send_mode);
|
||||
let ctx = RunContext::new(ctx, task, config, restrict, mode, send_mode);
|
||||
ctx.run().await
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::parser::description::DescriptionParser;
|
||||
use crate::pixivapp::illust::PixivAppIllust;
|
||||
use crate::push::every_push::{EveryPushClient, EveryPushTextType};
|
||||
use crate::push::pushdeer::PushdeerClient;
|
||||
use crate::utils::get_file_name_from_url;
|
||||
use crate::utils::{get_file_name_from_url, parse_pixiv_id};
|
||||
use crate::{get_helper, gettext};
|
||||
use json::JsonValue;
|
||||
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
|
||||
@@ -15,6 +15,9 @@ struct RunContext<'a> {
|
||||
ctx: Arc<ServerContext>,
|
||||
illust: Option<&'a PixivAppIllust>,
|
||||
data: Option<&'a JsonValue>,
|
||||
pdata: Option<&'a JsonValue>,
|
||||
tdata: Option<&'a JsonValue>,
|
||||
translated_table: Option<&'a JsonValue>,
|
||||
cfg: &'a PushConfig,
|
||||
}
|
||||
|
||||
@@ -28,7 +31,11 @@ impl<'a> RunContext<'a> {
|
||||
None => {}
|
||||
}
|
||||
match self.data {
|
||||
Some(d) => d["illustTitle"].as_str(),
|
||||
Some(d) => return d["title"].as_str().or_else(|| d["illustTitle"].as_str()),
|
||||
None => {}
|
||||
}
|
||||
match self.tdata {
|
||||
Some(d) => d["title"].as_str(),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
@@ -42,12 +49,11 @@ impl<'a> RunContext<'a> {
|
||||
None => {}
|
||||
}
|
||||
match self.data {
|
||||
Some(d) => d["illustId"]
|
||||
.as_u64()
|
||||
.or_else(|| match d["illustId"].as_str() {
|
||||
Some(s) => s.parse().ok(),
|
||||
None => None,
|
||||
}),
|
||||
Some(d) => return parse_pixiv_id(&d["id"]).or_else(|| parse_pixiv_id(&d["illustId"])),
|
||||
None => {}
|
||||
}
|
||||
match self.tdata {
|
||||
Some(d) => parse_pixiv_id(&d["id"]),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
@@ -61,6 +67,10 @@ impl<'a> RunContext<'a> {
|
||||
None => {}
|
||||
}
|
||||
match self.data {
|
||||
Some(d) => return d["userName"].as_str(),
|
||||
None => {}
|
||||
}
|
||||
match self.tdata {
|
||||
Some(d) => d["userName"].as_str(),
|
||||
None => None,
|
||||
}
|
||||
@@ -90,10 +100,11 @@ impl<'a> RunContext<'a> {
|
||||
None => {}
|
||||
}
|
||||
match self.data {
|
||||
Some(d) => d["userId"].as_u64().or_else(|| match d["userId"].as_str() {
|
||||
Some(s) => s.parse().ok(),
|
||||
None => None,
|
||||
}),
|
||||
Some(d) => return parse_pixiv_id(&d["userId"]),
|
||||
None => {}
|
||||
}
|
||||
match self.tdata {
|
||||
Some(d) => parse_pixiv_id(&d["userId"]),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
@@ -112,7 +123,15 @@ impl<'a> RunContext<'a> {
|
||||
None => {}
|
||||
}
|
||||
match self.data {
|
||||
Some(d) => d["pageCount"].as_u64(),
|
||||
Some(d) => return d["pageCount"].as_u64(),
|
||||
None => {}
|
||||
}
|
||||
match self.tdata {
|
||||
Some(d) => return d["pageCount"].as_u64(),
|
||||
None => {}
|
||||
}
|
||||
match self.pdata {
|
||||
Some(d) => Some(d.len() as u64),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
@@ -135,6 +154,20 @@ impl<'a> RunContext<'a> {
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
match self.pdata {
|
||||
Some(d) => {
|
||||
return d[index as usize]["urls"]["original"]
|
||||
.as_str()
|
||||
.map(|s| s.to_owned())
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
if index == 0 {
|
||||
match self.data {
|
||||
Some(d) => return d["urls"]["original"].as_str().map(|s| s.to_owned()),
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -187,9 +220,101 @@ impl<'a> RunContext<'a> {
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
match self.data {
|
||||
Some(d) => {
|
||||
if let Some(id) = d["aiType"].as_u64() {
|
||||
if id == 2 {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
match self.tdata {
|
||||
Some(d) => {
|
||||
if let Some(id) = d["aiType"].as_u64() {
|
||||
if id == 2 {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn add_ai_tag(&self) -> bool {
|
||||
match self.cfg {
|
||||
PushConfig::EveryPush(e) => e.add_ai_tag,
|
||||
PushConfig::PushDeer(e) => e.add_ai_tag,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_translated_tag(&self) -> bool {
|
||||
match self.cfg {
|
||||
PushConfig::EveryPush(e) => e.add_translated_tag,
|
||||
PushConfig::PushDeer(e) => e.add_translated_tag,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_tags_md(&self, text: &mut String, ensure_ascii: bool) {
|
||||
if self.add_ai_tag() && self.is_ai() {
|
||||
text.push_str("#");
|
||||
text.push_str(gettext("AI generated"));
|
||||
text.push_str(" ");
|
||||
}
|
||||
if let Some(i) = self.illust {
|
||||
for tag in i.tags() {
|
||||
if let Some(name) = tag.name() {
|
||||
let encoded = if ensure_ascii {
|
||||
percent_encode(name.as_bytes(), NON_ALPHANUMERIC).to_string()
|
||||
} else {
|
||||
name.to_owned()
|
||||
};
|
||||
text.push_str(&format!(
|
||||
"[#{}](https://www.pixiv.net/tags/{}) ",
|
||||
name, &encoded
|
||||
));
|
||||
if self.add_translated_tag() {
|
||||
if let Some(t) = tag.translated_name() {
|
||||
text.push_str(&format!(
|
||||
"[#{}](https://www.pixiv.net/tags/{}) ",
|
||||
t, &encoded
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
text.push_str(" \n");
|
||||
return;
|
||||
}
|
||||
if let Some(d) = self.data {
|
||||
for tag in d["tags"]["tags"].members() {
|
||||
if let Some(name) = &tag["tag"].as_str() {
|
||||
let encoded = if ensure_ascii {
|
||||
percent_encode(name.as_bytes(), NON_ALPHANUMERIC).to_string()
|
||||
} else {
|
||||
name.to_string()
|
||||
};
|
||||
text.push_str(&format!(
|
||||
"[#{}](https://www.pixiv.net/tags/{}) ",
|
||||
name, &encoded
|
||||
));
|
||||
if self.add_translated_tag() {
|
||||
if let Some(t) = &tag["translation"]["en"].as_str() {
|
||||
text.push_str(&format!(
|
||||
"[#{}](https://www.pixiv.net/tags/{}) ",
|
||||
t, &encoded
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
text.push_str(" \n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_every_push(&self, cfg: &EveryPushConfig) -> Result<(), PixivDownloaderError> {
|
||||
let client = EveryPushClient::new(&cfg.push_server);
|
||||
match cfg.typ {
|
||||
@@ -285,30 +410,7 @@ impl<'a> RunContext<'a> {
|
||||
}
|
||||
}
|
||||
if cfg.add_tags {
|
||||
if cfg.add_ai_tag && self.is_ai() {
|
||||
text.push_str("#");
|
||||
text.push_str(gettext("AI generated"));
|
||||
text.push_str(" ");
|
||||
}
|
||||
if let Some(i) = self.illust {
|
||||
for tag in i.tags() {
|
||||
if let Some(name) = tag.name() {
|
||||
text.push_str(&format!(
|
||||
"[#{}](https://www.pixiv.net/tags/{}) ",
|
||||
name, name
|
||||
));
|
||||
if cfg.add_translated_tag {
|
||||
if let Some(t) = tag.translated_name() {
|
||||
text.push_str(&format!(
|
||||
"[#{}](https://www.pixiv.net/tags/{}) ",
|
||||
t, name
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
text.push_str(" \n");
|
||||
self.add_tags_md(&mut text, false);
|
||||
}
|
||||
if cfg.author_locations.contains(&AuthorLocation::Bottom) {
|
||||
if let Some(a) = &author {
|
||||
@@ -415,31 +517,7 @@ impl<'a> RunContext<'a> {
|
||||
}
|
||||
}
|
||||
if cfg.add_tags {
|
||||
if cfg.add_ai_tag && self.is_ai() {
|
||||
text.push_str("#");
|
||||
text.push_str(gettext("AI generated"));
|
||||
text.push_str(" ");
|
||||
}
|
||||
if let Some(i) = self.illust {
|
||||
for tag in i.tags() {
|
||||
if let Some(name) = tag.name() {
|
||||
let encoded = percent_encode(name.as_bytes(), NON_ALPHANUMERIC);
|
||||
text.push_str(&format!(
|
||||
"[#{}](https://www.pixiv.net/tags/{}) ",
|
||||
name, &encoded
|
||||
));
|
||||
if cfg.add_translated_tag {
|
||||
if let Some(t) = tag.translated_name() {
|
||||
text.push_str(&format!(
|
||||
"[#{}](https://www.pixiv.net/tags/{}) ",
|
||||
t, &encoded
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
text.push_str(" \n");
|
||||
self.add_tags_md(&mut text, true);
|
||||
}
|
||||
if cfg.author_locations.contains(&AuthorLocation::Bottom) {
|
||||
if let Some(a) = &author {
|
||||
@@ -495,31 +573,7 @@ impl<'a> RunContext<'a> {
|
||||
}
|
||||
}
|
||||
if cfg.add_tags {
|
||||
if cfg.add_ai_tag && self.is_ai() {
|
||||
text.push_str("#");
|
||||
text.push_str(gettext("AI generated"));
|
||||
text.push_str(" ");
|
||||
}
|
||||
if let Some(i) = self.illust {
|
||||
for tag in i.tags() {
|
||||
if let Some(name) = tag.name() {
|
||||
let encoded = percent_encode(name.as_bytes(), NON_ALPHANUMERIC);
|
||||
text.push_str(&format!(
|
||||
"[#{}](https://www.pixiv.net/tags/{}) ",
|
||||
name, &encoded
|
||||
));
|
||||
if cfg.add_translated_tag {
|
||||
if let Some(t) = tag.translated_name() {
|
||||
text.push_str(&format!(
|
||||
"[#{}](https://www.pixiv.net/tags/{}) ",
|
||||
t, &encoded
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
text.push_str(" \n");
|
||||
self.add_tags_md(&mut text, true);
|
||||
}
|
||||
if cfg.author_locations.contains(&AuthorLocation::Bottom) {
|
||||
if let Some(a) = &author {
|
||||
@@ -550,12 +604,18 @@ pub async fn send_message(
|
||||
ctx: Arc<ServerContext>,
|
||||
illust: Option<&PixivAppIllust>,
|
||||
data: Option<&JsonValue>,
|
||||
pdata: Option<&JsonValue>,
|
||||
tdata: Option<&JsonValue>,
|
||||
translated_table: Option<&JsonValue>,
|
||||
cfg: &PushConfig,
|
||||
) -> Result<(), PixivDownloaderError> {
|
||||
let ctx = RunContext {
|
||||
ctx,
|
||||
illust,
|
||||
data,
|
||||
pdata,
|
||||
tdata,
|
||||
translated_table,
|
||||
cfg,
|
||||
};
|
||||
ctx.run().await
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::gettext;
|
||||
use json::JsonValue;
|
||||
use reqwest::IntoUrl;
|
||||
use std::env;
|
||||
use std::io::Write;
|
||||
@@ -69,3 +70,10 @@ pub fn get_file_name_from_url<U: IntoUrl>(url: U) -> Option<String> {
|
||||
}
|
||||
Some(String::from(r.unwrap()))
|
||||
}
|
||||
|
||||
pub fn parse_pixiv_id(id: &JsonValue) -> Option<u64> {
|
||||
id.as_u64().or_else(|| match id.as_str() {
|
||||
Some(s) => s.parse::<u64>().ok(),
|
||||
None => None,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user