Skip to content
 
 

Repository files navigation

Meting-API · Go

兼容 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

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 .

API 与 MetingJS

入口:GET /?server=netease&type=search&id=hello,根目录带音乐参数时返回 API 数据,不带参数时显示参数说明和请求示例;/api 保留为兼容别名及原默认请求。默认 server=neteasetype=searchid=hello;汽水必须显式提供数字字符串 ID。

参数 说明
server neteasetencentkugoubaidukuwoqishui
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-cachex-error-messageContent-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 限制保留原有语义,并非用户身份认证。

EdgeOne 部署准备

部署排错记录见 验证记录。完整线上播放需项目配置正确,真实 KV 联通应单独验证。

  1. 在 EdgeOne 导入本仓库的 main,项目根目录选仓库根目录,使用 Go 1.26。edgeone.json 已指定构建命令和 public 静态目录。构建需安装开发依赖用于检查边缘代码,生产音乐业务只运行 Go。
  2. 在项目环境变量中设置 METING_EDGEONE=1、随机 METING_TOKEN、独立随机 METING_INTERNAL_TOKEN(至少 32 字符)。METING_URL 可不设置,自动使用当前访问域名;需要固定资源地址时再设为 https://你的域名。签名密钥、内部密钥、公开地址及所有 Cookie 相关变量必须同时对边缘函数和 Go 云函数生效。不要将这些值提交到仓库。
  3. 保持 HTTP_PREFIX 为空,保留默认部署区域。Go 的内部公开路由前缀是 /core,不是 HTTP_PREFIX。必要时显式设置 METING_CORE_URL=https://你的项目域名/core/,末尾保留斜杠。不要将它指向 /api
  4. 在 EdgeOne 创建 KV 命名空间并绑定到项目,变量名为 METING_CACHE。这是平台提供的全局绑定,不是包含访问凭证的普通字符串变量。未绑定时网关仍能直接调用 Go。
  5. 部署后使用 /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 / 404 / 502 排查

  • 默认域名 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=1METING_TOKENMETING_INTERNAL_TOKEN;两个密钥分别生成,内部密钥不少于 32 字符。
  • 网关返回 502:若提示内部鉴权,核对 Go 与边缘函数的密钥是否一致;若提示预览授权缺失或过期,重新打开控制台生成的预览链接。默认内部调用使用当前域名 /core/,仅向同一项目预览域名传递平台的两项预览 Cookie;不向其他域名、音乐平台或 KV 传递。内部业务鉴权仍须保留。
  • 根入口为 edge-functions/index.js,根据音乐参数选择说明页或接口。静态文件优先级更高,不能在 public 中恢复 index.html,否则带参数的根请求也会被静态页覆盖。

KV 行为

仅缓存匿名 songplaylist(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_PORTCORE_PORT 修改端口。

本地适配器运行与部署相同的网关代码并模拟 /core 前缀剥离,但不能代替真实 EdgeOne 路由、运行时限制及最终一致性验证。

验证

cd cloud-functions
go test -race ./...
go vet ./...
go build ./...
cd ..
npm ci
npm test
npm run build

Go 测试覆盖六平台协议、签名、映射、资源鉴权、Cookie 隔离、大整数、汽水分页/试听/歌词/音频过期、并发缓存及取消。边缘测试覆盖 KV 命中、过期、损坏、故障回退、密钥轮换、内部认证、CDN 重定向限制、HEAD/Range/416 与超过 6 MB 的流式响应。

npm run build 将无 Node 内置依赖的边缘代码编译到 .build/edge-functions,用于离线检查;真实部署由 EdgeOne 从 edge-functionscloud-functions 生成平台产物,不上传这个离线目录。完整验证记录见 docs/VALIDATION.md

许可

MIT。移植协议的版权声明见 THIRD_PARTY_NOTICES

Contributors

Languages