1use super::result::{
6 Article, Bangumi, BiliUser, LiveData, LiveRoom, LiveUser, Movie, SearchData, Video,
7};
8use super::search_params::{CategoryId, Duration, OrderSort, SearchOrder, SearchType, UserType};
9use crate::{BilibiliRequest, BpiClient, BpiError, BpiResponse};
10
11impl BpiClient {
12 pub async fn search_article(
25 &self,
26 keyword: &str,
27 order: Option<SearchOrder>,
28 category_id: Option<CategoryId>,
29 page: Option<i32>,
30 ) -> Result<BpiResponse<SearchData<Vec<Article>>>, BpiError> {
31 let category_id_str = category_id.unwrap_or(CategoryId::All).as_num().to_string();
32 let page_str = page.unwrap_or(1).to_string();
33
34 let params = vec![
35 ("search_type", SearchType::Article.as_str().to_string()),
36 ("keyword", keyword.to_string()),
37 (
38 "order",
39 order.unwrap_or(SearchOrder::TotalRank).as_str().to_string(),
40 ),
41 ("category_id", category_id_str),
42 ("page", page_str),
43 ];
44
45 let signed_params = self.get_wbi_sign2(params).await?;
46
47 self.get("https://api.bilibili.com/x/web-interface/wbi/search/type")
48 .with_bilibili_headers()
49 .query(&signed_params)
50 .send_bpi("搜索专栏")
51 .await
52 }
53
54 pub async fn search_bangumi(
65 &self,
66 keyword: &str,
67 page: Option<i32>,
68 ) -> Result<BpiResponse<SearchData<Vec<Bangumi>>>, BpiError> {
69 let page_str = page.unwrap_or(1).to_string();
70
71 let params = vec![
72 ("search_type", SearchType::MediaBangumi.as_str().to_string()),
73 ("keyword", keyword.to_string()),
74 ("page", page_str),
75 ];
76
77 let signed_params = self.get_wbi_sign2(params).await?;
78
79 self.get("https://api.bilibili.com/x/web-interface/wbi/search/type")
80 .query(&signed_params)
81 .send_bpi("搜索番剧")
82 .await
83 }
84
85 pub async fn search_bili_user(
98 &self,
99 keyword: &str,
100 order_sort: Option<OrderSort>,
101 user_type: Option<UserType>,
102 page: Option<i32>,
103 ) -> Result<BpiResponse<SearchData<Vec<BiliUser>>>, BpiError> {
104 let page_str = page.unwrap_or(1).to_string();
105
106 let params = vec![
107 ("search_type", SearchType::BiliUser.as_str().to_string()),
108 ("keyword", keyword.to_string()),
109 (
110 "order_sort",
111 order_sort
112 .unwrap_or(OrderSort::Ascending)
113 .as_num()
114 .to_string(),
115 ),
116 (
117 "user_type",
118 user_type.unwrap_or(UserType::All).as_num().to_string(),
119 ),
120 ("page", page_str),
121 ];
122
123 let signed_params = self.get_wbi_sign2(params).await?;
124
125 self.get("https://api.bilibili.com/x/web-interface/wbi/search/type")
126 .query(&signed_params)
127 .send_bpi("搜索用户")
128 .await
129 }
130
131 pub async fn search_live(
142 &self,
143 keyword: &str,
144 page: Option<i32>,
145 ) -> Result<BpiResponse<SearchData<LiveData>>, BpiError> {
146 let page_str = page.unwrap_or(1).to_string();
147
148 let params = vec![
149 ("search_type", SearchType::Live.as_str().to_string()),
150 ("keyword", keyword.to_string()),
151 ("page", page_str),
152 ];
153
154 let signed_params = self.get_wbi_sign2(params).await?;
155
156 self.get("https://api.bilibili.com/x/web-interface/wbi/search/type")
157 .query(&signed_params)
158 .send_bpi("搜索直播间及主播")
159 .await
160 }
161
162 pub async fn search_live_room(
174 &self,
175 keyword: &str,
176 order: Option<SearchOrder>,
177 page: Option<i32>,
178 ) -> Result<BpiResponse<SearchData<Vec<LiveRoom>>>, BpiError> {
179 let page_str = page.unwrap_or(1).to_string();
180
181 let params = vec![
182 ("search_type", SearchType::LiveRoom.as_str().to_string()),
183 ("keyword", keyword.to_string()),
184 (
185 "order",
186 order.unwrap_or(SearchOrder::Online).as_str().to_string(),
187 ),
188 ("page", page_str),
189 ];
190
191 let signed_params = self.get_wbi_sign2(params).await?;
192
193 self.get("https://api.bilibili.com/x/web-interface/wbi/search/type")
194 .query(&signed_params)
195 .send_bpi("搜索直播间")
196 .await
197 }
198
199 pub async fn search_live_user(
212 &self,
213 keyword: &str,
214 order_sort: Option<OrderSort>,
215 user_type: Option<UserType>,
216 page: Option<i32>,
217 ) -> Result<BpiResponse<SearchData<Vec<LiveUser>>>, BpiError> {
218 let page_str = page.unwrap_or(1).to_string();
219
220 let params = vec![
221 ("search_type", SearchType::LiveUser.as_str().to_string()),
222 ("keyword", keyword.to_string()),
223 (
224 "order_sort",
225 order_sort
226 .unwrap_or(OrderSort::Ascending)
227 .as_num()
228 .to_string(),
229 ),
230 (
231 "user_type",
232 user_type.unwrap_or(UserType::All).as_num().to_string(),
233 ),
234 ("page", page_str),
235 ];
236
237 let signed_params = self.get_wbi_sign2(params).await?;
238
239 self.get("https://api.bilibili.com/x/web-interface/wbi/search/type")
240 .query(&signed_params)
241 .send_bpi("搜索主播")
242 .await
243 }
244
245 pub async fn search_movie(
256 &self,
257 keyword: &str,
258 page: Option<i32>,
259 ) -> Result<BpiResponse<SearchData<Vec<Movie>>>, BpiError> {
260 let page_str = page.unwrap_or(1).to_string();
261
262 let params = vec![
263 ("search_type", SearchType::MediaFt.as_str().to_string()),
264 ("keyword", keyword.to_string()),
265 ("page", page_str),
266 ];
267
268 let signed_params = self.get_wbi_sign2(params).await?;
269
270 self.get("https://api.bilibili.com/x/web-interface/wbi/search/type")
271 .query(&signed_params)
272 .send_bpi("搜索影视")
273 .await
274 }
275
276 pub async fn search_video(
290 &self,
291 keyword: &str,
292 order: Option<SearchOrder>,
293 duration: Option<Duration>,
294 tids: Option<u32>,
295 page: Option<i32>,
296 ) -> Result<BpiResponse<SearchData<Vec<Video>>>, BpiError> {
297 let page_str = page.unwrap_or(1).to_string();
298
299 let params = vec![
300 ("search_type", SearchType::Video.as_str().to_string()),
301 ("keyword", keyword.to_string()),
302 (
303 "order",
304 order.unwrap_or(SearchOrder::TotalRank).as_str().to_string(),
305 ),
306 (
307 "duration",
308 duration.unwrap_or(Duration::All).as_num().to_string(),
309 ),
310 ("tids", tids.unwrap_or(0).to_string()),
311 ("page", page_str),
312 ];
313
314 let signed_params = self.get_wbi_sign2(params).await?;
315
316 self.get("https://api.bilibili.com/x/web-interface/wbi/search/type")
317 .query(&signed_params)
318 .send_bpi("搜索视频")
319 .await
320 }
321}
322#[cfg(test)]
323mod tests {
324 use super::*;
325 use crate::search::search_params::{CategoryId, Duration, OrderSort, SearchOrder, UserType};
326 use tracing::info;
327
328 #[tokio::test]
329 async fn test_search_article() {
330 let bpi = BpiClient::new();
331 let resp = bpi
332 .search_article(
333 "Rust",
334 Some(SearchOrder::PubDate),
335 Some(CategoryId::Technology),
336 None,
337 )
338 .await;
339 assert!(resp.is_ok());
340 if let Ok(r) = resp {
341 info!("搜索文章返回: {:?}", r);
342 if let Some(data) = r.data {
343 if let Some(results) = data.result {
344 assert!(!results.is_empty());
345 for article in results {
346 info!("文章标题: {}", article.title);
347 }
348 } else {
349 info!("未找到任何文章结果。");
350 }
351 }
352 }
353 }
354
355 #[tokio::test]
356 async fn test_search_bangumi() {
357 let bpi = BpiClient::new();
358 let resp = bpi.search_bangumi("天气之子", None).await;
359 assert!(resp.is_ok());
360 if let Ok(r) = resp {
361 info!("搜索番剧返回: {:?}", r);
362 if let Some(data) = r.data {
363 if let Some(results) = data.result {
364 assert!(!results.is_empty());
365 for bangumi in results {
366 info!("番剧标题: {}", bangumi.title);
367 }
368 } else {
369 info!("未找到任何番剧结果。");
370 }
371 }
372 }
373 }
374
375 #[tokio::test]
376 async fn test_search_bili_user() {
377 let bpi = BpiClient::new();
378 let resp = bpi
379 .search_bili_user(
380 "老番茄",
381 Some(OrderSort::Descending),
382 Some(UserType::All),
383 None,
384 )
385 .await;
386 assert!(resp.is_ok());
387 if let Ok(r) = resp {
388 info!("搜索用户返回: {:?}", r);
389 if let Some(data) = r.data {
390 if let Some(results) = data.result {
391 assert!(!results.is_empty());
392 for user in results {
393 info!("用户昵称: {}", user.uname);
394 }
395 } else {
396 info!("未找到任何用户结果。");
397 }
398 }
399 }
400 }
401
402 #[tokio::test]
403 async fn test_search_live_room() {
404 let bpi = BpiClient::new();
405 let resp = bpi.search_live_room("游戏", None, None).await;
406 assert!(resp.is_ok());
407 if let Ok(r) = resp {
408 info!("搜索直播间返回: {:?}", r);
409 if let Some(data) = r.data {
410 if let Some(results) = data.result {
411 assert!(!results.is_empty());
412 for room in results {
413 info!("直播间标题: {}", room.title);
414 }
415 } else {
416 info!("未找到任何直播间结果。");
417 }
418 }
419 }
420 }
421
422 #[tokio::test]
423 async fn test_search_live_user() {
424 let bpi = BpiClient::new();
425 let resp = bpi
426 .search_live_user(
427 "散人",
428 Some(OrderSort::Descending),
429 Some(UserType::All),
430 None,
431 )
432 .await;
433 assert!(resp.is_ok());
434 if let Ok(r) = resp {
435 info!("搜索主播返回: {:?}", r);
436 if let Some(data) = r.data {
437 if let Some(results) = data.result {
438 assert!(!results.is_empty());
439 for user in results {
440 info!("主播昵称: {}", user.uname);
441 }
442 } else {
443 info!("未找到任何主播结果。");
444 }
445 }
446 }
447 }
448
449 #[tokio::test]
450 async fn test_search_movie() {
451 let bpi = BpiClient::new();
452 let resp = bpi.search_movie("哈利波特", None).await;
453 assert!(resp.is_ok());
454 if let Ok(r) = resp {
455 info!("搜索影视返回: {:?}", r);
456 if let Some(data) = r.data {
457 if let Some(results) = data.result {
458 assert!(!results.is_empty());
459 for movie in results {
460 info!("影视标题: {}", movie.title);
461 }
462 } else {
463 info!("未找到任何影视结果。");
464 }
465 }
466 }
467 }
468
469 #[tokio::test]
470 async fn test_search_video() {
471 let bpi = BpiClient::new();
472 let resp = bpi
473 .search_video(
474 "Rust 教程",
475 Some(SearchOrder::Online),
476 Some(Duration::From10To30),
477 Some(171),
478 None,
479 )
480 .await;
481 assert!(resp.is_ok());
482 if let Ok(r) = resp {
483 info!("搜索视频返回: {:?}", r);
484 if let Some(data) = r.data {
485 if let Some(results) = data.result {
486 assert!(!results.is_empty());
487 for video in results {
488 info!("视频标题: {}", video.title);
489 }
490 } else {
491 info!("未找到任何视频结果。");
492 }
493 }
494 }
495 }
496}