相关文章推荐
文武双全的爆米花  ·  十倍机会 ...·  1 年前    · 
开朗的小笼包  ·  insert into ...·  1 年前    · 
打篮球的青椒  ·  python - How to ...·  1 年前    · 
小程序也能用的网易云音乐api库

小程序也能用的网易云音乐api库

之前我们已经开发过一款小程序适用的qq音乐api库 github.com/FisherWY/QQM 了,这次开发网易云音乐api库的原因是qq音乐api库在小程序中iOS环境下无法使用小程序提供的背景音频播放器播放的问题
网易云的加密算法真的比其他几家api复杂太多了。。。完爆QQ和酷狗 想要直接用的话可以到Github直接取我封装好的api库。 Github地址 github.com/JabinGP/NetE

依赖

本api库参考了Github上面开源的node库,因为我们只想要查找音乐和播放音乐这两个功能,虽然Github那个库很方便,但是我们不想为了两个接口特意去跑一个node.js服务。Github上的库 1. big-integer.js
这里注意,不要使用最新版的,最新版的库再模拟器上运行没有问题,但是在真机调试的上传包阶段会报错说无法识别big-integer.js,最后在我的尝试下,选用了一个老版本的库解决了这个问题。 2. crypto-js 这个库是用来aes加密的,在node上面有一个原生的crypto,但是在小程序里我们没有,所以我照着Github上的源码一点一点用这个库翻译过来的,还有Buffer在小程序里也没有,我使用这个库的方法代替了。

获取api的原理

网上很多帖子讲的很清楚了,这里推荐几篇文章,我只做一个简单的总结,方便大家理解这个库。 网易云的加密算法大概使用了两个: 1. AES加密+BASE64编码 2. RSA加密

加密大致流程: 1. api请求信息先被转成json字符串格式,然后再使用一个固定的密钥aes+base64编码加密,得到了第一个 加密结果a 。 2. 客户端从 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/ 随机生成一个新的16位密钥,然后用这个密钥去加密 加密结果a ,得到 加密结果b 。 3.这样我们的数据就被双重加密了,但是我们要发给服务器去查询对应的数据,服务器知道第一个固定的密钥是多少,可以解开第一个加密结果,但是服务器可不知道我们第二次加密用的是什么,所以服务器还需要得到我们的第二个生成的随机加密密钥。 4. 第二个随机加密密钥要是直接发给服务器好像就不太安全了,所以客户端对第二个随机加密密钥也进行了加密,使用的就是RSA加密,加密后得到的数据我们称为 c 5. 将 b c 发送给服务器,服务器就会返回给我们对应的结果了。

加密核心代码

这段代码传入对象后可以直接加密成符合网易云api加密的结果。

