本人比較喜歡聽音樂,尤其是上下班路上,由於公司離住的地方不遠,走着上班也就40分鍾,喜歡邊走邊聽音樂,聽慣了那種流行音樂,總有點泛濫的感覺,於是到處扒拉那種不一樣的聽歌軟件(什么酷狗,酷我,蝦米,咪咕之類的就不算了),找到一些比較符合自己口味的,像LavaRadio,落網 http://www.luoo.net/。第一次用落網的感覺是,哇,這個小清新居然整理了這么多優質音樂,他每一期提供的音樂風格各有不同,總有那么點讓人期待的感覺,后來,后來就一直用落網了....
不過讓人遺憾的是,落網的音樂不能下載,上班路上安可沒有那么多的流量,雖說用落網的客戶端也可以聽緩存的音樂,不過斷網的情況下效果還是不理想。於是就想辦法折騰落網的音樂,發現一個Chrome插件:聲海盜,這貨可以下載落網的音樂,用過后,感覺效果還是不太好,比如下載的音樂全是01.MP3這樣的,而且必須得播放時才能下載,不是很方便。
實在抵擋不住那么好的音樂的誘惑,決定自己開發個小工具來下載落網的音樂,用着方便就行了,這么想也就這么做了,剛好最近學習Nodejs,就拿Nodejs來開發了。
開工前的准備
分析了落網的頁面源代碼,發現最后的腳本中有個播放列表的數組(竊喜),那么開發思路就有了,這樣就可以很方便拿到他的播放列表了,然后根據播放列表的信息去下載MP3就可以了。他的列表代碼這這樣滴:
<script> try{ var volPlaylist = [{"id":"11999","title":"\u4e16\u754c\u662f\u5757\u5fe7\u4f24\u7684\u77f3\u5934","artist":"\u6cbc\u6cfd","album":"\u8fdc","mp3":"http:\/\/luoo.800edu.net\/low\/luoo\/radio614\/01.mp3","poster":"http:\/\/img.luoo.net\/pics\/albums\/6679\/cover.jpg_580x580.jpg","poster_small":"http:\/\/img.luoo.net\/pics\/albums\/6679\/cover.jpg_60x60.jpg"},........]; var navid = '1'; }catch(e){} ...... </script>
需要解決的問題:
- 根據期刊地址,發送http請求,從返回的html代碼中拿到這段playList代碼;
- 下載mp3到本地,每個期刊創建一個目錄,便於組織這些音樂;
- 讓用戶按照自己的喜好,下載自己喜歡的音樂,也就是得有個界面讓用戶去選;
- 下載完以后干啥呢,聽唄!,還得解決下載后播放音樂的問題。
開工
針對以上問題去研究實現方案,四處奔波后(主要在npmjs上,上面有大量的第三方包),基本解決以上問題,解決方法如下:
- 發送Http請求的問題,這個可以直接用Nodejs自帶的http模塊,不過簡單的處理還行,復雜的處理比如下載MP3就沒有那么方便了,不過幸好有個request模塊解決了這個問題,發送請求特方便,下載文件同樣很Easy。
- 如何從返回的html代碼中獲取那個playlist列表呢,jsdom+jquery去提取?算了,直接分析字符串獲取吧,也不麻煩。
- 創建目錄啥的就不說了,直接用自帶的fs模塊去搞就行了。
- 弄個界面?起初想到了node-webkit,雖然用這貨雖然可以把UI做的很棒,不過有些重量級了,我還是崇尚簡潔輕量的,后來找到term-list模塊,可以直接在控制台中弄個簡單的列表選擇界面,純文本的哦,真方便。
- 下載音樂是需要時間的,怎么才能更友好的下載呢(提示下載進度),發現request模塊並沒有下載進度相關的信息,后來找到request-progress可以解決這個問題,然后再從界面上顯示進度,可以使用progress這個東東。
- 咋播放下載下來的音樂呢,簡單點就直接使用本地的播放器播放得了,那就用open模塊,可以自動調用本地app打開MP3.
萬事俱備,只欠代碼,來吧,開搞吧!
感覺講代碼什么的最頭疼了,說幾個關鍵的吧,其余的根據各模塊的官方文檔都能搞明白。
從html代碼中提取想要的信息,也就是字符串截取:
function findContent(html, key, endTag, offset) { var start = html.indexOf(key); var end = html.indexOf(endTag, start); return html.substring(start + key.length, end + offset); }
然后就可以很方便的獲取播放列表了:playList = JSON.parse(findContent(html, 'var volPlaylist = ', '}];', 2));
簡化版的MP3下載代碼:
progress(request(mp3Info.mp3)) // 請求MP3的下載地址 .on('progress', function (state) { // 這里處理下載進度 }) .pipe(fs.createWriteStream(mp3File)) // MP3保存到本地文件 .on('close', function (err) { // 這里處理下載結束 });
結合request和request-progress模塊去下載東西還是蠻方便的,若沒有這些模塊,我不知道要寫多少代碼,向此模塊的作者致敬!
其他的全是些UI方面的東西了,也沒啥值得說的,下面大家可以去GitHub找到完整的代碼。
貼圖欣賞
程序跑起來的效果圖如下,輸入要獲取的期刊url,就可以分析出他的下載列表,然后選擇自己喜歡的音樂回車開始下載,下載完成后,再次回車就可以播放了:
luoo-down,Windowns版的截圖:
MP3下載目錄,每期的歌曲單獨存放到自己的目錄中:
問題
大家知道我們獲取播放列表是從落網的網頁源代碼中獲取的,要是哪一天落網把頁面改了,找不到播放列表了,那就妥妥的悲劇了。希望落網的程序猿們不要動那段播放列表的代碼哦。
其實這個問題可以分析他們客戶端調用的接口,接口變動總該是少的吧,他們有個Android的客戶端,可以反編譯分析下接口地址,不過會麻煩些,沒有折騰,現在這個版本能用就先用着吧。
開源
開發完成后,邊聽歌,邊調試,最后搞穩定了,差不多可以分享給大家用了,就扔到github上去了,開源分享精神必須是有滴!
GitHub項目地址:https://github.com/stanzhai/luoo-down
大家有興趣的可以加入進來一起完善哦。
總結
總結下用到的一些模塊:
var fs = require('fs') , path = require('path') , readline = require('readline') , request = require('request') // 非常方便的發送http請求 , progress = require('request-progress') // 處理請求進度 , ProgressBar = require('progress') // 控制台進度顯示 , open = require('open') // 使用本地應用打開文件 , colors = require('colors') // 控制台文本顏色 , List = require('term-list'); // 控制台列表菜單項
相關技術點:Nodejs網頁信息采集,文件下載及下載進度,控制台(終端)UI。
感覺不錯?那就點個推薦吧。