bpi_rs/video/
summary.rs

1//! 视频 AI 总结相关接口
2//!
3//! 文档: 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    /// 文档: https://socialsisteryi.github.io/bilibili-API-collect/docs/video/summary.html#获取视频ai总结
60    ///
61    /// # 参数
62    /// | 名称     | 类型         | 说明                 |
63    /// | -------- | ------------| -------------------- |
64    /// | `aid`    | Option<u64> | 稿件 avid,可选      |
65    /// | `bvid`   | Option<&str>| 稿件 bvid,可选      |
66    /// | `cid`    | u64         | 视频 cid             |
67    /// | `up_mid` | u64         | UP主 mid             |
68    ///
69    /// `aid` 和 `bvid` 必须提供一个。
70    pub async fn video_ai_summary(
71        &self,
72        aid: Option<u64>,
73        bvid: Option<&str>,
74        cid: u64,
75        up_mid: u64,
76    ) -> Result<BpiResponse<AiSummaryResponseData>, BpiError> {
77        if aid.is_none() && bvid.is_none() {
78            return Err(BpiError::parse("必须提供 aid 或 bvid"));
79        }
80
81        let mut params = vec![("cid", cid.to_string()), ("up_mid", up_mid.to_string())];
82
83        if let Some(a) = aid {
84            params.push(("aid", a.to_string()));
85        }
86
87        if let Some(b) = bvid {
88            params.push(("bvid", b.to_string()));
89        }
90
91        let wbi_params = self.get_wbi_sign2(params).await?;
92
93        let req = self
94            .get("https://api.bilibili.com/x/web-interface/view/conclusion/get")
95            .query(&wbi_params);
96
97        req.send_bpi("获取视频 AI 总结内容").await
98    }
99}
100
101// --- 测试模块 ---
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106    use tracing::info;
107
108    const TEST_AID: u64 = 10001;
109
110    const TEST_CID: u64 = 16546;
111    const TEST_UP_MID: u64 = 34893;
112
113    #[tokio::test]
114
115    async fn test_video_ai_summary_by_aid() -> Result<(), BpiError> {
116        let bpi = BpiClient::new();
117        let resp = bpi
118            .video_ai_summary(Some(TEST_AID), None, TEST_CID, TEST_UP_MID)
119            .await?;
120        let data = resp.into_data()?;
121
122        info!("视频 AI 总结: {:?}", data);
123
124        if data.code == 0 {
125            assert!(data.model_result.is_some());
126        }
127
128        Ok(())
129    }
130}