├── .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 |
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 |
--------------------------------------------------------------------------------