剪切修改
This commit is contained in:
1 parent
1dcb27ccd9
commit
1b67933cac
5 files changed
+358
-33
No files matched your search
+71
-9
@@ -22,7 +22,7 @@ const renderJobs = new Map();
|
||||
const refreshJobs = new Map();
|
||||
|
||||
/** 插件代码版本:每次改动递增,用于确认 Eagle 是否加载到最新代码 */
|
||||
const VERSION = 'v7';
|
||||
const VERSION = 'v10';
|
||||
|
||||
function coordinationKey(src) {
|
||||
const root = utils.findLibraryRoot(src) || path.dirname(path.dirname(src));
|
||||
@@ -61,24 +61,25 @@ async function runDiscovery(src, retries, delay) {
|
||||
let previousSignature = null;
|
||||
let stableCount = 0;
|
||||
for (let i = 0; i < retries; i++) {
|
||||
// 前 5 秒只接受同批资源;之后允许在同一个 Eagle 文件夹内补齐分批拖入的链路。
|
||||
// 前台缩略图回调只接受同批资源,绝不借用库里旧的同名 atlas/贴图。
|
||||
// 分批拖入的跨批次补齐统一交给后台任务处理。
|
||||
assets = utils.discoverAssets(src, {
|
||||
strictStem: true,
|
||||
preferCompatibleJson: true,
|
||||
allowCrossBatch: i >= Math.min(20, retries - 1),
|
||||
allowCrossBatch: false,
|
||||
});
|
||||
const signature = assetSignature(assets);
|
||||
if (signature && signature === previousSignature) stableCount += 1;
|
||||
else stableCount = signature ? 1 : 0;
|
||||
previousSignature = signature;
|
||||
if (stableCount >= 3) return assets;
|
||||
await sleep(delay);
|
||||
if (i < retries - 1) await sleep(delay);
|
||||
}
|
||||
return assets;
|
||||
}
|
||||
|
||||
/** 同一资源组的 atlas/json/skel 回调共享发现任务,避免大批量导入时重复全库扫描。 */
|
||||
async function discoverWithRetry(src, retries = 48, delay = 250) {
|
||||
async function discoverWithRetry(src, retries = 5, delay = 120) {
|
||||
const key = coordinationKey(src);
|
||||
if (discoveryJobs.has(key)) return discoveryJobs.get(key);
|
||||
const job = runDiscovery(src, retries, delay);
|
||||
@@ -90,6 +91,17 @@ async function discoverWithRetry(src, retries = 48, delay = 250) {
|
||||
}
|
||||
}
|
||||
|
||||
/** JSON 仍在落盘时只做极短等待;普通 JSON 一经确认便立即退出 Spine 流程。 */
|
||||
async function classifyJsonBriefly(src) {
|
||||
let kind = 'pending';
|
||||
for (let i = 0; i < 3; i++) {
|
||||
kind = utils.classifySkeletonJson(src);
|
||||
if (kind !== 'pending') return kind;
|
||||
if (i < 2) await sleep(80);
|
||||
}
|
||||
return kind;
|
||||
}
|
||||
|
||||
/** 同一份稳定资源只渲染一次,atlas/json/skel 并发回调共享输出 Buffer。 */
|
||||
async function renderThumbnailShared(assets, options) {
|
||||
const key = assetSignature(assets) + '|' + options.version + '|' + options.runtimeDir + '|' + options.size;
|
||||
@@ -138,6 +150,18 @@ module.exports = async ({ src, dest, item }) => {
|
||||
return resolve(item);
|
||||
}
|
||||
|
||||
// manifest 只能按扩展名接管 JSON,不能按文件内容过滤。因此先做低成本内容判定:
|
||||
// 普通 JSON 立即生成中性缩略图,不扫描资源库,也不启动后台补刷。
|
||||
if (srcExt === '.json') {
|
||||
const jsonKind = await classifyJsonBriefly(src);
|
||||
if (jsonKind === 'other') {
|
||||
log('普通 JSON,跳过 Spine 资源发现: ' + src);
|
||||
await writeNeutralJsonPlaceholder(dest);
|
||||
item.width = 400; item.height = 400;
|
||||
return resolve(item);
|
||||
}
|
||||
}
|
||||
|
||||
let assets = await discoverWithRetry(src);
|
||||
|
||||
// 找不到完整 Spine 资源链(骨骼 + atlas + atlas 引用的全部贴图)
|
||||
@@ -198,7 +222,7 @@ module.exports = async ({ src, dest, item }) => {
|
||||
log('renderThumbnail 第 ' + (attempt + 1) + ' 次失败: ' + (e && e.message) + ',重新等待同批资源...');
|
||||
if (attempt === 2) break;
|
||||
await sleep(500);
|
||||
assets = await discoverWithRetry(src, 16, 250);
|
||||
assets = await discoverWithRetry(src, 4, 150);
|
||||
if (!assets.skeletonPath || !assets.atlasPath || !assetSignature(assets)) continue;
|
||||
version = utils.detectVersion(assets.skeletonPath);
|
||||
runtimeDir = utils.chooseRuntimeDir(version, assets.isBinary);
|
||||
@@ -325,9 +349,11 @@ function scheduleBackgroundReconcile(src) {
|
||||
const job = (async () => {
|
||||
let previousSignature = null;
|
||||
let stableCount = 0;
|
||||
// 最长观察约 10 分钟,覆盖大型文件夹分批落盘;每个资源组只有一个任务。
|
||||
for (let i = 0; i < 300; i++) {
|
||||
await sleep(2000);
|
||||
// 前几秒快速观察,随后降低频率;总计约两分钟。若更晚补拖第三个文件,
|
||||
// 新文件自己的缩略图回调会再次触发同一资源链发现,无需常驻轮询十分钟。
|
||||
for (let i = 0; i < 36; i++) {
|
||||
const delay = i < 8 ? 1000 : (i < 20 ? 2500 : 5000);
|
||||
await sleep(delay);
|
||||
const assets = utils.discoverAssets(src, {
|
||||
strictStem: true,
|
||||
preferCompatibleJson: true,
|
||||
@@ -371,6 +397,42 @@ function scheduleBackgroundReconcile(src) {
|
||||
.finally(() => { if (backgroundJobs.get(key) === job) backgroundJobs.delete(key); });
|
||||
}
|
||||
|
||||
/** 普通 JSON 使用中性文件图形,避免错误显示 Spine badge。 */
|
||||
async function writeNeutralJsonPlaceholder(dest) {
|
||||
const size = 400;
|
||||
if (render.canvasLib) {
|
||||
try {
|
||||
const canvas = render.canvasLib.createCanvas(size, size);
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.fillStyle = '#202126';
|
||||
ctx.fillRect(0, 0, size, size);
|
||||
ctx.fillStyle = '#555963';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(112, 72);
|
||||
ctx.lineTo(244, 72);
|
||||
ctx.lineTo(288, 116);
|
||||
ctx.lineTo(288, 328);
|
||||
ctx.lineTo(112, 328);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
ctx.fillStyle = '#777c87';
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(244, 72);
|
||||
ctx.lineTo(244, 116);
|
||||
ctx.lineTo(288, 116);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
fs.writeFileSync(dest, canvas.toBuffer('image/png'));
|
||||
return;
|
||||
} catch (e) { /* fall through */ }
|
||||
}
|
||||
const pixels = Buffer.alloc(size * size * 4);
|
||||
for (let i = 0; i < pixels.length; i += 4) {
|
||||
pixels[i] = 32; pixels[i + 1] = 33; pixels[i + 2] = 38; pixels[i + 3] = 255;
|
||||
}
|
||||
fs.writeFileSync(dest, encodePng(size, size, pixels));
|
||||
}
|
||||
|
||||
/** 将任意图片作为缩略图复制/转换为 PNG(PNG 直接拷贝,JPG/WebP 用 @napi-rs/canvas 转换) */
|
||||
async function copyImageAsThumb(src, dest) {
|
||||
const ext = path.extname(src).toLowerCase();
|
||||
|
||||
Reference in new issue
Block a user