最近,在優化一個本身寫的音樂播放器。主要目的是回顧、概括,並但願可以寫出一個屬於本身的common lib。今天,主要是關於在線音樂API的一些分析結果。這次,主要分析的是歌詞、專輯部分。在線搜索音樂、熱門音樂及mp3的下載等,會在PART 2.2進行補充。git
原始API來源於網絡資料,部分是後面使用我的補充的。主要包括百度API、騰訊API及歌詞迷API,其中只有歌詞迷的API是官方正式發佈的。三個API都有着各自的優勢、缺點,以下:github
(1) 百度API,請求方式穩定,速度快,資源最多,獲取歌詞比較準確;可是數據結構相對繁雜些,每行的歌詞長度差別比較大。api
(2) 騰訊API,請求方式相對穩定,速度快,資源較多,準確度高,每行的歌詞長度至關;但JSON(Xml相對正常)數據結構並不徹底標準,解析麻煩, 專輯圖片封面(約50KB|500 x 500 像素)較大。網絡
(3)歌詞迷API,有官方正式API,使用簡單,專輯封面相對小些(約10KB|185 x 160 像素);遺憾的是資源相對少,尤爲在最新的資源方面,有點慢。數據結構
提醒:以上全是我的開發的總結,並無完總體系性的驗證。app
如專輯封面大小問題,視乎我的開發須要而定,若是須要大圖片,騰訊的保真度高,若是須要小圖片,無疑歌詞迷更好些。ide
本人在歌詞方面使用的騰訊API,專輯封面使用的是歌詞迷API。 post
整個實現思路比較明確,大致上的類圖設計以下:優化
直接使用LyricLoader的loadLyric()方法進行歌詞下載,loadLyric()方法封裝了具體的處理邏輯,具體實現下載,由子類實現IDownload<Lyric>接口。摘取部分代碼:編碼
/** * 歌詞助手 * * @author Osmondy * */public abstract class LyricLoader implements IDownload<Lyric>{ public LyricLoader(String name) { } /** * 獲取網絡請求歌詞地址 * * @param music * @return */ public abstract String getServerLyricUrl(Music music); /** * 返回本地存儲歌詞的路徑 * * @param music * @return */ protected String getLocalLyricPath(String songname, String singername) { } /** * 返回歌詞, Step1: 本地歌詞目錄加載; Step2: 網絡下載. * * @param music * @return */ public Lyric loadLyric(Music music) { if (TextUtils.isEmpty(music.getArtist()) || TextUtils.isEmpty(music.getTitle())) { Log.W(TAG, "Empty aritst or title, can't find lyric."); return null; } Lyric lyric = null; String localPath = getLocalLyricPath(music.getTitle(), music.getArtist()); File file = new File(localPath); if (file.exists()) { // 本地存在歌詞文件, 直接加載此歌詞. Log.D(TAG, "Loading lyric from local path."); try { lyric = loadLocalLyric(localPath); if (lyric != null) { lyric.setSongname(music.getTitle()); lyric.setSingername(music.getArtist()); Log.I(TAG, "Load local lyric finished. Lyric: " + lyric); } } catch (IOException e) { if (e instanceof FileNotFoundException) { Log.W(TAG, "Lyric not found."); } else { e.printStackTrace(); } } return lyric; } String requestUrl = getServerLyricUrl(music); if (!TextUtils.isEmpty(requestUrl)) { Log.D(TAG, "---------- Download lyric start ----------"); try { lyric = download(requestUrl, localPath); } catch (HttpRequestException e) { e.printStackTrace(); } Log.D(TAG, "---------- Download lyric end ----------"); return lyric; } Log.W(TAG, "Not found a correct server lyric path."); return null; } /** * 保存歌曲文件, 默認保存至{@link AppConfig#DIRECTORY_LYRIC}, 子類可自行重寫保存至其它路徑. 保存時, * 先保存成*.lrc.tmp, 下載及保存成功後, 再重命名爲*.lrc. 防止異常或中止下載歌詞, 下次沒法再次下載. * * @param is * @param music * @return */ protected boolean saveLyric(InputStream is, String savePath) { } /** * 返回指定地址的歌詞文件 * * @param path * @return * @throws IOException */ public Lyric loadLocalLyric(String path) throws IOException { } }
抽象類LyricLoader提供了對歌詞保存、加載的默認處理方式,子類能夠自行重寫saveLyric()、loadLocalLyric()定義本身的處理方式。子類的實現以百度API爲例,它使用的是父類LyricLoader提供的默認實現。
/** * 歌詞來源於Baidu * * @author Osmondy * */public class BaiduLyricHelper extends LyricLoader{ private static final String TAG = "BaiduLyricHelper"; /** * 歌曲信息請求地址 */ protected static final String SONGINFO_BASE_URL = "http://box.zhangmen.baidu.com/x"; /** * 歌詞文件請求地址 */ protected static final String LYRIC_BASE_URL = "http://box.zhangmen.baidu.com/bdlrc"; public BaiduLyricHelper() { super("BaiDu"); } @Override public Lyric download(String requestUrl, String savePath) throws HttpRequestException { } @Override public String getServerLyricUrl(Music music) { } }
比較完整的代碼已經上傳至github:https://github.com/osmondy/LyricApi
原始API以下:
(1) 百度API
歌曲信息請求地址:http://box.zhangmen.baidu.com/x?op=12&count=1&title=歌詞名稱$$歌手名稱$$$$
歌詞信息請求地址:http://box.zhangmen.baidu.com/bdlrc/歌詞ID除以100/歌詞ID.lrc
/** * 返回請求歌詞的地址, 經過 SongInfo生成最終可請求到歌詞文件的地址. </br> * * @param songInfo * @return */ protected String getServerLyricUrlBySongInfo(SongInfo songInfo) { int lrcid = songInfo.getLrcid(); int postfix = lrcid / 100; StringBuffer sb = new StringBuffer(); sb.append(LYRIC_BASE_URL); sb.append("/"); sb.append(postfix); sb.append("/"); sb.append(lrcid); sb.append(".lrc"); return sb.toString(); } @Override public String getServerLyricUrl(Music music) { if (TextUtils.isEmpty(music.getTitle()) || TextUtils.isEmpty(music.getArtist())) { return null; } //protected static final String SONGINFO_BASE_URL = "http://box.zhangmen.baidu.com/x?op=12&count=1&title="; Log.D(TAG, "Songname: " + music.getTitle() + ", Singername: " + music.getArtist()); StringBuffer sb = new StringBuffer(); try { sb.append(SONGINFO_BASE_URL); sb.append("?"); sb.append("op=12"); sb.append("&"); sb.append("count=1"); sb.append("&"); sb.append("title="); sb.append(URLEncoder.encode(music.getTitle(), "utf-8")); sb.append("$$"); sb.append(URLEncoder.encode(music.getArtist(), "utf-8")); sb.append("$$$$"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return sb.toString(); }
構建請求的URL
(2) 騰訊API
編碼並不是是UTF-8,而是GBK(gb2312)。
歌曲信息請求地址:http://qqmusic.qq.com/fcgi-bin/qm_getLyricId.fcg?name=連哭都是個人錯&singer=東來東往&from=qqplayer
歌詞請求地址:http://music.qq.com/miniportal/static/lyric/歌曲ID求餘100/歌曲ID.xml
專輯封面請求地址:http://imgcache.qq.com/music/photo/album/專輯ID求餘100/albumpic_專輯ID_0.jpg
/** * 返回請求歌詞的地址, 經過 SongInfo生成最終可請求到歌詞文件的地址. </br> * 請求地址格式: http://music.qq.com/miniportal/static/lyric/67/183767.xml * * @param songInfo * @return */ protected String getServerLyricUrlBySongInfo(SongInfo songInfo) { String id = songInfo.getId(); if (!StringUtils.isNumeric(id)) { return null; } int postfix = Integer.parseInt(id) % 100; StringBuffer sb = new StringBuffer(); sb.append(LYRIC_BASE_URL); sb.append("/"); sb.append(postfix); sb.append("/"); sb.append(id); sb.append(".xml"); return sb.toString(); } /** * 返回請求歌曲信息的地址. * 請求地址格式: http://qqmusic.qq.com/fcgi-bin/qm_getLyricId.fcg?name=連哭都是個人錯&singer=東來東往&from=qqplayer */ @Override public String getServerLyricUrl(Music music) { if (TextUtils.isEmpty(music.getTitle()) || TextUtils.isEmpty(music.getArtist())) { return null; } Log.D(TAG, "Songname: " + music.getTitle() + ", Singername: " + music.getArtist()); StringBuffer sb = new StringBuffer(); try { sb.append(SONGINFO_BASE_URL); sb.append("?"); sb.append("name=" + URLEncoder.encode(music.getTitle(), "gbk")); sb.append("&"); sb.append("singer=" + URLEncoder.encode(music.getArtist(), "gbk")); sb.append("&"); sb.append("from=qqplayer"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return sb.toString(); }
構建請求的URL
(3)歌詞迷API
直接提供官方地址:http://api.geci.me/en/latest/
歌詞請求地址:http://geci.me/api/lyric/:歌曲名/:歌手名
專輯封面請求地址:http://geci.me/api/cover/:專輯ID
@Override public String getServerLyricUrl(Music music) { if (TextUtils.isEmpty(music.getTitle()) || TextUtils.isEmpty(music.getArtist())) { return null; } Log.D(TAG, "Songname: " + music.getTitle() + ", Singername: " + music.getArtist()); StringBuffer sb = new StringBuffer(); try { sb.append(LYRIC_BASE_PATH); sb.append("/"); sb.append(URLEncoder.encode(music.getTitle(), "utf-8")); sb.append("/"); sb.append(URLEncoder.encode(music.getArtist(), "utf-8")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return sb.toString(); } /** * 返回歌曲專輯信息請求地址 * * @param albumId * @return */ public String getServerAlbumUrl(String albumId) { return ALBUM_BASE_PATH + "/" + albumId; }
構建請求的URL
最後,附上騰訊如何獲取新歌榜及總榜的請求。
新歌榜:http://music.qq.com/musicbox/shop/v3/data/hit/hit_newsong.js 總榜:http://music.qq.com/musicbox/shop/v3/data/hit/hit_all.js