├── .editorconfig ├── LICENSE ├── README.md ├── cache └── playlist │ └── netease_427139429.json ├── composer.json ├── composer.lock ├── docs ├── favicon.png └── index.html ├── favicon.png ├── index.php ├── public └── index.php └── src └── Meting.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = false 8 | indent_style = space 9 | 10 | [*.md] 11 | indent_size = 2 12 | insert_final_newline = false 13 | trim_trailing_whitespace = false 14 | 15 | [*.php] 16 | indent_size = 4 17 | insert_final_newline = true 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 injahow 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Meting 3 |

4 | 5 | # meting-api 6 | 7 | ## Descriptions 8 | 9 | - 这是基于 [Meting](https://github.com/metowolf/Meting) 创建的 APlayer API 10 | - 灵感源于 [https://api.fczbl.vip/163/](https://api.fczbl.vip/163/) 11 | - 部分参考 [Meting-API](https://github.com/metowolf/Meting-API) 12 | 13 | ## Build Setup 14 | 15 | ```bash 16 | # 克隆仓库 17 | $ git clone https://github.com/injahow/meting-api.git 18 | 19 | $ cd meting-api 20 | 21 | # 安装依赖 22 | $ composer install 23 | 24 | # 或者使用中国镜像 25 | $ composer config -g repo.packagist composer https://packagist.phpcomposer.com 26 | 27 | $ composer install 28 | ``` 29 | 30 | 或者下载打包文件[https://github.com/injahow/meting-api/releases](https://github.com/injahow/meting-api/releases) 31 | 32 | 或者直接使用 Meting.php 33 | 34 | ```php 35 | // include __DIR__ . '/vendor/autoload.php'; 36 | include __DIR__ . '/src/Meting.php'; 37 | ``` 38 | 39 | 修改代码参数 40 | 41 | ```php 42 | =5.4.0" 27 | }, 28 | "suggest": { 29 | "ext-bcmath": "Required to use BC Math calculate RSA.", 30 | "ext-openssl": "Required to use OpenSSL encrypt params." 31 | }, 32 | "type": "library", 33 | "autoload": { 34 | "psr-4": { 35 | "Metowolf\\": "src/" 36 | } 37 | }, 38 | "notification-url": "https://packagist.org/downloads/", 39 | "license": ["MIT"], 40 | "authors": [ 41 | { 42 | "name": "metowolf", 43 | "email": "i@i-meto.com", 44 | "homepage": "https://i-meto.com" 45 | } 46 | ], 47 | "description": "A powerful music API framework to accelerate development.", 48 | "homepage": "https://github.com/metowolf/Meting", 49 | "keywords": ["lightweight", "lighty", "music api", "php music"], 50 | "time": "2019-06-16T05:55:00+00:00" 51 | } 52 | ], 53 | "packages-dev": [], 54 | "aliases": [], 55 | "minimum-stability": "stable", 56 | "stability-flags": [], 57 | "prefer-stable": false, 58 | "prefer-lowest": false, 59 | "platform": [], 60 | "platform-dev": [] 61 | } 62 | -------------------------------------------------------------------------------- /docs/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/injahow/meting-api/230e38df9aff90e41d02e9bdeec5f0c866764ac7/docs/favicon.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Meting-APlayer 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/injahow/meting-api/230e38df9aff90e41d02e9bdeec5f0c866764ac7/favicon.png -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | format(true); 53 | 54 | // 设置cookie 55 | /*if ($server == 'netease') { 56 | $api->cookie('os=pc; osver=Microsoft-Windows-10-Professional-build-10586-64bit; appver=2.0.3.131777; channel=netease; MUSIC_U=****** ; __remember_me=true'); 57 | }*/ 58 | 59 | if ($type == 'playlist') { 60 | 61 | if (CACHE) { 62 | $file_path = __DIR__ . '/cache/playlist/' . $server . '_' . $id . '.json'; 63 | if (file_exists($file_path)) { 64 | if ($_SERVER['REQUEST_TIME'] - filemtime($file_path) < CACHE_TIME) { 65 | echo file_get_contents($file_path); 66 | exit; 67 | } 68 | } 69 | } 70 | 71 | $data = $api->playlist($id); 72 | if ($data == '[]') { 73 | echo '{"error":"unknown playlist id"}'; 74 | exit; 75 | } 76 | $data = json_decode($data); 77 | $playlist = array(); 78 | foreach ($data as $song) { 79 | $playlist[] = array( 80 | 'name' => $song->name, 81 | 'artist' => implode('/', $song->artist), 82 | 'url' => API_URI . '?server=' . $song->source . '&type=url&id=' . $song->url_id . (AUTH ? '&auth=' . auth($song->source . 'url' . $song->url_id) : ''), 83 | 'pic' => API_URI . '?server=' . $song->source . '&type=pic&id=' . $song->pic_id . (AUTH ? '&auth=' . auth($song->source . 'pic' . $song->pic_id) : ''), 84 | 'lrc' => API_URI . '?server=' . $song->source . '&type=lrc&id=' . $song->lyric_id . (AUTH ? '&auth=' . auth($song->source . 'lrc' . $song->lyric_id) : '') 85 | ); 86 | } 87 | $playlist = json_encode($playlist); 88 | 89 | if (CACHE) { 90 | // ! mkdir /cache/playlist 91 | file_put_contents($file_path, $playlist); 92 | } 93 | 94 | echo $playlist; 95 | } else { 96 | $need_song = !in_array($type, ['url', 'pic', 'lrc']); 97 | if ($need_song && !in_array($type, ['name', 'artist', 'song'])) { 98 | echo '{"error":"unknown type"}'; 99 | exit; 100 | } 101 | 102 | if (APCU_CACHE) { 103 | $apcu_time = $type == 'url' ? 600 : 36000; 104 | $apcu_type_key = $server . $type . $id; 105 | if (apcu_exists($apcu_type_key)) { 106 | $data = apcu_fetch($apcu_type_key); 107 | return_data($type, $data); 108 | } 109 | if ($need_song) { 110 | $apcu_song_id_key = $server . 'song_id' . $id; 111 | if (apcu_exists($apcu_song_id_key)) { 112 | $song = apcu_fetch($apcu_song_id_key); 113 | } 114 | } 115 | } 116 | 117 | if (!$need_song) { 118 | $data = song2data($api, null, $type, $id); 119 | } else { 120 | if (!isset($song)) $song = $api->song($id); 121 | if ($song == '[]') { 122 | echo '{"error":"unknown song"}'; 123 | exit; 124 | } 125 | if (APCU_CACHE) { 126 | apcu_store($apcu_song_id_key, $song, $apcu_time); 127 | } 128 | $data = song2data($api, json_decode($song)[0], $type, $id); 129 | } 130 | 131 | if (APCU_CACHE) { 132 | apcu_store($apcu_type_key, $data, $apcu_time); 133 | } 134 | 135 | return_data($type, $data); 136 | } 137 | 138 | function api_uri() // static 139 | { 140 | return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . strtok($_SERVER['REQUEST_URI'], '?'); 141 | } 142 | 143 | function auth($name) 144 | { 145 | return hash_hmac('sha1', $name, AUTH_SECRET); 146 | } 147 | 148 | function song2data($api, $song, $type, $id) 149 | { 150 | $data = ''; 151 | switch ($type) { 152 | case 'name': 153 | $data = $song->name; 154 | break; 155 | 156 | case 'artist': 157 | $data = implode('/', $song->artist); 158 | break; 159 | 160 | case 'url': 161 | $m_url = json_decode($api->url($id, 320))->url; 162 | if ($m_url == '') break; 163 | // url format 164 | if ($api->server == 'netease') { 165 | if ($m_url[4] != 's') $m_url = str_replace('http', 'https', $m_url); 166 | } 167 | 168 | $data = $m_url; 169 | break; 170 | 171 | case 'pic': 172 | $data = json_decode($api->pic($id, 90))->url; 173 | break; 174 | 175 | case 'lrc': 176 | $lrc_data = json_decode($api->lyric($id)); 177 | if ($lrc_data->lyric == '') { 178 | $lrc = '[00:00.00]这似乎是一首纯音乐呢,请尽情欣赏它吧!'; 179 | } else if ($lrc_data->tlyric == '') { 180 | $lrc = $lrc_data->lyric; 181 | } else if (TLYRIC) { // lyric_cn 182 | $lrc_arr = explode("\n", $lrc_data->lyric); 183 | $lrc_cn_arr = explode("\n", $lrc_data->tlyric); 184 | $lrc_cn_map = array(); 185 | foreach ($lrc_cn_arr as $i => $v) { 186 | if ($v == '') continue; 187 | $line = explode(']', $v, 2); 188 | // 格式化处理 189 | $line[1] = trim(preg_replace('/\s\s+/', ' ', $line[1])); 190 | $lrc_cn_map[$line[0]] = $line[1]; 191 | unset($lrc_cn_arr[$i]); 192 | } 193 | foreach ($lrc_arr as $i => $v) { 194 | if ($v == '') continue; 195 | $key = explode(']', $v, 2)[0]; 196 | if (!empty($lrc_cn_map[$key]) && $lrc_cn_map[$key] != '//') { 197 | $lrc_arr[$i] .= ' (' . $lrc_cn_map[$key] . ')'; 198 | unset($lrc_cn_map[$key]); 199 | } 200 | } 201 | $lrc = implode("\n", $lrc_arr); 202 | } else { 203 | $lrc = $lrc_data->lyric; 204 | } 205 | $data = $lrc; 206 | break; 207 | 208 | case 'song': 209 | $data = json_encode(array(array( 210 | 'name' => $song->name, 211 | 'artist' => implode('/', $song->artist), 212 | 'url' => API_URI . '?server=' . $song->source . '&type=url&id=' . $song->url_id . (AUTH ? '&auth=' . auth($song->source . 'url' . $song->url_id) : ''), 213 | 'pic' => API_URI . '?server=' . $song->source . '&type=pic&id=' . $song->pic_id . (AUTH ? '&auth=' . auth($song->source . 'pic' . $song->pic_id) : ''), 214 | 'lrc' => API_URI . '?server=' . $song->source . '&type=lrc&id=' . $song->lyric_id . (AUTH ? '&auth=' . auth($song->source . 'lrc' . $song->lyric_id) : '') 215 | ))); 216 | break; 217 | } 218 | if ($data == '') exit; 219 | return $data; 220 | } 221 | 222 | function return_data($type, $data) 223 | { 224 | if (in_array($type, ['url', 'pic'])) { 225 | header('Location: ' . $data); 226 | } else { 227 | echo $data; 228 | } 229 | exit; 230 | } 231 | -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Meting-API 8 | 9 | 10 | 11 |

