picgo-plugin-gitee 2.1.1 → 2.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/src/index.js +198 -82
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "picgo-plugin-gitee",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.3",
|
|
4
4
|
"description": "picgo uploader for gitee",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"release:patch": "npm run bump:patch && npm publish",
|
|
32
32
|
"release:minor": "npm run bump:minor && npm publish",
|
|
33
33
|
"release:major": "npm run bump:major && npm publish",
|
|
34
|
-
"prepublishOnly": "node --check src/index.js"
|
|
34
|
+
"prepublishOnly": "node --check src/index.js",
|
|
35
|
+
"publish": "npm publish"
|
|
35
36
|
},
|
|
36
37
|
"keywords": [
|
|
37
38
|
"picgo",
|
package/src/index.js
CHANGED
|
@@ -22,14 +22,87 @@ module.exports = (ctx) => {
|
|
|
22
22
|
const guiMenu = (ctx) => {
|
|
23
23
|
return [
|
|
24
24
|
{
|
|
25
|
-
label: "
|
|
25
|
+
label: "同步 Gitee 远程文件到相册",
|
|
26
26
|
async handle(ctx, guiApi) {
|
|
27
|
-
|
|
27
|
+
// picgo GUI 有时会把 async handle 里的异常静默吞掉。
|
|
28
|
+
// 手动加 try/catch 确保任何错误都能反馈到 UI 和日志。
|
|
29
|
+
const started = Date.now();
|
|
30
|
+
log("info", "[同步]按钮被点击");
|
|
31
|
+
log("info", `[同步]ctx 类型: ${typeof ctx}, guiApi 类型: ${typeof guiApi}`);
|
|
32
|
+
log(
|
|
33
|
+
"info",
|
|
34
|
+
`[同步]guiApi.galleryDB: ${guiApi && !!guiApi.galleryDB}, guiApi.showNotification: ${guiApi && !!guiApi.showNotification}`
|
|
35
|
+
);
|
|
36
|
+
try {
|
|
37
|
+
await syncRemoteToGallery(ctx, guiApi);
|
|
38
|
+
log("success", `[同步]完成,耗时 ${Date.now() - started}ms`);
|
|
39
|
+
} catch (err) {
|
|
40
|
+
const msg = err && err.message ? err.message : String(err);
|
|
41
|
+
const stack = err && err.stack ? err.stack : "";
|
|
42
|
+
log("error", "[同步]失败:" + msg);
|
|
43
|
+
if (stack) log("error", stack);
|
|
44
|
+
// 通道 1:ctx.emit('notification') — PicGo 主窗口消息中心(最可靠)
|
|
45
|
+
try {
|
|
46
|
+
if (ctx && ctx.emit) {
|
|
47
|
+
ctx.emit("notification", {
|
|
48
|
+
title: "Gitee 同步失败",
|
|
49
|
+
body: msg,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
} catch (e) {
|
|
53
|
+
log("error", "[同步]ctx.emit 失败:" + e.message);
|
|
54
|
+
}
|
|
55
|
+
// 通道 2:showMessageBox — 系统弹窗,最醒目
|
|
56
|
+
if (guiApi && guiApi.showMessageBox) {
|
|
57
|
+
try {
|
|
58
|
+
guiApi.showMessageBox({
|
|
59
|
+
type: "error",
|
|
60
|
+
title: "Gitee 同步失败",
|
|
61
|
+
message: msg,
|
|
62
|
+
detail: stack ? stack.split("\n").slice(0, 3).join("\n") : "",
|
|
63
|
+
});
|
|
64
|
+
} catch (e) {
|
|
65
|
+
log("error", "[同步]showMessageBox 失败:" + e.message);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// 通道 3:showNotification — 系统通知(依赖 OS 权限)
|
|
69
|
+
if (guiApi && guiApi.showNotification) {
|
|
70
|
+
try {
|
|
71
|
+
guiApi.showNotification({
|
|
72
|
+
title: "Gitee 同步失败",
|
|
73
|
+
body: msg,
|
|
74
|
+
});
|
|
75
|
+
} catch (e) {
|
|
76
|
+
log("error", "[同步]showNotification 失败:" + e.message);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
28
80
|
},
|
|
29
81
|
},
|
|
30
82
|
];
|
|
31
83
|
};
|
|
32
84
|
|
|
85
|
+
// 统一的日志输出,兼容 picgo-core (ctx.log.info) 和 picgo GUI (ctx.log 可能行为不同)
|
|
86
|
+
// 同时打 console 兜底,确保 Electron 主进程也能看到
|
|
87
|
+
const log = function (level, msg) {
|
|
88
|
+
try {
|
|
89
|
+
// picgo 的 ctx.log 支持 info / warn / success / error
|
|
90
|
+
const fn = ctx.log && (ctx.log[level] || ctx.log.info);
|
|
91
|
+
if (fn) fn.call(ctx.log, msg);
|
|
92
|
+
} catch (e) {
|
|
93
|
+
// 静吞
|
|
94
|
+
}
|
|
95
|
+
// 兜底:直接打到 stdout,这样无论 picgo GUI 把日志写到哪,这里都至少有一条记录
|
|
96
|
+
// (用户终端如果通过 npm 链接加载插件,能看到;GUI 主进程 console 也能看到)
|
|
97
|
+
if (typeof console !== "undefined") {
|
|
98
|
+
try {
|
|
99
|
+
console.log("[picgo-plugin-gitee]", level.toUpperCase(), msg);
|
|
100
|
+
} catch (e) {
|
|
101
|
+
// 静吞
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
33
106
|
// 相册里只保留图片文件,避免把 README/配置文件等也拉进来
|
|
34
107
|
const IMAGE_EXTS = new Set([
|
|
35
108
|
"jpg", "jpeg", "png", "gif", "webp", "bmp", "svg",
|
|
@@ -90,66 +163,54 @@ module.exports = (ctx) => {
|
|
|
90
163
|
|
|
91
164
|
// 把 Gitee 远程文件写入 PicGo GUI 相册。
|
|
92
165
|
// 关键点:每条记录带 type="gitee",这样从相册删除时会触发 onRemove 真正删除 gitee 上的文件。
|
|
166
|
+
// 异常统一向上抛出,由 guiMenu.handle 里的 catch 统一弹窗。
|
|
93
167
|
const syncRemoteToGallery = async function (ctx, guiApi) {
|
|
94
168
|
if (!guiApi || !guiApi.galleryDB) {
|
|
95
|
-
|
|
96
|
-
"[同步操作]失败当前:当前不在 PicGo GUI 环境(galleryDB 不可用)"
|
|
97
|
-
);
|
|
98
|
-
return;
|
|
169
|
+
throw new Error("当前不在 PicGo GUI 环境(galleryDB 不可用)");
|
|
99
170
|
}
|
|
171
|
+
log("info", "[同步]开始检查配置");
|
|
100
172
|
let userConfig;
|
|
101
173
|
try {
|
|
102
174
|
userConfig = getUserConfig();
|
|
103
175
|
} catch (err) {
|
|
104
|
-
|
|
105
|
-
guiApi.showNotification({
|
|
106
|
-
title: "Gitee 同步失败",
|
|
107
|
-
body: "请先在插件设置中填写 owner / repo / token",
|
|
108
|
-
});
|
|
109
|
-
return;
|
|
176
|
+
throw new Error("请先在插件设置中填写 owner / repo / token");
|
|
110
177
|
}
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
!userConfig.repo ||
|
|
114
|
-
!userConfig.token
|
|
115
|
-
) {
|
|
116
|
-
guiApi.showNotification &&
|
|
117
|
-
guiApi.showNotification({
|
|
118
|
-
title: "Gitee 同步失败",
|
|
119
|
-
body: "请先在插件设置中填写 owner / repo / token",
|
|
120
|
-
});
|
|
121
|
-
return;
|
|
178
|
+
if (!userConfig.owner || !userConfig.repo || !userConfig.token) {
|
|
179
|
+
throw new Error("请先在插件设置中填写 owner / repo / token");
|
|
122
180
|
}
|
|
181
|
+
log(
|
|
182
|
+
"info",
|
|
183
|
+
`[同步]配置 OK: owner=${userConfig.owner} repo=${userConfig.repo} path=${userConfig.path || "(root)"}`
|
|
184
|
+
);
|
|
123
185
|
|
|
124
|
-
guiApi
|
|
125
|
-
guiApi.showNotification({
|
|
126
|
-
title: "正在同步",
|
|
127
|
-
body: "正在从 Gitee 拉取远程文件...",
|
|
128
|
-
});
|
|
186
|
+
notify("success", ctx, guiApi, "正在同步", "正在从 Gitee 拉取远程文件...");
|
|
129
187
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
} catch (err) {
|
|
134
|
-
guiApi.showNotification &&
|
|
135
|
-
guiApi.showNotification({
|
|
136
|
-
title: "Gitee 同步失败",
|
|
137
|
-
body: err.message,
|
|
138
|
-
});
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
188
|
+
log("info", "[同步]开始递归拉取 Gitee 文件列表");
|
|
189
|
+
const remoteFiles = await listAllRemoteFiles(userConfig);
|
|
190
|
+
log("info", `[同步]仓库共 ${remoteFiles.length} 个图片文件`);
|
|
141
191
|
|
|
142
192
|
if (remoteFiles.length === 0) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
193
|
+
log("warn", "[同步]仓库里没有任何图片文件");
|
|
194
|
+
notify(
|
|
195
|
+
"success",
|
|
196
|
+
ctx,
|
|
197
|
+
guiApi,
|
|
198
|
+
"Gitee 同步完成",
|
|
199
|
+
"仓库里没有任何图片文件"
|
|
200
|
+
);
|
|
148
201
|
return;
|
|
149
202
|
}
|
|
150
203
|
|
|
151
|
-
|
|
152
|
-
|
|
204
|
+
log("info", "[同步]读取本地相册");
|
|
205
|
+
// galleryDB.get() 返回 { total, data }(picgo/store 格式),
|
|
206
|
+
// 也可能直接返回数组(早期版本兼容)。两种都处理。
|
|
207
|
+
const getResult = await guiApi.galleryDB.get();
|
|
208
|
+
const existing = Array.isArray(getResult)
|
|
209
|
+
? getResult
|
|
210
|
+
: getResult && Array.isArray(getResult.data)
|
|
211
|
+
? getResult.data
|
|
212
|
+
: [];
|
|
213
|
+
log("info", `[同步]本地相册已有 ${existing.length} 条`);
|
|
153
214
|
const existingUrls = new Set(existing.map((x) => x.imgUrl));
|
|
154
215
|
const newItems = remoteFiles
|
|
155
216
|
.filter((f) => !existingUrls.has(f.imgUrl))
|
|
@@ -158,30 +219,78 @@ module.exports = (ctx) => {
|
|
|
158
219
|
imgUrl: f.imgUrl,
|
|
159
220
|
type: uploadedName,
|
|
160
221
|
sha: f.sha,
|
|
161
|
-
// picgo 相册的可选字段
|
|
162
222
|
extname: f.fileName.split(".").pop(),
|
|
163
223
|
}));
|
|
224
|
+
log(
|
|
225
|
+
"info",
|
|
226
|
+
`[同步]需要新增 ${newItems.length} 条(已存在 ${remoteFiles.length - newItems.length} 条)`
|
|
227
|
+
);
|
|
164
228
|
|
|
165
229
|
if (newItems.length === 0) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
230
|
+
notify(
|
|
231
|
+
"success",
|
|
232
|
+
ctx,
|
|
233
|
+
guiApi,
|
|
234
|
+
"Gitee 同步完成",
|
|
235
|
+
"没有新增文件,相册已是最新"
|
|
236
|
+
);
|
|
171
237
|
return;
|
|
172
238
|
}
|
|
173
239
|
|
|
240
|
+
log("info", "[同步]写入相册 insertMany");
|
|
174
241
|
await guiApi.galleryDB.insertMany(newItems);
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
242
|
+
log(
|
|
243
|
+
"success",
|
|
244
|
+
`[同步]完成:共 ${remoteFiles.length} 个,新增 ${newItems.length} 个`
|
|
245
|
+
);
|
|
246
|
+
notify(
|
|
247
|
+
"success",
|
|
248
|
+
ctx,
|
|
249
|
+
guiApi,
|
|
250
|
+
"Gitee 同步完成",
|
|
251
|
+
`新增 ${newItems.length} 个文件到相册(共发现 ${remoteFiles.length} 个)`
|
|
182
252
|
);
|
|
183
253
|
};
|
|
184
254
|
|
|
255
|
+
// 多通道通知:根据场景选用合适的通道。
|
|
256
|
+
// picgo 文档说明:ctx.emit('notification') 是给**失败**提示专用的通道;
|
|
257
|
+
// 成功时 picgo 主进程已经处理(gui 弹窗/消息中心),插件无需重复 emit。
|
|
258
|
+
// 所以 notify 函数有 success / fail 两种模式。
|
|
259
|
+
const notify = function (mode, ctx, guiApi, title, body) {
|
|
260
|
+
log(mode === "fail" ? "error" : "success", `[notify] ${title}: ${body}`);
|
|
261
|
+
|
|
262
|
+
if (mode === "fail") {
|
|
263
|
+
// 失败:ctx.emit(picgo 主窗口消息中心)+ showMessageBox 弹窗(最醒目)+ showNotification
|
|
264
|
+
try {
|
|
265
|
+
if (ctx && ctx.emit) {
|
|
266
|
+
ctx.emit("notification", { title, body });
|
|
267
|
+
}
|
|
268
|
+
} catch (e) {
|
|
269
|
+
log("error", "[notify] ctx.emit 失败:" + e.message);
|
|
270
|
+
}
|
|
271
|
+
try {
|
|
272
|
+
if (guiApi && guiApi.showMessageBox) {
|
|
273
|
+
guiApi.showMessageBox({
|
|
274
|
+
type: "error",
|
|
275
|
+
title: title,
|
|
276
|
+
message: body,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
} catch (e) {
|
|
280
|
+
log("error", "[notify] showMessageBox 失败:" + e.message);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// 成功和失败都触发系统通知(依赖 OS 权限)
|
|
285
|
+
try {
|
|
286
|
+
if (guiApi && guiApi.showNotification) {
|
|
287
|
+
log("11111", guiApi.showNotification({ title, body }));
|
|
288
|
+
}
|
|
289
|
+
} catch (e) {
|
|
290
|
+
log("error", "[notify] showNotification 失败:" + e.message);
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
|
|
185
294
|
const getHeaders = function () {
|
|
186
295
|
return {
|
|
187
296
|
"Content-Type": "application/json;charset=UTF-8",
|
|
@@ -264,13 +373,8 @@ module.exports = (ctx) => {
|
|
|
264
373
|
delete imgList[i].buffer;
|
|
265
374
|
}
|
|
266
375
|
|
|
267
|
-
//
|
|
268
|
-
|
|
269
|
-
ctx.emit("notification", {
|
|
270
|
-
title: "上传完成",
|
|
271
|
-
body: `成功 ${successCount} 张${failCount > 0 ? `,失败 ${failCount} 张` : ""}`,
|
|
272
|
-
});
|
|
273
|
-
}
|
|
376
|
+
// 上传成功:让 picgo 主进程自己处理(它会弹"上传成功"提示),
|
|
377
|
+
// 不重复 emit('notification')。这里只写日志 + 显示消息中心(如果失败)。
|
|
274
378
|
if (fails.length > 0) {
|
|
275
379
|
ctx.log.info("[上传操作]失败明细:" + JSON.stringify(fails));
|
|
276
380
|
}
|
|
@@ -303,7 +407,9 @@ module.exports = (ctx) => {
|
|
|
303
407
|
};
|
|
304
408
|
|
|
305
409
|
// trigger delete file
|
|
306
|
-
|
|
410
|
+
// trigger delete file
|
|
411
|
+
// 从 PicGo GUI 2.3.0 起,remove 事件的第二个参数是 guiApi(Electron 专属)
|
|
412
|
+
const onRemove = async function (files, guiApi) {
|
|
307
413
|
const rms = files.filter((each) => each.type === uploadedName);
|
|
308
414
|
if (rms.length === 0) {
|
|
309
415
|
return;
|
|
@@ -326,10 +432,13 @@ module.exports = (ctx) => {
|
|
|
326
432
|
"[删除操作]获取 sha 失败,跳过:" + each.imgUrl + " " + err.message
|
|
327
433
|
);
|
|
328
434
|
fails.push(`${each.fileName || each.imgUrl}: 获取 sha 失败`);
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
435
|
+
notify(
|
|
436
|
+
"fail",
|
|
437
|
+
ctx,
|
|
438
|
+
guiApi,
|
|
439
|
+
"删除失败",
|
|
440
|
+
`${each.fileName || each.imgUrl} 获取 sha 失败`
|
|
441
|
+
);
|
|
333
442
|
continue;
|
|
334
443
|
}
|
|
335
444
|
|
|
@@ -366,21 +475,28 @@ module.exports = (ctx) => {
|
|
|
366
475
|
err.message
|
|
367
476
|
);
|
|
368
477
|
fails.push(`${each.fileName || each.imgUrl}: ${err.message}`);
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
478
|
+
notify(
|
|
479
|
+
"fail",
|
|
480
|
+
ctx,
|
|
481
|
+
guiApi,
|
|
482
|
+
"删除失败",
|
|
483
|
+
`${each.fileName || each.imgUrl} ${err.message}`
|
|
484
|
+
);
|
|
373
485
|
}
|
|
374
486
|
}
|
|
375
487
|
|
|
376
|
-
//
|
|
488
|
+
// 删除成功:用 guiApi.showNotification 通知(picgo 不自动处理此通知)
|
|
377
489
|
if (successNames.length > 0) {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
490
|
+
try {
|
|
491
|
+
if (guiApi && guiApi.showNotification) {
|
|
492
|
+
guiApi.showNotification({
|
|
493
|
+
title: "删除完成",
|
|
494
|
+
body: `成功同步删除 ${successNames.length} 个`,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
} catch (e) {
|
|
498
|
+
log("error", "[删除操作]showNotification 失败:" + e.message);
|
|
499
|
+
}
|
|
384
500
|
}
|
|
385
501
|
if (fails.length > 0) {
|
|
386
502
|
ctx.log.info("[删除操作]失败明细:" + JSON.stringify(fails));
|