use crate::models::Account;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Dimension {
pub width: u32,
pub height: u32,
pub rotate: u8,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Stat {
pub aid: u64,
pub view: u64,
pub danmaku: u64,
pub reply: u64,
#[serde(rename = "favorite")]
pub favorite: Option<u64>,
#[serde(rename = "fav")]
pub fav: Option<u64>,
pub coin: u64,
pub share: u64,
pub now_rank: i64,
pub his_rank: i64,
pub like: u64,
pub dislike: u64,
pub evaluation: String,
#[serde(rename = "argue_msg")]
pub argue_msg: Option<String>,
pub vt: u64,
pub vv: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ArgueInfo {
pub argue_link: String,
pub argue_msg: String,
pub argue_type: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Owner {
pub mid: u64,
pub name: String,
pub face: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Rights {
pub bp: u8,
pub elec: u8,
pub download: u8,
pub movie: u8,
pub pay: u8,
pub hd5: u8,
pub no_reprint: u8,
pub autoplay: u8,
pub ugc_pay: u8,
pub is_cooperation: u8,
pub ugc_pay_preview: u8,
pub no_background: Option<u8>,
pub clean_mode: Option<u8>,
pub is_stein_gate: Option<u8>,
pub arc_pay: u8,
pub free_watch: u8,
pub is_360: Option<u8>,
pub no_share: Option<u8>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Page {
pub cid: u64,
pub page: u32,
pub from: String,
pub part: String,
pub duration: u64,
pub vid: String,
pub weblink: String,
pub dimension: Dimension,
pub first_frame: Option<String>,
pub ctime: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubtitleListItem {
pub id: u64,
pub lan: String,
pub lan_doc: String,
pub is_lock: bool,
pub subtitle_url: String,
#[serde(rename = "type")]
pub sub_type: u8,
pub id_str: String,
pub ai_type: u8,
pub ai_status: u8,
pub author: Account,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Subtitle {
pub allow_submit: bool,
pub list: Vec<SubtitleListItem>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StaffVip {
#[serde(rename = "type")]
pub type_: u8,
pub status: u8,
pub due_date: u64,
pub vip_pay_type: u8,
pub theme_type: u8,
pub label: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StaffOfficial {
pub role: i32,
pub title: String,
pub desc: String,
#[serde(rename = "type")]
pub type_: i32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StaffItem {
pub mid: u64,
pub title: String,
pub name: String,
pub face: String,
pub vip: StaffVip,
pub official: StaffOfficial,
pub follower: u64,
pub label_style: u8,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HonorItem {
pub aid: u64,
pub hover_type: u8,
pub desc: String,
pub weekly_recommend_num: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HonorReply {
pub honor: Vec<HonorItem>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserGarb {
pub url_image_ani_cut: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EpisodeArc {
pub aid: u64,
pub videos: u32,
pub type_id: u32,
pub type_name: String,
pub copyright: u8,
pub pic: String,
pub title: String,
pub pubdate: u64,
pub ctime: u64,
pub desc: String,
pub state: u8,
pub duration: u64,
pub rights: Rights,
pub author: Owner,
pub stat: Stat,
pub dynamic: String,
pub dimension: Dimension,
pub desc_v2: serde_json::Value,
pub is_chargeable_season: bool,
pub is_blooper: bool,
pub enable_vt: u8,
pub vt_display: String,
pub type_id_v2: u32,
pub type_name_v2: String,
pub is_lesson_video: u8,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SectionEpisode {
pub season_id: u64,
pub section_id: u64,
pub id: u64,
pub aid: u64,
pub cid: u64,
pub title: String,
pub attribute: u32,
pub arc: EpisodeArc,
pub page: Page,
pub bvid: String,
pub pages: Vec<Page>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Section {
pub season_id: u64,
pub id: u64,
pub title: String,
#[serde(rename = "type")]
pub type_: u8,
pub episodes: Vec<SectionEpisode>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UgcSeasonStat {
pub season_id: u64,
pub view: u64,
pub danmaku: u64,
pub reply: u64,
pub fav: u64,
pub coin: u64,
pub share: u64,
pub now_rank: i64,
pub his_rank: i64,
pub like: u64,
pub vt: u64,
pub vv: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UgcSeason {
pub id: u64,
pub title: String,
pub cover: String,
pub mid: u64,
pub intro: String,
pub sign_state: u8,
pub attribute: u32,
pub sections: Vec<Section>,
pub stat: UgcSeasonStat,
pub ep_count: u32,
pub season_type: u8,
pub is_pay_season: bool,
pub enable_vt: u8,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VideoData {
pub aid: u64,
pub bvid: String,
pub videos: u32,
pub tid: u32,
pub tid_v2: u32,
pub tname: String,
pub tname_v2: String,
pub copyright: u8,
pub pic: String,
pub title: String,
pub pubdate: u64,
pub ctime: u64,
pub desc: String,
pub desc_v2: Option<Vec<serde_json::Value>>,
pub state: u32,
pub duration: u64, #[serde(default = "default_forward")] pub forward: u64,
pub mission_id: Option<u64>,
#[serde(default = "default_redirect_url")] pub redirect_url: String,
pub rights: Rights,
pub owner: Owner,
pub stat: Stat,
pub argue_info: ArgueInfo,
pub dynamic: String,
pub cid: u64,
pub dimension: Dimension,
pub premiere: serde_json::Value,
pub teenage_mode: u8,
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 no_cache: bool,
pub pages: Vec<Page>,
pub subtitle: Subtitle,
pub ugc_season: Option<UgcSeason>, #[serde(default = "default_staff")]
pub staff: Vec<StaffItem>, pub is_season_display: bool,
pub user_garb: UserGarb,
pub honor_reply: serde_json::Value,
pub like_icon: String,
pub need_jump_bv: bool,
pub disable_show_up_info: bool,
pub is_story_play: u32,
pub is_view_self: bool,
pub is_upower_exclusive_with_qa: bool,
}
fn default_forward() -> u64 {
0
}
fn default_redirect_url() -> String {
String::new()
}
fn default_staff() -> Vec<StaffItem> {
let v: Vec<StaffItem> = Vec::new();
v
}
#[cfg(test)]
mod tests {
use crate::ids::Aid;
use crate::video::params::VideoViewParams;
use crate::{BpiClient, BpiError};
#[ignore = "legacy live API test; requires explicit BPI_LIVE_TEST review"]
#[tokio::test]
async fn test_video_info() -> Result<(), BpiError> {
let bpi = BpiClient::new().expect("client should build");
let data = bpi
.video()
.view(VideoViewParams::from_aid(Aid::new(10001)?))
.await?;
assert!(!data.title.is_empty());
Ok(())
}
}