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('date',time()-$t);
+ $db->query($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 - 00:00 / 00:00\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;