🔥 2.0.0, 架构重写,支持自定义歌曲

- 重写前端逻辑
 - 支持自定义 API
 - 支持自定义歌曲
 - 去除 jQuery 依赖
 - 升级 Meting 库
This commit is contained in:
METO 2017-09-20 23:21:31 +08:00
parent 9ca611c898
commit f1200960a5
6 changed files with 280 additions and 301 deletions

View File

@ -6,66 +6,154 @@ class Meting_Action extends Typecho_Widget implements Widget_Interface_Do {
public function execute(){}
public function action(){
$this->on($this->request->is('do=musicjs'))->musicjs();
$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();
$this->on($this->request->isGet())->api();
}
private function check($a,$b){
if(!in_array($a,array('netease','tencent','baidu','xiami','kugou')))return false;
if(!in_array($b,array('song','album','search','artist','playlist','lrc','url','pic')))return false;
return true;
}
private function api(){
$this->filterReferer();
$server=$this->request->get('server');
$type=$this->request->get('type');
$id=$this->request->get('id');
if(!$this->check($server,$type)||empty($id))die('[]');
if(!extension_loaded('Meting'))include_once 'include/Meting.php';
$api=new \Metowolf\Meting($server);
$api->format(true);
$EID=md5($server.'/'.$type.'/'.$id);
$salt=Typecho_Widget::widget('Widget_Options')->plugin('Meting')->salt;
if(in_array($type,array('lrc','pic','url'))){
$auth1=md5($salt.$id.$salt);
$auth2=$this->request->get('auth');
if(strcmp($auth1,$auth2)){
http_response_code(403);
die();
}
}
if($type=='lrc'){
$data=$this->cacheRead($EID,60*60*24);
if(empty($data)){
$data=$api->lyric($id);
$this->cacheWrite($EID,$data);
}
$data=json_decode($data,true);
header("Content-Type: application/javascript");
echo $data['lyric'];
}
elseif($type=='pic'){
$data=$this->cacheRead($EID,60*60*24);
if(empty($data)){
$data=$api->pic($id,90);
$this->cacheWrite($EID,$data);
}
$data=json_decode($data,true);
$this->response->redirect($data['url']);
}
elseif($type=='url'){
$data=$this->cacheRead($EID,60*15);
if(empty($data)){
$rate=Typecho_Widget::widget('Widget_Options')->plugin('Meting')->bitrate;
$cookie=Typecho_Widget::widget('Widget_Options')->plugin('Meting')->cookie;
if($server=='netease')$api->cookie($cookie);
$data=$api->url($id,$rate);
$this->cacheWrite($EID,$data);
}
$data=json_decode($data,true);
$url=$data['url'];
$url=str_replace('http://m8','https://m8',$url);
$url=str_replace('http://m7','https://m8',$url);
$url=str_replace('http://m10','https://m10',$url);
if(empty($url))$url='https://api.i-meto.com/Public/music/empty.mp3';
$this->response->redirect($url);
}
else{
$data=$this->cacheRead($EID,60*60*2);
if(empty($data)){
$data=$api->$type($id);
$this->cacheWrite($EID,$data);
}
$data=json_decode($data,1);
$url=Typecho_Common::url('action/metingapi',Helper::options()->index);
$music=array();
foreach($data as $vo){
$music[]=array(
'title' => $vo['name'],
'author' => implode(' / ',$vo['artist']),
'url' => $url.'?server='.$vo['source'].'&type=url&id='.$vo['url_id'].'&auth='.md5($salt.$vo['url_id'].$salt),
'pic' => $url.'?server='.$vo['source'].'&type=pic&id='.$vo['pic_id'].'&auth='.md5($salt.$vo['pic_id'].$salt),
'lrc' => $url.'?server='.$vo['source'].'&type=lrc&id='.$vo['lyric_id'].'&auth='.md5($salt.$vo['lyric_id'].$salt),
);
}
header("Content-Type: application/javascript");
echo json_encode($music);
}
}
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('/toplist\?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\/(\w+)/i',$url,$id))list($id,$type)=array($id[1],'playlist');
elseif(preg_match('/album\/(\w+)/i',$url,$id))list($id,$type)=array($id[1],'album');
elseif(preg_match('/[\/.]\w+\/[songdem]+\/(\w+)/i',$url,$id))list($id,$type)=array($id[1],'song');
elseif(preg_match('/artist\/(\w+)/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('/song\/#hash\=(\w+)/i',$url,$id))list($id,$type)=array($id[1],'song');
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";
$url=$this->request->get('data');
$url=trim($url);
if(empty($url))return;
$server='netease';$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('/toplist\?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');
}
echo "[/Meting]";
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\/(\w+)/i',$url,$id))list($id,$type)=array($id[1],'playlist');
elseif(preg_match('/album\/(\w+)/i',$url,$id))list($id,$type)=array($id[1],'album');
elseif(preg_match('/[\/.]\w+\/[songdem]+\/(\w+)/i',$url,$id))list($id,$type)=array($id[1],'song');
elseif(preg_match('/artist\/(\w+)/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('/song\/#hash\=(\w+)/i',$url,$id))list($id,$type)=array($id[1],'song');
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{
echo "[Meting]\n[Music title=\"歌曲名\" author=\"歌手\" url=\"{$url}\"/]\n[/Meting]\n";
return;
}
echo "[Meting]\n[Music server=\"{$server}\" id=\"{$id}\" type=\"{$type}\"/]\n[/Meting]\n";
}
private function curl($url){
@ -80,143 +168,10 @@ class Meting_Action extends Typecho_Widget implements Widget_Interface_Do {
return $result;
}
private function musicjs(){
if(!extension_loaded('Meting'))include_once 'include/Meting.php';
self::filterReferer();
$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 \Metowolf\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' => isset($setting['theme'])?$setting['theme']:Typecho_Widget::widget('Widget_Options')->plugin('Meting')->theme?:'red',
'preload' => isset($setting['preload'])?$setting['preload']:Typecho_Widget::widget('Widget_Options')->plugin('Meting')->preload?:'auto',
'autoplay' => isset($setting['autoplay'])?$setting['autoplay']:Typecho_Widget::widget('Widget_Options')->plugin('Meting')->autoplay?:'false',
'height' => isset($setting['height'])?$setting['height']:Typecho_Widget::widget('Widget_Options')->plugin('Meting')->height?:'340px',
'mode' => isset($setting['mode'])?$setting['mode']:Typecho_Widget::widget('Widget_Options')->plugin('Meting')->mode?:'circulation',
'music' => array(),
);
foreach($music as $vo){
$URI=Typecho_Common::url('action/metingapi',Helper::options()->index);
$player['music'][]=array(
'title'=>$vo['name'],
'author'=>implode(' / ',$vo['artist']),
'url'=>$URI.'?site='.$vo['source'].'&do=url&id='.$vo['url_id'],
'pic'=>$URI.'?site='.$vo['source'].'&do=pic&id='.$vo['pic_id'],
'lrc'=>$URI.'?site='.$vo['source'].'&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(){
if(!extension_loaded('Meting'))include_once 'include/Meting.php';
self::filterReferer();
$id=$this->request->get('id');
$site=$this->request->get('site');
$rate=Typecho_Widget::widget('Widget_Options')->plugin('Meting')->bitrate;
$cookie=Typecho_Widget::widget('Widget_Options')->plugin('Meting')->cookie;
$cachekey="url/{$site}/{$id}/{$rate}";
$data=self::cacheRead($cachekey,60*15);
if(!$data){
$api=(new \Metowolf\Meting($site));
if($cookie!='')$api->cookie($cookie);
$data=$api->format(true)->url($id,$rate);
$data=json_decode($data,1);
self::cacheWrite($cachekey,$data);
}
if(empty($data['url']))$data['url']="https://api.i-meto.com/Public/music/empty.mp3";
if($site=='netease'){
$data['url']=str_replace("http://","https://",$data['url']);
}
$this->response->redirect($data['url']);
}
private function pic(){
if(!extension_loaded('Meting'))include_once 'include/Meting.php';
self::filterReferer();
$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 \Metowolf\Meting($site))->pic($id,90);
$data=json_decode($data,1);
self::cacheWrite($cachekey,$data);
}
$this->response->redirect($data['url']);
}
private function lrc(){
if(!extension_loaded('Meting'))include_once 'include/Meting.php';
self::filterReferer();
$id=$this->request->get('id');
$site=$this->request->get('site');
$cachekey="lyric/{$site}/{$id}";
$data=self::cacheRead($cachekey,60*60*24*10);
if(!$data){
$data=(new \Metowolf\Meting($site))->format(true)->lyric($id);
$data=json_decode($data,1);
self::cacheWrite($cachekey,$data);
}
if(!empty($data['tlyric']))$text=$this->lrctran($data['lyric'],$data['tlyric']);
else $text=$data['lyric'];
if(strlen($text)==0)$text='[00:00.00]无歌词';
echo $text;
}
private function lrctran($lyric,$tlyric){
preg_match_all('/\[(\d{2}:\d{2}\.\d+)\]([^\n]+)/i',$lyric,$t1);
preg_match_all('/\[(\d{2}:\d{2}\.\d+)\]([^\n]+)/i',$tlyric,$t2);
$from=$to=$t1[0];
$len1=sizeof($t1[0]);
$len2=sizeof($t2[0]);
for($i=0,$j=0;$i<$len1;$i++){
while($t1[1][$i]>$t2[1][$j]&&$j+1<$len2)$j++;
if($t1[1][$i]==$t2[1][$j]){
$t=trim(str_replace('/','',$t2[2][$j]));
if($t)$to[$i].=" (".$t2[2][$j].")";
$j++;
}
}
return str_replace($from,$to,$lyric);
}
private function cacheWrite($k,$v){
if(!is_array($v)||is_null($v))return;
if(empty($v)||is_null($v))return;
$db=Typecho_Db::get();
$insert=$db->insert('table.metingv1')->rows(array('id'=>md5($k),'value'=>serialize($v),'last'=>time()));
$insert=$db->insert('table.metingv1')->rows(array('id'=>md5($k),'value'=>$v,'last'=>time()));
return $db->query($insert);
}
@ -231,21 +186,19 @@ class Meting_Action extends Typecho_Widget implements Widget_Interface_Do {
$db->query($delete);
return false;
}
return unserialize($result['value']);
header("Meting-Cache: HIT");
return $result['value'];
}
else{
header("Meting-Cache: MISS");
return false;
}
else return false;
}
/*private function cacheFlush(){
$db=Typecho_Db::get();
$delete=$db->delete('table.metingv1')->where('last<?',time());
$db->query($delete);
}*/
private function filterReferer(){
if(isset($_SERVER['HTTP_REFERER'])&&strpos($_SERVER['HTTP_REFERER'],$_SERVER['HTTP_HOST'])===false){
http_response_code(403);
die();
die('[]');
}
}
}

