Skip to content

插件函数

本文说明插件需要暴露给 Lyrico 的函数接口。开发者实现搜索、歌词获取和封面搜索时,主要查阅这一页。

插件入口脚本必须定义全局函数作为宿主调用的接口。宿主把请求解析成 JavaScript 对象后传入,并负责把返回值序列化为 JSON。插件应直接返回对象、数组、字符串或 null,不要调用 JSON.stringify(),否则结果会被重复序列化并导致真机解析失败。

函数总览

函数触发场景返回类型对应能力
searchSongs(request)用户搜索歌曲JavaScript 数组searchSongs
getLyrics(request)搜索歌词候选API 4–5 为 JavaScript 候选数组;API 1–3 为歌词对象、字符串或 nullgetLyrics
searchCovers(request)搜索封面图片JavaScript 数组searchCovers

函数通过 QuickJS 的全局作用域暴露,不需要(也不能)使用 export

javascript
function searchSongs(request) { ... }   // ✅ 全局函数
function getLyrics(request) { ... }     // ✅ 全局函数
function searchCovers(request) { ... }  // ✅ 全局函数

searchSongs(request)

歌曲搜索,宿主将用户输入的关键词传递给此函数。

请求参数

宿主传入的 JSON 对象(被序列化前):

json
{
  "keyword": "示例歌曲",
  "page": 1,
  "pageSize": 20,
  "separator": "/",
  "config": {
    "cover_size": "1200"
  }
}
字段类型默认值说明
keywordstring-用户输入的搜索关键词
pageint1页数(从 1 开始)
pageSizeint20每页数量
separatorstring"/"多艺术家之间的分隔符
configobject{}用户在设置页面配置的键值对

返回值

直接返回 JavaScript 数组或包含结果数组的对象。支持两种顶层格式:

格式 1:直接返回数组(推荐)

javascript
function searchSongs(request) {
  return [
    {
      id: "12345",
      title: "示例歌曲",
      artist: "示例歌手",
      album: "示例专辑",
      duration: 240000,
      date: "2024-01-01",
      trackNumber: "2",
      picUrl: "https://cdn.example.com/cover/abc.jpg",
      fields: {
        title: "示例歌曲",
        artist: "示例歌手",
        album: "示例专辑",
        date: "2024-01-01"
      }
    }
  ];
}

格式 2:包装在对象中(兼容多个键名)

javascript
function searchSongs(request) {
  return {
    items: [...]    // 也可用 "results"、"songs"、"data"
  };
}

Song 对象字段

解析器支持灵活的字段名映射:

语义支持的 JSON 键名(任意一个即可)
歌曲 IDid, songId, trackId
标题title, name, songName
艺术家artist, artists, singer
专辑album, albumName
时长duration, durationMs, duration_ms
发行日期date, releaseDate, release_date
音轨号trackNumber, trackerNumber, track_number
封面 URLpicUrl, coverUrl, cover_url, artworkUrl
标准元数据字段fields
插件私有上下文internal

artist 字段还支持数组格式(自动以 / 连接):

json
{
  "id": "12345",
  "title": "歌曲名",
  "artist": ["歌手A", "歌手B"]
}

fields 标准字段

fields 只允许放入宿主标准字段。未知 key 会被忽略并产生调试 warning;平台私有 ID、hash、token 等上下文必须放入 internal

json
{
  "id": "12345",
  "title": "歌曲名",
  "artist": "歌手",
  "fields": {
    "title": "歌曲名",
    "artist": "歌手",
    "album": "专辑名",
    "date": "2024-01-01",
    "track_number": "3",
    "cover_url": "https://..."
  },
  "internal": {
    "song_id": "12345",
    "lyrics_id": "abc"
  }
}

当前标准字段包括:titleartistalbumalbum_artistgenredatetrack_numberdisc_numbercomposerlyricistcommentlyricscover_urllanguagecopyrightratingreplaygain_track_gainreplaygain_track_peakreplaygain_album_gainreplaygain_album_peakreplaygain_reference_loudness

internal 不展示、不写入标签、不参与批量匹配字段选择,只会原样传回产生该结果的同一个插件。


