use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
use serde::{ Deserialize, Serialize };
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct InteractiveVideoInfoResponseData {
pub title: String,
pub edge_id: u64,
#[serde(default)]
pub story_list: Vec<InteractiveVideoStory>,
pub edges: Option<InteractiveVideoEdges>,
pub preload: Option<InteractiveVideoPreload>,
#[serde(default)]
pub hidden_vars: Vec<InteractiveVideoHiddenVar>,
pub is_leaf: u8,
#[serde(default)]
pub no_tutorial: u8,
#[serde(default)]
pub no_backtracking: u8,
#[serde(default)]
pub no_evaluation: u8,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct InteractiveVideoStory {
pub node_id: u64,
pub edge_id: u64,
pub title: String,
pub cid: u64,
pub start_pos: u64,
pub cover: String,
#[serde(default)]
pub is_current: u8,
pub cursor: u64,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct InteractiveVideoEdges {
pub dimension: Option<InteractiveVideoDimension>,
#[serde(default)]
pub questions: Vec<InteractiveVideoQuestion>,
pub skin: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct InteractiveVideoDimension {
pub width: u32,
pub height: u32,
pub rotate: u8,
pub sar: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct InteractiveVideoQuestion {
pub id: u64,
#[serde(rename = "type")]
pub question_type: u8,
pub start_time_r: u32,
pub duration: i64,
pub pause_video: u8,
pub title: String,
pub choices: Vec<InteractiveVideoChoice>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct InteractiveVideoChoice {
pub id: u64,
pub platform_action: String,
pub native_action: String,
pub condition: String,
pub cid: u64,
pub option: String,
#[serde(default)]
pub is_default: Option<u8>,
#[serde(default)]
pub is_hidden: Option<u8>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct InteractiveVideoPreload {
#[serde(default)]
pub video: Vec<InteractiveVideoPreloadVideo>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct InteractiveVideoPreloadVideo {
pub aid: u64,
pub cid: u64,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct InteractiveVideoHiddenVar {
pub value: i64,
pub id: String,
pub id_v2: String,
#[serde(rename = "type")]
pub var_type: u8,
pub is_show: u8,
pub name: String,
}
impl BpiClient {
pub async fn video_interactive_video_info(
&self,
aid: Option<u64>,
bvid: Option<&str>,
graph_version: u64,
edge_id: Option<u64>
) -> Result<BpiResponse<InteractiveVideoInfoResponseData>, BpiError> {
if aid.is_none() && bvid.is_none() {
return Err(BpiError::parse("必须提供 aid 或 bvid"));
}
let mut req = self
.get("https://api.bilibili.com/x/stein/edgeinfo_v2")
.query(&[("graph_version", &graph_version.to_string())]);
if let Some(a) = aid {
req = req.query(&[("aid", &a.to_string())]);
}
if let Some(b) = bvid {
req = req.query(&[("bvid", b)]);
}
if let Some(e) = edge_id {
req = req.query(&[("edge_id", &e.to_string())]);
}
req.send_bpi("获取互动视频模块详细信息").await
}
}
#[cfg(test)]
mod tests {
use super::*;
use tracing::info;
const TEST_AID: u64 = 114347430905959;
const TEST_GRAPH_VERSION: u64 = 1273647;
#[tokio::test]
async fn test_video_interactive_video_info_by_aid() -> Result<(), BpiError> {
let bpi = BpiClient::new();
let resp = bpi.video_interactive_video_info(
Some(TEST_AID),
None,
TEST_GRAPH_VERSION,
None
).await?;
let data = resp.into_data()?;
info!("互动视频信息: {:?}", data);
assert!(!data.title.is_empty());
assert!(!data.story_list.is_empty());
Ok(())
}
}