Created
November 19, 2019 13:11
-
-
Save Jupiops/84a359810ab347ed67cac1c5b8380657 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace NeteaseMusic; | |
class NeteaseMusic | |
{ | |
private const SECRET = '7246674226682325323F5E6544673A51'; | |
private $_MINI_MODE = false; | |
private function randomUserAgent() | |
{ | |
$userAgentList = [ | |
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36", | |
"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1", | |
"Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1", | |
"Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Mobile Safari/537.36", | |
"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Mobile Safari/537.36", | |
"Mozilla/5.0 (Linux; Android 5.1.1; Nexus 6 Build/LYZ28E) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Mobile Safari/537.36", | |
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_2 like Mac OS X) AppleWebKit/603.2.4 (KHTML, like Gecko) Mobile/14F89;GameHelper", | |
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.1.1 Safari/603.2.4", | |
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_0 like Mac OS X) AppleWebKit/602.1.38 (KHTML, like Gecko) Version/10.0 Mobile/14A300 Safari/602.1", | |
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", | |
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:46.0) Gecko/20100101 Firefox/46.0", | |
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:46.0) Gecko/20100101 Firefox/46.0", | |
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)", | |
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)", | |
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)", | |
"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Win64; x64; Trident/6.0)", | |
"Mozilla/5.0 (Windows NT 6.3; Win64, x64; Trident/7.0; rv:11.0) like Gecko", | |
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/13.10586", | |
"Mozilla/5.0 (iPad; CPU OS 10_0 like Mac OS X) AppleWebKit/602.1.38 (KHTML, like Gecko) Version/10.0 Mobile/14A300 Safari/602.1" | |
]; | |
return $userAgentList[rand(0, count($userAgentList) - 1)]; | |
} | |
private function randomCookies($music_u) | |
{ | |
$cookiesList = [ | |
'os=pc; osver=Microsoft-Windows-10-Professional-build-10586-64bit; appver=2.0.3.131777; channel=netease; __remember_me=true', | |
'MUSIC_U=' . $music_u . '; buildver=1506310743; resolution=1920x1080; mobilename=MI5; osver=7.0.1; channel=coolapk; os=android; appver=4.2.0', | |
'osver=%E7%89%88%E6%9C%AC%2010.13.3%EF%BC%88%E7%89%88%E5%8F%B7%2017D47%EF%BC%89; os=osx; appver=1.5.9; MUSIC_U=' . $music_u . '; channel=netease;' | |
]; | |
return $cookiesList[rand(0, count($cookiesList) - 1)]; | |
} | |
public function mini() | |
{ | |
$this->_MINI_MODE = true; | |
return $this; | |
} | |
/** | |
* Get a list of songs based on keywords | |
* @param {Integer} string Key words | |
* @return | |
*/ | |
function search($keyword, $type = 1, $limit = 10, $page = 1) | |
{ | |
$body = array( | |
'method' => 'POST', | |
'params' => array( | |
's' => $keyword, | |
// 1: Single, 10: Album, 100: Singer, 1000: Song, 1002: User, 1004: MV, 1006: Lyrics, 1009: Radio, 1014: Video | |
'type' => $type, | |
'limit' => $limit, | |
'total' => true, | |
'offset' => $page - 1 | |
), | |
'url' => 'http://music.163.com/api/cloudsearch/pc' | |
); | |
$form = $this->neteaseAESECB($body); | |
$options = $this->getHttpOption('POST', strlen($form)); | |
$raw = $this->makeRequest('/api/linux/forward', $form, $options); | |
if ($this->_MINI_MODE) { | |
$this->_MINI_MODE = false; | |
$raw = json_decode($raw, true, 512, JSON_BIGINT_AS_STRING); | |
return json_encode($this->clear_data($raw["result"]["songs"]), JSON_UNESCAPED_SLASHES); | |
} else return $raw; | |
} | |
/** | |
* Get artist information based on artist id | |
* @param {Integer} string Artist id | |
* @return {Promise} | |
*/ | |
function artist($id, $limit = 50) | |
{ | |
$body = array( | |
'method' => 'GET', | |
'params' => array( | |
'id' => $id, | |
'ext' => true, | |
'top' => $limit | |
), | |
'url' => "http://music.163.com/api/v1/artist/$id" | |
); | |
$form = $this->neteaseAESECB($body); | |
$options = $this->getHttpOption('POST', strlen($form)); | |
$raw = $this->makeRequest('/api/linux/forward', $form, $options); | |
if ($this->_MINI_MODE) { | |
$this->_MINI_MODE = false; | |
$raw = json_decode($raw, true, 512, JSON_BIGINT_AS_STRING); | |
return json_encode($this->clear_data($raw["hotSongs"]), JSON_UNESCAPED_SLASHES); | |
} else return $raw; | |
} | |
/** | |
* Get song list information and song list based on song list id | |
* @param {Integer} string Song list id | |
* @return {Promise} | |
*/ | |
function playlist($id) | |
{ | |
$body = array( | |
'method' => 'POST', | |
'params' => array( | |
'id' => $id, | |
'n' => 1000 | |
), | |
'url' => 'http://music.163.com/api/v3/playlist/detail' | |
); | |
$form = $this->neteaseAESECB($body); | |
$options = $this->getHttpOption('POST', strlen($form)); | |
$raw = $this->makeRequest('/api/linux/forward', $form, $options); | |
return $raw; | |
} | |
/** | |
* 根据专辑 id 获取专辑信息及歌曲列表 | |
* @param {Integer} string 专辑 id | |
* @return {Promise} | |
*/ | |
function album($id) | |
{ | |
$body = array( | |
'method' => 'GET', | |
'params' => array('id' => $id), | |
'url' => "http://music.163.com/api/v1/album/$id" | |
); | |
$form = $this->neteaseAESECB($body); | |
$options = $this->getHttpOption('POST', strlen($form)); | |
$raw = $this->makeRequest('/api/linux/forward', $form, $options); | |
if ($this->_MINI_MODE) { | |
$this->_MINI_MODE = false; | |
$raw = json_decode($raw, true, 512, JSON_BIGINT_AS_STRING); | |
return json_encode($this->clear_data($raw["songs"]), JSON_UNESCAPED_SLASHES); | |
} else return $raw; | |
} | |
/** | |
* 根据歌曲 id 获取歌曲信息 | |
* @param {Integer} string 歌曲 id | |
* @return {Promise} | |
*/ | |
function song($id) | |
{ | |
$body = array( | |
'method' => 'POST', | |
'params' => array( | |
'c' => "[{id: $id}]" | |
), | |
'url' => 'http://music.163.com/api/v3/song/detail' | |
); | |
$form = $this->neteaseAESECB($body); | |
$options = $this->getHttpOption('POST', strlen($form)); | |
$raw = $this->makeRequest('/api/linux/forward', $form, $options); | |
if ($this->_MINI_MODE) { | |
$this->_MINI_MODE = false; | |
$raw = json_decode($raw, true, 512, JSON_BIGINT_AS_STRING); | |
return json_encode($this->clear_data($raw["songs"]), JSON_UNESCAPED_SLASHES); | |
} else return $raw; | |
} | |
/** | |
* Get song resource address based on song id | |
* @param {Integer} string song id | |
* @return {Promise} | |
*/ | |
function url($id, $br = 320) | |
{ | |
$body = array( | |
'method' => 'POST', | |
'params' => array( | |
'ids' => "[$id]", | |
'br' => $br * 1000 | |
), | |
'url' => 'http://music.163.com/api/song/enhance/player/url' | |
); | |
$form = $this->neteaseAESECB($body); | |
$options = $this->getHttpOption('POST', strlen($form)); | |
return $this->makeRequest('/api/linux/forward', $form, $options); | |
} | |
private | |
function getHttpOption($method, $contentLength) | |
{ | |
$header = array( | |
'Referer' => 'https://music.163.com/', | |
'Cookie' => $this->randomCookies($this->getRandomHex(128)), | |
'User-Agent' => $this->randomUserAgent(), | |
'X-Real-IP' => '118.88.88.88' | |
); | |
if ('POST' === $method) { | |
$header['Content-Type'] = 'application/x-www-form-urlencoded'; | |
if ($contentLength) { | |
$header['Content-Length'] = $contentLength; | |
} | |
} | |
$options = array('http' => array( | |
'method' => $method, | |
'header' => $this->build_header_str($header), | |
// "ssl" => array( | |
//// "verify_peer" => false, | |
//// "verify_peer_name" => false, | |
//// ), | |
)); | |
return $options; | |
} | |
private | |
function makeRequest($path, $form, $options) | |
{ | |
$options['http']['content'] = $form; | |
return file_get_contents('https://music.163.com' . $path, false, stream_context_create($options)); | |
} | |
private | |
function build_header_str($input) | |
{ | |
return implode("\r\n", array_map( | |
function ($v, $k) { | |
return sprintf("%s: %s", $k, $v); | |
}, | |
$input, | |
array_keys($input) | |
)) . "\r\n"; | |
} | |
private function getRandomHex($strlen) | |
{ | |
return implode(array_map(function () { | |
return dechex(mt_rand(0, 15)); | |
}, array_fill(0, $strlen, null))); | |
} | |
function neteaseAESECB($body) | |
{ | |
$body = json_encode($body, JSON_UNESCAPED_SLASHES); | |
$password = pack("H*", self::SECRET); | |
$cipher = openssl_encrypt($body, "AES-128-ECB", $password, OPENSSL_RAW_DATA); | |
$hex = bin2hex($cipher); | |
$form = array( | |
'eparams' => strtoupper($hex) | |
); | |
return http_build_query($form); | |
} | |
private function clear_data($result) | |
{ | |
// you can modify it by yourself, change to your API?! | |
foreach ($result as $key => $vo) { | |
$data[$key] = array( | |
'id' => $vo["id"], | |
'name' => $vo["name"], | |
'album' => array( | |
'id' => $vo['al']['id'], | |
'name' => $vo['al']['name'], | |
'cover' => array_key_exists('picUrl', $vo['al']) ? $vo['al']['picUrl'] : 'https://p1.music.126.net/' . self::Id2Url($vo['al']['pic_str'] ?: $vo['al']['pic']) . '/' . ($vo['al']['pic_str'] ?: $vo['al']['pic']) . '.jpg', | |
), | |
// 'cover' => 'https://p4.music.126.net/' . self::Id2Url($vo['al']['pic_str'] ?: $vo['al']['pic']) . '/' . ($vo['al']['pic_str'] ?: $vo['al']['pic']) . '.jpg', | |
// 'url' => 'http://music.163.com/song/media/outer/url?id=' . $vo["id"], | |
//'lyric'=>$vo["id"], | |
'artists' => array(), | |
); | |
foreach ($vo['ar'] as $key_ => $vvo) { | |
$data[$key]['artists'][$key_]['id'] = $vvo['id']; | |
$data[$key]['artists'][$key_]['name'] = $vvo['name']; | |
} | |
// $data[$key]['artist'] = implode(', ', $data[$key]['artist']); | |
} | |
return $data; | |
} | |
/* static url encrypt, use for pic*/ | |
public function Id2Url($id) | |
{ | |
$byte1[] = $this->Str2Arr('3go8&$8*3*3h0k(2)2'); | |
$byte2[] = $this->Str2Arr($id); | |
$magic = $byte1[0]; | |
$song_id = $byte2[0]; | |
for ($i = 0; $i < count($song_id); $i++) $song_id[$i] = $song_id[$i] ^ $magic[$i % count($magic)]; | |
$result = base64_encode(md5($this->Arr2Str($song_id), 1)); | |
$result = str_replace('/', '_', $result); | |
$result = str_replace('+', '-', $result); | |
return $result; | |
} | |
private function Str2Arr($string) | |
{ | |
$bytes = array(); | |
for ($i = 0; $i < strlen($string); $i++) $bytes[] = ord($string[$i]); | |
return $bytes; | |
} | |
private function Arr2Str($bytes) | |
{ | |
$str = ''; | |
for ($i = 0; $i < count($bytes); $i++) $str .= chr($bytes[$i]); | |
return $str; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment