use std::{path::Path, sync::Arc, time::Duration};
use anyhow::{anyhow, bail, Result};
use biliapi::Request;
use cookie_store::CookieStore;
use log::*;
use qrcode::{render::unicode, QrCode};
use reqwest::Client;
use reqwest_cookie_store::CookieStoreMutex;
use tokio::{fs, io::AsyncWriteExt, time::sleep};
async fn load_persisted_cookie_store_from_file(path: impl AsRef<Path>) -> Result<CookieStore> {
let cookies = fs::read_to_string(path.as_ref()).await?;
CookieStore::load_json(cookies.as_bytes()).map_err(|e| anyhow!(e))
}
async fn persisted_client(
cookie_json: impl AsRef<Path>,
) -> Result<(Client, Arc<CookieStoreMutex>)> {
let cookies = load_persisted_cookie_store_from_file(cookie_json)
.await
.unwrap_or_else(|e| {
warn!("failed to load cookies: {:?}", e);
CookieStore::default()
});
let cookies = Arc::new(CookieStoreMutex::new(cookies));
let client = reqwest::ClientBuilder::new()
.user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36")
.cookie_provider(Arc::clone(&cookies))
.build()?;
Ok((client, cookies))
}
async fn login(client: &Client) -> Result<()> {
let request = biliapi::requests::QrLoginRequest::request(client, ()).await?;
let code = QrCode::new(request.url.as_bytes())?;
let string = code
.render::<unicode::Dense1x2>()
.dark_color(unicode::Dense1x2::Light)
.light_color(unicode::Dense1x2::Dark)
.build();
info!("请使用哔哩哔哩手机客户端扫描下面的二维码以登录");
println!("{}", string);
sleep(Duration::from_secs(5)).await;
let mut retry: i32 = 0;
loop {
sleep(Duration::from_secs(3)).await;
let resp =
biliapi::requests::CheckQrLogin::request(client, request.oauth_key.clone()).await?;
match resp.is_success() {
Some(true) => {
info!("登录成功!");
break;
}
Some(false) => {
bail!("二维码登录失败");
}
None => {
if retry > 10 {
bail!("二维码登录超时了");
}
}
}
retry += 1;
}
Ok(())
}
async fn save_cookies(cookies: Arc<CookieStoreMutex>, path: impl AsRef<Path>) -> Result<()> {
let mut buffer = &mut vec![];
{
let c = cookies.lock().unwrap();
c.save_json(&mut buffer).map_err(|e| anyhow!(e))?;
}
let mut f = tokio::fs::File::create(path.as_ref()).await?;
f.write_all(buffer).await?;
Ok(())
}
#[tokio::main]
async fn main() -> Result<()> {
dotenv::dotenv()?;
pretty_env_logger::init();
let (client, cookies) = persisted_client("./persisted_cookies.json").await?;
match biliapi::requests::MyAccountInfo::request(&client, ()).await {
Ok(data) => {
info!("my account info: {:?}", data);
}
Err(e) => {
warn!("not login: {:?}", e);
info!("login now");
login(&client).await?;
}
}
let bird_info = biliapi::requests::UploaderStat::request(&client, 282994).await?;
info!("获取信息成功:{:?}", bird_info);
save_cookies(cookies, "./persisted_cookies.json").await?;
Ok(())
}