library API:资源库级操作
前面几章都在处理「资源库里的东西」,这一节处理「资源库本身」。Eagle 的 library 模块很小——一个方法加三个属性——但对插件的健壮性影响不小,尤其是资源库切换这个场景。
一个方法,三个属性
// 方法:拿到详细信息
const data = await eagle.library.info();
// 属性:直接读,不用 await
console.log(eagle.library.name); // 'test'
console.log(eagle.library.path); // 'C:\Users\User\Pictures\Design.library'
console.log(eagle.library.modificationTime); // 1681281134495
三者的区别在于信息量:
| 成员 | 类型 | 同步性 | 内容 |
|---|---|---|---|
eagle.library.name |
string | 直接读 | 资源库名称 |
eagle.library.path |
string | 直接读 | 资源库所在绝对路径 |
eagle.library.modificationTime |
number | 直接读 | 最后修改时间戳 |
eagle.library.info() |
Promise (Object) | 需 await | 详细信息,含文件夹、智能文件夹、标签群组等 |
属性是同步的普通取值,info() 是异步的。这不是随意的设计——info() 要把文件夹树、智能文件夹、标签群组这些结构化数据组装出来,量级和成本都不同,所以走 Promise。
拿到完整信息之后
eagle.library.info() 的返回值是一个对象,包含当前资源库的各类索引信息。它是「一次性了解资源库全貌」的最佳入口:
eagle.onPluginCreate(async (plugin) => {
const info = await eagle.library.info();
// info 里能看到资源库内的文件夹、智能文件夹、标签群组等结构信息
console.log('资源库详情:', info);
// 想知道用户安装了什么,直接看应用信息
eagle.log.info(`当前资源库: ${eagle.library.name},路径 ${eagle.library.path}`);
});
什么时候该用它?当你需要一次性统计资源库规模、给用户展示「当前库里有 X 个文件夹、Y 个智能文件夹」这类概览信息时,用属性零成本,用 info() 一次拿全。
关键场景:资源库切换
Eagle 支持多资源库,用户随时可能切换。这件事对插件的影响比对用户大得多:所有 item、folder 的 id 都是挂在特定资源库下的,切换之后它们全部失效。
后台服务插件是最容易被这个坑到的——它随应用启动,如果用户在你处理任务的中途切换了资源库,你的缓存里全是无效 id:
eagle.onPluginCreate(async (plugin) => {
let libraryPath = eagle.library.path;
const scan = async () => {
const info = await eagle.library.info();
const folders = info.folders || [];
eagle.log.info(`资源库 ${eagle.library.name} 共 ${folders.length} 个文件夹`);
};
await scan();
// 资源库切换时重新扫一遍
eagle.onLibraryChanged((newLibraryPath) => {
if (newLibraryPath !== libraryPath) {
libraryPath = newLibraryPath;
eagle.log.warn(`资源库已切换到 ${newLibraryPath},清空缓存并重扫`);
clearCache();
scan();
}
});
});
function clearCache() {
// 把模块内缓存的 folderId / itemId 全部作废
}
这个「缓存失效 + 重扫」的动作如果不做,后果是隐蔽的:插件继续拿着上一个库的 id 去查询,拿到的是空结果或者错位的数据。官方在背景服务插件的文档里专门写了警告:需要依赖相对的资源库路径时,透过 onLibraryChanged 做出对应调整,避免程序执行过程发生错误。
拿到路径之后
eagle.library.path 的用处不止显示。很多原生操作需要它作为基准:
const fs = require('fs');
const path = require('path');
eagle.onPluginCreate(async () => {
// 元数据文件所在目录
const metadataDir = path.join(eagle.library.path, 'metadata');
// 检查某个 ID 是否属于当前库
const belongsHere = (id) => fs.existsSync(path.join(metadataDir, id + '.json'));
});
有了库路径,你甚至能读到 Eagle 的原始元数据文件。但官方在多处强调:即便能读,也不建议直接改——应该用 save()。直接写文件绕过了主应用的索引维护,会导致 UI 显示和数据实际内容不一致。
路径拼接务必用 path.join,并配合递归建目录:
const dest = path.join(eagle.library.path, 'exports', 'batch01');
fs.mkdirSync(dest, { recursive: true });
手写斜杠拼接在 Windows 上侥幸能跑,在 macOS 上会直接出问题。而 Eagle 同时跑在这两个系统上,这类问题往往到跨平台测试才暴露。
一个实用组合
把 library 和 item 的计数能力组合起来,能做出资源库概览面板:
eagle.onPluginCreate(async () => {
const total = await eagle.item.countAll();
const unfiled = await eagle.item.count({ isUnfiled: true });
const panels = [
{ label: '资源库', value: eagle.library.name },
{ label: '素材总数', value: total },
{ label: '未分类', value: unfiled }
];
document.querySelector('#panel').innerHTML = panels
.map(p => `<div class="row"><span>${p.label}</span><b>${p.value}</b></div>`)
.join('');
});
注意这里全用 count() 系列而不是 get().length——countAll() 和 count() 都是官方做过性能优化的实现,在大库上比拉全量数据再数长度快得多。
开发流程
- 启动时读一次
eagle.library.name和path,存进模块级变量。 - 注册
eagle.onLibraryChanged,回调里比对路径并清空所有缓存。 - 需要概览统计时用
countAll()加count(),不要getAll()。 - 需要结构化索引(文件夹树、智能文件夹、标签群组)时调
info()。 - 任何本地文件操作都以
path.join(eagle.library.path, ...)现算路径为准。 - 用
eagle.log记录资源库切换事件,方便排查跨库问题。
常见坑
缓存了 item id 却没监听资源库切换。 切库后这些 id 全部无效,插件会安静地返回空结果,没有任何报错。监听
eagle.onLibraryChanged,在回调里清空所有缓存。把
eagle.library.path当成固定值。 它会随用户切换资源库而变。任何在启动时算一次然后长期使用的绝对路径,都是定时炸弹。路径要每次用的时候现算。延伸注意: * 直接改
metadata目录里的文件。 能读到不等于该改。所有写入走save(),否则索引和数据会不一致,审核也明确建议避免直接改资源库文件。整理人:WorkBuddy
