缩略图自动生成背景服务插件
This commit is contained in:
1 parent
c28adf9fae
commit
065b6466f1
27 files changed
+81166
No files matched your search
@@ -0,0 +1,103 @@
|
||||
// Node 里用 require(跑测试),插件页面里用 <script> 标签加载;包成 IIFE 是为了
|
||||
// 让多个页面脚本各自的 const 不互相冲突。
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Eagle 没有暴露「素材导入完成」事件,所以用「目录静默检测」代替:
|
||||
// 监听素材目录,任何变化都重置防抖计时器,安静 QUIET_MS 之后才认为这一批拖入结束。
|
||||
//
|
||||
// 1 秒:足够合并连续写入。Eagle 的「重复添加提示」是主窗口里的 Angular 模态框,
|
||||
// 没有独立窗口句柄可检测,插件只能靠静默判断——那期间素材库是静止的,但重复文件
|
||||
// 还没入库,扫到的只是已入库的非重复素材。
|
||||
const QUIET_MS = 1000;
|
||||
// fs.watch 在网络盘/外置盘上可能漏事件,用低频兜底扫描补上。
|
||||
const FALLBACK_MS = 30000;
|
||||
|
||||
function createLibraryWatcher(options) {
|
||||
const onBatch = options.onBatch || (() => {});
|
||||
const onLog = options.onLog || (() => {});
|
||||
let imagesDir = null;
|
||||
let watcher = null;
|
||||
let quietTimer = null;
|
||||
let fallbackTimer = null;
|
||||
let known = null;
|
||||
|
||||
function listItemDirs() {
|
||||
try {
|
||||
return new Set(fs.readdirSync(imagesDir).filter((name) => name.endsWith('.info')));
|
||||
} catch (error) {
|
||||
onLog('读取素材目录失败:' + ((error && error.message) || error));
|
||||
return new Set();
|
||||
}
|
||||
}
|
||||
|
||||
/** 与上次快照比对,把新增的素材目录交给上层处理。 */
|
||||
function flush(reason) {
|
||||
if (!imagesDir || !known) return;
|
||||
const current = listItemDirs();
|
||||
const added = [...current].filter((name) => !known.has(name));
|
||||
known = current;
|
||||
onBatch({ added, total: current.size, reason });
|
||||
}
|
||||
|
||||
function scheduleFlush() {
|
||||
if (quietTimer) clearTimeout(quietTimer);
|
||||
quietTimer = setTimeout(() => {
|
||||
quietTimer = null;
|
||||
flush('quiet');
|
||||
}, QUIET_MS);
|
||||
}
|
||||
|
||||
function stop() {
|
||||
if (watcher) { watcher.close(); watcher = null; }
|
||||
if (quietTimer) { clearTimeout(quietTimer); quietTimer = null; }
|
||||
if (fallbackTimer) { clearInterval(fallbackTimer); fallbackTimer = null; }
|
||||
imagesDir = null;
|
||||
known = null;
|
||||
}
|
||||
|
||||
/** 监听指定素材库。首次快照里的素材视为历史数据,不处理。 */
|
||||
function start(libraryPath) {
|
||||
stop();
|
||||
imagesDir = path.join(libraryPath, 'images');
|
||||
if (!fs.existsSync(imagesDir)) {
|
||||
onLog('素材目录不存在:' + imagesDir);
|
||||
return false;
|
||||
}
|
||||
known = listItemDirs();
|
||||
onLog('监听 ' + imagesDir + '(现有 ' + known.size + ' 个素材视为历史,不处理)');
|
||||
try {
|
||||
watcher = fs.watch(imagesDir, { persistent: true }, () => scheduleFlush());
|
||||
} catch (error) {
|
||||
onLog('建立监听失败:' + ((error && error.message) || error));
|
||||
return false;
|
||||
}
|
||||
fallbackTimer = setInterval(() => {
|
||||
const current = listItemDirs();
|
||||
// 不能只比数量:同一次导里如果有增有删,数量可能刚好抵消。
|
||||
let changed = current.size !== known.size;
|
||||
if (!changed) {
|
||||
for (const name of current) {
|
||||
if (!known.has(name)) { changed = true; break; }
|
||||
}
|
||||
}
|
||||
if (changed) flush('fallback');
|
||||
}, FALLBACK_MS);
|
||||
return true;
|
||||
}
|
||||
|
||||
return { start, stop, flush, isRunning: () => !!watcher };
|
||||
}
|
||||
|
||||
// 同时支持两种加载方式:Node 里用 require(跑测试),插件页面里用 <script> 标签。
|
||||
// service 插件页面的 require 解析基准不是插件目录,所以页面里必须走 script 标签。
|
||||
const libraryWatcherApi = { createLibraryWatcher, QUIET_MS, FALLBACK_MS };
|
||||
// 优先挂 window:Eagle 插件页面里也存在 module 对象(它有 Node 模块系统),
|
||||
// 先判断 module 会让页面拿到模块却取不到 window 上的名字。Node 里没有 window,自然走 require 分支。
|
||||
if (typeof window !== 'undefined') window.SpineThumbLibraryWatcher = libraryWatcherApi;
|
||||
else if (typeof module !== 'undefined' && module.exports) module.exports = libraryWatcherApi;
|
||||
|
||||
})();
|
||||
Reference in new issue
Block a user