最近,在优化一个自己写的音乐播放器。主要目的是回顾、归纳,并希望能够写出一个属于自己的common lib。今天,主要是关于在线音乐API的一些分析结果。此次,主要分析的是歌词、专辑部分。在线搜索音乐、热门音乐及mp3的下载等,会在PART 2.2进行补充。

原始API来源于网络资料,部分是后面使用个人补充的。主要包括百度API、腾讯API及歌词迷API,其中只有歌词迷的API是官方正式发布的。三个API都有着各自的优点、缺点,如下:

(1) 百度API,请求方式稳定,速度快,资源最多,获取歌词比较准确;但是数据结构相对繁杂些,每行的歌词长度差异比较大。

(2) 腾讯API,请求方式相对稳定,速度快,资源较多,准确度高,每行的歌词长度相当;但JSON(Xml相对正常)数据结构并不完全标准,解析麻烦, 专辑图片封面(约50KB|500 x 500 像素)较大。

(3)歌词迷API,有官方正式API,使用简单,专辑封面相对小些(约10KB|185 x 160 像素);遗憾的是资源相对少,尤其在最新的资源方面,有点慢。

提醒:以上全是个人开发的总结,并没有完整体系性的验证。

如专辑封面大小问题,视乎个人开发需要而定,如果需要大图片,腾讯的保真度高,如果需要小图片,无疑歌词迷更好些。

本人在歌词方面使用的腾讯API,专辑封面使用的是歌词迷API。


整个实现思路比较明确,大体上的类图设计如下:

直接使用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 / ; 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) % ; 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

[原创] 在线音乐API的研究 (Part 2.1)的更多相关文章

  1. 在线音乐API的研究 (Part 2.1)

    本文转载于:http://www.cnblogs.com/osmondy/p/LyricApi.html 最近,在优化一个自己写的音乐播放器.主要目的是回顾.归纳,并希望能够写出一个属于自己的comm ...

  2. 在线音乐播放器-----酷狗音乐api接口抓取

    首先身为一个在线音乐播放器,需要前端和数据库的搭配使用. 在数据库方面,我们没有办法制作,首先是版权问题,再加上数据量.所以我们需要借用其他网络播放器的数据库. 但是这些在线播放器,如百度,酷狗,酷我 ...

  3. 百度音乐API抓取

    百度音乐API抓取 前段时间做了一个本地音乐的播放器 github地址,想实现在线播放的功能,于是到处寻找API,很遗憾,不是歌曲不全就是质量不高.在网上发现这么一个APIMRASONG博客,有“获取 ...

  4. 【QQ音乐Api】移花接木 打造自己的音乐电台

    最近突发奇想想做个在线音乐小网页.需求很简单,如下 搜索歌曲 或 歌手 在线播放音乐 借用qq 或者 百度的 音乐接口 需求明确那就直接动手了 我首先尝试的百度音乐,但是不能在线播放(提示forbid ...

  5. iOS在线音乐播放SZKAVPlayer(基于AVPlayer的封装)

    由于最近闲着没事,想找有关在线音乐播放的demo学习一下,在gitHub跟code4APP上面查找了很多帖子,结果很多在线音乐都是基于AudioStream实现的,我感觉用起来不太方便.后来突然发现, ...

  6. 如何使用 python 爬取酷我在线音乐

    前言 写这篇博客的初衷是加深自己对网络请求发送和响应的理解,仅供学习使用,请勿用于非法用途!文明爬虫,从我做起.下面进入正题. 获取歌曲信息列表 在酷我的搜索框中输入关键词 aiko,回车之后可以看到 ...

  7. Andriod小项目——在线音乐播放器

    转载自: http://blog.csdn.net/sunkes/article/details/51189189 Andriod小项目——在线音乐播放器 Android在线音乐播放器 从大一开始就已 ...

  8. 在线音乐网站【04】Part two 功能实现

       上一篇博客里面已近总结了三个功能的具体实现,今天把剩余功能的具体实现补充总结,如果你想对整个小项目有清楚的了解,建议去看下前几篇博客. 1.在线音乐网站(1)需求和功能结构 2.在线音乐网站(2 ...

  9. 在线音乐网站【03】Part one 功能实现

    今天打算把网站功能的具体实现给总结一下,如果你想了解整个小项目,建议你先看看前面2篇博客. 1.在线音乐网站(1)需求和功能结构 2.在线音乐网站(2)数据库和开发环境 7.网站主要模块实现 a.在线 ...

随机推荐

  1. SVM 支持向量机算法介绍

    转自:https://zhuanlan.zhihu.com/p/21932911?refer=baina 参考:http://www.cnblogs.com/LeftNotEasy/archive/2 ...

  2. ACM-ICPC 2018 沈阳赛区网络预赛 K. Supreme Number

    A prime number (or a prime) is a natural number greater than 11 that cannot be formed by multiplying ...

  3. [转]Python 之 使用 PIL 库做图像处理

    Python 之 使用 PIL 库做图像处理 1. 简介. 图像处理是一门应用非常广的技术,而拥有非常丰富第三方扩展库的 Python 当然不会错过这一门盛宴.PIL (Python Imaging ...

  4. 使用pipework将Docker容器桥接到本地网络环境中

    在使用Docker的过程中,有时候我们会有将Docker容器配置到和主机同一网段的需求.要实现这个需求,我们只要将Docker容器和主机的网卡桥接起来,再给Docker容器配上IP就可以了.pipew ...

  5. 九度oj 题目1125:大整数的因子

    题目描述: 已知正整数k满足2<=k<=9,现给出长度最大为30位的十进制非负整数c,求所有能整除c的k. 输入: 若干个非负整数c,c的位数<=30每行一个c,当c=-1时中止 ( ...

  6. BZOJ 1016 [JSOI2008]最小生成树计数 ——Matrix-Tree定理

    考虑从小往大加边,然后把所有联通块的生成树个数计算出来. 然后把他们缩成一个点,继续添加下一组. 最后乘法原理即可. 写起来很恶心 #include <queue> #include &l ...

  7. ubuntu问题解答集锦

    一.su root提示认证失败 su root提示认证失败 ubuntu root是默认禁用了,不答应用root登陆,所以先要设置root密码.   执行:sudo passwd root 接着输入密 ...

  8. 聊天程序(基于Socket、Thread)

    聊天程序简述 1.目的:主要是为了阐述Socket,以及应用多线程,本文侧重Socket相关网路编程的阐述.如果您对多线程不了解,大家可以看下我的上一篇博文浅解多线程 . 2.功能:此聊天程序功能实现 ...

  9. 洛谷P2365 任务安排 [解法一]

    题目描述 N个任务排成一个序列在一台机器上等待完成(顺序不得改变),这N个任务被分成若干批,每批包含相邻的若干任务.从时刻0开始,这些任务被分批加工,第i个任务单独完成所需的时间是Ti.在每批任务开始 ...

  10. 【HDOJ5950】Recursive sequence(矩阵乘法,快速幂)

    题意:f[1]=a,f[2]=b,f[i]=2f[i-2]+f[i-1]+i^4(i>=3),多组询问求f[n]对2147493647取模 N,a,b < 2^31 思路:重点在于i^4的 ...