introduce libpixiv library and login via refresh token

This commit is contained in:
theBreadCompany 2024-09-23 00:34:38 +02:00
parent c7dc97aa13
commit 0fcf36a861
8 changed files with 344 additions and 74 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
/target
result
.vscode

261
Cargo.lock generated
View file

@ -4,18 +4,18 @@ version = 3
[[package]]
name = "addr2line"
version = "0.22.0"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "ahash"
@ -39,6 +39,21 @@ dependencies = [
"memchr",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "async-stream"
version = "0.3.5"
@ -101,17 +116,17 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "backtrace"
version = "0.3.73"
version = "0.3.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a"
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets 0.52.6",
]
[[package]]
@ -140,9 +155,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]]
name = "bytemuck"
version = "1.17.1"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773d90827bc3feecfb67fab12e24de0749aad83c74b9504ecde46237b5cd24e2"
checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae"
[[package]]
name = "byteorder"
@ -152,15 +167,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.7.1"
version = "1.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3"
[[package]]
name = "cc"
version = "1.1.16"
version = "1.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9d013ecb737093c0e86b151a7b837993cf9ec6c502946cfb44bedc392421e0b"
checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0"
dependencies = [
"shlex",
]
@ -171,6 +186,20 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-targets 0.52.6",
]
[[package]]
name = "cookie"
version = "0.18.1"
@ -326,6 +355,18 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "fallible-iterator"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
[[package]]
name = "fallible-streaming-iterator"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "fastrand"
version = "2.1.1"
@ -460,6 +501,19 @@ dependencies = [
"byteorder",
]
[[package]]
name = "fxpixiv"
version = "0.1.0"
dependencies = [
"json",
"maud",
"reqwest",
"rocket",
"rusqlite",
"scraper",
"tl",
]
[[package]]
name = "generator"
version = "0.7.5"
@ -495,9 +549,9 @@ dependencies = [
[[package]]
name = "gimli"
version = "0.29.0"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64"
[[package]]
name = "glob"
@ -548,6 +602,18 @@ name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
dependencies = [
"ahash",
]
[[package]]
name = "hashlink"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af"
dependencies = [
"hashbrown",
]
[[package]]
name = "hermit-abi"
@ -722,9 +788,9 @@ dependencies = [
[[package]]
name = "hyper-util"
version = "0.1.7"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
checksum = "da62f120a8a37763efb0cf8fdf264b884c7b8b9ac8660b900c8661030c00e6ba"
dependencies = [
"bytes",
"futures-channel",
@ -740,6 +806,29 @@ dependencies = [
"tracing",
]
[[package]]
name = "iana-time-zone"
version = "0.1.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "idna"
version = "0.5.0"
@ -769,9 +858,9 @@ checksum = "c8fae54786f62fb2918dcfae3d568594e50eb9b5c25bf04371af6fe7516452fb"
[[package]]
name = "ipnet"
version = "2.9.0"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4"
[[package]]
name = "is-terminal"
@ -817,6 +906,27 @@ version = "0.2.158"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
[[package]]
name = "libpixiv"
version = "0.1.0"
dependencies = [
"chrono",
"md5",
"reqwest",
"serde_json",
"tokio",
]
[[package]]
name = "libsqlite3-sys"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
dependencies = [
"pkg-config",
"vcpkg",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.14"
@ -905,6 +1015,12 @@ dependencies = [
"syn",
]
[[package]]
name = "md5"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
[[package]]
name = "memchr"
version = "2.7.4"
@ -919,11 +1035,11 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "miniz_oxide"
version = "0.7.4"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1"
dependencies = [
"adler",
"adler2",
]
[[package]]
@ -996,6 +1112,15 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
@ -1317,18 +1442,6 @@ dependencies = [
"yansi",
]
[[package]]
name = "fxpixiv"
version = "0.1.0"
dependencies = [
"json",
"maud",
"reqwest",
"rocket",
"scraper",
"tl",
]
[[package]]
name = "quote"
version = "1.0.37"
@ -1370,9 +1483,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.5.3"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853"
dependencies = [
"bitflags",
]
@ -1580,6 +1693,20 @@ dependencies = [
"uncased",
]
[[package]]
name = "rusqlite"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e"
dependencies = [
"bitflags",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
"smallvec",
]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
@ -1588,9 +1715,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustix"
version = "0.38.36"
version = "0.38.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f55e80d50763938498dd5ebb18647174e0c76dc38c5505294bb224624f30f36"
checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
dependencies = [
"bitflags",
"errno",
@ -1601,9 +1728,9 @@ dependencies = [
[[package]]
name = "rustls"
version = "0.23.12"
version = "0.23.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044"
checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8"
dependencies = [
"once_cell",
"rustls-pki-types",
@ -1630,9 +1757,9 @@ checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
[[package]]
name = "rustls-webpki"
version = "0.102.7"
version = "0.102.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84678086bd54edf2b415183ed7a94d0efb049f1b646a33e22a36f3794be6ae56"
checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9"
dependencies = [
"ring",
"rustls-pki-types",
@ -1653,11 +1780,11 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "schannel"
version = "0.1.23"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b"
dependencies = [
"windows-sys 0.52.0",
"windows-sys 0.59.0",
]
[[package]]
@ -1703,9 +1830,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
version = "2.11.1"
version = "2.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf"
checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6"
dependencies = [
"core-foundation-sys",
"libc",
@ -1732,18 +1859,18 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.209"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.209"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [
"proc-macro2",
"quote",
@ -2046,6 +2173,7 @@ dependencies = [
"bytes",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
@ -2132,9 +2260,9 @@ dependencies = [
[[package]]
name = "toml_edit"
version = "0.22.20"
version = "0.22.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
checksum = "3b072cee73c449a636ffd6f32bd8de3a9f7119139aff882f44943ce2986dc5cf"
dependencies = [
"indexmap",
"serde",
@ -2264,30 +2392,30 @@ checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
[[package]]
name = "unicode-ident"
version = "1.0.12"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "unicode-normalization"
version = "0.1.23"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5"
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
dependencies = [
"tinyvec",
]
[[package]]
name = "unicode-width"
version = "0.1.13"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
[[package]]
name = "unicode-xid"
version = "0.2.5"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "untrusted"
@ -2453,6 +2581,15 @@ dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-core"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-registry"
version = "0.2.0"

View file

@ -1,12 +1,6 @@
[package]
name = "fxpixiv"
version = "0.1.0"
edition = "2021"
[workspace]
[dependencies]
tl = "0.7.8"
reqwest = "0.12.7"
json = "0.12.4"
rocket = "0.5.1"
scraper = "0.20.0"
maud = "0.26.0"
members = [
"fxpixiv",
"libpixiv",
]

13
fxpixiv/Cargo.toml Normal file
View file

@ -0,0 +1,13 @@
[package]
name = "fxpixiv"
version = "0.1.0"
edition = "2021"
[dependencies]
tl = "0.7.8"
reqwest = "0.12.7"
json = "0.12.4"
rocket = "0.5.1"
scraper = "0.20.0"
maud = "0.26.0"
rusqlite = "0.32.1"

12
libpixiv/Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
name = "libpixiv"
version = "0.1.0"
edition = "2021"
[dependencies]
chrono = "0.4.38"
md5 = "0.7.0"
reqwest = "0.12.7"
serde_json = "1.0.128"
tokio = { version = "1.40.0", features = ["full"] }

14
libpixiv/README.md Normal file
View file

@ -0,0 +1,14 @@
# libpixiv
Client library for [pixiv](https://pixiv.net)
## Usage
For now, no OAuth2 login will be provided, so please make sure to provide refresh tokens yourself.
## Testing
You can run the following command to test existing features:
```bash
PIXIV_REFRESH_TOKEN= cargo test --lib
```

99
libpixiv/src/lib.rs Normal file
View file

@ -0,0 +1,99 @@
extern crate md5;
extern crate reqwest;
extern crate serde_json;
use chrono::{Local};
use serde_json::{Value};
use std::sync::{Arc, Mutex};
use reqwest::header::{HeaderName, USER_AGENT, CONTENT_TYPE};
struct PixivAppClient {
/// bearer token
access_token: Arc<Mutex<String>>,
refresh_token: Arc<Mutex<String>>,
http_client: reqwest::Client,
host: &str,
}
impl PixivAppClient {
fn new(token: &str) -> Self {
let client = Self {
access_token: Arc::new(Mutex::new(String::new())),
refresh_token: Arc::new(Mutex::new(String::from(token))),
http_client: reqwest::Client::new(),
host: "https://app-api.pixiv.net/"
};
client
}
fn md5(input: &str) -> String {
let result = md5::compute(input);
format!("{:02x}", result)
}
pub async fn refresh_token(&mut self) {
let time = Local::now().format("%y-%m-%dT%H:%m:%s+00:00");
let time_str = format!("{}", time);
let cloned_refresh_token = Arc::clone(&self.refresh_token);
let cloned_refresh_token_str = &cloned_refresh_token.lock().unwrap();
let client_id = "MOBrBDS8blbauoSck0ZfDbtuzpyT";
let client_secret = "lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj";
let hash_input = format!("{}{}\n", &time_str, "28c1fdd170a5204386cb1313c7077b34f83e4aaf4aa829ce78c231e05b0bae2c");
let hash = PixivAppClient::md5(hash_input.as_str());
let req = self.http_client
.post("https://oauth.secure.pixiv.net/auth/token")
.header(CONTENT_TYPE, "application/x-www-form-urlencoded")
.header(USER_AGENT, "PixivAndroidApp/5.0.115 (Android 6.0; PixivBot)")
.header(HeaderName::from_lowercase(b"x-client-time").unwrap(), &time_str)
.header(HeaderName::from_lowercase(b"x-client-hash").unwrap(), hash)
.body(format!("grant_type=refresh_token&client_id={}&refresh_token={}&client_secret={}&get_secure_url=1", client_id, cloned_refresh_token_str, client_secret))
.build()
.expect("failed to build login request");
if let Some(body) = req.body() {
if let Some(bytes) = body.as_bytes() {
println!("Body: {}", String::from_utf8_lossy(bytes));
} else {
println!("Body: Non-text data or stream");
}
}
let r = match self.http_client
.execute(req)
.await {
Ok(r) => r.text().await.unwrap(),
Err(_e) => return
};
let d: Value = serde_json::from_str(&r).unwrap();
assert!(!d["response"]["access_token"].is_null());
assert!(!d["response"]["refresh_token"].is_null());
self.access_token = Arc::new(Mutex::new(String::from(d["response"]["access_token"].as_str().unwrap())));
self.refresh_token = Arc::new(Mutex::new(String::from(d["response"]["refresh_token"].as_str().unwrap())));
}
}
#[cfg(test)]
mod client_tests {
use super::*;
use std::{assert, panic, env};
#[tokio::test]
async fn login() {
let token = env::var("PIXIV_REFRESH_TOKEN");
let mut client = PixivAppClient::new(&token.expect("expecting PIXIV_REFRESH_TOKEN variable for testing!"));
client.refresh_token().await;
let cloned_access_token = Arc::clone(&client.access_token);
match cloned_access_token.lock() {
Ok(t) => assert!(!t.is_empty()),
Err(_) => panic!("No token received!"),
};
}
}