bpi_rs/creativecenter/season/
edit.rs1use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
6use serde::{ Deserialize, Serialize };
7use serde_json::json;
8
9#[derive(Debug, Clone, Serialize, Deserialize, Default)]
11pub struct SeasonEdit {
12 pub id: u64, pub title: String, pub cover: String, #[serde(default)]
16 pub desc: Option<String>, #[serde(default)]
18 pub season_price: Option<u32>, #[serde(default, rename = "isEnd")]
20 pub is_end: Option<u32>, }
22
23#[derive(Debug, Clone, Default, Serialize, Deserialize)]
25pub struct SeasonSectionEdit {
26 pub id: u64,
27 #[serde(rename = "type")]
28 pub type_field: u64,
29 #[serde(rename = "seasonId")]
30 pub season_id: u64,
31 pub title: String,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct SectionSort {
37 pub id: u64, pub order: u32, }
40
41#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
42pub struct EpisodeEdit {
43 pub id: u64,
44 pub title: String,
45 pub aid: u64,
46 pub cid: u64,
47 #[serde(rename = "seasonId")]
48 pub season_id: u64,
49 #[serde(rename = "sectionId")]
50 pub section_id: u64,
51 pub sorts: Vec<EpisodeSort>,
52 pub order: u64,
53}
54
55#[derive(Serialize)]
56struct EpisodeEditPayload {
57 #[serde(flatten)]
58 section: EpisodeEdit,
59 sorts: Vec<EpisodeSort>,
60}
61
62#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
63pub struct EpisodeSort {
64 pub id: u64,
65 pub sort: u64,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct SeasonSectionSort {
71 pub id: u64, pub sort: u32, }
74
75#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
76pub struct SectionAddEpisodesRequest {
77 #[serde(rename = "sectionId")]
78 pub section_id: u64,
79 pub episodes: Vec<Episode>,
80}
81
82#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
83pub struct Episode {
84 pub title: String,
85 pub aid: u64,
86 pub cid: u64,
87 pub charging_pay: i64,
88 pub member_first: i64,
89 pub limited_free: bool,
90}
91
92impl BpiClient {
93 pub async fn season_edit(
106 &self,
107 season: SeasonEdit,
108 sorts: Vec<SeasonSectionSort>
109 ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
110 let csrf = self.csrf()?;
111
112 let payload = json!({
113 "season": season,
114 "sorts": sorts
115 });
116
117 self
118 .post("https://member.bilibili.com/x2/creative/web/season/edit")
119 .query(&[("csrf", csrf)])
120 .json(&payload)
121 .send_bpi("编辑合集信息").await
122 }
123 pub async fn season_section_edit(
136 &self,
137 section: SeasonSectionEdit,
138 sorts: Vec<SectionSort>
139 ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
140 let csrf = self.csrf()?;
141
142 let payload = json!({
143 "section": section,
144 "sorts": sorts
145 });
146
147 self
148 .post("https://member.bilibili.com/x2/creative/web/season/section/edit")
149 .query(&[("csrf", csrf)])
150 .json(&payload)
151 .send_bpi("编辑合集小节").await
152 }
153
154 pub async fn season_section_episode_edit(
167 &self,
168 section: EpisodeEdit,
169 sorts: Vec<EpisodeSort>
170 ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
171 let csrf = self.csrf()?;
172
173 let payload = EpisodeEditPayload { section, sorts };
174
175 self
176 .post("https://member.bilibili.com/x2/creative/web/season/section/episode/edit")
177 .query(&[("csrf", csrf)])
178 .json(&payload)
179 .send_bpi("编辑合集章节").await
180 }
181
182 pub async fn season_enable_section(
188 &self,
189 season_id: u64,
190 enable: bool
191 ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
192 let csrf = self.csrf()?;
193 let params = vec![
194 ("csrf", csrf),
195 ("season_id", season_id.to_string()),
196 ("no_section", (if enable { "0" } else { "1" }).to_string())
197 ];
198
199 self
200 .post("https://member.bilibili.com/x2/creative/web/season/section/switch")
201 .form(¶ms)
202 .send_bpi("切换 小节/正常 模式").await
203 }
204 pub async fn season_section_add_episodes(
219 &self,
220 section_id: u64,
221 episodes: Vec<Episode>
222 ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
223 let csrf = self.csrf()?;
224
225 let payload = SectionAddEpisodesRequest {
226 section_id,
227 episodes,
228 };
229
230 self
231 .post("https://member.bilibili.com/x2/creative/web/season/section/episodes/add")
232 .json(&payload)
233 .query(&[("csrf", csrf)])
234 .send_bpi("编辑投稿视频合集").await
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241
242 const TEST_SECTION_ID: u64 = 7032691;
243 const TEST_SEASON_ID: u64 = 6363779;
244 const TEST_PART_ID: u64 = 147443135; const TEST_AID: u64 = 772876546;
247 const TEST_CID: u64 = 829554597;
248
249 #[tokio::test]
250 async fn test_edit_season_info() -> Result<(), Box<BpiError>> {
251 let bpi = BpiClient::new();
252
253 let season = SeasonEdit {
254 id: TEST_SEASON_ID,
255 title: "修改后的合集标题".to_string(),
256 cover: "https://archive.biliimg.com/bfs/archive/fb699e3f5ae17285cf5f6ebd42c156482f829215.jpg".to_string(),
257 desc: Some("修改后的合集简介".to_string()),
258 ..Default::default()
259 };
260
261 let sorts = vec![SeasonSectionSort {
262 id: TEST_SECTION_ID,
263 sort: 1,
264 }];
265
266 bpi.season_edit(season, sorts).await?;
267
268 tracing::info!("编辑合集信息成功");
269 Ok(())
270 }
271
272 #[tokio::test]
273 async fn test_edit_season_section() -> Result<(), Box<BpiError>> {
274 let bpi = BpiClient::new();
275
276 let section = SeasonSectionEdit {
277 id: TEST_SECTION_ID,
278 season_id: TEST_SEASON_ID,
279 title: "测试修改小节标题".to_string(),
280 ..Default::default()
281 };
282
283 let sorts = vec![SectionSort {
284 id: TEST_PART_ID,
285 order: 1,
286 }];
287
288 bpi.season_section_edit(section, sorts).await?;
289
290 tracing::info!("编辑合集小节成功");
291 Ok(())
292 }
293
294 #[tokio::test]
295 async fn test_edit_season_episode() -> Result<(), Box<BpiError>> {
296 let bpi = BpiClient::new();
297
298 let section = EpisodeEdit {
299 id: TEST_PART_ID,
300 season_id: TEST_SEASON_ID,
301 section_id: TEST_SECTION_ID,
302 sorts: vec![],
303 title: "测试修改章节标题".to_string(),
304 aid: TEST_AID,
305 cid: TEST_CID,
306 order: 1,
307 };
308
309 let sorts = vec![EpisodeSort {
310 id: TEST_PART_ID,
311 sort: 1,
312 }];
313
314 bpi.season_section_episode_edit(section, sorts).await?;
315
316 tracing::info!("编辑合集小节成功");
317 Ok(())
318 }
319
320 #[tokio::test]
321 async fn test_add_episodes() -> Result<(), BpiError> {
322 let bpi = BpiClient::new();
323
324 bpi
325 .season_section_add_episodes(
326 TEST_SECTION_ID,
327 vec![Episode {
328 title: "新增章节标题".to_string(),
329 aid: TEST_AID,
330 cid: TEST_CID,
331 ..Default::default()
332 }]
333 ).await
334 .map(|_| ()) .or_else(|e| {
336 if e.code() == Some(20080) { Ok(()) } else { Err(e) }
337 })?;
338
339 Ok(())
340 }
341}