Node.js 实现网易云歌单自动扩展:从单曲到整张专辑一键生成扩展歌单
Node.js实现网易云歌单自动扩展:从单曲到整张专辑一键生成扩展歌单
💡 灵感来源
这个功能的灵感源于我平时使用网易云音乐时的体验:
我们常常会自己创建歌单,收集喜欢的单曲,但这些单曲背后往往对应整张专辑。
每当想一次性沉浸在多张专辑的音乐中时,总需要手动去查找、切换,非常耗时。
于是我想到:如果能有一个功能,根据已有歌单自动生成包含单曲对应整张专辑的扩展歌单,那就舒服了。
🛠 实现过程
为了实现这个想法,我选择了使用开源的网易云 API:
https://github.com/nooblong/NeteaseCloudMusicApiBackup
- 我编写了脚本,全程借助ChatGPT逐步实现功能。 (脚本在最后面)
- 在配置环境和调试过程中,遇到了一些问题,也不断碰壁,但通过 AI 的帮助逐步解决。
- 最终,脚本成功实现了自动扩展歌单的功能,并支持生成备份文件。
- 甚至下面的功能描述和部分使用过程,也是 AI 帮助我整理和美化的,但基础逻辑和核心实现都是我自己完成的。
代码已上传github https://github.com/GNChuhuda/netease-expandplaylist
功能
🎵 全新“歌单扩展”功能上线!
从精选单曲到完整专辑,一次扩展,沉浸畅听,让音乐体验翻倍!
🔥 功能亮点
- 🎯自动扩展歌单:将每首单曲对应专辑的所有歌曲一键汇聚
- ⏱️省时省力:无需手动整理,一次生成完整歌单
- 🌟轻松发现更多作品:探索整张专辑和更多相关歌曲
⚡ 使用步骤超简单
- 📂读取原歌单:快速获取你的收藏单曲
- 💿拉取专辑歌曲:智能抓取每首单曲对应专辑里的全部歌曲
- 🆕创建新歌单:一键生成专属扩展歌单
- ➕批量添加歌曲:自动添加整张专辑,让歌单瞬间丰满
- 💾生成备份文件:支持 JSON/TXT 导出,方便保存或分享
✨让你的歌单,从精选单曲升级为完整专辑盛宴!🎶
功能展示
下面歌单从30首扩展到了416首
| 原始歌单 | 扩展歌单 |
|---|---|
1️⃣ 环境准备
安装 Node.js
- 建议版本:v22+(其实16+应该是ok的)
- 检查版本:
node-v安装 Git
- 用于克隆仓库:
git--version2️⃣ 克隆 API 仓库
使用nooblong/NeteaseCloudMusicApiBackup:
自己随便找个D盘创建一个新的文件夹,然后在该文件夹打开powershell(shift+右键),再运行下面的命令
git clone https://github.com/nooblong/NeteaseCloudMusicApiBackup.git cd NeteaseCloudMusicApiBackup3️⃣ 安装依赖并添加新脚本
清空旧依赖(可选,一般可以跳过这步):
rd/s/q node_modulesdelpackage-lock.jsonr安装依赖(还是同一个地方powershell运行指令)
npm configsetregistry https://registry.npmmirror.com npm config get registry npm install--verbose(npm config get registry只是检查是不是用国内这个镜像)
⚠️ 碰到的问题及解决方法:
ECONNRESET / CERT_HAS_EXPIRED→ 使用国内镜像--registry=https://registry.npmmirror.comEPERM mkdir→ 检查缓存目录权限或改为 D 盘可写目录
把文件expandPlaylist添加到这个文件夹中(相当于和node.js同一个目录)
4️⃣ 启动 API 服务
node app.js- 默认监听
http://localhost:3000 - 确认终端显示
server running at 3000
5️⃣ 浏览器扫码登录
- 访问:http://localhost:3000
看到一个界面
- 点第四个功能, 二维码登录,用网易云APP扫描二维码并确认登录
(这里我记得这里按理说应该是会弹到网易云那个界面去二维码登录的)如果没有弹出或者出现问题,就参考http://localhost:3000/docs/#/?id=neteasecloudmusicapi 的说明文档的二维码登录的分步骤走,然后访问完/login/qr/create?key=xxx这个接口后网页上会显示qrurl字段,把这个url对应网页打开就能弹到网易云扫码登录界面 - 检查登录状态:访问http://localhost:3000/login/status
- 返回了一堆信息,不是null就行
- 返回 JSON 中包含
profile.nickname可以确认是不是自己的账号
6️⃣ 获取浏览器 cookie
- 进入网易云音乐首页,打开浏览器开发者工具 → Application → Cookies → 找到
MUSIC_U或 Network → 任意请求 → Request Headers → Cookie → 找到MUSIC_U这个字段的值,需要把这段值修改填充到expandPlaylist.js文件中
⚠️ 注意:
- cookie 会过期,需要重新扫码
- 确保复制时没有换行
7️⃣ 改写并运行扩展歌单脚本
①expandPlaylist.js需要改的部分:
//填充cookieconstCOOKIE="";//改成你要扩展的原始歌单的 ID(这个手机版复制分享歌单链接中可以看到,例如13391617669)constPLAYLIST_ID=1;//扩展新建的歌单名字也可以修改constnewPlaylistName="扩展歌单";- 脚本:
expandPlaylist.js
②运行命令:注意由于有一个窗口已经在跑API:app.js;所以需要新开一个powershell窗口运行如下指令
node expandPlaylist.js- 终端显示:
- 原歌单数量
- 涉及专辑数量
- 扩展后总歌曲数
- 新歌单 ID
- 批量添加进度
8️⃣ 输出结果
- JSON 文件:
expanded_playlist.json - 文本文件:
expanded_playlist.txt - 新歌单已在网易云可见,并加入所有扩展歌曲
脚本源码(expandPlaylist.js)
// expandPlaylist.jsconstaxios=require("axios");constfs=require("fs");// —— 请改成你的浏览器扫码登录后的 cookie —— //constCOOKIE="";constAPI_BASE="http://localhost:3000";// 本地 API 服务// 改成你要扩展的歌单 IDconstPLAYLIST_ID=1;asyncfunctionsleep(ms){returnnewPromise(resolve=>setTimeout(resolve,ms));}// 创建 axios 实例,带上 cookieconstclient=axios.create({baseURL:API_BASE,headers:{Cookie:COOKIE}});asyncfunctionmain(){try{console.log("读取原歌单中...");constplaylistRes=awaitclient.get("/playlist/track/all",{params:{id:PLAYLIST_ID,limit:1000}});consttracks=playlistRes.data.songs;console.log(`原歌单歌曲数:${tracks.length}`);constalbumIdSet=newSet();tracks.forEach(track=>{if(track.al&&track.al.id)albumIdSet.add(track.al.id);});console.log(`涉及专辑数:${albumIdSet.size}`);constallSongIdSet=newSet();letindex=1;for(constalbumIdofalbumIdSet){console.log(`[${index}/${albumIdSet.size}] 拉取专辑${albumId}`);constalbumRes=awaitclient.get("/album",{params:{id:albumId}});constsongs=albumRes.data.songs||[];songs.forEach(song=>allSongIdSet.add(song.id));index++;awaitsleep(300);// 防止请求太快}console.log(`扩展后歌曲总数:${allSongIdSet.size}`);// —— 创建新歌单 —— //constnewPlaylistName="扩展歌单";console.log(`创建新歌单:${newPlaylistName}`);constcreateRes=awaitclient.post("/playlist/create",null,{params:{name:newPlaylistName,privacy:0}// privacy: 0 公开, 10 私密});constnewPlaylistId=createRes.data.id;console.log(`新歌单 ID:${newPlaylistId}`);// —— 批量添加歌曲 —— //constsongIds=Array.from(allSongIdSet);console.log(`添加${songIds.length}首歌曲到新歌单...`);constchunkSize=100;// 网易云接口每次添加不超过 100 首for(leti=0;i<songIds.length;i+=chunkSize){constchunk=songIds.slice(i,i+chunkSize);awaitclient.post("/playlist/tracks",null,{params:{op:"add",pid:newPlaylistId,tracks:chunk.join(",")}});console.log(`已添加${i+chunk.length}/${songIds.length}`);awaitsleep(500);// 避免接口过快}// —— 保存备份文件 —— //constresult={sourcePlaylistId:PLAYLIST_ID,totalSongs:allSongIdSet.size,newPlaylistId,songIds};fs.writeFileSync("expanded_playlist.json",JSON.stringify(result,null,2));fs.writeFileSync("expanded_playlist.txt",songIds.join("\n"));console.log("扩展完成!");console.log(" - expanded_playlist.json");console.log(" - expanded_playlist.txt");}catch(err){console.error("脚本出错:",err.response?.data||err.message);}}main();