如何从ios酷我音乐盒中导出已下载的音乐文件
本文所涉及内容用于技术学习,请勿用于不正当用途,否则后果自负。
酷我音乐ios版下载的音乐文件,通过同步助手等软件查看时,发现音乐文件都是一串数字命名。通过网上查找和自己尝试,发现那些文件都是音频文件改了文件名而已。只要修改回文件名,就能和正常的音乐一样播放了。
在网上找到了一个软件,也就是上面参考网址中的软件,但是使用的时候出现了问题,提示subscript out of range,程序不能继续执行。于是就决定自己用Java写一个来处理。
首先从手机复制出cloud.db数据库文件,为SQLite数据库文件。用SQLite Database Browser打开,看到里面和音乐关系比较密切的主要有三个table,playlistsInfo中保存着播放列表信息,字段title为列表名称,字段id为列表id。playlistMusics保存了音乐和播放列表的对应关系,字段title、artist等为音乐信息,rid为音乐资源id,字段playlist_id对应了所属播放列表id。musicResource中是音乐信息和文件对应关系,字段file为对应的文件名,format为文件格式,rid为音乐资源id。
于是程序的工作流程是这样的:
1、首先从musicResource逐一读取每首音乐的rid;
2、通过rid在playlistMusics中查找playlist_id,可能找不到,也可能不止一个,因为同一首音乐可能在多个列表中,这里简单的取最大的playlist_id,通常应该是相对比较新的播放列表;
3、然后在playlistsInfo中找到playlist_id对应的播放列表名,作为目标音乐的子文件夹;
4、最后把源文件重命名为“歌手名 - 歌曲名.扩展名”,并移动到目标文件夹即可。
为方便交流学习,这里提供本程序源码。
import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
public class Test {
/**
* 存储播放列表的数据结构
*
* @author jzj
*/
static class PlayList {
int id;
String name;
public PlayList(String name, int id) {
this.name = name;
this.id = id;
}
}
// 数据库完整路径
static final String db_path = "G:\\IOS\\cloud.db";
// 源文件夹
static final String src_dir = "G:\\IOS\\Music\\";
// 目标文件夹
static final String dst_dir = "G:\\IOS\\Music1\\";
public static void main(String[] args) throws Exception {
Class.forName("org.sqlite.JDBC");
Connection conn = DriverManager.getConnection("jdbc:sqlite:" + db_path);
Statement stat1 = conn.createStatement();
Statement stat2 = conn.createStatement();
// 读取播放列表
List<PlayList> lists = new ArrayList<Test.PlayList>();
ResultSet rs_list = stat1.executeQuery("select * from playlistsInfo;");
while (rs_list.next()) {
final int id = rs_list.getInt("id");
final String name = rs_list.getString("title");
switch (name) {
// 忽略这几个列表
case "本地歌曲":
case "默认列表":
case "最近播放":
case "我的电台":
break;
case "我喜欢听":
default:
lists.add(new PlayList(name, id));
}
}
// 读取音乐信息
ResultSet rs_res = stat1.executeQuery("select * from musicResource;");
while (rs_res.next()) {
// 源文件路径
String fname = rs_res.getString("file");
if (fname == null || fname.length() == 0) // 如果file字段为空则跳过
continue;
String src_path = src_dir + fname;
File src = new File(src_path);
if (!src.exists()) // 如果源文件不存在则跳过
continue;
// 获取音乐rid
int rid = rs_res.getInt("rid");
// 查找该音乐所在播放列表id, 如果没有找到则为-1
ResultSet rs_pl = stat2.executeQuery(new StringBuilder(
"select playlist_id from playlistMusics where rid=")
.append(rid).append(\';\').toString());
int playlist_id = -1;
while (rs_pl.next()) { // 默认将一首歌放在编号最大的播放列表中(也就是最新创建的列表)
int p_id = rs_pl.getInt("playlist_id");
if (p_id > playlist_id)
playlist_id = p_id;
}
rs_pl.close();
// 目标文件夹路径
StringBuilder b2 = new StringBuilder(dst_dir);
if (playlist_id >= 0) {
String playlist_name = getPlaylist(lists, playlist_id);
if (playlist_name != null) {
b2.append(playlist_name).append(\'\\\');
}
}
String dir = b2.toString();
new File(dir).mkdirs();
// 目标文件名: "艺术家 - 歌曲名.扩展名"
StringBuilder b3 = new StringBuilder();
b3.append(rs_res.getString("artist")).append(" - ")
.append(rs_res.getString("title")).append(\'.\')
.append(rs_res.getString("format"));
String dst_path = dir + b3.toString();
// 移动和重命名
File dst = new File(dst_path);
src.renameTo(dst);
// 输出信息
System.out.println(new StringBuilder(src_path).append(" ---> ")
.append(dst_path));
}
rs_res.close();
conn.close();
}
static String getPlaylist(List<PlayList> lists, int playlist_id) {
for (PlayList pl : lists) {
if (pl.id == playlist_id)
return pl.name;
}
return null;
}
}
由于涉及数据库操作,需要在Java工程中添加数据库支持包,可参看这篇文章 http://ttitfly.iteye.com/blog/143934
使用nested包:sqlitejdbc-v037-nested.jar
将音频文件全部复制出来,放到程序中src_dir所指定的目录,数据库文件cloud.db保存在db_path指定的位置,设置好目标文件夹dst_dir,执行程序即可整流导出的音频文件,实测700多首音乐,只需不到一分钟时间即可完成重命名和移动工作。
完整工程可在此下载: