168
0
0

顺雅(Msapi)开发状态

文章摘要

顺雅(Msapi)是一套可自托管的网页音乐播放器:服务端自己部署,前台一行 <script> 就能嵌进任何网站。本文是这个项目的完整状态说明——它是什么、能做什么、架构如何、经历了哪些版本、目前处于什么阶段、以及已知的边界在哪里。

当前版本 v1.6.9(2026-10-20),共发布 11 个版本,许可全文见仓库 LICENSE。v1.6.5 之后的一批改动——后台路由改为目录式入口、密钥删除级联清理、歌单配置导出导入、域名开关按用户独立、配置入库——已随 v1.6.9 打包发布,逐条见第六节。

一、项目是什么

它解决的是一个很具体的问题:想给自己的网站加一个好看、可控、不依赖第三方平台的音乐播放器。

市面上的选择大致两类:要么是纯前端组件(播放器有了,但歌单得自己维护),要么是第三方托管服务(省事,但内容与数据都在别人那里)。顺雅位于两者之间:

  • 服务端自己部署:密钥、歌单、播放器配置都在你自己的服务器与数据库里。
  • 前台一行接入:不需要改宿主站的构建流程,也不需要引入框架。
  • 界面完全自绘:播放器界面全部运行在封闭的 Shadow DOM 内,不污染宿主页面的样式。

二、能做什么

2.1 播放器界面

  • 两套内置皮肤,后台随时切换:
    • 悬浮玻璃:可拖动的悬浮面板,支持边缘吸附与沉浸全屏。
    • 玫瑰玻璃:常驻侧边抽屉,可收起为贴边把柄;封面环形播放进度、播放时顺时针旋转(暂停冻结角度不回正);歌曲列表从所点击的按钮位置放大展开;移动端为底部抽屉,可上下拖动并吸附半屏/全屏。
  • 歌词:LRC 解析与逐行同步,窄屏下自动跑马灯,且只在换行时重排、匀速滚动。
  • 音量与增强:音量滑杆呼出式交互,支持 1x / 2x / 3x 音量增强。
  • 播放模式:顺序、单曲循环、随机。
  • 不压住宿主导航:自动探测宿主固定在底部的导航栏/工具条高度,播放器与歌词框抬到它上方;探测要求真贴底且宽度、高度、可点子项都符合,悬浮球与 cookie 横幅不会被误判;后台可按域名改成固定值。
  • 失败会说清原因:缺密钥、密钥无效、密钥被停用、请求太频繁、连不上服务器、域名未授权、密钥与域名不匹配、歌单为空……各有一条独立提示,不再一律显示同一句话。
  • 对宿主页面克制:不动宿主的滚动条与全局样式;播放器区域内的右键菜单、文本选中与图片拖拽均已禁用,但宿主页面不受影响。
  • 换页不掉链子:对 Swup / Pjax 这类无刷新换页框架做了去重,来回切页面不会出现两个播放器同时出声。
  • 二次打开更快:可选接入 Redis,把上游解析结果(播放地址/歌词/封面)、歌单配置与播放状态缓存起来——同一首歌再次点开不再回源,换设备也能接着上次的位置。不接也不影响使用。
  • 封面可从自有存储出图:把封面本地化到自己的对象存储后,播放器直接用自家存储的图片,不再受第三方图床可用性与防盗链的影响。

2.2 后台管理

  • 密钥管理:为不同站点签发不同密钥,可给密钥绑定授权域名(拿错密钥访问会被直接拦住并说明原因),普通用户有创建数量限制;删除密钥会连带清掉名下歌单与歌曲,不留孤儿数据。
  • 歌单配置导出导入:每个密钥的歌单可导出为 JSON(只含歌单数据,不含任何密钥信息),换站或重装时一键导回。
  • 域名授权:按宿主主机名决定谁能加载播放器;授权与「自动登记新域名」两个开关按用户独立(互不影响),自动登记默认关闭,也能为每个域名单独设置歌词框与播放器的底部修正量。
  • 歌单与歌曲:支持远程歌单快照、歌曲排序、封面缓存;同步与编辑保存时自动清掉上游已下架的歌曲。
  • 播放器配置:皮肤选择、每个皮肤各自的初始位置(停靠边 + 垂直百分比);可指定一条密钥作为落地页演示播放器。
  • 存储设置(“设置 → 储存”):对象存储、封面本地化的按歌单进度、Redis 播放器缓存(含连通性自检、TTL 与手动开关)三块。
  • 账号与权限:超级管理员 / 管理员 / 普通用户三级,人员与设置仅管理员可见。
  • 邮件模板:所有通知邮件均可自定义,含 HTML 与纯文本两份。
  • 安装与升级:图形化安装器;数据结构统一到 SQL 单一来源,升级不需要手工改表;配置文件收敛为只放数据库连接,其余配置入库统一管理。