getLyrics(request)

编辑页的独立歌词搜索会把当前歌曲的标题、艺术家、专辑和年份放在 song 中传入。 getLyrics 可以直接用这些普通字段搜索,不要求插件实现 searchSongs,也不要求用户 提供平台歌曲 ID。只要插件同时声明 searchSongs,无论其 API 版本,宿主都会先让用户 选择该插件的同源歌曲候选,再把选中的 idfieldsinternal 原样传给同一插件的 getLyrics。单曲搜索页不会调用独立歌词源。

请求参数

json
{
  "song": {
    "id": "12345",
    "title": "示例歌曲",
    "artist": "示例歌手",
    "album": "示例专辑",
    "duration": 240000,
    "sourceId": "com.example.music_source",
    "pluginId": "com.example.music_source",
    "fields": {
      "title": "示例歌曲"
    },
    "internal": {
      "lyrics_id": "abc"
    }
  },
  "page": 1,
  "pageSize": 20,
  "config": {}
}
字段类型说明
song.idstring歌曲 ID;独立歌词搜索时不保证是源平台 ID
song.titlestring歌曲标题
song.artiststring艺术家
song.albumstring专辑名
song.durationlong时长(毫秒)
song.sourceIdstring源插件 ID
song.pluginIdstring插件 ID
song.fieldsobject搜索时返回的标准字段
song.internalobject搜索时返回的插件私有上下文
pageint候选页码,从 1 开始;不分页的插件可以忽略
pageSizeint本页期望返回的候选数量
configobject用户配置项

返回值

API 4–5 应返回歌词对象数组(也可包装在 itemsresultscandidates 中)。每个歌词 对象必须在 tags 中提供 ti(标题)、ar(艺术家)、al(专辑)和 date(年份), 宿主从这些既有歌词标签生成候选列表,避免再声明一套重复的顶层歌曲字段:

javascript
function getLyrics(request) {
  return [{
    type: "rawPlainLrc",
    tags: {
      ti: "示例歌曲",
      ar: "示例歌手",
      al: "示例专辑",
      date: "2024"
    },
    rawPlainLrc: "[00:00.00]第一句歌词"
  }];
}

API 1–3 的函数签名和原有返回完全不变:可返回单个结构化歌词对象、完整原始歌词文本, 或 null 表示未找到歌词。宿主会把旧结果包装为一个候选,并使用请求中的歌曲信息供 用户判断。下面各格式既是 API 1–3 的顶层返回格式,也是 API 4–5 数组中的候选格式。

宿主先读取 type 判断载荷类型;当 typestructured 时解析 original / translated / romanization 列表,当 type 为 raw 类型时直接使用对应 raw 字段。

格式 1:结构化逐词歌词(推荐)

javascript
function getLyrics(request) {
  return {
    type: "structured",
    tags: {
      ti: "歌曲标题",
      ar: "艺术家",
      al: "专辑名"
    },
    original: [
      [0, 2000, [[0, 500, "第一"], [500, 1000, "句"], [1000, 2000, "歌词"]]],
      [2000, 4000, [[2000, 3000, "第二"], [3000, 4000, "句"]]]
    ],
    translated: [
      [0, 2000, "First line lyrics"],
      [2000, 4000, "Second line lyrics"]
    ],
    romanization: null
  };
}

以下格式示例中的单个对象用于展示候选载荷。API 4–5 的实际 getLyrics 回调必须将对象放入数组返回(return [result]);无结果返回 []

结构化歌词的行格式

API 5 扩展了结构化歌词载荷:逐词 romanization、行级扩展、agents / metadata、时间粒度与语言字段、bodyDur 和多音节 Ruby 注音。返回这些扩展时应声明 apiVersion: 5。候选数组及必填歌曲标签沿用 API 4,旧的整行文本格式仍兼容。宿主 API 独立编号,当前仍为 4。

originalromanization 都可使用逐词格式:

[lineStartMs, lineEndMs, [[wordStartMs, wordEndMs, "text"], ...], extensions?]

