说明
为什么我要部署这个项目?
答案很简单:听歌不想花钱。
在没有部署之前,我是用的酷我提供的API。【感谢酷我】
但是近半年来,酷我的API越来越不稳定了。所以,在此基础上,我使用了GD Studio's Online Music Platform API,本文也是根据此api获取的音乐播放链接。
开始之前
你需要准备:
- 一个能访问github的电脑。
- 安装了node.js 24版本或以上
- 配置好了npm镜像以及下载了pnpm模块
后端的部署
首先,后端部署比较简单只需要访问
将项目源码下载到你的电脑,然后按照教程,在项目目录里执行
pnpm i然后等待模块儿全部安装完毕
当你看到如上图一样的就表示你安装成功了,接下来就可以执行
# 默认端口 3000
node app.js执行后就会显示
访问后端,你就会看到
到此,后端部署完毕【当然,到这里还是使用的网易云音乐的api,无法获取付费音乐,如何修改我们放在第三步来说明】
前端的部署
前端我试了很多的网易云项目,如AlgerMusicPlayer、SPlayer、YesPlayMusic等。
YesPlayMusic界面比较素(太素了,啥也没有),所以没有使用;AlgerMusicPlayer没有手机端适配,没有使用;最终选择了SPlayer作为前端。
同样的,将项目下载下来,在项目目录执行
pnpm install安装完依赖后,复制 .env.example 为 .env 并按需修改,如:将 .env 文件中的 VITE_API_URL 改为第一步得到的 API 地址VITE_API_URL = http://localhost:3000;【最后不能/】
执行编译打包之前,需要再添加一句SKIP_NATIVE_BUILD=true
所以最后你的.env文件样式可能是
# WEB 端口
VITE_WEB_PORT=14558
# API 端口
VITE_SERVER_PORT=25884
# API 地址 - 结尾不要加 /
VITE_API_URL=http://localhost:3000
SKIP_NATIVE_BUILD=true
执行编译打包
pnpm build文件输出在out/renderer 目录,本地测试可以按照下面的方法
测试
cd out/renderer
npx http-server -p 4000访问http://127.0.0.1:4000/,就可以发现已经部署成功了!
修改后端Api
因为版权限制,无法听vip音乐,除非你账号本来就有vip【不过既然你有vip了还部署这个干嘛】
目前网站默认关闭试听,需要在设置里打开
而且试听会有提醒,需要去掉。
我们需要做的有这几点:
- 默认打开播放试听
- 去除试听弹窗
修改音乐播放链接使用的api
实施计划
默认打开播放试听
在SPlayer里,src/stores/setting.ts中的第552行
playSongDemo: false, -> playSongDemo: true,这样就实现了默认播放试听【如果你不打开,它不让你播放】
去除试听弹窗
在SPlayer里,src/core/player/SongManager.ts中的第552行【?行数还一样了】
if (isTrial) window.$message.warning("当前歌曲仅可试听"); -> // if (isTrial) window.$message.warning("当前歌曲仅可试听");注释掉就行了。修改完不要忘记再次执行
pnpm build npx http-server -p 4000修改音乐播放链接使用的api
回到api-enhanced,首先安装axios【跨域请求第三方api】
pnpm add axios修改文件module/song_url_v1.js【这次改的有点多】
//module/song_url_v1.js // 歌曲链接 - v1 // 此版本不再采用 br 作为音质区分的标准 // 而是采用 standard, exhigh, lossless, hires, jyeffect(高清环绕声), sky(沉浸环绕声), jymaster(超清母带) 进行音质判断 // 当unblock为true时, 会尝试使用unblockmusic-utils进行解锁, 同时音质设置不会生效, 但仍然为必须传入参数 const logger = require('../util/logger.js') const createOption = require('../util/option.js') const axios = require('axios') module.exports = async (query, request) => { const { matchID, } = require('@neteasecloudmusicapienhanced/unblockmusic-utils') require('dotenv').config() if (query.unblock === 'true') { try { const result = await matchID(query.id, query.source) logger.info('Starting unblock(uses modules unblock):', query.id, result) const useProxy = process.env.ENABLE_PROXY || 'false' let proxyUrl = '' if (result.data.url && result.data.url.includes('kuwo')) { proxyUrl = useProxy === 'true' && process.env.PROXY_URL ? process.env.PROXY_URL + result.data.url : result.data.url } return { status: 200, body: { code: 200, msg: 'Warning: Customizing unblock sources is not supported on this endpoint. Please use `/song/url/match` instead.', data: [ { id: Number(query.id), url: result.data.url, type: 'flac', level: query.level, freeTrialInfo: 'null', fee: 0, proxyUrl: proxyUrl || '', }, ], }, cookie: [], } } catch (e) { console.error('Error in unblocking music:', e) } } const ids = String(query.id).split(',') let br = 999 const level = query.level || 'lossless' if (level === 'standard') br = 128 else if (level === 'higher') br = 192 else if (level === 'exhigh') br = 320 else if (level === 'lossless' || level === 'hires' || level === 'sky' || level === 'jyeffect' || level === 'jymaster') br = 999 const tasks = ids.map(async (id) => { try { const res = await axios.get('https://music-api.gdstudio.xyz/api.php', { params: { types: 'url', source: 'netease', id: id, br: br, }, }) const data = res.data return { id: Number(id), url: data.url, br: data.br * 1000, size: data.size, md5: null, code: 200, expi: 1200, type: data.url ? data.url.split('.').pop() : 'mp3', gain: 0, fee: 0, payed: 0, flag: 0, canExtend: false, freeTrialInfo: null, level: level, encodeType: data.url ? data.url.split('.').pop() : 'mp3', } } catch (e) { return { id: Number(id), url: null, br: 0, size: 0, code: 200, freeTrialInfo: null, } } }) const result = await Promise.all(tasks) result.sort((a, b) => { return ids.indexOf(String(a.id)) - ids.indexOf(String(b.id)) }) return { status: 200, body: { code: 200, data: result, }, } }修改文件module/song_url.js
//module/song_url.js // 歌曲链接 const createOption = require('../util/option.js') const axios = require('axios') module.exports = async (query, request) => { const ids = String(query.id).split(',') let br = 999 const queryBr = parseInt(query.br || 999000) if (queryBr < 192000) br = 128 else if (queryBr < 320000) br = 192 else if (queryBr < 740000) br = 320 else if (queryBr < 999000) br = 740 const tasks = ids.map(async (id) => { try { const res = await axios.get('https://music-api.gdstudio.xyz/api.php', { params: { types: 'url', source: 'netease', id: id, br: br, }, }) const data = res.data return { id: Number(id), url: data.url, br: data.br * 1000, size: data.size, md5: null, code: 200, expi: 1200, type: data.url ? data.url.split('.').pop() : 'mp3', gain: 0, fee: 0, payed: 0, flag: 0, canExtend: false, freeTrialInfo: null, level: 'standard', encodeType: data.url ? data.url.split('.').pop() : 'mp3', } } catch (e) { return { id: Number(id), url: null, br: 0, size: 0, code: 200, freeTrialInfo: null, } } }) const result = await Promise.all(tasks) result.sort((a, b) => { return ids.indexOf(String(a.id)) - ids.indexOf(String(b.id)) }) return { status: 200, body: { code: 200, data: result, }, } }总结
- 引入 axios : 使用 axios 库来请求外部 API ( music-api.gdstudio.xyz )。
- 替换获取逻辑 :
- 移除了原有的调用网易云官方接口 ( /api/song/enhance/player/url ) 的代码。
- 改为并行请求外部 API,支持同时获取多个歌曲 ID 的链接。
- 保留了原有的响应数据结构 ( { code: 200, data: [...] } ),确保对现有客户端的兼容性。
- 参数映射 :
- 旧版 ( song_url.js ) : 将传入的 br (码率,如 320000) 映射为 API 支持的档位 (128, 192, 320, 740, 999)。
新版 ( song_url_v1.js ) : 将传入的 level (音质等级,如 standard, exhigh, lossless) 映射为 API 支持的 br 档位。
- standard -> 128
- higher -> 192
- exhigh -> 320
lossless , hires , sky , jyeffect , jymaster -> 999
注意事项
部署在服务器时,你不能进行以下操作
单独前端启用 HTTPS或单独后端启用 HTTPS,这会报错【现代浏览器会阻止这种混合内容(Mixed Content)请求】,你将无法在前端获取任何内容。写在最后
不用重复造轮子太爽了,本站右上角的音乐使用的就是此API.
enjoy yourself!后续
将SPlayer与api-enhanced合并打包为了镜像,一键部署前后端,将SPlayer打包后的out文件夹放在api-enhanced根目录。
需要修改的地方:
- SPlayer:
- .env
- api-enhanced:
- Dockerfile
- .dockerignore
server.js
#.env # WEB 端口 VITE_WEB_PORT=14558 # API 端口 VITE_SERVER_PORT=25884 # API 地址 - 结尾不要加 / VITE_API_URL= SKIP_NATIVE_BUILD=true#Dockerfile FROM node:lts-alpine RUN apk add --no-cache tini ENV NODE_ENV=production USER node WORKDIR /app # 先只拷贝 package.json 和 yarn.lock(如果有),利用 Docker 缓存机制加速安装 COPY --chown=node:node package.json yarn.lock* ./ # 配置国内淘宝镜像源,极大地加快下载速度 RUN yarn config set registry https://registry.npmmirror.com && yarn install --production --network-timeout=100000 # 然后再拷贝其余代码 COPY --chown=node:node . ./ EXPOSE 3000 CMD [ "/sbin/tini", "--", "node", "app.js" ]#.dockerignore /** !/module !/plugins !/public !/static !/util !/app.js !/server.js !/package.json !/index.js !/generateConfig.js !/main.js !/data !/.env !/pnpm-lock.yaml !/out/renderer//server.js //140行添加 app.use(express.static(path.join(__dirname, 'out/renderer'))) //340行添加 app.use((req, res, next) => { if (!req.path.includes('.')) { res.sendFile(path.join(__dirname, 'out/renderer/index.html')) } else { res.status(404).end() } })最后在api-enhanced根目录执行build_and_export.bat
@echo off chcp 65001 >nul setlocal :: Define Variables set IMAGE_NAME=ncm-api-splayer set TAR_FILE=%IMAGE_NAME%.tar echo ======================================================== echo NCM API Enhanced + SPlayer Docker Build Script echo ======================================================== :: 1. Build Docker Image echo [1/3] Building Docker Image... docker build -t %IMAGE_NAME% . if %errorlevel% neq 0 ( echo [ERROR] Docker build failed! pause exit /b %errorlevel% ) :: 2. Export Image echo [2/3] Exporting image to %TAR_FILE% ... docker save -o %TAR_FILE% %IMAGE_NAME% if %errorlevel% neq 0 ( echo [ERROR] Image export failed! pause exit /b %errorlevel% ) :: 3. Complete echo [3/3] Success! echo. echo Image saved to: %CD%\%TAR_FILE% echo File size: for %%I in ("%TAR_FILE%") do echo %%~zI bytes echo. echo ======================================================== echo Next steps: echo 1. Upload %TAR_FILE% to your server echo 2. Run on server: docker load -i %TAR_FILE% echo 3. Run container: docker run -d -p 3000:3000 --restart=always %IMAGE_NAME% echo ======================================================== pause文件存放在api-enhanced根目录ncm-api-splayer.tar,上传到服务器导入镜像后执行
docker run -d -p 4000:3000 ncm-api-splayer4000可改为任意端口,直接访问ip:4000即可部署完毕。
评论已关闭