mirror of
https://github.com/lanyeeee/bilibili-video-downloader.git
synced 2026-09-05 23:57:23 +08:00
feat: 后端支持获取用户信息
This commit is contained in:
@@ -12,7 +12,7 @@ use tauri::{
|
||||
AppHandle,
|
||||
};
|
||||
|
||||
use crate::types::{qrcode_data::QrcodeData, qrcode_status::QrcodeStatus};
|
||||
use crate::types::{qrcode_data::QrcodeData, qrcode_status::QrcodeStatus, user_info::UserInfo};
|
||||
|
||||
const USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36";
|
||||
const REFERRER: &str = "https://www.bilibili.com/";
|
||||
@@ -97,6 +97,41 @@ impl BiliClient {
|
||||
}
|
||||
Ok(qrcode_status)
|
||||
}
|
||||
|
||||
pub async fn get_user_info(&self, sessdata: &str) -> anyhow::Result<UserInfo> {
|
||||
// 发送获取用户信息的请求
|
||||
let request = self
|
||||
.api_client
|
||||
.read()
|
||||
.get("https://api.bilibili.com/x/web-interface/nav")
|
||||
.header("cookie", format!("SESSDATA={sessdata}"));
|
||||
let http_resp = request.send().await?;
|
||||
// 检查http响应状态码
|
||||
let status = http_resp.status();
|
||||
let body = http_resp.text().await?;
|
||||
if status != StatusCode::OK {
|
||||
return Err(anyhow!("预料之外的状态码({status}): {body}"));
|
||||
}
|
||||
// 尝试将body解析为BiliResp
|
||||
let bili_resp: BiliResp =
|
||||
serde_json::from_str(&body).context(format!("将body解析为BiliResp失败: {body}"))?;
|
||||
// 检查BiliResp的code字段
|
||||
if bili_resp.code == -101 {
|
||||
return Err(anyhow!("cookie错误或已过期,请重新登录: {bili_resp:?}"));
|
||||
} else if bili_resp.code != 0 {
|
||||
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
|
||||
}
|
||||
// 检查BiliResp的data是否存在
|
||||
let Some(data) = bili_resp.data else {
|
||||
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
|
||||
};
|
||||
// 尝试将data解析为UserInfo
|
||||
let data_str = data.to_string();
|
||||
let user_info: UserInfo = serde_json::from_str(&data_str)
|
||||
.context(format!("将data解析为UserInfo失败: {data_str}"))?;
|
||||
|
||||
Ok(user_info)
|
||||
}
|
||||
}
|
||||
|
||||
fn create_api_client(_app: &AppHandle) -> ClientWithMiddleware {
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{
|
||||
errors::{CommandError, CommandResult},
|
||||
extensions::AppHandleExt,
|
||||
logger,
|
||||
types::{qrcode_data::QrcodeData, qrcode_status::QrcodeStatus},
|
||||
types::{qrcode_data::QrcodeData, qrcode_status::QrcodeStatus, user_info::UserInfo},
|
||||
};
|
||||
|
||||
#[tauri::command]
|
||||
@@ -80,3 +80,14 @@ pub async fn get_qrcode_status(app: AppHandle, qrcode_key: String) -> CommandRes
|
||||
.map_err(|err| CommandError::from("获取二维码状态", err))?;
|
||||
Ok(qrcode_status)
|
||||
}
|
||||
|
||||
#[tauri::command(async)]
|
||||
#[specta::specta]
|
||||
pub async fn get_user_info(app: AppHandle, sessdata: String) -> CommandResult<UserInfo> {
|
||||
let bili_client = app.get_bili_client();
|
||||
let user_info = bili_client
|
||||
.get_user_info(&sessdata)
|
||||
.await
|
||||
.map_err(|err| CommandError::from("获取用户信息失败", err))?;
|
||||
Ok(user_info)
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ pub fn run() {
|
||||
save_config,
|
||||
generate_qrcode,
|
||||
get_qrcode_status,
|
||||
get_user_info,
|
||||
])
|
||||
.events(tauri_specta::collect_events![LogEvent]);
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod log_level;
|
||||
pub mod qrcode_data;
|
||||
pub mod qrcode_status;
|
||||
pub mod user_info;
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specta::Type;
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct UserInfo {
|
||||
#[serde(rename = "isLogin")]
|
||||
pub is_login: bool,
|
||||
pub email_verified: i64,
|
||||
pub face: String,
|
||||
pub face_nft: i64,
|
||||
pub face_nft_type: i64,
|
||||
pub level_info: LevelInfoInUserInfo,
|
||||
pub mid: i64,
|
||||
pub mobile_verified: i64,
|
||||
pub money: f64,
|
||||
pub moral: i64,
|
||||
pub official: Official,
|
||||
#[serde(rename = "officialVerify")]
|
||||
pub official_verify: OfficialVerify,
|
||||
pub pendant: PendantInUserInfo,
|
||||
pub scores: i64,
|
||||
pub uname: String,
|
||||
#[serde(rename = "vipDueDate")]
|
||||
pub vip_due_date: i64,
|
||||
#[serde(rename = "vipStatus")]
|
||||
pub vip_status: i64,
|
||||
#[serde(rename = "vipType")]
|
||||
pub vip_type: i64,
|
||||
pub vip_pay_type: i64,
|
||||
pub vip_theme_type: i64,
|
||||
pub vip_label: VipLabel,
|
||||
pub vip_avatar_subscript: i64,
|
||||
pub vip_nickname_color: String,
|
||||
pub vip: VipInUserInfo,
|
||||
pub wallet: Option<Wallet>,
|
||||
pub has_shop: bool,
|
||||
pub shop_url: String,
|
||||
pub answer_status: i64,
|
||||
pub is_senior_member: i64,
|
||||
pub wbi_img: WbiImg,
|
||||
pub is_jury: bool,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
#[allow(clippy::struct_field_names)]
|
||||
pub struct LevelInfoInUserInfo {
|
||||
pub current_level: i64,
|
||||
pub current_min: i64,
|
||||
pub current_exp: i64,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct Official {
|
||||
pub role: i64,
|
||||
pub title: String,
|
||||
pub desc: String,
|
||||
#[serde(rename = "type")]
|
||||
pub type_field: i64,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct OfficialVerify {
|
||||
#[serde(rename = "type")]
|
||||
pub type_field: i64,
|
||||
pub desc: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct PendantInUserInfo {
|
||||
pub pid: i64,
|
||||
pub name: String,
|
||||
pub image: String,
|
||||
pub expire: i64,
|
||||
pub image_enhance: String,
|
||||
pub image_enhance_frame: String,
|
||||
pub n_pid: i64,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct VipLabel {
|
||||
pub path: String,
|
||||
pub text: String,
|
||||
pub label_theme: String,
|
||||
pub text_color: String,
|
||||
pub bg_style: i64,
|
||||
pub bg_color: String,
|
||||
pub border_color: String,
|
||||
pub use_img_label: bool,
|
||||
pub img_label_uri_hans: String,
|
||||
pub img_label_uri_hant: String,
|
||||
pub img_label_uri_hans_static: String,
|
||||
pub img_label_uri_hant_static: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
#[allow(clippy::struct_field_names)]
|
||||
pub struct VipInUserInfo {
|
||||
#[serde(rename = "type")]
|
||||
pub type_field: i64,
|
||||
pub status: i64,
|
||||
pub due_date: i64,
|
||||
pub vip_pay_type: i64,
|
||||
pub theme_type: i64,
|
||||
pub label: LabelInUserInfo,
|
||||
pub avatar_subscript: i64,
|
||||
pub nickname_color: String,
|
||||
pub role: i64,
|
||||
pub avatar_subscript_url: String,
|
||||
pub tv_vip_status: i64,
|
||||
pub tv_vip_pay_type: i64,
|
||||
pub tv_due_date: i64,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
#[allow(clippy::struct_field_names)]
|
||||
pub struct LabelInUserInfo {
|
||||
pub path: String,
|
||||
pub text: String,
|
||||
pub label_theme: String,
|
||||
pub text_color: String,
|
||||
pub bg_style: i64,
|
||||
pub bg_color: String,
|
||||
pub border_color: String,
|
||||
pub use_img_label: bool,
|
||||
pub img_label_uri_hans: String,
|
||||
pub img_label_uri_hant: String,
|
||||
pub img_label_uri_hans_static: String,
|
||||
pub img_label_uri_hant_static: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct IconResource {}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct Wallet {
|
||||
pub mid: i64,
|
||||
pub bcoin_balance: f64,
|
||||
pub coupon_balance: f64,
|
||||
pub coupon_due_time: i64,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
|
||||
pub struct WbiImg {
|
||||
pub img_url: String,
|
||||
pub sub_url: String,
|
||||
}
|
||||
@@ -34,6 +34,14 @@ async getQrcodeStatus(qrcodeKey: string) : Promise<Result<QrcodeStatus, CommandE
|
||||
if(e instanceof Error) throw e;
|
||||
else return { status: "error", error: e as any };
|
||||
}
|
||||
},
|
||||
async getUserInfo(sessdata: string) : Promise<Result<UserInfo, CommandError>> {
|
||||
try {
|
||||
return { status: "ok", data: await TAURI_INVOKE("get_user_info", { sessdata }) };
|
||||
} catch (e) {
|
||||
if(e instanceof Error) throw e;
|
||||
else return { status: "error", error: e as any };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,10 +63,20 @@ logEvent: "log-event"
|
||||
export type CommandError = { err_title: string; err_message: string }
|
||||
export type Config = { downloadDir: string; enableFileLogger: boolean }
|
||||
export type JsonValue = null | boolean | number | string | JsonValue[] | { [key in string]: JsonValue }
|
||||
export type LabelInUserInfo = { path: string; text: string; label_theme: string; text_color: string; bg_style: number; bg_color: string; border_color: string; use_img_label: boolean; img_label_uri_hans: string; img_label_uri_hant: string; img_label_uri_hans_static: string; img_label_uri_hant_static: string }
|
||||
export type LevelInfoInUserInfo = { current_level: number; current_min: number; current_exp: number }
|
||||
export type LogEvent = { timestamp: string; level: LogLevel; fields: { [key in string]: JsonValue }; target: string; filename: string; line_number: number }
|
||||
export type LogLevel = "TRACE" | "DEBUG" | "INFO" | "WARN" | "ERROR"
|
||||
export type Official = { role: number; title: string; desc: string; type: number }
|
||||
export type OfficialVerify = { type: number; desc: string }
|
||||
export type PendantInUserInfo = { pid: number; name: string; image: string; expire: number; image_enhance: string; image_enhance_frame: string; n_pid: number }
|
||||
export type QrcodeData = { url: string; qrcode_key: string }
|
||||
export type QrcodeStatus = { url: string; refresh_token: string; timestamp: number; code: number; message: string }
|
||||
export type UserInfo = { isLogin: boolean; email_verified: number; face: string; face_nft: number; face_nft_type: number; level_info: LevelInfoInUserInfo; mid: number; mobile_verified: number; money: number; moral: number; official: Official; officialVerify: OfficialVerify; pendant: PendantInUserInfo; scores: number; uname: string; vipDueDate: number; vipStatus: number; vipType: number; vip_pay_type: number; vip_theme_type: number; vip_label: VipLabel; vip_avatar_subscript: number; vip_nickname_color: string; vip: VipInUserInfo; wallet: Wallet | null; has_shop: boolean; shop_url: string; answer_status: number; is_senior_member: number; wbi_img: WbiImg; is_jury: boolean }
|
||||
export type VipInUserInfo = { type: number; status: number; due_date: number; vip_pay_type: number; theme_type: number; label: LabelInUserInfo; avatar_subscript: number; nickname_color: string; role: number; avatar_subscript_url: string; tv_vip_status: number; tv_vip_pay_type: number; tv_due_date: number }
|
||||
export type VipLabel = { path: string; text: string; label_theme: string; text_color: string; bg_style: number; bg_color: string; border_color: string; use_img_label: boolean; img_label_uri_hans: string; img_label_uri_hant: string; img_label_uri_hans_static: string; img_label_uri_hant_static: string }
|
||||
export type Wallet = { mid: number; bcoin_balance: number; coupon_balance: number; coupon_due_time: number }
|
||||
export type WbiImg = { img_url: string; sub_url: string }
|
||||
|
||||
/** tauri-specta globals **/
|
||||
|
||||
|
||||
Reference in New Issue
Block a user