diff --git a/Action.php b/Action.php new file mode 100644 index 0000000..6c9a3c5 --- /dev/null +++ b/Action.php @@ -0,0 +1,215 @@ +on($this->request->is('do=js'))->js(); + $this->on($this->request->is('do=url'))->url(); + $this->on($this->request->is('do=pic'))->pic(); + $this->on($this->request->is('do=lrc'))->lrc(); + $this->on($this->request->is('do=parse'))->shortcode(); + } + + private function shortcode(){ + $urls=$this->request->get('data'); + $urls=explode("\n",$urls); + echo "[Meting]\n"; + foreach($urls as $url){ + $url=trim($url); + if($url=="")continue; + $server='音乐平台';$id='编号';$type='类型'; + if(strpos($url,'163.com')!==false){ + $server='netease'; + if(preg_match('/playlist\?id=(\d+)/i',$url,$id))list($id,$type)=array($id[1],'playlist'); + elseif(preg_match('/album\?id=(\d+)/i',$url,$id))list($id,$type)=array($id[1],'album'); + elseif(preg_match('/song\?id=(\d+)/i',$url,$id))list($id,$type)=array($id[1],'song'); + elseif(preg_match('/artist\?id=(\d+)/i',$url,$id))list($id,$type)=array($id[1],'artist'); + } + elseif(strpos($url,'qq.com')!==false){ + $server='tencent'; + if(preg_match('/playlist\/([^\.]*)/i',$url,$id))list($id,$type)=array($id[1],'playlist'); + elseif(preg_match('/album\/([^\.]*)/i',$url,$id))list($id,$type)=array($id[1],'album'); + elseif(preg_match('/song\/([^\.]*)/i',$url,$id))list($id,$type)=array($id[1],'song'); + elseif(preg_match('/singer\/([^\.]*)/i',$url,$id))list($id,$type)=array($id[1],'artist'); + } + elseif(strpos($url,'xiami.com')!==false){ + $server='xiami'; + if(preg_match('/collect\/([^\.]*)/i',$url,$id))list($id,$type)=array($id[1],'playlist'); + elseif(preg_match('/album\/([^\.]*)/i',$url,$id))list($id,$type)=array($id[1],'album'); + elseif(preg_match('/song\/([^\.]*)/i',$url,$id))list($id,$type)=array($id[1],'song'); + elseif(preg_match('/artist\/([^\.]*)/i',$url,$id))list($id,$type)=array($id[1],'artist'); + + if(!preg_match('/^\d*$/i',$id,$t)){ + $data=self::curl($url); + preg_match('/'.$type.'\/(\d+)/i',$data,$id); + $id=$id[1]; + } + } + elseif(strpos($url,'kugou.com')!==false){ + $server='kugou'; + if(preg_match('/special\/single\/(\d+)/i',$url,$id))list($id,$type)=array($id[1],'playlist'); + elseif(preg_match('/album\/single\/(\d+)/i',$url,$id))list($id,$type)=array($id[1],'album'); + elseif(preg_match('/singer\/home\/(\d+)/i',$url,$id))list($id,$type)=array($id[1],'artist'); + } + elseif(strpos($url,'baidu.com')!==false){ + $server='baidu'; + if(preg_match('/songlist\/(\d+)/i',$url,$id))list($id,$type)=array($id[1],'playlist'); + elseif(preg_match('/album\/(\d+)/i',$url,$id))list($id,$type)=array($id[1],'album'); + elseif(preg_match('/song\/(\d+)/i',$url,$id))list($id,$type)=array($id[1],'song'); + elseif(preg_match('/artist\/(\d+)/i',$url,$id))list($id,$type)=array($id[1],'artist'); + } + else list($id,$type)=array($url,'search'); + echo '[Music server="'.$server.'" id="'.$id.'" type="'.$type.'"/]'."\n"; + } + echo "[/Meting]"; + } + + private function curl($url){ + $curl=curl_init(); + curl_setopt($curl,CURLOPT_URL,$url); + curl_setopt($curl,CURLOPT_RETURNTRANSFER,1); + curl_setopt($curl,CURLOPT_CONNECTTIMEOUT,10); + curl_setopt($curl,CURLOPT_TIMEOUT,10); + curl_setopt($curl,CURLOPT_REFERER,$url); + $result=curl_exec($curl); + curl_close($curl); + return $result; + } + + private function js(){ + $PID=$this->request->get('id'); + $data=$this->request->get('d'); + $data=json_decode(base64_decode($data),1); + $setting=$this->request->get('s'); + $setting=json_decode(base64_decode($setting),1); + $music=array(); + foreach($data as $vo){ + $EID=md5('js/'.$vo['server'].'/'.$vo['type'].'/'.$vo['id']); + $t=self::cacheRead($EID,60*60*24*3); + if(!$t){ + $API=(new Meting($vo['server']))->format(true); + $t=call_user_func_array(array($API,$vo['type']),array($vo['id'])); + $t=json_decode($t,1); + self::cacheWrite($EID,$t); + } + $music=array_merge($music,$t); + } + $player=array( + 'theme' => $setting['theme']?:Typecho_Widget::widget('Widget_Options')->plugin('Meting')->theme?:'red', + 'preload' => $setting['preload']?:Typecho_Widget::widget('Widget_Options')->plugin('Meting')->preload?:'auto', + 'autoplay' => $setting['autoplay']?:Typecho_Widget::widget('Widget_Options')->plugin('Meting')->autoplay?:'false', + 'height' => $setting['height']?:Typecho_Widget::widget('Widget_Options')->plugin('Meting')->height?:'340px', + 'mode' => $setting['mode']?:Typecho_Widget::widget('Widget_Options')->plugin('Meting')->mode?:'circulation', + 'music' => array(), + ); + foreach($music as $vo){ + $URI=Typecho_Common::url('MetingAPI?site='.$vo['source'],Helper::options()->index); + $player['music'][]=array( + 'title'=>$vo['name'], + 'author'=>implode(' / ',$vo['artist']), + 'url'=>$URI.'&do=url&id='.$vo['url_id'], + 'pic'=>$URI.'&do=pic&id='.$vo['pic_id'], + 'lrc'=>$URI.'&do=lrc&id='.$vo['lyric_id'], + ); + } + if(sizeof($player['music'])==1)$player['music']=$player['music'][0]; + $player['music']=json_encode($player['music']); + + header('content-type:application/javascript'); + echo" +var Meting{$PID} = new APlayer({ + element: document.getElementById('MetingPlayer'+{$PID}), + autoplay: ".$player['autoplay'].", + preload: \"".$player['preload']."\", + showlrc: 3, + mutex: true, + mode: '".$player['mode']."', + theme: \"".$player['theme']."\", + music: ".$player['music'].", + listmaxheight: '".$player['height']."', +}); +"; + } + + private function url(){ + $id=$this->request->get('id'); + $site=$this->request->get('site'); + $rate=Typecho_Widget::widget('Widget_Options')->plugin('Meting')->bitrate; + + $cachekey="url/{$site}/{$id}"; + $data=self::cacheRead($cachekey,60*15); + if(!$data){ + $data=(new Meting($site))->url($id,$rate); + $data=json_decode($data,1); + self::cacheWrite($cachekey,$data); + } + if(empty($data['url']))$data['url']='https://oc1pe0tot.qnssl.com/copyright.m4a'; + $this->response->redirect($data['url']); + } + + private function pic(){ + $id=$this->request->get('id'); + $site=$this->request->get('site'); + + $cachekey="pic/{$site}/{$id}"; + $data=self::cacheRead($cachekey,60*60*24*30); + if(!$data){ + $data=(new Meting($site))->pic($id,90); + $data=json_decode($data,1); + self::cacheWrite($cachekey,$data); + } + $this->response->redirect($data['url']); + } + + private function lrc(){ + $id=$this->request->get('id'); + $site=$this->request->get('site'); + + $cachekey="lrc/{$site}/{$id}"; + $data=self::cacheRead($cachekey,60*60*24*10); + if(!$data){ + $data=(new Meting($site))->lyric($id); + $data=json_decode($data,1); + self::cacheWrite($cachekey,$data); + } + + switch($site){ + case 'netease':$text=$data['lrc']['lyric'];break; + case 'tencent':$text=base64_decode($data['lyric']);break; + case 'xiami':$text=$data['lyric'];break; + case 'kugou':$text=$data['lyric'];break; + case 'baidu':$text=$data['lrcContent'];break; + } + if(strlen($text)==0)$text='[00:00.00]无歌词'; + echo $text; + } + + private function cacheWrite($k,$v){ + if(!is_array($v))return; + $db=Typecho_Db::get(); + $prefix=$db->getPrefix(); + $insert=$db->insert($prefix.'meting')->rows(array('id'=>sha1($k),'value'=>serialize($v),'date'=>time())); + return $db->query($insert); + } + + private function cacheRead($k,$t=60*60){ + $db=Typecho_Db::get(); + $prefix=$db->getPrefix(); + $query= $db->select('value','date')->from($prefix.'meting')->where('id=?',sha1($k)); + $result = $db->fetchAll($query); + if(sizeof($result)){ + if(time()-$result[0]['date']>$t){ + $delete = $db->delete('value','date')->from($prefix.'meting')->where('datequery($delete); + return false; + } + return unserialize($result[0]['value']); + } + else return false; + } + +} diff --git a/Plugin.php b/Plugin.php new file mode 100644 index 0000000..87c91e4 --- /dev/null +++ b/Plugin.php @@ -0,0 +1,293 @@ +filter =array('Meting_Plugin','playerFilter'); + Typecho_Plugin::factory('Widget_Abstract_Contents')->contentEx=array('Meting_Plugin','playerReplace'); + Typecho_Plugin::factory('Widget_Abstract_Contents')->excerptEx=array('Meting_Plugin','playerReplace'); + Typecho_Plugin::factory('Widget_Archive')->header=array('Meting_Plugin','header'); + Typecho_Plugin::factory('Widget_Archive')->footer=array('Meting_Plugin','footer'); + Typecho_Plugin::factory('admin/write-post.php')->bottom = array('Meting_Plugin', 'addButton'); + Typecho_Plugin::factory('admin/write-page.php')->bottom = array('Meting_Plugin', 'addButton'); + } + + /** + * 禁用插件方法,如果禁用失败,直接抛出异常 + * + * @static + * @access public + * @return void + * @throws Typecho_Plugin_Exception + */ + public static function deactivate(){ + Meting_Plugin::uninstall(); + Helper::removeRoute("Meting_Route"); + } + + /** + * 获取插件配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form 配置面板 + * @return void + */ + public static function config(Typecho_Widget_Helper_Form $form){ + $t = new Typecho_Widget_Helper_Form_Element_Text( + 'theme', null, '#e9e9e9', + _t('播放器颜色'), + _t('播放器默认的主题颜色,支持如 #372e21、#75c、red,该设定会被[Meting]标签中的theme属性覆盖,默认为 #e9e9e9')); + $form->addInput($t); + $t = new Typecho_Widget_Helper_Form_Element_Text( + 'height', null, '340px', + _t('播放器列表最大高度'), + _t('')); + $form->addInput($t); + $t = new Typecho_Widget_Helper_Form_Element_Radio( + 'autoplay', array('true'=>_t('是'),'false'=>_t('否')),'false', + _t('全局自动播放'), + _t('')); + $form->addInput($t); + $t = new Typecho_Widget_Helper_Form_Element_Radio( + 'mode', array('circulation'=>_t('循环'),'single'=>_t('单曲'),'order'=>_t('列表'),'random'=>_t('随机')),'circulation', + _t('全局播放模式'), + _t('')); + $form->addInput($t); + $t= new Typecho_Widget_Helper_Form_Element_Radio( + 'preload', array('auto'=>_t('自动'),'none'=>_t('不加载'),'metadata'=>_t('加载元数据')), 'auto', + _t('预加载属性'), + _t('')); + $form->addInput($t); + $t= new Typecho_Widget_Helper_Form_Element_Radio( + 'bitrate', array('128'=>_t('流畅品质'),'192'=>_t('清晰品质'),'320'=>_t('高品质')), '192', + _t('默认音质'), + _t('')); + $form->addInput($t); + } + + public static function personalConfig(Typecho_Widget_Helper_Form $form){} + + public static function render(){} + + /** + * 获取插件配置面板 + * + * @access public + * @param Typecho_Widget_Helper_Form $form 配置面板 + * @return void + */ + public static function header(){ + $dir=Helper::options()->pluginUrl.'/Meting/assets/'; + echo "\n\n"; + } + + public static function footer(){} + + public static function getPID(){ + return ++self::$PID; + } + + public static function playerFilter($data){ + return $data; + } + + public static function playerReplace($data,$widget,$last){ + $text=$last?:$data; + if($widget instanceof Widget_Archive){ + $data=$text; + $pattern=self::get_shortcode_regex(array('Meting')); + $text=preg_replace_callback("/$pattern/",array('Meting_Plugin','parseCallback'),$data); + } + return $text; + } + + public static function parseCallback($matches){ + $t=self::shortcode_parse_atts(htmlspecialchars_decode($matches[3])); + $setting=base64_encode(json_encode($t)); + + $matches[5]=htmlspecialchars_decode($matches[5]); + $pattern=self::get_shortcode_regex(array('Music')); + preg_match_all("/$pattern/",$matches[5],$all); + $data=array(); + foreach($all[3] as $vo){ + $t=self::shortcode_parse_atts(htmlspecialchars_decode($vo)); + if(!in_array($t['server'],['netease','tencent','xiami','baidu','kugou']))continue; + if(!in_array($t['type'],['search','album','playlist','artist','song']))continue; + $data[]=$t; + } + $id=self::getPID(); + $dir=Typecho_Common::url('MetingAPI',Helper::options()->index); + $data=base64_encode(json_encode($data)); + return << + + +EOF; + } + + public static function addButton(){ + $url=Typecho_Common::url('MetingAPI?do=parse',Helper::options()->index); + echo << +$(function() { + if($('#wmd-button-row').length>0)$('#wmd-button-row').append('
  • 音乐
  • '); + $(document).on('click', '#wmd-music-button', function() { + $('body').append( + '
    '+ + '
    '+ + '
    '+ + '
    '+ + '

    插入音乐

    '+ + '

    请在下方的输入框内输入要插入的音乐地址,如多个地址请用回车隔开'+ + '

    '+ + '
    '+ + '
    '+ + ''+ + ''+ + '
    '+ + '
    '+ + '
    '); + $('.wmd-prompt-dialog textarea').val('http://').select(); + }); + $(document).on('click','#cancel',function() { + $('#MetingPanel').remove(); + $('textarea').focus(); + }); + $(document).on('click','#ok',function() { + callback=$.ajax({ + type:'POST', + url:"{$url}", + data:{data:$('.wmd-prompt-dialog textarea').val()}, + async:false + }); + $('#MetingPanel').remove(); + myField = document.getElementById('text'); + if (document.selection) { + myField.focus(); + sel = document.selection.createRange(); + sel.text = callback.responseText; + myField.focus(); + } + else if (myField.selectionStart || myField.selectionStart == '0') { + var startPos = myField.selectionStart; + var endPos = myField.selectionEnd; + var cursorPos = startPos; + myField.value = myField.value.substring(0, startPos) + + callback.responseText + + myField.value.substring(endPos, myField.value.length); + cursorPos += callback.responseText.length; + myField.focus(); + myField.selectionStart = cursorPos; + myField.selectionEnd = cursorPos; + } + else{ + myField.value += callback.responseText; + myField.focus(); + } + }); +}); + +EOF; + } + + # https://github.com/WordPress/WordPress/blob/master/wp-includes/shortcodes.php#L508 + private static function shortcode_parse_atts($text) { + $atts = array(); + $pattern = '/([\w-]+)\s*=\s*"([^"]*)"(?:\s|$)|([\w-]+)\s*=\s*\'([^\']*)\'(?:\s|$)|([\w-]+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/'; + $text = preg_replace("/[\x{00a0}\x{200b}]+/u", " ", $text); + if ( preg_match_all($pattern, $text, $match, PREG_SET_ORDER) ) { + foreach ($match as $m) { + if (!empty($m[1])) + $atts[strtolower($m[1])] = stripcslashes($m[2]); + elseif (!empty($m[3])) + $atts[strtolower($m[3])] = stripcslashes($m[4]); + elseif (!empty($m[5])) + $atts[strtolower($m[5])] = stripcslashes($m[6]); + elseif (isset($m[7]) && strlen($m[7])) + $atts[] = stripcslashes($m[7]); + elseif (isset($m[8])) + $atts[] = stripcslashes($m[8]); + } + foreach( $atts as &$value ) { + if ( false !== strpos( $value, '<' ) ) { + if ( 1 !== preg_match( '/^[^<]*+(?:<[^>]*+>[^<]*+)*+$/', $value ) ) { + $value = ''; + } + } + } + } else { + $atts = ltrim($text); + } + return $atts; + } + + # https://github.com/WordPress/WordPress/blob/master/wp-includes/shortcodes.php#L254 + private static function get_shortcode_regex( $tagnames = null ) { + $tagregexp = join( '|', array_map('preg_quote', $tagnames) ); + return '\[(\[?)('.$tagregexp.')(?![\w-])([^\]\/]*(?:\/(?!\])[^\]\/]*)*?)(?:(\/)\]|\](?:([^\[]*+(?:\[(?!\/\2\])[^\[]*+)*+)\[\/\2\])?)(\]?)'; + } + + public static function install(){ + $db=Typecho_Db::get(); + $prefix=$db->getPrefix(); + $scripts=file_get_contents(__DIR__.'/include/install.sql'); + $scripts=str_replace('typecho_',$prefix,$scripts); + $scripts=explode(';', $scripts); + try{ + foreach($scripts as $script){ + $script=trim($script); + if($script){ + $db->query($script,Typecho_Db::WRITE); + } + } + }catch(Typecho_Db_Exception $e){ + $code=$e->getCode(); + if($code=='42S01')return; + throw new Typecho_Plugin_Exception('数据表建立失败,插件启用失败。错误号: '.$code); + } + } + + public static function uninstall(){ + $db=Typecho_Db::get(); + $prefix=$db->getPrefix(); + $scripts=file_get_contents(__DIR__.'/include/uninstall.sql'); + $scripts=str_replace('typecho_',$prefix,$scripts); + $scripts=explode(';', $scripts); + try{ + foreach($scripts as $script){ + $script=trim($script); + if($script){ + $db->query($script,Typecho_Db::WRITE); + } + } + }catch(Typecho_Db_Exception $e){ + $code=$e->getCode(); + if($code=='42S01')return; + throw new Typecho_Plugin_Exception('数据表清空失败,插件禁用失败。错误号: '.$code); + } + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..f6e81d0 --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +# Meting for Typecho +在 Typecho 中使用 APlayer 播放在线音乐吧~ +[在线演示](http://demo.i-meto.com) + +## 介绍 + 1. 支持国内五大音乐平台(网易云、QQ、虾米、百度、酷狗)的单曲/专辑/歌单播放 + 2. 简单快捷,复制音乐详情页面网址,后台自动生成播放代码 + 3. **支持不同音乐平台歌曲混合播放** + 4. 前端 Aplayer,后端 Meting 及时更新,保证兼容性及 API 高可用性 + +## 声明 +本作品仅供个人学习研究使用,请勿将其用作商业用途。 +**!!切勿使用本插件代码下载版权保护音乐!!** + +## 安装 + 1. 在本页面右上角点击 Download ZIP 下载压缩包 + 2. 上传到 /usr/plugins 目录 + 3. **修改文件夹名为 Meting** + 4. 后台启用插件 + +## 使用 +在文章编辑页面,点击编辑器上的 **音乐** 按钮,在弹出的窗口中输入音乐地址(见支持列表),最后点击确定即可 +**歌曲混合特性:** 允许添加多个音乐地址,每行一个,插件会自动将所有歌曲合并在同一个歌单进行播放。 + +## 支持列表 +网易云音乐 http://music.163.com + - 单曲 http://music.163.com/#/song?id=424474911 + - 专辑 http://music.163.com/#/album?id=34808540 + - 歌手 http://music.163.com/#/artist?id=3681 + - 歌单 http://music.163.com/#/playlist?id=436843836 + +QQ 音乐 http://y.qq.com +- 单曲 https://y.qq.com/portal/song/000jDQWP4JiB3y.html +- 专辑 https://y.qq.com/portal/album/003rytri2FHG3V.html +- 歌手 https://y.qq.com/portal/singer/003Nz2So3XXYek.html +- 歌单 https://y.qq.com/portal/playlist/1144188779.html + +虾米音乐 http://www.xiami.com or http://h.xiami.com +- 单曲 http://www.xiami.com/song/bf08DNT3035f +- 专辑 http://www.xiami.com/album/ddOZW6a10eb +- 歌手 http://www.xiami.com/artist/be6yda0f8 +- 歌单 http://www.xiami.com/collect/254478782 + +酷狗音乐 http://www.kugou.com +- 单曲 暂不支持直接解析,可直接修改短代码实现 +- 专辑 http://www.kugou.com/yy/album/single/1645030.html +- 歌手 http://www.kugou.com/yy/singer/home/3520.html +- 歌单 http://www.kugou.com/yy/special/single/119859.html + +百度音乐 http://music.baidu.com/ +- 单曲 http://music.baidu.com/song/268275324 +- 专辑 http://music.baidu.com/album/268275533 +- 歌手 http://music.baidu.com/artist/1219 +- 歌单 http://music.baidu.com/songlist/364201689 + +## FAQ +Q: 如何清除歌单、歌词缓存? +A: 为了减少服务器压力,插件设置对歌单、歌词数据进行缓存,缓存会根据时间周期自动更新管理,无需人工干预。**如果需要强制清除,可以通过禁用再启用插件实现,不影响文章中歌曲信息** +... + +更多问题可以通过 issue 页面提交,或者通过 Telegram、邮件向我反馈 + +## LICENSE +Meting-Typecho-Plugin is under the MIT license. diff --git a/assets/APlayer.min.js b/assets/APlayer.min.js new file mode 100644 index 0000000..6042ea9 --- /dev/null +++ b/assets/APlayer.min.js @@ -0,0 +1,3 @@ +!function(e,a){"object"==typeof exports&&"object"==typeof module?module.exports=a():"function"==typeof define&&define.amd?define("APlayer",[],a):"object"==typeof exports?exports.APlayer=a():e.APlayer=a()}(this,function(){return function(e){function a(l){if(t[l])return t[l].exports;var r=t[l]={exports:{},id:l,loaded:!1};return e[l].call(r.exports,r,r.exports,a),r.loaded=!0,r.exports}var t={};return a.m=e,a.c=t,a.p="",a(0)}([function(e,a,t){"use strict";function l(e,a){if(!(e instanceof a))throw new TypeError("Cannot call a class as a function")}var r=function(){function e(e,a){for(var t=0;t\n \n \n \n '},this.isMobile=/mobile/i.test(window.navigator.userAgent),this.isMobile&&(a.autoplay=!1);var s={element:document.getElementsByClassName("aplayer")[0],narrow:!1,autoplay:!1,mutex:!0,showlrc:0,theme:"#b7daff",mode:"circulation"};for(var p in s)s.hasOwnProperty(p)&&!a.hasOwnProperty(p)&&(a[p]=s[p]);if(this.playIndex="[object Array]"===Object.prototype.toString.call(a.music)?0:-1,this.option=a,this.audios=[],this.mode=a.mode,this.secondToTime=function(e){if(isNaN(e))return"00:00";var a=function(e){return e<10?"0"+e:""+e},t=parseInt(e/60),l=parseInt(e-60*t),r=parseInt(t/60),i=parseInt(e/60-60*parseInt(e/60/60));return e>=3600?a(r)+":"+a(i)+":"+a(l):a(t)+":"+a(l)},this.element=this.option.element,2===this.option.showlrc||this.option.showlrc===!0){this.savelrc=[];for(var c=0;c0?a:0,a=a<1?a:1,m[e+"Bar"].style[t]=100*a+"%"},this.updateLrc=function(){var e=arguments.length<=0||void 0===arguments[0]?n.audio.currentTime:arguments[0];if(n.lrcIndex>n.lrc.length-1||e=n.lrc[n.lrcIndex+1][0])for(var a=0;a=n.lrc[a][0]&&(!n.lrc[a+1]||e-1,this.music=this.multiple?this.option.music[this.playIndex]:this.option.music,this.option.showlrc&&this.element.classList.add("aplayer-withlrc"),this.option.music.length>1&&this.element.classList.add("aplayer-list"),this.multiple||"circulation"===this.mode||"order"===this.mode||(this.mode="circulation"),this.getRandomOrder();var y='\n
    \n
    \n \n
    \n
    \n
    \n
    \n \n \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n
    \n \n - 00:00 / 00:00\n \n
    \n \n
    \n
    \n
    \n
    \n
    \n
    \n \n "+(this.multiple?'":"")+"\n
    \n
    \n
    ");if(this.multiple){y+='\n
    \n
      ';for(var h=0;h\n \n '+(h+1)+'\n '+this.option.music[h].title+'\n '+this.option.music[h].author+"\n ";y+="\n
    \n
    "}this.element.innerHTML=y,this.element.offsetWidth<300&&(this.element.getElementsByClassName("aplayer-icon-mode")[0].style.display="none"),this.ptime=this.element.getElementsByClassName("aplayer-ptime")[0],this.element.getElementsByClassName("aplayer-info")[0].offsetWidth<200&&this.element.getElementsByClassName("aplayer-time")[0].classList.add("aplayer-time-narrow");var m={};m.barWrap=this.element.getElementsByClassName("aplayer-bar-wrap")[0],this.option.narrow&&this.element.classList.add("aplayer-narrow"),this.button=this.element.getElementsByClassName("aplayer-button")[0],this.button.addEventListener("click",function(e){n.toggle()}),this.multiple&&!function(){for(var e=n.element.getElementsByClassName("aplayer-list")[0].getElementsByTagName("li"),a=function(a){e[a].addEventListener("click",function(){var t=parseInt(e[a].getElementsByClassName("aplayer-list-index")[0].innerHTML)-1;t!==n.playIndex?(n.setMusic(t),n.play()):n.toggle()})},t=0;t0?l:0,l=l<1?l:1,n.updateBar("played",l,"width"),n.option.showlrc&&n.updateLrc(parseFloat(m.playedBar.style.width)/100*n.audio.duration),n.element.getElementsByClassName("aplayer-ptime")[0].innerHTML=n.secondToTime(l*n.audio.duration)},b=function E(){document.removeEventListener("mouseup",E),document.removeEventListener("mousemove",g),isNaN(n.audio.duration)?n.updateBar("played",0,"width"):(n.audio.currentTime=parseFloat(m.playedBar.style.width)/100*n.audio.duration,n.playedTime=setInterval(function(){n.updateBar("played",n.audio.currentTime/n.audio.duration,"width"),n.option.showlrc&&n.updateLrc(),n.element.getElementsByClassName("aplayer-ptime")[0].innerHTML=n.secondToTime(n.audio.currentTime),n.trigger("playing")},100))};f.addEventListener("mousedown",function(){v=m.barWrap.clientWidth,clearInterval(n.playedTime),document.addEventListener("mousemove",g),document.addEventListener("mouseup",b)}),m.volumeBar=this.element.getElementsByClassName("aplayer-volume")[0];var x=this.element.getElementsByClassName("aplayer-volume-bar")[0];this.volumeicon=this.element.getElementsByClassName("aplayer-time")[0].getElementsByTagName("button")[0];var w=35;this.element.getElementsByClassName("aplayer-volume-bar-wrap")[0].addEventListener("click",function(e){var a=e||window.event,t=(w-a.clientY+r(x))/w;t=t>0?t:0,t=t<1?t:1,n.volume(t)}),this.volumeicon.addEventListener("click",function(){n.audio.muted?(n.audio.muted=!1,n.volumeicon.className=1===n.audio.volume?"aplayer-icon aplayer-icon-volume-up":"aplayer-icon aplayer-icon-volume-down",1===n.audio.volume?(n.volumeicon.className="aplayer-icon aplayer-icon-volume-up",n.volumeicon.innerHTML=n.getSVG("volume-up")):(n.volumeicon.className="aplayer-icon aplayer-icon-volume-down",n.volumeicon.innerHTML=n.getSVG("volume-down")),n.updateBar("volume",n.audio.volume,"height")):(n.audio.muted=!0,n.volumeicon.className="aplayer-icon aplayer-icon-volume-off",n.volumeicon.innerHTML=n.getSVG("volume-off"),n.updateBar("volume",0,"height"))});var A=this.element.getElementsByClassName("aplayer-icon-mode")[0];A.addEventListener("click",function(){n.multiple?"random"===n.mode?n.mode="single":"single"===n.mode?n.mode="order":"order"===n.mode?n.mode="circulation":"circulation"===n.mode&&(n.mode="random"):"circulation"===n.mode?n.mode="order":n.mode="circulation",A.innerHTML=n.getSVG(n.mode),n.audio.loop=!(n.multiple||"order"===n.mode)}),this.multiple&&!function(){var e=n.element.getElementsByClassName("aplayer-list")[0];e.style.height=e.offsetHeight+"px",n.element.getElementsByClassName("aplayer-icon-menu")[0].addEventListener("click",function(){e.classList.contains("aplayer-list-hide")?e.classList.remove("aplayer-list-hide"):e.classList.add("aplayer-list-hide")})}(),"random"===this.mode?this.setMusic(this.randomOrder[0]):this.setMusic(0),i.push(this)}return r(e,[{key:"setMusic",value:function(e){var a=this;this.multiple&&"undefined"!=typeof e&&(this.playIndex=e);var t=this.playIndex;this.music=this.multiple?this.option.music[t]:this.option.music,this.music.pic&&(this.element.getElementsByClassName("aplayer-pic")[0].style.backgroundImage="url('"+this.music.pic+"')"),this.element.getElementsByClassName("aplayer-title")[0].innerHTML=this.music.title,this.element.getElementsByClassName("aplayer-author")[0].innerHTML=" - "+this.music.author,this.multiple&&(this.element.getElementsByClassName("aplayer-list-light")[0]&&this.element.getElementsByClassName("aplayer-list-light")[0].classList.remove("aplayer-list-light"),this.element.getElementsByClassName("aplayer-list")[0].getElementsByTagName("li")[t].classList.add("aplayer-list-light")),this.audio&&(this.pause(),this.audio.currentTime=0),this.multiple&&(this.element.getElementsByClassName("aplayer-list")[0].scrollTop=33*t),this.multiple&&!this.audios[t]||this.playIndex===-1?(this.audio=document.createElement("audio"),this.audio.src=this.music.url,this.audio.preload=this.option.preload?this.option.preload:"auto",this.audio.addEventListener("play",function(){if(a.button.classList.contains("aplayer-play")){if(a.button.classList.remove("aplayer-play"),a.button.classList.add("aplayer-pause"),a.button.innerHTML="",setTimeout(function(){a.button.innerHTML='\n "},100),a.option.mutex)for(var e=0;e'+a.getSVG("play")+" "},100),clearInterval(a.playedTime),a.trigger("pause"))}),this.audio.addEventListener("durationchange",function(){1!==a.audio.duration&&(a.element.getElementsByClassName("aplayer-dtime")[0].innerHTML=a.secondToTime(a.audio.duration))}),this.audio.addEventListener("progress",function(){var e=a.audio.buffered.length?a.audio.buffered.end(a.audio.buffered.length-1)/a.audio.duration:0;a.updateBar("loaded",e,"width")}),this.audio.addEventListener("error",function(){a.element.getElementsByClassName("aplayer-author")[0].innerHTML=" - Error happens ╥﹏╥",a.trigger("pause")}),this.audio.addEventListener("canplay",function(){a.trigger("canplay")}),this.ended=!1,this.multiple?this.audio.addEventListener("ended",function(){return a.isMobile?(a.ended=!0,void a.pause()):void(0!==a.audio.currentTime&&("random"===a.mode?a.setMusic(a.nextRandomNum()):"single"===a.mode?a.setMusic(a.playIndex):"order"===a.mode?a.playIndex=200&&r.status<300||304===r.status){t=r.responseText,a.lrcs[e]=l(t),a.lrc=a.lrcs[e];var i="";a.lrcContents=a.element.getElementsByClassName("aplayer-lrc-contents")[0];for(var n=0;n"+a.lrc[n][1]+"

    ";a.lrcContents.innerHTML=i,a.lrcIndex||(a.lrcIndex=0),a.lrcContents.getElementsByTagName("p")[0].classList.add("aplayer-lrc-current"),a.lrcContents.style.transform="translateY(0px)",a.lrcContents.style.webkitTransform="translateY(0px)"}else console.log("Request was unsuccessful: "+r.status)};var i=void 0;i=a.multiple?a.option.music[e].lrc:a.option.music.lrc,r.open("get",i,!0),r.send(null)}(),t?a.lrcs[e]=l(t):a.lrcs[e]=[["00:00","Loading"]]}(),a.lrc=a.lrcs[e];var r="";a.lrcContents=a.element.getElementsByClassName("aplayer-lrc-contents")[0];for(var i=0;i"+a.lrc[i][1]+"

    ";a.lrcContents.innerHTML=r,a.lrcIndex||(a.lrcIndex=0),a.lrcContents.getElementsByTagName("p")[0].classList.add("aplayer-lrc-current"),a.lrcContents.style.transform="translateY(0px)",a.lrcContents.style.webkitTransform="translateY(0px)"}(),1!==this.audio.duration&&(this.element.getElementsByClassName("aplayer-dtime")[0].innerHTML=this.audio.duration?this.secondToTime(this.audio.duration):"00:00"),this.option.autoplay&&!this.isMobile&&this.play(),this.option.autoplay=!0,this.isMobile&&this.pause()}},{key:"play",value:function(e){"[object Number]"===Object.prototype.toString.call(e)&&(this.audio.currentTime=e),this.audio.paused&&this.audio.play()}},{key:"pause",value:function(){this.audio.paused||this.audio.pause()}},{key:"volume",value:function(e){this.updateBar("volume",e,"height"),this.audio.volume=e,this.audio.muted&&(this.audio.muted=!1),1===e?(this.volumeicon.className="aplayer-icon aplayer-icon-volume-up",this.volumeicon.innerHTML=this.getSVG("volume-up")):(this.volumeicon.className="aplayer-icon aplayer-icon-volume-down",this.volumeicon.innerHTML=this.getSVG("volume-down"))}},{key:"on",value:function(e,a){"function"==typeof a&&this.event[e].push(a)}},{key:"toggle",value:function(){this.button.classList.contains("aplayer-play")?this.play():this.button.classList.contains("aplayer-pause")&&this.pause()}},{key:"getRandomOrder",value:function(){function e(e,a){return null==a&&(a=e,e=0),e+Math.floor(Math.random()*(a-e+1))}function a(a){for(var t,l=a.length,r=new Array(l),i=0;i=0&&b.splice(a,1)}function o(e){var a=document.createElement("style");return a.type="text/css",i(e,a),a}function s(e){var a=document.createElement("link");return a.rel="stylesheet",i(e,a),a}function p(e,a){var t,l,r;if(a.singleton){var i=g++;t=v||(v=o(a)),l=c.bind(null,t,i,!1),r=c.bind(null,t,i,!0)}else e.sourceMap&&"function"==typeof URL&&"function"==typeof URL.createObjectURL&&"function"==typeof URL.revokeObjectURL&&"function"==typeof Blob&&"function"==typeof btoa?(t=s(a),l=u.bind(null,t),r=function(){n(t),t.href&&URL.revokeObjectURL(t.href)}):(t=o(a),l=d.bind(null,t),r=function(){n(t)});return l(e),function(a){if(a){if(a.css===e.css&&a.media===e.media&&a.sourceMap===e.sourceMap)return;l(e=a)}else r()}}function c(e,a,t,l){var r=t?"":l.css;if(e.styleSheet)e.styleSheet.cssText=x(a,r);else{var i=document.createTextNode(r),n=e.childNodes;n[a]&&e.removeChild(n[a]),n.length?e.insertBefore(i,n[a]):e.appendChild(i)}}function d(e,a){var t=a.css,l=a.media;if(l&&e.setAttribute("media",l),e.styleSheet)e.styleSheet.cssText=t;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(t))}}function u(e,a){var t=a.css,l=a.sourceMap;l&&(t+="\n/*# sourceMappingURL=data:application/json;base64,"+btoa(unescape(encodeURIComponent(JSON.stringify(l))))+" */");var r=new Blob([t],{type:"text/css"}),i=e.href;e.href=URL.createObjectURL(r),i&&URL.revokeObjectURL(i)}var y={},h=function(e){var a;return function(){return"undefined"==typeof a&&(a=e.apply(this,arguments)),a}},m=h(function(){return/msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase())}),f=h(function(){return document.head||document.getElementsByTagName("head")[0]}),v=null,g=0,b=[];e.exports=function(e,a){a=a||{},"undefined"==typeof a.singleton&&(a.singleton=m()),"undefined"==typeof a.insertAt&&(a.insertAt="bottom");var t=r(e);return l(t,a),function(e){for(var i=[],n=0;n + * Released under the MIT license + */ + +class Meting +{ + protected $_SITE; + protected $_TEMP; + protected $_FORMAT = false; + + function __construct($v='netease'){ + self::site($v); + } + + public function site($v){ + $this->_SITE=$v; + } + + public function format($v = true){ + $this->_FORMAT=$v; + return $this; + } + + private function curl($API){ + if(isset($API['encode']))$API=call_user_func_array(array($this,$API['encode']),array($API)); + + $BASE=self::curlset(); + $curl=curl_init(); + if($API['method']=='POST'){ + if(is_array($API['body']))$API['body']=http_build_query($API['body']); + curl_setopt($curl,CURLOPT_URL,$API['url']); + curl_setopt($curl,CURLOPT_POSTFIELDS,$API['body']); + curl_setopt($curl,CURLOPT_POST,1); + } + elseif($API['method']=='GET'){ + if(isset($API['body']))$API['url']=$API['url'].'?'.http_build_query($API['body']); + curl_setopt($curl,CURLOPT_URL,$API['url']); + } + curl_setopt($curl,CURLOPT_RETURNTRANSFER,1); + curl_setopt($curl,CURLOPT_CONNECTTIMEOUT,10); + curl_setopt($curl,CURLOPT_TIMEOUT,10); + curl_setopt($curl,CURLOPT_COOKIE,$BASE['cookie']); + curl_setopt($curl,CURLOPT_REFERER,$BASE['referer']); + curl_setopt($curl,CURLOPT_USERAGENT,$BASE['useragent']); + $result=curl_exec($curl); + curl_close($curl); + + if(isset($API['decode']))$result=call_user_func_array(array($this,$API['decode']),array($result)); + + if($this->_FORMAT){ + if(isset($API['format'])){ + $result=json_decode($result,1); + $result=self::clean($result,$API['format']); + $result=json_encode($result); + } + $this->_FORMAT=false; + } + return $result; + } + + private function pickup($array,$rule){ + $t=explode('#',$rule); + foreach($t as $vo){ + if($array==null)return null; + $array=$array[$vo]; + } + return $array; + } + + private function clean($raw,$rule){ + if(!empty($rule))$raw=self::pickup($raw,$rule); + if($raw==null)$raw=array(); + elseif(!isset($raw[0]))$raw=array($raw); + $result=array_map(array($this,'format_'.$this->_SITE),$raw); + return $result; + } + + public function search($keyword,$page=1,$limit=30){ + $API=array( + 'netease' => array( + 'method' => 'POST', + 'url' => 'http://music.163.com/api/linux/forward', + 'body' => array( + 'method' => 'POST', + 'params' => array( + 's' => $keyword, + 'type' => 1, + 'limit' => $limit, + 'total' => 'true', + 'offset' => $page-1, + ), + 'url' => 'http://music.163.com/api/cloudsearch/pc', + ), + 'encode' => 'netease_AESECB', + 'format' => 'result#songs', + ), + 'tencent' => array( + 'method' => 'GET', + 'url' => 'https://c.y.qq.com/soso/fcgi-bin/search_cp', + 'body' => array( + 'p' => $page, + 'n' => $limit, + 'w' => $keyword, + 'aggr' => 1, + 'lossless' => 1, + 'cr' => 1, + ), + 'decode' => 'jsonp2json', + 'format' => 'data#song#list', + ), + 'xiami' => array( + 'method' => 'GET', + 'url' => 'http://api.xiami.com/web', + 'body' => array( + 'v' => '2.0', + 'app_key' => '1', + 'key' => $keyword, + 'page' => $page, + 'limit' => $limit, + 'r' => 'search/songs', + ), + 'format' => 'data#songs', + ), + 'kugou' => array( + 'method' => 'GET', + 'url' => 'http://mobilecdn.kugou.com/api/v3/search/song', + 'body' => array( + 'iscorrect' => 1, + 'pagesize' => $limit, + 'plat' => 20, + 'sver' => 3, + 'showtype' => 14, + 'page' => $page, + 'keyword' => $keyword, + ), + 'format' => 'data#info', + ), + 'baidu' => array( + 'method' => 'GET', + 'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting', + 'body' => array( + 'method' => 'baidu.ting.search.merge', + 'isNew' => 1, + 'query' => $keyword, + 'page_size' => $limit, + 'page_no' => $page, + 'type' => 0, + 'format' => 'json', + 'from' => 'ios', + 'channel' => '(null)', + 'cuid' => 'appstore', + 'from' => 'ios', + 'version' => '5.9.5', + ), + 'format' => 'result#song_info#song_list', + ), + ); + return self::curl($API[$this->_SITE]); + } + + public function song($id){ + $API=array( + 'netease' => array( + 'method' => 'POST', + 'url' => 'http://music.163.com/api/linux/forward', + 'body' => array( + 'method' => 'POST', + 'params' => array( + 'c' => '[{"id":'.$id.'}]', + ), + 'url' => 'http://music.163.com/api/v3/song/detail', + ), + 'encode' => 'netease_AESECB', + 'format' => 'songs', + ), + 'tencent'=>array( + 'method' => 'GET', + 'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_play_single_song.fcg', + 'body' => array( + 'songmid' => $id, + 'format' => 'json', + ), + 'decode' => 'tencent_singlesong', + 'format' => 'data', + ), + 'xiami'=>array( + 'method' => 'GET', + 'url' => 'http://api.xiami.com/web', + 'body' => array( + 'v' => '2.0', + 'app_key' => '1', + 'id' => $id, + 'r' => 'song/detail', + ), + 'format' => 'data#song', + ), + 'kugou'=>array( + 'method' => 'POST', + 'url' => 'http://m.kugou.com/app/i/getSongInfo.php?', + 'body' => array( + "cmd" => "playInfo", + "hash" => $id, + "from" => "mkugou", + ), + 'format' => '', + ), + 'baidu' => array( + 'method' => 'GET', + 'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting', + 'body' => array( + 'method' => 'baidu.ting.song.play', + 'songid' => $id, + 'format' => 'json', + 'from' => 'ios', + 'channel' => '(null)', + 'cuid' => 'appstore', + 'from' => 'ios', + 'version' => '5.9.5', + ), + 'format' => 'songinfo', + ), + ); + return self::curl($API[$this->_SITE]); + } + + public function album($id){ + $API=array( + 'netease'=>array( + 'method' => 'POST', + 'url' => 'http://music.163.com/api/linux/forward', + 'body' => array( + 'method' => 'GET', + 'params' => array( + 'id' => $id, + ), + 'url' => 'http://music.163.com/api/v1/album/'.$id, + ), + 'encode' => 'netease_AESECB', + 'format' => 'songs', + ), + 'tencent'=>array( + 'method' => 'GET', + 'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_album_info_cp.fcg', + 'body' => array( + 'albummid' => $id, + ), + 'format' => 'data#list', + ), + 'xiami'=>array( + 'method' => 'GET', + 'url' => 'http://api.xiami.com/web', + 'body' => array( + 'v' => '2.0', + 'app_key' => '1', + 'id' => $id, + 'r' => 'album/detail', + ), + 'format' => 'data#songs', + ), + 'kugou'=>array( + 'method' => 'GET', + 'url' => 'http://mobilecdn.kugou.com/api/v3/album/song', + 'body' => array( + 'albumid' => $id, + 'plat' => 2, + 'page' => 1, + 'pagesize' => -1, + 'version' => 8400, + ), + 'format' => 'data#info', + ), + 'baidu' => array( + 'method' => 'GET', + 'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting', + 'body' => array( + 'method' => 'baidu.ting.album.getAlbumInfo', + 'album_id' => $id, + 'format' => 'json', + 'from' => 'ios', + 'channel' => '(null)', + 'cuid' => 'appstore', + 'from' => 'ios', + 'version' => '5.9.5', + ), + 'format' => 'songlist', + ), + ); + return self::curl($API[$this->_SITE]); + } + + public function artist($id,$limit=50){ + $API=array( + 'netease'=>array( + 'method' => 'POST', + 'url' => 'http://music.163.com/api/linux/forward', + 'body' => array( + 'method' => 'GET', + 'params' => array( + 'top' => $limit, + "id" => $id, + "ext" => "true", + ), + 'url' => 'http://music.163.com/api/v1/artist/'.$id, + ), + 'encode' => 'netease_AESECB', + 'format' => 'hotSongs', + ), + 'tencent'=>array( + 'method' => 'GET', + 'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg', + 'body' => array( + 'singermid' => $id, + 'begin' => 0, + 'num' => $limit, + ), + 'format' => 'data#list', + ), + 'xiami' => array( + 'method' => 'GET', + 'url' => 'http://api.xiami.com/web', + 'body' => array( + 'v' => '2.0', + 'app_key' => '1', + 'id' => $id, + 'limit' => $limit, + 'page' => 1, + 'r' => 'artist/hot-songs', + ), + 'format' => 'data', + ), + 'kugou'=>array( + 'method' => 'GET', + 'url' => 'http://mobilecdn.kugou.com/api/v3/singer/song', + 'body' => array( + 'singerid' => $id, + 'page' => 1, + 'plat' => 0, + 'pagesize' => $limit, + 'version' => 8400, + ), + 'format' => 'data#info', + ), + 'baidu' => array( + 'method' => 'GET', + 'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting', + 'body' => array( + 'method' => 'baidu.ting.artist.getSongList', + 'tinguid' => $id, + 'limits' => $limit, + 'format' => 'json', + 'from' => 'ios', + 'channel' => '(null)', + 'cuid' => 'appstore', + 'from' => 'ios', + 'version' => '5.9.5', + ), + 'format' => 'songlist', + ), + ); + return self::curl($API[$this->_SITE]); + } + + public function playlist($id){ + $API=array( + 'netease'=>array( + 'method' => 'POST', + 'url' => 'http://music.163.com/api/linux/forward', + 'body' => array( + 'method' => 'POST', + 'params' => array( + 'id' => $id, + "n" => 1000, + ), + 'url' => 'http://music.163.com/api/v3/playlist/detail', + ), + 'encode' => 'netease_AESECB', + 'format' => 'playlist#tracks', + ), + 'tencent'=>array( + 'method' => 'GET', + 'url' => 'https://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg', + 'body' => array( + 'disstid' => $id, + 'utf8' => 1, + 'type' => 1, + ), + 'decode' => 'jsonp2json', + 'format' => 'cdlist#0#songlist', + ), + 'xiami'=>array( + 'method' => 'GET', + 'url' => 'http://api.xiami.com/web', + 'body' => array( + 'v' => '2.0', + 'app_key' => '1', + 'id' => $id, + 'r' => 'collect/detail', + ), + 'format' => 'data#songs', + ), + 'kugou'=>array( + 'method' => 'GET', + 'url' => 'http://mobilecdn.kugou.com/api/v3/special/song', + 'body' => array( + 'specialid' => $id, + 'page' => 1, + 'plat' => 2, + 'pagesize' => -1, + 'version' => 8400, + ), + 'format' => 'data#info', + ), + 'baidu' => array( + 'method' => 'GET', + 'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting', + 'body' => array( + 'method' => 'baidu.ting.diy.gedanInfo', + 'listid' => $id, + 'format' => 'json', + 'from' => 'ios', + 'channel' => '(null)', + 'cuid' => 'appstore', + 'from' => 'ios', + 'version' => '5.9.5', + ), + 'format' => 'content', + ), + ); + return self::curl($API[$this->_SITE]); + } + + public function url($id,$br=320){ + $API=array( + 'netease'=>array( + 'method' => 'POST', + 'url' => 'http://music.163.com/api/linux/forward', + 'body' => array( + 'method' => 'POST', + 'params' => array( + 'ids' => array($id), + 'br' => $br*1000, + ), + 'url' => 'http://music.163.com/api/song/enhance/player/url', + ), + 'encode' => 'netease_AESECB', + 'decode' => 'netease_url', + ), + 'tencent'=>array( + 'method' => 'GET', + 'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_play_single_song.fcg', + 'body' => array( + 'songmid' => $id, + 'format' => 'json', + ), + 'decode' => 'tencent_url', + ), + 'xiami'=>array( + 'method' => 'GET', + 'url' => 'http://www.xiami.com/song/playlist/id/'.$id.'/object_name/default/object_id/0/cat/json', + 'body' => array(), + 'decode' => 'xiami_url', + ), + 'kugou'=>array( + 'method' => 'POST', + 'url' => 'http://media.store.kugou.com/v1/get_res_privilege', + 'body' => array( + "relate" => 1, + "userid" => 0, + "vip" => 0, + "appid" => 1390, + "token" => "", + "behavior" => "download", + "clientver" => "1", + "resource" => array(array( + "id" => 0, + "type" => "audio", + "hash" => $id, + )), + ), + 'encode' => 'kugou_json', + 'decode' => 'kugou_url', + ), + 'baidu' => array( + 'method' => 'GET', + 'url' => 'http://music.baidu.com/data/music/fmlink', + 'body' => array( + 'songIds' => $id, + 'rate' => $br, + 'type' => 'mp3', + ), + 'decode' => 'baidu_url', + ), + ); + $this->_temp['br']=$br; + return self::curl($API[$this->_SITE]); + } + + public function lyric($id){ + $API=array( + 'netease'=>array( + 'method' => 'POST', + 'url' => 'http://music.163.com/api/linux/forward', + 'body' => array( + 'method' => 'POST', + 'params' => array( + 'id' => $id, + 'os' => 'linux', + 'lv' => -1, + 'kv' => -1, + 'tv' => -1, + ), + 'url' => 'http://music.163.com/api/song/lyric', + ), + 'encode' => 'netease_AESECB', + ), + 'tencent'=>array( + 'method' => 'GET', + 'url' => 'https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric.fcg', + 'body' => array( + 'songmid' => $id, + 'nobase64' => 0, + ), + 'decode' => 'jsonp2json', + ), + 'xiami'=>array( + 'method' => 'GET', + 'url' => 'http://api.xiami.com/web', + 'body' => array( + 'v' => '2.0', + 'app_key' => '1', + 'id' => $id, + 'r' => 'song/detail', + ), + 'decode' => 'xiami_lyric', + ), + 'kugou'=>array( + 'method' => 'GET', + 'url' => 'http://m.kugou.com/app/i/krc.php', + 'body' => array( + 'keyword' => '%20-%20', + 'timelength' => 1000000, + 'cmd' => 100, + 'hash' => $id, + ), + 'decode' => 'kugou_lyric' + ), + 'baidu' => array( + 'method' => 'GET', + 'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting', + 'body' => array( + 'method' => 'baidu.ting.song.lry', + 'songid' => $id, + 'format' => 'json', + 'from' => 'ios', + 'channel' => '(null)', + 'cuid' => 'appstore', + 'from' => 'ios', + 'version' => '5.9.5', + ), + ), + ); + return self::curl($API[$this->_SITE]); + } + + public function pic($id,$size=300){ + switch($this->_SITE){ + case 'netease': + $url='https://p3.music.126.net/'.self::netease_pickey($id).'/'.$id.'.jpg?param='.$size.'z'.$size.'&quality=100'; + break; + case 'tencent': + $url='https://y.gtimg.cn/music/photo_new/T002R'.$size.'x'.$size.'M000'.$id.'.jpg?max_age=2592000'; + break; + case 'xiami': + $data=$this->format(false)->song($id); + $url=json_decode($data,1)['data']['song']['logo']; + $url=str_replace(['_1.','http:','img.'],['.','https:','pic.'],$url).'@'.$size.'h_'.$size.'w_90q_1c.webp'; + break; + case 'kugou': + $API=array( + 'method' =>'GET', + 'url' => 'http://tools.mobile.kugou.com/api/v1/singer_header/get_by_hash', + 'body' => array( + 'hash' => $id, + 'size' => 400, + 'format' => 'json', + ), + ); + $data=$this->curl($API); + $url=json_decode($data,1)['url']; + break; + case 'baidu': + $data=self::song($id); + $data=json_decode($data,1); + $url=$data['songinfo']['pic_big']?:$data['songinfo']['pic_small']; + } + $arr=array('url'=>$url); + return json_encode($arr); + } + + private function curlset(){ + $BASE=array( + 'netease'=>array( + 'referer' => 'http://music.163.com/', + 'cookie' => 'os=linux; appver=1.0.0.1026; osver=Ubuntu%2016.10; MUSIC_U=78d411095f4b022667bc8ec49e9a44cca088df057d987f5feaf066d37458e41c4a7d9447977352cf27ea9fee03f6ec4441049cea1c6bb9b6; __remember_me=true', + 'useragent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.30 Safari/537.36', + ), + 'tencent'=>array( + 'referer' => 'http://y.qq.com/portal/player.html', + 'cookie' => 'qqmusic_uin=12345678; qqmusic_key=12345678; qqmusic_fromtag=30; ts_last=y.qq.com/portal/player.html;', + 'useragent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.30 Safari/537.36', + ), + 'xiami'=>array( + 'referer' => 'http://h.xiami.com/', + 'cookie' => 'user_from=2;XMPLAYER_addSongsToggler=0;XMPLAYER_isOpen=0;_xiamitoken=123456789;', + 'useragent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.30 Safari/537.36', + ), + 'kugou'=>array( + 'referer' => 'http://www.kugou.com/webkugouplayer/flash/webKugou.swf', + 'cookie' => '_WCMID=123456789', + 'useragent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.30 Safari/537.36', + ), + 'baidu'=>array( + 'referer' => 'http://ting.baidu.com/', + 'cookie' => 'BAIDUID=123456789', + 'useragent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.30 Safari/537.36', + ), + ); + return $BASE[$this->_SITE]; + } + + /** + * 乱七八糟的函数,加密解密... + * 正在努力重构这些代码 TAT + */ + private function netease_AESECB($API){ + $KEY='7246674226682325323F5E6544673A51'; + $body=json_encode($API['body']); + $body=openssl_encrypt($body,'aes-128-ecb',hex2bin($KEY)); + $body=strtoupper(bin2hex(base64_decode($body))); + + $API['body']=array( + 'eparams'=>$body, + ); + return $API; + } + private function kugou_json($API){ + $API['body']=json_encode($API['body']); + return $API; + } + private function jsonp2json($jsonp){ + if($jsonp[0] !== '[' && $jsonp[0] !== '{') { + $jsonp = substr($jsonp, strpos($jsonp, '(')); + } + return trim($jsonp,'();'); + } + private function tencent_singlesong($result){ + $result=json_decode($result,1); + $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; + return json_encode($result); + } + private function netease_pickey($id){ + $magic=str_split('3go8&$8*3*3h0k(2)2'); + $song_id=str_split($id); + for($i=0;$i str_replace('http:','https:',$data['data'][0]['url']), + 'br' => $data['data'][0]['br']/1000, + ); + return json_encode($url); + } + private function tencent_url($result){ + $data=json_decode($result,1); + $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, + ), + 'decode' => 'jsonp2json', + ); + $KEY=json_decode($this->curl($API),1)['key']; + + $type=array( + 'size_320mp3'=>array(320,'M800','mp3'), + 'size_128mp3'=>array(128,'M500','mp3'), + 'size_96aac'=>array(96,'C400','m4a'), + 'size_48aac'=>array(48,'C200','m4a'), + ); + foreach($type as $key=>$vo){ + if($data['data'][0]['file'][$key]&&$vo[0]<=$this->_temp['br']){ + $url=array( + 'url' => 'http://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], + ); + break; + } + } + return json_encode($url); + } + private function xiami_url($result){ + $data=json_decode($result,1); + $max=0; + foreach($data['data']['trackList'][0]['allAudios'] as $vo){ + if($vo['rate']<=$this->_temp['br']&&$vo['rate']>$max){ + $max=$vo['rate']; + $url=array( + 'url' => str_replace('http:','https:',$vo['filePath']), + 'br' => $vo['rate'], + ); + } + } + return json_encode($url); + } + private function kugou_url($result){ + $data=json_decode($result,1); + + $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( + 'method' => 'GET', + 'url' => 'http://trackercdn.kugou.com/i/v2/', + 'body' => array( + 'hash' => $vo['hash'], + 'key' => md5($vo['hash'].'kgcloudv2'), + 'pid' => 1, + 'behavior' => 'play', + 'cmd' => '23', + 'version' => 8400, + ), + ); + $t=json_decode($this->curl($API),1); + if(isset($t['url'])){ + $max=$t['bitRate']/1000; + $url=array( + 'url' => $t['url'], + 'br' => $t['bitRate']/1000, + ); + } + } + } + return json_encode($url); + } + private function baidu_url($result){ + $data=json_decode($result,1); + $url=array( + 'url' => $data['data']['songList'][0]['songLink'], + 'br' => $data['data']['songList'][0]['rate'], + ); + $url['url']=str_replace('http://yinyueshiting.baidu.com','https://gss0.bdstatic.com/y0s1hSulBw92lNKgpU_Z2jR7b2w6buu',$url['url']); + return json_encode($url); + } + /** + * 歌词处理模块 + * 用于规范化歌词输出 + */ + private function xiami_lyric($result){ + $result=json_decode($result,1); + $API=array( + 'method' => 'GET', + 'url' => $result['data']['song']['lyric'], + ); + $data=$this->curl($API); + $data=preg_replace('/<\d{1,8}>/','',$data); + $arr=array( + 'lyric' => $data, + ); + return json_encode($arr); + } + private function kugou_lyric($result){ + $arr=array( + 'lyric' => $result, + ); + return json_encode($arr); + } + /** + * Format - 规范化函数 + * 用于统一返回的参数,可用 ->format() 一次性开关开启 + */ + private function format_netease($data){ + $result=array( + 'id' => $data['id'], + 'name' => $data['name'], + 'artist' => array(), + 'pic_id' => $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]; + } + foreach($data['ar'] as $vo)$result['artist'][]=$vo['name']; + return $result; + } + private function format_tencent($data){ + if(isset($data['musicData']))$data=$data['musicData']; + $result=array( + 'id' => $data['songmid'], + 'name' => $data['songname'], + 'artist' => array(), + 'pic_id' => $data['albummid'], + 'url_id' => $data['songmid'], + 'lyric_id' => $data['songmid'], + 'source' => 'tencent', + ); + foreach($data['singer'] as $vo)$result['artist'][]=$vo['name']; + return $result; + } + private function format_xiami($data){ + $result=array( + 'id' => $data['song_id'], + 'name' => $data['song_name'], + 'artist' => explode(';',$data['singers']?:$data['artist_name']), + 'pic_id' => $data['song_id'], + 'url_id' => $data['song_id'], + 'lyric_id' => $data['song_id'], + 'source' => 'xiami', + ); + return $result; + } + private function format_kugou($data){ + $result=array( + 'id' => $data['hash'], + 'name' => "", + 'artist' => array(), + 'url_id' => $data['hash'], + 'pic_id' => $data['hash'], + 'lyric_id' => $data['hash'], + 'source' => 'kugou', + ); + list($result['artist'],$result['name'])=explode(' - ',$data['filename']?:$data['fileName']); + $result['artist']=explode('、',$result['artist']); + return $result; + } + private function format_baidu($data){ + $result=array( + 'id' => $data['song_id'], + 'name' => $data['title'], + 'artist' => explode(',',$data['author']), + 'pic_id' => $data['song_id'], + 'url_id' => $data['song_id'], + 'lyric_id' => $data['song_id'], + 'source' => 'baidu', + ); + return $result; + } +} diff --git a/include/install.sql b/include/install.sql new file mode 100644 index 0000000..2eb16ea --- /dev/null +++ b/include/install.sql @@ -0,0 +1,6 @@ +CREATE TABLE typecho_meting ( + id binary(40) NOT NULL, + value TEXT NOT NULL, + date int(11) NOT NULL +)ENGINE=MYISAM DEFAULT CHARSET=utf8; +ALTER TABLE typecho_meting ADD UNIQUE(id); diff --git a/include/uninstall.sql b/include/uninstall.sql new file mode 100644 index 0000000..154b44e --- /dev/null +++ b/include/uninstall.sql @@ -0,0 +1 @@ +TRUNCATE typecho_meting;