View File

@ -2,21 +2,20 @@
if(!defined('__TYPECHO_ROOT_DIR__'))exit;
/**
* Meting for Typecho | Typecho 中使用 APlayer 播放在线音乐吧~
* APlayer for Typecho | Typecho 中使用 APlayer 播放在线音乐吧~
*
* @package Meting
* @package APlayer for Typecho | Meting
* @author METO
* @version 1.2.5
* @version 2.0.0
* @dependence 14.10.10-*
* @link https://github.com/metowolf/Meting-Typecho-Plugin
* @link https://github.com/MoePlayer/APlayer-Typecho
*
*/
define('METING_VERSION','1.2.5');
define('METING_VERSION','2.0.0');
class Meting_Plugin extends Typecho_Widget implements Typecho_Plugin_Interface
{
protected static $PID = 0;
/**
* 激活插件方法,如果激活失败,直接抛出异常
*
@ -56,11 +55,6 @@ class Meting_Plugin extends Typecho_Widget implements Typecho_Plugin_Interface
* @return void
*/
public static function config(Typecho_Widget_Helper_Form $form){
$t = new Typecho_Widget_Helper_Form_Element_Radio(
'cloudapi', array('true'=>_t('是'),'false'=>_t('否')),'false',
_t('METO 云解析 (beta)'),
_t('当插件无法正常工作时,可以勾选开启。<b>歌单混播、音质调节将失效</b>'));
$form->addInput($t);
$t = new Typecho_Widget_Helper_Form_Element_Text(
'theme', null, '#ad7a86',
_t('播放器颜色'),
@ -92,10 +86,44 @@ class Meting_Plugin extends Typecho_Widget implements Typecho_Plugin_Interface
_t(''));
$form->addInput($t);
$t = new Typecho_Widget_Helper_Form_Element_Text(
'cookie', null, '',
_t('自定义 Cookie (高级)'),
_t('通过更改 Cookie使其享受与客户端一样的体验'));
'api', null, Typecho_Common::url('action/metingapi',Helper::options()->index)."?server=:server&type=:type&id=:id&r=:r",
_t('云解析地址'),
_t('示例https://api.i-meto.com/meting/api?server=:server&type=:type&id=:id&r=:r'));
$form->addInput($t);
$t = new Typecho_Widget_Helper_Form_Element_Text(
'salt', null, md5(time()."Meting"),
_t('接口保护'),
_t('加盐保护 API 接口不被滥用,自动生成无需设置。'));
$form->addInput($t);
$t = new Typecho_Widget_Helper_Form_Element_Textarea(
'cookie', null, '',
_t('网易云音乐 Cookie (高级)'),
_t('如果您是网易云音乐的会员,可以将您的 cookie 填入此处来获取云盘等付费资源,听歌将不会计入下载次数。<br><b>如果不知道这是什么意思,忽略即可。</b>'));
$form->addInput($t);
$t = new Typecho_Widget_Helper_Form_Element_Radio(
'clean', array(_t('关闭'), _t('清除所有缓存')), 0,
_t('保存时清除所有缓存'));
$form->addInput($t);
}
/**
* 手动保存配置句柄
* @param $config array 插件配置
* @param $is_init bool 是否初始化
*/
public static function configHandle($config, $is_init)
{
if($is_init!=true){
if($config['api']==""){
$config['api']=Typecho_Common::url('action/metingapi',Helper::options()->index)."?server=:server&type=:type&id=:id&r=:r";
}
if($config['clean']==1){
self::clean();
$config['clean']=0;
}
}
Helper::configPlugin('Meting', $config);
}
public static function personalConfig(Typecho_Widget_Helper_Form $form){}
@ -108,22 +136,17 @@ class Meting_Plugin extends Typecho_Widget implements Typecho_Plugin_Interface
* @return void
*/
public static function header(){
$dir=Helper::options()->pluginUrl.'/Meting/assets/';
$api=Typecho_Widget::widget('Widget_Options')->plugin('Meting')->api;
$dir=Helper::options()->pluginUrl.'/Meting/assets';
$ver=METING_VERSION;
echo "<!-- Meting Start -->\n";
echo "<script type=\"text/javascript\" src=\"{$dir}APlayer.min.js?v={$ver}\"></script>\n";
echo "<!-- Meting End -->\n";
echo "<script type=\"text/javascript\" src=\"{$dir}/aplayer.min.js?v={$ver}\"></script>\n";
echo "<script>var meting_api=\"{$api}\";</script>";
}
public static function footer(){
$dir=Helper::options()->pluginUrl.'/Meting/assets';
$ver=METING_VERSION;
if(Typecho_Widget::widget('Widget_Options')->plugin('Meting')->cloudapi=='true'){
echo "<script type=\"text/javascript\" src=\"https://api.i-meto.com/music/aplayer.min.js?v={$ver}\"></script>\n";
}
}
public static function getPID(){
return ++self::$PID;
echo "<script type=\"text/javascript\" src=\"{$dir}/Meting.min.js?v={$ver}\"></script>\n";
}
public static function playerReplace($data,$widget,$last){
@ -146,35 +169,36 @@ class Meting_Plugin extends Typecho_Widget implements Typecho_Plugin_Interface
public static function parseMusic($matches,$setting){
$data=array();
$str="";
foreach($matches as $vo){
$t=self::shortcode_parse_atts(htmlspecialchars_decode($vo));
if(!in_array($t['server'],array('netease','tencent','xiami','baidu','kugou')))continue;
if(!in_array($t['type'],array('search','album','playlist','artist','song')))continue;
$data[]=$t;
}
$id=self::getPID();
$dir=Typecho_Common::url('action/metingapi',Helper::options()->index);
if(Typecho_Widget::widget('Widget_Options')->plugin('Meting')->cloudapi=='true'){
$str="<div class=\"aplayer\" data-id=\"{$data[0]['id']}\" data-server=\"{$data[0]['server']}\" data-type=\"{$data[0]['type']}\"";
$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',
'theme' => Typecho_Widget::widget('Widget_Options')->plugin('Meting')->theme?:'red',
'preload' => Typecho_Widget::widget('Widget_Options')->plugin('Meting')->preload?:'auto',
'autoplay' => Typecho_Widget::widget('Widget_Options')->plugin('Meting')->autoplay?:'false',
'height' => Typecho_Widget::widget('Widget_Options')->plugin('Meting')->height?:'340px',
'mode' => Typecho_Widget::widget('Widget_Options')->plugin('Meting')->mode?:'circulation',
);
foreach($player as $key=>$vo){
$str.=" data-{$key}=\"{$vo}\"";
if(isset($t['server'])){
if(!in_array($t['server'],array('netease','tencent','xiami','baidu','kugou')))continue;
if(!in_array($t['type'],array('search','album','playlist','artist','song')))continue;
$data=$t;
$str.="<div class=\"aplayer\" data-id=\"{$data['id']}\" data-server=\"{$data['server']}\" data-type=\"{$data['type']}\"";
if(is_array($setting))foreach($setting as $key=>$vo)$player[$key]=$vo;
foreach($player as $key=>$vo)$str.=" data-{$key}=\"{$vo}\"";
$str.="></div>\n";
}
else{
$data=$t;
$str.="<div class=\"aplayer\" data-title=\"{$data['title']}\" data-author=\"{$data['author']}\" data-url=\"{$data['url']}\"";
if(is_array($setting))foreach($setting as $key=>$vo)$player[$key]=$vo;
foreach($player as $key=>$vo)$str.=" data-{$key}=\"{$vo}\"";
$str.="></div>\n";
}
$str.="></div>\n";
return $str;
}
else{
$setting=base64_encode(json_encode($setting));
$data=base64_encode(json_encode($data));
return "<div id=\"MetingPlayer{$id}\" class=\"aplayer\" /></div>
<script type=\"text/javascript\" src=\"{$dir}?do=musicjs&s={$setting}&d={$data}&id={$id}\" async defer></script>";
}
return $str;
}
public static function addButton(){
@ -226,9 +250,9 @@ class Meting_Plugin extends Typecho_Widget implements Typecho_Plugin_Interface
$dbname=$db->getPrefix().'metingv1';
try{
$db->query("CREATE TABLE IF NOT EXISTS {$dbname} (
id CHAR(32) PRIMARY KEY NOT NULL UNIQUE,
value TEXT NOT NULL,
last int NOT NULL
id VARCHAR(32) PRIMARY KEY NOT NULL UNIQUE,
value TEXT NOT NULL,
last int NOT NULL
)");
}catch(Typecho_Db_Exception $e){
$code=$e->getCode();
@ -236,11 +260,18 @@ class Meting_Plugin extends Typecho_Widget implements Typecho_Plugin_Interface
}
}
private function clean(){
$db=Typecho_Db::get();
$dbname=$db->getPrefix().'metingv1';
$delete=$db->delete($dbname);
$db->query($delete);
}
public static function uninstall(){
$db=Typecho_Db::get();
$dbname=$db->getPrefix().'metingv1';
try{
$db->query("DROP TABLE IF EXISTS {$dbname};");
$db->query("DROP TABLE IF EXISTS {$dbname}");
}catch(Typecho_Db_Exception $e){
$code=$e->getCode();
throw new Typecho_Plugin_Exception('插件禁用失败。错误号:'.$code);

View File

@ -1,17 +1,18 @@
<p align="center">
<img src="https://user-images.githubusercontent.com/2666735/28999701-04c6ba7e-7a84-11e7-804b-3ffa4f7d5d95.png" alt="Meting">
<img src="https://user-images.githubusercontent.com/2666735/30651452-58ae6c88-9deb-11e7-9e13-6beae3f6c54c.png" alt="Meting">
</p>
# APlayer for Typecho (Meting)
# APlayer for Typecho
在 Typecho 中使用 APlayer 播放在线音乐吧~
[发布页面](https://i-meto.com/meting-typecho/)
## 介绍
1. 支持国内五大音乐平台网易云、QQ、虾米、百度、酷狗的单曲/专辑/歌单播放
2. 简单快捷,复制音乐详情页面网址,后台自动生成播放代码
3. **支持不同音乐平台歌曲混合播放**
4. 前端 APlayer后端 Meting 及时更新,保证兼容性及 API 高可用性
5. 支持 MySql、SQLite 数据库
3. 前端 APlayer后端 Meting 及时更新,保证兼容性及 API 高可用性
4. 支持 MySql、SQLite 数据库
5. 支持自定义歌曲播放
6. **自定义 API 支持**
## 声明
本作品仅供个人学习研究使用,请勿将其用作商业用途。
@ -25,7 +26,6 @@
## 使用
在文章编辑页面,点击编辑器上的 **音乐图标** 按钮,在弹出的窗口中输入音乐地址(见支持列表),最后点击确定即可
**歌曲混合特性:** 允许添加多个音乐地址,每行一个,插件会自动将所有歌曲合并在同一个歌单进行播放。
## 支持列表
网易云音乐 http://music.163.com
@ -67,4 +67,4 @@ A: 为了减少服务器压力,插件设置对歌单、歌词数据进行缓
更多问题可以通过 issue 页面提交,或者通过 Telegram、邮件向我反馈
## LICENSE
APlayer-Typecho-Plugin is under the MIT license.
APlayer-Typecho is under the MIT license.

2
assets/Meting.min.js vendored Normal file
View File

@ -0,0 +1,2 @@
/*! meting.aplayer.js v1.2.1 | MIT License */
!function(){function e(e,t){var a=[],r=e.dataset;a.element=e,a.music=t,a.showlrc=a.music[0].lrc?3:0,a.narrow="true"===r.narrow,a.autoplay="true"===r.autoplay,a.mutex="false"!==r.mutex,a.mode=r.mode||"circulation",a.preload=r.preload||"auto",a.listmaxheight=r.listmaxheight||"340px",a.theme=r.theme||"#ad7a86",new APlayer(a)}console.log("\n %c Meting 1.2.1 %c https://i-meto.com/ghost-aplayer/ \n\n","color: #fff; background-image: linear-gradient(90deg, rgb(47, 172, 178) 0%, rgb(45, 190, 96) 100%); padding:5px 1px;","background-image: linear-gradient(90deg, rgb(45, 190, 96) 0%, rgb(255, 255, 255) 100%); padding:5px 0;");var t="https://api.i-meto.com/meting/api?server=:server&type=:type&id=:id&r=:r";"undefined"!=typeof meting_api&&(t=meting_api);var a=document.querySelectorAll(".aplayer");Array.prototype.forEach.call(a,function(a,r){if(a.dataset.id){var i=new XMLHttpRequest,o=t;o=(o=(o=(o=o.replace(":server",a.dataset.server)).replace(":type",a.dataset.type)).replace(":id",a.dataset.id)).replace(":r",Math.random()),i.open("GET",o,!0),i.onload=function(){if(i.status>=200&&i.status<400){var t=JSON.parse(i.responseText);e(a,t)}},i.send()}else{var n=[];n.title=a.dataset.title,n.author=a.dataset.author,n.url=a.dataset.url,n.pic=a.dataset.pic,n.lrc=a.dataset.lrc,e(a,[n])}})}();

View File

@ -8,7 +8,7 @@ $(function() {
'<div>'+
'<p><b>插入音乐</b></p>'+
'<p>请在下方的输入框内输入要插入的音乐地址,如多个地址请用回车隔开'+
'<p><textarea type="text" rows="5" cols="40"></textarea></p>'+
'<p><input type="text"></input></p>'+
'</div>'+
'<form>'+
'<button type="button" class="btn btn-s primary" id="ok">确定</button>'+
@ -16,7 +16,7 @@ $(function() {
'</form>'+
'</div>'+
'</div>');
$('.wmd-prompt-dialog textarea').val('http://').select();
$('.wmd-prompt-dialog input').val('http://').select();
});
$(document).on('click','#cancel',function() {
$('#MetingPanel').remove();
@ -26,7 +26,7 @@ $(function() {
callback=$.ajax({
type:'POST',
url:murl,
data:{data:$('.wmd-prompt-dialog textarea').val()},
data:{data:$('.wmd-prompt-dialog input').val()},
async:false
});
$('#MetingPanel').remove();

View File

@ -3,7 +3,7 @@
* Meting music framework
* https://i-meto.com
* https://github.com/metowolf/Meting
* Version 1.3.8
* Version 1.3.9
*
* Copyright 2017, METO Sheel <i@i-meto.com>
* Released under the MIT license
@ -153,15 +153,15 @@ class Meting
'method' => 'GET',
'url' => 'https://c.y.qq.com/soso/fcgi-bin/client_search_cp',
'body' => array(
'format' => 'json',
'p' => $page,
'n' => $limit,
'w' => $keyword,
'aggr' => 1,
'lossless' => 1,
'cr' => 1,
'platform' => 'yqq',
'new_json' => 1,
),
'decode' => 'jsonp2json',
'format' => 'data#song#list',
);
break;
@ -321,12 +321,14 @@ class Meting
case 'tencent':
$API=array(
'method' => 'GET',
'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_album_info_cp.fcg',
'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_album_detail_cp.fcg',
'body' => array(
'albummid' => $id,
'platform' => 'yqq',
'platform' => 'mac',
'format' => 'json',
'newsong' => 1,
),
'format' => 'data#list',
'format' => 'data#getSongInfo',
);
break;
case 'xiami':
@ -406,7 +408,8 @@ class Meting
'begin' => 0,
'num' => $limit,
'order' => 'listen',
'platform' => 'yqq',
'platform' => 'mac',
'newsong' => 1,
),
'format' => 'data#list',
);
@ -484,15 +487,14 @@ class Meting
case 'tencent':
$API=array(
'method' => 'GET',
'url' => 'https://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg',
'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_playlist_cp.fcg',
'body' => array(
'disstid' => $id,
'utf8' => 1,
'type' => 1,
'platform' => 'yqq',
'id' => $id,
'format' => 'json',
'newsong' => 1,
'platform' => 'jqspaframe.json',
),
'decode' => 'jsonp2json',
'format' => 'cdlist#0#songlist',
'format' => 'data#cdlist#0#songlist',
);
break;
case 'xiami':
@ -653,7 +655,7 @@ class Meting
'url' => 'https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg',
'body' => array(
'songmid' => $id,
'g_tk' => 5381,
'g_tk' => '5381',
),
'decode' => 'tencent_lyric',
);
@ -751,7 +753,7 @@ class Meting
),
'tencent'=>array(
'referer' => 'https://y.qq.com/portal/player.html',
'cookie' => 'pgv_pvi=3832878080; pgv_si=s4066364416; pgv_pvid=3938077488; yplayer_open=1; qqmusic_fromtag=66; ts_last=y.qq.com/portal/player.html; ts_uid=5141451452; player_exist=1; yq_index=1',
'cookie' => 'pgv_pvi=22038528; pgv_si=s3156287488; pgv_pvid=5535248600; yplayer_open=1; ts_last=y.qq.com/portal/player.html; ts_uid=4847550686; yq_index=0; qqmusic_fromtag=66; player_exist=1',
'useragent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',
),
'xiami'=>array(
@ -794,13 +796,6 @@ class Meting
);
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);
@ -834,11 +829,9 @@ class Meting
{
$data=json_decode($result, 1);
if (isset($data['data'][0]['uf']['url'])) {
$url=array(
'url' => $data['data'][0]['uf']['url'],
'br' =>$data['data'][0]['uf']['br']/1000,
);
} elseif (isset($data['data'][0]['url'])) {
$data['data'][0]['url']=$data['data'][0]['uf']['url'];
}
if (isset($data['data'][0]['url'])) {
$url=array(
'url' => $data['data'][0]['url'],
'br' => $data['data'][0]['br']/1000,
@ -859,10 +852,10 @@ class Meting
'method' => 'GET',
'url' => 'https://c.y.qq.com/base/fcgi-bin/fcg_musicexpress.fcg',
'body' => array(
'json' => 3,
'guid' => $GUID,
'json' => 3,
'guid' => $GUID,
'format' => 'json',
),
'decode' => 'jsonp2json',
);
$KEY=json_decode($this->curl($API), 1);
$KEY=$KEY['key'];
@ -918,8 +911,8 @@ class Meting
}
$urlt=str_replace('^', '0', urldecode($urlt));
$url=array(
'url' => str_replace('http://','https://',urldecode($urlt)),
'br' => 320,
'url' => str_replace('http://','https://',urldecode($urlt)),
'br' => 320,
);
} else {
$url=array(
@ -1002,7 +995,7 @@ class Meting
}
private function tencent_lyric($result)
{
$result=$this->jsonp2json($result);
$result=substr($result,18,-1);
if (!$this->_FORMAT) {
return $result;
}
@ -1098,13 +1091,13 @@ class Meting
$data=$data['musicData'];
}
$result=array(
'id' => $data['songmid'],
'name' => $data['songname'],
'id' => $data['mid'],
'name' => $data['name'],
'artist' => array(),
'album' => isset($data['albumname'])?$data['albumname']:$data['album']['name'],
'pic_id' => $data['albummid'],
'url_id' => $data['songmid'],
'lyric_id' => $data['songmid'],
'album' => trim($data['album']['title']),
'pic_id' => $data['album']['mid'],
'url_id' => $data['mid'],
'lyric_id' => $data['mid'],
'source' => 'tencent',
);
foreach ($data['singer'] as $vo) {