Hexo Butterfly 集成 Mineradio 粒子音乐播放器
Hexo Butterfly 集成 Mineradio 粒子音乐播放器
本文记录如何把 Mineradio 的粒子视觉和播放器界面移植到 Hexo Butterfly,并改造成一个不依赖自建音乐服务端的独立静态页面。
最终效果可以在本站的八音盒中查看:进入页面后直接显示 Mineradio,左侧为固定网易云歌单,右上角“视觉设置”可以切换粒子预设和调整视觉参数。
一、最终方案
这次没有把播放器嵌入普通文章页,而是把它放在一个独立目录中:
1 | source/relaxation/music/ |
访问 /relaxation/music/ 时,浏览器直接打开这个静态页面,不经过 Butterfly 的文章布局。这样可以避免主题导航栏、侧边栏、文章容器和播放器的全屏视觉互相挤压。
整个页面只保留以下能力:
| 功能 | 实现方式 |
|---|---|
| 播放列表 | 从指定网易云歌单读取,同时保留本地快照兜底 |
| 歌曲搜索 | 只搜索当前已经载入的歌单,不调用搜索服务 |
| 音频、封面、歌词 | 使用歌单接口返回的静态资源地址 |
| 粒子动画 | 沿用 Mineradio 的 Three.js 视觉模块 |
| 视觉设置 | 保留粒子预设、颜色、歌词和动态参数 |
| 页面数据 | 使用浏览器内存和 localStorage,不写入服务端 |
这里的“纯前端”是指不需要自己部署 Node.js、NeteaseCloudMusicApi 或数据库。页面仍然需要访问网易云相关资源和 Meting 歌单接口,因此离线状态、接口跨域策略或第三方服务变化都会影响播放。
二、准备工作
建议先准备好以下环境:
- Node.js 18 或更高版本
- 一个可以正常生成的 Hexo 博客
- Butterfly 主题
- 一个公开可访问的网易云歌单
在博客根目录安装依赖:
1 | npm install |
不需要全局安装 Hexo。本文后续统一使用项目内的 npm scripts:
1 | npm run server |
Mineradio 使用 GPL-3.0 许可证。分发修改后的页面时,请保留 LICENSE、NOTICE.md、原项目署名和对应源码,并自行确认自己的发布方式符合许可证与音乐平台规则。
三、复制播放器静态目录
为了保证 Shader、粒子预设、歌词渲染和播放器状态模块的加载顺序一致,最稳妥的方式是复制完整的静态目录,而不是只摘取某一个 Three.js 文件。
示例成品目录位于:
1405720461/02 - source/relaxation/music
1 | git clone --depth 1 https://github.com/1405720461/02.git mineradio-hexo-example |
1 | git clone --depth 1 https://github.com/1405720461/02.git mineradio-hexo-example |
目录中的几个关键文件分别负责:
| 文件 | 用途 |
|---|---|
index.html |
Mineradio 完整页面结构及资源加载入口 |
css/index.css |
原播放器视觉样式 |
css/static-blog.css |
博客静态版裁剪和响应式适配 |
js/index-loader.js |
按原有顺序加载播放器、歌词和粒子模块 |
js/static-adapter.js |
在浏览器内兼容原项目的 /api/* 调用 |
js/static-bootstrap.js |
读取歌单、建立固定播放队列并初始化页面 |
playlist-cache.json |
远程歌单请求失败时使用的本地快照 |
vendor/ |
Three.js、GSAP、music-tempo 等浏览器依赖 |
四、让 Hexo 原样复制播放器
Hexo 默认会处理 source 下的页面。Mineradio 已经是完整 HTML,不应该再次套入 Markdown 渲染器或 Butterfly 模板,所以要在博客根目录的 _config.yml 中增加 skip_render:
1 | skip_render: |
如果原来已经配置了多个目录,可以继续放在数组中:
1 | skip_render: |
生成后应当存在 public/relaxation/music/index.html。如果只有一个 Butterfly 文章页面,通常说明 skip_render 的路径或缩进写错了。
五、添加 Butterfly 菜单入口
打开 _config.butterfly.yml,在 menu 中增加八音盒入口:
1 | menu: |
这个链接会离开 Butterfly 的普通内容页,直接进入 Mineradio 全屏页面。播放器左上角的返回按钮再回到博客首页。
不建议把整个播放器放进文章正文或 iframe,主要原因有三个:
- Butterfly 的文章宽度会压缩 3D 舞台和左右面板。
- iframe 会增加音频自动播放、全屏和移动端手势的限制。
- 主题的全局播放器、歌词栏和 Mineradio 会同时占用底部空间。
六、绑定自己的网易云歌单
网易云歌单地址通常类似:
1 | https://music.163.com/#/playlist?id=2733900165 |
其中 2733900165 就是歌单 ID。打开 js/static-bootstrap.js,修改以下两项:
1 | var PLAYLIST_ID = '2733900165'; |
页面会请求:
1 | https://api.i-meto.com/meting/api?server=netease&type=playlist&id=你的歌单ID |
请求返回的歌曲会被统一整理为 Mineradio 需要的数据结构:
1 | { |
还要同步修改 js/static-adapter.js 中返回的歌单信息,避免界面中仍然显示旧 ID:
1 | playlist: { |
生成本地歌单快照
playlist-cache.json 可以让页面先显示上一次成功获取的歌单,再在后台尝试刷新远程数据。更换歌单后,建议重新生成快照。
1 | $playlistId = "2733900165" |
1 | PLAYLIST_ID="2733900165" |
七、用前端适配层替代服务端 API
原版 Mineradio 的多个模块会访问 /api/song/url、/api/lyric、/api/search 等接口。直接把页面复制到 Hexo 后,这些地址会返回 404,这也是搜索列表经常显示“搜索失败”的原因。
这里没有重写全部播放器模块,而是在 static-adapter.js 中保存原生 fetch,再拦截同域名的 /api/* 请求:
1 | var nativeFetch = window.fetch.bind(window); |
这样原播放器仍然以为自己拿到了 API 响应,实际数据全部来自浏览器中已经载入的 tracks。
本地歌单搜索
搜索功能只过滤固定歌单,不请求网易云搜索服务:
1 | var query = String( |
点击搜索结果时,只切换到该歌曲播放,原歌单和播放队列的顺序保持不变。
歌词请求
如果歌单数据中已经有歌词,就直接使用;否则读取歌曲的 lrc 地址,并在内存中缓存结果:
1 | var response = await nativeFetch(track.lrc, { |
八、裁剪不适合纯前端的功能
原项目包含登录、评论、每日推荐、多平台账户、桌面端热键和本地文件等能力。静态博客版没有配套服务端时,应当关闭这些入口,避免用户点击后得到失败提示。
css/static-blog.css 中可以统一隐藏:
1 | #login-btn, |
适配层还会为空的推荐、登录状态和上报接口返回可识别的静态响应,避免原模块持续报错:
1 | if (path === '/api/listen/report') { |
九、保留粒子控制面板
粒子预设和视觉参数仍然使用 Mineradio 原来的模块。页面右上角保留一个有文字的入口:
1 | <button |
控制面板可以调整粒子预设、主色、歌词、镜头、动态和性能参数。配置保存在浏览器 localStorage 中,因此不需要数据库,但清除站点数据后会恢复默认值。
移动端还要注意:
- 歌单和视觉设置入口分别放在搜索框下方两侧。
- 打开面板后隐藏入口,避免按钮压在面板内容上。
- 面板底部要避开播放器和
safe-area-inset-bottom。 - 所有图标按钮使用固定宽高,避免 SVG 看起来偏移。
十、移除 Butterfly 原有音乐播放器
如果博客之前启用了 APlayer 或 MetingJS,建议关闭全局注入,否则进入其他页面时仍会看到旧播放器和滚动歌词。
项目根目录 _config.yml:
1 | aplayer: |
Butterfly 配置中的播放器注入也需要按自己的主题版本关闭,或者至少排除八音盒路径:
1 | aplayerInject: |
如果你使用的是自定义底部播放器,还需要从 _config.butterfly.yml 的 inject.bottom 中移除对应脚本,并检查自定义 CSS 或主题布局文件中是否仍有歌词容器。
十一、本地验证与构建
先生成站点:
1 | npm run build -- --force |
再启动本地服务:
1 | npm run server |
打开:
1 | http://localhost:4000/relaxation/music/ |
建议至少检查以下内容:
- 页面进入后没有 Butterfly 文章容器和启动遮罩。
- 歌单数量和
playlist-cache.json一致。 - 点击歌单和搜索结果都能切歌,队列顺序不变。
- “视觉设置”可以打开和关闭,粒子预设可以切换。
- 桌面端没有横向滚动条。
- 390 × 844 等移动端尺寸下,歌单可以展开、收起和重新打开。
- 浏览器控制台没有未处理异常和
/api/*404。
常见问题排查
页面显示 404
检查 _config.yml 的 skip_render 是否为 relaxation/music/**,并确认 public/relaxation/music/index.html 已生成。
歌单载入失败
先直接访问 Meting 请求地址,确认返回的是 JSON 数组;再检查歌单是否公开、歌单 ID 是否正确,以及 playlist-cache.json 是否是有效 JSON。
搜索始终失败
打开浏览器网络面板。如果 /api/search 返回 404,说明 static-adapter.js 没有在原播放器模块之前加载。它应当放在 <head> 中,并早于 js/index-loader.js。
有封面但无法播放
音频地址可能已经过期、歌曲需要会员、资源只提供试听,或当前网络无法访问对应域名。刷新页面可以重新获取歌单,但纯前端方案不能绕过平台版权和会员限制。
移动端不能自动播放
这是浏览器的自动播放策略。让用户主动点击一次播放按钮后再创建或恢复 AudioContext,不要尝试强制绕过。
十二、这个方案的边界
纯前端版本适合个人博客展示和播放公开歌单,但它不是网易云官方播放器,也不能替代完整的音乐服务端。
尤其需要注意:
- VIP 或版权受限歌曲是否只能试听,由音乐平台和实际音频地址决定。
- 仅在前端填写网易云账号信息并不能安全获得完整会员能力,也不应把 Cookie 写进公开仓库。
- 第三方接口、音频地址、歌词地址和跨域规则可能变化,需要保留本地快照和失败提示。
- 歌曲、封面和歌词的使用应遵守对应平台协议与版权规则。
如果以后确实需要账号授权、稳定会员音质或私人歌单,就应重新评估服务端代理、凭证保管和访问控制,而不是继续把敏感信息堆在前端。
