Skip to main content

bpi_rs/video/
summary.rs

1//! 视频 AI 总结相关接口
2//!
3//! [查看 API 文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/video)
4use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
5use serde::{ Deserialize, Serialize };
6
7// --- 响应数据结构体 ---
8
9/// AI 总结提纲分段要点
10#[derive(Debug, Clone, Deserialize, Serialize)]
11pub struct AiSummaryPartOutline {
12    /// 要点起始时间,单位为秒
13    pub timestamp: u64,
14    /// 小结内容
15    pub content: String,
16}
17
18/// AI 总结提纲
19#[derive(Debug, Clone, Deserialize, Serialize)]
20pub struct AiSummaryOutline {
21    /// 分段标题
22    pub title: String,
23    /// 分段要点
24    pub part_outline: Vec<AiSummaryPartOutline>,
25    /// 分段起始时间,单位为秒
26    pub timestamp: u64,
27}
28
29/// AI 总结摘要内容
30#[derive(Debug, Clone, Deserialize, Serialize)]
31pub struct AiSummaryModelResult {
32    /// 数据类型, 0: 没有摘要, 1: 仅有摘要总结, 2: 有摘要及提纲
33    pub result_type: u8,
34    /// 视频摘要
35    pub summary: String,
36    /// 分段提纲
37    pub outline: Option<Vec<AiSummaryOutline>>,
38}
39
40/// 视频 AI 总结响应数据
41#[derive(Debug, Clone, Deserialize, Serialize)]
42pub struct AiSummaryResponseData {
43    /// 返回值, -1: 不支持 AI 摘要, 0: 有摘要, 1: 无摘要
44    pub code: i8,
45    /// 摘要内容
46    pub model_result: Option<AiSummaryModelResult>,
47    /// 摘要 id
48    pub stid: Option<String>,
49    pub status: Option<u8>,
50    /// 点赞数
51    pub like_num: u64,
52    /// 点踩数
53    pub dislike_num: u64,
54}
55
56impl BpiClient {
57    /// 获取视频 AI 总结内容
58    ///
59    /// # 文档
60    /// [查看API文档](https://socialsisteryi.github.io/bilibili-API-collect/docs/video/summary.html#获取视频ai总结)
61    ///
62    /// # 参数
63    /// | 名称     | 类型         | 说明                 |
64    /// | -------- | ------------| -------------------- |
65    /// | `aid`    | `Option<u64>` | 稿件 avid,可选      |
66    /// | `bvid`   | `Option<&str>`| 稿件 bvid,可选      |
67    /// | `cid`    | u64         | 视频 cid             |
68    /// | `up_mid` | u64         | UP主 mid             |
69    ///
70    /// `aid` 和 `bvid` 必须提供一个。
71    pub async fn video_ai_summary(
72        &self,
73        aid: Option<u64>,
74        bvid: Option<&str>,
75        cid: u64,
76        up_mid: u64
77    ) -> Result<BpiResponse<AiSummaryResponseData>, BpiError> {
78        if aid.is_none() && bvid.is_none() {
79            return Err(BpiError::parse("必须提供 aid 或 bvid"));
80        }
81
82        let mut params = vec![("cid", cid.to_string()), ("up_mid", up_mid.to_string())];
83
84        if let Some(a) = aid {
85            params.push(("aid", a.to_string()));
86        }
87
88        if let Some(b) = bvid {
89            params.push(("bvid", b.to_string()));
90        }
91
92        let wbi_params = self.get_wbi_sign2(params).await?;
93
94        let req = self
95            .get("https://api.bilibili.com/x/web-interface/view/conclusion/get")
96            .query(&wbi_params);
97
98        req.send_bpi("获取视频 AI 总结内容").await
99    }
100}
101
102// --- 测试模块 ---
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107    use tracing::info;
108
109    const TEST_AID: u64 = 10001;
110
111    const TEST_CID: u64 = 16546;
112    const TEST_UP_MID: u64 = 34893;
113
114    #[tokio::test]
115    async fn test_video_ai_summary_by_aid() -> Result<(), BpiError> {
116        let bpi = BpiClient::new();
117        let resp = bpi.video_ai_summary(Some(TEST_AID), None, TEST_CID, TEST_UP_MID).await?;
118        let data = resp.into_data()?;
119
120        info!("视频 AI 总结: {:?}", data);
121
122        if data.code == 0 {
123            assert!(data.model_result.is_some());
124        }
125
126        Ok(())
127    }
128}