三、技术架构

v1.5.0 之后,播放器分成两层:

modules/
  core/        引擎:鉴权、状态、音频内核、界面契约、歌词、主题、底部避让(全站只有一份)
  router/      皮肤①:悬浮玻璃
  rose/        皮肤②:玫瑰玻璃
  registry.php 清单装载器
  • 引擎与皮肤分离:播放能力全部收在 core/;皮肤只负责界面与交互。
  • 皮肤由清单自述:每个皮肤一个目录 + 一份 skin.json,registry.php 统一装载;后台的皮肤选择器读的就是同一份清单。
  • 皮肤契约:皮肤需实现 loadCSS / createWidget / getHTML / _css 四项,引擎通过 data-mp 属性绑定元素。
  • 新增皮肤的成本:一个目录 + 一份清单 + 界面实现,不需要改动引擎与入口。
  • 不依赖第三方 CDN:APlayer 1.10.1 只作音频内核,随包自托管(assets/lib/aplayer/),界面样式全部自绘。
  • 可选缓存层(v1.6.5):admin/lib/redis.php 一条缓存通道,装了 phpredis 走原生扩展、没装走纯 PHP RESP 实现,两者对外契约一致,上层不用改代码。
  • 后台目录式路由(v1.6.9):后台页面从单文件改为 admin/pages/<action>/index.php 目录入口,靠 nginx 内置的目录索引与自动补斜杠工作,零 rewrite / try_files 配置,任何主机都能直接部署。
  • 配置入库(v1.6.9):站点 / 后台 / 接口配置与签名密钥的权威副本在数据库,按功能分组、后台可编辑;config.php 只留数据库连接这一段(库不可达时唯一的兜底),由统一入口合并读取。

四、接入方式

  1. 部署服务端,用图形化安装器完成建库与初始化。
  2. 在后台创建一个密钥(可选:给密钥绑定授权域名,这条密钥就只能在该域名下使用)。
  3. 把下面这行放进任意网站的 <body> 里:
<script src="https://你的域名/api.php?key=你的密钥" defer></script>

想让它出现得更快,可以在 <head> 里加一行预连接:

<link rel="preconnect" href="https://你的域名" crossorigin>

密钥走查询参数而不是标签属性——因为标签属性服务端读不到。旧写法与旧入口已在 v1.5.0 下线。

五、全部版本时间线

版本日期主题一句话
v1.0.009-23首个正式版服务本体、安装器、鉴权与可嵌入的播放器
v1.1.009-24后台重构单入口路由、邮件模板、封面缓存
v1.2.009-24换页修复页面脚本外置加载
v1.3.009-24降低门槛去掉激活校验,令牌自动签发
v1.4.009-27播放器重构路由式模块化架构
v1.4.210-01结构统一SQL 单一来源、多实例、歌单快照
v1.5.010-04引擎与皮肤分离新增玫瑰玻璃皮肤、按皮肤存位置
v1.6.010-06面向宿主站点域名授权与自动登记、底部避让重做、失败原因分开提示
v1.6.210-08宿主站点收敛·第二回合底栏探测重写、让出量分 PC 与移动端、域名授权可指定密钥、沉浸式歌单弹窗贴底
v1.6.510-10往数据层走Redis 播放器数据缓存、封面本地化、后台交互硬伤修复
v1.6.910-20结构与数据收口后台目录式路由、密钥删除级联清理、歌单导出导入、域名开关按用户独立、配置入库

