Metadata API
元数据读写、封面嵌入、批量操作、评分。共 10 个 API(含别名 metadata.removeField → metadata.removeTag 共享同一 handler)。
定位容器内的单曲
CUE、ISO 镜像与多轨文件都是一个文件路径下含多首曲目。所有读取方法用同一种方式定位其中一首:
- 在路径后追加
|subsong:N,例如D:\album.cue|subsong:2; - 或在普通路径之外另传
cueIndex: N。两者同时给出时以cueIndex为准。
编号沿用 foobar2000 的规则,CUE 从 1 开始:|subsong:1 是第一首。不带后缀的容器路径(或 |subsong:0)指向 subsong 0,而 CUE 中并不存在该编号,因此会返回 Failed to get track info —— 这是宿主的编号方式,不是请求写错了。
读取普通单轨文件无需后缀;|subsong:0 与不写等价。
读取
metadata.read
读取指定文件的元数据(结构化格式)。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
path | string | 是 | — | 支持 路径|subsong:N。 |
cueIndex | integer | 否 | -1 | 显式指定容器内曲目序号,优先级高于路径后缀。 |
读取链路会先剥离
|subsong:N后缀并 canonicalize 路径,再按解析出的 subsong 通过handle_create()读取 cached info;若 cached info 缺少关键元数据,则以同一 subsong 退回 direct file read。CUE / ISO 等多轨容器的寻址规则见定位容器内的单曲。
返回值: {"error":"...","info":"...","path":"...","success":true,"tags":"..."}
tags保留文件内原始字段名,不强制转为大写;如果你需要统一的大写扁平字段,请使用metadata.readByPath。
metadata.readRaw
(v1.4.1+)直接从文件读取元数据,绕过 metadb 缓存。
与 metadata.read 返回格式一致,但始终从磁盘文件直接解码读取,不走 foobar2000 metadb 内存缓存。适用于需要获取最新文件标签的场景(如刚写入标签后立即读回验证)。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
path | string | 是 | — | 支持 路径|subsong:N。 |
cueIndex | integer | 否 | -1 | 显式指定容器内曲目序号,优先级高于路径后缀。 |
返回值: {"error":"...","info":"...","path":"...","source":"...","success":true,"tags":"..."}
始终直接打开文件解码器读取,不受 metadb 缓存影响。
source字段固定为"file"。
const raw = await fb2k.invoke('metadata.readRaw', { path: 'E:\\Music\\song.flac' });
console.log(raw.tags.TITLE, raw.source); // "file"2
metadata.readByPath
(v1.1.0+)读取元数据(扁平格式)。
与 metadata.read 不同,此 API 返回扁平结构,所有标签键名转为大写;但两者共享同一条 fallback 读取链路。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
path | string | 是 | — | 支持 路径|subsong:N。 |
cueIndex | integer | 否 | -1 | 显式指定容器内曲目序号,优先级高于路径后缀。 |
返回值: {"TRACKNUMBER":"...","canonicalPath":"...","error":"...","path":"...","success":true}
若文件缺少
TRACKNUMBER标签,会尝试从文件名提取。若 cached info 不完整,会自动退回 direct file read。多轨容器寻址见定位容器内的单曲。
const meta = await fb2k.invoke('metadata.readByPath', { path: 'E:\\Music\\song.flac' });
console.log(meta.TITLE, meta.ARTIST, meta.DURATION);2
metadata.readBatch
(v1.1.11+)批量读取多个文件的元数据,包含所有标签和技术信息(大写键名)。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
paths | array | 是 | 每个元素都可带 |subsong:N 后缀,逐项独立解析。 |
本方法没有
cueIndex参数:批量调用中每个路径只能通过|subsong:N指定曲目。见定位容器内的单曲。
返回值:
{
"success": true,
"total": 3,
"successCount": 3,
"errorCount": 0,
"results": [
{ "path": "...", "success": true, "tags": { "TITLE": "...", "ARTIST": "..." } }
]
}2
3
4
5
6
7
8
9
const batch = await fb2k.invoke('metadata.readBatch', {
paths: ['E:\\Music\\a.flac', 'E:\\Music\\b.flac']
});
batch.results.forEach(r => {
if (r.success) console.log(r.tags.TITLE);
});2
3
4
5
6
metadata.probeBatchAsync
可取消的批量元数据探测;读盘在 worker 线程,不阻塞 UI。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
paths | array<string> | 是 | — | 待探测路径,缺失、空数组、非数组值或非字符串条目均返回 INVALID_PARAMS;条目可带 |subsong:N 后缀;逐项 MediaRead 校验为 fail-fast,任一条不过即整批返回 PERMISSION_DENIED,不产生 operationId。 |
includeTags | boolean | 否 | true | 为每条成功结果附上扁平标签表;只要技术信息时传 false。 |
返回值: {"operationId":"probe_...","success":true,"totalCount":42}
返回值只是派工回执,结果经事件送达:metadata:probeProgress(分批)与收尾的 metadata:probeComplete(被取消的批次同样以 cancelled: true 收尾)。每条结果带 infoSource(cached / direct / none),失败项带 failure(not-found / unsupported-format / read-error)。
const receipt = await fb2k.invoke('metadata.probeBatchAsync', {
paths: ['E:\\Music\\a.flac', 'E:\\Music\\b.flac']
});2
3
metadata.cancelProbe
取消进行中的 metadata.probeBatchAsync 操作。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
operationId | string | 是 | 来自 metadata.probeBatchAsync 回执;缺失或为空返回 operationId is required。 |
返回值: {"cancelled":true,"success":true}
cancelled: false 表示该操作已结束或从未存在,两者对调用方故意不可区分。
const { cancelled } = await fb2k.invoke('metadata.cancelProbe', { operationId });写入
metadata.write
写入元数据标签到文件。使用 metadb_io_v2::update_info_async 异步写入。标签键名自动转为大写。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
path | string | 是 | — | 支持 路径|subsong:N。 |
tags | object | 是 | — | 值为 null 或空字符串表示删除该标签。 |
cueIndex | integer | 否 | -1 | 显式指定容器内曲目序号,优先级高于路径后缀。 |
返回值: {"canonicalPath":"...","dispatched":"...","error":"...","handlePath":"...","note":"...","path":"...","subsong":"...","success":true,"tagsApplied":"...","tagsRemoved":"...","tagsSet":"..."}
异步派发模式
metadata.write 立即返回 dispatched: true,表示写入已提交给 foobar2000 引擎。实际完成后会广播 metadata:writeComplete 事件(见下文)。
await fb2k.invoke('metadata.write', {
path: 'E:\\Music\\song.flac',
tags: { TITLE: 'New Title', ARTIST: 'New Artist', COMMENT: null }
});
// CUE 子轨写入
await fb2k.invoke('metadata.write', {
path: 'E:\\Music\\album.flac|subsong:2',
tags: { COMMENT: 'Track 3 comment' }
});2
3
4
5
6
7
8
9
10
metadata.removeTag
移除指定标签。标签名自动转为大写。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
path | string | 是 | — | 支持 路径|subsong:N。 |
tags | array | 是 | — | 要移除的标签名数组。 |
cueIndex | integer | 否 | -1 | 显式指定容器内曲目序号,优先级高于路径后缀。 |
返回值: {"dispatched":"...","error":"...","note":"...","path":"...","removedCount":"...","removedTags":"...","subsong":"...","success":true}
await fb2k.invoke('metadata.removeTag', {
path: 'E:\\Music\\song.flac',
tags: ['COMMENT', 'LYRICS']
});
// CUE 子轨移除
await fb2k.invoke('metadata.removeTag', {
path: 'E:\\Music\\album.flac|subsong:2',
tags: ['COMMENT']
});2
3
4
5
6
7
8
9
10
metadata.writeBatch
批量写入多个文件的元数据。逐个调用 metadata.write。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
items | array | 是 | 每项为 { path, tags }。 |
返回值: { "success": true, "successCount": 3, "failCount": 0, "errors": [] }
await fb2k.invoke('metadata.writeBatch', {
items: [
{ path: 'E:\\Music\\a.flac', tags: { GENRE: 'Rock' } },
{ path: 'E:\\Music\\b.flac', tags: { GENRE: 'Pop' } }
]
});2
3
4
5
6
metadata.embedArtwork
将封面图嵌入到音频文件。使用 foobar2000 SDK 的 album_art_editor。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
path | string | 是 | — | embedded target 要求文件格式支持 album_art_editor。 |
imageData | string | 是 | — | 裸 Base64 图片字节,不带 Data URL 前缀。 |
type | string | 否 | front | 封面类型,取值见下。 |
target | string | string[] | 否 | embedded | embedded(写入文件标签)、file(写同目录外挂图)、all(两者),或由 embedded/file 组成的数组。 |
filename | string | 否 | — | 仅 file target 使用;留空按类型自动命名(cover.jpg 等),不允许路径分隔符。 |
支持的封面类型: front/cover_front, back/cover_back, disc, icon, artist
返回值: {"path":"...","results":[],"success":true,"type":"..."}
// 将 Base64 图片嵌入为封面
await fb2k.invoke('metadata.embedArtwork', {
path: 'E:\\Music\\song.flac',
imageData: base64String,
type: 'front'
});2
3
4
5
6
imageData 必须是裸 Base64 图片字节。不要传 data:image/...;base64, 头、base64: 标记或 fb2k:// URL。若来源是 Artwork API 返回的标准 Data URL,应先取第一个逗号之后的 payload:
const cover = await fb2k.invoke('artwork.getCurrent', { type: 'front' });
const payload = cover.dataUrl.slice(cover.dataUrl.indexOf(',') + 1);
await fb2k.invoke('metadata.embedArtwork', {
path: 'E:\\Music\\song.flac',
imageData: payload,
type: 'front',
});2
3
4
5
6
7
metadata.removeEmbeddedArt
移除音频文件中的嵌入封面。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
path | string | 是 | — | 文件格式须支持 album_art_editor。 |
type | string | 否 | — | 要移除的封面类型;留空移除全部。 |
removeAll | boolean | 否 | false | 为 true 时移除全部封面,忽略 type。 |
返回值: {"error":"...","path":"...","removedTypes":"...","success":true}
metadata.removeField
metadata.removeTag 的别名。移除指定标签。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
path | string | 是 | — | 支持 路径|subsong:N。 |
tags | array | 是 | — | 要移除的标签名数组。 |
cueIndex | integer | 否 | -1 | 显式指定容器内曲目序号,优先级高于路径后缀。 |
返回值: {"dispatched":"...","error":"...","note":"...","path":"...","removedCount":"...","removedTags":"...","subsong":"...","success":true}
事件
metadata:writeComplete
当 metadata.write 或 metadata.removeTag 的异步写入完成时广播此事件。
| 字段 | 类型 | 说明 |
|---|---|---|
| operation | string | "write" "removeTag" 触发操作 |
| path | string | 音频文件路径 |
| subsong | number | subsong 索引 |
| code | number | 完成码:0=成功, 1=中止, 2=错误 |
| success | boolean | 是否成功 |
| status | string | "success" / "aborted" / "error" |
fb2k.on('metadata:writeComplete', (e) => {
if (e.success) {
console.log(`写入完成: ${e.path} subsong=${e.subsong}`);
} else {
console.error(`写入失败: ${e.status}`);
}
});2
3
4
5
6
7
评分
rating.get
获取曲目评分。优先从 foo_playcount 读取,回退到文件 RATING 标签。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
path | string | 是 | — | 支持 路径|subsong:N。 |
cueIndex | integer | 否 | -1 | 显式指定容器内曲目序号,优先级高于路径后缀。 |
返回值:
{
"success": true,
"path": "C:\\Music\\song.flac",
"rating": 5,
"storage": "stats"
}2
3
4
5
6
| storage 值 | 含义 |
|---|---|
| "stats" | 来自 foo_playcount |
| "file" | 来自文件 RATING 标签 |
rating.set
设置曲目评分。优先通过 foo_playcount 上下文菜单设置,回退到写入文件 RATING 标签。
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
path | string | 否 | — | 省略时作用于当前播放曲目,其次为活动播放列表选中项;传空字符串会被路径安全校验拒绝。 |
rating | integer | 是 | — | 0–5,0 表示清除评分。 |
cueIndex | integer | 否 | -1 | 显式指定容器内曲目序号,优先级高于路径后缀。 |
返回值: {"menuPath":"...","note":"...","path":"...","rating":"...","storage":"...","success":true}
await fb2k.invoke('rating.set', { path: 'C:\\Music\\song.flac', rating: 5 });
await fb2k.invoke('rating.set', { rating: 0 }); // 清除当前播放曲目评分2
使用说明
metadata.read、metadata.readByPath和metadata.readRaw都需要path。readRaw绕过 metadb 缓存,接受默认值为-1的cueIndex;path|subsong:N可选择容器 subsong。成功结果会在结构化{ success, path, tags, info }中添加source: "file"。metadata.write、metadata.removeTag和兼容端点metadata.removeField都会异步派发更新。派发成功不等于已经持久化完成:请监听广播事件metadata:writeComplete,其 payload 为{ operation, path, subsong, code, success, status }。metadata.embedArtwork需要非空path和 Base64imageData。type默认front,filename默认空字符串,target默认embedded,可取embedded、file、all或由embedded与file组成的数组。多个 target 时,{ success, path, type, results }在任一 target 成功时即为成功。metadata.removeEmbeddedArt接受removeAll和可选的type;空type同样表示删除全部封面。目标格式必须支持album_art_editor。rating.set只接受0到5,0表示清除评分。存在匹配的 foo_playcount 上下文菜单时优先使用它,否则写入文件RATING标签。rating.get通过storage返回stats或file。