Spotube 中文文档 下载 App

Spotube 插件 API 接口——MetadataSearchAPI / AudioAPI / ScrobbleAPI / LyricsAPI

四个接口的作用

Spotube 插件可以实现四个接口中的一个或多个。接口定义在 plugin_interfaces 库(dev.krtirtho.spotube group),全部是 Kotlin interface。

MetadataSearchAPI——元数据搜索

interface MetadataSearchAPI : ZiplineService {
    suspend fun search(query: String, types: List<SearchType>): List<SearchResult>
    suspend fun getTrack(id: String): Track?
    suspend fun getAlbum(id: String): Album?
    suspend fun getArtist(id: String): Artist?
    suspend fun getPlaylist(id: String): Playlist?
    suspend fun getArtistTracks(artistId: String, limit: Int): List<Track>
    suspend fun getAlbumTracks(albumId: String): List<Track>
    suspend fun getPlaylistsByCategory(categoryId: String): List<Playlist>
}

插件需要实现:

SearchResult、Track、Album、Artist、Playlist 这些数据模型也是 plugin_interfaces 提供的。

AudioAPI——音频流

interface AudioAPI : ZiplineService {
    suspend fun getAudioStream(track: Track): AudioStream?
    suspend fun isAvailable(track: Track): Boolean
    suspend fun supportedCodecs(): List<String>  // "mp3", "aac", "flac", "opus", "wav"
}

ScrobbleAPI——播放记录上报

interface ScrobbleAPI : ZiplineService {
    suspend fun scrobble(track: Track, progressMs: Long, durationMs: Long)
    suspend fun updateNowPlaying(track: Track?, durationMs: Long)
}

LyricsAPI——歌词获取

interface LyricsAPI : ZiplineService {
    suspend fun getLyrics(track: Track): Lyrics?
}

返回的 Lyrics 对象包含 LRC 格式字符串。

宿主 Bridge——插件能做什么

插件运行在 Zipline 沙箱里,不能直接访问文件系统、网络、传感器、剪贴板。所有操作都要通过宿主暴露的 Bridge。Spotube 提供的 Bridge 能力(节选):

Bridge能做什么
HttpClient发起 HTTP / HTTPS 请求(GET / POST / PUT / DELETE),支持 headers 和 body
FileStorage读写插件自己的私有目录(不影响其他插件,也不影响 Spotube 数据目录)
AuthTokenStore安全存储 OAuth token(Spotube 用 flutter_secure_storage 包做底层)
Logger打印 debug / info / warning / error 日志(出现在 Spotube 的 debug log 导出里)
UI(可选)弹出 Alert / Dialog / Snackbar 提示
Player(只读)读当前播放状态(Track / 进度 / 播放中)

Bridge 能力有限制——插件不能请求 Spotube 没暴露的能力。这是设计:沙箱保证安全,Bridge 列表可审计。

插件入口 main()

每个插件都需要一个顶层 main() 函数:

fun main() {
    val host = Zipline.get()
    host.register<MetadataSearchAPI>(MyMetadataPlugin())
    host.register<AudioAPI>(MyAudioPlugin())
    // 可以注册多个接口实现
}

这是 Spotube 加载插件时调的第一个函数。Zipline.get() 拿到宿主 Bridge,host.register() 告诉 Spotube "我能做 X"。

数据模型(节选)

Spotube 定义的核心数据模型(都在 plugin_interfaces 里):

data class Track(
    val id: String,                    // 插件内部唯一 ID(比如 Spotify track ID 或 YouTube video ID)
    val title: String,
    val artists: List<Artist>,
    val album: Album?,
    val durationMs: Long,
    val coverImageUrl: String?,
    val explicit: Boolean,
    val musicBrainzId: String?,         // 可选,MBID 让跨插件匹配更准
    val pluginId: String                // 哪个插件返回的
)

data class AudioStream(
    val url: String,                    // 音频流 URL
    val codec: String,                  // "mp3" / "aac" / "flac" / "opus"
    val bitrateKbps: Int?,
    val sampleRate: Int?,               // 44100 / 48000 / 96000 / 192000
    val bitDepth: Int?,                 // 16 / 24(FLAC 无损需要)
    val headers: Map<String, String>?   // 某些源需要额外 HTTP headers
)

接口可选性

插件不需要实现所有四个接口。最常见的选择: