198 lines
5.7 KiB
JavaScript
198 lines
5.7 KiB
JavaScript
// 复合后缀兼容修改:只生成计划并持久化原值;真正修改 Eagle 字段由插件在用户确认后执行。
|
|
(function () {
|
|
'use strict';
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const STORE_VERSION = 1;
|
|
|
|
function fullName(name, ext) {
|
|
return String(name || '') + (ext ? '.' + ext : '');
|
|
}
|
|
|
|
/** 返回需要展示给用户的字段变更;不需要修改或被安全条件阻断时返回 null / blocked。 */
|
|
function planChange(item, eagleItem, splitCompoundName) {
|
|
const sourceName = String((item && item.entryName) || '');
|
|
const currentName = String((eagleItem && eagleItem.name) || '');
|
|
const currentExt = String((eagleItem && eagleItem.ext) || '');
|
|
const currentFileName = fullName(currentName, currentExt);
|
|
if (!sourceName || currentFileName !== sourceName) {
|
|
return {
|
|
blocked: true,
|
|
reason: 'sourceMismatch',
|
|
sourceName,
|
|
currentName,
|
|
currentExt,
|
|
currentFileName,
|
|
};
|
|
}
|
|
const parts = splitCompoundName(sourceName);
|
|
if (!parts || (currentName === parts.name && currentExt === parts.ext)) return null;
|
|
if (fullName(parts.name, parts.ext) !== sourceName) {
|
|
return {
|
|
blocked: true,
|
|
reason: 'sourceMismatch',
|
|
sourceName,
|
|
currentName,
|
|
currentExt,
|
|
currentFileName,
|
|
};
|
|
}
|
|
return {
|
|
sourceName,
|
|
currentName,
|
|
currentExt,
|
|
currentFileName,
|
|
newName: parts.name,
|
|
newExt: parts.ext,
|
|
newFileName: fullName(parts.name, parts.ext),
|
|
};
|
|
}
|
|
|
|
function emptyStore() {
|
|
return { version: STORE_VERSION, entries: {} };
|
|
}
|
|
|
|
function readStore(file) {
|
|
try {
|
|
const data = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
if (!data || data.version !== STORE_VERSION || !data.entries || typeof data.entries !== 'object') {
|
|
throw new Error('备份文件格式不正确');
|
|
}
|
|
return data;
|
|
} catch (error) {
|
|
if (error && error.code === 'ENOENT') return emptyStore();
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
function writeStore(file, data) {
|
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
const temp = file + '.' + process.pid + '.tmp';
|
|
fs.writeFileSync(temp, JSON.stringify(data, null, 2) + '\n', 'utf8');
|
|
fs.renameSync(temp, file);
|
|
}
|
|
|
|
function createStore(userDataPath) {
|
|
const file = path.join(userDataPath, 'SpineThumbnailGenerateServer', 'compound-name-backups.json');
|
|
return {
|
|
file,
|
|
list() {
|
|
return Object.values(readStore(file).entries)
|
|
.sort((left, right) => (right.updatedAt || 0) - (left.updatedAt || 0));
|
|
},
|
|
remember(change) {
|
|
const data = readStore(file);
|
|
if (!data.entries[change.itemId]) {
|
|
data.entries[change.itemId] = {
|
|
itemId: change.itemId,
|
|
sourceName: change.sourceName,
|
|
folderPath: change.folderPath || '',
|
|
originalName: change.currentName,
|
|
originalExt: change.currentExt,
|
|
appliedName: change.newName,
|
|
appliedExt: change.newExt,
|
|
updatedAt: Date.now(),
|
|
};
|
|
writeStore(file, data);
|
|
}
|
|
return data.entries[change.itemId];
|
|
},
|
|
remove(itemId) {
|
|
const data = readStore(file);
|
|
if (!data.entries[itemId]) return;
|
|
delete data.entries[itemId];
|
|
writeStore(file, data);
|
|
},
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 为恢复对话框生成可审计列表,并标出哪些条目允许恢复。
|
|
* 用户后来亲自改过的字段不会被覆盖。
|
|
*/
|
|
async function inspectBackups(store, getItem, sourceExists) {
|
|
const entries = store.list();
|
|
const rows = [];
|
|
for (const entry of entries) {
|
|
let item = null;
|
|
try {
|
|
item = await getItem(entry.itemId);
|
|
if (!item) {
|
|
let sourcePresent = false;
|
|
try {
|
|
sourcePresent = sourceExists ? await sourceExists(entry) : false;
|
|
} catch (error) { /* 检查失败时保守按缺失处理 */ }
|
|
if (sourcePresent) {
|
|
rows.push({
|
|
entry,
|
|
item: null,
|
|
currentName: entry.appliedName,
|
|
currentExt: entry.appliedExt,
|
|
status: 'pluginApplied',
|
|
});
|
|
} else {
|
|
rows.push({ entry, item: null, status: 'missing' });
|
|
}
|
|
continue;
|
|
}
|
|
const currentName = String(item.name || '');
|
|
const currentExt = String(item.ext || '');
|
|
if (currentName === entry.originalName && currentExt === entry.originalExt) {
|
|
rows.push({ entry, item, currentName, currentExt, status: 'alreadyOriginal' });
|
|
continue;
|
|
}
|
|
if (currentName !== entry.appliedName || currentExt !== entry.appliedExt) {
|
|
rows.push({ entry, item, currentName, currentExt, status: 'changed' });
|
|
continue;
|
|
}
|
|
rows.push({ entry, item, currentName, currentExt, status: 'ready' });
|
|
} catch (error) {
|
|
rows.push({ entry, item, status: 'error' });
|
|
}
|
|
}
|
|
return rows;
|
|
}
|
|
|
|
async function restoreBackups(store, selectedItemIds, getItem, saveItem) {
|
|
const selected = new Set(selectedItemIds || []);
|
|
const entries = store.list().filter((entry) => selected.has(entry.itemId));
|
|
const result = { total: entries.length, success: 0, skipped: 0, failed: 0 };
|
|
for (const entry of entries) {
|
|
let item = null;
|
|
try {
|
|
item = await getItem(entry.itemId);
|
|
if (!item) {
|
|
result.skipped += 1;
|
|
continue;
|
|
}
|
|
const currentName = String(item.name || '');
|
|
const currentExt = String(item.ext || '');
|
|
if (currentName === entry.originalName && currentExt === entry.originalExt) {
|
|
store.remove(entry.itemId);
|
|
result.success += 1;
|
|
continue;
|
|
}
|
|
if (currentName !== entry.appliedName || currentExt !== entry.appliedExt) {
|
|
result.skipped += 1;
|
|
continue;
|
|
}
|
|
item.name = entry.originalName;
|
|
item.ext = entry.originalExt;
|
|
await saveItem(item);
|
|
store.remove(entry.itemId);
|
|
result.success += 1;
|
|
} catch (error) {
|
|
result.failed += 1;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
const api = { fullName, planChange, createStore, inspectBackups, restoreBackups, STORE_VERSION };
|
|
if (typeof window !== 'undefined') window.SpineThumbCompoundCompat = api;
|
|
else if (typeof module !== 'undefined' && module.exports) module.exports = api;
|
|
|
|
})();
|