// 生成随机数,size默认16
function createSecretKey(size) {
    const keys = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
    let key = ""
    for (let i = 0; i < size; i++) {
        let pos = Math.random() * keys.length
        pos = Math.floor(pos)
        key = key + keys.charAt(pos)
    return key
// aes加密方法
function aesEncrypt(word, secKey) {
    let key = CryptoJS.enc.Utf8.parse(secKey);  //十六位十六进制数作为密钥
    let iv = CryptoJS.enc.Utf8.parse(aes_mv);   //十六位十六进制数作为密钥偏移量
    let srcs = CryptoJS.enc.Utf8.parse(word);
    let encrypted = CryptoJS.AES.encrypt(srcs, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
    let res = encrypted.toString();
    console.log(res);
    return res;
// 填充方法
function zfill(str, size) {
    while (str.length < size) str = "0" + str
    return str
// rsa加密方法
function rsaEncrypt(text, pubKey, modulus) {
    const _text = text.split('').reverse().join('')
    const biText = bigInt(CryptoJS.enc.Utf8.parse(_text).toString(), 16),
        biEx = bigInt(pubKey, 16),
        biMod = bigInt(modulus, 16),
        biRet = biText.modPow(biEx, biMod)
    return zfill(biRet.toString(16), 256)
// 加密总入口
function Encrypt(obj) {
    const text = JSON.stringify(obj)
    const secKey = createSecretKey(16)
    const encText = aesEncrypt(aesEncrypt(text, nonce), secKey)
    const encSecKey = rsaEncrypt(secKey, pubKey, modulus)
    return {
        params: encText,
        encSecKey: encSecKey

封装好的Api库

首先到Github下载我的Api库 github.com/JabinGP/NetE 下载完成后,这个库应该是可以直接导入微信小程序开发工具运行的,但是有几个注意事项 1. 这个库是用TypeScript写的,但是最后编译成了JS运行,但是编译后JS代码可读性很差,所以我保留了TypeScript源文件,就在NetEaseCloudMusicApi/ts_src里面,应用库的时候不需要使用到 2. 关闭小程序开发工具的详情页的 ES6转ES5 ,可以使用await处理异步请求(因为库是用 Promise 写的,起码要能用 Promise ,实例代码使用的是 await / async ) 3. await关键字只能在async修饰过的函数体内部使用,不懂的可以查一下await和async的用法。 4. NetEaseCloudMusicApi/Libary 文件夹里面包含了项目依赖的js文件,应用的时候最好整个 NetEaseCloudMusicApi 文件夹复制到项目里面使用。 5. 测试的时候可以勾选不校验合法域名。


搜索歌曲

搜索歌曲需要的参数有两个 1. 歌曲名关键字 2. 希望返回的数据数量(用于查询结果分页分次查询)

使用说明 1. 首先引入 MusicManager 常量 2. 通过 MusicManager getSearchHelper 方法获取一个查询器 searchHelper ,需要将参数传入 getSearchHelper 方法 3. 通过 searchHelper getSearchResult 方法获取数据(默认第一页) 4. 可以通过 searchHelper nextPage previousPage getCurrentPage 对应进行下一页,上一页,和查看当前页数的操作 5. 执行完切换页数后可以再次调用 getSearchResult 方法查看新页数的查询结果

代码实例

const {MusicManager} = require("../../NetEaseCloudMusicApi/src/MusicManager");
async function test(){
     // 搜索歌曲
    let musicSearchHelper = MusicManager.getMusicSearchHelper({ keyword: "one more time one more chance", limit: 10 });
    console.log(`现在是第${musicSearchHelper.getCurrentPage()}页`);
    console.log(await musicSearchHelper.getSearchResult());
    musicSearchHelper.nextPage();
    console.log(`现在是第${musicSearchHelper.getCurrentPage()}页`);
    console.log(await musicSearchHelper.getSearchResult());
    musicSearchHelper.previousPage();
    console.log(`现在是第${musicSearchHelper.getCurrentPage()}页`);
    console.log(await musicSearchHelper.getSearchResult());
    console.log(musicSearchHelper);
test();



通过搜索歌曲的结果获取音乐Url

有了搜索结果,我们还需要url才能播放资源

1. 首先引入 MusicManager 常量

2. 假设已经从上面获取了一个音乐查询结果

3. 从查询结果里面获取音乐id值

4. 通过 MusicManager getUrlHelper 方法获取一个url获取器,需要将 id 传给 getUrlHelper 方法

5. 通过 urlHelper getUrlResult 方法获取url

需要注意的是,由于网易云接口时常返回空回复,所以这里我通过20以内的重复次请求直到有结果就停止,如果20次以后还是没有结果(据我测试20次以内都请求到结果了),也就是返回一个空的字符串"",需要使用者自己重新调用一次 urlHelper getUrlResult 方法

(2019.04.27更新)空回复问题已经解决,现在每次请求都有回应,原因是自作聪明做了请求数据格式转换,其实只要把对象丢给微信的请求函数就可以了。由于以下代码不会对使用产生不良影响,所以暂时不更改。

代码实例

const {MusicManager} = require("../../NetEaseCloudMusicApi/src/MusicManager");
async function test(){
    // 搜索歌曲
    let musicSearchHelper = MusicManager.getMusicSearchHelper({ keyword: "one more time one more chance", limit: 10 });
    console.log(`现在是第${musicSearchHelper.getCurrentPage()}页`);
    console.log(await musicSearchHelper.getSearchResult());
    musicSearchHelper.nextPage();
    console.log(`现在是第${musicSearchHelper.getCurrentPage()}页`);
    console.log(await musicSearchHelper.getSearchResult());
    musicSearchHelper.previousPage();
    console.log(`现在是第${musicSearchHelper.getCurrentPage()}页`);
    console.log(await musicSearchHelper.getSearchResult());
    console.log(musicSearchHelper);
    // 获取歌曲url
    let songs = await musicSearchHelper.getSearchResult();
    let musicId  = songs[0].id;
    let musicUrlHelper = MusicManager.getMusicUrlHelper(musicId);
    console.log(`歌曲的ID是:${musicId}`);
    let url = await musicUrlHelper.getUrlResult();
    console.log(`歌曲的url链接是:${url}`);
test();



4.26更新

新增搜索用户以及用户歌单获取接口

搜索用户

MusicManager.getUserSearchHelper()方法,需要传入对象如{ userName: "JabinGP", limit: 20 },分别代表用户名和最大查询数目 ,调用方法后获取一个UserSearchHelper实例,通过这个实例的getSearchResult()方法获取搜索结果。

async function test(){
  // 搜索用户
  let userSearchHelper = MusicManager.getUserSearchHelper({ userName: "JabinGP", limit: 20 });
  let users = await userSearchHelper.getSearchResult();
  console.log(users);

获取用户歌单

MusicManager.getUserListHelper()方法,需要传入一个用户id。该方法返回一个UserListHelper实例,通过实例的getILikeList()方法或者getAllLists()方法获取我喜欢列表或者获取所有列表,前者返回一个数组,后者返回一个对象。

async function test(){
  // 搜索用户
  let userSearchHelper = MusicManager.getUserSearchHelper({ userName: "JabinGP", limit: 20 });
  let users = await userSearchHelper.getSearchResult();
  console.log(users);
  // 获取我喜欢歌单
  let userListHelper = MusicManager.getUserListHelper(users[0].userId);
  let iLikeList = await userListHelper.getILikeList()
  console.log(iLikeList);

通过歌单里的Id获取歌曲url

与前面一致,不再赘述

完整实例

完整实例代码在项目page下的index.js中,运行项目就会自动执行输出结果。

async function test(){
    // 搜索歌曲
    let musicSearchHelper = MusicManager.getMusicSearchHelper({ keyword: "one more time one more chance", limit: 10 });
    console.log(`现在是第${musicSearchHelper.getCurrentPage()}页`);
    console.log(await musicSearchHelper.getSearchResult());
    musicSearchHelper.nextPage();
    console.log(`现在是第${musicSearchHelper.getCurrentPage()}页`);
    console.log(await musicSearchHelper.getSearchResult());
    musicSearchHelper.previousPage();
    console.log(`现在是第${musicSearchHelper.getCurrentPage()}页`);
    console.log(await musicSearchHelper.getSearchResult());
    console.log(musicSearchHelper);
    // 获取歌曲url
    let songs = await musicSearchHelper.getSearchResult();
    let musicId  = songs[0].id;
    let musicUrlHelper = MusicManager.getMusicUrlHelper(musicId);
    console.log(`歌曲的ID是:${musicId}`);
    let url = await musicUrlHelper.getUrlResult();
    console.log(`歌曲的url链接是:${url}`);
    // 搜索用户
    let userSearchHelper = MusicManager.getUserSearchHelper({ userName: "JabinGP", limit: 20 });
    let users = await userSearchHelper.getSearchResult();
    console.log(users);
    // 获取用户歌单
    let userListHelper = MusicManager.getUserListHelper(users[0].userId);
    let iLikeList = await userListHelper.getILikeList()
    console.log(iLikeList);
    // 获取我喜欢歌单
    let userListDeatilHelper = MusicManager.getUserListDetailHelper(iLikeList.id);
    let listDetail = await userListDeatilHelper.getDeatil();
    console.log(listDetail);
    let timer=0;
    for(let song of listDetail.tracks){
      musicUrlHelper.musicId=song.id;
      console.log(`歌曲的ID是:${musicUrlHelper.musicId}`);
      let url2 = await musicUrlHelper.getUrlResult();