original 中的词可以用第 4 个元素携带 Ruby 注音音节:

[wordStartMs, wordEndMs, "基文本", [[syllableStartMs, syllableEndMs, "注音"], ...]]

一个基文本可以对应多个注音音节;单音节仍使用单元素数组。音节时间是绝对毫秒值;缺失时必须在对应位置传 null。导出 TTML 时会把缺失边界规范化为完整时间:优先衔接相邻音节,连续的全空音节在可用词时间内均分,首尾再回退到词时间。无注音的词保持原来的 3 元素格式。

javascript
[27820, 27950, "詮", [[27820, 27880, "せ"], [27880, 27950, "ん"]]]

两者也都兼容整行文本;translated 只使用这种格式:

[lineStartMs, lineEndMs, "text"]

导出 TTML 时,逐词音译保留每个词的时间,并在相邻音节之间补充必要的空格。

TTML 扩展

以下字段只影响 TTML 导出。导出为 LRC 时,TTML 专属结构不会保留。

这里描述的是 structured 协议能够表达的 TTML 子集,不是 AMLL TTML DB 的投稿规范。未知 XML 节点仍可能无法通过 structured 载荷表示;需要保留完整源文档时应返回 type: "rawTtml"。如果用户随后执行繁简转换、轨道筛选等操作,宿主仍会解析并重写该文档,未建模结构可能丢失。

original 行可在第 4 个元素中提供扩展属性:

javascript
[0, 6000, [[0, 500, "第一"], [500, 1000, "句"]], {
  "ttm:agent": "v1",
  "itunes:song-part": "Verse",
  "divBegin": "0",
  "divEnd": "6000"
}]
  • ttm:agent 引用 agents 中同名的演唱者。
  • itunes:song-part 用于生成 <div itunes:song-part="...">。旧写法 itunes:songPart 仍可读取,但导出统一使用 song-part
  • divBegindivEnd 是 Lyrico 的段落时间传递字段,单位为毫秒。只需放在该段第一行;导出时会成为 <div>beginend

宿主会为所有输出的 <p> 重新生成连续的 itunes:keyL1L2……),插件无需提供。扩展属性只接受无前缀名称以及 ttm:itunes: 前缀;其他前缀会被忽略。

agents 用来生成 <ttm:agent>id 必填,typename 可选:

javascript
agents: [
  { "id": "v1", "type": "person", "name": "艺人 A" },
  { "id": "v1000", "type": "group" }
]

metadata 用来补充 <head> 中的元素,节点格式为 { name, namespace?, attributes?, text?, children? }songwriters 会写入 Apple 风格的 <iTunesMetadata>,其他节点写入普通 <metadata>。目前有以下约束:

  • songwriters 必须包含一个或多个带文本的 songwriter 子节点;
  • translationstransliterationsttm:agent 已有专门字段,不应再放入 metadata
  • 自定义前缀需要同时提供 namespace,例如 { "name": "amll:meta", "namespace": "http://www.example.com/ns/amll", ... }

根、body 属性和辅助轨语言可用下列字段设置:

字段TTML 位置
timing<tt itunes:timing>;常用值为 WordLine
language<tt xml:lang>
bodyDur<body dur>;有效的 TTML 时间表达式(也接受 body_dur),不随歌词偏移量改变;非法值会被丢弃
translatedLang内联翻译的 xml:lang
romanizationLang<transliteration>xml:lang

语言字段使用 BCP 47 标签,例如 zh-Hansja-Latn

结构化模型只建模 <body>dur 属性;其它 body 属性以及 Ruby 注音 span 上的额外属性不会通过 structured 往返保留。

格式 2:完整原始歌词文本

javascript
function getLyrics(request) {
  return {
    type: "rawPlainLrc",
    tags: {
      ti: "歌曲标题",
      ar: "艺术家",
      al: "专辑名"
    },
    rawPlainLrc: "[00:00.00]第一句歌词\n[00:05.00]第二句歌词"
  };
}

支持的 raw type 值与对应内容字段:

