1use super::result::{
6 Article,
7 Bangumi,
8 BiliUser,
9 LiveData,
10 LiveRoom,
11 LiveUser,
12 Movie,
13 SearchData,
14 Video,
15};
16use super::search_params::{ CategoryId, Duration, OrderSort, SearchOrder, SearchType, UserType };
17use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
18
19impl BpiClient {
20 pub async fn search_article(
34 &self,
35 keyword: &str,
36 order: Option<SearchOrder>,
37 category_id: Option<CategoryId>,
38 page: Option<i32>
39 ) -> Result<BpiResponse<SearchData<Vec<Article>>>, BpiError> {
40 let category_id_str = category_id.unwrap_or(CategoryId::All).as_num().to_string();
41 let page_str = page.unwrap_or(1).to_string();
42
43 let params = vec![
44 ("search_type", SearchType::Article.as_str().to_string()),
45 ("keyword", keyword.to_string()),
46 ("order", order.unwrap_or(SearchOrder::TotalRank).as_str().to_string()),
47 ("category_id", category_id_str),
48 ("page", page_str)
49 ];
50
51 let signed_params = self.get_wbi_sign2(params).await?;
52
53 self
54 .get("https://api.bilibili.com/x/web-interface/wbi/search/type")
55 .with_bilibili_headers()
56 .query(&signed_params)
57 .send_bpi("搜索专栏").await
58 }
59
60 pub async fn search_bangumi(
72 &self,
73 keyword: &str,
74 page: Option<i32>
75 ) -> Result<BpiResponse<SearchData<Vec<Bangumi>>>, BpiError> {
76 let page_str = page.unwrap_or(1).to_string();
77
78 let params = vec![
79 ("search_type", SearchType::MediaBangumi.as_str().to_string()),
80 ("keyword", keyword.to_string()),
81 ("page", page_str)
82 ];
83
84 let signed_params = self.get_wbi_sign2(params).await?;
85
86 self
87 .get("https://api.bilibili.com/x/web-interface/wbi/search/type")
88 .query(&signed_params)
89 .send_bpi("搜索番剧").await
90 }
91
92 pub async fn search_bili_user(
106 &self,
107 keyword: &str,
108 order_sort: Option<OrderSort>,
109 user_type: Option<UserType>,
110 page: Option<i32>
111 ) -> Result<BpiResponse<SearchData<Vec<BiliUser>>>, BpiError> {
112 let page_str = page.unwrap_or(1).to_string();
113
114 let params = vec![
115 ("search_type", SearchType::BiliUser.as_str().to_string()),
116 ("keyword", keyword.to_string()),
117 ("order_sort", order_sort.unwrap_or(OrderSort::Ascending).as_num().to_string()),
118 ("user_type", user_type.unwrap_or(UserType::All).as_num().to_string()),
119 ("page", page_str)
120 ];
121
122 let signed_params = self.get_wbi_sign2(params).await?;
123
124 self
125 .get("https://api.bilibili.com/x/web-interface/wbi/search/type")
126 .query(&signed_params)
127 .send_bpi("搜索用户").await
128 }
129
130 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
157 .get("https://api.bilibili.com/x/web-interface/wbi/search/type")
158 .query(&signed_params)
159 .send_bpi("搜索直播间及主播").await
160 }
161
162 pub async fn search_live_room(
175 &self,
176 keyword: &str,
177 order: Option<SearchOrder>,
178 page: Option<i32>
179 ) -> Result<BpiResponse<SearchData<Vec<LiveRoom>>>, BpiError> {
180 let page_str = page.unwrap_or(1).to_string();
181
182 let params = vec![
183 ("search_type", SearchType::LiveRoom.as_str().to_string()),
184 ("keyword", keyword.to_string()),
185 ("order", order.unwrap_or(SearchOrder::Online).as_str().to_string()),
186 ("page", page_str)
187 ];
188
189 let signed_params = self.get_wbi_sign2(params).await?;
190
191 self
192 .get("https://api.bilibili.com/x/web-interface/wbi/search/type")
193 .query(&signed_params)
194 .send_bpi("搜索直播间").await
195 }
196
197 pub async fn search_live_user(
211 &self,
212 keyword: &str,
213 order_sort: Option<OrderSort>,
214 user_type: Option<UserType>,
215 page: Option<i32>
216 ) -> Result<BpiResponse<SearchData<Vec<LiveUser>>>, BpiError> {
217 let page_str = page.unwrap_or(1).to_string();
218
219 let params = vec![
220 ("search_type", SearchType::LiveUser.as_str().to_string()),
221 ("keyword", keyword.to_string()),
222 ("order_sort", order_sort.unwrap_or(OrderSort::Ascending).as_num().to_string()),
223 ("user_type", user_type.unwrap_or(UserType::All).as_num().to_string()),
224 ("page", page_str)
225 ];
226
227 let signed_params = self.get_wbi_sign2(params).await?;
228
229 self
230 .get("https://api.bilibili.com/x/web-interface/wbi/search/type")
231 .query(&signed_params)
232 .send_bpi("搜索主播").await
233 }
234
235 pub async fn search_movie(
247 &self,
248 keyword: &str,
249 page: Option<i32>
250 ) -> Result<BpiResponse<SearchData<Vec<Movie>>>, BpiError> {
251 let page_str = page.unwrap_or(1).to_string();
252
253 let params = vec![
254 ("search_type", SearchType::MediaFt.as_str().to_string()),
255 ("keyword", keyword.to_string()),
256 ("page", page_str)
257 ];
258
259 let signed_params = self.get_wbi_sign2(params).await?;
260
261 self
262 .get("https://api.bilibili.com/x/web-interface/wbi/search/type")
263 .query(&signed_params)
264 .send_bpi("搜索影视").await
265 }
266
267 pub async fn search_video(
282 &self,
283 keyword: &str,
284 order: Option<SearchOrder>,
285 duration: Option<Duration>,
286 tids: Option<u32>,
287 page: Option<i32>
288 ) -> Result<BpiResponse<SearchData<Vec<Video>>>, BpiError> {
289 let page_str = page.unwrap_or(1).to_string();
290
291 let params = vec![
292 ("search_type", SearchType::Video.as_str().to_string()),
293 ("keyword", keyword.to_string()),
294 ("order", order.unwrap_or(SearchOrder::TotalRank).as_str().to_string()),
295 ("duration", duration.unwrap_or(Duration::All).as_num().to_string()),
296 ("tids", tids.unwrap_or(0).to_string()),
297 ("page", page_str)
298 ];
299
300 let signed_params = self.get_wbi_sign2(params).await?;
301
302 self
303 .get("https://api.bilibili.com/x/web-interface/wbi/search/type")
304 .query(&signed_params)
305 .send_bpi("搜索视频").await
306 }
307}
308#[cfg(test)]
309mod tests {
310 use super::*;
311 use crate::search::search_params::{ CategoryId, Duration, OrderSort, SearchOrder, UserType };
312 use tracing::info;
313
314 #[tokio::test]
315 async fn test_search_article() {
316 let bpi = BpiClient::new();
317 let resp = bpi.search_article(
318 "Rust",
319 Some(SearchOrder::PubDate),
320 Some(CategoryId::Technology),
321 None
322 ).await;
323 assert!(resp.is_ok());
324 if let Ok(r) = resp {
325 info!("搜索文章返回: {:?}", r);
326 if let Some(data) = r.data {
327 if let Some(results) = data.result {
328 assert!(!results.is_empty());
329 for article in results {
330 info!("文章标题: {}", article.title);
331 }
332 } else {
333 info!("未找到任何文章结果。");
334 }
335 }
336 }
337 }
338
339 #[tokio::test]
340 async fn test_search_bangumi() {
341 let bpi = BpiClient::new();
342 let resp = bpi.search_bangumi("天气之子", None).await;
343 assert!(resp.is_ok());
344 if let Ok(r) = resp {
345 info!("搜索番剧返回: {:?}", r);
346 if let Some(data) = r.data {
347 if let Some(results) = data.result {
348 assert!(!results.is_empty());
349 for bangumi in results {
350 info!("番剧标题: {}", bangumi.title);
351 }
352 } else {
353 info!("未找到任何番剧结果。");
354 }
355 }
356 }
357 }
358
359 #[tokio::test]
360 async fn test_search_bili_user() {
361 let bpi = BpiClient::new();
362 let resp = bpi.search_bili_user(
363 "老番茄",
364 Some(OrderSort::Descending),
365 Some(UserType::All),
366 None
367 ).await;
368 assert!(resp.is_ok());
369 if let Ok(r) = resp {
370 info!("搜索用户返回: {:?}", r);
371 if let Some(data) = r.data {
372 if let Some(results) = data.result {
373 assert!(!results.is_empty());
374 for user in results {
375 info!("用户昵称: {}", user.uname);
376 }
377 } else {
378 info!("未找到任何用户结果。");
379 }
380 }
381 }
382 }
383
384 #[tokio::test]
385 async fn test_search_live_room() {
386 let bpi = BpiClient::new();
387 let resp = bpi.search_live_room("游戏", None, None).await;
388 assert!(resp.is_ok());
389 if let Ok(r) = resp {
390 info!("搜索直播间返回: {:?}", r);
391 if let Some(data) = r.data {
392 if let Some(results) = data.result {
393 assert!(!results.is_empty());
394 for room in results {
395 info!("直播间标题: {}", room.title);
396 }
397 } else {
398 info!("未找到任何直播间结果。");
399 }
400 }
401 }
402 }
403
404 #[tokio::test]
405 async fn test_search_live_user() {
406 let bpi = BpiClient::new();
407 let resp = bpi.search_live_user(
408 "散人",
409 Some(OrderSort::Descending),
410 Some(UserType::All),
411 None
412 ).await;
413 assert!(resp.is_ok());
414 if let Ok(r) = resp {
415 info!("搜索主播返回: {:?}", r);
416 if let Some(data) = r.data {
417 if let Some(results) = data.result {
418 assert!(!results.is_empty());
419 for user in results {
420 info!("主播昵称: {}", user.uname);
421 }
422 } else {
423 info!("未找到任何主播结果。");
424 }
425 }
426 }
427 }
428
429 #[tokio::test]
430 async fn test_search_movie() {
431 let bpi = BpiClient::new();
432 let resp = bpi.search_movie("哈利波特", None).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 movie in results {
440 info!("影视标题: {}", movie.title);
441 }
442 } else {
443 info!("未找到任何影视结果。");
444 }
445 }
446 }
447 }
448
449 #[tokio::test]
450 async fn test_search_video() {
451 let bpi = BpiClient::new();
452 let resp = bpi.search_video(
453 "Rust 教程",
454 Some(SearchOrder::Online),
455 Some(Duration::From10To30),
456 Some(171),
457 None
458 ).await;
459 assert!(resp.is_ok());
460 if let Ok(r) = resp {
461 info!("搜索视频返回: {:?}", r);
462 if let Some(data) = r.data {
463 if let Some(results) = data.result {
464 assert!(!results.is_empty());
465 for video in results {
466 info!("视频标题: {}", video.title);
467 }
468 } else {
469 info!("未找到任何视频结果。");
470 }
471 }
472 }
473 }
474}