六、逐版本详解

v1.0.0(2026-09-23)首个正式版

把整件事的骨架立起来:音乐 API 服务本体(密钥鉴权、歌单与歌曲数据接口)、图形化安装器、JWT 令牌签发,以及一行 <script> 即可嵌入网站的播放器模块。

v1.1.0(2026-09-24)后台路由重构与邮件模板

  • 后台单入口路由:所有页面走统一入口,页面之间切换不再整页刷新
  • 新增邮件模板管理与封面缓存
  • 修复切换页面后页面脚本未加载的问题;密钥改为掩码显示

v1.2.0(2026-09-24)页面脚本外置加载

页面脚本改为外置加载,彻底修复无刷新切换页面后脚本失效的问题,并修复由此连带的密钥页与导航问题。v1.1 与 v1.2 其实是连着修同一件事:v1.1 先补一手,v1.2 换成外置加载才算根治。

v1.3.0(2026-09-24)自动签发播放器令牌

移除账户激活与到期校验,令牌改由后台自动签发。接入成本从「部署 + 建密钥 + 配令牌」降到只需「部署 + 建密钥」。

v1.4.0(2026-09-27)播放器模块化架构

播放器拆分为路由式模块化架构,核心、皮肤、交互各自成模块按需加载。「皮肤」这个概念第一次出现在项目里——虽然那时还只有一套。

v1.4.2(2026-10-01)结构统一与多实例播放

  • 安装与升级统一到 SQL 单一来源,不再依赖两套结构定义
  • 播放器支持同一页面多个实例
  • 后台新增歌曲排序与远程歌单快照
  • 重写文档、补充界面截图;添加 MIT 许可证(后续版本已更新许可)

v1.5.0(2026-10-04)引擎与皮肤彻底分离

目前结构改动最大的一版:43 个文件、约 3200 行增删。

  • 架构分层:播放能力收进 modules/core/,皮肤只画界面;皮肤由清单自述、统一装载。
  • 新增「玫瑰玻璃」皮肤:侧边抽屉、封面环形进度与播放旋转、音量增强、歌曲列表弹层、移动端可拖动抽屉。
  • 初始位置按皮肤分别保存:切换皮肤时各自记住自己的停靠边与垂直位置。
  • 嵌入方式统一:只剩一行 ?key= 写法,旧入口与旧写法下线。
  • 一批修复:换页框架下重复加载导致声音重叠;歌词跑马灯在移动端反复抽搐;切换皮肤时偶发的停靠方向判反导致播放器整屉飞出屏幕;后台登录后设置未恢复导致保存时覆盖数据库配置;后台皮肤卡片保存或换页后点不动。
  • 清理:移除旧版单体皮肤、路由旧入口与旧转发壳,仓库不再留冗余文件。

v1.6.0(2026-10-06)面向宿主站点收敛

这一版几乎都在处理「播放器嵌在别人站上」的各种意外——不再是播放器自己能不能跑,而是它别给宿主站添乱。

  • 底部避让重做:底部占用改由一个专门的探测模块负责,只认真正贴底的导航栏(高度、宽度、可点子项都要满足),悬浮球、横幅、吸底广告不再被当成底栏;差值要连续两次采样一致才生效,换页、转屏、DOM 变化都会重测,另有定时兜底。
  • 失败原因分开说:缺密钥、密钥无效、密钥停用、限流、服务不可达、域名未授权、密钥与域名不匹配、歌单为空、歌单加载失败等各有独立提示,同一种提示不会叠加;启动失败时也不再补一条「后台还没有歌单」把真正原因盖住。
  • 域名授权:新增域名页与「自动登记」,按宿主主机名放行或拦截(主机名精确匹配,不做 www 与子域合并);密钥也能绑定授权域名,在别的域名下用会被直接拦住并说明是哪条密钥。
  • 后台细节:设置页拆成站点 / 邮件 / 安全 / 接口 / 存储五个子页;新建密钥改为弹窗,带名称与授权域名;提交表单与切换开关不再整页刷新,页内的测试播放器不会再被刷掉。
  • 安全与清理:在线用户接口补管理员门槛,前端错误上报接口补限流与内容清洗并改写服务器日志;移除没有界面入口的调试开关与空占位文件。

