為微信小程序開發(fā)的網(wǎng)易云音樂api庫

之前我們已經(jīng)開發(fā)過一款小程序適用的qq音樂api庫https://github.com/FisherWY/QQMusicPlugin了督弓,這次開發(fā)網(wǎng)易云音樂api庫的原因是qq音樂api庫在小程序中iOS環(huán)境下無法使用小程序提供的背景音頻播放器播放的問題

網(wǎng)易云的加密算法真的比其他幾家api復(fù)雜太多了弧烤。毛甲。。完爆QQ和酷狗
想要直接用的話可以到Github直接取我封裝好的api庫摇零。
Github地址https://github.com/JabinGP/NetEaseCloudMusicApi

依賴

本api庫參考了Github上面開源的node庫,因為我們只想要查找音樂和播放音樂這兩個功能,雖然Github那個庫很方便挺狰,但是我們不想為了兩個接口特意去跑一個node.js服務(wù)明郭。Github上的庫

  1. big-integer.js
    這里注意,不要使用最新版的丰泊,最新版的庫再模擬器上運行沒有問題薯定,但是在真機調(diào)試的上傳包階段會報錯說無法識別big-integer.js,最后在我的嘗試下瞳购,選用了一個老版本的庫解決了這個問題话侄。
  2. crypto-js
    這個庫是用來aes加密的,在node上面有一個原生的crypto学赛,但是在小程序里我們沒有年堆,所以我照著Github上的源碼一點一點用這個庫翻譯過來的,還有Buffer在小程序里也沒有盏浇,我使用這個庫的方法代替了变丧。

獲取api的原理

網(wǎng)上很多帖子講的很清楚了,這里推薦幾篇文章绢掰,我只做一個簡單的總結(jié)痒蓬,方便大家理解這個庫。
網(wǎng)易云的加密算法大概使用了兩個:

  1. AES加密+BASE64編碼
  2. RSA加密

加密大致流程:

  1. api請求信息先被轉(zhuǎn)成json字符串格式滴劲,然后再使用一個固定的密鑰aes+base64編碼加密攻晒,得到了第一個加密結(jié)果a
  2. 客戶端從abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/隨機生成一個新的16位密鑰班挖,然后用這個密鑰去加密加密結(jié)果a炎辨,得到加密結(jié)果b
    3.這樣我們的數(shù)據(jù)就被雙重加密了聪姿,但是我們要發(fā)給服務(wù)器去查詢對應(yīng)的數(shù)據(jù)碴萧,服務(wù)器知道第一個固定的密鑰是多少,可以解開第一個加密結(jié)果末购,但是服務(wù)器可不知道我們第二次加密用的是什么破喻,所以服務(wù)器還需要得到我們的第二個生成的隨機加密密鑰。
  3. 第二個隨機加密密鑰要是直接發(fā)給服務(wù)器好像就不太安全了盟榴,所以客戶端對第二個隨機加密密鑰也進(jìn)行了加密曹质,使用的就是RSA加密,加密后得到的數(shù)據(jù)我們稱為c
  4. bc發(fā)送給服務(wù)器擎场,服務(wù)器就會返回給我們對應(yīng)的結(jié)果了羽德。

加密核心代碼

這段代碼傳入對象后可以直接加密成符合網(wǎng)易云api加密的結(jié)果。

// 生成隨機數(shù)迅办,size默認(rèn)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);  //十六位十六進(jìn)制數(shù)作為密鑰
    let iv = CryptoJS.enc.Utf8.parse(aes_mv);   //十六位十六進(jìn)制數(shù)作為密鑰偏移量
    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)
}