参数说明

12 | server: 数据源 13 |
14 |      netease 网易云音乐(默认)
15 |      tencent QQ音乐
16 |
17 | type: 类型
18 |      name 歌曲名
19 |      artist 歌手
20 |      url 链接
21 |      pic 封面
22 |      lrc 歌词
23 |      song 单曲
24 |      playlist 歌单

25 | id: 类型ID(封面ID/单曲ID/歌单ID)
26 |
27 | GitHub:meting-api,此API基于 Meting 构建。

28 | 例如:?type=url&id=416892104
29 | ?type=song&id=591321
30 | ?type=playlist&id=2619366284 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/Meting.php: -------------------------------------------------------------------------------- 1 | 9 | * Released under the MIT license 10 | */ 11 | 12 | namespace Metowolf; 13 | 14 | class Meting 15 | { 16 | const VERSION = '1.5.10'; 17 | 18 | public $raw; 19 | public $data; 20 | public $info; 21 | public $error; 22 | public $status; 23 | 24 | public $server; 25 | public $proxy = null; 26 | public $format = false; 27 | public $header; 28 | 29 | public function __construct($value = 'netease') 30 | { 31 | $this->site($value); 32 | } 33 | 34 | public function site($value) 35 | { 36 | $suppose = array('netease', 'tencent', 'xiami', 'kugou', 'baidu', 'kuwo'); 37 | $this->server = in_array($value, $suppose) ? $value : 'netease'; 38 | $this->header = $this->curlset(); 39 | 40 | return $this; 41 | } 42 | 43 | public function cookie($value) 44 | { 45 | $this->header['Cookie'] = $value; 46 | 47 | return $this; 48 | } 49 | 50 | public function format($value = true) 51 | { 52 | $this->format = $value; 53 | 54 | return $this; 55 | } 56 | 57 | public function proxy($value) 58 | { 59 | $this->proxy = $value; 60 | 61 | return $this; 62 | } 63 | 64 | private function exec($api) 65 | { 66 | if (isset($api['encode'])) { 67 | $api = call_user_func_array(array($this, $api['encode']), array($api)); 68 | } 69 | if ($api['method'] == 'GET') { 70 | if (isset($api['body'])) { 71 | $api['url'] .= '?'.http_build_query($api['body']); 72 | $api['body'] = null; 73 | } 74 | } 75 | 76 | $this->curl($api['url'], $api['body']); 77 | 78 | if (!$this->format) { 79 | return $this->raw; 80 | } 81 | 82 | $this->data = $this->raw; 83 | 84 | if (isset($api['decode'])) { 85 | $this->data = call_user_func_array(array($this, $api['decode']), array($this->data)); 86 | } 87 | if (isset($api['format'])) { 88 | $this->data = $this->clean($this->data, $api['format']); 89 | } 90 | 91 | return $this->data; 92 | } 93 | 94 | private function curl($url, $payload = null, $headerOnly = 0) 95 | { 96 | $header = array_map(function ($k, $v) { 97 | return $k.': '.$v; 98 | }, array_keys($this->header), $this->header); 99 | $curl = curl_init(); 100 | if (!is_null($payload)) { 101 | curl_setopt($curl, CURLOPT_POST, 1); 102 | curl_setopt($curl, CURLOPT_POSTFIELDS, is_array($payload) ? http_build_query($payload) : $payload); 103 | } 104 | curl_setopt($curl, CURLOPT_HEADER, $headerOnly); 105 | curl_setopt($curl, CURLOPT_TIMEOUT, 20); 106 | curl_setopt($curl, CURLOPT_ENCODING, 'gzip'); 107 | curl_setopt($curl, CURLOPT_IPRESOLVE, 1); 108 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 109 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); 110 | curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10); 111 | curl_setopt($curl, CURLOPT_URL, $url); 112 | curl_setopt($curl, CURLOPT_HTTPHEADER, $header); 113 | if ($this->proxy) { 114 | curl_setopt($curl, CURLOPT_PROXY, $this->proxy); 115 | } 116 | for ($i = 0; $i < 3; $i++) { 117 | $this->raw = curl_exec($curl); 118 | $this->info = curl_getinfo($curl); 119 | $this->error = curl_errno($curl); 120 | $this->status = $this->error ? curl_error($curl) : ''; 121 | if (!$this->error) { 122 | break; 123 | } 124 | } 125 | curl_close($curl); 126 | 127 | return $this; 128 | } 129 | 130 | private function pickup($array, $rule) 131 | { 132 | $t = explode('.', $rule); 133 | foreach ($t as $vo) { 134 | if (!isset($array[$vo])) { 135 | return array(); 136 | } 137 | $array = $array[$vo]; 138 | } 139 | 140 | return $array; 141 | } 142 | 143 | private function clean($raw, $rule) 144 | { 145 | $raw = json_decode($raw, true); 146 | if (!empty($rule)) { 147 | $raw = $this->pickup($raw, $rule); 148 | } 149 | if (!isset($raw[0]) && count($raw)) { 150 | $raw = array($raw); 151 | } 152 | $result = array_map(array($this, 'format_'.$this->server), $raw); 153 | 154 | return json_encode($result); 155 | } 156 | 157 | public function search($keyword, $option = null) 158 | { 159 | switch ($this->server) { 160 | case 'netease': 161 | $api = array( 162 | 'method' => 'POST', 163 | 'url' => 'http://music.163.com/api/cloudsearch/pc', 164 | 'body' => array( 165 | 's' => $keyword, 166 | 'type' => isset($option['type']) ? $option['type'] : 1, 167 | 'limit' => isset($option['limit']) ? $option['limit'] : 30, 168 | 'total' => 'true', 169 | 'offset' => isset($option['page']) && isset($option['limit']) ? ($option['page'] - 1) * $option['limit'] : 0, 170 | ), 171 | 'encode' => 'netease_AESCBC', 172 | 'format' => 'result.songs', 173 | ); 174 | break; 175 | case 'tencent': 176 | $api = array( 177 | 'method' => 'GET', 178 | 'url' => 'https://c.y.qq.com/soso/fcgi-bin/client_search_cp', 179 | 'body' => array( 180 | 'format' => 'json', 181 | 'p' => isset($option['page']) ? $option['page'] : 1, 182 | 'n' => isset($option['limit']) ? $option['limit'] : 30, 183 | 'w' => $keyword, 184 | 'aggr' => 1, 185 | 'lossless' => 1, 186 | 'cr' => 1, 187 | 'new_json' => 1, 188 | ), 189 | 'format' => 'data.song.list', 190 | ); 191 | break; 192 | case 'xiami': 193 | $api = array( 194 | 'method' => 'GET', 195 | 'url' => 'https://acs.m.xiami.com/h5/mtop.alimusic.search.searchservice.searchsongs/1.0/', 196 | 'body' => array( 197 | 'data' => array( 198 | 'key' => $keyword, 199 | 'pagingVO' => array( 200 | 'page' => isset($option['page']) ? $option['page'] : 1, 201 | 'pageSize' => isset($option['limit']) ? $option['limit'] : 30, 202 | ), 203 | ), 204 | 'r' => 'mtop.alimusic.search.searchservice.searchsongs', 205 | ), 206 | 'encode' => 'xiami_sign', 207 | 'format' => 'data.data.songs', 208 | ); 209 | break; 210 | case 'kugou': 211 | $api = array( 212 | 'method' => 'GET', 213 | 'url' => 'http://mobilecdn.kugou.com/api/v3/search/song', 214 | 'body' => array( 215 | 'api_ver' => 1, 216 | 'area_code' => 1, 217 | 'correct' => 1, 218 | 'pagesize' => isset($option['limit']) ? $option['limit'] : 30, 219 | 'plat' => 2, 220 | 'tag' => 1, 221 | 'sver' => 5, 222 | 'showtype' => 10, 223 | 'page' => isset($option['page']) ? $option['page'] : 1, 224 | 'keyword' => $keyword, 225 | 'version' => 8990, 226 | ), 227 | 'format' => 'data.info', 228 | ); 229 | break; 230 | case 'baidu': 231 | $api = array( 232 | 'method' => 'GET', 233 | 'url' => 'http://musicapi.taihe.com/v1/restserver/ting', 234 | 'body' => array( 235 | 'from' => 'qianqianmini', 236 | 'method' => 'baidu.ting.search.merge', 237 | 'isNew' => 1, 238 | 'platform' => 'darwin', 239 | 'page_no' => isset($option['page']) ? $option['page'] : 1, 240 | 'query' => $keyword, 241 | 'version' => '11.2.1', 242 | 'page_size' => isset($option['limit']) ? $option['limit'] : 30, 243 | ), 244 | 'format' => 'result.song_info.song_list', 245 | ); 246 | break; 247 | case 'kuwo': 248 | $api = array( 249 | 'method' => 'GET', 250 | 'url' => 'http://www.kuwo.cn/api/www/search/searchMusicBykeyWord', 251 | 'body' => array( 252 | 'key' => $keyword, 253 | 'pn' => isset($option['page']) ? $option['page'] : 1, 254 | 'rn' => isset($option['limit']) ? $option['limit'] : 30, 255 | 'httpsStatus' => 1, 256 | ), 257 | 'format' => 'data.list', 258 | ); 259 | break; 260 | } 261 | 262 | return $this->exec($api); 263 | } 264 | 265 | public function song($id) 266 | { 267 | switch ($this->server) { 268 | case 'netease': 269 | $api = array( 270 | 'method' => 'POST', 271 | 'url' => 'http://music.163.com/api/v3/song/detail/', 272 | 'body' => array( 273 | 'c' => '[{"id":'.$id.',"v":0}]', 274 | ), 275 | 'encode' => 'netease_AESCBC', 276 | 'format' => 'songs', 277 | ); 278 | break; 279 | case 'tencent': 280 | $api = array( 281 | 'method' => 'GET', 282 | 'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_play_single_song.fcg', 283 | 'body' => array( 284 | 'songmid' => $id, 285 | 'platform' => 'yqq', 286 | 'format' => 'json', 287 | ), 288 | 'format' => 'data', 289 | ); 290 | break; 291 | case 'xiami': 292 | $api = array( 293 | 'method' => 'GET', 294 | 'url' => 'https://acs.m.xiami.com/h5/mtop.alimusic.music.songservice.getsongdetail/1.0/', 295 | 'body' => array( 296 | 'data' => array( 297 | 'songId' => $id, 298 | ), 299 | 'r' => 'mtop.alimusic.music.songservice.getsongdetail', 300 | ), 301 | 'encode' => 'xiami_sign', 302 | 'format' => 'data.data.songDetail', 303 | ); 304 | break; 305 | case 'kugou': 306 | $api = array( 307 | 'method' => 'POST', 308 | 'url' => 'http://m.kugou.com/app/i/getSongInfo.php', 309 | 'body' => array( 310 | 'cmd' => 'playInfo', 311 | 'hash' => $id, 312 | 'from' => 'mkugou', 313 | ), 314 | 'format' => '', 315 | ); 316 | break; 317 | case 'baidu': 318 | $api = array( 319 | 'method' => 'GET', 320 | 'url' => 'http://musicapi.taihe.com/v1/restserver/ting', 321 | 'body' => array( 322 | 'from' => 'qianqianmini', 323 | 'method' => 'baidu.ting.song.getInfos', 324 | 'songid' => $id, 325 | 'res' => 1, 326 | 'platform' => 'darwin', 327 | 'version' => '1.0.0', 328 | ), 329 | 'encode' => 'baidu_AESCBC', 330 | 'format' => 'songinfo', 331 | ); 332 | break; 333 | case 'kuwo': 334 | $api = array( 335 | 'method' => 'GET', 336 | 'url' => 'http://www.kuwo.cn/api/www/music/musicInfo', 337 | 'body' => array( 338 | 'mid' => $id, 339 | 'httpsStatus' => 1, 340 | ), 341 | 'format' => 'data', 342 | ); 343 | break; 344 | } 345 | 346 | return $this->exec($api); 347 | } 348 | 349 | public function album($id) 350 | { 351 | switch ($this->server) { 352 | case 'netease': 353 | $api = array( 354 | 'method' => 'POST', 355 | 'url' => 'http://music.163.com/api/v1/album/'.$id, 356 | 'body' => array( 357 | 'total' => 'true', 358 | 'offset' => '0', 359 | 'id' => $id, 360 | 'limit' => '1000', 361 | 'ext' => 'true', 362 | 'private_cloud' => 'true', 363 | ), 364 | 'encode' => 'netease_AESCBC', 365 | 'format' => 'songs', 366 | ); 367 | break; 368 | case 'tencent': 369 | $api = array( 370 | 'method' => 'GET', 371 | 'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_album_detail_cp.fcg', 372 | 'body' => array( 373 | 'albummid' => $id, 374 | 'platform' => 'mac', 375 | 'format' => 'json', 376 | 'newsong' => 1, 377 | ), 378 | 'format' => 'data.getSongInfo', 379 | ); 380 | break; 381 | case 'xiami': 382 | $api = array( 383 | 'method' => 'GET', 384 | 'url' => 'https://acs.m.xiami.com/h5/mtop.alimusic.music.albumservice.getalbumdetail/1.0/', 385 | 'body' => array( 386 | 'data' => array( 387 | 'albumId' => $id, 388 | ), 389 | 'r' => 'mtop.alimusic.music.albumservice.getalbumdetail', 390 | ), 391 | 'encode' => 'xiami_sign', 392 | 'format' => 'data.data.albumDetail.songs', 393 | ); 394 | break; 395 | case 'kugou': 396 | $api = array( 397 | 'method' => 'GET', 398 | 'url' => 'http://mobilecdn.kugou.com/api/v3/album/song', 399 | 'body' => array( 400 | 'albumid' => $id, 401 | 'area_code' => 1, 402 | 'plat' => 2, 403 | 'page' => 1, 404 | 'pagesize' => -1, 405 | 'version' => 8990, 406 | ), 407 | 'format' => 'data.info', 408 | ); 409 | break; 410 | case 'baidu': 411 | $api = array( 412 | 'method' => 'GET', 413 | 'url' => 'http://musicapi.taihe.com/v1/restserver/ting', 414 | 'body' => array( 415 | 'from' => 'qianqianmini', 416 | 'method' => 'baidu.ting.album.getAlbumInfo', 417 | 'album_id' => $id, 418 | 'platform' => 'darwin', 419 | 'version' => '11.2.1', 420 | ), 421 | 'format' => 'songlist', 422 | ); 423 | break; 424 | case 'kuwo': 425 | $api = array( 426 | 'method' => 'GET', 427 | 'url' => 'http://www.kuwo.cn/api/www/playlist/playListInfo', 428 | 'body' => array( 429 | 'pid' => $id, 430 | 'httpsStatus' => 1, 431 | ), 432 | 'format' => 'data.musicList', 433 | ); 434 | break; 435 | } 436 | 437 | return $this->exec($api); 438 | } 439 | 440 | public function artist($id, $limit = 50) 441 | { 442 | switch ($this->server) { 443 | case 'netease': 444 | $api = array( 445 | 'method' => 'POST', 446 | 'url' => 'http://music.163.com/api/v1/artist/'.$id, 447 | 'body' => array( 448 | 'ext' => 'true', 449 | 'private_cloud' => 'true', 450 | 'ext' => 'true', 451 | 'top' => $limit, 452 | 'id' => $id, 453 | ), 454 | 'encode' => 'netease_AESCBC', 455 | 'format' => 'hotSongs', 456 | ); 457 | break; 458 | case 'tencent': 459 | $api = array( 460 | 'method' => 'GET', 461 | 'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg', 462 | 'body' => array( 463 | 'singermid' => $id, 464 | 'begin' => 0, 465 | 'num' => $limit, 466 | 'order' => 'listen', 467 | 'platform' => 'mac', 468 | 'newsong' => 1, 469 | ), 470 | 'format' => 'data.list', 471 | ); 472 | break; 473 | case 'xiami': 474 | $api = array( 475 | 'method' => 'GET', 476 | 'url' => 'https://acs.m.xiami.com/h5/mtop.alimusic.music.songservice.getartistsongs/1.0/', 477 | 'body' => array( 478 | 'data' => array( 479 | 'artistId' => $id, 480 | 'pagingVO' => array( 481 | 'page' => 1, 482 | 'pageSize' => $limit, 483 | ), 484 | ), 485 | 'r' => 'mtop.alimusic.music.songservice.getartistsongs', 486 | ), 487 | 'encode' => 'xiami_sign', 488 | 'format' => 'data.data.songs', 489 | ); 490 | break; 491 | case 'kugou': 492 | $api = array( 493 | 'method' => 'GET', 494 | 'url' => 'http://mobilecdn.kugou.com/api/v3/singer/song', 495 | 'body' => array( 496 | 'singerid' => $id, 497 | 'area_code' => 1, 498 | 'page' => 1, 499 | 'plat' => 0, 500 | 'pagesize' => $limit, 501 | 'version' => 8990, 502 | ), 503 | 'format' => 'data.info', 504 | ); 505 | break; 506 | case 'baidu': 507 | $api = array( 508 | 'method' => 'GET', 509 | 'url' => 'http://musicapi.taihe.com/v1/restserver/ting', 510 | 'body' => array( 511 | 'from' => 'qianqianmini', 512 | 'method' => 'baidu.ting.artist.getSongList', 513 | 'artistid' => $id, 514 | 'limits' => $limit, 515 | 'platform' => 'darwin', 516 | 'offset' => 0, 517 | 'tinguid' => 0, 518 | 'version' => '11.2.1', 519 | ), 520 | 'format' => 'songlist', 521 | ); 522 | break; 523 | case 'kuwo': 524 | $api = array( 525 | 'method' => 'GET', 526 | 'url' => 'http://www.kuwo.cn/api/www/artist/artistMusic', 527 | 'body' => array( 528 | 'artistid' => $id, 529 | 'httpsStatus' => 1, 530 | ), 531 | 'format' => 'data.list', 532 | ); 533 | break; 534 | } 535 | 536 | return $this->exec($api); 537 | } 538 | 539 | public function playlist($id) 540 | { 541 | switch ($this->server) { 542 | case 'netease': 543 | $api = array( 544 | 'method' => 'POST', 545 | 'url' => 'http://music.163.com/api/v6/playlist/detail', 546 | 'body' => array( 547 | 's' => '0', 548 | 'id' => $id, 549 | 'n' => '1000', 550 | 't' => '0', 551 | ), 552 | 'encode' => 'netease_AESCBC', 553 | 'format' => 'playlist.tracks', 554 | ); 555 | break; 556 | case 'tencent': 557 | $api = array( 558 | 'method' => 'GET', 559 | 'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_playlist_cp.fcg', 560 | 'body' => array( 561 | 'id' => $id, 562 | 'format' => 'json', 563 | 'newsong' => 1, 564 | 'platform' => 'jqspaframe.json', 565 | ), 566 | 'format' => 'data.cdlist.0.songlist', 567 | ); 568 | break; 569 | case 'xiami': 570 | $api = array( 571 | 'method' => 'GET', 572 | 'url' => 'https://acs.m.xiami.com/h5/mtop.alimusic.music.list.collectservice.getcollectdetail/1.0/', 573 | 'body' => array( 574 | 'data' => array( 575 | 'listId' => $id, 576 | 'isFullTags' => false, 577 | 'pagingVO' => array( 578 | 'page' => 1, 579 | 'pageSize' => 1000, 580 | ), 581 | ), 582 | 'r' => 'mtop.alimusic.music.list.collectservice.getcollectdetail', 583 | ), 584 | 'encode' => 'xiami_sign', 585 | 'format' => 'data.data.collectDetail.songs', 586 | ); 587 | break; 588 | case 'kugou': 589 | $api = array( 590 | 'method' => 'GET', 591 | 'url' => 'http://mobilecdn.kugou.com/api/v3/special/song', 592 | 'body' => array( 593 | 'specialid' => $id, 594 | 'area_code' => 1, 595 | 'page' => 1, 596 | 'plat' => 2, 597 | 'pagesize' => -1, 598 | 'version' => 8990, 599 | ), 600 | 'format' => 'data.info', 601 | ); 602 | break; 603 | case 'baidu': 604 | $api = array( 605 | 'method' => 'GET', 606 | 'url' => 'http://musicapi.taihe.com/v1/restserver/ting', 607 | 'body' => array( 608 | 'from' => 'qianqianmini', 609 | 'method' => 'baidu.ting.diy.gedanInfo', 610 | 'listid' => $id, 611 | 'platform' => 'darwin', 612 | 'version' => '11.2.1', 613 | ), 614 | 'format' => 'content', 615 | ); 616 | break; 617 | case 'kuwo': 618 | $api = array( 619 | 'method' => 'GET', 620 | 'url' => 'http://www.kuwo.cn/api/www/playlist/playListInfo', 621 | 'body' => array( 622 | 'pid' => $id, 623 | 'httpsStatus' => 1, 624 | ), 625 | 'format' => 'data.musicList', 626 | ); 627 | break; 628 | } 629 | 630 | return $this->exec($api); 631 | } 632 | 633 | public function url($id, $br = 320) 634 | { 635 | switch ($this->server) { 636 | case 'netease': 637 | $api = array( 638 | 'method' => 'POST', 639 | 'url' => 'http://music.163.com/api/song/enhance/player/url', 640 | 'body' => array( 641 | 'ids' => array($id), 642 | 'br' => $br * 1000, 643 | ), 644 | 'encode' => 'netease_AESCBC', 645 | 'decode' => 'netease_url', 646 | ); 647 | break; 648 | case 'tencent': 649 | $api = array( 650 | 'method' => 'GET', 651 | 'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_play_single_song.fcg', 652 | 'body' => array( 653 | 'songmid' => $id, 654 | 'platform' => 'yqq', 655 | 'format' => 'json', 656 | ), 657 | 'decode' => 'tencent_url', 658 | ); 659 | break; 660 | case 'xiami': 661 | $api = array( 662 | 'method' => 'GET', 663 | 'url' => 'https://acs.m.xiami.com/h5/mtop.alimusic.music.songservice.getsongs/1.0/', 664 | 'body' => array( 665 | 'data' => array( 666 | 'songIds' => array( 667 | $id, 668 | ), 669 | ), 670 | 'r' => 'mtop.alimusic.music.songservice.getsongs', 671 | ), 672 | 'encode' => 'xiami_sign', 673 | 'decode' => 'xiami_url', 674 | ); 675 | break; 676 | case 'kugou': 677 | $api = array( 678 | 'method' => 'POST', 679 | 'url' => 'http://media.store.kugou.com/v1/get_res_privilege', 680 | 'body' => json_encode( 681 | array( 682 | 'relate' => 1, 683 | 'userid' => '0', 684 | 'vip' => 0, 685 | 'appid' => 1000, 686 | 'token' => '', 687 | 'behavior' => 'download', 688 | 'area_code' => '1', 689 | 'clientver' => '8990', 690 | 'resource' => array(array( 691 | 'id' => 0, 692 | 'type' => 'audio', 693 | 'hash' => $id, 694 | )), ) 695 | ), 696 | 'decode' => 'kugou_url', 697 | ); 698 | break; 699 | case 'baidu': 700 | $api = array( 701 | 'method' => 'GET', 702 | 'url' => 'http://musicapi.taihe.com/v1/restserver/ting', 703 | 'body' => array( 704 | 'from' => 'qianqianmini', 705 | 'method' => 'baidu.ting.song.getInfos', 706 | 'songid' => $id, 707 | 'res' => 1, 708 | 'platform' => 'darwin', 709 | 'version' => '1.0.0', 710 | ), 711 | 'encode' => 'baidu_AESCBC', 712 | 'decode' => 'baidu_url', 713 | ); 714 | break; 715 | case 'kuwo': 716 | $api = array( 717 | 'method' => 'GET', 718 | 'url' => 'http://www.kuwo.cn/url', 719 | 'body' => array( 720 | 'rid' => $id, 721 | 'response' => 'url', 722 | 'type' => 'convert_url3', 723 | 'br' => '128kmp3', 724 | 'from' => 'web', 725 | 't' => time(), 726 | 'httpsStatus' => 1, 727 | ), 728 | 'decode' => 'kuwo_url', 729 | ); 730 | break; 731 | } 732 | $this->temp['br'] = $br; 733 | 734 | return $this->exec($api); 735 | } 736 | 737 | public function lyric($id) 738 | { 739 | switch ($this->server) { 740 | case 'netease': 741 | $api = array( 742 | 'method' => 'POST', 743 | 'url' => 'http://music.163.com/api/song/lyric', 744 | 'body' => array( 745 | 'id' => $id, 746 | 'os' => 'linux', 747 | 'lv' => -1, 748 | 'kv' => -1, 749 | 'tv' => -1, 750 | ), 751 | 'encode' => 'netease_AESCBC', 752 | 'decode' => 'netease_lyric', 753 | ); 754 | break; 755 | case 'tencent': 756 | $api = array( 757 | 'method' => 'GET', 758 | 'url' => 'https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg', 759 | 'body' => array( 760 | 'songmid' => $id, 761 | 'g_tk' => '5381', 762 | ), 763 | 'decode' => 'tencent_lyric', 764 | ); 765 | break; 766 | case 'xiami': 767 | $api = array( 768 | 'method' => 'GET', 769 | 'url' => 'https://acs.m.xiami.com/h5/mtop.alimusic.music.lyricservice.getsonglyrics/1.0/', 770 | 'body' => array( 771 | 'data' => array( 772 | 'songId' => $id, 773 | ), 774 | 'r' => 'mtop.alimusic.music.lyricservice.getsonglyrics', 775 | ), 776 | 'encode' => 'xiami_sign', 777 | 'decode' => 'xiami_lyric', 778 | ); 779 | break; 780 | case 'kugou': 781 | $api = array( 782 | 'method' => 'GET', 783 | 'url' => 'http://krcs.kugou.com/search', 784 | 'body' => array( 785 | 'keyword' => '%20-%20', 786 | 'ver' => 1, 787 | 'hash' => $id, 788 | 'client' => 'mobi', 789 | 'man' => 'yes', 790 | ), 791 | 'decode' => 'kugou_lyric', 792 | ); 793 | break; 794 | case 'baidu': 795 | $api = array( 796 | 'method' => 'GET', 797 | 'url' => 'http://musicapi.taihe.com/v1/restserver/ting', 798 | 'body' => array( 799 | 'from' => 'qianqianmini', 800 | 'method' => 'baidu.ting.song.lry', 801 | 'songid' => $id, 802 | 'platform' => 'darwin', 803 | 'version' => '1.0.0', 804 | ), 805 | 'decode' => 'baidu_lyric', 806 | ); 807 | break; 808 | case 'kuwo': 809 | $api = array( 810 | 'method' => 'GET', 811 | 'url' => 'http://m.kuwo.cn/newh5/singles/songinfoandlrc', 812 | 'body' => array( 813 | 'musicId' => $id, 814 | 'httpsStatus' => 1, 815 | ), 816 | 'decode' => 'kuwo_lyric', 817 | ); 818 | break; 819 | } 820 | 821 | return $this->exec($api); 822 | } 823 | 824 | public function pic($id, $size = 300) 825 | { 826 | switch ($this->server) { 827 | case 'netease': 828 | $url = 'https://p3.music.126.net/'.$this->netease_encryptId($id).'/'.$id.'.jpg?param='.$size.'y'.$size; 829 | break; 830 | case 'tencent': 831 | $url = 'https://y.gtimg.cn/music/photo_new/T002R'.$size.'x'.$size.'M000'.$id.'.jpg?max_age=2592000'; 832 | break; 833 | case 'xiami': 834 | $format = $this->format; 835 | $data = $this->format(false)->song($id); 836 | $this->format = $format; 837 | $data = json_decode($data, true); 838 | $url = $data['data']['data']['songDetail']['albumLogo']; 839 | $url = str_replace('http:', 'https:', $url).'@1e_1c_100Q_'.$size.'h_'.$size.'w'; 840 | break; 841 | case 'kugou': 842 | $format = $this->format; 843 | $data = $this->format(false)->song($id); 844 | $this->format = $format; 845 | $data = json_decode($data, true); 846 | $url = $data['imgUrl']; 847 | $url = str_replace('{size}', '400', $url); 848 | break; 849 | case 'baidu': 850 | $format = $this->format; 851 | $data = $this->format(false)->song($id); 852 | $this->format = $format; 853 | $data = json_decode($data, true); 854 | $url = isset($data['songinfo']['pic_radio']) ? $data['songinfo']['pic_radio'] : $data['songinfo']['pic_small']; 855 | break; 856 | case 'kuwo': 857 | $format = $this->format; 858 | $data = $this->format(false)->song($id); 859 | $this->format = $format; 860 | $data = json_decode($data, true); 861 | $url = isset($data['data']['pic']) ? $data['data']['pic'] : $data['data']['albumpic']; 862 | break; 863 | } 864 | 865 | return json_encode(array('url' => $url)); 866 | } 867 | 868 | private function curlset() 869 | { 870 | switch ($this->server) { 871 | case 'netease': 872 | return array( 873 | 'Referer' => 'https://music.163.com/', 874 | 'Cookie' => 'appver=8.2.30; os=iPhone OS; osver=15.0; EVNSM=1.0.0; buildver=2206; channel=distribution; machineid=iPhone13.3', 875 | 'User-Agent' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 CloudMusic/0.1.1 NeteaseMusic/8.2.30', 876 | 'X-Real-IP' => long2ip(mt_rand(1884815360, 1884890111)), 877 | 'Accept' => '*/*', 878 | 'Accept-Language' => 'zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4', 879 | 'Connection' => 'keep-alive', 880 | 'Content-Type' => 'application/x-www-form-urlencoded', 881 | ); 882 | case 'tencent': 883 | return array( 884 | 'Referer' => 'http://y.qq.com', 885 | 'Cookie' => 'pgv_pvi=22038528; pgv_si=s3156287488; pgv_pvid=5535248600; yplayer_open=1; ts_last=y.qq.com/portal/player.html; ts_uid=4847550686; yq_index=0; qqmusic_fromtag=66; player_exist=1', 886 | 'User-Agent' => 'QQ%E9%9F%B3%E4%B9%90/54409 CFNetwork/901.1 Darwin/17.6.0 (x86_64)', 887 | 'Accept' => '*/*', 888 | 'Accept-Language' => 'zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4', 889 | 'Connection' => 'keep-alive', 890 | 'Content-Type' => 'application/x-www-form-urlencoded', 891 | ); 892 | case 'xiami': 893 | return array( 894 | 'Cookie' => '_m_h5_tk=15d3402511a022796d88b249f83fb968_1511163656929; _m_h5_tk_enc=b6b3e64d81dae577fc314b5c5692df3c', 895 | 'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) XIAMI-MUSIC/3.1.1 Chrome/56.0.2924.87 Electron/1.6.11 Safari/537.36', 896 | 'Accept' => 'application/json', 897 | 'Content-type' => 'application/x-www-form-urlencoded', 898 | 'Accept-Language' => 'zh-CN', 899 | ); 900 | case 'kugou': 901 | return array( 902 | 'User-Agent' => 'IPhone-8990-searchSong', 903 | 'UNI-UserAgent' => 'iOS11.4-Phone8990-1009-0-WiFi', 904 | ); 905 | case 'baidu': 906 | return array( 907 | 'Cookie' => 'BAIDUID='.$this->getRandomHex(32).':FG=1', 908 | 'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) baidu-music/1.2.1 Chrome/66.0.3359.181 Electron/3.0.5 Safari/537.36', 909 | 'Accept' => '*/*', 910 | 'Content-type' => 'application/json;charset=UTF-8', 911 | 'Accept-Language' => 'zh-CN', 912 | ); 913 | case 'kuwo': 914 | return array( 915 | 'Cookie' => 'Hm_lvt_cdb524f42f0ce19b169a8071123a4797=1623339177,1623339183; _ga=GA1.2.1195980605.1579367081; Hm_lpvt_cdb524f42f0ce19b169a8071123a4797=1623339982; kw_token=3E7JFQ7MRPL; _gid=GA1.2.747985028.1623339179; _gat=1', 916 | 'csrf' => '3E7JFQ7MRPL', 917 | 'Host' => 'www.kuwo.cn', 918 | 'Referer' => 'http://www.kuwo.cn/', 919 | 'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36', 920 | ); 921 | } 922 | } 923 | 924 | private function getRandomHex($length) 925 | { 926 | if (function_exists('random_bytes')) { 927 | return bin2hex(random_bytes($length / 2)); 928 | } 929 | if (function_exists('mcrypt_create_iv')) { 930 | return bin2hex(mcrypt_create_iv($length / 2, MCRYPT_DEV_URANDOM)); 931 | } 932 | if (function_exists('openssl_random_pseudo_bytes')) { 933 | return bin2hex(openssl_random_pseudo_bytes($length / 2)); 934 | } 935 | } 936 | 937 | private function bchexdec($hex) 938 | { 939 | $dec = 0; 940 | $len = strlen($hex); 941 | for ($i = 1; $i <= $len; $i++) { 942 | $dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i)))); 943 | } 944 | 945 | return $dec; 946 | } 947 | 948 | private function bcdechex($dec) 949 | { 950 | $hex = ''; 951 | do { 952 | $last = bcmod($dec, 16); 953 | $hex = dechex($last).$hex; 954 | $dec = bcdiv(bcsub($dec, $last), 16); 955 | } while ($dec > 0); 956 | 957 | return $hex; 958 | } 959 | 960 | private function str2hex($string) 961 | { 962 | $hex = ''; 963 | for ($i = 0; $i < strlen($string); $i++) { 964 | $ord = ord($string[$i]); 965 | $hexCode = dechex($ord); 966 | $hex .= substr('0'.$hexCode, -2); 967 | } 968 | 969 | return $hex; 970 | } 971 | 972 | private function netease_AESCBC($api) 973 | { 974 | $modulus = '157794750267131502212476817800345498121872783333389747424011531025366277535262539913701806290766479189477533597854989606803194253978660329941980786072432806427833685472618792592200595694346872951301770580765135349259590167490536138082469680638514416594216629258349130257685001248172188325316586707301643237607'; 975 | $pubkey = '65537'; 976 | $nonce = '0CoJUm6Qyw8W8jud'; 977 | $vi = '0102030405060708'; 978 | 979 | if (extension_loaded('bcmath')) { 980 | $skey = $this->getRandomHex(16); 981 | } else { 982 | $skey = 'B3v3kH4vRPWRJFfH'; 983 | } 984 | 985 | $body = json_encode($api['body']); 986 | 987 | if (function_exists('openssl_encrypt')) { 988 | $body = openssl_encrypt($body, 'aes-128-cbc', $nonce, false, $vi); 989 | $body = openssl_encrypt($body, 'aes-128-cbc', $skey, false, $vi); 990 | } else { 991 | $pad = 16 - (strlen($body) % 16); 992 | $body = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $nonce, $body.str_repeat(chr($pad), $pad), MCRYPT_MODE_CBC, $vi)); 993 | $pad = 16 - (strlen($body) % 16); 994 | $body = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $skey, $body.str_repeat(chr($pad), $pad), MCRYPT_MODE_CBC, $vi)); 995 | } 996 | 997 | if (extension_loaded('bcmath')) { 998 | $skey = strrev(utf8_encode($skey)); 999 | $skey = $this->bchexdec($this->str2hex($skey)); 1000 | $skey = bcpowmod($skey, $pubkey, $modulus); 1001 | $skey = $this->bcdechex($skey); 1002 | $skey = str_pad($skey, 256, '0', STR_PAD_LEFT); 1003 | } else { 1004 | $skey = '85302b818aea19b68db899c25dac229412d9bba9b3fcfe4f714dc016bc1686fc446a08844b1f8327fd9cb623cc189be00c5a365ac835e93d4858ee66f43fdc59e32aaed3ef24f0675d70172ef688d376a4807228c55583fe5bac647d10ecef15220feef61477c28cae8406f6f9896ed329d6db9f88757e31848a6c2ce2f94308'; 1005 | } 1006 | 1007 | $api['url'] = str_replace('/api/', '/weapi/', $api['url']); 1008 | $api['body'] = array( 1009 | 'params' => $body, 1010 | 'encSecKey' => $skey, 1011 | ); 1012 | 1013 | return $api; 1014 | } 1015 | 1016 | private function baidu_AESCBC($api) 1017 | { 1018 | $key = 'DBEECF8C50FD160E'; 1019 | $vi = '1231021386755796'; 1020 | 1021 | $data = 'songid='.$api['body']['songid'].'&ts='.intval(microtime(true) * 1000); 1022 | 1023 | if (function_exists('openssl_encrypt')) { 1024 | $data = openssl_encrypt($data, 'aes-128-cbc', $key, false, $vi); 1025 | } else { 1026 | $pad = 16 - (strlen($data) % 16); 1027 | $data = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data.str_repeat(chr($pad), $pad), MCRYPT_MODE_CBC, $vi)); 1028 | } 1029 | 1030 | $api['body']['e'] = $data; 1031 | 1032 | return $api; 1033 | } 1034 | 1035 | private function xiami_sign($api) 1036 | { 1037 | $data = $this->curl('https://acs.m.xiami.com/h5/mtop.alimusic.recommend.songservice.getdailysongs/1.0/?appKey=12574478&t=1560663823000&dataType=json&data=%7B%22requestStr%22%3A%22%7B%5C%22header%5C%22%3A%7B%5C%22platformId%5C%22%3A%5C%22mac%5C%22%7D%2C%5C%22model%5C%22%3A%5B%5D%7D%22%7D&api=mtop.alimusic.recommend.songservice.getdailysongs&v=1.0&type=originaljson&sign=22ad1377ee193f3e2772c17c6192b17c', null, 1); 1038 | preg_match_all('/_m_h5[^;]+/', $data->raw, $match); 1039 | $this->header['Cookie'] = $match[0][0].'; '.$match[0][1]; 1040 | $data = json_encode(array( 1041 | 'requestStr' => json_encode(array( 1042 | 'header' => array( 1043 | 'platformId' => 'mac', 1044 | ), 1045 | 'model' => $api['body']['data'], 1046 | )), 1047 | )); 1048 | $appkey = '12574478'; 1049 | $cookie = $this->header['Cookie']; 1050 | preg_match('/_m_h5_tk=([^_]+)/', $cookie, $match); 1051 | $token = $match[1]; 1052 | $t = time() * 1000; 1053 | $sign = md5(sprintf('%s&%s&%s&%s', $token, $t, $appkey, $data)); 1054 | $api['body'] = array( 1055 | 'appKey' => $appkey, 1056 | 't' => $t, 1057 | 'dataType' => 'json', 1058 | 'data' => $data, 1059 | 'api' => $api['body']['r'], 1060 | 'v' => '1.0', 1061 | 'type' => 'originaljson', 1062 | 'sign' => $sign, 1063 | ); 1064 | 1065 | return $api; 1066 | } 1067 | 1068 | private function netease_encryptId($id) 1069 | { 1070 | $magic = str_split('3go8&$8*3*3h0k(2)2'); 1071 | $song_id = str_split($id); 1072 | for ($i = 0; $i < count($song_id); $i++) { 1073 | $song_id[$i] = chr(ord($song_id[$i]) ^ ord($magic[$i % count($magic)])); 1074 | } 1075 | $result = base64_encode(md5(implode('', $song_id), 1)); 1076 | $result = str_replace(array('/', '+'), array('_', '-'), $result); 1077 | 1078 | return $result; 1079 | } 1080 | 1081 | private function netease_url($result) 1082 | { 1083 | $data = json_decode($result, true); 1084 | if (isset($data['data'][0]['uf']['url'])) { 1085 | $data['data'][0]['url'] = $data['data'][0]['uf']['url']; 1086 | } 1087 | if (isset($data['data'][0]['url'])) { 1088 | $url = array( 1089 | 'url' => $data['data'][0]['url'], 1090 | 'size' => $data['data'][0]['size'], 1091 | 'br' => $data['data'][0]['br'] / 1000, 1092 | ); 1093 | } else { 1094 | $url = array( 1095 | 'url' => '', 1096 | 'size' => 0, 1097 | 'br' => -1, 1098 | ); 1099 | } 1100 | 1101 | return json_encode($url); 1102 | } 1103 | 1104 | private function tencent_url($result) 1105 | { 1106 | $data = json_decode($result, true); 1107 | $guid = mt_rand() % 10000000000; 1108 | 1109 | $type = array( 1110 | array('size_flac', 999, 'F000', 'flac'), 1111 | array('size_320mp3', 320, 'M800', 'mp3'), 1112 | array('size_192aac', 192, 'C600', 'm4a'), 1113 | array('size_128mp3', 128, 'M500', 'mp3'), 1114 | array('size_96aac', 96, 'C400', 'm4a'), 1115 | array('size_48aac', 48, 'C200', 'm4a'), 1116 | array('size_24aac', 24, 'C100', 'm4a'), 1117 | ); 1118 | 1119 | $uin = '0'; 1120 | preg_match('/uin=(\d+)/', $this->header['Cookie'], $uin_match); 1121 | if (count($uin_match)) { 1122 | $uin = $uin_match[1]; 1123 | } 1124 | 1125 | $payload = array( 1126 | 'req_0' => array( 1127 | 'module' => 'vkey.GetVkeyServer', 1128 | 'method' => 'CgiGetVkey', 1129 | 'param' => array( 1130 | 'guid' => (string) $guid, 1131 | 'songmid' => array(), 1132 | 'filename' => array(), 1133 | 'songtype' => array(), 1134 | 'uin' => $uin, 1135 | 'loginflag' => 1, 1136 | 'platform' => '20', 1137 | ), 1138 | ), 1139 | ); 1140 | 1141 | foreach ($type as $vo) { 1142 | $payload['req_0']['param']['songmid'][] = $data['data'][0]['mid']; 1143 | $payload['req_0']['param']['filename'][] = $vo[2].$data['data'][0]['file']['media_mid'].'.'.$vo[3]; 1144 | $payload['req_0']['param']['songtype'][] = $data['data'][0]['type']; 1145 | } 1146 | 1147 | $api = array( 1148 | 'method' => 'GET', 1149 | 'url' => 'https://u.y.qq.com/cgi-bin/musicu.fcg', 1150 | 'body' => array( 1151 | 'format' => 'json', 1152 | 'platform' => 'yqq.json', 1153 | 'needNewCode' => 0, 1154 | 'data' => json_encode($payload), 1155 | ), 1156 | ); 1157 | $response = json_decode($this->exec($api), true); 1158 | $vkeys = $response['req_0']['data']['midurlinfo']; 1159 | 1160 | foreach ($type as $index => $vo) { 1161 | if ($data['data'][0]['file'][$vo[0]] && $vo[1] <= $this->temp['br']) { 1162 | if (!empty($vkeys[$index]['vkey'])) { 1163 | $url = array( 1164 | 'url' => $response['req_0']['data']['sip'][0].$vkeys[$index]['purl'], 1165 | 'size' => $data['data'][0]['file'][$vo[0]], 1166 | 'br' => $vo[1], 1167 | ); 1168 | break; 1169 | } 1170 | } 1171 | } 1172 | if (!isset($url['url'])) { 1173 | $url = array( 1174 | 'url' => '', 1175 | 'size' => 0, 1176 | 'br' => -1, 1177 | ); 1178 | } 1179 | 1180 | return json_encode($url); 1181 | } 1182 | 1183 | private function xiami_url($result) 1184 | { 1185 | $data = json_decode($result, true); 1186 | 1187 | $type = array( 1188 | 's' => 740, 1189 | 'h' => 320, 1190 | 'l' => 128, 1191 | 'f' => 64, 1192 | 'e' => 32, 1193 | ); 1194 | $max = 0; 1195 | $url = array(); 1196 | foreach ($data['data']['data']['songs'][0]['listenFiles'] as $vo) { 1197 | if ($type[$vo['quality']] <= $this->temp['br'] && $type[$vo['quality']] > $max) { 1198 | $max = $type[$vo['quality']]; 1199 | $url = array( 1200 | 'url' => $vo['listenFile'], 1201 | 'size' => $vo['fileSize'], 1202 | 'br' => $type[$vo['quality']], 1203 | ); 1204 | } 1205 | } 1206 | if (!isset($url['url'])) { 1207 | $url = array( 1208 | 'url' => '', 1209 | 'size' => 0, 1210 | 'br' => -1, 1211 | ); 1212 | } 1213 | 1214 | return json_encode($url); 1215 | } 1216 | 1217 | private function kugou_url($result) 1218 | { 1219 | $data = json_decode($result, true); 1220 | 1221 | $max = 0; 1222 | $url = array(); 1223 | foreach ($data['data'][0]['relate_goods'] as $vo) { 1224 | if ($vo['info']['bitrate'] <= $this->temp['br'] && $vo['info']['bitrate'] > $max) { 1225 | $api = array( 1226 | 'method' => 'GET', 1227 | 'url' => 'http://trackercdn.kugou.com/i/v2/', 1228 | 'body' => array( 1229 | 'hash' => $vo['hash'], 1230 | 'key' => md5($vo['hash'].'kgcloudv2'), 1231 | 'pid' => 3, 1232 | 'behavior' => 'play', 1233 | 'cmd' => '25', 1234 | 'version' => 8990, 1235 | ), 1236 | ); 1237 | $t = json_decode($this->exec($api), true); 1238 | if (isset($t['url'])) { 1239 | $max = $t['bitRate'] / 1000; 1240 | $url = array( 1241 | 'url' => reset($t['url']), 1242 | 'size' => $t['fileSize'], 1243 | 'br' => $t['bitRate'] / 1000, 1244 | ); 1245 | } 1246 | } 1247 | } 1248 | if (!isset($url['url'])) { 1249 | $url = array( 1250 | 'url' => '', 1251 | 'size' => 0, 1252 | 'br' => -1, 1253 | ); 1254 | } 1255 | 1256 | return json_encode($url); 1257 | } 1258 | 1259 | private function baidu_url($result) 1260 | { 1261 | $data = json_decode($result, true); 1262 | 1263 | $max = 0; 1264 | $url = array(); 1265 | foreach ($data['songurl']['url'] as $vo) { 1266 | if ($vo['file_bitrate'] <= $this->temp['br'] && $vo['file_bitrate'] > $max) { 1267 | $url = array( 1268 | 'url' => $vo['file_link'], 1269 | 'br' => $vo['file_bitrate'], 1270 | ); 1271 | } 1272 | } 1273 | if (!isset($url['url'])) { 1274 | $url = array( 1275 | 'url' => '', 1276 | 'br' => -1, 1277 | ); 1278 | } 1279 | 1280 | return json_encode($url); 1281 | } 1282 | 1283 | private function kuwo_url($result) 1284 | { 1285 | $data = json_decode($result, true); 1286 | 1287 | $url = array(); 1288 | if ($data['code'] == 200 && isset($data['url'])) { 1289 | $url = array( 1290 | 'url' => $data['url'], 1291 | 'br' => 128, 1292 | ); 1293 | } else { 1294 | $url = array( 1295 | 'url' => '', 1296 | 'br' => -1, 1297 | ); 1298 | } 1299 | 1300 | return json_encode($url); 1301 | } 1302 | 1303 | private function netease_lyric($result) 1304 | { 1305 | $result = json_decode($result, true); 1306 | $data = array( 1307 | 'lyric' => isset($result['lrc']['lyric']) ? $result['lrc']['lyric'] : '', 1308 | 'tlyric' => isset($result['tlyric']['lyric']) ? $result['tlyric']['lyric'] : '', 1309 | ); 1310 | 1311 | return json_encode($data, JSON_UNESCAPED_UNICODE); 1312 | } 1313 | 1314 | private function tencent_lyric($result) 1315 | { 1316 | $result = substr($result, 18, -1); 1317 | $result = json_decode($result, true); 1318 | $data = array( 1319 | 'lyric' => isset($result['lyric']) ? base64_decode($result['lyric']) : '', 1320 | 'tlyric' => isset($result['trans']) ? base64_decode($result['trans']) : '', 1321 | ); 1322 | 1323 | return json_encode($data, JSON_UNESCAPED_UNICODE); 1324 | } 1325 | 1326 | private function xiami_lyric($result) 1327 | { 1328 | $result = json_decode($result, true); 1329 | 1330 | if (count($result['data']['data']['lyrics'])) { 1331 | $data = $result['data']['data']['lyrics'][0]['content']; 1332 | $data = preg_replace('/<[^>]+>/', '', $data); 1333 | preg_match_all('/\[([\d:\.]+)\](.*)\s\[x-trans\](.*)/i', $data, $match); 1334 | if (count($match[0])) { 1335 | for ($i = 0; $i < count($match[0]); $i++) { 1336 | $A[] = '['.$match[1][$i].']'.$match[2][$i]; 1337 | $B[] = '['.$match[1][$i].']'.$match[3][$i]; 1338 | } 1339 | $arr = array( 1340 | 'lyric' => str_replace($match[0], $A, $data), 1341 | 'tlyric' => str_replace($match[0], $B, $data), 1342 | ); 1343 | } else { 1344 | $arr = array( 1345 | 'lyric' => $data, 1346 | 'tlyric' => '', 1347 | ); 1348 | } 1349 | } else { 1350 | $arr = array( 1351 | 'lyric' => '', 1352 | 'tlyric' => '', 1353 | ); 1354 | } 1355 | 1356 | return json_encode($arr, JSON_UNESCAPED_UNICODE); 1357 | } 1358 | 1359 | private function kugou_lyric($result) 1360 | { 1361 | $result = json_decode($result, true); 1362 | $api = array( 1363 | 'method' => 'GET', 1364 | 'url' => 'http://lyrics.kugou.com/download', 1365 | 'body' => array( 1366 | 'charset' => 'utf8', 1367 | 'accesskey' => $result['candidates'][0]['accesskey'], 1368 | 'id' => $result['candidates'][0]['id'], 1369 | 'client' => 'mobi', 1370 | 'fmt' => 'lrc', 1371 | 'ver' => 1, 1372 | ), 1373 | ); 1374 | $data = json_decode($this->exec($api), true); 1375 | $arr = array( 1376 | 'lyric' => base64_decode($data['content']), 1377 | 'tlyric' => '', 1378 | ); 1379 | 1380 | return json_encode($arr, JSON_UNESCAPED_UNICODE); 1381 | } 1382 | 1383 | private function baidu_lyric($result) 1384 | { 1385 | $result = json_decode($result, true); 1386 | $data = array( 1387 | 'lyric' => isset($result['lrcContent']) ? $result['lrcContent'] : '', 1388 | 'tlyric' => '', 1389 | ); 1390 | 1391 | return json_encode($data, JSON_UNESCAPED_UNICODE); 1392 | } 1393 | 1394 | private function kuwo_lyric($result) 1395 | { 1396 | $result = json_decode($result, true); 1397 | if (count($result['data']['lrclist'])) { 1398 | $kuwolrc = ''; 1399 | for ($i = 0; $i < count($result['data']['lrclist']); $i++) { 1400 | $otime = $result['data']['lrclist'][$i]['time']; 1401 | $osec = explode('.', $otime)[0]; 1402 | $min = str_pad(floor($osec / 60), 2, "0", STR_PAD_LEFT); 1403 | $sec = str_pad($osec - $min * 60, 2, "0", STR_PAD_LEFT); 1404 | $msec = explode('.', $otime)[1]; 1405 | $olyric = $result['data']['lrclist'][$i]['lineLyric']; 1406 | $kuwolrc = $kuwolrc . '[' . $min . ':' . $sec . '.' . $msec . ']' . $olyric . "\n"; 1407 | } 1408 | $arr = array( 1409 | 'lyric' => $kuwolrc, 1410 | 'tlyric' => '', 1411 | ); 1412 | } else { 1413 | $arr = array( 1414 | 'lyric' => '', 1415 | 'tlyric' => '', 1416 | ); 1417 | } 1418 | return json_encode($arr, JSON_UNESCAPED_UNICODE); 1419 | } 1420 | 1421 | protected function format_netease($data) 1422 | { 1423 | $result = array( 1424 | 'id' => $data['id'], 1425 | 'name' => $data['name'], 1426 | 'artist' => array(), 1427 | 'album' => $data['al']['name'], 1428 | 'pic_id' => isset($data['al']['pic_str']) ? $data['al']['pic_str'] : $data['al']['pic'], 1429 | 'url_id' => $data['id'], 1430 | 'lyric_id' => $data['id'], 1431 | 'source' => 'netease', 1432 | ); 1433 | if (isset($data['al']['picUrl'])) { 1434 | preg_match('/\/(\d+)\./', $data['al']['picUrl'], $match); 1435 | $result['pic_id'] = $match[1]; 1436 | } 1437 | foreach ($data['ar'] as $vo) { 1438 | $result['artist'][] = $vo['name']; 1439 | } 1440 | 1441 | return $result; 1442 | } 1443 | 1444 | protected function format_tencent($data) 1445 | { 1446 | if (isset($data['musicData'])) { 1447 | $data = $data['musicData']; 1448 | } 1449 | $result = array( 1450 | 'id' => $data['mid'], 1451 | 'name' => $data['name'], 1452 | 'artist' => array(), 1453 | 'album' => trim($data['album']['title']), 1454 | 'pic_id' => $data['album']['mid'], 1455 | 'url_id' => $data['mid'], 1456 | 'lyric_id' => $data['mid'], 1457 | 'source' => 'tencent', 1458 | ); 1459 | foreach ($data['singer'] as $vo) { 1460 | $result['artist'][] = $vo['name']; 1461 | } 1462 | 1463 | return $result; 1464 | } 1465 | 1466 | protected function format_xiami($data) 1467 | { 1468 | $result = array( 1469 | 'id' => $data['songId'], 1470 | 'name' => $data['songName'], 1471 | 'artist' => array(), 1472 | 'album' => $data['albumName'], 1473 | 'pic_id' => $data['songId'], 1474 | 'url_id' => $data['songId'], 1475 | 'lyric_id' => $data['songId'], 1476 | 'source' => 'xiami', 1477 | ); 1478 | foreach ($data['singerVOs'] as $vo) { 1479 | $result['artist'][] = $vo['artistName']; 1480 | } 1481 | 1482 | return $result; 1483 | } 1484 | 1485 | protected function format_kugou($data) 1486 | { 1487 | $result = array( 1488 | 'id' => $data['hash'], 1489 | 'name' => isset($data['filename']) ? $data['filename'] : $data['fileName'], 1490 | 'artist' => array(), 1491 | 'album' => isset($data['album_name']) ? $data['album_name'] : '', 1492 | 'url_id' => $data['hash'], 1493 | 'pic_id' => $data['hash'], 1494 | 'lyric_id' => $data['hash'], 1495 | 'source' => 'kugou', 1496 | ); 1497 | list($result['artist'], $result['name']) = explode(' - ', $result['name'], 2); 1498 | $result['artist'] = explode('、', $result['artist']); 1499 | 1500 | return $result; 1501 | } 1502 | 1503 | protected function format_baidu($data) 1504 | { 1505 | $result = array( 1506 | 'id' => $data['song_id'], 1507 | 'name' => $data['title'], 1508 | 'artist' => explode(',', $data['author']), 1509 | 'album' => $data['album_title'], 1510 | 'pic_id' => $data['song_id'], 1511 | 'url_id' => $data['song_id'], 1512 | 'lyric_id' => $data['song_id'], 1513 | 'source' => 'baidu', 1514 | ); 1515 | 1516 | return $result; 1517 | } 1518 | 1519 | protected function format_kuwo($data) 1520 | { 1521 | $result = array( 1522 | 'id' => $data['rid'], 1523 | 'name' => $data['name'], 1524 | 'artist' => explode(',', $data['artist']), 1525 | 'album' => $data['album'], 1526 | 'pic_id' => $data['rid'], 1527 | 'url_id' => $data['rid'], 1528 | 'lyric_id' => $data['rid'], 1529 | 'source' => 'kuwo', 1530 | ); 1531 | 1532 | return $result; 1533 | } 1534 | 1535 | } 1536 | --------------------------------------------------------------------------------