v1.6.2(2026-10-08)宿主站点收敛,第二回合

v1.6.0 发布之后,真正接到站上又暴露出几个问题,这一版都在收拾它们——底栏探测的判定、让出量的计算范围、域名授权的粒度,以及移动端沉浸式歌单弹窗的一个定位错误。

  • 底部避让的判定太严:v1.6.0 那套探测要求底栏「真贴底」(与视口底边只差几个像素)、高度 ≥40px、至少 3 个链接、嵌套不超过 2 层。拿 8 种真实底栏形态一测,只认出 3 种——矮一点的栏、纯 div 拼的 tab、离底十几像素的悬浮胶囊全漏了。重写后这 8 种全部认出,没有底栏的页面依然不会误判;悬浮胶囊按「离底距离 + 自身高度」算真实占位,不再拿元素整体高度去估。
  • 让位的只有歌词框:播放器本体与拖拽的边界夹取此前根本不看让出量,底栏一高,播放器照样被压住。现在初始位置、窗口缩放回退、拖拽吸附三处都吃这个值:实测底栏 56px,播放器最低点正好上移 56px,与底栏顶部的间隙保持不变。
  • 让出量分成 PC 与移动端两套:后台域名页从「一个自动开关 + 两行修正」改成 PC / 移动端各一块,各有自动开关、歌词修正、播放器修正。客户端每 60 秒把实测到的底栏高度报回后台,直接显示成「最近探测」,可一键填进修正量。自动开着是「探测值 + 修正量 + 8px 保护带」,关掉就只按修正量让位。
  • 域名授权可以指定密钥:一个账号签发了多条密钥时,可以只让某一条密钥使用某个域名,其余密钥不受影响;不指定就是该账号所有密钥共用(老数据保持原样)。权限同时收紧:超级管理员能改所有人的授权,管理员与普通用户只能改自己名下的。
  • 移动端沉浸式歌单弹窗贴回屏幕底边:沉浸式是全屏遮罩,宿主底栏本来就被它盖住,弹窗却还按宿主底栏的高度往上让位,结果弹窗下面空出一条、露出宿主页的底栏。现在弹窗直接贴屏幕底边,底部内边距改用 iOS 安全区。
  • 顺手修掉的两处:域名备注保存后被写成 0;普通用户拿别人那行的 id 提交保存,会在自己名下凭空多出一行。

v1.6.5(2026-10-10)播放器数据缓存与封面本地化

前几版都在收拾「嵌在别人站上」的问题,这一版换了方向——往自己的数据层走:一边让播放器不再重复回源,一边把封面从第三方图床搬到自己能管的地方。

  • Redis 播放器数据缓存(新增):缓存三类数据——上游解析结果(播放地址、歌词、封面)、歌单配置 JSON、播放器状态(按 IP + 设备校验)。播放地址这类短时效数据用短 TTL,歌词与封面用长 TTL。实测同一首歌第二次播放从约 700ms 降到 8ms 量级。
  • 缓存的取舍写死在设计里:手动开关优先级最高——关掉即完全不使用;开启时按连通性自动生效 / 自动停用,服务端恢复后自动继续,不需要人工干预;连不上只是跳过缓存(fail-open),绝不把错误抛给访客,也不会把 PHP 警告写进接口响应。
  • 封面本地化(新增):封面按内容寻址存进自己的对象存储,同一张图跨歌单只存一份;删除歌单先释放索引,无人使用的图片进回收池、过了保留期才真正删除——删了又加回来能直接复用。后台可对整个歌单分批预热,也能一键迁移历史封面。
  • 存储设置页:「设置 → 储存」现在有三块——对象存储、封面本地化进度(每个歌单一张卡片,本地还没有歌的会如实标注)、Redis 播放器缓存(含连通性自检与各项 TTL)。
  • 后台交互的几处硬伤:表单提交改用隐藏字段承载「点了哪个按钮」,修掉「点保存没反应、配置像回弹」;校验失效时给出明确提示而不是一页报错;封面卡片只统计当前管理员名下的歌单,别人的歌单不再混进来。
  • 播放器与后台重构:歌词、播放器与提示层继续拆分重写,沉浸模式与播放状态云同步进入常规能力。
  • 数据层:增量同步增加「结构指纹」短路(稳态下不再逐条比对表结构);MySQL 与 SQLite 双驱动的结构同步、索引与列修补;新增配置项的自动播种。
  • 清理:删掉 9 个失去入口的文件(两端各一套未使用的 skin / tokens 样式与 users 脚本、一个 extract 处理器、两份过时的设计文档,内容已并入代码与本文)。

