☰
Myspoti 扩展 API 完全参考:Actions、Hooks 与 myspoti 对象逐项详解
2026/11/1 1:15:51 网站建设 项目流程

【免费下载链接】myspoti

Native Android client for Spotify, built with Jetpack Compose

项目地址:https://gitcode.com/gh_mirrors/my/myspoti
点击查看免费下载

Myspoti 是一款原生 Android 的 Spotify 客户端,通过 Jetpack Compose 构建,支持免广告播放、离线下载和逐字歌词。它的扩展系统允许开发者用一个 JavaScript 文件加一个manifest.json接入歌词、音源、下载、首页分区、菜单按钮甚至登录自己的服务器。本文面向新手,逐项详解 Myspoti 扩展 API 中的 Actions(权限)、Hooks(钩子)与myspoti对象全部能力,帮你快速上手 Myspoti 扩展开发。

一、Myspoti 扩展机制总览 🧩

一个扩展就是两个文件:

  • manifest.json:声明扩展的身份信息和它能做什么(actions列表);
  • main.js:实际逻辑,运行在沙箱里。

Myspoti 在沙箱中执行脚本,能做的事完全由一份封闭的 Actions 目录决定。每个 action 都有固定图标、简短标签、安全级别(normal / critical)和它解锁的 API,用户在安装前会看到每个 action 及其"理由(why)"。沙箱严格执行清单声明:调用未声明的能力会直接抛错。

官方完整文档:docs/extensions/API.md

最小可运行的扩展长这样(示例来自官方 lrclib 歌词样例):

