bpi_rs/fav/
list.rs

1use crate::{BilibiliRequest, BpiClient, BpiError, BpiResponse};
2use serde::{Deserialize, Serialize};
3
4// --- 获取收藏夹内容明细列表 ---
5
6/// 收藏夹内容明细列表中的 UP 主信息
7#[derive(Debug, Clone, Deserialize, Serialize)]
8pub struct FavListUpper {
9    pub mid: u64,
10    pub name: String,
11    pub face: String,
12    pub followed: Option<bool>,
13    pub vip_type: Option<u8>,
14    pub vip_status: Option<u8>,
15}
16
17/// 收藏夹内容明细列表中的状态数
18#[derive(Debug, Clone, Deserialize, Serialize)]
19pub struct FavListCntInfo {
20    /// 收藏
21    pub collect: u64,
22    /// 播放
23    pub play: u64,
24
25    /// 分享 (仅info)
26    pub share: Option<u64>,
27    /// 点赞 (仅info)
28    pub thumb_up: Option<u64>,
29    ///弹幕 (仅media)
30    pub danmaku: Option<u64>,
31    /// 播放文本 (仅media)
32    pub view_text_1: Option<String>,
33}
34
35/// 收藏夹元数据
36#[derive(Debug, Clone, Deserialize, Serialize)]
37pub struct FavListInfo {
38    pub id: u64,
39    pub fid: u64,
40    pub mid: u64,
41    pub attr: u32,
42    pub title: String,
43    pub cover: String,
44    pub upper: FavListUpper,
45    pub cover_type: u8,
46    pub cnt_info: FavListCntInfo,
47    #[serde(rename = "type")]
48    pub type_name: u32,
49    pub intro: String,
50    pub ctime: u64,
51    pub mtime: u64,
52    pub state: u8,
53    pub fav_state: u8,
54    pub like_state: u8,
55    pub media_count: u32,
56}
57
58/// 收藏夹中的单个内容
59#[derive(Debug, Clone, Deserialize, Serialize)]
60pub struct FavListMedia {
61    pub id: u64,
62    #[serde(rename = "type")]
63    pub type_name: u8,
64    pub title: String,
65    pub cover: String,
66    pub intro: String,
67    pub page: Option<u32>,
68    pub duration: u32,
69    pub upper: FavListUpper,
70    pub attr: u8,
71    pub cnt_info: FavListCntInfo,
72    pub link: String,
73    pub ctime: u64,
74    pub pubtime: u64,
75    pub fav_time: u64,
76    pub bv_id: Option<String>,
77    pub bvid: Option<String>,
78    pub season: Option<serde_json::Value>,
79}
80
81/// 收藏夹内容明细列表数据
82#[derive(Debug, Clone, Deserialize, Serialize)]
83pub struct FavListDetailData {
84    pub info: FavListInfo,
85    pub medias: Vec<FavListMedia>,
86    pub has_more: bool,
87    pub ttl: u64,
88}
89
90// --- 获取收藏夹全部内容id ---
91
92/// 收藏夹全部内容ID列表中的单个ID
93#[derive(Debug, Clone, Deserialize, Serialize)]
94pub struct FavResourceIdItem {
95    pub id: u64,
96    #[serde(rename = "type")]
97    pub type_name: u8,
98    pub bv_id: Option<String>,
99    pub bvid: Option<String>,
100}
101
102impl BpiClient {
103    /// 获取收藏夹内容明细列表
104    ///
105    /// 文档: https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/fav
106    ///
107    /// 参数
108    ///
109    /// | 名称 | 类型 | 说明 |
110    /// | ---- | ---- | ---- |
111    /// | `media_id` | u64 | 收藏夹 media_id |
112    /// | `tid` | Option<u32> | 分区 tid |
113    /// | `keyword` | Option<&str> | 关键词过滤 |
114    /// | `order` | Option<&str> | 排序,如 `mtime` |
115    /// | `typ` | Option<u8> | 内容类型 |
116    /// | `ps` | u32 | 每页条数 |
117    /// | `pn` | Option<u32> | 页码 |
118    pub async fn fav_list_detail(
119        &self,
120        media_id: u64,
121        tid: Option<u32>,
122        keyword: Option<&str>,
123        order: Option<&str>,
124        typ: Option<u8>,
125        ps: u32,
126        pn: Option<u32>,
127    ) -> Result<BpiResponse<FavListDetailData>, BpiError> {
128        let mut request = self
129            .get("https://api.bilibili.com/x/v3/fav/resource/list")
130            .query(&[
131                ("media_id", media_id.to_string()),
132                ("ps", ps.to_string()),
133                ("platform", "web".to_string()),
134            ]);
135
136        if let Some(tid) = tid {
137            request = request.query(&[("tid", tid)]);
138        }
139        if let Some(keyword) = keyword {
140            request = request.query(&[("keyword", keyword)]);
141        }
142        if let Some(order) = order {
143            request = request.query(&[("order", order)]);
144        }
145        if let Some(typ) = typ {
146            request = request.query(&[("type", typ)]);
147        }
148        if let Some(pn) = pn {
149            request = request.query(&[("pn", pn)]);
150        }
151
152        request.send_bpi("获取收藏夹内容明细列表").await
153    }
154
155    /// 获取收藏夹全部内容id
156    ///
157    /// 文档: https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/fav
158    ///
159    /// 参数
160    ///
161    /// | 名称 | 类型 | 说明 |
162    /// | ---- | ---- | ---- |
163    /// | `media_id` | u64 | 收藏夹 media_id |
164    pub async fn fav_resource_ids(
165        &self,
166        media_id: u64,
167    ) -> Result<BpiResponse<Vec<FavResourceIdItem>>, BpiError> {
168        self.get("https://api.bilibili.com/x/v3/fav/resource/ids")
169            .query(&[
170                ("media_id", media_id.to_string()),
171                ("platform", "web".to_string()),
172            ])
173            .send_bpi("获取收藏夹全部内容id")
174            .await
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181    use tracing::info;
182
183    #[tokio::test]
184    async fn test_get_fav_list_detail() {
185        let bpi = BpiClient::new();
186        let media_id = 1572769770;
187        let resp = bpi
188            .fav_list_detail(media_id, None, None, Some("mtime"), Some(0), 5, Some(1))
189            .await;
190
191        info!("{:?}", resp);
192        assert!(resp.is_ok());
193
194        let resp_data = resp.unwrap();
195        info!("code: {}", resp_data.code);
196        if let Some(data) = resp_data.data {
197            info!("has_more: {}", data.has_more);
198            info!("total media count: {}", data.info.media_count);
199            info!("retrieved media count: {}", data.medias.len());
200            info!("first media item: {:?}", data.medias.first());
201        }
202    }
203
204    #[tokio::test]
205    async fn test_get_fav_resource_ids() {
206        let bpi = BpiClient::new();
207        let media_id = 1572769770;
208        let resp = bpi.fav_resource_ids(media_id).await;
209
210        info!("{:?}", resp);
211        assert!(resp.is_ok());
212
213        let resp_data = resp.unwrap();
214        info!("code: {}", resp_data.code);
215        if let Some(data) = resp_data.data {
216            info!("total IDs retrieved: {}", data.len());
217            info!("first ID item: {:?}", data.first());
218        }
219    }
220}