v1.6.9(2026-10-20)后台路由与数据的结构收口

v1.6.5 之后的使用中暴露的几类问题——部署门槛、删数据不干净、配置两处存放互相打架——这一版集中收口,86 个文件、约 7400 行增删,是结构层面最大的一次整理。

  • 后台路由改为目录式入口:后台页面从 admin/pages/<action>.php 单文件改为 admin/pages/<action>/index.php 目录索引,靠 nginx 内置的目录索引与自动补斜杠工作——零 rewrite / try_files 配置,虚拟主机、宝塔、默认站点都能直接部署;判定链收进统一的 front.php / boot.php。顺带下线了在线歌厅(房间)功能与后台 service 层。
  • 密钥删除改为级联清理:此前删除密钥只删密钥一行,名下的歌单与歌曲全部变成永远留在库里的孤儿数据(实测库里积累出 6 个孤儿歌单、44 首孤儿歌曲)。现在删除会连带清掉歌单与歌曲、释放无引用的歌曲封面、解绑域名授权(域名本身与授权状态保留——那是用户资产);删除前先做归属校验,确认框如实展示「将连带删除:N 个歌单、M 首歌」。
  • 歌单配置导出与一键导入:每个密钥的歌单可导出为 JSON——只含歌单与歌曲数据,不含密钥、密钥名与任何归属信息,导出文件名带密钥名 + 用户名 + 时间戳;换站或重装时选文件导入,同名歌单自动跳过。入口在配置页每个密钥的卡片里。
  • 同步时删除上游已下架的歌曲:歌单同步与编辑保存会把上游已经下架的歌从本地快照里删掉(本轮同步开始前就已缺失的才删,两轮确认防误删),保存后的提示会写清「新增 X · 删除 X · 下架 X」。
  • 域名开关按用户独立:「启用域名授权」与「自动添加检测到的域名」从全站一份改为每个用户一套——A 用户关掉自动添加不再影响 B 用户,运行时按密钥主人取值;「自动添加」默认关闭(它会让任何主机名自动写进域名表并放行,缺省开等于新装即可被任意站白嫖);超管新增「用户开关总览」只读视图,能看清每个用户的生效值。
  • 配置文件收敛为仅数据库连接:config.php 从「site / admin / api / 密钥全在文件」收敛为只留 db 一段——其余配置的权威副本入库(按功能分组、后台可编辑),由统一合并入口读取并兼容旧结构回落;安装器同步新结构,签名密钥与演示密钥物理隔离成独立配置行,整行 REPLACE 不会再把它们冲掉。
  • 一批修复:安装向导守卫在深层路径下的连环 302(相对跳转逐层命中,实测叠出几十层 /install/install/…,改根绝对路径一次到位);封面本地化的统计范围漏掉别人的歌单(超管现在可见全部并带归属标注,批量入口补归属校验);SQL 模板中极验配置表缺失主键(新装会导致保存直接失败);恢复一个被误删的通用配置保存器。