// 加密總?cè)肟?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庫https://github.com/JabinGP/NetEaseCloudMusicApi
下載完成后宅静,這個庫應(yīng)該是可以直接導(dǎo)入微信小程序開發(fā)工具運行的,但是有幾個注意事項

  1. 這個庫是用TypeScript寫的站欺,但是最后編譯成了JS運行姨夹,但是編譯后JS代碼可讀性很差纤垂,所以我保留了TypeScript源文件,就在NetEaseCloudMusicApi/ts_src里面磷账,應(yīng)用庫的時候不需要使用到
  2. 關(guān)閉小程序開發(fā)工具的詳情頁的ES6轉(zhuǎn)ES5峭沦,可以使用await處理異步請求(因為庫是用Promise寫的,起碼要能用Promise逃糟,實例代碼使用的是await/async
  3. await關(guān)鍵字只能在async修飾過的函數(shù)體內(nèi)部使用吼鱼,不懂的可以查一下await和async的用法。
  4. NetEaseCloudMusicApi/Libary文件夾里面包含了項目依賴的js文件绰咽,應(yīng)用的時候最好整個NetEaseCloudMusicApi文件夾復(fù)制到項目里面使用菇肃。
  5. 測試的時候可以勾選不校驗合法域名。


    1

開始使用之前的準(zhǔn)備

  1. 找到NetEaseCloudMusicApi這個文件夾剃诅,里面應(yīng)該包括Libary巷送、src驶忌、ts_src三個文件夾矛辕,Libary是我引用的開源庫,ts_src中是TypeScript源文件付魔,src是ts_src編譯后產(chǎn)生的JavaScript文件夾聊品,也就是說不考慮讀ts源文件的話,可以把ts_src刪了几苍,但是17.4 KB 的大小對應(yīng)用包體積應(yīng)該沒有什么影響吧翻屈,留著也行。
  2. 在要使用到的庫中如下引用
const {MusicManager} = require("../../NetEaseCloudMusicApi/src/MusicManager");

注意要用花括號吧MusicManager括起來妻坝,這一句可以需要變化的地方只有
../../NetEaseCloudMusicApi/src/MusicManager中的../../伸眶,后面的路徑都代表了NetEaseCloudMusicApi文件夾和NetEaseCloudMusicApi里面文件的路徑,因為我的庫就是這樣的結(jié)構(gòu)刽宪,所以不需要改變厘贼,../../就要根據(jù)你項目中實際結(jié)構(gòu)來改變了。

MusicManager

該類有以下方法:該類提供了所有獲取其他對象的方法圣拄,可以通過該類獲取其他需要的對象而不是new

  • getMusicSearchHelper()
    需要參數(shù):{keyword:"搜索歌曲關(guān)鍵詞",limit:數(shù)字}
    返回:MusicSearchHelper搜索器
  • getMusicUrlHelper()
    需要參數(shù):musicId(數(shù)字類型的歌曲id)
    返回:MusicUrlHelperUrl獲取器
  • getUserSearchHelper()
    需要參數(shù):{userName:"搜索用戶的用戶名關(guān)鍵詞",limit:數(shù)字}
    返回:UserSearchHelper用戶查詢器
  • getUserListHelper()
    需要參數(shù):userId(數(shù)字類型的用戶id值)
    返回:UserListHelper用戶列表查詢器
  • getUserListDetailHelper()
    需要參數(shù):listId(數(shù)字類型的列表id)
    返回:UserListDetailHelper用戶列表詳情信息獲取器

MusicSearchHelper

用于搜索音樂
可用方法:

  • getSearchResult()---獲取數(shù)據(jù)(默認(rèn)第一頁)
  • nextPage()--- 下一頁
  • previousPage()---上一頁
  • getCurrentPage()---查看當(dāng)前頁數(shù)的
    執(zhí)行完切換頁數(shù)后需要再次調(diào)用getSearchResult方法查看新的查詢結(jié)果嘴秸。

MusicUrlHelper

用于將搜索音樂結(jié)果中的id轉(zhuǎn)換為url播放鏈接
可用方法:

  • getUrlResult() ---獲取url播放鏈接

UserSearchHelper

用于根據(jù)用戶名關(guān)鍵字搜索用戶
可用方法:

  • getSearchResult()---獲取搜索結(jié)果

UserListHelper

用于獲取用戶id后根據(jù)id獲取用戶歌單信息
可用方法:

  • getAllLists()---獲取用戶所有歌單
  • getILikeList()---獲取用戶的我喜歡歌單

UserListDetailHelper

用于獲取歌單id后獲取歌單內(nèi)歌曲列表
可用方法:

  • getDeatil()---獲取歌單內(nèi)列表

搜索歌曲

  1. 通過MusicManager獲取一個MusicSearchHelper搜索器
  2. MusicSearchHelper的方法:
    • getSearchResult()---獲取數(shù)據(jù)(默認(rèn)第一頁)
    • nextPage()--- 下一頁
    • previousPage()---上一頁
    • getCurrentPage()---查看當(dāng)前頁數(shù)的
  3. 執(zhí)行完切換頁數(shù)后需要再次調(diào)用getSearchResult方法查看新的查詢結(jié)果。

代碼實例

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

通過搜索歌曲的結(jié)果獲取音樂Url

有了搜索結(jié)果庇谆,我們還需要url才能播放資源

  1. 通過MusicManager獲取一個MusicUrlHelperUrl獲取器
  2. 通過MusicUrlHelpergetUrlResult方法獲取url
  3. 需要注意的是岳掐,由于網(wǎng)易云接口時常返回空回復(fù),所以這里我通過20以內(nèi)的重復(fù)次請求直到有結(jié)果就停止饭耳,如果20次以后還是沒有結(jié)果(據(jù)我測試20次以內(nèi)都請求到結(jié)果了)串述,也就是返回一個空的字符串"",需要使用者自己重新調(diào)用一次urlHelpergetUrlResult方法(2019.04.27)現(xiàn)在不會返回空值了寞肖,返回空值發(fā)現(xiàn)問題出在使用微信請求時自作聰明將json轉(zhuǎn)成了a=xxxx&b=xxx的格式剖煌,導(dǎo)致微信不能正常轉(zhuǎn)換請求數(shù)據(jù)材鹦,現(xiàn)在每次請求都能獲取結(jié)果。
    代碼實例
const {MusicManager} = require("../../NetEaseCloudMusicApi/src/MusicManager");

async function test(){
    // 搜索歌曲
    let musicSearchHelper = MusicManager.getMusicSearchHelper({ keyword: "one more time one more chance", limit: 10 });
    console.log(`現(xiàn)在是第${musicSearchHelper.getCurrentPage()}頁`);
    console.log(await musicSearchHelper.getSearchResult());
    musicSearchHelper.nextPage();
    console.log(`現(xiàn)在是第${musicSearchHelper.getCurrentPage()}頁`);
    console.log(await musicSearchHelper.getSearchResult());
    musicSearchHelper.previousPage();
    console.log(`現(xiàn)在是第${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();
3

4.26更新

新增搜索用戶以及用戶歌單獲取接口

搜索用戶

  1. 通過MusicManager獲取一個UserSearchHelper用戶查詢器
  2. 通過UserSearchHelpergetSearchResult()方法獲取搜索結(jié)果
async function test(){
  // 搜索用戶
  let userSearchHelper = MusicManager.getUserSearchHelper({ userName: "JabinGP", limit: 20 });
  let users = await userSearchHelper.getSearchResult();
  console.log(users);
}

獲取用戶歌單

  1. 通過MusicManager獲取一個UserListHelper用戶查詢器
  2. 通過UserListHelper
    • getILikeList() ---獲取我喜歡歌單耕姊,返回一個列表對象
    • getAllLists()---獲取所有歌單桶唐,返回一個列表對象的數(shù)組
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中茉兰,運行項目就會自動執(zhí)行輸出結(jié)果尤泽。

async function test(){
    // 搜索歌曲
    let musicSearchHelper = MusicManager.getMusicSearchHelper({ keyword: "one more time one more chance", limit: 10 });
    console.log(`現(xiàn)在是第${musicSearchHelper.getCurrentPage()}頁`);
    console.log(await musicSearchHelper.getSearchResult());
    musicSearchHelper.nextPage();
    console.log(`現(xiàn)在是第${musicSearchHelper.getCurrentPage()}頁`);
    console.log(await musicSearchHelper.getSearchResult());
    musicSearchHelper.previousPage();
    console.log(`現(xiàn)在是第${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();
      console.log(`歌曲的url鏈接是:${url2}`);
      if(timer++>20)break;
    }
  }   
test();

結(jié)尾

2019 4.25目前就只有這兩個接口,因為我們項目就只需要這兩個接口规脸,如果有需要更多接口的坯约,可以在下方評論,以上示例代碼都在Github項目上的index.js中莫鸭,也就是你把文件導(dǎo)入微信開發(fā)者工具后闹丐,取消勾選一下詳情的ES6轉(zhuǎn)ES5以及取消勾選合法域名檢驗,就可以在控制臺看到以上示例代碼的輸出了

2019 4.26更新搜索用戶和獲取用戶歌單以及獲取歌單詳細(xì)三個接口被因。

如果對你有幫助卿拴,點個Star吧~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市梨与,隨后出現(xiàn)的幾起案子堕花,更是在濱河造成了極大的恐慌,老刑警劉巖粥鞋,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缘挽,死亡現(xiàn)場離奇詭異,居然都是意外死亡呻粹,警方通過查閱死者的電腦和手機壕曼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來等浊,“玉大人腮郊,你說我怎么就攤上這事≡涞啵” “怎么了伴榔?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長庄萎。 經(jīng)常有香客問我踪少,道長,這世上最難降的妖魔是什么糠涛? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任援奢,我火速辦了婚禮,結(jié)果婚禮上忍捡,老公的妹妹穿的比我還像新娘集漾。我一直安慰自己切黔,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布具篇。 她就那樣靜靜地躺著纬霞,像睡著了一般。 火紅的嫁衣襯著肌膚如雪驱显。 梳的紋絲不亂的頭發(fā)上诗芜,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天,我揣著相機與錄音埃疫,去河邊找鬼伏恐。 笑死,一個胖子當(dāng)著我的面吹牛栓霜,可吹牛的內(nèi)容都是我干的翠桦。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼胳蛮,長吁一口氣:“原來是場噩夢啊……” “哼销凑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鹰霍,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤闻鉴,失蹤者是張志新(化名)和其女友劉穎茵乱,沒想到半個月后茂洒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡瓶竭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年督勺,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斤贰。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡智哀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出荧恍,到底是詐尸還是另有隱情瓷叫,我是刑警寧澤,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布送巡,位于F島的核電站摹菠,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏骗爆。R本人自食惡果不足惜次氨,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望摘投。 院中可真熱鬧煮寡,春花似錦虹蓄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至坐儿,卻和暖如春体箕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背挑童。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工累铅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人站叼。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓娃兽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親尽楔。 傳聞我的和親對象是個殘疾皇子投储,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,060評論 2 355