myspoti.provide('lyrics', function (track) { var r = myspoti.http.get('https://lrclib.net/api/get?' + myspoti.query({ track_name: track.title, artist_name: track.artist })); if (!r.ok) return null; // 返回 null = "我没有答案",App 继续问其他来源 return { provider: 'LRCLIB', lines: parseLrc(r.json().syncedLyrics) }; });

二、manifest.json 字段逐项详解 📋

清单放在扩展包根目录,未知字段会被忽略。核心字段如下(完整规则见 API.md 第 2 节):

字段必填说明
id✅反向域名式小写 id(如io.github.you.lyrics),发布后永不改变
name/version/versionCode✅展示名;版本号。versionCode越大越新,是升级比较的依据
author/description作者必填最多 60 / 500 字符
actions✅从第 4 节目录里选,每项{ "name", "why", "hosts"? },why 必填(1~80 字符,展示给用户)
settings❌用户可调的选项,类型支持text、secret、toggle、number、choice、button,需要settings.pageaction
category/tags❌商店分类(lyrics、playback、library、discovery、stats、look、social、tools、other)与搜索标签
minAppVersion❌兼容的最低 Myspoti 版本,旧版本会显示"需要 X 或更新"
replaces/conflictsWith❌替代 / 与谁互斥的扩展 id(各最多 10 个)
changelog/welcome/homepage/icon❌更新说明、首次安装引导、主页、图标(≤256px 方形 PNG)

打包约束:zip ≤ 2 MB、≤ 30 个文件、脚本 ≤ 1 MB、图标 ≤ 256 KB,且每次构建结果一致(固定顺序与日期),任何人可重建并比对 sha256。

两个官方样例可直接参考:

  • docs/extensions/samples/lrclib-lyrics/manifest.json(歌词 + 网络 + 设置页)
  • docs/extensions/samples/blocks-demo/manifest.json(全部 UI 块类型演示)

三、Actions 权限目录逐项详解 🔐

Actions 是一份封闭目录,用户安装时看到图标、标签和你的why;critical 级别会用警示色标出。逐项列表(图标与级别以 API.md 第 4 节 为准):

Action级别解锁的能力
playback.controlnormalmyspoti.player.*播放控制;Spotify 播放/暂停/快进/随机/音量/倍速/切设备
playback.queuenormal编辑 Spotify 播放队列(加入、下一首播放、移动、移除、清空)
playback.eventsnormalmyspoti.player.current()、track/playback事件、播放器状态读取
lyrics.providenormallyrics钩子:提供歌词
trackinfo.providenormaltrackinfo钩子:在歌曲演职员表下方加原生块
audio.sourcecriticalaudio-source钩子:从外部地址播放音频(顺序询问)
download.sourcecriticaldownload-source钩子:提供可下载文件(顺序询问)
home.sectionsnormalhome钩子:在首页末尾加分区
search.providenormalsearch钩子:在搜索结果下方加内容
menu.itemsnormalmenu钩子:往歌曲/专辑/歌单/艺人 ⋯ 菜单加条目
player.buttonsnormalplayer钩子:在播放器控制区下加按钮
settings.pagenormal独立设置页、myspoti.settings、setting事件
notifynormalmyspoti.notify()屏幕短消息
library.readnormal读资料库、 liked songs、用户、最近、设备列表、Blends 读取
library.writecritical资料库增删、关注、隐私会话、口味屏蔽等写操作
playlists.writecritical歌单编辑、Blends 邀请/加入/改名/退出
spotify.catalognormalSpotify 搜索、目录、公开歌单、首页、播客等浏览
spotify.rawcriticalraw.gql/raw.service/raw.request等完整访问,可声明固定写操作
networknormal(*为 critical)myspoti.http访问清单里列出的主机;whenOnline与online事件
webview.pagecritical扩展自己的锁定 WebView 页面(page/message钩子)
webview.authcriticalmyspoti.auth.open()网站登录(独立浏览器 profile)
storagenormalmyspoti.storage本地存储(共 512 KB)
attestnormalmyspoti.attest硬件密钥证明(向自己的服务器证明"这真是 Myspoti")

关键规则:

  • 不能单独取消勾选:要么授予扩展列出的全部 action,要么不装;
  • Additive(叠加型):歌词、详情、首页、搜索、菜单、按钮——每个扩展都问,答案按用户排序合并;
  • Ordered(顺序型):audio.source与download.source——按用户在设置里排的顺序逐个询问,直到有人回答;
  • 破坏性 Spotify 写操作(删除、清空等)运行时逐次弹确认框,可给raw.request的写操作选"Always allow"。

四、Hooks 钩子逐项详解 🪝

钩子是 App 主动询问扩展的固定时点,用myspoti.provide(name, fn)注册。fn同步接收一个对象并同步返回答案(不能返回 Promise);返回null/undefined表示"没有答案",抛错或超时记为该扩展的最后错误。完整定义见 API.md 第 3 节。

钩子所需 Action输入回答
lyricslyrics.providetrack歌词对象(带时间戳行 / 纯文本)
audio-sourceaudio.sourcetrack(含ahead预取标记){ url, headers?, expiresInMs?, codec?, bitDepth?, sampleRate?, … }或 DASH manifest
download-sourcedownload.sourcetrack +quality(hi-res/cd/small)文件列表(最多 10 个,按优劣排序),支持flac、dash-flac、m4a、mp3
trackinfotrackinfo.providetrack原生块列表,显示在歌曲演职员表下方
homehome.sections{}首页末尾的分区块(每扩展最多 2 个分区)
searchsearch.provide{ query }搜索结果下方的块
menumenu.items{ kind, uri, title, subtitle }menu块(每扩展 2 条)
playerplayer.buttonstrackbutton块(每扩展 1 个)
page/messagewebview.page{}/ 页面 post 的消息{ title?, html }/ 回传页面的回复
tap任一 blocks action{ action, surface, context }无(点击回调,处理完可再刷新界面)
status无需 action{}{ live: true/false, detail? },5 分钟一次的存活探测;live: false时音源/下载被跳过

歌词(lyrics)要点

  • 同步歌词:{ provider, synced: true, lines: [{ time, end?, text, words? }] },每行 ≤500 字符,总行数 ≤3000,词级时间戳每行最多 200 个词;
  • 非同步歌词:{ plain: '一行一句的文本' };
  • 时间单位是毫秒;没有内容的歌词等同"无答案"。

音源(audio-source)要点

  • 返回url后,歌曲在Myspoti 自己的播放器中播放(不走 Spotify 播放器);
  • 也支持Lossless DASH:返回 base64 的 MPEG-DASH manifest(≤1 MB),Myspoti 按段顺序播放、可拖动;先检查myspoti.features.indexOf('audio.manifest') >= 0;
  • ahead: true表示"提前 10 秒预取下一首";expiresInMs声明地址有效期(复用最长 10 分钟,401/403/404/410 会自动重新询问一次);
  • 声明codec/bitDepth/sampleRate会在播放时显示音质行(如 "FLAC · 24-bit · 96 kHz")。

下载(download-source)要点

  • 输入含用户所选quality;返回的文件按你的顺序逐一校验:FLAC 必须是真 FLAC,m4a/mp3 必须含手机可解码的音轨,且时长与 Spotify 记录相差不超过 -5s/+15s;
  • dash-flac用 base64 manifest 代替url,Myspoti 会抓取分段并合成完整 FLAC;
  • isrcMatched: true仅当你确认"这个文件就是这首歌(按 ISRC 找到的)"时声明——Myspoti 本身不与参考录音比对。

原生块(Blocks):扩展不画 UI,只描述 JSON

扩展永远不自己绘制界面,而是返回原生块的 JSON,由 App 用自己的字体、配色和图标绘制(标注"From X")。块类型(blocks-demo 样例 每种都演示了一个):

类型关键字段出现位置
sectiontitle、blocks(≤12,不可嵌套)首页 / 搜索 / 歌曲详情
cardtitle、subtitle?、image?+open或action同上
tracks/albums/playlists/artistsitems: [{ uri, title, subtitle?, image? }](≤50)歌曲可播放,其余打开对应页面
buttonlabel、icon、action含播放器
menulabel、icon、action仅 ⋯ 菜单
texttext(≤1000)、style:title/body/caption首页 / 搜索 / 详情
imageurl(https 公网)、ratio?(0.5~3)、open或action首页 / 搜索 / 详情
  • open只能打开spotify:uri(track/album/playlist/artist/show/episode),不能打开网页;
  • action是 1~64 字符的自定义 id,点击时触发你的tap钩子;
  • 图标从固定名称表选(add、download、favorite、shuffle等),不认识的按extension显示;
  • myspoti.ui.refresh('home'|'search'|'menu'|'player'|'trackinfo')可请求 App 重新询问。

网页(page / message)与网站登录(auth)

  • webview.page给扩展一个免费页面(≤512 KB HTML),在锁定的 WebView 中展示:无导航、无 cookie、无存储,资源只能 GET 你network里列的主机;页面与脚本之间用MyspotiPage.postMessage()与message钩子通信;
  • webview.auth是独立登录页:myspoti.auth.open({ url, callback })立刻打开并返回 id,到达回调(自定义 scheme 或 https 地址)后myspoti.on('auth')收到{ id, url };独立 profile 的 cookie 在多次登录间保留,扩展拿不到 cookie;hidden: true可先静默加载,8 秒无人介入才全屏展示——适合"通常无需人工"的校验;
  • 错误回调:"closed"、"timeout"(3 分钟)、"network: …"。

五、事件逐项详解 📡

事件所需 Action说明
trackplayback.events每首新歌开始时收到歌曲字段 +origin/providerId,用于区分音频来源
playbackplayback.events播放全程事件流:start/progress(每 10s)/pause/resume/seek/skip/end,含positionMs、playedMs、source(local/spotify)、reason等
onlinenetwork网络恢复时触发,配合myspoti.work.whenOnline()补发离线队列(30s 时限)
settingsettings.page用户改设置时收到{ key };button类型设置收到{ key, pressed: true }
authwebview.auth网站登录完成或出错

每次投递限时 3 秒(setting为 30 秒);监听器内报错只记日志、不抛出;同一事件的后续监听器仍会执行。

六、myspoti 对象逐项详解 🧰

脚本跑在 Rhino 1.8(ES6 语法可用)的独立线程上,只能看到标准 JS 对象、myspoti和console:没有require、fetch、定时器、DOM,eval与从字符串构造函数一律被禁("code from strings is not allowed in extensions")。一切调用都是同步的。

基础信息与生命周期

调用说明
myspoti.apiVersion当前为1
myspoti.extension清单里的{ id, name, version }
myspoti.features本机支持的增强能力列表(audio.manifest、audio.expires、audio.quality、audio.ahead、http.all、status、attest等);旧版本没有此属性,检测写法:myspoti.features && myspoti.features.indexOf(name) >= 0
myspoti.provide(hook, fn)/myspoti.on(event, fn)注册钩子 / 事件(重复注册同名钩子会覆盖)
myspoti.work.whenOnline()请求一次online事件(需要network)

网络:myspoti.http

get/post/request返回{ status, headers, body, ok, json() };4xx/5xx 不抛错,网络失败、主机被拒或超限才抛Error("http: ...")。规则(由 App 强制):

  • 只能访问清单里列出的主机(*.domain匹配子域),https 优先,明文 http 仅限清单精确命名的主机;
  • 本地地址一律拒绝(localhost、内网段、IPv6 字面量);无 cookie、无缓存、不自动跟随重定向;
  • 默认User-Agent为Myspoti-Extension/1 (<id>);超时默认 15s(钳制在 1~20s);响应体 ≤ 5 MB;120 次/分钟(滑动窗口);
  • myspoti.http.all([...]):一次并发最多4 个请求,脚本仍保持同步,失败项以{ error }占位(需要http.all特性)。

存储、设置与工具

调用说明
myspoti.storage.get/set/remove(key)自己的 JSON 存储,键值合计 512 KB,写满抛"storage full (512 KB)"
myspoti.settings.get(key)用户值 → 清单默认值 →undefined;button类型无值
myspoti.notify(text)屏幕短消息(≤200 字符,带扩展名),需要notify
myspoti.log(...)/console.log/info/warn/error同一日志(≤2000 字符)
myspoti.query(obj)生成查询串,null/undefined自动省略
myspoti.util.base64Encode/Decode、md5/sha1/sha256、hmacSha256UTF-8 / 十六进制摘要
myspoti.ui.refresh(place)请求刷新指定界面
myspoti.page.post(data)向自己打开的页面发数据

本机播放器:myspoti.player

current()(当前歌曲或null)、play()/pause()/next()/previous()/seek(ms),全部经"播放仲裁器"执行:两个扩展 2 秒内发出同类命令时,Playback 区排序靠前者胜出;30 秒内互相矛盾 3 次,后者会被暂停其播放命令并通知用户;每扩展每分钟最多 20 条命令。

Spotify 账号访问:myspoti.spotify

myspoti.spotify.<area>.<method>(...)直接操作 Myspoti 中登录的账号。共 13 个区域(API.md 第 6 节 有完整方法表):

区域典型方法所需权限
playerstate、queue、play、pause、seek、next、setShuffle、setRepeat、addToQueue、setVolume、setSpeed、media读 =playback.events;写 =playback.control/playback.queue
searchall、tracks、artists、albums、genres、recent、clearRecentspotify.catalog
catalogtrack、album、albumTracks、artist、artistTopTracks、discography、appearsOn、similarAlbumsspotify.catalog
librarycontains、items、likedTracks、add、remove、pin、文件夹增删改移读 =library.read,写 =library.write
playlistsplaylist、tracks、create、rename、addTracks、removeTracks、setCover等 26 个方法catalog/library.read/playlists.write
homefeed、categories、category、whatsNewspotify.catalog
podcastsshow、episodes、episode、audioUrl、audiobook、savedEpisodes读catalog,写library.write
usersme、profile、followers、top、friendActivity、history、follow读library.read,写library.write
extraslyrics、canvas、credits、radio、口味/封禁管理spotify.catalog/library.write
deviceslist、transfer、pullHerelibrary.read/playback.control
recentsrecentEntrieslibrary.read
blendsinvitation、group、story、invite、join、rename、leave读library.read,写playlists.write
rawoperations、gql、service、get、request(携带账号凭据访问*.spotify.com/*.spotifycdn.com)spotify.raw

使用规则:

  • 参数按位置传递;URI 为字符串(列表 ≤1000 项);枚举按名称传、结果为大写名称;总参数 ≤256 KB;
  • 错误以Error抛出,消息以spotify.<area>.<method>:开头;用户拒绝或 120 秒未应答会抛the user refused,记得 catch;
  • 限制:60 次调用/分钟、响应 ≤2 MB、必须在钩子时限内完成;
  • 需确认的调用:所有"删除/清空"类方法、raw.gql的 mutation、raw.service(总是)、非 GET/HEAD 的raw.request(可用 Always allow 按 method+host+路径模式授权);
  • 结果统一为普通 JSON:Page<T>是{ items, offset, total, nextOffset },枚举为大写名,深度截断到 12 层。

硬件证明:myspoti.attest

用于和你的服务器通信时证明"请求来自一台真的在跑该扩展的 Myspoti"(zip 里没有秘密可言,密钥只能由硬件 keystore 生成):

调用作用
enroll(challenge)在硬件 keystore(StrongBox/TEE)中为本扩展创建 EC P-256 签名密钥,返回{ publicKey, certChain };每小时最多 5 次
publicKey()当前公钥(未注册为null)
sign(text)对 ≤64 KB 文本做 ECDSA-SHA256 签名;120 次/分钟
reset()删除密钥(卸载扩展也会删)

它证明密钥在硬件中、由 App 包创建、且由你的服务器发起 challenge;但不能证明 App 未被篡改(root 场景),也不证明用户身份——建议配合按密钥限频。桌面调试器用的是软件测试密钥,服务端按正式流程校验时应拒绝。

七、限制一览表 📏

项目限制
脚本顶层加载3 秒
钩子调用按类型:lyrics8s、audio-source15s、download-source30s
track/playback事件3 秒/次
myspoti.http120 次/分、5 MB/响应、20s/请求、并发 4
myspoti.spotify60 次/分、256 KB 参数、2 MB/响应
storage512 KB
歌词3000 行、每行 500 字符、每行 200 词
下载10 个文件/次、DASH manifest 1 MB
notify/log200 / 2000 字符
包体2 MB zip、30 文件、1 MB 脚本、256 KB 图标

八、开发、测试与发布流程 🚀

官方命令行工具myspoti-ext在电脑上按 App 相同的方式运行扩展(构建与用法见 docs/cli.md,入口 tools/myspoti-cli):

命令作用
check <folder>打印清单问题、以用户视角列出每个 action,并用样例触发一遍 blocks 钩子校验块合法性
run <folder\|zip> lyrics --title T --artist A调用指定钩子并打印 App 解析后的答案(audio-source、download-source、home、menu、status、track、playback等同理)
pack <folder>校验并构建确定性 zip,打印 sha256 与索引条目
index <zip目录> <名称>为扩展仓库生成index.json

要点:

  • 不加--cdp时所有myspoti.spotify调用会抛错(适合测歌词/音源代码);加上--cdp PORT后走你本机 Chrome 里已登录的 Spotify Web Player 做真实调用,需确认的调用会在终端问y/N;
  • check只跑顶层代码、不触发钩子、无账号无网络,run才能暴露钩子内部问题;
  • 发布:在自己的仓库加myspoti-extension话题,商店会自动发现(6 小时内);升级只有在不新增 action/主机/why 时才自动生效,否则显示"Update needs your OK"卡片让用户逐项确认;
  • 扩展源码实现在 base/extensions/(如 Actions.kt、Hooks.kt)。

九、安全模型:新手最容易踩的边界 🛡️

  1. 隔离:每个扩展独立线程、存储、设置、日志与登录 profile;API 中不存在"别的扩展",块点击与页面消息只回到画它的扩展;
  2. 沙箱白名单:清单没声明的能力调用即抛错("x is not in this extension's manifest (actions)");
  3. 确认机制:破坏性写操作逐次询问用户,120 秒未答即拒绝;raw.request写操作可被"Always allow"授权并可在扩展页撤销;
  4. 网络面收窄:network主机清单之外的地址直接拒绝,本地地址永不放行,无 cookie;
  5. 受保护界面:扩展无法遮挡、延迟或观测 App 的捐赠提醒、新版本卡片、安装确认框、报告按钮与关于页。

总结 🎯

Myspoti 扩展 API 的设计核心可以概括为三句话:Actions 声明能力边界,Hooks 定义触发时机,myspoti对象提供全部同步工具。想写一个歌词扩展?从 lrclib 样例 复制起步,只需lyrics.provide+network两个 action;想做 UI 增强?参考 blocks-demo 样例 掌握 7 种原生块;要读写 Spotify 账号,则逐节对照第 6 节方法表选择最小权限。完整规范始终请以 docs/extensions/API.md 为准——本文覆盖的是 API version 1 的全部内容。

【免费下载链接】myspoti

Native Android client for Spotify, built with Jetpack Compose

项目地址:https://gitcode.com/gh_mirrors/my/myspoti
点击查看免费下载
上一篇:【免费下载】 PyRadiomics 安装与使用教程
下一篇:MediaInfo终极指南:如何快速获取多媒体文件详细信息

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询