七、八个阶段

  • 09-23 → 09-24|两天打地基:四个版本压缩在两天内,从「能跑」到「能天天用」。
  • 09-27|播放器第一次重构:开始有「皮肤」的概念,为后来的分层埋下伏笔。
  • 10-01|结构收口:数据结构统一、补齐工程门面(文档与许可证)。
  • 10-04|引擎与皮肤分离:加一套新皮肤不再需要复制播放逻辑,这是本轮最大的结构收益。
  • 10-06|面向宿主站点:视角从「播放器自己」换到「宿主页面的规矩」——底部导航不能压、域名要说清能不能用、失败要让宿主知道原因。
  • 10-08|把上一版的尾巴收完:判定、粒度、定位这三类问题逐个收口;同日仓库的 LICENSE 也从 MIT 版换成了新的许可全文。
  • 10-10|往数据层走:播放器数据缓存与封面本地化落地——前者省掉重复回源,后者把封面搬到自己能管的存储上;顺带把后台交互的几处硬伤修掉。
  • 10-20|结构与数据的收口:部署门槛降到零(目录式路由)、删数据删干净(级联清理)、配置只放一处(入库)——把前几版攒下的结构债一次还清。

八、说明文档的演进

文档跟着代码改了五次:v1.0.0 恢复并同步 README;v1.4.2 重写 README、补充界面截图并修正过期内容;v1.5.0 把「项目结构」章节改为「引擎 + 皮肤」两层,嵌入章节统一为一行 ?key= 写法,并清掉已删除文件的说明;v1.6.2 之后把「开源许可」章节改写为「许可协议」(徽章与项目结构注释同步),并修正技术栈里一句过期的 CDN 说明;v1.6.5 给 README 补了两节——「生产环境必需的 PHP 设置」(display_errors = 0、log_errors = 1、xdebug.mode = off)与「同页放多个播放器必须显式区分」,并删掉两份已被代码取代的设计文档。

目前仓库 README.md 覆盖:特性、界面预览、安装步骤、生产环境 PHP 设置、嵌入方式(含同页多实例)、皮肤机制、自定义参数、项目结构、API 端点与技术栈。

九、当前状态与已知边界

已经稳定可用的:服务端部署与升级、密钥与歌单管理、两套皮肤、歌词同步、多实例播放、响应式布局、邮件通知;v1.6.0 起,域名授权与宿主底部避让也进入常规能力;v1.6.2 起,底栏探测覆盖更多真实形态,让出量按 PC 与移动端分别校准;v1.6.5 起,可选的 Redis 播放器数据缓存与封面本地化也进入常规能力;v1.6.9 起,零配置后台路由、密钥级联清理、歌单配置导出导入与配置入库也进入常规能力。

已知边界(写出来是为了避免预期落差):

  • 音乐内容依赖第三方接口:歌单、封面、歌词与音频来自第三方音乐平台。本站不存储、不转码、不分发任何音频文件,其可用性由第三方决定——对方调整或限流时,可能无法播放。
  • 缓存与封面本地化都是可选项:不接 Redis 时行为与旧版完全一致(每次回源);封面本地化需要先配好对象存储。缓存服务不可用时播放器照常工作,只是不走缓存。
  • 皮肤数量有限:目前两套。分层架构已经就位,但新皮肤仍需逐个实现。
  • 底部避让不是万能的:宿主页面结构千奇百怪,自动探测按「像不像底栏」判断,遇到特殊布局可以在后台为那个域名直接填固定修正量。
  • 移动端的部分交互仍在打磨:抽屉拖拽、弹层动画这类手势交互,在不同机型与浏览器上的手感还需要持续调整。
  • 不提供音频下载与转存:这是设计选择,也是版权上的边界。
  • 许可:以仓库根目录的 LICENSE 为准。

十、相关链接

从只能自己用到别人也能一行接入,这个项目最花时间的其实不是写功能,而是把结构收拾干净——让下一套皮肤、下一个版本不用再复制一遍逻辑。v1.5.0 把结构做完了,v1.6.0 开始还债:把「嵌在别人站上」这件事一件件收敛好,v1.6.2 把这笔债又还掉一层。v1.6.5 把视线从「宿主页面」移回自己身上:缓存和存储,都是为了让播放器在被嵌进去之后依然跑得轻快、可控。到了 v1.6.9,债基本还清——路由零配置、删除不剩孤儿、配置只放一处,接下来可以把时间花在新东西上。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或者给予支持!

顺雅(Msapi)开发状态
/archives/shunya-progress-overview
作者
Zeph
发布于
2026-09-23
许可协议
CC BY-NC-SA 4.0

评论

欢迎来到我的博客!