feat: 后端支持获取番剧视频信息

This commit is contained in:
lanyeeee
2025-07-14 06:14:53 +08:00
parent 2c6497daa4
commit 4115474f36
7 changed files with 460 additions and 2 deletions
+43
View File
@@ -15,6 +15,7 @@ use tauri::{
use crate::{
extensions::AppHandleExt,
types::{
bangumi_info::BangumiInfo, get_bangumi_info_params::GetBangumiInfoParams,
get_normal_info_params::GetNormalInfoParams, normal_info::NormalInfo,
qrcode_data::QrcodeData, qrcode_status::QrcodeStatus, user_info::UserInfo,
},
@@ -178,6 +179,48 @@ impl BiliClient {
Ok(normal_info)
}
pub async fn get_bangumi_info(
&self,
params: GetBangumiInfoParams,
) -> anyhow::Result<BangumiInfo> {
use GetBangumiInfoParams::{EpId, SeasonId};
let params = match params {
EpId(ep_id) => json!({"ep_id": ep_id}),
SeasonId(season_id) => json!({"season_id": season_id}),
};
// 发送获取番剧视频信息的请求
let request = self
.api_client
.read()
.get("https://api.bilibili.com/pgc/view/web/season")
.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解析为BangumiInfo
let data_str = data.to_string();
let bangumi_info: BangumiInfo = serde_json::from_str(&data_str)
.context(format!("将data解析为BangumiInfo失败: {data_str}"))?;
Ok(bangumi_info)
}
fn get_cookie(&self) -> String {
let sessdata = self.app.get_config().read().sessdata.clone();
format!("SESSDATA={sessdata}")
+15 -2
View File
@@ -7,8 +7,7 @@ use crate::{
extensions::AppHandleExt,
logger,
types::{
get_normal_info_params::GetNormalInfoParams, normal_info::NormalInfo,
qrcode_data::QrcodeData, qrcode_status::QrcodeStatus, user_info::UserInfo,
bangumi_info::BangumiInfo, get_bangumi_info_params::GetBangumiInfoParams, get_normal_info_params::GetNormalInfoParams, normal_info::NormalInfo, qrcode_data::QrcodeData, qrcode_status::QrcodeStatus, user_info::UserInfo
},
};
@@ -108,3 +107,17 @@ pub async fn get_normal_info(
.map_err(|err| CommandError::from("获取普通视频信息失败", err))?;
Ok(normal_info)
}
#[tauri::command(async)]
#[specta::specta]
pub async fn get_bangumi_info(
app: AppHandle,
params: GetBangumiInfoParams,
) -> CommandResult<BangumiInfo> {
let bili_client = app.get_bili_client();
let bangumi_info = bili_client
.get_bangumi_info(params)
.await
.map_err(|err| CommandError::from("获取番剧视频信息失败", err))?;
Ok(bangumi_info)
}
+1
View File
@@ -31,6 +31,7 @@ pub fn run() {
get_qrcode_status,
get_user_info,
get_normal_info,
get_bangumi_info,
])
.events(tauri_specta::collect_events![LogEvent]);
+354
View File
@@ -0,0 +1,354 @@
use anyhow::{anyhow, Context};
use serde::{Deserialize, Serialize};
use specta::Type;
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct BangumiInfo {
pub activity: Activity,
pub actors: String,
pub alias: String,
pub areas: Vec<Area>,
pub bkg_cover: String,
pub cover: String,
pub delivery_fragment_video: bool,
pub enable_vt: bool,
pub episodes: Vec<EpInBangumi>,
pub evaluate: String,
pub hide_ep_vv_vt_dm: i64,
pub icon_font: IconFont,
pub jp_title: String,
pub link: String,
pub media_id: i64,
pub mode: i64,
pub new_ep: NewEp,
pub payment: Option<PaymentInBangumi>,
pub play_strategy: Option<PlayStrategy>,
pub positive: Positive,
pub publish: Publish,
pub rating: Option<Rating>,
pub record: String,
pub rights: RightsInBangumi,
pub season_id: i64,
pub season_title: String,
pub seasons: Vec<Season>,
pub section: Option<Vec<SectionInBangumi>>,
pub series: Series,
pub share_copy: String,
pub share_sub_title: String,
pub share_url: String,
pub show: Show,
pub show_season_type: i64,
pub square_cover: String,
pub staff: String,
pub stat: StatInBangumi,
pub status: i64,
pub styles: Vec<String>,
pub subtitle: String,
pub title: String,
pub total: i64,
#[serde(rename = "type")]
pub type_field: i64,
pub up_info: Option<UpInfoInBangumi>,
pub user_status: UserStatusInBangumi,
}
impl BangumiInfo {
#[allow(clippy::cast_possible_wrap)]
pub fn get_episode_with_order(&self, ep_id: i64) -> anyhow::Result<(&EpInBangumi, i64)> {
let episode_with_order = self
.episodes
.iter()
.enumerate()
.map(|(i, ep)| (ep, i as i64 + 1))
.find(|(ep, _)| ep.id == ep_id);
let episode_with_order = if let Some(episode_with_order) = episode_with_order {
// 如果在正片中找到了对应的ep_id
episode_with_order
} else {
// 如果在正片中没有找到对应的ep_id,则在section中查找
let Some(sections) = &self.section else {
return Err(anyhow!("找不到对应的ep_id为`{ep_id}`的番剧"));
};
let section_index = sections
.iter()
.position(|s| s.episodes.iter().any(|e| e.id == ep_id))
.context(format!("找不到含有ep_id为`{ep_id}`的ep的section"))?;
sections[section_index]
.episodes
.iter()
.enumerate()
.map(|(i, e)| (e, i as i64 + 1))
.find(|(e, _)| e.id == ep_id)
.context(format!("在section中找不到ep_id为`{ep_id}`的ep"))?
};
Ok(episode_with_order)
}
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct Activity {
pub head_bg_url: String,
pub id: i64,
pub title: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct Area {
pub id: i64,
pub name: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
#[allow(clippy::struct_field_names)]
pub struct EpInBangumi {
pub aid: i64,
pub badge: String,
pub badge_info: BadgeInfo,
pub badge_type: Option<i64>,
pub bvid: Option<String>,
pub cid: i64,
pub cover: String,
pub dimension: Option<DimensionInBangumi>,
pub duration: Option<u64>,
pub enable_vt: bool,
pub ep_id: i64,
pub from: Option<String>,
pub id: i64,
pub is_view_hide: bool,
pub link: String,
pub link_type: Option<String>,
pub long_title: Option<String>,
pub pub_time: i64,
pub pv: i64,
pub release_date: Option<String>,
pub rights: Option<RightsInBangumiEp>,
pub section_type: i64,
pub share_copy: Option<String>,
pub share_url: Option<String>,
pub short_link: Option<String>,
#[serde(rename = "showDrmLoginDialog")]
pub show_drm_login_dialog: bool,
pub show_title: Option<String>,
pub skip: Option<Skip>,
pub status: i64,
pub subtitle: Option<String>,
pub title: String,
pub vid: Option<String>,
pub icon_font: Option<IconFont>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct BadgeInfo {
pub bg_color: String,
pub bg_color_night: String,
pub text: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct DimensionInBangumi {
pub height: i64,
pub rotate: i64,
pub width: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct RightsInBangumiEp {
pub allow_dm: i64,
pub allow_download: i64,
pub area_limit: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct Skip {
pub ed: Ed,
pub op: Op,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct Ed {
pub end: i64,
pub start: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct Op {
pub end: i64,
pub start: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct IconFont {
pub name: String,
pub text: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct NewEp {
pub desc: String,
pub id: i64,
pub is_new: i64,
pub title: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct PaymentInBangumi {
pub discount: i64,
pub pay_type: PayType,
pub price: String,
pub promotion: String,
pub tip: String,
pub view_start_time: i64,
pub vip_discount: i64,
pub vip_first_promotion: String,
pub vip_price: String,
pub vip_promotion: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct PayType {
pub allow_discount: i64,
pub allow_pack: i64,
pub allow_ticket: i64,
pub allow_time_limit: i64,
pub allow_vip_discount: i64,
pub forbid_bb: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct PlayStrategy {
pub strategies: Vec<String>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct Positive {
pub id: i64,
pub title: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct Publish {
pub is_finish: i64,
pub is_started: i64,
pub pub_time: String,
pub pub_time_show: String,
pub unknow_pub_date: i64,
pub weekday: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct Rating {
pub count: i64,
pub score: f64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct RightsInBangumi {
pub allow_bp: i64,
pub allow_bp_rank: i64,
pub allow_download: i64,
pub allow_review: i64,
pub area_limit: i64,
pub ban_area_show: i64,
pub can_watch: i64,
pub copyright: String,
pub forbid_pre: i64,
pub freya_white: i64,
pub is_cover_show: i64,
pub is_preview: i64,
pub only_vip_download: i64,
pub resource: String,
pub watch_platform: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
#[allow(clippy::struct_field_names)]
pub struct Season {
pub badge: String,
pub badge_info: BadgeInfo,
pub badge_type: i64,
pub cover: String,
pub enable_vt: bool,
pub horizontal_cover_1610: String,
pub horizontal_cover_169: String,
pub icon_font: IconFont,
pub media_id: i64,
pub new_ep: NewEpInSeason,
pub season_id: i64,
pub season_title: String,
pub season_type: i64,
pub stat: StatInSeason,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct NewEpInSeason {
pub cover: String,
pub id: i64,
pub index_show: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct StatInSeason {
pub favorites: i64,
pub series_follow: i64,
pub views: i64,
pub vt: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
#[allow(clippy::struct_field_names)]
pub struct Series {
pub display_type: i64,
pub series_id: i64,
pub series_title: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct Show {
pub wide_screen: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct StatInBangumi {
pub coins: i64,
pub danmakus: i64,
pub favorite: i64,
pub favorites: i64,
pub follow_text: String,
pub likes: i64,
pub reply: i64,
pub share: i64,
pub views: i64,
pub vt: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct UpInfoInBangumi {
pub avatar: String,
pub mid: i64,
pub uname: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct UserStatusInBangumi {
pub area_limit: i64,
pub ban_area_show: i64,
pub follow: i64,
pub follow_status: i64,
pub login: i64,
pub pay: i64,
pub pay_pack_paid: i64,
pub sponsor: i64,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub struct SectionInBangumi {
pub attr: i64,
pub episodes: Vec<EpInBangumi>,
pub id: i64,
pub title: String,
#[serde(rename = "type")]
pub type_field: i64,
pub type2: i64,
}
@@ -0,0 +1,8 @@
use serde::{Deserialize, Serialize};
use specta::Type;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Type)]
pub enum GetBangumiInfoParams {
EpId(i64),
SeasonId(i64),
}
+2
View File
@@ -1,3 +1,5 @@
pub mod bangumi_info;
pub mod get_bangumi_info_params;
pub mod get_normal_info_params;
pub mod log_level;
pub mod normal_info;