type内容字段说明
rawPlainLrcrawPlainLrc普通 LRC
rawVerbatimLrcrawVerbatimLrc逐字 LRC
rawEnhancedLrcrawEnhancedLrc增强型逐字 LRC
rawTtmlrawTtmlTTML
rawMultiPersonEnhancedLrcrawMultiPersonEnhancedLrc多人增强 LRC

若插件没有显式提供 type,宿主会按 structured 处理;这只用于兼容旧插件,新插件应显式声明。

格式 3:返回 null 表示无歌词

javascript
function getLyrics(request) {
  if (noLyricsFound) {
    return null;
    // 或者
    return { notFound: true };
  }
}

LyricsResult 字段

字段类型说明
typestringstructured 或 raw 类型
tagsobject歌曲元信息标签
originalLine[]type: "structured" 使用,原文歌词(逐词或整行)
translatedLine[] | nulltype: "structured" 使用,翻译歌词
romanizationLine[] | nulltype: "structured" 使用,音译歌词(罗马音等);行支持逐词(逐字注音)或整行文本
agentsAgent[]type: "structured" 使用,演唱者列表(可选;写回 TTML head <ttm:agent>,详见上文扩展字段)
metadataMetadataElement[]type: "structured" 使用,补充 TTML head 的元素树(可选,约束见上文)
timingstringtype: "structured" 使用,时间粒度标志(可选;词级传 "Word",写回根 <tt itunes:timing>
languagestringtype: "structured" 使用,原文语言码 BCP47(可选;写回根 <tt xml:lang>
bodyDurstringtype: "structured" 使用,<body dur> 的原始 TTML 时间表达式(可选;也接受 body_dur
translatedLangstringtype: "structured" 使用,翻译轨语言码 BCP47(可选;写回内联翻译的 xml:lang
romanizationLangstringtype: "structured" 使用,音译轨语言码 BCP47(可选;写回 head 音译的 xml:lang
rawPlainLrcstringtype: "rawPlainLrc" 使用
rawVerbatimLrcstringtype: "rawVerbatimLrc" 使用
rawEnhancedLrcstringtype: "rawEnhancedLrc" 使用
rawTtmlstringtype: "rawTtml" 使用
rawMultiPersonEnhancedLrcstringtype: "rawMultiPersonEnhancedLrc" 使用

searchCovers(request)

封面图片搜索。宿主直接按用户输入的关键词调用 searchCovers,不要求插件实现 searchSongs,也没有前置歌曲候选选择步骤。

请求参数

json
{
  "keyword": "示例歌曲",
  "page": 1,
  "pageSize": 5,
  "config": {}
}
字段类型默认值说明
keywordstring-搜索关键词
pageint1页数(从 1 开始)
pageSizeint5结果数量
configobject{}用户配置项

返回值

顶层格式与 searchSongs 相同,但封面候选不要求平台歌曲 ID。API 4–5 的每个结果必须 返回标题、艺术家、专辑、年份以及封面 URL,供用户判断后应用;日期可使用 yeardatereleaseDate,封面可使用 picUrlcoverUrl 等兼容键名。API 1–3 的既有 返回格式继续兼容。

javascript
function searchCovers(request) {
  return [{
    title: "示例歌曲",
    artist: "示例歌手",
    album: "示例专辑",
    year: "2024",
    picUrl: "https://cdn.example.com/cover.jpg"
  }];
}

错误处理

插件函数内部异常会被宿主捕获并记录到 Logcat。确保使用 try...catch 处理可预见的错误:

javascript
function searchSongs(request) {
  try {
    // 主要搜索逻辑
    return searchByEapi(request);
  } catch (e) {
    Platform.log.warn("Plugin", "Primary search failed: " + e.message);
    // 回退逻辑
    return searchByFallback(request);
  }
}

函数未定义时的行为:

  • 若能力中未声明某函数(如 getLyrics),宿主不会调用该函数
  • 若声明了但函数不存在,调用会失败并被忽略

数据解析容错

宿主解析器是 宽松的

  • JSON 键名有多个候选项(如 id/songId/trackId
  • 多余的字段会被忽略
  • 顶级可以是数组或包装对象
  • null 字段当作默认值处理