0%

Obsidian+Hexo实现仪表盘及更新博客

看了一篇Obsidian + Hugo 最佳配置推荐文章,做了一个Obsidian+Hexo博客仪表盘,配合之前的Hexo发布脚本,基本上在仅使用Obsidian的情况下完成博客的更新发布流程。先看效果图:

准备

  • Obsidian插件
    • Charts
    • Dataview
    • Homepage(可选)

步骤

  • 安装好上述插件
  • 在你当前vaults下创建一个blog目录,用于管理博客的原始文件
  • 使用Homepage或者创建一个空白的md文件,输入以下仪表盘代码(需要去掉开头的\\)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
\\ ```dataviewjs
\\ let la = ["未发布","已发布"]
\\ let da = []
\\ const draftPage = dv.pages(`"blog"`).filter(p => p.draft).length
\\ const notDraftPage = dv.pages(`"blog"`).filter(p => !p.draft).length
\\ da[0] = notDraftPage
\\ da[1] = draftPage
\\
\\
\\ dv.paragraph(`\`\`\`chart
\\ type: pie
\\ labels: ["未发布","已发布"]
\\ series:
\\ - title: none
\\ data: [${da}]
\\ width: 25%
\\ legendPosition: left
\\ labelColors: true
\\ \`\`\`
\\ `);
\\ ```
\\
\\ ### 草稿箱
\\
\\ ```dataview
\\ table updated AS "更新时间",date AS "创建时间"
\\ from "blog"
\\ where draft=true
\\ sort updated desc
\\ ```
\\ ### 已发布
\\
\\ ```dataview
\\ table updated AS "更新时间",date AS "创建时间"
\\ from "blog"
\\ where draft=false or !draft
\\ sort updated desc
\\ ```
  • 配合以下的RunJS发布Hexo脚本,原理:将原文档上传到Hexo源码仓库,触发Github Actions更新博客
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
/**
* @RunJS hexo/publish
*/

// setting
const token = 'xxx';
const owner = 'xxx';
const repo = 'xxx';
const remote_image_path = 'source/images'
const action = 'deploy.yml'
const attachment_path = 'media/image'
const image_map_path = 'media/script/data/image-map.json'

async function readJsonFile(filePath) {
try {
// 使用vault的read方法读取文件内容
const fileContent = await this.app.vault.read(this.app.vault.getAbstractFileByPath(filePath));
// 将读取到的内容解析为JSON对象
const jsonData = JSON.parse(fileContent);
// console.log(jsonData);
return jsonData;
} catch (error) {
console.error("Error reading JSON file:", error);
return null;
}
}

function arrayBufferToBase64(buffer) {
let binary = '';
const bytes = new Uint8Array(buffer);
const len = bytes.byteLength;
for (let i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
// 使用 btoa 函数将二进制字符串转换为 Base64 字符串
return window.btoa(binary);
}

async function readImageAsDataURL(img, fileExt) {
try {
// 使用vault的readBinary方法读取二进制文件内容
const fileContent = await this.app.vault.readBinary(img);
// 创建一个Blob对象
const blob = new Blob([fileContent], { type: `image/${fileExt}` }); // 假设图片是PNG格式
return blob
} catch (error) {
console.error("Error reading image file:", error);
}
}

async function getFileSha(owner, repo, path) {
const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path}`;
const requestUrlParam = {
url: url,
method: 'GET',
headers: {
'Authorization': `token ${token}`,
'Accept': 'application/vnd.github.v3+json'
}
};

try {
const response = await requestUrl(requestUrlParam);
if (response.status === 200 || response.status === 201) {
return response.json.sha
} else {
console.error('失败', response.status, response.text);
}
} catch (error) {
console.log(`异常: ${error}`);
}

}

function base64EncodeUnicode(str) {
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function (match, p1) {
return String.fromCharCode('0x' + p1);
}));
}


async function updateFile(owner, repo, path, content, sha, message = "update via Obsidian") {
const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path}`;
requestUrlParam = {
url: url,
method: 'PUT',
headers: {
'Authorization': `token ${token}`,
'Accept': 'application/vnd.github.v3+json',
},
body: JSON.stringify({
message,
content: content,
sha
})
}
try {
const response = await requestUrl(requestUrlParam);
if (response.status === 200 || response.status === 201) {
// console.log(response.json)
return response.json
} else {
console.error('失败', response.status, response.text);
}
} catch (error) {
new Notice(`异常: ${error}`);
}
}

async function uploadFileToGitHub(owner, repo, path, content) {
try {
const sha = await getFileSha(owner, repo, path);
const result = await updateFile(owner, repo, path, content, sha, "Update via Obsidian");
console.log("文件更新成功:", result);
} catch (error) {
console.error("更新文件时发生错误:", error);
}
}

function isUrl(link) {
try {
return Boolean(new URL(link));
} catch (_) {
return false;
}
}

async function triggerHexoDeploy(owner, repo, action) {
const url = `https://api.github.com/repos/${owner}/${repo}/actions/workflows/${action}/dispatches`;
console.log(url)
const requestUrlParam = {
url: url,
method: 'POST',
headers: {
'Authorization': `token ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
ref: "master"
})
};
try {
const response = await requestUrl(requestUrlParam);
if (response.status === 204) {
console.log('触发博客更新成功');
} else {
console.error('触发博客更新失败', response.status, response.text);
}
} catch (error) {
new Notice(`触发博客更新请求异常: ${error}`);
}
}

// main
const activeFile = this.app.workspace.getActiveFile();
if (!activeFile) {
console.log("No file is currently active.");
return;
}
console.log(activeFile)
let content = await this.app.vault.read(activeFile)
let image_map = await readJsonFile(image_map_path)
// 图片上传
const imageRegex = /!\[[^$].*?\]\(([^$].*?)\)|!\[\[([^$].*?)\]\]/g;
// const imageRegex2 = /!\[\[(.*?)\]\]/g
const images = []
let match
let img
let img_name
let img_content
while ((match = imageRegex.exec(content)) !== null) {
if (match[1] || match[2]) {
img_name = match[1] || match[2]
images.push(name);
}
console.log(img_name)
if (!isUrl(img_name)) {
if (match[1]) {
img = await this.app.vault.getAbstractFileByPath(img_name);
} else {
img = await this.app.vault.getAbstractFileByPath(`${attachment_path}/${img_name}`);
}
console.log(img)
if (!image_map.hasOwnProperty(img.name)) {
console.log(`upload: ${img.name}`)
img_content = await this.app.vault.readBinary(img);
img_content = arrayBufferToBase64(img_content);
await uploadFileToGitHub(owner, repo, `${remote_image_path}/${img.name}`, img_content)
image_map[img.name] = `https://blog.ziiyc.com/images/${img.name}`
await this.app.vault.adapter.write(image_map_path, JSON.stringify(image_map, null, 2));
}
if (match[1]) {
content = content.replace(img_name, image_map[img.name])
}
if (match[2]) {
content = content.replace(`![[${img_name}]]`, `![](${image_map[img.name]})`)
}
}
}

content = content.replaceAll('','')
console.log(content)
const path = `source/_posts/${activeFile.name}`;
await uploadFileToGitHub(owner, repo, path, base64EncodeUnicode(content))
await triggerHexoDeploy(owner, repo, action)
new Notice("博客更新成功")

欢迎关注我的其它发布渠道