尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Nuclear 插件开发指南:从目录结构、Manifest 到 Provider 注册与插件商店发布

Nuclear 插件开发指南:从目录结构、Manifest 到 Provider 注册与插件商店发布 Nuclear 插件开发指南从目录结构、Manifest 到 Provider 注册与插件商店发布【免费下载链接】nuclearStreaming music player that finds free music for you项目地址: https://gitcode.com/GitHub_Trending/nu/nuclear本篇技术文章基于 Nuclear 仓库内的插件编写技能文档 .agents/skills/writing-plugins/SKILL.md结合plugin-sdk包与播放器端插件加载器PluginLoader、esbuild-wasm 编译器的真实源码完整讲解 Nuclear 插件的运行模型、目录结构、package.jsonManifest 规范、Streaming/Metadata 两种 Provider 的注册方式、全部可用 API以及打包发布到插件商店的完整流程。读完后你可以独立搭建一个可被 Nuclear 在浏览器内编译加载的插件并完成从 GitHub Release 到plugin-registry提交的发布闭环。一、执行模型插件是独立仓库在浏览器内经 esbuild-wasm 编译Nuclear 插件是独立的仓库standalone repos不内置于主程序且在浏览器Tauri WebView内通过 esbuild-wasm 编译。理解这一模型是理解后续所有结构约定的前提。编译器实现在 pluginCompiler.ts 中源码注释与实现透露了几个关键设计决策为什么用 esbuild-wasmTauri WebView 里没有 Node 的fs也没有原生 esbuild 二进制因此插件即使写成 TypeScript也能在浏览器上下文中被编译后执行见 pluginCompiler.ts 顶部注释 L1-L21。虚拟文件系统编译器通过自定义的tauri-fsesbuild 插件用 Tauri 的readTextFile读取插件目录下的相对导入全程不接触 Node fspluginCompiler.ts。纯 JS 文件跳过编译compilePlugin只对.ts/.tsx入口做编译.js入口直接读取文本执行pluginCompiler.ts。SDK 被标记为 externalnuclearplayer/plugin-sdk不在插件 bundle 中打包而是运行时由宿主注入避免插件意外捆绑宿主依赖pluginCompiler.ts。输出为 CJS编译产物format: cjs、jsx: automatic、内联 sourcemap目标 ES2022因此插件默认导出必须兼容module.exports.default的 CommonJS 环境。编译缓存以入口路径为 key记录参与上一次构建的所有文件的内容哈希任何一个被导入文件发生变化都会触发重新编译保证编辑后 reload 拿到的是新代码pluginCompiler.ts。加载与沙箱化逻辑集中在 PluginLoader.ts插件代码最终通过new Function(exports, module, require, code)在受控环境中求值require是一个白名单 shim只允许四个模块——nuclearplayer/plugin-sdk、nuclearplayer/ui、react、react/jsx-runtime任何其它模块名都会抛出Module not foundPluginLoader.ts。插件必须导出一个默认对象否则加载失败。二、插件目录结构与入口点按 SKILL.md 的最小结构一个插件仓库长这样my-plugin/ package.json # 带 nuclear 元数据的 Manifest src/ index.ts # 入口点默认导出 NuclearPlugin如果插件使用本地构建工具如 tsup预先打包结构可以扩展为带dist/的形态参见 plugin-sdk README。生命周期钩子入口文件默认导出一个NuclearPlugin对象。SDK 的类型定义在 types.tsexport type NuclearPlugin { onLoad?(api: NuclearPluginAPI): void | Promisevoid; onUnload?(api: NuclearPluginAPI): void | Promisevoid; onEnable?(api: NuclearPluginAPI): void | Promisevoid; onDisable?(api: NuclearPluginAPI): void | Promisevoid; };四个钩子全部可选按 PluginLoader.ts 的逻辑load()在解析完代码后若检测到onLoad会立即调用并await。SKILL.md 给出的标准骨架import type { NuclearPlugin, NuclearPluginAPI } from nuclearplayer/plugin-sdk; const plugin: NuclearPlugin { onLoad(api: NuclearPluginAPI) {}, onEnable(api: NuclearPluginAPI) { // 在这里注册 Provider }, onDisable() { // 在这里注销 Provider }, onUnload() {}, }; export default plugin;各钩子语义plugin-sdk READMEonLoad(api)— 插件代码加载完成、Manifest 解析后执行onEnable(api)— 用户在设置中启用插件时执行注册 Provider 的正确时机onDisable(api)— 用户禁用插件时执行注销 Provider 的正确时机onUnload(api)— 插件从内存移除前执行。入口文件的解析顺序Manifest 未声明main时PluginLoader.ts 按以下候选顺序探测第一个存在的文件index.js、index.ts、index.tsx、dist/index.js、dist/index.ts、dist/index.tsx全部找不到则抛出明确的错误信息。注意 Manifest 层面对缺失main只产生警告will attempt fallback resolution不会阻断加载pluginManifest.ts。三、Manifestpackage.json 的字段规范SKILL.md 中的完整 Manifest 示例{ name: nuclear-plugin-example, version: 0.1.0, description: What this plugin does, author: Your Name, license: AGPL-3.0-only, main: src/index.ts, type: module, nuclear: { displayName: Example Plugin, category: streaming, icon: { type: link, link: https://example.com/icon.svg } }, dependencies: { nuclearplayer/plugin-sdk: ^1.1.0 } }字段校验zod Schema 的确切行为宿主端使用 zod 对package.json做严格校验实现在 pluginManifest.ts字段是否必填校验/归一化行为name必填非空字符串加载时 trim作为插件唯一 idversion必填非空字符串Semver 约定trimdescription必填非空字符串trimauthor必填非空字符串trimmain可选缺省时给出警告并走候选文件回退解析nuclear可选见下表整个 Schema 使用.passthrough()因此额外字段如license、dependencies不会导致校验失败。SDK 侧对应的类型定义在 types.tsnuclear子字段说明源码依据displayName展示名缺省回退到namePluginLoader.tscategory单分类过渡字段pluginManifest.ts源码注释标注待迁移到categories后移除categories分类数组缺省由category派生为单元素数组pluginManifest.tsicon仅支持{ type: link, link: url }严格模式拒绝多余键pluginManifest.tspermissions信息性声明解析时自动 trim、去重去重会写入警告、按字母序排序pluginManifest.ts另外nuclear中出现displayName/category/categories/icon/permissions之外的未知键也会产生警告pluginManifest.ts是排查拼写错误的线索。插件可用的categories取值SKILL.md 面向 Provider 注册场景列出了streaming、metadata、lyrics三类面向商店提交的完整合法值更多packages/docs的 publishing.md 给出的注册表合法分类为streaming、metadata、lyrics、scrobbling、dashboard、playlists、discovery、other且注册表提交时必填。SDK 端的ProviderKind类型与此呼应除了上述核心种类外还开放了任意字符串扩展providers.ts。四、Provider 类型与注册Streaming 和 MetadataStreaming Provider把曲目解析为可播放音频流SDK 中流媒体 Provider 的完整类型定义在 types/streaming.tsexport type StreamingProvider ProviderDescriptorstreaming { searchForTrack: ( artist: string, title: string, album?: string, ) PromiseStreamCandidate[]; searchForTrackV2?: (track: Track) PromiseStreamCandidate[]; getStreamUrl: (candidateId: string) PromiseStream; getStreamUrlV2?: (candidate: StreamCandidate) PromiseStream; supportsLocalFiles?: boolean; };SKILL.md 的最小实现示例const provider: StreamingProvider { id: my-streaming, kind: streaming, name: My Streaming, async searchForTrack(artist, title, album?) { /* return StreamCandidate[] */ }, async getStreamUrl(candidateId) { /* return Stream */ }, }; api.Providers.register(provider); api.Providers.unregister(my-streaming);两点源码层面的补充除了searchForTrack还实现了searchForTrackV2接收完整Track对象与getStreamUrlV2接收完整StreamCandidate宿主会优先使用 V2 签名以便传递更完整的上下文——从类型定义的结构可以推断 V2 是新增能力的向后兼容扩展。id/kind/name来自通用描述符ProviderDescriptorproviders.ts注册进宿主后由ProvidersHost统一管理register返回 provider idunregister返回是否成功另外还提供list(kind?)、get、getActive、setActive、subscribe等方法providers.ts。api.Providers门面封装在 api/providers.ts直接透传ProvidersHost。Metadata Provider搜索艺术家/专辑并拉取详情SKILL.md 给出的元数据 Provider 形态const provider: MetadataProvider { id: my-metadata, kind: metadata, name: My Metadata, searchCapabilities: [artists, albums], streamingProviderId: my-streaming, // 可选将流媒体锁定到指定 Provider async searchArtists(params) { /* ... */ }, async searchAlbums(params) { /* ... */ }, async fetchArtistBio(id) { /* ... */ }, async fetchAlbumDetails(id) { /* ... */ }, };其中searchCapabilities声明该 Provider 支持的搜索维度streamingProviderId是可选字段声明后使用该元数据源检索出的曲目会锁定由对应 streaming Provider 解析流地址保证能搜到就能播。宿主侧暴露给插件反查元数据的能力由MetadataHost类型定义types/metadata.tssearch、fetchArtistBio、fetchArtistSocialStats、fetchArtistAlbums、fetchArtistTopTracks、fetchArtistPlaylists、fetchArtistRelatedArtists、fetchAlbumDetails。params/Album/ArtistBio等模型类型统一来自nuclearplayer/model包并由 plugin-sdk 入口 再导出插件直接import type { Album, Track, ArtistCredit } from nuclearplayer/plugin-sdk即可使用。五、可用 API 全览SKILL.md 列出的核心 APIapi.Providers— 注册/注销 Providerapi.Settings— 插件设置存储api.Http— fetch 封装api.Ytdlp— yt-dlp 集成api.Queue— 播放队列控制api.Metadata— 搜索音乐元数据api.Streaming— 解析流地址api.Logger— 结构化日志trace/debug/info/warn/error实际上 SDK 提供的NuclearPluginAPI即NuclearAPI的子类覆盖面更广api/index.ts 中共暴露 15 个域 APIAPI用途api.Settings定义、读取、持久化插件设置并可注册自定义设置控件widgetapi.Providers注册/注销 Providerapi.Queue读取与操作播放队列api.Streaming通过候选曲目解析音频流api.Metadata搜索/拉取艺术家、专辑、曲目详情api.Http从插件发起 HTTP 请求并绕过 CORSapi.Ytdlpyt-dlp 搜索与流信息api.Favorites管理用户收藏曲目api.Logger结构化日志api.Dashboard获取仪表盘内容热门曲目、新发行等api.Discovery从 Provider 获取曲目推荐api.Playback控制播放、音量、随机、循环api.Playlists创建、更新、删除播放列表api.Events订阅播放器生命周期事件如曲目播放结束api.Shell在系统浏览器中打开 URL宿主为每个插件实例化这套 API 的接线代码在 createPluginAPI.ts其中Settings使用按pluginId/displayName隔离的设置宿主createPluginSettingsHostLogger使用按插件 id 划分的日志宿主createLoggerHost其余为进程内共享的单例宿主。以api.Settings为例api/settings.ts 提供了register(SettingDefinition[])声明式定义设置项、get/set读写插件私有设置、getGlobal/setGlobal读写全局设置、subscribe监听变更以及registerWidget/unregisterWidget注册自定义 React 设置控件控件渲染依赖编译时注入的nuclearplayer/ui与react白名单模块。SDK 还导出useSettingReact hookindex.ts供 TSX 插件消费。六、本地开发与调试流程SDK README 给出的快速起步mkdir my-plugin cd my-plugin pnpm init -y pnpm add nuclearplayer/plugin-sdk创建src/index.ts并默认导出生命周期对象后可以按需选择两条路径不预编译直接把.ts源码打包进plugin.zipNuclear 会用 esbuild-wasm 即时编译预编译推荐加载更快用任意能输出单文件的 bundler。SDK README 给出的 tsup 示例{ devDependencies: { tsup: ^8 }, scripts: { build: tsup src/index.ts --dts --format cjs --minify --out-dir dist } }产物必须兼容 CommonJS 环境module.exports或exports.default。日常开发循环是修改代码 → 重新构建 → 在 Nuclear 中重新加载插件。注意每次改动后都需要 reload 插件才能生效编译缓存的失效逻辑前文第二节保证 reload 拿到的一定是重新编译后的代码。七、发布GitHub Release 注册表提交Nuclear 插件商店由一个静态注册表支撑注册表只保存插件元数据名称、仓库、分类插件代码本体留在开发者自己的 GitHub 仓库中用户安装时Nuclear 从该仓库拉取最新 GitHub Release 的plugin.zip。发布共三步1. 打 tag 触发自动发版按 SKILL.md在插件仓库添加.github/workflows/release.ymlname: Release on: push: tags: [v*] jobs: release: runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkoutv4 - run: zip -r plugin.zip src package.json README.md - uses: softprops/action-gh-releasev2 with: files: plugin.zip generate_release_notes: true然后打 tag 推送git tag v0.1.0 git push origin v0.1.0。2. plugin.zip 的内容约定publishing.md 明确Nuclear 只认最新 Release 中名为plugin.zip的资源缺失即安装失败。zip 内文件必须位于根层级不能套子目录plugin.zip ├── index.js # 入口或 main 指向的文件 ├── package.json # 插件元数据 └── ... # 插件需要的其它文件如果 zip 里是my-plugin/index.js这种带子目录的结构插件将无法加载。由于 Nuclear 支持即时编译 TSzip 里可以直接放.ts/.tsx源码但预编译产物加载更快。3. 向 plugin-registry 提交 PRForkNuclearPlayer/plugin-registry把插件条目加入plugins.json的plugins数组并发起 PR。条目字段约束publishing.md字段必填约束id是必须与package.json的name一致小写、连字符、2-64 字符name是展示名1-64 字符description是10-200 字符author是1-64 字符repo是owner/repo-name格式category是必须与package.json的nuclear.category一致categories是与插件 Provider 类型匹配的分类数组tags否至多 10 个小写连字符、去重version否由 CI 自动填充downloadUrl否最新plugin.zip直链由 CI 自动填充addedAt是ISO 8601 时间戳示例条目{ id: nuclear-plugin-discogs, name: Discogs, description: Fetch album and artist metadata from Discogs, author: nukeop, repo: NuclearPlayer/nuclear-plugin-discogs, category: metadata, categories: [metadata], tags: [discogs, metadata], version: 1.0.0, downloadUrl: https://github.com/NuclearPlayer/nuclear-plugin-discogs/releases/download/v1.0.0/plugin.zip, addedAt: 2026-01-25T00:00:00Z }版本更新策略发布新版本不需要改注册表只需创建带新plugin.zip的 GitHub Release用户下次安装或自动更新时即会拉到。Nuclear 在启动时检查插件更新自动更新默认开启用户可在设置的 Plugins 页关闭见 publishing.md 及宿主侧自动更新实现 pluginAutoUpdate.ts。只有变更描述、分类、标签等元数据时才需要再提注册表 PR。参考路径汇总技能文档.agents/skills/writing-plugins/SKILL.mdSDK 与入门packages/plugin-sdk/README.md、packages/plugin-sdk/src/index.ts、packages/plugin-sdk/src/types.tsProvider/流媒体/元数据类型packages/plugin-sdk/src/types/providers.ts、packages/plugin-sdk/src/types/streaming.ts、packages/plugin-sdk/src/types/metadata.ts宿主加载链PluginLoader.ts、pluginCompiler.ts、pluginManifest.ts、createPluginAPI.ts发布文档packages/docs/plugins/publishing.md以及 providers.md、streaming.md、plugin-system.md 等专题文档可进一步深入【免费下载链接】nuclearStreaming music player that finds free music for you项目地址: https://gitcode.com/GitHub_Trending/nu/nuclear创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表