兼容 MetingJS 的多平台音乐 API。音乐业务已迁移到 Go 1.26 + net/http + Chi,支持网易云、QQ、酷狗、百度、酷我与汽水音乐。独立服务和 Docker 镜像无需 Bun、Node.js、@meting/core 或另一个音乐 API。
本 fork 使用 main。原平台协议移植自 metowolf/Meting,保留 MIT 声明。平台接口可用性、登录权限与地区限制由上游决定。
独立运行:MetingJS → Go /(兼容 /api)→ 音乐平台 / 汽水 CDN
↳ 内存缓存
EdgeOne:MetingJS → 边缘函数 /(兼容 /api)→ METING_CACHE(匿名元数据 / 歌词)
↓ 内部密钥认证
Go 云函数 /core/api → 六个平台
↓ /core/resolve 返回临时音频描述
边缘函数直接流式转发汽水 CDN → 浏览器
EdgeOne 的 Go 云函数采用 Framework 模式,以 cloud-functions/core.go 注册 /core 前缀,平台剥离前缀后交给 Chi。公共 / 与 /api 属于边缘函数,两者不会相互循环。/core/api 和 /core/resolve 需要独立内部密钥;浏览器通过根目录或 /api 访问音乐接口。其他平台的音频、所有封面继续通过 302 跳转。
汽水音频不经过云函数响应体,不写入 KV,由边缘函数直接传递 CDN 的流,避免云函数 6 MB 响应限制。独立模式则由 Go 直接代理。两种模式均支持 HEAD、单段 Range、206、416,对失效的 CDN 地址重新解析并重试一次。
相关官方说明:Go Framework、云函数限制、KV 存储。
安装 Go 1.26,在仓库根目录执行:
cd cloud-functions
HTTP_PORT=9000 go run .打开 http://localhost:9000/demo?server=qishui&type=playlist&id=7096700219496368135。该演示显式使用当前 API;APlayer 与 MetingJS 默认从 CDN 加载。
构建可独立运行的二进制:
cd cloud-functions
go build -trimpath -o meting-api .
HTTP_PORT=9000 METING_URL=https://music.example.com METING_TOKEN=replace-with-private-secret ./meting-api使用本地 fork 前端:将 everfu/MetingJS 放在本仓库旁,先在前端仓库执行 npm ci && npm run build。然后在本仓库根目录执行:
cd cloud-functions
HTTP_PORT=3318 METING_JS_PATH=../../MetingJS/dist/Meting.min.js go run .METING_JS_PATH 按进程工作目录解析,演示通过 /meting.js 使用该文件。前端播放器无需改写,其他页面默认 API 不受影响。
docker build -t meting-api:go .
docker run --rm -p 9000:9000 \
-e METING_URL=https://music.example.com \
-e METING_TOKEN=replace-with-private-secret \
meting-api:go容器以非 root 用户运行。Cookie 文件可只读挂载到 /app/cookie;前端构建可挂载后通过 METING_JS_PATH 指定。Docker Hub 无法访问时,可使用同一官方基础镜像的公共镜像源:
docker build \
--build-arg GO_IMAGE=public.ecr.aws/docker/library/golang:1.26-alpine \
--build-arg RUNTIME_IMAGE=public.ecr.aws/docker/library/alpine:3.23 \
-t meting-api:go .入口:GET /?server=netease&type=search&id=hello,根目录带音乐参数时返回 API 数据,不带参数时显示参数说明和请求示例;/api 保留为兼容别名及原默认请求。默认 server=netease、type=search、id=hello;汽水必须显式提供数字字符串 ID。
| 参数 | 说明 |
|---|---|
server |
netease、tencent、kugou、baidu、kuwo、qishui |
type |
原五平台:search/song/album/artist/playlist/url/pic/lrc |
id |
搜索词、歌曲或列表 ID;大整数始终作为字符串处理 |
auth / token |
url/pic/lrc 的资源签名,token 优先;列表接口返回的链接已带签名 |
签名保持 HMAC-SHA1(METING_TOKEN, server + type + id),输出十六进制。歌曲列表字段仍为 title/author/url/pic/lrc;汽水额外返回 playback.preview/duration/start,单位为秒,未知值为 null。无可用资源返回 404,参数错误 400,签名错误 401;原平台调用错误保留 500,汽水上游错误为 502。错误说明位于 URL 编码的 x-error-message 响应头,响应体保留 服务器未知异常 约定。响应支持跨域与 Range 请求头。
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/aplayer@1.10.1/dist/APlayer.min.css">
<script src="https://cdn.jsdelivr.net/npm/aplayer@1.10.1/dist/APlayer.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/gh/everfu/MetingJS@main/dist/Meting.min.js"></script>
<meting-js
server="qishui"
type="playlist"
id="7096700219496368135"
api="https://music.example.com/?server=:server&type=:type&id=:id">
</meting-js>汽水提供匿名歌单、单曲、封面、歌词与音频代理。单曲示例:/api?server=qishui&type=song&id=7079108541549643812。首版支持 song/playlist/url/pic/lrc,搜索、专辑、歌手请求明确返回 400,不自动解析分享链接。
歌单按游标分页并保留顺序、去重、跳过非歌曲资源。仅在实际播放时解析所选歌曲的音频地址,不提前解析整张歌单。只使用匿名获得的未加密 AAC/M4A 音频,不接入 Cookie、扫码登录或解密流程。
标题中的 “试听” 表示上游仅提供片段,并不代表完整歌曲。歌词从汽水逐字格式转换为 LRC,减去可靠的试听起点并裁剪到片段范围;无法确定试听起点时不显示错误同步歌词。无地址、需要登录或只有加密资源时返回不可播放错误。封面及音频 CDN 域名受限制,代理只接受歌曲 ID,不接受任意 URL。
协议参考 guowenye/qishui-api,汽水实现独立编写,没有引入其运行时或复制解密代码。
EdgeOne 的 edgeone.json 对所有路径配置 Access-Control-Allow-Origin: *,Go 与边缘函数返回相同的跨域响应头。预检 OPTIONS 返回 204,允许标准 HTTP 方法和任意请求头,并显式允许 Authorization;预检有效期为 86400 秒。实际接口仍按各自支持的方法处理请求。
播放器可以从任意网站匿名调用 API,也可以读取响应头中的 x-cache、x-error-message、Content-Range 等信息。浏览器请求使用默认凭据策略或 credentials: "omit";通配来源不支持 credentials: "include"。资源签名和内部接口鉴权仍然生效。
| 变量 | 说明 / 默认值 |
|---|---|
HTTP_PORT |
本地端口,默认 9000;云函数提供的 PORT 优先 |
HTTP_PREFIX |
本地路由前缀,默认空;EdgeOne 保持空 |
METING_URL |
对外公开基地址;未设置时按当前访问域名生成(包括 EdgeOne),后续绑定域名无需修改;有前缀时包含前缀 |
METING_TOKEN |
资源签名密钥,本地默认 token;EdgeOne 必须更换 |
METING_EDGEONE |
EdgeOne 必须设置为 1,启用内部入口鉴权、禁止云函数直接输出音频 |
METING_INTERNAL_TOKEN |
边缘调用 Go 的独立密钥,至少 32 字符,不得与签名密钥相同 |
METING_CORE_URL |
边缘网关的内部调用地址,默认 METING_URL 的 origin 加 /core/ |
METING_COOKIE_NETEASE/TENCENT/KUGOU/BAIDU/KUWO |
对应平台 Cookie,优先于本地文件;汽水忽略 Cookie |
METING_COOKIE_ALLOW_HOSTS |
允许使用服务端 Cookie 的 Referer 主机名,逗号分隔、精确匹配;空值不限制 |
METING_COOKIE_DIR |
本地 Cookie 目录,默认当前工作目录下 cookie,文件名为平台名 |
METING_JS_PATH |
本地 MetingJS 构建文件 |
HTTPS_ENABLED |
本地 TLS:true/1/yes/on;默认关闭 |
HTTPS_PORT |
本地 TLS 端口,默认 443 |
SSL_KEY_PATH / SSL_CERT_PATH |
本地 TLS 文件路径 |
使用 go run 且工作目录为 cloud-functions 时,原根目录 Cookie 可用 METING_COOKIE_DIR=../cookie。Cookie 文件每次读取会反映修改,缓存按实际 Cookie 的摘要隔离。浏览器自己的 Cookie 不透传给音乐平台。Referer 限制保留原有语义,并非用户身份认证。
部署排错记录见 验证记录。完整线上播放需项目配置正确,真实 KV 联通应单独验证。
- 在 EdgeOne 导入本仓库的
main,项目根目录选仓库根目录,使用 Go 1.26。edgeone.json已指定构建命令和public静态目录。构建需安装开发依赖用于检查边缘代码,生产音乐业务只运行 Go。 - 在项目环境变量中设置
METING_EDGEONE=1、随机METING_TOKEN、独立随机METING_INTERNAL_TOKEN(至少 32 字符)。METING_URL可不设置,自动使用当前访问域名;需要固定资源地址时再设为https://你的域名。签名密钥、内部密钥、公开地址及所有 Cookie 相关变量必须同时对边缘函数和 Go 云函数生效。不要将这些值提交到仓库。 - 保持
HTTP_PREFIX为空,保留默认部署区域。Go 的内部公开路由前缀是/core,不是HTTP_PREFIX。必要时显式设置METING_CORE_URL=https://你的项目域名/core/,末尾保留斜杠。不要将它指向/api。 - 在 EdgeOne 创建 KV 命名空间并绑定到项目,变量名为
METING_CACHE。这是平台提供的全局绑定,不是包含访问凭证的普通字符串变量。未绑定时网关仍能直接调用 Go。 - 部署后使用
/core/health检查 Go,用/?server=qishui&type=song&id=7079108541549643812检查公开接口。浏览器直接请求/core/api或/core/resolve应得到 401。演示入口为/core/demo,介绍页为/about.html;根目录无音乐参数时显示接口说明,带参数时提供 API。
EdgeOne 官方 CLI 检查配置:npx edgeone validate。官方本地调试命令 npx edgeone makers dev --skip-env-sync 仍需登录;无账号时使用下述本地联调。
- 默认域名 401,
x-eop-msg: eo_time missing:这是 EdgeOne 预览访问限制,发生在 API 之前。控制台“预览”链接携带验证参数且仅有效 3 小时;正式播放器必须使用绑定的自定义域名。打开预览链接后,同域 API、演示和音频请求可使用平台设置的预览 Cookie 临时测试。不要把预览令牌写入代码、KV 或METING_URL。 /core/health或/core/demo返回 404:检查生产环境是否设置了METING_EDGEONE=1,并重新部署。该变量缺失时旧入口运行本地 HTTP 模式,云端/core前缀未被处理。仅修改变量而不重新部署不会生效。- 根目录或
/api返回 503:查看x-error-message,会直接指出缺少的变量。至少设置METING_EDGEONE=1、METING_TOKEN和METING_INTERNAL_TOKEN;两个密钥分别生成,内部密钥不少于 32 字符。 - 网关返回 502:若提示内部鉴权,核对 Go 与边缘函数的密钥是否一致;若提示预览授权缺失或过期,重新打开控制台生成的预览链接。默认内部调用使用当前域名
/core/,仅向同一项目预览域名传递平台的两项预览 Cookie;不向其他域名、音乐平台或 KV 传递。内部业务鉴权仍须保留。 - 根入口为
edge-functions/index.js,根据音乐参数选择说明页或接口。静态文件优先级更高,不能在public中恢复index.html,否则带参数的根请求也会被静态页覆盖。
仅缓存匿名 song、playlist(60 秒)和 lrc(5 分钟)。其他接口不写 KV。携带浏览器 Cookie 或配置了对应平台 Cookie 的请求绕过共享 KV,且只有 Go 明确标为匿名的结果才允许写入。
缓存键为 m2_音源_操作_SHA256摘要,仅含字母、数字、下划线。摘要包含 ID、签名密钥与公开地址;修改密钥或地址后旧链接缓存自动失效。KV 不保存密钥、Cookie、音频字节或临时 CDN 音频地址,只保存返回给播放器的签名资源链接、文本和显式 expiresAt。每次读取检查过期时间;缺少 KV、读取/写入失败、数据损坏均回退到 Go。
KV 为最终一致性缓存,不参与鉴权或精确计数。过期记录不会依赖平台 TTL 自动删除,需定期在受信任的终端运行:
# 使用环境变量提供 METING_URL 和 METING_INTERNAL_TOKEN
npm run cleanup:kv该工具调用受内部密钥保护的 POST /internal/cleanup,按最多 256 个键分页清理过期记录,同时清理旧版 m1_ 缓存,不触碰其他前缀的数据。
npm ci
METING_JS_PATH=../../MetingJS/dist/Meting.min.js npm run dev:edge脚本启动 Go(3320)与本地边缘网关(3319),自动生成仅本次进程使用的密钥,使用内存模拟 METING_CACHE。打开 http://localhost:3319/demo?server=qishui&type=playlist&id=7096700219496368135,重复请求可见 x-cache: MISS/HIT。可用 EDGE_PORT、CORE_PORT 修改端口。
本地适配器运行与部署相同的网关代码并模拟 /core 前缀剥离,但不能代替真实 EdgeOne 路由、运行时限制及最终一致性验证。
cd cloud-functions
go test -race ./...
go vet ./...
go build ./...
cd ..
npm ci
npm test
npm run buildGo 测试覆盖六平台协议、签名、映射、资源鉴权、Cookie 隔离、大整数、汽水分页/试听/歌词/音频过期、并发缓存及取消。边缘测试覆盖 KV 命中、过期、损坏、故障回退、密钥轮换、内部认证、CDN 重定向限制、HEAD/Range/416 与超过 6 MB 的流式响应。
npm run build 将无 Node 内置依赖的边缘代码编译到 .build/edge-functions,用于离线检查;真实部署由 EdgeOne 从 edge-functions 和 cloud-functions 生成平台产物,不上传这个离线目录。完整验证记录见 docs/VALIDATION.md。
MIT。移植协议的版权声明见 THIRD_PARTY_NOTICES。