Skip to main content

bpi_rs/video_ranking/
ranking.rs

1use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
2use serde::{ Deserialize, Serialize };
3
4// --- 获取分区视频排行榜列表 ---
5
6/// 排行榜列表中的单个视频
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct RankingVideoItem {
9    // 文档未提供具体字段,通常与视频详细信息相似
10    // 这里用 serde_json::Value 代替
11    #[serde(flatten)]
12    pub inner: serde_json::Value,
13}
14
15/// 排行榜列表数据
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct RankingListData {
18    /// 备注信息
19    pub note: String,
20    /// 视频列表
21    pub list: Vec<RankingVideoItem>,
22}
23
24impl BpiClient {
25    /// 获取分区视频排行榜列表
26    ///
27    /// # 文档
28    /// [查看API文档](https://socialsisteryi.github.io/bilibili-API-collect/docs/video_ranking/ranking.html#获取分区视频排行榜列表)
29    ///
30    /// # 参数
31    /// | 名称        | 类型           | 说明                 |
32    /// | ----------- | --------------| -------------------- |
33    /// | `rid`       | `Option<u32>`   | 目标分区 tid,默认0(全站) |
34    /// | `type_name` | `Option<&str>`  | 榜单类型 all/rookie/origin,可选 |
35    pub async fn video_ranking_list(
36        &self,
37        rid: Option<u32>,
38        type_name: Option<&str>
39    ) -> Result<BpiResponse<RankingListData>, BpiError> {
40        let mut request = self.get("https://api.bilibili.com/x/web-interface/ranking/v2");
41
42        if let Some(r) = rid {
43            request = request.query(&[("rid", r)]);
44        }
45        if let Some(t) = type_name {
46            request = request.query(&[("type", t)]);
47        }
48
49        // 添加 User-Agent 以通过鉴权
50        request = request.header(
51            "User-Agent",
52            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"
53        );
54
55        // WBI签名相关参数文档未给出完整说明,忽略
56        // request = request.query(&[("w_rid", "wbi_signature"), ("wts", "wbi_timestamp")]);
57
58        request.send_bpi("获取分区视频排行榜列表").await
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65    use tracing::info;
66
67    #[tokio::test]
68    async fn test_video_ranking_list() {
69        let bpi = BpiClient::new();
70        // 获取全站排行榜
71        let resp = bpi.video_ranking_list(None, None).await;
72
73        info!("{:?}", resp);
74        assert!(resp.is_ok());
75
76        let resp_data = resp.unwrap();
77        info!("code: {}", resp_data.code);
78        if let Some(data) = resp_data.data {
79            info!("note: {}", data.note);
80            info!("排行榜视频数: {}", data.list.len());
81            if let Some(first_item) = data.list.first() {
82                // 因为 RankingVideoItem 使用了 serde_json::Value,这里打印原始 JSON
83                info!("first item: {}", serde_json::to_string_pretty(&first_item).unwrap());
84            }
85        }
86    }
87
88    #[tokio::test]
89    async fn test_video_ranking_list_with_rid() {
90        let bpi = BpiClient::new();
91        // 获取日常分区排行榜 (rid=21)
92        let resp = bpi.video_ranking_list(Some(21), None).await;
93
94        info!("{:?}", resp);
95        assert!(resp.is_ok());
96
97        let resp_data = resp.unwrap();
98        info!("code: {}", resp_data.code);
99        if let Some(data) = resp_data.data {
100            info!("note: {}", data.note);
101            info!("排行榜视频数: {}", data.list.len());
102        }
103    }
104
105    #[tokio::test]
106    async fn test_video_ranking_list_with_type() {
107        let bpi = BpiClient::new();
108        // 获取新人排行榜
109        let resp = bpi.video_ranking_list(None, Some("rookie")).await;
110
111        info!("{:?}", resp);
112        assert!(resp.is_ok());
113
114        let resp_data = resp.unwrap();
115        info!("code: {}", resp_data.code);
116        if let Some(data) = resp_data.data {
117            info!("note: {}", data.note);
118            info!("排行榜视频数: {}", data.list.len());
119        }
120    }
121}