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.get("https://api.bilibili.com/pgc/web/timeline")
83 .query(&[
84 ("types", types.as_i32().to_string()),
85 ("before", before.to_string()),
86 ("after", after.to_string()),
87 ])
88 .send_bpi("获取番剧或影视时间线")
89 .await
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 #[tokio::test]
98 async fn test_bangumi_timeline() {
99 let bpi = BpiClient::new();
100 let result = bpi.bangumi_timeline(BangumiTimelineType::Anime, 3, 7).await;
101 assert!(result.is_ok());
102 let response = result.unwrap();
103 assert_eq!(response.code, 0);
104 let data = response.data.unwrap();
105
106 assert!(!data.is_empty());
107 for day in &data {
108 assert!(!day.date.is_empty());
109 assert!(day.day_of_week >= 1 && day.day_of_week <= 7);
110 assert!(!day.episodes.is_empty());
111 for episode in &day.episodes {
112 assert!(!episode.title.is_empty());
113 assert!(episode.season_id > 0);
114 }
115 }
116 }
117
118 #[tokio::test]
119 async fn test_bangumi_timeline_invalid_before() {
120 let bpi = BpiClient::new();
121 let result = bpi.bangumi_timeline(BangumiTimelineType::Anime, 8, 7).await;
122 assert!(result.is_err());
123 let error = result.unwrap_err();
124 match error {
125 BpiError::InvalidParameter { field, message } => {
126 assert_eq!(field, "before");
127 assert_eq!(message, "before参数必须在0-7之间");
128 }
129 _ => panic!("Expected InvalidParameter error"),
130 }
131 }
132
133 #[tokio::test]
134 async fn test_bangumi_timeline_invalid_after() {
135 let bpi = BpiClient::new();
136 let result = bpi.bangumi_timeline(BangumiTimelineType::Anime, 3, 8).await;
137 assert!(result.is_err());
138 let error = result.unwrap_err();
139 match error {
140 BpiError::InvalidParameter { field, message } => {
141 assert_eq!(field, "after");
142 assert_eq!(message, "after参数必须在0-7之间");
143 }
144 _ => panic!("Expected InvalidParameter error"),
145 }
146 }
147
148 #[test]
149 fn test_bangumi_timeline_type() {
150 assert_eq!(BangumiTimelineType::Anime.as_i32(), 1);
151 assert_eq!(BangumiTimelineType::Movie.as_i32(), 3);
152 assert_eq!(BangumiTimelineType::ChineseAnimation.as_i32(), 4);
153 }
154}