1use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
2use serde::{ Deserialize, Serialize };
3
4#[derive(Debug, Clone, Deserialize, Serialize)]
8pub struct FavFolderUpper {
9 pub mid: u64,
10 pub name: String,
11 pub face: String,
12 pub followed: bool,
13 pub vip_type: u8,
14 #[serde(rename = "vip_statue")]
16 pub vip_status: u8,
17}
18
19#[derive(Debug, Clone, Deserialize, Serialize)]
21pub struct FavFolderCntInfo {
22 pub collect: u64,
23 pub play: u64,
24 pub thumb_up: u64,
25 pub share: u64,
26}
27
28#[derive(Debug, Clone, Deserialize, Serialize)]
30pub struct FavFolderInfo {
31 pub id: u64,
32 pub fid: u64,
33 pub mid: u64,
34 pub attr: u32,
35 pub title: String,
36 pub cover: String,
37 pub upper: FavFolderUpper,
38 pub cover_type: u8,
39 pub cnt_info: FavFolderCntInfo,
40 #[serde(rename = "type")]
41 pub type_name: u32,
42 pub intro: String,
43 pub ctime: u64,
44 pub mtime: u64,
45 pub state: u8,
46 pub fav_state: u8,
47 pub like_state: u8,
48 pub media_count: u32,
49}
50
51#[derive(Debug, Clone, Deserialize, Serialize)]
55pub struct CreatedFolderItem {
56 pub id: u64,
57 pub fid: u64,
58 pub mid: u64,
59 pub attr: u32,
60 pub title: String,
61 pub fav_state: u8,
62 pub media_count: u32,
63}
64
65#[derive(Debug, Clone, Deserialize, Serialize)]
67pub struct CreatedFolderListData {
68 pub count: u32,
69 pub list: Vec<CreatedFolderItem>,
70}
71
72#[derive(Debug, Clone, Deserialize, Serialize)]
76pub struct CollectedFolderUpper {
77 pub mid: u64,
78 pub name: String,
79 pub face: String,
80}
81
82#[derive(Debug, Clone, Deserialize, Serialize)]
84pub struct CollectedFolderItem {
85 pub id: u64,
86 pub fid: u64,
87 pub mid: u64,
88 pub attr: u32,
89 pub title: String,
90 pub cover: String,
91 pub upper: CollectedFolderUpper,
92 pub cover_type: u8,
93 pub intro: String,
94 pub ctime: u64,
95 pub mtime: u64,
96 pub state: u8,
97 pub fav_state: u8,
98 pub media_count: u32,
99}
100
101#[derive(Debug, Clone, Deserialize, Serialize)]
103pub struct CollectedFolderListData {
104 pub count: u32,
105 pub list: Vec<CollectedFolderItem>,
106}
107
108#[derive(Debug, Clone, Deserialize, Serialize)]
112pub struct ResourceInfoUpper {
113 pub mid: u64,
114 pub name: String,
115 pub face: String,
116}
117
118#[derive(Debug, Clone, Deserialize, Serialize)]
120pub struct ResourceInfoCntInfo {
121 pub collect: u64,
122 pub play: u64,
123 pub danmaku: u64,
124}
125
126#[derive(Debug, Clone, Deserialize, Serialize)]
128pub struct ResourceInfoItem {
129 pub id: u64,
130 #[serde(rename = "type")]
131 pub type_name: u8,
132 pub title: String,
133 pub cover: String,
134 pub intro: String,
135 pub page: Option<u32>,
136 pub duration: u32,
137 pub upper: ResourceInfoUpper,
138 pub attr: u8,
139 pub cnt_info: ResourceInfoCntInfo,
140 pub link: String,
141 pub ctime: u64,
142 pub pubtime: u64,
143 pub fav_time: u64,
144 pub bv_id: Option<String>,
145 pub bvid: Option<String>,
146 pub season: Option<serde_json::Value>,
147}
148
149impl BpiClient {
150 pub async fn fav_folder_info(
161 &self,
162 media_id: u64
163 ) -> Result<BpiResponse<FavFolderInfo>, BpiError> {
164 self
165 .get("https://api.bilibili.com/x/v3/fav/folder/info")
166 .query(&[("media_id", media_id)])
167 .send_bpi("获取收藏夹元数据").await
168 }
169
170 pub async fn fav_created_list(
183 &self,
184 up_mid: u64,
185 typ: Option<u8>,
186 rid: Option<u64>
187 ) -> Result<BpiResponse<CreatedFolderListData>, BpiError> {
188 let mut request = self
189 .get("https://api.bilibili.com/x/v3/fav/folder/created/list-all")
190 .query(&[("up_mid", up_mid.to_string())]);
191
192 if let Some(t) = typ {
193 request = request.query(&[("type", t)]);
194 }
195 if let Some(r) = rid {
196 request = request.query(&[("rid", r)]);
197 }
198
199 request
200 .query(&[("web_location", "333.1387")])
201 .send_bpi("获取指定用户创建的所有收藏夹信息").await
202 }
203
204 pub async fn fav_collected_list(
217 &self,
218 up_mid: u64,
219 pn: u32,
220 ps: u32
221 ) -> Result<BpiResponse<CollectedFolderListData>, BpiError> {
222 self
223 .get("https://api.bilibili.com/x/v3/fav/folder/collected/list")
224 .query(
225 &[
226 ("up_mid", up_mid.to_string()),
227 ("pn", pn.to_string()),
228 ("ps", ps.to_string()),
229 ("platform", "web".to_string()),
230 ]
231 )
232 .send_bpi("查询用户收藏的视频收藏夹").await
233 }
234
235 pub async fn fav_resource_infos(
247 &self,
248 resources: &str
249 ) -> Result<BpiResponse<Vec<ResourceInfoItem>>, BpiError> {
250 self
251 .get("https://api.bilibili.com/x/v3/fav/resource/infos")
252 .query(
253 &[
254 ("resources", resources),
255 ("platform", "web"),
256 ]
257 )
258 .send_bpi("批量获取指定收藏id的内容").await
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use super::*;
265 use tracing::info;
266
267 #[tokio::test]
268 async fn test_get_fav_folder_info() {
269 let bpi = BpiClient::new();
270 let media_id = 3717139570;
272 let resp = bpi.fav_folder_info(media_id).await;
273
274 info!("{:?}", resp);
275 assert!(resp.is_ok());
276
277 let resp_data = resp.unwrap();
278 info!("code: {}", resp_data.code);
279 if let Some(data) = resp_data.data {
280 info!("folder title: {}", data.title);
281 info!("folder media_count: {}", data.media_count);
282 info!("upper info: {:?}", data.upper);
283 }
284 }
285
286 #[tokio::test]
287 async fn test_get_fav_created_list() {
288 let bpi = BpiClient::new();
289
290 let up_mid = 4279370;
291 let resp = bpi.fav_created_list(up_mid, None, None).await;
292
293 info!("{:?}", resp);
294 assert!(resp.is_ok());
295
296 let resp_data = resp.unwrap();
297 info!("code: {}", resp_data.code);
298 if let Some(data) = resp_data.data {
299 info!("created folders count: {}", data.count);
300 info!("first folder info: {:?}", data.list.first());
301 }
302 }
303
304 #[tokio::test]
305 async fn test_get_fav_collected_list() {
306 let bpi = BpiClient::new();
307
308 let up_mid = 4279370;
309 let resp = bpi.fav_collected_list(up_mid, 1, 20).await;
310
311 info!("{:?}", resp);
312 assert!(resp.is_ok());
313
314 let resp_data = resp.unwrap();
315 info!("code: {}", resp_data.code);
316 if let Some(data) = resp_data.data {
317 info!("collected folders count: {}", data.count);
318 info!("first collected folder info: {:?}", data.list.first());
319 }
320 }
321
322 #[tokio::test]
323 async fn test_get_fav_resource_infos() {
324 let bpi = BpiClient::new();
325 let resources = "115087859779103:2";
326 let resp = bpi.fav_resource_infos(resources).await;
327
328 info!("{:?}", resp);
329 assert!(resp.is_ok());
330
331 let resp_data = resp.unwrap();
332 info!("code: {}", resp_data.code);
333 if let Some(data) = resp_data.data {
334 info!("retrieved {} resources", data.len());
335 info!("first resource info: {:?}", data.first());
336 }
337 }
338}