Skip to main content

bpi_rs/video/
report.rs

1// 视频观看进度上报相关接口
2//
3// [查看 API 文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/video)
4
5// --- 测试模块 ---
6
7use crate::BilibiliRequest;
8use crate::BpiError;
9use crate::response::BpiResult;
10use crate::video::VideoClient;
11
12const WATCH_PROGRESS_ENDPOINT: &str = "https://api.bilibili.com/x/v2/history/report";
13
14/// Parameters for reporting video watch progress.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub struct VideoWatchProgressParams {
17    aid: u64,
18    cid: u64,
19    progress: u64,
20}
21
22impl VideoWatchProgressParams {
23    pub fn new(aid: u64, cid: u64) -> BpiResult<Self> {
24        if aid == 0 {
25            return Err(BpiError::invalid_parameter("aid", "id must be non-zero"));
26        }
27        if cid == 0 {
28            return Err(BpiError::invalid_parameter("cid", "id must be non-zero"));
29        }
30
31        Ok(Self {
32            aid,
33            cid,
34            progress: 0,
35        })
36    }
37
38    pub fn progress(mut self, progress: u64) -> Self {
39        self.progress = progress;
40        self
41    }
42
43    fn into_multipart(self, csrf: &str) -> reqwest::multipart::Form {
44        reqwest::multipart::Form::new()
45            .text("aid", self.aid.to_string())
46            .text("cid", self.cid.to_string())
47            .text("csrf", csrf.to_string())
48            .text("progress", self.progress.to_string())
49    }
50}
51
52impl<'a> VideoClient<'a> {
53    /// Reports video watch progress and returns the canonical payload result.
54    pub async fn report_watch_progress(
55        &self,
56        params: VideoWatchProgressParams,
57    ) -> BpiResult<Option<serde_json::Value>> {
58        let csrf = self.client.csrf()?;
59        let form = params.into_multipart(&csrf);
60
61        self.client
62            .post(WATCH_PROGRESS_ENDPOINT)
63            .multipart(form)
64            .send_bpi_optional_payload("video.watch_progress.report")
65            .await
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn watch_progress_params_rejects_zero_aid() {
75        let err = VideoWatchProgressParams::new(0, 100).unwrap_err();
76
77        assert!(matches!(
78            err,
79            BpiError::InvalidParameter { field: "aid", .. }
80        ));
81    }
82}