Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
zzh committed Oct 17, 2018
1 parent a6183ec commit ec87e67
Show file tree
Hide file tree
Showing 6 changed files with 321 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
@@ -0,0 +1,4 @@
*.iml
.idea
target
music163
13 changes: 13 additions & 0 deletions README.md
@@ -0,0 +1,13 @@
# 163musicDownload

Download all songs or individual songs of Netease Cloud Music Songs (下载网易云音乐歌单所有歌曲或单独歌曲)

### 使用
1. 下载[music163Download-1.0.jar](https://github.com/zonas0574/163musicDownload/releases/tag/1.0)
2. 运行`java -jar music163Download-1.0.jar`
3. 按命令行中提醒依次输入以下参数
- 类型(1.歌单id 2.歌曲id)
- id 只有歌曲id支持多个(用,隔开)

### 借鉴
163music API:[163MusicHotComments](https://github.com/zonas0574/163MusicHotComments/blob/master/src/main/java/zonas/MusicAPI.java)
51 changes: 51 additions & 0 deletions pom.xml
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.zonas</groupId>
<artifactId>music163Download</artifactId>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>net.dongliu</groupId>
<artifactId>requests</artifactId>
<version>4.12.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>zonas.App</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
83 changes: 83 additions & 0 deletions src/main/java/zonas/API.java
@@ -0,0 +1,83 @@
package zonas;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class API {

@SuppressWarnings("unchecked")
public static String playlist_detail(String id, List<Map<String, Object>> fileList) {
String url = Util.HOSTURL + "weapi/v3/playlist/detail?csrf_token=";
String param = "{id:'" + id + "', csrf_token:''}";
String json = Util.getJson(param, url);
Map<String, Object> map = Util.getMapByJson(json);
if (map.get("code").toString().contains("404")) {
System.out.println("解析歌单失败,id:" + id);
return "";
}
List<Map<String, Object>> playlist = (List<Map<String, Object>>) ((Map<String, Object>) map.get("playlist")).get("tracks");
return listToIds(playlist, fileList);
}

@SuppressWarnings("unchecked")
public static String song_detail(String id, List<Map<String, Object>> fileList) {
String url = Util.HOSTURL + "weapi/v3/song/detail?csrf_token=";
String[] ids = id.split(",");
StringBuilder c = new StringBuilder();
for (String i : ids) {
c.append("{\"id\":").append(i).append("},");
}
String param = "{ids:'[" + id + "]',c:'[" + c.substring(0, c.length() - 1) + "]', csrf_token:''}";
String json = Util.getJson(param, url);
Map<String, Object> map = Util.getMapByJson(json);
if (map.get("code").toString().contains("404")) {
System.out.println("解析歌曲详情失败,id:" + id);
return "";
}
List<Map<String, Object>> songList = (List<Map<String, Object>>) map.get("songs");
return listToIds(songList, fileList);
}

@SuppressWarnings("unchecked")
public static List<Map<String, Object>> song_url(String ids, List<Map<String, Object>> fileList) {
String url = Util.HOSTURL + "weapi/song/enhance/player/url?csrf_token=";
String param = "{ids:'[" + ids + "]',br:999000, csrf_token:''}";
String json = Util.getJson(param, url);
Map<String, Object> map = Util.getMapByJson(json);
List<Map<String, Object>> songs = (List<Map<String, Object>>) map.get("data");
for (Map<String, Object> song : songs) {
for (Map<String, Object> aSongList : fileList) {
if (Util.StringToInt(song.get("id").toString()) == Util.StringToInt(aSongList.get("id").toString())) {
aSongList.put("url", song.get("url"));
}
}

}
return fileList;
}

@SuppressWarnings("unchecked")
private static String listToIds(List<Map<String, Object>> inList, List<Map<String, Object>> outList) {
StringBuilder ids = new StringBuilder();
Map<String, Object> songs;
for (Map<String, Object> map : inList) {
songs = new HashMap<String, Object>();
ids.append(Util.StringToInt(map.get("id").toString())).append(",");
List<Map<String, Object>> ar = (List<Map<String, Object>>) map.get("ar");
StringBuilder name = new StringBuilder();
for (Map<String, Object> map1 : ar) {
name.append(map1.get("name").toString()).append("&");
}
songs.put("id", Util.StringToInt(map.get("id").toString()));
songs.put("name", name.substring(0, name.length() - 1) + "-" + map.get("name"));
outList.add(songs);
}
if (inList.isEmpty()) {
return ids.toString();
} else {
return ids.substring(0, ids.length() - 1);
}

}
}
47 changes: 47 additions & 0 deletions src/main/java/zonas/App.java
@@ -0,0 +1,47 @@
package zonas;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutionException;

public class App {
public static void main(String[] args) throws ExecutionException, InterruptedException, IOException {
Scanner sc = new Scanner(System.in);
System.out.print("请选择下载类型(1.歌单id 2.歌曲id):");
String temp = sc.nextLine();
List<Map<String, Object>> playlist = new ArrayList<Map<String, Object>>();
String ids;
if ("1".equals(temp)) {
System.out.print("请输入歌单id(仅限单id):");
String playId = sc.nextLine();
ids = API.playlist_detail(playId, playlist);
} else if ("2".equals(temp)) {
System.out.print("请输入歌曲id(支持多个id用,隔开):");
String songIds = sc.nextLine();
ids = API.song_detail(songIds, playlist);
} else {
System.out.println("请输入正确类型");
return;
}
System.out.println("歌曲id:" + ids);
playlist = API.song_url(ids, playlist);
System.out.println("解析完成,共下载" + playlist.size() + "首歌曲");
if (!Util.createDir("music163")) {
System.out.println("创建文件夹失败,退出下载");
return;
}
for (Map<String, Object> map : playlist) {
if (map.get("url") != null) {
byte[] data = Util.fileDownload(map.get("url").toString());
FileOutputStream outputStream = new FileOutputStream(new File("music163" + File.separator + map.get("name") + ".mp3"));
outputStream.write(data);
System.out.println("已下载歌曲:" + map.get("name"));
outputStream.close();
} else {
System.out.println("无法获取下载地址:" + map.get("name"));
}
}
}
}
123 changes: 123 additions & 0 deletions src/main/java/zonas/Util.java
@@ -0,0 +1,123 @@
package zonas;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import net.dongliu.requests.Requests;
import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;

public class Util {
private Util() {
}

public static final String HOSTURL = "http://music.163.com/";

private static String aesEncrypt(String text, byte[] key, byte[] iv) {
String encryptText = "";
try {
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv));
byte[] cipherData = cipher.doFinal(text.getBytes("UTF-8"));
encryptText = new Base64().encodeAsString(cipherData);
} catch (GeneralSecurityException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return encryptText;
}

private static String getParams(String param) {
byte[] forthParam = {'0', 'C', 'o', 'J', 'U', 'm', '6', 'Q', 'y', 'w', '8', 'W', '8', 'j', 'u', 'd'};
byte[] iv = {'0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', '0', '7', '0', '8'};
byte[] secondKey = {'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F'};
String encText;
encText = aesEncrypt(param, forthParam, iv);
encText = aesEncrypt(encText, secondKey, iv);
return encText;
}

public static String getJson(String params, String url) {
String encSecKey = "257348aecb5e556c066de214e531faadd1c55d814f9be95fd06d6bff9f4c7a41f831f6394d5a3fd2e3881736d94a02ca919d952872e7d0a50ebfa1769a7a62d512f5f1ca21aec60bc3819a9c3ffca5eca9a0dba6d6f7249b06f5965ecfff3695b54e1c28f3f624750ed39e7de08fc8493242e26dbc4484a01c76f739e135637c";
Map<String, String> data = new HashMap<String, String>();
data.put("params", getParams(params));
data.put("encSecKey", encSecKey);
Map<String, String> headers = new HashMap<String, String>();
headers.put("Accept", "*/*");
headers.put("Accept-Encoding", "gzip, deflate");
headers.put("Accept-Language", "zh-CN,zh;q=0.8");
headers.put("Connection", "keep-alive");
headers.put("Content-Type", "application/x-www-form-urlencoded");
headers.put("Host", "music.163.com");
headers.put("Origin", HOSTURL);
headers.put("Referer", HOSTURL);
headers.put("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36");
return Requests.post(url).socksTimeout(2000).headers(headers).body(data).send().readToText();
}

public static Map<String, Object> getMapByJson(String json) {
Gson g = new Gson();
return g.fromJson(json, new TypeToken<Map<String, Object>>() {
}.getType());
}

public static int StringToInt(String s) {
return Double.valueOf(s).intValue();
}

public static byte[] fileDownload(final String netURL) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(10);
Future<byte[]> future = executorService.submit(new Callable<byte[]>() {
public byte[] call() {
byte[] getData = new byte[0];
InputStream is = null;
try {
URL url = new URL(netURL);
URLConnection connection = url.openConnection();
is = connection.getInputStream();
getData = toByteArray(is);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return getData;
}
});
return future.get();
}

private static byte[] toByteArray(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024 * 4];
int n = 0;
while ((n = in.read(buffer)) != -1) {
out.write(buffer, 0, n);
}
return out.toByteArray();
}

public static boolean createDir(String destDirName) {
File dir = new File(destDirName);
if (!dir.exists()) {
return dir.mkdirs();
}
return true;
}

}

0 comments on commit ec87e67

Please sign in to comment.