在線音樂API的研究 (Part 2.1)

最近,在優化一個本身寫的音樂播放器。主要目的是回顧、概括,並但願可以寫出一個屬於本身的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

       整個實現思路比較明確,大致上的類圖設計以下:優化

Osmondy Lyric

      直接使用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

相關文章
相關標籤/搜索