Skip to main content

bpi_rs/note/
action.rs

1use crate::{ BilibiliRequest, BpiClient, BpiError, BpiResponse };
2use serde::{ Deserialize, Serialize };
3use serde_json::json;
4// --- 保存视频笔记 ---
5
6/// 保存视频笔记的响应数据
7#[derive(Debug, Clone, Deserialize, Serialize)]
8pub struct NoteAddResponseData {
9    /// 笔记ID
10    pub note_id: String,
11}
12
13// --- 删除视频笔记 ---
14
15impl BpiClient {
16    /// 保存视频笔记
17    ///
18    /// # 文档
19    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/note)
20    ///
21    /// # 参数
22    ///
23    /// | 名称 | 类型 | 说明 |
24    /// | ---- | ---- | ---- |
25    /// | `oid` | u64 | 目标 ID(视频 avid) |
26    /// | `title` | &str | 笔记标题 |
27    /// | `summary` | &str | 笔记预览文本 |
28    /// | `content` | &str | 笔记正文 |
29    /// | `note_id` | `Option<&str>` | 笔记 ID(创建时可省略) |
30    /// | `tags` | `Option<&str>` | 跳转标签列表 |
31    /// | `publish` | `Option<bool>` | 是否公开:false 不公开,true 公开 |
32    /// | `auto_comment` | `Option<bool>` | 是否添加到评论区 |
33    pub async fn note_add(
34        &self,
35        oid: u64,
36        title: &str,
37        summary: &str,
38        content: &str,
39        note_id: Option<&str>,
40        tags: Option<&str>,
41        publish: Option<bool>,
42        auto_comment: Option<bool>
43    ) -> Result<BpiResponse<NoteAddResponseData>, BpiError> {
44        let csrf = self.csrf()?;
45
46        let content = json!([{"insert": content}]);
47
48        let mut form = vec![
49            ("oid", oid.to_string()),
50            ("oid_type", "0".to_string()),
51            ("title", title.to_string()),
52            ("summary", summary.to_string()),
53            ("content", content.to_string()),
54            ("cls", "1".to_string()),
55            ("from", "save".to_string()),
56            ("platform", "web".to_string()),
57            ("csrf", csrf)
58        ];
59
60        if let Some(tags) = tags {
61            form.push(("tags", tags.to_string()));
62        }
63
64        if let Some(note_id) = note_id {
65            form.push(("note_id", note_id.to_string()));
66        }
67
68        if let Some(publish) = publish {
69            form.push(("publish", (if publish { "1" } else { "0" }).to_string()));
70        }
71        if let Some(auto_comment) = auto_comment {
72            form.push(("auto_comment", (if auto_comment { "1" } else { "0" }).to_string()));
73        }
74
75        self.post("https://api.bilibili.com/x/note/add").form(&form).send_bpi("保存视频笔记").await
76    }
77
78    /// 保存视频笔记(精简参数)
79    ///
80    /// # 文档
81    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/note)
82    ///
83    /// # 参数
84    ///
85    /// | 名称 | 类型 | 说明 |
86    /// | ---- | ---- | ---- |
87    /// | `oid` | u64 | 目标 ID(视频 avid) |
88    /// | `title` | &str | 笔记标题 |
89    /// | `summary` | &str | 笔记预览文本 |
90    /// | `content` | &str | 笔记正文 |
91    /// | `note_id` | `Option<&str>` | 笔记 ID(创建时可省略) |
92    pub async fn note_add_simple(
93        &self,
94        oid: u64,
95        title: &str,
96        summary: &str,
97        content: &str,
98        note_id: Option<&str>
99    ) -> Result<BpiResponse<NoteAddResponseData>, BpiError> {
100        self.note_add(oid, title, summary, content, note_id, None, None, None).await
101    }
102
103    /// 删除视频笔记
104    ///
105    /// # 文档
106    /// [查看API文档](https://github.com/SocialSisterYi/bilibili-API-collect/tree/master/docs/note)
107    ///
108    /// # 参数
109    ///
110    /// | 名称 | 类型 | 说明 |
111    /// | ---- | ---- | ---- |
112    /// | `oid` | u64 | 目标 ID(视频 avid) |
113    /// | `note_id` | `Option<String>` | 笔记 ID |
114    pub async fn note_del(
115        &self,
116        oid: u64,
117        note_id: Option<String>
118    ) -> Result<BpiResponse<serde_json::Value>, BpiError> {
119        let csrf = self.csrf()?;
120
121        let mut form = vec![("oid", oid.to_string()), ("csrf", csrf)];
122
123        if let Some(note_id) = note_id {
124            form.push(("note_id", note_id.to_string()));
125        }
126
127        self.post("https://api.bilibili.com/x/note/del").form(&form).send_bpi("删除视频笔记").await
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134    use tracing::info;
135
136    #[tokio::test]
137    async fn test_note_add_and_del() {
138        let bpi = BpiClient::new();
139        let oid = 464606672;
140        let title = "测试笔记";
141        let summary = "这是个测试摘要";
142        let content =
143            "Lorem Ipsum is simply dummy text \
144        of the printing and typesetting industry. Lorem Ipsum\
145         has been the industry's standard dummy text ever since \
146         the 1500s, when an unknown printer took a galley of type \
147         and scrambled it to make a type specimen book. \
148         It has survived not only five centuries, but also the leap into electronic typesetting, \
149         remaining essentially unchanged. It was popularised \
150         in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, \
151         and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum";
152
153        // 1. 保存笔记
154        let add_resp = bpi.note_add(
155            oid,
156            title,
157            summary,
158            content,
159            None,
160            None,
161            Some(false),
162            Some(false)
163        ).await;
164
165        info!("Add note result: {:?}", add_resp);
166        assert!(add_resp.is_ok());
167
168        let note_id = add_resp.unwrap().data.unwrap().note_id;
169        info!("New note ID: {}", note_id);
170
171        tokio::time::sleep(tokio::time::Duration::from_secs(10)).await;
172
173        // 2. 删除笔记
174        let del_resp = bpi.note_del(oid, Some(note_id)).await;
175        info!("Delete note result: {:?}", del_resp);
176        assert!(del_resp.is_ok());
177    }
178}