bpi_rs/bangumi/
timeline.rs1use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
5use serde::{ Deserialize, Serialize };
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum BangumiTimelineType {
10 Anime = 1,
12 Movie = 3,
14 ChineseAnimation = 4,
16}
17
18impl BangumiTimelineType {
19 pub fn as_i32(self) -> i32 {
20 self as i32
21 }
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct BangumiTimelineDay {
26 pub date: String,
27 pub date_ts: i64,
28 pub day_of_week: i32,
29 pub episodes: Vec<BangumiTimelineEpisode>,
30 pub is_today: i32,
31}
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct BangumiTimelineEpisode {
35 pub cover: String,
36 pub delay: i32,
37 pub delay_id: i64,
38 pub delay_index: String,
39 pub delay_reason: String,
40 pub ep_cover: String,
41 pub episode_id: i64,
42 pub pub_index: String,
43 pub pub_time: String,
44 pub pub_ts: i64,
45 pub published: i32,
46 pub follows: String,
47 pub plays: String,
48 pub season_id: i64,
49 pub square_cover: String,
50 pub title: String,
51}
52
53impl BpiClient {
54 pub async fn bangumi_timeline(
63 &self,
64 types: BangumiTimelineType,
65 before: i32,
66 after: i32
67 ) -> Result<BpiResponse<Vec<BangumiTimelineDay>>, BpiError> {
68 if before < 0 || before > 7 {
70 return Err(BpiError::InvalidParameter {
71 field: "before",
72 message: "before参数必须在0-7之间",
73 });
74 }
75 if after < 0 || after > 7 {
76 return Err(BpiError::InvalidParameter {
77 field: "after",
78 message: "after参数必须在0-7之间",
79 });
80 }
81
82 self
83 .get("https://api.bilibili.com/pgc/web/timeline")
84 .query(
85 &[
86 ("types", types.as_i32().to_string()),
87 ("before", before.to_string()),
88 ("after", after.to_string()),
89 ]
90 )
91 .send_bpi("获取番剧或影视时间线").await
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98
99 #[tokio::test]
100 async fn test_bangumi_timeline() {
101 let bpi = BpiClient::new();
102 let result = bpi.bangumi_timeline(BangumiTimelineType::Anime, 3, 7).await;
103 assert!(result.is_ok());
104 let response = result.unwrap();
105 assert_eq!(response.code, 0);
106 let data = response.data.unwrap();
107
108 assert!(!data.is_empty());
109 for day in &data {
110 assert!(!day.date.is_empty());
111 assert!(day.day_of_week >= 1 && day.day_of_week <= 7);
112 assert!(!day.episodes.is_empty());
113 for episode in &day.episodes {
114 assert!(!episode.title.is_empty());
115 assert!(episode.season_id > 0);
116 }
117 }
118 }
119
120 #[tokio::test]
121 async fn test_bangumi_timeline_invalid_before() {
122 let bpi = BpiClient::new();
123 let result = bpi.bangumi_timeline(BangumiTimelineType::Anime, 8, 7).await;
124 assert!(result.is_err());
125 let error = result.unwrap_err();
126 match error {
127 BpiError::InvalidParameter { field, message } => {
128 assert_eq!(field, "before");
129 assert_eq!(message, "before参数必须在0-7之间");
130 }
131 _ => panic!("Expected InvalidParameter error"),
132 }
133 }
134
135 #[tokio::test]
136 async fn test_bangumi_timeline_invalid_after() {
137 let bpi = BpiClient::new();
138 let result = bpi.bangumi_timeline(BangumiTimelineType::Anime, 3, 8).await;
139 assert!(result.is_err());
140 let error = result.unwrap_err();
141 match error {
142 BpiError::InvalidParameter { field, message } => {
143 assert_eq!(field, "after");
144 assert_eq!(message, "after参数必须在0-7之间");
145 }
146 _ => panic!("Expected InvalidParameter error"),
147 }
148 }
149
150 #[test]
151 fn test_bangumi_timeline_type() {
152 assert_eq!(BangumiTimelineType::Anime.as_i32(), 1);
153 assert_eq!(BangumiTimelineType::Movie.as_i32(), 3);
154 assert_eq!(BangumiTimelineType::ChineseAnimation.as_i32(), 4);
155 }
156}