diff --git a/README.MD b/README.MD index a4bf540..537a856 100644 --- a/README.MD +++ b/README.MD @@ -77,6 +77,14 @@ php 5.4+, curl_exec, file_get_contents, json_decode, openssl_encrypt ### 更新日志 ----- +#### v2.4 `2018/3/11` +- 修复网易云音乐无法播放 +- 增加标题栏滚动效果(感谢@lzcykevin) +- 增加歌曲循环播放控制(感谢@yuxizhe) +- 修复百度音乐无法播放 +- 优化连续播放失败的歌曲过多时,自动终止播放。防止卡死 +- 压缩图片素材,限制封面图片尺寸,优化页面加载速度 + #### v2.32 `2017/9/15` - 修复播放历史记录歌曲时播放失败的 BUG - 新增播放歌曲时浏览器标题栏显示相关信息 diff --git a/api.php b/api.php index e48a518..1451be2 100644 --- a/api.php +++ b/api.php @@ -1,9 +1,9 @@ url($id); + if($source == 'netease' && json_decode($data, true)['url'] == '') { // 修复网易云链接获取失效(双保险) + echojson('{"url":"https://music.163.com/song/media/outer/url?id='.$id.'.mp3","br":320}'); + return; + } + echojson($data); break; diff --git a/images/album_cover_player.png b/images/album_cover_player.png index 19a008b..882f9b5 100644 Binary files a/images/album_cover_player.png and b/images/album_cover_player.png differ diff --git a/images/history.png b/images/history.png index ecaa7e8..18b0867 100644 Binary files a/images/history.png and b/images/history.png differ diff --git a/images/icon_list_menu.png b/images/icon_list_menu.png index 951efed..0fe907a 100644 Binary files a/images/icon_list_menu.png and b/images/icon_list_menu.png differ diff --git a/images/player.png b/images/player.png index a869b7b..61a0fef 100644 Binary files a/images/player.png and b/images/player.png differ diff --git a/images/player_cover.png b/images/player_cover.png index 5bf2690..30ef9fd 100644 Binary files a/images/player_cover.png and b/images/player_cover.png differ diff --git a/index.html b/index.html index 5a3c5bc..0b61085 100644 --- a/index.html +++ b/index.html @@ -18,7 +18,7 @@ - MKOnlinePlayer v2.3 + MKOnlinePlayer v2.4 @@ -51,7 +51,7 @@
- @@ -148,7 +148,7 @@ - + diff --git a/js/ajax.js b/js/ajax.js index 248987e..03fc0e2 100644 --- a/js/ajax.js +++ b/js/ajax.js @@ -1,8 +1,8 @@ /************************************************** - * MKOnlinePlayer v2.31 + * MKOnlinePlayer v2.4 * Ajax 后台数据交互请求模块 - * 编写:mengkun(http://mkblog.cn) - * 时间:2017-9-13 + * 编写:mengkun(https://mkblog.cn) + * 时间:2018-3-11 *************************************************/ // ajax加载搜索结果 @@ -188,7 +188,7 @@ function ajaxPic(music, callback) // ajax加载用户歌单 // 参数:歌单网易云 id, 歌单存储 id,回调函数 -function ajaxPlayList(lid, id, callback){ +function ajaxPlayList(lid, id, callback) { if(!lid) return false; // 已经在加载了,跳过 @@ -218,7 +218,7 @@ function ajaxPlayList(lid, id, callback){ }; if(jsonData.playlist.coverImgUrl !== '') { - tempList.cover = jsonData.playlist.coverImgUrl; + tempList.cover = jsonData.playlist.coverImgUrl + "?param=200y200"; } else { tempList.cover = musicList[id].cover; } @@ -235,7 +235,7 @@ function ajaxPlayList(lid, id, callback){ url_id: jsonData.playlist.tracks[i].id, // 链接ID pic_id: null, // 封面ID lyric_id: jsonData.playlist.tracks[i].id, // 歌词ID - pic: jsonData.playlist.tracks[i].al.picUrl, // 专辑图片 + pic: jsonData.playlist.tracks[i].al.picUrl + "?param=300y300", // 专辑图片 url: null // mp3链接 }; } @@ -354,7 +354,7 @@ function ajaxUserList(uid) tempList = { id: jsonData.playlist[i].id, // 列表的网易云 id name: jsonData.playlist[i].name, // 列表名字 - cover: jsonData.playlist[i].coverImgUrl, // 列表封面 + cover: jsonData.playlist[i].coverImgUrl + "?param=200y200", // 列表封面 creatorID: uid, // 列表创建者id creatorName: jsonData.playlist[i].creator.nickname, // 列表创建者名字 creatorAvatar: jsonData.playlist[i].creator.avatarUrl, // 列表创建者头像 diff --git a/js/functions.js b/js/functions.js index 34d667d..705099a 100644 --- a/js/functions.js +++ b/js/functions.js @@ -1,8 +1,8 @@ /************************************************** - * MKOnlinePlayer v2.32 + * MKOnlinePlayer v2.4 * 封装函数及UI交互模块 - * 编写:mengkun(http://mkblog.cn) - * 时间:2017-9-15 + * 编写:mengkun(https://mkblog.cn) + * 时间:2018-3-11 *************************************************/ // 判断是否是移动设备 var isMobile = { @@ -28,8 +28,9 @@ $(function(){ console.warn('播放器调试模式已开启,正常使用时请在 js/player.js 中按说明关闭调试模式'); } - rem.isMobile = isMobile.any(); + rem.isMobile = isMobile.any(); // 判断是否是移动设备 rem.webTitle = document.title; // 记录页面原本的标题 + rem.errCount = 0; // 连续播放失败的歌曲数归零 initProgress(); // 初始化音量条、进度条(进度条初始化要在 Audio 前,别问我为什么……) initAudio(); // 初始化 audio 标签,事件绑定 @@ -397,6 +398,7 @@ function openDownloadDialog(url, saveName) } var aLink = document.createElement('a'); aLink.href = url; + aLink.target = "_blank"; aLink.download = saveName || ''; // HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效 var event; if(window.MouseEvent) event = new MouseEvent('click'); diff --git a/js/player.js b/js/player.js index 7efe1c7..2ebe34b 100644 --- a/js/player.js +++ b/js/player.js @@ -1,8 +1,8 @@ /************************************************** - * MKOnlinePlayer v2.32 + * MKOnlinePlayer v2.4 * 播放器主功能模块 * 编写:mengkun(https://mkblog.cn) - * 时间:2018-2-11 + * 时间:2018-3-11 *************************************************/ // 播放器功能配置 var mkPlayer = { @@ -16,7 +16,7 @@ var mkPlayer = { dotshine: true, // 是否开启播放进度条的小点闪动效果[不支持IE](true/false) *开启后会有些卡 mdotshine: false, // 是否开启[移动端]播放进度条的小点闪动效果[不支持IE](true/false) volume: 0.6, // 默认音量值(0~1之间) - version: "v2.32", // 播放器当前版本号(仅供调试) + version: "v2.4", // 播放器当前版本号(仅供调试) debug: false // 是否开启调试模式(true/false) }; @@ -36,8 +36,14 @@ function audioErr() { // 没播放过,直接跳过 if(rem.playlist === undefined) return true; - layer.msg('当前歌曲播放失败,自动播放下一首'); - nextMusic(); // 切换下一首歌 + if(rem.errCount > 10) { // 连续播放失败的歌曲过多 + layer.msg('似乎出了点问题~播放已停止'); + rem.errCount = 0; + } else { + rem.errCount++; // 记录连续播放失败的歌曲数目 + layer.msg('当前歌曲播放失败,自动播放下一首'); + nextMusic(); // 切换下一首歌 + } } // 点击暂停按钮的事件 @@ -62,29 +68,29 @@ function pause() { // 循环顺序 function orderChange() { - if(!rem.order){ - rem.order = 2 - } - rem.order++; - if(rem.order > 3){ - rem.order = 1 - } - var orderDiv = $(".btn-order") - orderDiv.removeClass() - - if ( 1 === rem.order ) { - orderDiv.addClass("player-btn btn-order btn-order-single") - orderDiv.attr("title","单曲循环") - } - else if ( 2 === rem.order ) { - orderDiv.addClass("player-btn btn-order btn-order-list") - orderDiv.attr("title","列表循环") - } - else if ( 3 === rem.order ) { - orderDiv.addClass("player-btn btn-order btn-order-random") - orderDiv.attr("title","随机播放") - } + if(!rem.order) rem.order = 2; + rem.order++; + if(rem.order > 3) rem.order = 1; + + var orderDiv = $(".btn-order"); + orderDiv.removeClass(); + switch(rem.order) { + case 1: // 单曲循环 + orderDiv.addClass("player-btn btn-order btn-order-single"); + orderDiv.attr("title","单曲循环"); + break; + + case 3: // 随机播放 + orderDiv.addClass("player-btn btn-order btn-order-random"); + orderDiv.attr("title","随机播放"); + break; + + default: // 顺序播放 + orderDiv.addClass("player-btn btn-order btn-order-list"); + orderDiv.attr("title","列表循环"); + } } + // 播放 function audioPlay() { rem.paused = false; // 更新状态(未暂停) @@ -142,28 +148,28 @@ function prevMusic() { // 播放下一首歌 function nextMusic() { - switch (rem.order ? rem.order : 1) { - case 1,2: - playList(rem.playid + 1); - break; - case 3: - if (musicList[1] && musicList[1].item.length) { - var id = parseInt(Math.random() * musicList[1].item.length) - playList(id); - } - break; - default: - playList(rem.playid + 1); - break; - } + switch (rem.order ? rem.order : 1) { + case 1,2: + playList(rem.playid + 1); + break; + case 3: + if (musicList[1] && musicList[1].item.length) { + var id = parseInt(Math.random() * musicList[1].item.length); + playList(id); + } + break; + default: + playList(rem.playid + 1); + break; + } } // 自动播放时的下一首歌 function autoNextMusic() { - if(rem.order && rem.order === 1) { - playList(rem.playid); - } else { - nextMusic() - } + if(rem.order && rem.order === 1) { + playList(rem.playid); + } else { + nextMusic(); + } } // 歌曲时间变动回调函数 @@ -328,6 +334,7 @@ function play(music) { return; } + rem.errCount = 0; // 连续播放失败的歌曲数归零 music_bar.goto(0); // 进度条强制归零 changeCover(music); // 更新封面展示 ajaxLyric(music, lyricCallback); // ajax加载歌词 @@ -337,7 +344,7 @@ function play(music) { // 我的要求并不高,保留这一句版权信息可好? // 保留了,你不会损失什么;而保留版权,是对作者最大的尊重。 -console.info('欢迎使用 MKOnlinePlayer!\n当前版本:'+mkPlayer.version+' \n作者:mengkun(http://mkblog.cn)\n歌曲来源于各大音乐平台\nGithub:https://github.com/mengkunsoft/MKOnlineMusicPlayer'); +console.info('欢迎使用 MKOnlinePlayer!\n当前版本:'+mkPlayer.version+' \n作者:mengkun(https://mkblog.cn)\n歌曲来源于各大音乐平台\nGithub:https://github.com/mengkunsoft/MKOnlineMusicPlayer'); // 音乐进度条拖动回调函数 function mBcallback(newVal) { diff --git a/plugns/Meting.php b/plugns/Meting.php index e3f1a7e..eb8fd2b 100644 --- a/plugns/Meting.php +++ b/plugns/Meting.php @@ -1,161 +1,174 @@ -* Released under the MIT license -*/ + * Meting music framework + * https://i-meto.com + * https://github.com/metowolf/Meting + * Version 1.5.2. + * + * Copyright 2018, METO Sheel + * Released under the MIT license + */ namespace Metowolf; class Meting { - protected $_SITE; - protected $_TEMP; - protected $_RETRY = 3; - protected $_FORMAT = false; + const VERSION = '1.5.2'; - public function __construct($v = 'netease') + public $raw; + public $data; + public $info; + public $error; + public $status; + + public $server; + public $format = false; + public $header = array( + 'Accept' => '*/*', + 'Accept-Encoding' => 'gzip, deflate', + 'Accept-Language' => 'zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4', + 'Connection' => 'keep-alive', + 'Content-Type' => 'application/x-www-form-urlencoded', + ); + + public function __construct($value = 'netease') { - $this->site($v); + $this->site($value); } - public function site($v) + public function site($value) { $suppose = array('netease', 'tencent', 'xiami', 'kugou', 'baidu'); - $this->_SITE = in_array($v,$suppose) ? $v : 'netease'; + $this->server = in_array($value, $suppose) ? $value : 'netease'; + $this->header = $this->curlset(); + return $this; } - public function cookie($v = '') + public function cookie($value) { - $this->_TEMP['cookie'] = $v; + $this->header['Cookie'] = $value; + return $this; } - public function format($v = true) + public function format($value = true) { - $this->_FORMAT = $v; + $this->format = $value; + return $this; } - private function curl($API) + private function exec($api) { - if (isset($API['encode'])) { - $API=call_user_func_array(array($this,$API['encode']), array($API)); + if (isset($api['encode'])) { + $api = call_user_func_array(array($this, $api['encode']), array($api)); } - $BASE = $this->curlset(); + if ($api['method'] == 'GET') { + if (isset($api['body'])) { + $api['url'] .= '?'.http_build_query($api['body']); + $api['body'] = null; + } + } + + $this->curl($api['url'], $api['body']); + + if (!$this->format) { + return $this->raw; + } + + $this->data = $this->raw; + + if (isset($api['decode'])) { + $this->data = call_user_func_array(array($this, $api['decode']), array($this->data)); + } + if (isset($api['format'])) { + $this->data = $this->clean($this->data, $api['format']); + } + + return $this->data; + } + + private function curl($url, $payload = null, $headerOnly = 0) + { + $header = array_map(function ($k, $v) { + return $k.': '.$v; + }, array_keys($this->header), $this->header); $curl = curl_init(); - if ($API['method'] == 'POST') { - if (is_array($API['body'])) { - $API['body'] = http_build_query($API['body']); - } + if (!is_null($payload)) { curl_setopt($curl, CURLOPT_POST, 1); - curl_setopt($curl, CURLOPT_POSTFIELDS, $API['body']); - } elseif ($API['method'] == 'GET') { - if (isset($API['body'])) { - $API['url'] = $API['url'].'?'.http_build_query($API['body']); - } + curl_setopt($curl, CURLOPT_POSTFIELDS, is_array($payload) ? http_build_query($payload) : $payload); } - curl_setopt($curl, CURLOPT_HEADER, 0); + curl_setopt($curl, CURLOPT_HEADER, $headerOnly); curl_setopt($curl, CURLOPT_TIMEOUT, 20); curl_setopt($curl, CURLOPT_ENCODING, 'gzip'); curl_setopt($curl, CURLOPT_IPRESOLVE, 1); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10); - curl_setopt($curl, CURLOPT_URL, $API['url']); - curl_setopt($curl, CURLOPT_COOKIE, isset($this->_TEMP['cookie'])?$this->_TEMP['cookie']:$BASE['cookie']); - curl_setopt($curl, CURLOPT_HTTPHEADER, array( - 'Accept: */*', - 'Accept-Encoding: gzip, deflate', - 'Accept-Language: zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4', - 'Connection: keep-alive', - 'Content-Type: application/x-www-form-urlencoded', - 'Referer: ' . $BASE['referer'], - 'User-Agent: ' . $BASE['useragent'] - )); - for ($i=0;$i<=$this->_RETRY;$i++) { - $data = curl_exec($curl); - $info = curl_getinfo($curl); - $error = curl_errno($curl); - $status = $error ? curl_error($curl) : ''; - if (!$error) { + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_HTTPHEADER, $header); + for ($i = 0; $i < 3; $i++) { + $this->raw = curl_exec($curl); + $this->info = curl_getinfo($curl); + $this->error = curl_errno($curl); + $this->status = $this->error ? curl_error($curl) : ''; + if (!$this->error) { break; } } curl_close($curl); - if ($error) { - return json_encode( - array( - 'error' => $error, - 'info' => $info, - 'status' => $status, - ) - ); - } - if ($this->_FORMAT && isset($API['decode'])) { - $data = call_user_func_array(array($this,$API['decode']), array($data)); - } - if ($this->_FORMAT && isset($API['format'])) { - $data = json_decode($data, 1); - $data = $this->clean($data, $API['format']); - $data = json_encode($data); - } - return $data; + + return $this; } private function pickup($array, $rule) { - $t = explode('#', $rule); + $t = explode('.', $rule); foreach ($t as $vo) { - if (!isset($array[$vo])){ + if (!isset($array[$vo])) { return array(); } $array = $array[$vo]; } + return $array; } private function clean($raw, $rule) { + $raw = json_decode($raw, true); if (!empty($rule)) { $raw = $this->pickup($raw, $rule); } - if (!isset($raw[0]) && sizeof($raw)) { + if (!isset($raw[0]) && count($raw)) { $raw = array($raw); } - $result = array_map(array($this,'format_'.$this->_SITE), $raw); - return $result; + $result = array_map(array($this, 'format_'.$this->server), $raw); + + return json_encode($result); } public function search($keyword, $option = null) { - switch ($this->_SITE) { + switch ($this->server) { case 'netease': - $API = array( + $api = array( 'method' => 'POST', - 'url' => 'http://music.163.com/api/linux/forward', + 'url' => 'http://music.163.com/api/cloudsearch/pc', 'body' => array( - 'method' => 'POST', - 'params' => array( - 's' => $keyword, - 'type' => isset($option['type']) ? $option['type'] : 1, - 'limit' => isset($option['limit']) ? $option['limit'] : 30, - 'total' => 'true', - 'offset' => isset($option['page']) && isset($option['limit']) ? ($option['page'] - 1) * $option['limit'] : 0, - ), - 'url' => 'http://music.163.com/api/cloudsearch/pc', + 's' => $keyword, + 'type' => isset($option['type']) ? $option['type'] : 1, + 'limit' => isset($option['limit']) ? $option['limit'] : 30, + 'total' => 'true', + 'offset' => isset($option['page']) && isset($option['limit']) ? ($option['page'] - 1) * $option['limit'] : 0, ), - 'encode' => 'netease_AESECB', - 'format' => 'result#songs', + 'encode' => 'netease_AESCBC', + 'format' => 'result.songs', ); break; case 'tencent': - $API = array( + $api = array( 'method' => 'GET', 'url' => 'https://c.y.qq.com/soso/fcgi-bin/client_search_cp', 'body' => array( @@ -168,26 +181,29 @@ class Meting 'cr' => 1, 'new_json' => 1, ), - 'format' => 'data#song#list', + 'format' => 'data.song.list', ); break; case 'xiami': - $API = array( + $api = array( 'method' => 'GET', - 'url' => 'http://api.xiami.com/web', + 'url' => 'http://h5api.m.xiami.com/h5/mtop.alimusic.search.searchservice.searchsongs/1.0/', 'body' => array( - 'v' => '2.0', - 'app_key' => '1', - 'key' => $keyword, - 'page' => isset($option['page']) ? $option['page'] : 1, - 'limit' => isset($option['limit']) ? $option['limit'] : 30, - 'r' => 'search/songs', + 'data' => array( + 'key' => $keyword, + 'pagingVO' => array( + 'page' => isset($option['page']) ? $option['page'] : 1, + 'pageSize' => isset($option['limit']) ? $option['limit'] : 30, + ), + ), + 'r' => 'mtop.alimusic.search.searchservice.searchsongs', ), - 'format' => 'data#songs', + 'encode' => 'xiami_sign', + 'format' => 'data.data.songs', ); break; case 'kugou': - $API = array( + $api = array( 'method' => 'GET', 'url' => 'http://ioscdn.kugou.com/api/v3/search/song', 'body' => array( @@ -199,15 +215,15 @@ class Meting 'showtype' => 10, 'page' => isset($option['page']) ? $option['page'] : 1, 'keyword' => $keyword, - 'version' => 8550 + 'version' => 8550, ), - 'format' => 'data#info', + 'format' => 'data.info', ); break; case 'baidu': - $API = array( + $api = array( 'method' => 'GET', - 'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting', + 'url' => 'https://gss2.baidu.com/6Ls1aze90MgYm2Gp8IqW0jdnxx1xbK/v1/restserver/ting', 'body' => array( 'from' => 'qianqianmini', 'method' => 'baidu.ting.search.merge', @@ -216,35 +232,32 @@ class Meting 'page_no' => isset($option['page']) ? $option['page'] : 1, 'query' => $keyword, 'version' => '11.0.2', - 'page_size' => isset($option['limit']) ? $option['limit'] : 30 + 'page_size' => isset($option['limit']) ? $option['limit'] : 30, ), - 'format' => 'result#song_info#song_list', + 'format' => 'result.song_info.song_list', ); break; } - return $this->curl($API); + + return $this->exec($api); } public function song($id) { - switch ($this->_SITE) { + switch ($this->server) { case 'netease': - $API = array( + $api = array( 'method' => 'POST', - 'url' => 'http://music.163.com/api/linux/forward', + 'url' => 'http://music.163.com/api/v3/song/detail/', 'body' => array( - 'method' => 'POST', - 'params' => array( - 'c' => '[{"id":'.$id.',"v":0}]', - ), - 'url' => 'http://music.163.com/api/v3/song/detail/', + 'c' => '[{"id":'.$id.',"v":0}]', ), - 'encode' => 'netease_AESECB', + 'encode' => 'netease_AESCBC', 'format' => 'songs', ); break; case 'tencent': - $API = array( + $api = array( 'method' => 'GET', 'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_play_single_song.fcg', 'body' => array( @@ -257,75 +270,73 @@ class Meting ); break; case 'xiami': - $API = array( + $api = array( 'method' => 'GET', - 'url' => 'http://api.xiami.com/web', + 'url' => 'http://h5api.m.xiami.com/h5/mtop.alimusic.music.songservice.getsongdetail/1.0/', 'body' => array( - 'v' => '2.0', - 'app_key' => '1', - 'id' => $id, - 'r' => 'song/detail', + 'data' => array( + 'songId' => $id, + ), + 'r' => 'mtop.alimusic.music.songservice.getsongdetail', ), - 'format' => 'data#song', + 'encode' => 'xiami_sign', + 'format' => 'data.data.songDetail', ); break; case 'kugou': - $API = array( + $api = array( 'method' => 'POST', 'url' => 'http://m.kugou.com/app/i/getSongInfo.php', 'body' => array( - "cmd" => "playInfo", - "hash" => $id, - "from" => "mkugou", + 'cmd' => 'playInfo', + 'hash' => $id, + 'from' => 'mkugou', ), 'format' => '', ); break; case 'baidu': - $API = array( + $api = array( 'method' => 'GET', - 'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting', + 'url' => 'https://gss2.baidu.com/6Ls1aze90MgYm2Gp8IqW0jdnxx1xbK/v1/restserver/ting', 'body' => array( - 'from' => 'qianqianmini', - 'method' => 'baidu.ting.song.getInfos', - 'songid' => $id, - 'res' => 1, - 'platform' => 'darwin', - 'version' => '1.0.0' + 'from' => 'qianqianmini', + 'method' => 'baidu.ting.song.getInfos', + 'songid' => $id, + 'res' => 1, + 'platform' => 'darwin', + 'version' => '1.0.0', ), 'encode' => 'baidu_AESCBC', 'format' => 'songinfo', ); break; } - return $this->curl($API); + + return $this->exec($api); } public function album($id) { - switch ($this->_SITE) { + switch ($this->server) { case 'netease': - $API = array( + $api = array( 'method' => 'POST', - 'url' => 'http://music.163.com/api/linux/forward', + 'url' => 'http://music.163.com/api/v1/album/'.$id, 'body' => array( - 'method' => 'GET', - 'params' => array( - "total" => "true", - "offset" => "0", - "id" => $id, - "limit" => "1000", - "ext" => "true", - "private_cloud" => "true" - ), - 'url' => 'http://music.163.com/api/v1/album/'.$id, + 'total' => 'true', + 'offset' => '0', + 'id' => $id, + 'limit' => '1000', + 'ext' => 'true', + 'private_cloud' => 'true', ), - 'encode' => 'netease_AESECB', + 'encode' => 'netease_AESCBC', 'format' => 'songs', ); break; case 'tencent': - $API = array( + $api = array( 'method' => 'GET', 'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_album_detail_cp.fcg', 'body' => array( @@ -334,24 +345,25 @@ class Meting 'format' => 'json', 'newsong' => 1, ), - 'format' => 'data#getSongInfo', + 'format' => 'data.getSongInfo', ); break; case 'xiami': - $API = array( + $api = array( 'method' => 'GET', - 'url' => 'http://api.xiami.com/web', + 'url' => 'http://h5api.m.xiami.com/h5/mtop.alimusic.music.albumservice.getalbumdetail/1.0/', 'body' => array( - 'v' => '2.0', - 'app_key' => '1', - 'id' => $id, - 'r' => 'album/detail', + 'data' => array( + 'albumId' => $id, + ), + 'r' => 'mtop.alimusic.music.albumservice.getalbumdetail', ), - 'format' => 'data#songs', + 'encode' => 'xiami_sign', + 'format' => 'data.data.albumDetail.songs', ); break; case 'kugou': - $API = array( + $api = array( 'method' => 'GET', 'url' => 'http://mobilecdn.kugou.com/api/v3/album/song', 'body' => array( @@ -361,51 +373,48 @@ class Meting 'pagesize' => -1, 'version' => 8550, ), - 'format' => 'data#info', + 'format' => 'data.info', ); break; case 'baidu': - $API = array( + $api = array( 'method' => 'GET', - 'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting', + 'url' => 'https://gss2.baidu.com/6Ls1aze90MgYm2Gp8IqW0jdnxx1xbK/v1/restserver/ting', 'body' => array( - 'from' => 'qianqianmini', - 'method' => 'baidu.ting.album.getAlbumInfo', - 'album_id' => $id, - 'platform' => 'darwin', - 'version' => '11.0.2' + 'from' => 'qianqianmini', + 'method' => 'baidu.ting.album.getAlbumInfo', + 'album_id' => $id, + 'platform' => 'darwin', + 'version' => '11.0.2', ), 'format' => 'songlist', ); break; } - return $this->curl($API); + + return $this->exec($api); } - public function artist($id, $limit=50) + public function artist($id, $limit = 50) { - switch ($this->_SITE) { + switch ($this->server) { case 'netease': - $API = array( + $api = array( 'method' => 'POST', - 'url' => 'http://music.163.com/api/linux/forward', + 'url' => 'http://music.163.com/api/v1/artist/'.$id, 'body' => array( - 'method' => 'GET', - 'params' => array( - "ext" => "true", - "private_cloud" => "true", - "ext" => "true", - "top" => $limit, - "id" => $id - ), - 'url' => 'http://music.163.com/api/v1/artist/'.$id, + 'ext' => 'true', + 'private_cloud' => 'true', + 'ext' => 'true', + 'top' => $limit, + 'id' => $id, ), - 'encode' => 'netease_AESECB', + 'encode' => 'netease_AESCBC', 'format' => 'hotSongs', ); break; case 'tencent': - $API = array( + $api = array( 'method' => 'GET', 'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg', 'body' => array( @@ -416,26 +425,29 @@ class Meting 'platform' => 'mac', 'newsong' => 1, ), - 'format' => 'data#list', + 'format' => 'data.list', ); break; case 'xiami': - $API = array( + $api = array( 'method' => 'GET', - 'url' => 'http://api.xiami.com/web', + 'url' => 'http://h5api.m.xiami.com/h5/mtop.alimusic.music.songservice.getartistsongs/1.0/', 'body' => array( - 'v' => '2.0', - 'app_key' => '1', - 'id' => $id, - 'limit' => $limit, - 'page' => 1, - 'r' => 'artist/hot-songs', + 'data' => array( + 'artistId' => $id, + 'pagingVO' => array( + 'page' => 1, + 'pageSize' => $limit, + ), + ), + 'r' => 'mtop.alimusic.music.songservice.getartistsongs', ), - 'format' => 'data', + 'encode' => 'xiami_sign', + 'format' => 'data.data.songs', ); break; case 'kugou': - $API = array( + $api = array( 'method' => 'GET', 'url' => 'http://mobilecdn.kugou.com/api/v3/singer/song', 'body' => array( @@ -445,53 +457,50 @@ class Meting 'pagesize' => $limit, 'version' => 8400, ), - 'format' => 'data#info', + 'format' => 'data.info', ); break; case 'baidu': - $API = array( + $api = array( 'method' => 'GET', - 'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting', + 'url' => 'https://gss2.baidu.com/6Ls1aze90MgYm2Gp8IqW0jdnxx1xbK/v1/restserver/ting', 'body' => array( - 'from' => 'qianqianmini', - 'method' => 'baidu.ting.artist.getSongList', - 'artistid' => $id, - 'limits' => 20, - 'platform' => 'darwin', - 'offset' => 0, - 'tinguid' => 0, - 'version' => '11.0.2' + 'from' => 'qianqianmini', + 'method' => 'baidu.ting.artist.getSongList', + 'artistid' => $id, + 'limits' => $limit, + 'platform' => 'darwin', + 'offset' => 0, + 'tinguid' => 0, + 'version' => '11.0.2', ), 'format' => 'songlist', ); break; } - return $this->curl($API); + + return $this->exec($api); } public function playlist($id) { - switch ($this->_SITE) { + switch ($this->server) { case 'netease': - $API = array( + $api = array( 'method' => 'POST', - 'url' => 'http://music.163.com/api/linux/forward', + 'url' => 'http://music.163.com/api/v3/playlist/detail', 'body' => array( - 'method' => 'POST', - 'params' => array( - "s" => "0", - "id" => $id, - "n" => "1000", - "t" => "0" - ), - 'url' => 'http://music.163.com/api/v3/playlist/detail', + 's' => '0', + 'id' => $id, + 'n' => '1000', + 't' => '0', ), - 'encode' => 'netease_AESECB', - 'format' => 'playlist#tracks', + 'encode' => 'netease_AESCBC', + 'format' => 'playlist.tracks', ); break; case 'tencent': - $API = array( + $api = array( 'method' => 'GET', 'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_playlist_cp.fcg', 'body' => array( @@ -500,24 +509,30 @@ class Meting 'newsong' => 1, 'platform' => 'jqspaframe.json', ), - 'format' => 'data#cdlist#0#songlist', + 'format' => 'data.cdlist.0.songlist', ); break; case 'xiami': - $API = array( + $api = array( 'method' => 'GET', - 'url' => 'http://api.xiami.com/web', + 'url' => 'http://h5api.m.xiami.com/h5/mtop.alimusic.music.list.collectservice.getcollectdetail/1.0/', 'body' => array( - 'v' => '2.0', - 'app_key' => '1', - 'id' => $id, - 'r' => 'collect/detail', + 'data' => array( + 'listId' => $id, + 'isFullTags' => false, + 'pagingVO' => array( + 'page' => 1, + 'pageSize' => 1000, + ), + ), + 'r' => 'mtop.alimusic.music.list.collectservice.getcollectdetail', ), - 'format' => 'data#songs', + 'encode' => 'xiami_sign', + 'format' => 'data.data.collectDetail.songs', ); break; case 'kugou': - $API = array( + $api = array( 'method' => 'GET', 'url' => 'http://mobilecdn.kugou.com/api/v3/special/song', 'body' => array( @@ -527,258 +542,259 @@ class Meting 'pagesize' => -1, 'version' => 8400, ), - 'format' => 'data#info', + 'format' => 'data.info', ); break; case 'baidu': - $API = array( + $api = array( 'method' => 'GET', - 'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting', + 'url' => 'https://gss2.baidu.com/6Ls1aze90MgYm2Gp8IqW0jdnxx1xbK/v1/restserver/ting', 'body' => array( - 'from' => 'qianqianmini', - 'method' => 'baidu.ting.diy.gedanInfo', - 'listid' => $id, - 'platform' => 'darwin', - 'version' => '11.0.2' + 'from' => 'qianqianmini', + 'method' => 'baidu.ting.diy.gedanInfo', + 'listid' => $id, + 'platform' => 'darwin', + 'version' => '11.0.2', ), 'format' => 'content', ); break; } - return $this->curl($API); + + return $this->exec($api); } - public function url($id, $br=320) + public function url($id, $br = 320) { - switch ($this->_SITE) { + switch ($this->server) { case 'netease': - $API = array( + $api = array( 'method' => 'POST', - 'url' => 'http://music.163.com/api/linux/forward', + 'url' => 'http://music.163.com/api/song/enhance/player/url', 'body' => array( - 'method' => 'POST', - 'params' => array( - 'ids' => array($id), - 'br' => $br*1000, - ), - 'url' => 'http://music.163.com/api/song/enhance/player/url', + 'ids' => array($id), + 'br' => $br * 1000, ), - 'encode' => 'netease_AESECB', + 'encode' => 'netease_AESCBC', 'decode' => 'netease_url', ); break; case 'tencent': - $API = array( + $api = array( 'method' => 'GET', 'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_play_single_song.fcg', 'body' => array( - 'songmid' => $id, + 'songmid' => $id, 'platform' => 'yqq', - 'format' => 'json', + 'format' => 'json', ), 'decode' => 'tencent_url', ); break; case 'xiami': - $API = array( + $api = array( 'method' => 'GET', - 'url' => 'http://www.xiami.com/song/gethqsong/sid/'.$id, + 'url' => 'http://h5api.m.xiami.com/h5/mtop.alimusic.music.songservice.getsongdetail/1.0/', 'body' => array( - 'v' => '2.0', - 'app_key' => '1', - 'id' => $id, - 'r' => 'song/detail', + 'data' => array( + 'songId' => $id, + ), + 'r' => 'mtop.alimusic.music.songservice.getsongdetail', ), + 'encode' => 'xiami_sign', 'decode' => 'xiami_url', ); break; case 'kugou': - $API = array( + $api = array( 'method' => 'POST', 'url' => 'http://media.store.kugou.com/v1/get_res_privilege', - 'body' => json_encode(array( - "relate" => 1, - "userid" => 0, - "vip" => 0, - "appid" => 1005, - "token" => "", - "behavior" => "download", - "clientver" => "8493", - "resource" => array(array( - "id" => 0, - "type" => "audio", - "hash" => $id, - ))) + 'body' => json_encode( + array( + 'relate' => 1, + 'userid' => 0, + 'vip' => 0, + 'appid' => 1005, + 'token' => '', + 'behavior' => 'download', + 'clientver' => '8493', + 'resource' => array(array( + 'id' => 0, + 'type' => 'audio', + 'hash' => $id, + )), ) ), 'decode' => 'kugou_url', ); break; case 'baidu': - $API = array( + $api = array( 'method' => 'GET', - 'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting', + 'url' => 'https://gss2.baidu.com/6Ls1aze90MgYm2Gp8IqW0jdnxx1xbK/v1/restserver/ting', 'body' => array( - 'from' => 'qianqianmini', - 'method' => 'baidu.ting.song.getInfos', - 'songid' => $id, - 'res' => 1, - 'platform' => 'darwin', - 'version' => '1.0.0' + 'from' => 'qianqianmini', + 'method' => 'baidu.ting.song.getInfos', + 'songid' => $id, + 'res' => 1, + 'platform' => 'darwin', + 'version' => '1.0.0', ), 'encode' => 'baidu_AESCBC', 'decode' => 'baidu_url', ); break; } - $this->_TEMP['br'] = $br; - return $this->curl($API); + $this->temp['br'] = $br; + + return $this->exec($api); } public function lyric($id) { - switch ($this->_SITE) { + switch ($this->server) { case 'netease': - $API = array( + $api = array( 'method' => 'POST', - 'url' => 'http://music.163.com/api/linux/forward', + 'url' => 'http://music.163.com/api/song/lyric', 'body' => array( - 'method' => 'POST', - 'params' => array( - 'id' => $id, - 'os' => 'linux', - 'lv' => -1, - 'kv' => -1, - 'tv' => -1, - ), - 'url' => 'http://music.163.com/api/song/lyric', + 'id' => $id, + 'os' => 'linux', + 'lv' => -1, + 'kv' => -1, + 'tv' => -1, ), - 'encode' => 'netease_AESECB', + 'encode' => 'netease_AESCBC', 'decode' => 'netease_lyric', ); break; case 'tencent': - $API = array( + $api = array( 'method' => 'GET', 'url' => 'https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg', 'body' => array( - 'songmid' => $id, - 'g_tk' => '5381', + 'songmid' => $id, + 'g_tk' => '5381', ), 'decode' => 'tencent_lyric', ); break; case 'xiami': - $API = array( + $api = array( 'method' => 'GET', - 'url' => 'http://api.xiami.com/web', + 'url' => 'http://h5api.m.xiami.com/h5/mtop.alimusic.music.lyricservice.getsonglyrics/1.0/', 'body' => array( - 'v' => '2.0', - 'app_key' => '1', - 'id' => $id, - 'r' => 'song/detail', + 'data' => array( + 'songId' => $id, + ), + 'r' => 'mtop.alimusic.music.lyricservice.getsonglyrics', ), + 'encode' => 'xiami_sign', 'decode' => 'xiami_lyric', ); break; case 'kugou': - $API = array( + $api = array( 'method' => 'GET', - 'url' => 'http://m.kugou.com/app/i/krc.php', + 'url' => 'http://lyrics.kugou.com/search', 'body' => array( - 'keyword' => '%20-%20', - 'timelength' => 1000000, - 'cmd' => 100, - 'hash' => $id, + 'keyword' => '%20-%20', + 'ver' => 1, + 'hash' => $id, + 'client' => 'pc', + 'man' => 'no', + 'duration' => 295058, ), - 'decode' => 'kugou_lyric' + 'decode' => 'kugou_lyric', ); break; case 'baidu': - $API = array( + $api = array( 'method' => 'GET', - 'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting', + 'url' => 'https://gss2.baidu.com/6Ls1aze90MgYm2Gp8IqW0jdnxx1xbK/v1/restserver/ting', 'body' => array( - 'from' => 'qianqianmini', - 'method' => 'baidu.ting.song.lry', - 'songid' => $id, - 'platform' => 'darwin', - 'version' => '1.0.0' + 'from' => 'qianqianmini', + 'method' => 'baidu.ting.song.lry', + 'songid' => $id, + 'platform' => 'darwin', + 'version' => '1.0.0', ), 'decode' => 'baidu_lyric', ); break; } - return $this->curl($API); + + return $this->exec($api); } - public function pic($id, $size=300) + public function pic($id, $size = 300) { - switch ($this->_SITE) { + switch ($this->server) { case 'netease': - $url='https://p3.music.126.net/'.$this->netease_pickey($id).'/'.$id.'.jpg?param='.$size.'y'.$size; + $url = 'https://p3.music.126.net/'.$this->netease_encryptId($id).'/'.$id.'.jpg?param='.$size.'y'.$size; break; case 'tencent': - $url='https://y.gtimg.cn/music/photo_new/T002R'.$size.'x'.$size.'M000'.$id.'.jpg?max_age=2592000'; + $url = 'https://y.gtimg.cn/music/photo_new/T002R'.$size.'x'.$size.'M000'.$id.'.jpg?max_age=2592000'; break; case 'xiami': - $format=$this->_FORMAT; - $data=$this->format(false)->song($id); - $this->format($format); - $data=json_decode($data, 1); - $url=$data['data']['song']['logo']; - $url=str_replace(array('_1.','http:','img.'), array('.','https:','pic.'), $url).'@'.$size.'h_'.$size.'w_100q_1c.jpg'; + $format = $this->format; + $data = $this->format(false)->song($id); + $this->format = $format; + $data = json_decode($data, true); + $url = $data['data']['data']['songDetail']['albumLogo']; + $url = str_replace('http:', 'https:', $url).'@1e_1c_100Q_'.$size.'h_'.$size.'w'; break; case 'kugou': - $format=$this->_FORMAT; - $data=$this->format(false)->song($id); - $this->format($format); - $data=json_decode($data, 1); - $url=$data['imgUrl']; - $url=str_replace('{size}', '400', $url); + $format = $this->format; + $data = $this->format(false)->song($id); + $this->format = $format; + $data = json_decode($data, true); + $url = $data['imgUrl']; + $url = str_replace('{size}', '400', $url); break; case 'baidu': - $format=$this->_FORMAT; - $data=$this->format(false)->song($id); - $this->format($format); - $data=json_decode($data, 1); - $url=isset($data['songinfo']['pic_radio'])?$data['songinfo']['pic_radio']:$data['songinfo']['pic_small']; + $format = $this->format; + $data = $this->format(false)->song($id); + $this->format = $format; + $data = json_decode($data, true); + $url = isset($data['songinfo']['pic_radio']) ? $data['songinfo']['pic_radio'] : $data['songinfo']['pic_small']; break; } - return json_encode(array('url'=>$url)); + + return json_encode(array('url' => $url)); } private function curlset() { - switch ($this->_SITE) { + switch ($this->server) { case 'netease': return array( - 'referer' => 'https://music.163.com/', - 'cookie' => 'os=linux; deviceId=' . $this->getRandomHex(52) . '; osver=Ubuntu%2016.04.3%20LTS; appver=1.1.0.1232; channel=netease; MUSIC_U=255b19fea4bdec0a0011f855c3708e3e97b229707fccab67d74da902317635b4f866558227e6c3335d9bd72ef1abb9ea77749c2dda21047b; __csrf=' . $this->getRandomHex(32), - 'useragent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36', + 'Referer' => 'https://music.163.com/', + 'Cookie' => 'os=pc; osver=Microsoft-Windows-10-Professional-build-10586-64bit; appver=2.0.3.131777; channel=netease; __remember_me=true', + 'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36', + 'X-Real-IP' => long2ip(mt_rand(1884815360, 1884890111)), ); case 'tencent': return array( - 'referer' => 'https://y.qq.com/portal/player.html', - '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', - 'useragent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36', + 'Referer' => 'https://y.qq.com/portal/player.html', + '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', + 'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36', ); case 'xiami': return array( - 'referer' => 'http://h.xiami.com/', - 'cookie' => '_xiamitoken=' . $this->getRandomHex(32) . '; _unsign_token=' . $this->getRandomHex(32), - 'useragent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36', + 'Referer' => 'http://h5api.m.xiami.com/', + 'Cookie' => '_m_h5_tk=15d3402511a022796d88b249f83fb968_1511163656929; _m_h5_tk_enc=b6b3e64d81dae577fc314b5c5692df3c', + 'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) XIAMI-MUSIC/3.0.9 Chrome/56.0.2924.87 Electron/1.6.11 Safari/537.36', ); case 'kugou': return array( - 'referer' => 'http://www.kugou.com/webkugouplayer/flash/webKugou.swf', - 'cookie' => 'kg_mid=' . $this->getRandomHex(32), - 'useragent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36', + 'Referer' => 'http://www.kugou.com/webkugouplayer/flash/webKugou.swf', + 'Cookie' => 'kg_mid='.$this->getRandomHex(32), + 'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36', ); case 'baidu': return array( - 'referer' => '', - 'cookie' => 'BAIDUID=' . $this->getRandomHex(32) . ':FG=1', - 'useragent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) baidu-music/1.0.2 Chrome/56.0.2924.87 Electron/1.6.11 Safari/537.36', + 'Cookie' => 'BAIDUID='.$this->getRandomHex(32).':FG=1', + 'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) baidu-music/1.0.2 Chrome/56.0.2924.87 Electron/1.6.11 Safari/537.36', ); } } @@ -786,185 +802,283 @@ class Meting private function getRandomHex($length) { if (function_exists('openssl_random_pseudo_bytes')) { - return bin2hex(openssl_random_pseudo_bytes($length)); + return bin2hex(openssl_random_pseudo_bytes($length / 2)); } else { - return bin2hex(mcrypt_create_iv($length, MCRYPT_DEV_URANDOM)); + return bin2hex(mcrypt_create_iv($length / 2, MCRYPT_DEV_URANDOM)); } } - /** - * 乱七八糟的函数,加密解密... - * 正在努力重构这些代码 TAT - */ - private function netease_AESECB($API) + private function bchexdec($hex) { - $KEY='7246674226682325323F5E6544673A51'; - $body=json_encode($API['body']); - if (function_exists('openssl_encrypt')) { - $body=openssl_encrypt($body, 'aes-128-ecb', pack('H*', $KEY)); - } else { - $PAD=16-(strlen($body)%16); - $body=base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, hex2bin($KEY), $body.str_repeat(chr($PAD), $PAD), MCRYPT_MODE_ECB)); + $dec = 0; + $len = strlen($hex); + for ($i = 1; $i <= $len; $i++) { + $dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i)))); } - $body=strtoupper(bin2hex(base64_decode($body))); - $API['body']=array( - 'eparams'=>$body, - ); - return $API; + return $dec; } - private function baidu_AESCBC($API) + + private function bcdechex($dec) + { + $hex = ''; + do { + $last = bcmod($dec, 16); + $hex = dechex($last).$hex; + $dec = bcdiv(bcsub($dec, $last), 16); + } while ($dec > 0); + + return $hex; + } + + private function str2hex($string) + { + $hex = ''; + for ($i = 0; $i < strlen($string); $i++) { + $ord = ord($string[$i]); + $hexCode = dechex($ord); + $hex .= substr('0'.$hexCode, -2); + } + + return $hex; + } + + private function netease_AESCBC($api) + { + $modulus = '157794750267131502212476817800345498121872783333389747424011531025366277535262539913701806290766479189477533597854989606803194253978660329941980786072432806427833685472618792592200595694346872951301770580765135349259590167490536138082469680638514416594216629258349130257685001248172188325316586707301643237607'; + $pubkey = '65537'; + $nonce = '0CoJUm6Qyw8W8jud'; + $vi = '0102030405060708'; + + if (extension_loaded('bcmath')) { + $skey = $this->getRandomHex(16); + } else { + $skey = 'B3v3kH4vRPWRJFfH'; + } + + $body = json_encode($api['body']); + + if (function_exists('openssl_encrypt')) { + $body = openssl_encrypt($body, 'aes-128-cbc', $nonce, false, $vi); + $body = openssl_encrypt($body, 'aes-128-cbc', $skey, false, $vi); + } else { + $pad = 16 - (strlen($body) % 16); + $body = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $nonce, $body.str_repeat(chr($pad), $pad), MCRYPT_MODE_CBC, $vi)); + $pad = 16 - (strlen($body) % 16); + $body = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $skey, $body.str_repeat(chr($pad), $pad), MCRYPT_MODE_CBC, $vi)); + } + + if (extension_loaded('bcmath')) { + $skey = strrev(utf8_encode($skey)); + $skey = $this->bchexdec($this->str2hex($skey)); + $skey = bcpowmod($skey, $pubkey, $modulus); + $skey = $this->bcdechex($skey); + $skey = str_pad($skey, 256, '0', STR_PAD_LEFT); + } else { + $skey = '85302b818aea19b68db899c25dac229412d9bba9b3fcfe4f714dc016bc1686fc446a08844b1f8327fd9cb623cc189be00c5a365ac835e93d4858ee66f43fdc59e32aaed3ef24f0675d70172ef688d376a4807228c55583fe5bac647d10ecef15220feef61477c28cae8406f6f9896ed329d6db9f88757e31848a6c2ce2f94308'; + } + + $api['url'] = str_replace('/api/', '/weapi/', $api['url']); + $api['body'] = array( + 'params' => $body, + 'encSecKey' => $skey, + ); + + return $api; + } + + private function baidu_AESCBC($api) { $key = 'DBEECF8C50FD160E'; $vi = '1231021386755796'; - $data = 'songid='.$API['body']['songid'].'&ts='.intval(microtime(true)*1000); + $data = 'songid='.$api['body']['songid'].'&ts='.intval(microtime(true) * 1000); if (function_exists('openssl_encrypt')) { $data = openssl_encrypt($data, 'aes-128-cbc', $key, false, $vi); } else { - $PAD = 16-(strlen($data)%16); - $data = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data.str_repeat(chr($PAD), $PAD), MCRYPT_MODE_CBC, $vi)); + $pad = 16 - (strlen($data) % 16); + $data = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data.str_repeat(chr($pad), $pad), MCRYPT_MODE_CBC, $vi)); } - $API['body']['e'] = $data; - return $API; + $api['body']['e'] = $data; + + return $api; } + + private function xiami_sign($api) + { + $data = $this->curl('http://h5api.m.xiami.com/h5/mtop.alimusic.search.searchservice.searchsongs/1.0/?appKey=12574478&t=1511168684000&dataType=json&data=%7B%22requestStr%22%3A%22%7B%5C%22model%5C%22%3A%7B%5C%22key%5C%22%3A%5C%22Dangerous+Woman%5C%22%2C%5C%22pagingVO%5C%22%3A%7B%5C%22page%5C%22%3A1%2C%5C%22pageSize%5C%22%3A30%7D%7D%7D%22%7D&api=mtop.alimusic.search.searchservice.searchsongs&v=1.0&type=originaljson&sign=f6c99a429e9ef703ea955f7cd113a467', null, 1); + preg_match_all('/_m_h5[^;]+/', $data->raw, $match); + $this->header['Cookie'] = $match[0][0].'; '.$match[0][1]; + $data = json_encode(array( + 'requestStr' => json_encode(array( + 'header' => array( + 'platformId' => 'mac', + ), + 'model' => $api['body']['data'], + )), + )); + $appkey = '12574478'; + $cookie = $this->header['Cookie']; + preg_match('/_m_h5_tk=([^_]+)/', $cookie, $match); + $token = $match[1]; + $t = time() * 1000; + $sign = md5(sprintf('%s&%s&%s&%s', $token, $t, $appkey, $data)); + $api['body'] = array( + 'appKey' => $appkey, + 't' => $t, + 'dataType' => 'json', + 'data' => $data, + 'api' => $api['body']['r'], + 'v' => '1.0', + 'type' => 'originaljson', + 'sign' => $sign, + ); + + return $api; + } + private function tencent_singlesong($result) { - $result=json_decode($result, 1); - $data=$result['data'][0]; - $t=array( - 'songmid' => $data['mid'], + $result = json_decode($result, true); + $data = $result['data'][0]; + $t = array( + 'songmid' => $data['mid'], 'songname' => $data['name'], 'albummid' => $data['album']['mid'], ); - foreach ($t as $key=>$vo) { - $result['data'][0][$key]=$vo; + foreach ($t as $key => $vo) { + $result['data'][0][$key] = $vo; } + return json_encode($result); } - private function netease_pickey($id) + + private function netease_encryptId($id) { - $magic=str_split('3go8&$8*3*3h0k(2)2'); - $song_id=str_split($id); - for ($i=0;$i $data['data'][0]['url'], - 'br' => $data['data'][0]['br']/1000, + $url = array( + 'url' => $data['data'][0]['url'], + 'size' => $data['data'][0]['size'], + 'br' => $data['data'][0]['br'] / 1000, ); } else { - $url=array( - 'url' => '', - 'br' => -1, + $url = array( + 'url' => '', + 'size' => 0, + 'br' => -1, ); } + return json_encode($url); } + private function tencent_url($result) { - $data=json_decode($result, 1); - $GUID=mt_rand()%10000000000; - $API=array( + $data = json_decode($result, true); + $guid = mt_rand() % 10000000000; + $api = array( 'method' => 'GET', 'url' => 'https://c.y.qq.com/base/fcgi-bin/fcg_musicexpress.fcg', 'body' => array( 'json' => 3, - 'guid' => $GUID, + 'guid' => $guid, 'format' => 'json', ), ); - $KEY=json_decode($this->curl($API), 1); - $KEY=$KEY['key']; + $key = json_decode($this->exec($api), true); + $key = $key['key']; - $type=array( - 'size_320mp3' => array(320,'M800','mp3'), - 'size_192aac' => array(192,'C600','m4a'), - 'size_128mp3' => array(128,'M500','mp3'), - 'size_96aac' => array(96 ,'C400','m4a'), - 'size_48aac' => array(48 ,'C200','m4a'), + $type = array( + 'size_320mp3' => array(320, 'M800', 'mp3'), + 'size_192aac' => array(192, 'C600', 'm4a'), + 'size_128mp3' => array(128, 'M500', 'mp3'), + 'size_96aac' => array(96, 'C400', 'm4a'), + 'size_48aac' => array(48, 'C200', 'm4a'), + 'size_24aac' => array(24, 'C100', 'm4a'), ); - foreach ($type as $key=>$vo) { - if ($data['data'][0]['file'][$key]&&$vo[0]<=$this->_TEMP['br']) { - $url=array( - 'url' => 'https://dl.stream.qqmusic.qq.com/'.$vo[1].$data['data'][0]['file']['media_mid'].'.'.$vo[2].'?vkey='.$KEY.'&guid='.$GUID.'&uid=0&fromtag=30', - 'br' => $vo[0], + foreach ($type as $index => $vo) { + if ($data['data'][0]['file'][$index] && $vo[0] <= $this->temp['br']) { + $url = array( + 'url' => 'https://dl.stream.qqmusic.qq.com/'.$vo[1].$data['data'][0]['file']['media_mid'].'.'.$vo[2].'?vkey='.$key.'&guid='.$guid.'&uid=0&fromtag=30', + 'size' => $data['data'][0]['file'][$index], + 'br' => $vo[0], ); break; } } if (!isset($url['url'])) { - $url=array( - 'url' => '', - 'br' => -1, + $url = array( + 'url' => '', + 'size' => 0, + 'br' => -1, ); } + return json_encode($url); } + private function xiami_url($result) { - $data=json_decode($result, 1); - if (!empty($data['location'])) { - $location = $data['location']; - $num = (int)$location[0]; - $str = substr($location, 1); - $len = floor(strlen($str)/$num); - $sub = strlen($str) % $num; - $qrc = array(); - $tmp = 0; - $urlt = ''; - for (;$tmp<$sub;$tmp++) { - $qrc[$tmp] = substr($str, $tmp*($len+1), $len+1); + $data = json_decode($result, true); + + $type = array( + 's' => 740, + 'h' => 320, + 'l' => 128, + 'f' => 64, + 'e' => 32, + ); + $max = 0; + $url = array(); + foreach ($data['data']['data']['songDetail']['listenFiles'] as $vo) { + if ($type[$vo['quality']] <= $this->temp['br'] && $type[$vo['quality']] > $max) { + $max = $type[$vo['quality']]; + $url = array( + 'url' => $vo['url'], + 'size' => $vo['filesize'], + 'br' => $type[$vo['quality']], + ); } - for (;$tmp<$num;$tmp++) { - $qrc[$tmp] = substr($str, $len*$tmp+$sub, $len); - } - for ($tmpa=0;$tmpa<$len+1;$tmpa++) { - for ($tmpb=0;$tmpb<$num;$tmpb++) { - if (isset($qrc[$tmpb][$tmpa])) { - $urlt.=$qrc[$tmpb][$tmpa]; - } - } - } - $urlt=str_replace('^', '0', urldecode($urlt)); - $url=array( - 'url' => str_replace('http://','https://',urldecode($urlt)), - 'br' => 320, - ); - } else { - $url=array( - 'url' => '', - 'br' => -1, + } + if (!isset($url['url'])) { + $url = array( + 'url' => '', + 'size' => 0, + 'br' => -1, ); } + return json_encode($url); } + private function kugou_url($result) { - $data=json_decode($result, 1); + $data = json_decode($result, true); - $max=0; - $url=array(); + $max = 0; + $url = array(); foreach ($data['data'][0]['relate_goods'] as $vo) { - if ($vo['info']['bitrate']<=$this->_TEMP['br']&&$vo['info']['bitrate']>$max) { - $API=array( + if ($vo['info']['bitrate'] <= $this->temp['br'] && $vo['info']['bitrate'] > $max) { + $api = array( 'method' => 'GET', 'url' => 'http://trackercdn.kugou.com/i/v2/', 'body' => array( @@ -976,207 +1090,228 @@ class Meting 'version' => 8400, ), ); - $t=json_decode($this->curl($API), 1); + $t = json_decode($this->exec($api), true); if (isset($t['url'])) { - $max=$t['bitRate']/1000; - $url=array( - 'url' => $t['url'], - 'br' => $t['bitRate']/1000, + $max = $t['bitRate'] / 1000; + $url = array( + 'url' => $t['url'], + 'size' => $t['fileSize'], + 'br' => $t['bitRate'] / 1000, ); } } } if (!isset($url['url'])) { - $url=array( - 'url' => '', - 'br' => -1, + $url = array( + 'url' => '', + 'size' => 0, + 'br' => -1, ); } + return json_encode($url); } + private function baidu_url($result) { - $data=json_decode($result, true); + $data = json_decode($result, true); - $max=0; - $url=array(); + $max = 0; + $url = array(); foreach ($data['songurl']['url'] as $vo) { - if ($vo['file_bitrate']<=$this->_TEMP['br']&&$vo['file_bitrate']>$max) { - $url=array( + if ($vo['file_bitrate'] <= $this->temp['br'] && $vo['file_bitrate'] > $max) { + $url = array( 'url' => $vo['file_link'], 'br' => $vo['file_bitrate'], ); } } if (!isset($url['url'])) { - $url=array( + $url = array( 'url' => '', 'br' => -1, ); } + return json_encode($url); } - /** - * 歌词处理模块 - * 用于规范化歌词输出 - */ + private function netease_lyric($result) { - if (!$this->_FORMAT) { - return $result; - } - $result=json_decode($result, 1); - $data=array( - 'lyric' => isset($result['lrc']['lyric'])?$result['lrc']['lyric']:'', - 'tlyric' => isset($result['tlyric']['lyric'])?$result['tlyric']['lyric']:'', + $result = json_decode($result, true); + $data = array( + 'lyric' => isset($result['lrc']['lyric']) ? $result['lrc']['lyric'] : '', + 'tlyric' => isset($result['tlyric']['lyric']) ? $result['tlyric']['lyric'] : '', ); + return json_encode($data); } + private function tencent_lyric($result) { - $result=substr($result,18,-1); - if (!$this->_FORMAT) { - return $result; - } - $result=json_decode($result, 1); - $data=array( - 'lyric' => isset($result['lyric'])?base64_decode($result['lyric']):'', - 'tlyric' => isset($result['trans'])?base64_decode($result['trans']):'', + $result = substr($result, 18, -1); + $result = json_decode($result, true); + $data = array( + 'lyric' => isset($result['lyric']) ? base64_decode($result['lyric']) : '', + 'tlyric' => isset($result['trans']) ? base64_decode($result['trans']) : '', ); + return json_encode($data); } + private function xiami_lyric($result) { - if (!$this->_FORMAT) { - return $result; - } - $result=json_decode($result, 1); - $data=''; - if(!empty($result['data']['song']['lyric'])){ - $API=array('method'=>'GET','url'=>$result['data']['song']['lyric']); - $data=$this->curl($API); - $data=preg_replace('/<[^>]+>/', '', $data); - } - preg_match_all('/\[([\d:\.]+)\](.*)\s\[x-trans\](.*)/i',$data,$match); - if(sizeof($match[0])){ - for($i=0;$i]+>/', '', $data); + preg_match_all('/\[([\d:\.]+)\](.*)\s\[x-trans\](.*)/i', $data, $match); + if (count($match[0])) { + for ($i = 0; $i < count($match[0]); $i++) { + $A[] = '['.$match[1][$i].']'.$match[2][$i]; + $B[] = '['.$match[1][$i].']'.$match[3][$i]; + } + $arr = array( + 'lyric' => str_replace($match[0], $A, $data), + 'tlyric' => str_replace($match[0], $B, $data), + ); + } else { + $arr = array( + 'lyric' => $data, + 'tlyric' => '', + ); } - $arr=array( - 'lyric' => str_replace($match[0],$A,$data), - 'tlyric' => str_replace($match[0],$B,$data), - ); - } - else{ - $arr=array( - 'lyric' => $data, + } else { + $arr = array( + 'lyric' => '', 'tlyric' => '', ); } + return json_encode($arr); } + private function kugou_lyric($result) { - if (!$this->_FORMAT) { - return $result; - } - $arr=array( - 'lyric' => $result, + $result = json_decode($result, true); + $api = array( + 'method' => 'GET', + 'url' => 'http://lyrics.kugou.com/download', + 'body' => array( + 'charset' => 'utf8', + 'accesskey' => $result['candidates'][0]['accesskey'], + 'id' => $result['candidates'][0]['id'], + 'client' => 'pc', + 'fmt' => 'lrc', + 'ver' => 1, + ), + ); + $data = json_decode($this->exec($api), true); + $arr = array( + 'lyric' => base64_decode($data['content']), 'tlyric' => '', ); + return json_encode($arr); } + private function baidu_lyric($result) { - if (!$this->_FORMAT) { - return $result; - } - $result=json_decode($result, 1); - $data=array( - 'lyric' => isset($result['lrcContent'])?$result['lrcContent']:'', + $result = json_decode($result, true); + $data = array( + 'lyric' => isset($result['lrcContent']) ? $result['lrcContent'] : '', 'tlyric' => '', ); + return json_encode($data); } - /** - * Format - 规范化函数 - * 用于统一返回的参数,可用 ->format() 一次性开关开启 - */ + private function format_netease($data) { - $result=array( - 'id' => $data['id'], - 'name' => $data['name'], - 'artist' => array(), - 'album' => $data['al']['name'], - 'pic_id' => isset($data['al']['pic_str'])?$data['al']['pic_str']:$data['al']['pic'], - 'url_id' => $data['id'], - 'lyric_id' => $data['id'], - 'source' => 'netease', + $result = array( + 'id' => $data['id'], + 'name' => $data['name'], + 'artist' => array(), + 'album' => $data['al']['name'], + 'pic_id' => isset($data['al']['pic_str']) ? $data['al']['pic_str'] : $data['al']['pic'], + 'url_id' => $data['id'], + 'lyric_id' => $data['id'], + 'source' => 'netease', ); if (isset($data['al']['picUrl'])) { preg_match('/\/(\d+)\./', $data['al']['picUrl'], $match); - $result['pic_id']=$match[1]; + $result['pic_id'] = $match[1]; } foreach ($data['ar'] as $vo) { - $result['artist'][]=$vo['name']; + $result['artist'][] = $vo['name']; } + return $result; } + private function format_tencent($data) { if (isset($data['musicData'])) { - $data=$data['musicData']; + $data = $data['musicData']; } - $result=array( - 'id' => $data['mid'], - 'name' => $data['name'], - 'artist' => array(), - 'album' => trim($data['album']['title']), - 'pic_id' => $data['album']['mid'], - 'url_id' => $data['mid'], - 'lyric_id' => $data['mid'], - 'source' => 'tencent', + $result = array( + 'id' => $data['mid'], + 'name' => $data['name'], + 'artist' => array(), + 'album' => trim($data['album']['title']), + 'pic_id' => $data['album']['mid'], + 'url_id' => $data['mid'], + 'lyric_id' => $data['mid'], + 'source' => 'tencent', ); foreach ($data['singer'] as $vo) { - $result['artist'][]=$vo['name']; + $result['artist'][] = $vo['name']; } + return $result; } + private function format_xiami($data) { - $result=array( - 'id' => $data['song_id'], - 'name' => $data['song_name'], - 'artist' => explode(';', isset($data['singers'])?$data['singers']:$data['artist_name']), - 'album' => $data['album_name'], - 'pic_id' => $data['song_id'], - 'url_id' => $data['song_id'], - 'lyric_id' => $data['song_id'], + $result = array( + 'id' => $data['songId'], + 'name' => $data['songName'], + 'artist' => array(), + 'album' => $data['albumName'], + 'pic_id' => $data['songId'], + 'url_id' => $data['songId'], + 'lyric_id' => $data['songId'], 'source' => 'xiami', ); + foreach ($data['singerVOs'] as $vo) { + $result['artist'][] = $vo['artistName']; + } + return $result; } + private function format_kugou($data) { - $result=array( + $result = array( 'id' => $data['hash'], - 'name' => isset($data['filename'])?$data['filename']:$data['fileName'], + 'name' => isset($data['filename']) ? $data['filename'] : $data['fileName'], 'artist' => array(), - 'album' => isset($data['album_name'])?$data['album_name']:'', + 'album' => isset($data['album_name']) ? $data['album_name'] : '', 'url_id' => $data['hash'], 'pic_id' => $data['hash'], 'lyric_id' => $data['hash'], 'source' => 'kugou', ); - list($result['artist'], $result['name'])=explode(' - ', $result['name'], 2); - $result['artist']=explode('、', $result['artist']); + list($result['artist'], $result['name']) = explode(' - ', $result['name'], 2); + $result['artist'] = explode('、', $result['artist']); + return $result; } + private function format_baidu($data) { - $result=array( + $result = array( 'id' => $data['song_id'], 'name' => $data['title'], 'artist' => explode(',', $data['author']), @@ -1186,6 +1321,7 @@ class Meting 'lyric_id' => $data['song_id'], 'source' => 'baidu', ); + return $result; } }