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