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(
160 &self,
161 media_id: u64,
162 ) -> Result<BpiResponse<FavFolderInfo>, BpiError> {
163 self.get("https://api.bilibili.com/x/v3/fav/folder/info")
164 .query(&[("media_id", media_id)])
165 .send_bpi("获取收藏夹元数据")
166 .await
167 }
168
169 pub async fn fav_created_list(
181 &self,
182 up_mid: u64,
183 typ: Option<u8>,
184 rid: Option<u64>,
185 ) -> Result<BpiResponse<CreatedFolderListData>, BpiError> {
186 let mut request = self
187 .get("https://api.bilibili.com/x/v3/fav/folder/created/list-all")
188 .query(&[("up_mid", up_mid.to_string())]);
189
190 if let Some(t) = typ {
191 request = request.query(&[("type", t)]);
192 }
193 if let Some(r) = rid {
194 request = request.query(&[("rid", r)]);
195 }
196
197 request
198 .query(&[("web_location", "333.1387")])
199 .send_bpi("获取指定用户创建的所有收藏夹信息")
200 .await
201 }
202
203 pub async fn fav_collected_list(
215 &self,
216 up_mid: u64,
217 pn: u32,
218 ps: u32,
219 ) -> Result<BpiResponse<CollectedFolderListData>, BpiError> {
220 self.get("https://api.bilibili.com/x/v3/fav/folder/collected/list")
221 .query(&[
222 ("up_mid", up_mid.to_string()),
223 ("pn", pn.to_string()),
224 ("ps", ps.to_string()),
225 ("platform", "web".to_string()),
226 ])
227 .send_bpi("查询用户收藏的视频收藏夹")
228 .await
229 }
230
231 pub async fn fav_resource_infos(
242 &self,
243 resources: &str,
244 ) -> Result<BpiResponse<Vec<ResourceInfoItem>>, BpiError> {
245 self.get("https://api.bilibili.com/x/v3/fav/resource/infos")
246 .query(&[("resources", resources), ("platform", "web")])
247 .send_bpi("批量获取指定收藏id的内容")
248 .await
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255 use tracing::info;
256
257 #[tokio::test]
258 async fn test_get_fav_folder_info() {
259 let bpi = BpiClient::new();
260 let media_id = 3717139570;
262 let resp = bpi.fav_folder_info(media_id).await;
263
264 info!("{:?}", resp);
265 assert!(resp.is_ok());
266
267 let resp_data = resp.unwrap();
268 info!("code: {}", resp_data.code);
269 if let Some(data) = resp_data.data {
270 info!("folder title: {}", data.title);
271 info!("folder media_count: {}", data.media_count);
272 info!("upper info: {:?}", data.upper);
273 }
274 }
275
276 #[tokio::test]
277 async fn test_get_fav_created_list() {
278 let bpi = BpiClient::new();
279
280 let up_mid = 4279370;
281 let resp = bpi.fav_created_list(up_mid, None, None).await;
282
283 info!("{:?}", resp);
284 assert!(resp.is_ok());
285
286 let resp_data = resp.unwrap();
287 info!("code: {}", resp_data.code);
288 if let Some(data) = resp_data.data {
289 info!("created folders count: {}", data.count);
290 info!("first folder info: {:?}", data.list.first());
291 }
292 }
293
294 #[tokio::test]
295 async fn test_get_fav_collected_list() {
296 let bpi = BpiClient::new();
297
298 let up_mid = 4279370;
299 let resp = bpi.fav_collected_list(up_mid, 1, 20).await;
300
301 info!("{:?}", resp);
302 assert!(resp.is_ok());
303
304 let resp_data = resp.unwrap();
305 info!("code: {}", resp_data.code);
306 if let Some(data) = resp_data.data {
307 info!("collected folders count: {}", data.count);
308 info!("first collected folder info: {:?}", data.list.first());
309 }
310 }
311
312 #[tokio::test]
313 async fn test_get_fav_resource_infos() {
314 let bpi = BpiClient::new();
315 let resources = "115087859779103:2";
316 let resp = bpi.fav_resource_infos(resources).await;
317
318 info!("{:?}", resp);
319 assert!(resp.is_ok());
320
321 let resp_data = resp.unwrap();
322 info!("code: {}", resp_data.code);
323 if let Some(data) = resp_data.data {
324 info!("retrieved {} resources", data.len());
325 info!("first resource info: {:?}", data.first());
326 }
327 }
328}