1use crate::models::VipLabel;
5use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
6use serde::{ Deserialize, Serialize };
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum BangumiArea {
11 MainlandChina = 1,
13 Japan = 2,
15 UnitedStates = 3,
17 UnitedKingdom = 4,
19 Canada = 5,
21 HongKong = 6,
23 Taiwan = 7,
25 SouthKorea = 8,
27 France = 9,
29 Thailand = 10,
31 Malaysia = 11,
33 Singapore = 12,
35 Spain = 13,
37 Russia = 14,
39 Germany = 15,
41 Other = 16,
43 Denmark = 17,
45 Ukraine = 18,
47 Israel = 19,
49 Iran = 20,
51 Bulgaria = 21,
53 Croatia = 22,
55 Iceland = 23,
57 Hungary = 24,
59 SouthAfrica = 25,
61 Indonesia = 26,
63 India = 27,
65 Colombia = 28,
67 Turkey = 30,
69 Mexico = 31,
71 Venezuela = 32,
73 Brazil = 33,
75 Greece = 34,
77 Italy = 35,
79 Norway = 36,
81 CzechRepublic = 37,
83 Morocco = 38,
85 NewZealand = 39,
87 Chile = 40,
89 Belgium = 41,
91 Poland = 42,
93 Australia = 43,
95 Ireland = 44,
97 Sweden = 45,
99 Switzerland = 46,
101 Finland = 47,
103 SovietUnion = 48,
105 Netherlands = 49,
107 Vietnam = 50,
109 Argentina = 51,
111 Malta = 52,
113 Cuba = 53,
115 Philippines = 54,
117 Kazakhstan = 55,
119 Lebanon = 56,
121 Cyprus = 57,
123 Qatar = 58,
125 UnitedArabEmirates = 59,
127 Austria = 60,
129 WestGermany = 61,
131 Luxembourg = 62,
133 Romania = 63,
135 Indonesia2 = 64,
137 Yugoslavia = 65,
139 Mongolia = 66,
141 Portugal = 70,
143}
144
145impl BangumiArea {
146 pub fn as_u32(self) -> u32 {
147 self as u32
148 }
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq)]
153pub enum BangumiType {
154 Anime = 1,
156 Movie = 2,
158 Documentary = 3,
160 ChineseAnimation = 4,
162 TVSeries = 5,
164 Manga = 6,
166 Variety = 7,
168}
169
170impl BangumiType {
171 pub fn as_u32(self) -> u32 {
172 self as u32
173 }
174}
175
176pub type BangumiInfoResponse = BpiResponse<BangumiInfoResult>;
178
179#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct BangumiInfoResult {
181 pub media: BangumiMedia,
182 pub review: Option<BangumiReview>,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize)]
186pub struct BangumiMedia {
187 pub areas: Vec<BangumiAreaInfo>,
188 pub cover: String,
189 pub horizontal_picture: String,
190 pub media_id: u64,
191 pub new_ep: BangumiMediaNewEp,
192 pub rating: BangumiRating,
193 pub season_id: u64,
194 pub share_url: String,
195 pub title: String,
196 pub r#type: u32,
197 pub type_name: String,
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize)]
201pub struct BangumiAreaInfo {
202 pub id: u32,
203 pub name: String,
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize)]
207pub struct BangumiMediaNewEp {
208 pub id: u64,
209 pub index: String,
210 pub index_show: String,
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct BangumiDetailNewEp {
215 pub id: u64,
216 pub desc: String,
217 pub is_new: u32,
218 pub title: String,
219}
220
221#[derive(Debug, Clone, Serialize, Deserialize)]
222pub struct BangumiRating {
223 pub count: u64,
224 pub score: f64,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
228pub struct BangumiReview {
229 pub is_coin: u32,
230 pub is_open: u32,
231}
232
233pub type BangumiDetailResponse = BpiResponse<BangumiDetailResult>;
235
236#[derive(Debug, Clone, Serialize, Deserialize)]
237pub struct BangumiDetailResult {
238 pub activity: Option<BangumiActivity>,
239 pub actors: String,
240 pub alias: String,
241 pub areas: Vec<BangumiAreaInfo>,
242 pub bkg_cover: String,
243 pub cover: String,
244 pub delivery_fragment_video: bool,
245 pub enable_vt: bool,
246 pub episodes: Vec<BangumiEpisode>,
247 pub evaluate: String,
248 pub freya: Option<BangumiFreya>,
249 pub hide_ep_vv_vt_dm: u32,
250 pub icon_font: Option<BangumiIconFont>,
251 pub jp_title: String,
252 pub link: String,
253 pub media_id: u64,
254 pub mode: u32,
255 pub multi_view_info: Option<BangumiMultiViewInfo>,
256 pub new_ep: BangumiDetailNewEp,
257 pub payment: Option<BangumiPayment>,
258 #[serde(rename = "payPack")]
259 pub pay_pack: Option<BangumiPayPack>,
260 pub play_strategy: Option<BangumiPlayStrategy>,
261 pub positive: Option<BangumiPositive>,
262 pub publish: BangumiPublish,
263 pub rating: Option<BangumiRating>,
264 pub record: String,
265 pub rights: BangumiRights,
266 pub season_id: u64,
267 pub season_title: String,
268 pub seasons: Vec<BangumiSeason>,
269 pub section: Option<Vec<BangumiSection>>,
270 pub section_bottom_desc: Option<String>,
271 pub series: Option<BangumiSeries>,
272 pub share_copy: String,
273 pub share_sub_title: String,
274 pub share_url: String,
275 pub show: Option<BangumiShow>,
276 pub show_season_type: u32,
277 pub square_cover: String,
278 pub staff: String,
279 pub stat: BangumiStat,
280 pub status: u32,
281 pub styles: Vec<String>,
282 pub subtitle: String,
283 pub title: String,
284 pub total: u32,
285 pub r#type: u32,
286 pub up_info: Option<BangumiUpInfo>,
287 pub user_status: Option<BangumiUserStatus>,
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct BangumiActivity {
292 pub head_bg_url: String,
293 pub id: u64,
294 pub title: String,
295 pub link: Option<String>,
296 pub pendants: Option<Vec<BangumiPendant>>,
297 pub cover: Option<String>,
298}
299
300#[derive(Debug, Clone, Serialize, Deserialize)]
301pub struct BangumiPendant {
302 pub image: String,
303 pub name: String,
304 pub pid: u64,
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize)]
308pub struct BangumiEpisode {
309 pub aid: u64,
310 pub badge: String,
311 pub badge_info: Option<BangumiBadgeInfo>,
312 pub badge_type: u32,
313 pub bvid: String,
314 pub cid: u64,
315 pub cover: String,
316 pub dimension: Option<BangumiDimension>,
317 pub duration: u64,
318 pub enable_vt: bool,
319 pub ep_id: u64,
320 pub from: String,
321 pub id: u64,
322 pub interaction: Option<BangumiInteraction>,
323 pub is_view_hide: bool,
324 pub link: String,
325 pub long_title: String,
326 pub multi_view_eps: Option<Vec<BangumiMultiViewEp>>,
327 pub pub_time: u64,
328 pub pv: u64,
329 pub release_date: String,
330 pub rights: Option<BangumiEpisodeRights>,
331 pub section_type: u32,
332 pub share_copy: String,
333 pub share_url: String,
334 pub short_link: String,
335 pub show_title: String,
336 #[serde(rename = "showDrmLoginDialog")]
337 pub show_drm_login_dialog: bool,
338 pub skip: Option<BangumiSkip>,
339 pub status: u32,
340 pub subtitle: String,
341 pub title: String,
342 pub vid: String,
343}
344
345#[derive(Debug, Clone, Serialize, Deserialize)]
346pub struct BangumiBadgeInfo {
347 pub bg_color: String,
348 pub bg_color_night: String,
349 pub text: String,
350}
351
352#[derive(Debug, Clone, Serialize, Deserialize)]
353pub struct BangumiDimension {
354 pub height: u32,
355 pub rotate: u32,
356 pub width: u32,
357}
358
359#[derive(Debug, Clone, Serialize, Deserialize)]
360pub struct BangumiInteraction {
361 pub graph_version: u32,
362 pub interaction: bool,
363}
364
365#[derive(Debug, Clone, Serialize, Deserialize)]
366pub struct BangumiMultiViewEp {
367 pub ep_id: u64,
368}
369
370#[derive(Debug, Clone, Serialize, Deserialize)]
371pub struct BangumiEpisodeRights {
372 pub allow_dm: u32,
373 pub allow_download: u32,
374 pub area_limit: u32,
375
376 pub allow_demand: Option<u32>,
377}
378
379#[derive(Debug, Clone, Serialize, Deserialize)]
380pub struct BangumiSkip {
381 pub ed: Option<BangumiSkipTime>,
382 pub op: Option<BangumiSkipTime>,
383}
384
385#[derive(Debug, Clone, Serialize, Deserialize)]
386pub struct BangumiSkipTime {
387 pub end: u32,
388 pub start: u32,
389}
390
391#[derive(Debug, Clone, Serialize, Deserialize)]
392pub struct BangumiFreya {
393 pub bubble_desc: String,
394 pub bubble_show_cnt: u32,
395 pub icon_show: u32,
396}
397
398#[derive(Debug, Clone, Serialize, Deserialize)]
399pub struct BangumiIconFont {
400 pub name: String,
401 pub text: String,
402}
403
404#[derive(Debug, Clone, Serialize, Deserialize)]
405pub struct BangumiMultiViewInfo {
406 pub changing_dance: String,
407 pub is_multi_view_season: bool,
408}
409
410#[derive(Debug, Clone, Serialize, Deserialize)]
411pub struct BangumiPayment {
412 pub discount: u32,
413 pub pay_type: BangumiPayType,
414 pub price: String,
415 pub promotion: String,
416 pub tip: String,
417 pub view_start_time: u64,
418 pub vip_discount: u32,
419 pub vip_first_promotion: String,
420 pub vip_price: String,
421 pub vip_promotion: String,
422}
423
424#[derive(Debug, Clone, Serialize, Deserialize)]
425pub struct BangumiPayType {
426 pub allow_discount: u32,
427 pub allow_pack: u32,
428 pub allow_ticket: u32,
429 pub allow_time_limit: u32,
430 pub allow_vip_discount: u32,
431 pub forbid_bb: u32,
432}
433
434#[derive(Debug, Clone, Serialize, Deserialize)]
435pub struct BangumiPayPack {
436 pub id: u64,
437 pub not_paid_text_for_app: String,
438 pub paid_text_for_app: String,
439 pub pay_pack_url: String,
440 pub status: u32,
441 pub title: String,
442}
443
444#[derive(Debug, Clone, Serialize, Deserialize)]
445pub struct BangumiPlayStrategy {
446 pub strategies: Vec<serde_json::Value>,
447}
448
449#[derive(Debug, Clone, Serialize, Deserialize)]
450pub struct BangumiPositive {
451 pub id: u64,
452 pub title: String,
453}
454
455#[derive(Debug, Clone, Serialize, Deserialize)]
456pub struct BangumiPublish {
457 pub is_finish: u32,
458 pub is_started: u32,
459 pub pub_time: String,
460 pub pub_time_show: String,
461 pub unknow_pub_date: u32,
462 pub weekday: u32,
463}
464
465#[derive(Debug, Clone, Serialize, Deserialize)]
466pub struct BangumiRights {
467 pub allow_bp: u32,
468 pub allow_bp_rank: u32,
469 pub allow_download: u32,
470 pub allow_review: u32,
471 pub area_limit: u32,
472 pub ban_area_show: u32,
473 pub can_watch: u32,
474 pub copyright: String,
475 pub forbid_pre: u32,
476 pub freya_white: u32,
477 pub is_cover_show: u32,
478 pub is_preview: u32,
479 pub only_vip_download: u32,
480 pub resource: String,
481 pub watch_platform: u32,
482}
483
484#[derive(Debug, Clone, Serialize, Deserialize)]
485pub struct BangumiSeason {
486 pub badge: String,
487 pub badge_info: Option<BangumiBadgeInfo>,
488 pub badge_type: u32,
489 pub cover: String,
490 pub enable_vt: bool,
491 pub horizontal_cover_1610: String,
492 pub horizontal_cover_169: String,
493 pub icon_font: Option<BangumiIconFont>,
494 pub media_id: u64,
495 pub new_ep: Option<BangumiSeasonNewEp>,
496 pub season_id: u64,
497 pub season_title: String,
498 pub season_type: u32,
499 pub stat: Option<BangumiSeasonStat>,
500}
501
502#[derive(Debug, Clone, Serialize, Deserialize)]
503pub struct BangumiSeasonNewEp {
504 pub cover: String,
505 pub id: u64,
506 pub index_show: String,
507}
508
509#[derive(Debug, Clone, Serialize, Deserialize)]
510pub struct BangumiSeasonStat {
511 pub favorites: u64,
512 pub series_follow: u64,
513 pub views: u64,
514 pub vt: u64,
515}
516
517#[derive(Debug, Clone, Serialize, Deserialize)]
518pub struct BangumiSection {
519 pub attr: u32,
520 pub episode_id: u64,
521 pub episode_ids: Vec<u64>,
522 pub episodes: Vec<BangumiSectionEpisode>,
523 pub id: u64,
524 pub report: Option<BangumiReport>,
525 pub title: String,
526 pub r#type: u32,
527 pub type2: u32,
528}
529
530#[derive(Debug, Clone, Serialize, Deserialize)]
531pub struct BangumiSectionEpisode {
532 pub aid: u64,
533 pub archive_attr: Option<u32>,
534 pub badge: String,
535 pub badge_info: Option<BangumiBadgeInfo>,
536 pub badge_type: u32,
537 pub bvid: String,
538 pub cid: u64,
539 pub cover: String,
540 pub dimension: Option<BangumiDimension>,
541 pub duration: u64,
542 pub enable_vt: bool,
543 pub ep_id: u64,
544 pub from: String,
545 pub icon_font: Option<BangumiIconFont>,
546 pub id: u64,
547 pub interaction: Option<BangumiInteraction>,
548 pub is_view_hide: bool,
549 pub link: String,
550 pub link_type: String,
551 pub long_title: String,
552 pub pub_time: u64,
553 pub pv: u64,
554 pub release_date: String,
555 pub report: Option<BangumiReport>,
556 pub rights: Option<BangumiEpisodeRights>,
557 pub section_type: u32,
558 pub share_copy: String,
559 pub share_url: String,
560 pub short_link: String,
561 pub show_title: String,
562 #[serde(rename = "showDrmLoginDialog")]
563 pub show_drm_login_dialog: bool,
564 pub skip: Option<BangumiSkip>,
565 pub stat: Option<BangumiStat>,
566 pub stat_for_unity: Option<BangumiStatForUnity>,
567 pub status: u32,
568 pub subtitle: String,
569 pub title: String,
570 pub toast_title: String,
571 pub up_info: Option<BangumiUpInfo>,
572 pub vid: String,
573}
574
575#[derive(Debug, Clone, Serialize, Deserialize)]
576pub struct BangumiReport {
577 pub aid: String,
578 pub ep_title: String,
579 pub position: String,
580 pub season_id: String,
581 pub season_type: String,
582 pub section_id: String,
583 pub section_type: String,
584}
585
586#[derive(Debug, Clone, Serialize, Deserialize)]
587pub struct BangumiStatForUnity {
588 pub coin: u64,
589 pub danmaku: Option<BangumiDanmaku>,
590 pub likes: u64,
591 pub reply: u64,
592 pub vt: Option<BangumiVt>,
593}
594
595#[derive(Debug, Clone, Serialize, Deserialize)]
596pub struct BangumiDanmaku {
597 pub icon: String,
598 pub pure_text: String,
599 pub text: String,
600 pub value: u64,
601}
602
603#[derive(Debug, Clone, Serialize, Deserialize)]
604pub struct BangumiVt {
605 pub icon: String,
606 pub pure_text: String,
607 pub text: String,
608 pub value: u64,
609}
610
611#[derive(Debug, Clone, Serialize, Deserialize)]
612pub struct BangumiStat {
613 pub coins: u64,
614 pub danmakus: u64,
615 pub favorite: u64,
616 pub favorites: u64,
617 pub follow_text: String,
618 pub hot: Option<u64>,
619 pub likes: u64,
620 pub reply: u64,
621 pub share: u64,
622 pub views: u64,
623 pub vt: u64,
624}
625
626#[derive(Debug, Clone, Serialize, Deserialize)]
627pub struct BangumiSeries {
628 pub display_type: u32,
629 pub series_id: u64,
630 pub series_title: String,
631}
632
633#[derive(Debug, Clone, Serialize, Deserialize)]
634pub struct BangumiShow {
635 pub wide_screen: u32,
636}
637
638#[derive(Debug, Clone, Serialize, Deserialize)]
639pub struct BangumiUpInfo {
640 pub avatar: String,
641 pub avatar_subscript_url: String,
642 pub follower: u64,
643 pub is_follow: u32,
644 pub mid: u64,
645 pub nickname_color: String,
646 pub pendant: Option<BangumiPendant>,
647 pub theme_type: u32,
648 pub uname: String,
649 pub verify_type: u32,
650 pub vip_label: Option<VipLabel>,
651 pub vip_status: u32,
652 pub vip_type: u32,
653}
654
655#[derive(Debug, Clone, Serialize, Deserialize)]
656pub struct BangumiUserStatus {
657 pub area_limit: u32,
658 pub ban_area_show: u32,
659 pub follow: u32,
660 pub follow_status: u32,
661 pub login: u32,
662 pub pay: u32,
663 pub pay_pack_paid: u32,
664 pub sponsor: u32,
665}
666
667#[derive(Debug, Clone, Serialize, Deserialize)]
668pub struct BangumiSectionResult {
669 pub main_section: BangumiMainSection,
670 pub section: Vec<BangumiMainSection>,
671}
672
673#[derive(Debug, Clone, Serialize, Deserialize)]
674pub struct BangumiMainSection {
675 pub episodes: Vec<BangumiSectionEpisodeInfo>,
676 pub id: u64,
677 pub r#type: u32,
678 pub title: String,
679}
680
681#[derive(Debug, Clone, Serialize, Deserialize)]
682pub struct BangumiSectionEpisodeInfo {
683 pub aid: u64,
684 pub badge: String,
685 pub badge_info: BangumiBadgeInfo,
686 pub badge_type: u32,
687 pub cid: u64,
688 pub cover: String,
689 pub from: String,
690 pub id: u64,
691 pub is_premiere: u32,
692 pub long_title: String,
693 pub share_url: String,
694 pub status: u32,
695 pub title: String,
696 pub vid: String,
697}
698
699impl BpiClient {
700 pub async fn bangumi_info(&self, media_id: u64) -> Result<BangumiInfoResponse, BpiError> {
708 let result: BangumiInfoResponse = self
709 .get("https://api.bilibili.com/pgc/review/user")
710 .query(&[("media_id", media_id.to_string())])
711 .send_bpi("获取剧集基本信息").await?;
712 Ok(result)
713 }
714
715 pub async fn bangumi_detail_by_season_id(
723 &self,
724 season_id: u64
725 ) -> Result<BangumiDetailResponse, BpiError> {
726 let result: BangumiDetailResponse = self
727 .get("https://api.bilibili.com/pgc/view/web/season")
728 .query(&[("season_id", season_id.to_string())])
729 .send_bpi("获取剧集明细").await?;
730 Ok(result)
731 }
732
733 pub async fn bangumi_detail_by_epid(
741 &self,
742 ep_id: u64
743 ) -> Result<BangumiDetailResponse, BpiError> {
744 self
745 .get("https://api.bilibili.com/pgc/view/web/season")
746 .query(&[("ep_id", ep_id.to_string())])
747 .send_bpi("获取剧集明细").await
748 }
749
750 pub async fn bangumi_sections_by_season_id(
758 &self,
759 season_id: u64
760 ) -> Result<BpiResponse<BangumiSectionResult>, BpiError> {
761 self
762 .get("https://api.bilibili.com/pgc/web/season/section")
763 .query(&[("season_id", season_id.to_string())])
764 .send_bpi("获取剧集分集信息").await
765 }
766}
767
768#[cfg(test)]
769mod tests {
770 use super::*;
771
772 const TEST_SEASON_ID: u64 = 1172; const TEST_EP_ID: u64 = 21265; const TEST_MEDIA_ID: u64 = 28220978; #[tokio::test]
777 async fn test_bangumi_info() -> Result<(), Box<BpiError>> {
778 let bpi = BpiClient::new();
779 let result = bpi.bangumi_info(TEST_MEDIA_ID).await?;
780 let data = result.into_data()?;
781 tracing::info!("{:#?}", data);
782
783 assert_eq!(data.media.media_id, TEST_MEDIA_ID);
784 assert!(!data.media.title.is_empty());
785 assert!(!data.media.areas.is_empty());
786
787 Ok(())
788 }
789
790 #[tokio::test]
791 async fn test_bangumi_detail_by_season_id() -> Result<(), Box<BpiError>> {
792 let bpi = BpiClient::new();
793 let result = bpi.bangumi_detail_by_season_id(TEST_SEASON_ID).await?;
794 let data = result.into_data()?;
795 tracing::info!("{:#?}", data);
796
797 assert_eq!(data.season_id, TEST_SEASON_ID);
798 assert!(!data.title.is_empty());
799 assert!(!data.episodes.is_empty());
800
801 Ok(())
802 }
803
804 #[tokio::test]
805 async fn test_bangumi_detail_by_epid() -> Result<(), Box<BpiError>> {
806 let bpi = BpiClient::new();
807 let result = bpi.bangumi_detail_by_epid(TEST_EP_ID).await?;
808 let data = result.into_data()?;
809 tracing::info!("{:#?}", data);
810
811 assert!(!data.title.is_empty());
812 assert!(!data.episodes.is_empty());
813
814 Ok(())
815 }
816
817 #[tokio::test]
818 async fn test_bangumi_section() -> Result<(), Box<BpiError>> {
819 let bpi = BpiClient::new();
820 let result = bpi.bangumi_sections_by_season_id(TEST_SEASON_ID).await?;
821 let data = result.into_data()?;
822 tracing::info!("{:#?}", data);
823
824 assert!(!data.main_section.episodes.is_empty());
825
826 Ok(())
827 }
828}