feat: 后端支持获取普通视频信息

This commit is contained in:
lanyeeee
2025-07-13 05:09:30 +08:00
parent d0c0a86977
commit 2c6497daa4
8 changed files with 440 additions and 3 deletions
+51 -1
View File
@@ -12,7 +12,13 @@ use tauri::{
AppHandle,
};
use crate::types::{qrcode_data::QrcodeData, qrcode_status::QrcodeStatus, user_info::UserInfo};
use crate::{
extensions::AppHandleExt,
types::{
get_normal_info_params::GetNormalInfoParams, normal_info::NormalInfo,
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/";
@@ -132,6 +138,50 @@ impl BiliClient {
Ok(user_info)
}
pub async fn get_normal_info(&self, params: GetNormalInfoParams) -> anyhow::Result<NormalInfo> {
use GetNormalInfoParams::{Aid, Bvid};
let params = match params {
Bvid(bvid) => json!({"bvid": bvid}),
Aid(aid) => json!({"aid": aid}),
};
// 发送获取普通视频信息的请求
let request = self
.api_client
.read()
.get("https://api.bilibili.com/x/web-interface/view")
.query(&params)
.header("cookie", self.get_cookie());
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 != 0 {
return Err(anyhow!("预料之外的code: {bili_resp:?}"));
}
// 检查BiliResp的data是否存在
let Some(data) = bili_resp.data else {
return Err(anyhow!("BiliResp中不存在data字段: {bili_resp:?}"));
};
// 尝试将data解析为NormalInfo
let data_str = data.to_string();
let normal_info: NormalInfo = serde_json::from_str(&data_str)
.context(format!("将data解析为NormalInfo失败: {data_str}"))?;
Ok(normal_info)
}
fn get_cookie(&self) -> String {
let sessdata = self.app.get_config().read().sessdata.clone();
format!("SESSDATA={sessdata}")
}
}
fn create_api_client(_app: &AppHandle) -> ClientWithMiddleware {
+18 -1
View File
@@ -6,7 +6,10 @@ use crate::{
errors::{CommandError, CommandResult},
extensions::AppHandleExt,
logger,
types::{qrcode_data::QrcodeData, qrcode_status::QrcodeStatus, user_info::UserInfo},
types::{
get_normal_info_params::GetNormalInfoParams, normal_info::NormalInfo,
qrcode_data::QrcodeData, qrcode_status::QrcodeStatus, user_info::UserInfo,
},
};
#[tauri::command]
@@ -91,3 +94,17 @@ pub async fn get_user_info(app: AppHandle, sessdata: String) -> CommandResult<Us
.map_err(|err| CommandError::from("获取用户信息失败", err))?;
Ok(user_info)
}
#[tauri::command(async)]
#[specta::specta]
pub async fn get_normal_info(
app: AppHandle,
params: GetNormalInfoParams,
) -> CommandResult<NormalInfo> {
let bili_client = app.get_bili_client();
let normal_info = bili_client
.get_normal_info(params)
.await
.map_err(|err| CommandError::from("获取普通视频信息失败", err))?;
Ok(normal_info)
}
+2
View File
@@ -9,6 +9,7 @@ use tauri::{AppHandle, Manager};
pub struct Config {
pub download_dir: PathBuf,
pub enable_file_logger: bool,
pub sessdata: String,
}
impl Config {
@@ -66,6 +67,7 @@ impl Config {
Config {
download_dir: app_data_dir.join("视频下载"),
enable_file_logger: true,
sessdata: String::new(),
}
}
}
+1
View File
@@ -30,6 +30,7 @@ pub fn run() {
generate_qrcode,
get_qrcode_status,
get_user_info,
get_normal_info,
])
.events(tauri_specta::collect_events![LogEvent]);
@@ -0,0 +1,8 @@
use serde::{Deserialize, Serialize};
use specta::Type;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub enum GetNormalInfoParams {
Bvid(String),
Aid(i64),
}
+2
View File
@@ -1,4 +1,6 @@
pub mod get_normal_info_params;
pub mod log_level;
pub mod normal_info;
pub mod qrcode_data;
pub mod qrcode_status;
pub mod user_info;
+325
View File
@@ -0,0 +1,325 @@
use serde::{Deserialize, Serialize};
use specta::Type;
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
#[allow(clippy::struct_excessive_bools)]
pub struct NormalInfo {
pub bvid: String,
pub aid: i64,
pub videos: i64,
pub tid: i64,
pub tid_v2: i64,
pub tname: String,
pub tname_v2: String,
pub copyright: i64,
pub pic: String,
pub title: String,
pub pubdate: i64,
pub ctime: i64,
pub desc: String,
pub desc_v2: Option<Vec<DescV2>>,
pub state: i64,
pub duration: u64,
pub rights: Rights,
pub owner: OwnerInNormal,
pub stat: StatInNormal,
pub argue_info: ArgueInfo,
pub dynamic: String,
pub cid: i64,
pub dimension: Dimension,
pub teenage_mode: i64,
pub is_chargeable_season: bool,
pub is_story: bool,
pub is_upower_exclusive: bool,
pub is_upower_play: bool,
pub is_upower_preview: bool,
pub enable_vt: i64,
pub vt_display: String,
pub is_upower_exclusive_with_qa: bool,
pub no_cache: bool,
pub pages: Vec<PageInNormal>,
pub subtitle: SubtitleInNormal,
pub staff: Option<Vec<Staff>>,
pub ugc_season: Option<UgcSeason>,
pub is_season_display: bool,
pub user_garb: UserGarb,
pub honor_reply: HonorReply,
pub like_icon: String,
pub need_jump_bv: bool,
pub disable_show_up_info: bool,
pub is_story_play: i64,
pub is_view_self: bool,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct DescV2 {
pub raw_text: String,
#[serde(rename = "type")]
pub type_field: i64,
pub biz_id: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct Rights {
pub bp: i64,
pub elec: i64,
pub download: i64,
pub movie: i64,
pub pay: i64,
pub hd5: i64,
pub no_reprint: i64,
pub autoplay: i64,
pub ugc_pay: i64,
pub is_cooperation: i64,
pub ugc_pay_preview: i64,
pub no_background: i64,
pub clean_mode: i64,
pub is_stein_gate: i64,
pub is_360: i64,
pub no_share: i64,
pub arc_pay: i64,
pub free_watch: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct OwnerInNormal {
pub mid: i64,
pub name: String,
pub face: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct StatInNormal {
pub aid: i64,
pub view: i64,
pub danmaku: i64,
pub reply: i64,
pub favorite: i64,
pub coin: i64,
pub share: i64,
pub now_rank: i64,
pub his_rank: i64,
pub like: i64,
pub dislike: i64,
pub evaluation: String,
pub vt: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
#[allow(clippy::struct_field_names)]
pub struct ArgueInfo {
pub argue_msg: String,
pub argue_type: i64,
pub argue_link: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct Dimension {
pub width: i64,
pub height: i64,
pub rotate: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
#[allow(clippy::struct_field_names)]
pub struct PageInNormal {
pub cid: i64,
pub page: i64,
pub from: String,
pub part: String,
pub duration: u64,
pub vid: String,
pub weblink: String,
pub dimension: Dimension,
pub ctime: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct SubtitleInNormal {
pub allow_submit: bool,
pub list: Vec<SubtitleDetailInNormal>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct SubtitleDetailInNormal {
pub id: i64,
pub lan: String,
pub lan_doc: String,
pub is_lock: bool,
pub subtitle_url: String,
#[serde(rename = "type")]
pub type_field: i64,
pub id_str: String,
pub ai_type: i64,
pub ai_status: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct UserGarb {
pub url_image_ani_cut: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct HonorReply {
pub honor: Option<Vec<Honor>>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct Honor {
pub aid: i64,
#[serde(rename = "type")]
pub type_field: i64,
pub desc: String,
pub weekly_recommend_num: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct UgcSeason {
pub id: i64,
pub title: String,
pub cover: String,
pub mid: i64,
pub intro: String,
pub sign_state: i64,
pub attribute: i64,
pub sections: Vec<SectionInNormal>,
pub stat: StatInNormalSeason,
pub ep_count: i64,
pub season_type: i64,
pub is_pay_season: bool,
pub enable_vt: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct SectionInNormal {
pub season_id: i64,
pub id: i64,
pub title: String,
#[serde(rename = "type")]
pub type_field: i64,
pub episodes: Vec<EpInNormal>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct EpInNormal {
pub season_id: i64,
pub section_id: i64,
pub id: i64,
pub aid: i64,
pub cid: i64,
pub title: String,
pub attribute: i64,
pub arc: Arc,
pub page: PageInNormalEp,
pub bvid: String,
pub pages: Vec<PageInNormalEp>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct Arc {
pub aid: i64,
pub videos: i64,
pub type_id: i64,
pub type_name: String,
pub copyright: i64,
pub pic: String,
pub title: String,
pub pubdate: i64,
pub ctime: i64,
pub desc: String,
pub state: i64,
pub duration: u64,
pub rights: RightsInNormalEp,
pub author: Author,
pub stat: StatInNormalEp,
pub dynamic: String,
pub dimension: Dimension,
pub is_chargeable_season: bool,
pub is_blooper: bool,
pub enable_vt: i64,
pub vt_display: String,
pub type_id_v2: i64,
pub type_name_v2: String,
pub is_lesson_video: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct Author {
pub mid: i64,
pub name: String,
pub face: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct StatInNormalEp {
pub aid: i64,
pub view: i64,
pub danmaku: i64,
pub reply: i64,
pub fav: i64,
pub coin: i64,
pub share: i64,
pub now_rank: i64,
pub his_rank: i64,
pub like: i64,
pub dislike: i64,
pub evaluation: String,
pub argue_msg: String,
pub vt: i64,
pub vv: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct StatInNormalSeason {
pub season_id: i64,
pub view: i64,
pub danmaku: i64,
pub reply: i64,
pub fav: i64,
pub coin: i64,
pub share: i64,
pub now_rank: i64,
pub his_rank: i64,
pub like: i64,
pub vt: i64,
pub vv: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct PageInNormalEp {
pub cid: i64,
pub page: i64,
pub from: String,
pub part: String,
pub duration: u64,
pub vid: String,
pub weblink: String,
pub dimension: Dimension,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct RightsInNormalEp {
pub bp: i64,
pub elec: i64,
pub download: i64,
pub movie: i64,
pub pay: i64,
pub hd5: i64,
pub no_reprint: i64,
pub autoplay: i64,
pub ugc_pay: i64,
pub is_cooperation: i64,
pub ugc_pay_preview: i64,
pub arc_pay: i64,
pub free_watch: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct Staff {
pub mid: i64,
pub title: String,
pub name: String,
pub face: String,
pub follower: i64,
pub label_style: i64,
}