mirror of
https://github.com/lifegpc/pixiv_downloader.git
synced 2026-07-08 01:32:41 +08:00
Add fanbox API client
This commit is contained in:
@@ -12,6 +12,7 @@ use crate::downloader::DownloaderResult;
|
||||
use crate::downloader::LocalFile;
|
||||
use crate::error::PixivDownloaderError;
|
||||
use crate::ext::try_err::TryErr;
|
||||
use crate::fanbox_api::FanboxClient;
|
||||
use crate::gettext;
|
||||
use crate::opthelper::get_helper;
|
||||
use crate::pixiv_link::PixivID;
|
||||
@@ -29,22 +30,22 @@ use std::sync::Arc;
|
||||
impl Main {
|
||||
pub async fn download(&mut self) -> i32 {
|
||||
let pw = Arc::new(PixivWebClient::new());
|
||||
if !pw.init() {
|
||||
println!("{}", gettext("Failed to initialize pixiv web api client."));
|
||||
return 1;
|
||||
}
|
||||
if !pw.check_login().await {
|
||||
return 1;
|
||||
}
|
||||
if !pw.logined() {
|
||||
println!(
|
||||
"{}",
|
||||
gettext("Warning: Web api client not logined, some future may not work.")
|
||||
);
|
||||
}
|
||||
let fc = Arc::new(FanboxClient::new());
|
||||
for id in self.cmd.as_ref().unwrap().ids.iter() {
|
||||
match id {
|
||||
PixivID::Artwork(id) => {
|
||||
if !pw.is_inited() {
|
||||
if !pw.init() {
|
||||
println!("{}", gettext("Failed to initialize pixiv web api client."));
|
||||
return 1;
|
||||
}
|
||||
if !pw.check_login().await {
|
||||
return 1;
|
||||
}
|
||||
if !pw.logined() {
|
||||
println!("{}", gettext("Warning: Web api client not logined, some future may not work."));
|
||||
}
|
||||
}
|
||||
let r = self.download_artwork(Arc::clone(&pw), id.clone()).await;
|
||||
let r = if r.is_ok() {
|
||||
0
|
||||
@@ -60,6 +61,18 @@ impl Main {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
PixivID::FanboxPost(_) => {
|
||||
if !fc.is_inited() {
|
||||
if !fc.init() {
|
||||
println!("{}", gettext("Failed to initialize fanbox api client."));
|
||||
return 1;
|
||||
}
|
||||
if !fc.check_login().await {
|
||||
return 1;
|
||||
}
|
||||
println!("Logined: {}", fc.logined());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
|
||||
114
src/fanbox_api.rs
Normal file
114
src/fanbox_api.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
use crate::ext::atomic::AtomicQuick;
|
||||
use crate::ext::replace::ReplaceWith2;
|
||||
use crate::ext::rw_lock::GetRwLock;
|
||||
use crate::gettext;
|
||||
use crate::opthelper::get_helper;
|
||||
use crate::parser::metadata::MetaDataParser;
|
||||
use crate::webclient::WebClient;
|
||||
use json::JsonValue;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::RwLock;
|
||||
|
||||
/// Fanbox API client
|
||||
pub struct FanboxClient {
|
||||
/// Web client
|
||||
client: WebClient,
|
||||
/// true if in is initialized
|
||||
inited: AtomicBool,
|
||||
/// Fanbox global data
|
||||
data: RwLock<Option<JsonValue>>,
|
||||
}
|
||||
|
||||
impl FanboxClient {
|
||||
/// Create an new instance
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
client: WebClient::default(),
|
||||
inited: AtomicBool::new(false),
|
||||
data: RwLock::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Initiailze the interface.
|
||||
///
|
||||
/// Returns true if successed
|
||||
pub fn init(&self) -> bool {
|
||||
let helper = get_helper();
|
||||
let c = helper.cookies();
|
||||
if c.is_some() {
|
||||
if !self.client.read_cookies(c.as_ref().unwrap()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
self.client.set_header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36");
|
||||
self.inited.qstore(true);
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns true if is initialized.
|
||||
pub fn is_inited(&self) -> bool {
|
||||
self.inited.qload()
|
||||
}
|
||||
|
||||
/// Initialize the client if needed.
|
||||
fn auto_init(&self) {
|
||||
if !self.is_inited() {
|
||||
let r = self.init();
|
||||
if !r {
|
||||
panic!("{}", gettext("Failed to initialize pixiv web api client."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check login status.
|
||||
pub async fn check_login(&self) -> bool {
|
||||
self.auto_init();
|
||||
let r = self.client.get("https://www.fanbox.cc", None).await;
|
||||
match r {
|
||||
Some(r) => {
|
||||
let status = r.status();
|
||||
let code = status.as_u16();
|
||||
if code >= 400 {
|
||||
println!("{} {}", gettext("Failed to get fanbox main page:"), status);
|
||||
return false;
|
||||
}
|
||||
match r.text_with_charset("UTF-8").await {
|
||||
Ok(data) => {
|
||||
let mut parser = MetaDataParser::new("metadata");
|
||||
if !parser.parse(data.as_str()) {
|
||||
println!("{}", gettext("Failed to parse fanbox main page."));
|
||||
return false;
|
||||
}
|
||||
if get_helper().verbose() {
|
||||
println!(
|
||||
"{}\n{}",
|
||||
gettext("Fanbox main page's data:"),
|
||||
parser.value.as_ref().unwrap().pretty(2).as_str()
|
||||
);
|
||||
}
|
||||
self.data.replace_with2(parser.value);
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{} {}", gettext("Failed to get fanbox main page:"), e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if is logged in.
|
||||
pub fn logined(&self) -> bool {
|
||||
let data = self.data.get_ref();
|
||||
if data.is_none() {
|
||||
return false;
|
||||
}
|
||||
let value = data.as_ref().unwrap();
|
||||
match value["user"]["isLoggedIn"].as_bool() {
|
||||
Some(b) => b,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ mod error;
|
||||
mod exif;
|
||||
/// Used to extend some thirdparty library
|
||||
mod ext;
|
||||
mod fanbox_api;
|
||||
mod i18n;
|
||||
mod list;
|
||||
mod opt;
|
||||
|
||||
@@ -38,7 +38,9 @@ impl MetaDataParser {
|
||||
return false;
|
||||
}
|
||||
let mkey = format!("meta-{}", self.key.as_str());
|
||||
if e.id.as_ref().unwrap() != mkey.as_str() {
|
||||
if e.id.as_ref().unwrap() != mkey.as_str()
|
||||
&& e.id.as_ref().unwrap() != self.key.as_str()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
let c = e.attributes.get("content");
|
||||
|
||||
@@ -7,6 +7,29 @@ use std::convert::TryInto;
|
||||
lazy_static! {
|
||||
#[doc(hidden)]
|
||||
static ref RE: Regex = Regex::new("^(https?://)?(www\\.)?pixiv\\.net/artworks/(?P<id>\\d+)").unwrap();
|
||||
#[doc(hidden)]
|
||||
static ref RE2: Regex = Regex::new("^(https?://)?(www\\.)?fanbox\\.cc/@(?P<creator>[^/]+)/posts/(?P<id>\\d+)").unwrap();
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
/// Fanbox post ID
|
||||
pub struct FanboxPostID {
|
||||
/// Creator ID
|
||||
creator_id: String,
|
||||
/// Post ID
|
||||
post_id: u64,
|
||||
}
|
||||
|
||||
impl FanboxPostID {
|
||||
/// Create a new id.
|
||||
/// * `creator_id` - Creator ID
|
||||
/// * `post_id` - Post ID
|
||||
pub fn new<C: AsRef<str> + ?Sized>(creator_id: &C, post_id: u64) -> Self {
|
||||
Self {
|
||||
creator_id: String::from(creator_id.as_ref()),
|
||||
post_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Repesent an Pixiv ID
|
||||
@@ -14,6 +37,8 @@ lazy_static! {
|
||||
pub enum PixivID {
|
||||
/// Artwork Id, include illust and manga
|
||||
Artwork(u64),
|
||||
/// Fanbox post
|
||||
FanboxPost(FanboxPostID),
|
||||
}
|
||||
|
||||
pub trait ToPixivID {
|
||||
@@ -27,14 +52,30 @@ impl PixivID {
|
||||
if num.is_ok() {
|
||||
return Some(PixivID::Artwork(num.unwrap()));
|
||||
}
|
||||
let re = RE.captures(s);
|
||||
if re.is_some() {
|
||||
let r = re.unwrap().name("id");
|
||||
if r.is_some() {
|
||||
let r = r.unwrap().as_str();
|
||||
let num = r.parse::<u64>();
|
||||
return Some(PixivID::Artwork(num.unwrap()));
|
||||
}
|
||||
match RE.captures(s) {
|
||||
Some(re) => match re.name("id") {
|
||||
Some(r) => match r.as_str().parse::<u64>() {
|
||||
Ok(r) => return Some(Self::Artwork(r)),
|
||||
Err(_) => {}
|
||||
},
|
||||
None => {}
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
match RE2.captures(s) {
|
||||
Some(re) => match re.name("creator") {
|
||||
Some(creator) => match re.name("id") {
|
||||
Some(id) => match id.as_str().parse::<u64>() {
|
||||
Ok(id) => {
|
||||
return Some(Self::FanboxPost(FanboxPostID::new(creator.as_str(), id)));
|
||||
}
|
||||
Err(_) => {}
|
||||
},
|
||||
None => {}
|
||||
},
|
||||
None => {}
|
||||
},
|
||||
None => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -44,16 +85,25 @@ impl PixivID {
|
||||
Self::Artwork(id) => {
|
||||
format!("https://www.pixiv.net/artworks/{}", id)
|
||||
}
|
||||
Self::FanboxPost(id) => {
|
||||
format!(
|
||||
"https://www.fanbox.cc/@{}/posts/{}",
|
||||
id.creator_id, id.post_id
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for PixivID {
|
||||
fn to_json(&self) -> Option<JsonValue> {
|
||||
match *self {
|
||||
PixivID::Artwork(id) => {
|
||||
Some(json::value!({"type": "artwork", "id": id, "link": self.to_link()}))
|
||||
match &self {
|
||||
&PixivID::Artwork(id) => {
|
||||
Some(json::value!({"type": "artwork", "id": id.clone(), "link": self.to_link()}))
|
||||
}
|
||||
&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()}),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,6 +143,7 @@ impl TryInto<u64> for PixivID {
|
||||
fn try_into(self) -> Result<u64, Self::Error> {
|
||||
match self {
|
||||
Self::Artwork(id) => Ok(id),
|
||||
Self::FanboxPost(id) => Ok(id.post_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,8 +151,9 @@ impl TryInto<u64> for PixivID {
|
||||
impl TryInto<u64> for &PixivID {
|
||||
type Error = ();
|
||||
fn try_into(self) -> Result<u64, Self::Error> {
|
||||
match *self {
|
||||
PixivID::Artwork(id) => Ok(id),
|
||||
match self {
|
||||
PixivID::Artwork(id) => Ok(id.clone()),
|
||||
PixivID::FanboxPost(id) => Ok(id.post_id.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user