Add fanbox API client

This commit is contained in:
2022-06-24 04:37:18 +00:00
committed by GitHub
parent 55e10430ee
commit fc1a44e55a
5 changed files with 209 additions and 27 deletions

View File

@@ -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
View 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,
}
}
}

View File

@@ -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;

View File

@@ -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");

View File

@@ -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()),
}
}
}