临时文件兜底

This commit is contained in:
tianmo committed 2026-09-21 10:06:53 +08:00
1 parent 08456f26cf
commit be058c6c80
3 files changed
+52 -3

No files matched your search

@@ -16,6 +16,24 @@ const QUIET_MS = 1000;
// fs.watch 在网络盘/外置盘上可能漏事件,用低频兜底扫描补上。
const FALLBACK_MS = 30000;
/** 删除指定目录中超过保留时间的本插件临时文件。 */
function cleanupStaleFiles(directory, prefix, maxAgeMs, now = Date.now()) {
let names;
try { names = fs.readdirSync(directory); } catch (error) { return 0; }
let removed = 0;
for (const name of names) {
if (!name.startsWith(prefix)) continue;
const file = path.join(directory, name);
try {
const stat = fs.lstatSync(file);
if (!stat.isFile() || now - stat.mtimeMs < maxAgeMs) continue;
fs.unlinkSync(file);
removed += 1;
} catch (error) { /* 单个文件失败不影响其余清理和监听 */ }
}
return removed;
}
function createLibraryWatcher(options) {
const onBatch = options.onBatch || (() => {});
const onLog = options.onLog || (() => {});
@@ -94,7 +112,7 @@ function createLibraryWatcher(options) {
// 同时支持两种加载方式:Node 里用 require(跑测试),插件页面里用 <script> 标签。
// service 插件页面的 require 解析基准不是插件目录,所以页面里必须走 script 标签。
const libraryWatcherApi = { createLibraryWatcher, QUIET_MS, FALLBACK_MS };
const libraryWatcherApi = { createLibraryWatcher, cleanupStaleFiles, QUIET_MS, FALLBACK_MS };
// 优先挂 window:Eagle 插件页面里也存在 module 对象(它有 Node 模块系统),
// 先判断 module 会让页面拿到模块却取不到 window 上的名字。Node 里没有 window,自然走 require 分支。
if (typeof window !== 'undefined') window.SpineThumbLibraryWatcher = libraryWatcherApi;
+5 -2
View File
@@ -2,7 +2,7 @@
// 每次改动递增。Eagle 里的 service 插件是启动时加载并常驻的,改完代码必须重新加载插件
// 才会生效;状态栏会显示这个版本号,方便确认加载的到底是不是最新代码。
const VERSION = 'v60';
const VERSION = 'v61';
const path = require('path');
const os = require('os');
@@ -19,7 +19,7 @@ if (missing.length) {
document.getElementById('status').textContent = '本地模块未加载:' + missing.join(', ');
throw new Error('本地模块未加载,请检查 index.html 里 script 的顺序:' + missing.join(', '));
}
const { createLibraryWatcher } = window.SpineThumbLibraryWatcher;
const { createLibraryWatcher, cleanupStaleFiles } = window.SpineThumbLibraryWatcher;
const { scanSpineItems } = window.SpineThumbSpineAssets;
const { renderThumbnailAuto, withTimeout } = window.SpineThumbRenderer;
const settingsStore = window.SpineThumbSettings;
@@ -42,6 +42,7 @@ const stats = { batches: 0, added: 0, spine: 0, complete: 0, generated: 0, skipp
const PROGRESS_MIN_DYNAMIC = 10;
// 弹窗上的「停止」按钮会把动作写进这个文件,宿主读回来执行暂停/继续/取消。
const CONTROL_FILE = path.join(os.tmpdir(), 'spine-thumbnail-progress-control.json');
const STALE_TEMP_AGE_MS = 24 * 60 * 60 * 1000;
let settings = { ...settingsStore.DEFAULTS };
let libraryPath = null;
let imagesDir = null;
@@ -290,6 +291,7 @@ async function processQueue() {
runStats = null;
runShowsProgress = false;
if (run && showsProgress) finishRun(run);
cleanupStaleFiles(os.tmpdir(), 'spine-thumb-', STALE_TEMP_AGE_MS);
}
}
@@ -492,6 +494,7 @@ filtersEl.addEventListener('click', (event) => {
// 先确认脚本本身执行到了这里,再谈 Eagle 事件有没有来。
document.getElementById('status').textContent = '脚本已加载 ' + VERSION + ',等待 Eagle 事件…';
cleanupStaleFiles(os.tmpdir(), 'spine-thumb-', STALE_TEMP_AGE_MS);
if (typeof eagle === 'undefined') {
setStatus('eagle API 未注入');
@@ -0,0 +1,28 @@
'use strict';
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { cleanupStaleFiles } = require('../js/library-watcher.js');
const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'spine-thumb-cleanup-'));
const now = Date.now();
const old = path.join(directory, 'spine-thumb-old.webp');
const fresh = path.join(directory, 'spine-thumb-fresh.webp');
const unrelated = path.join(directory, 'other-old.webp');
try {
for (const file of [old, fresh, unrelated]) fs.writeFileSync(file, 'x');
const oldTime = new Date(now - 25 * 60 * 60 * 1000);
fs.utimesSync(old, oldTime, oldTime);
fs.utimesSync(unrelated, oldTime, oldTime);
assert.equal(cleanupStaleFiles(directory, 'spine-thumb-', 24 * 60 * 60 * 1000, now), 1);
assert.equal(fs.existsSync(old), false);
assert.equal(fs.existsSync(fresh), true);
assert.equal(fs.existsSync(unrelated), true);
console.log('PASS: 只清理超过 24 小时的本插件临时文件');
} finally {
fs.rmSync(directory, { recursive: true, force: true });
}