mirror of
https://github.com/MoePlayer/APlayer-Typecho.git
synced 2024-12-21 20:30:24 +08:00
commit
5df05614a6
412
Action.php
412
Action.php
@ -1,206 +1,320 @@
|
||||
<?php
|
||||
if(!defined('__TYPECHO_ROOT_DIR__'))exit;
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
class Meting_Action extends Typecho_Widget implements Widget_Interface_Do {
|
||||
class Meting_Action extends Typecho_Widget implements Widget_Interface_Do
|
||||
{
|
||||
public function execute()
|
||||
{
|
||||
}
|
||||
|
||||
public function execute(){}
|
||||
|
||||
public function action(){
|
||||
public function action()
|
||||
{
|
||||
$this->on($this->request->is('do=update'))->update();
|
||||
$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;
|
||||
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(){
|
||||
private function api()
|
||||
{
|
||||
$this->filterReferer();
|
||||
$server=$this->request->get('server');
|
||||
$type=$this->request->get('type');
|
||||
$id=$this->request->get('id');
|
||||
$server = $this->request->get('server');
|
||||
$type = $this->request->get('type');
|
||||
$id = $this->request->get('id');
|
||||
|
||||
if(!$this->check($server,$type)||empty($id))die('[]');
|
||||
if (!$this->check($server, $type) || empty($id)) {
|
||||
die('[]');
|
||||
}
|
||||
|
||||
if(!extension_loaded('Meting'))include_once 'include/Meting.php';
|
||||
$api=new \Metowolf\Meting($server);
|
||||
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 (!extension_loaded('Meting')) {
|
||||
$cachetype = Typecho_Widget::widget('Widget_Options')->plugin('Meting')->cachetype;
|
||||
if ($cachetype != 'none') {
|
||||
$cachehost = Typecho_Widget::widget('Widget_Options')->plugin('Meting')->cachehost;
|
||||
$cacheport = Typecho_Widget::widget('Widget_Options')->plugin('Meting')->cacheport;
|
||||
include_once 'driver/cache.interface.php';
|
||||
include_once 'driver/'.$cachetype.'.class.php';
|
||||
$this->cache = new MetingCache(array(
|
||||
'host' => $cachehost,
|
||||
'port' => $cacheport
|
||||
));
|
||||
}
|
||||
}
|
||||
$api = new \Metowolf\Meting($server);
|
||||
$api->format(true);
|
||||
$cookie = Typecho_Widget::widget('Widget_Options')->plugin('Meting')->cookie;
|
||||
if ($server == 'netease' && !empty($cookie)) {
|
||||
$api->cookie($cookie);
|
||||
}
|
||||
|
||||
if(in_array($type,array('lrc','pic','url'))){
|
||||
$auth1=md5($salt.$id.$salt);
|
||||
$auth2=$this->request->get('auth');
|
||||
if(strcmp($auth1,$auth2)){
|
||||
$EID = $server.'/'.$type.'/'.$id;
|
||||
$salt = Typecho_Widget::widget('Widget_Options')->plugin('Meting')->salt;
|
||||
|
||||
if (!empty($salt) && in_array($type, array('lrc','pic','url'))) {
|
||||
$auth1 = md5($salt.$type.$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);
|
||||
if ($type == 'lrc') {
|
||||
$data = $this->cacheRead($EID);
|
||||
if (empty($data)) {
|
||||
$data = $api->lyric($id);
|
||||
$this->cacheWrite($EID, $data, 86400);
|
||||
}
|
||||
$data=json_decode($data,true);
|
||||
$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);
|
||||
} elseif ($type == 'pic') {
|
||||
$data = $this->cacheRead($EID);
|
||||
if (empty($data)) {
|
||||
$data = $api->pic($id, 90);
|
||||
$this->cacheWrite($EID, $data, 86400);
|
||||
}
|
||||
$data=json_decode($data,true);
|
||||
$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'&&!empty($cookie))$api->cookie($cookie);
|
||||
$data=$api->url($id,$rate);
|
||||
$this->cacheWrite($EID,$data);
|
||||
} elseif ($type == 'url') {
|
||||
$data = $this->cacheRead($EID);
|
||||
if (empty($data)) {
|
||||
$rate = Typecho_Widget::widget('Widget_Options')->plugin('Meting')->bitrate;
|
||||
$data = $api->url($id, $rate);
|
||||
$this->cacheWrite($EID, $data, 1200);
|
||||
}
|
||||
$data=json_decode($data,true);
|
||||
$url=$data['url'];
|
||||
$data = json_decode($data, true);
|
||||
$url = $data['url'];
|
||||
|
||||
if($server=='netease'){
|
||||
$url=str_replace('://m7c.','://m8.',$url);
|
||||
$url=str_replace('://m8c.','://m8.',$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 ($server == 'netease') {
|
||||
$url = str_replace('://m7c.', '://m7.', $url);
|
||||
$url = str_replace('://m8c.', '://m8.', $url);
|
||||
$url = str_replace('http://m8.', 'https://m9.', $url);
|
||||
$url = str_replace('http://m7.', 'https://m9.', $url);
|
||||
$url = str_replace('http://m10.', 'https://m10.', $url);
|
||||
}
|
||||
|
||||
if(empty($url))$url='https://api.i-meto.com/Public/music/empty.mp3';
|
||||
if ($server == 'baidu') {
|
||||
$url = str_replace('http://zhangmenshiting.qianqian.com', 'https://gss3.baidu.com/y0s1hSulBw92lNKgpU_Z2jR7b2w6buu', $url);
|
||||
}
|
||||
|
||||
if (empty($url)) {
|
||||
$url = 'https://coding.meting.api.i-meto.com/empty.mp3';
|
||||
if ($server == 'netease') {
|
||||
$url = 'https://music.163.com/song/media/outer/url?id='.$id.'.mp3';
|
||||
}
|
||||
}
|
||||
$this->response->redirect($url);
|
||||
}
|
||||
else{
|
||||
$data=$this->cacheRead($EID,60*60*2);
|
||||
if(empty($data)){
|
||||
$data=$api->$type($id);
|
||||
$this->cacheWrite($EID,$data);
|
||||
} else {
|
||||
$data = $this->cacheRead($EID);
|
||||
if (empty($data)) {
|
||||
$data = $api->$type($id);
|
||||
$this->cacheWrite($EID, $data, 7200);
|
||||
}
|
||||
$data=json_decode($data,1);
|
||||
$url=Typecho_Common::url('action/metingapi',Helper::options()->index);
|
||||
$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),
|
||||
$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.'url'.$vo['url_id'].$salt),
|
||||
'pic' => $url.'?server='.$vo['source'].'&type=pic&id='.$vo['pic_id'].'&auth='.md5($salt.'pic'.$vo['pic_id'].$salt),
|
||||
'lrc' => $url.'?server='.$vo['source'].'&type=lrc&id='.$vo['lyric_id'].'&auth='.md5($salt.'lrc'.$vo['lyric_id'].$salt),
|
||||
);
|
||||
}
|
||||
header("Content-Type: application/javascript");
|
||||
echo json_encode($music);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function shortcode(){
|
||||
$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');
|
||||
}
|
||||
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}\" pic=\"图片文件URL\" lrc=\"歌词文件URL\"/]\n[/Meting]\n";
|
||||
private function shortcode()
|
||||
{
|
||||
$url = $this->request->get('data');
|
||||
$url = trim($url);
|
||||
if (empty($url)) {
|
||||
return;
|
||||
}
|
||||
echo "[Meting]\n[Music server=\"{$server}\" id=\"{$id}\" type=\"{$type}\"/]\n[/Meting]\n";
|
||||
$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');
|
||||
}
|
||||
} elseif (strpos($url, 'qq.com') !== false) {
|
||||
$server = 'tencent';
|
||||
if (preg_match('/playsquare\/([^\.]*)/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('/#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 {
|
||||
die("[Meting]\n[Music title=\"歌曲名\" author=\"歌手\" url=\"{$url}\" pic=\"图片文件URL\" lrc=\"歌词文件URL\"/]\n[/Meting]\n");
|
||||
return;
|
||||
}
|
||||
die("[Meting]\n[Music server=\"{$server}\" id=\"{$id}\" type=\"{$type}\"/]\n[/Meting]\n");
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
private function update()
|
||||
{
|
||||
$isAdmin = call_user_func(function () {
|
||||
$hasLogin = $this->widget('Widget_User')->hasLogin();
|
||||
$isAdmin = false;
|
||||
if (!$hasLogin) {
|
||||
return false;
|
||||
}
|
||||
$isAdmin = $this->widget('Widget_User')->pass('administrator', true);
|
||||
return $isAdmin;
|
||||
}, $this);
|
||||
if (!$isAdmin) {
|
||||
die('非管理员,禁止操作!');
|
||||
}
|
||||
|
||||
header("Content-Type: application/javascript");
|
||||
|
||||
$shasum = $this->curl('https://raw.githubusercontent.com/MoePlayer/APlayer-Typecho/master/shasum.txt');
|
||||
|
||||
echo "获取最新特征库...\n";
|
||||
echo $shasum."\n\n";
|
||||
|
||||
$shasum = explode("\n", $shasum);
|
||||
array_pop($shasum);
|
||||
|
||||
echo "开始检查本地文件...\n";
|
||||
|
||||
foreach ($shasum as $remote) {
|
||||
list($remote_sha256, $filename) = explode(' ', $remote);
|
||||
if (!file_exists(__DIR__.'/'.$filename)) {
|
||||
continue;
|
||||
}
|
||||
$local_sha256 = hash('sha256', file_get_contents(__DIR__.'/'.$filename));
|
||||
if (!hash_equals($local_sha256, $remote_sha256)) {
|
||||
echo "下载 ".$filename;
|
||||
$url = 'https://raw.githubusercontent.com/MoePlayer/APlayer-Typecho/master'.substr($filename, 1);
|
||||
|
||||
if (file_put_contents(__DIR__.'/'.$filename, $this->curl($url))) {
|
||||
echo " (OK)\n";
|
||||
} else {
|
||||
die("\n下载失败,错误信息: $url\n");
|
||||
}
|
||||
} else {
|
||||
echo "无需更新 ".$filename."\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "\n\n如果插件出现错误,建议禁用再启用一次插件完成升级。";
|
||||
die();
|
||||
}
|
||||
|
||||
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 cacheWrite($k,$v){
|
||||
if(empty($v)||is_null($v))return;
|
||||
$db=Typecho_Db::get();
|
||||
$insert=$db->insert('table.metingv1')->rows(array('id'=>md5($k),'value'=>$v,'last'=>time()));
|
||||
return $db->query($insert);
|
||||
private function cacheWrite($k, $v, $t)
|
||||
{
|
||||
if (!isset($this->cache)) {
|
||||
return;
|
||||
}
|
||||
return $this->cache->set($k, $v, $t);
|
||||
}
|
||||
|
||||
private function cacheRead($k,$t=3600){
|
||||
$db=Typecho_Db::get();
|
||||
$query=$db->select('value','last')->from('table.metingv1')->where('id=?',md5($k));
|
||||
$result=$db->fetchRow($query);
|
||||
|
||||
if(isset($result['value'])){
|
||||
if(time()-$result['last']>$t){
|
||||
$delete=$db->delete('table.metingv1')->where('last<?',time()-$t);
|
||||
$db->query($delete);
|
||||
return false;
|
||||
}
|
||||
header("Meting-Cache: HIT");
|
||||
return $result['value'];
|
||||
}
|
||||
else{
|
||||
header("Meting-Cache: MISS");
|
||||
private function cacheRead($k)
|
||||
{
|
||||
if (!isset($this->cache)) {
|
||||
return false;
|
||||
}
|
||||
return $this->cache->get($k);
|
||||
}
|
||||
|
||||
private function filterReferer(){
|
||||
if(isset($_SERVER['HTTP_REFERER'])&&strpos($_SERVER['HTTP_REFERER'],$_SERVER['HTTP_HOST'])===false){
|
||||
private function filterReferer()
|
||||
{
|
||||
$salt = Typecho_Widget::widget('Widget_Options')->plugin('Meting')->salt;
|
||||
if (empty($salt)) {
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Connection, User-Agent, Cookie");
|
||||
return;
|
||||
}
|
||||
if (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], $_SERVER['HTTP_HOST']) === false) {
|
||||
http_response_code(403);
|
||||
die('[]');
|
||||
}
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 METO
|
||||
Copyright (c) 2018 METO <i@i-meto.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
356
Plugin.php
356
Plugin.php
@ -1,18 +1,20 @@
|
||||
<?php
|
||||
if(!defined('__TYPECHO_ROOT_DIR__'))exit;
|
||||
if (!defined('__TYPECHO_ROOT_DIR__')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* APlayer for Typecho | 在 Typecho 中使用 APlayer 播放在线音乐吧~
|
||||
* 在 Typecho 中使用 APlayer 播放在线音乐吧~
|
||||
*
|
||||
* @package APlayer for Typecho | Meting
|
||||
* @author METO
|
||||
* @version 2.0.1
|
||||
* @version 2.1.0
|
||||
* @dependence 14.10.10-*
|
||||
* @link https://github.com/MoePlayer/APlayer-Typecho
|
||||
*
|
||||
*/
|
||||
|
||||
define('METING_VERSION','2.0.1');
|
||||
define('METING_VERSION', '2.1.0');
|
||||
|
||||
class Meting_Plugin extends Typecho_Widget implements Typecho_Plugin_Interface
|
||||
{
|
||||
@ -23,13 +25,14 @@ class Meting_Plugin extends Typecho_Widget implements Typecho_Plugin_Interface
|
||||
* @return void
|
||||
* @throws Typecho_Plugin_Exception
|
||||
*/
|
||||
public static function activate(){
|
||||
Meting_Plugin::install();
|
||||
public static function activate()
|
||||
{
|
||||
Meting_Plugin::installCheck();
|
||||
Helper::addAction('metingapi', 'Meting_Action');
|
||||
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('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');
|
||||
}
|
||||
@ -42,8 +45,8 @@ class Meting_Plugin extends Typecho_Widget implements Typecho_Plugin_Interface
|
||||
* @return void
|
||||
* @throws Typecho_Plugin_Exception
|
||||
*/
|
||||
public static function deactivate(){
|
||||
Meting_Plugin::uninstall();
|
||||
public static function deactivate()
|
||||
{
|
||||
Helper::removeAction("metingapi");
|
||||
}
|
||||
|
||||
@ -54,56 +57,114 @@ class Meting_Plugin extends Typecho_Widget implements Typecho_Plugin_Interface
|
||||
* @param Typecho_Widget_Helper_Form $form 配置面板
|
||||
* @return void
|
||||
*/
|
||||
public static function config(Typecho_Widget_Helper_Form $form){
|
||||
public static function config(Typecho_Widget_Helper_Form $form)
|
||||
{
|
||||
$t = new Typecho_Widget_Helper_Form_Element_Text(
|
||||
'theme', null, '#ad7a86',
|
||||
'theme',
|
||||
null,
|
||||
'#ad7a86',
|
||||
_t('播放器颜色'),
|
||||
_t('播放器默认的主题颜色,支持如 #372e21、#75c、red,该设定会被[Meting]标签中的theme属性覆盖,默认为 #ad7a86'));
|
||||
_t('播放器默认的主题颜色,支持如 #372e21、#75c、red,该设定会被[Meting]标签中的theme属性覆盖,默认为 #ad7a86')
|
||||
);
|
||||
$form->addInput($t);
|
||||
$t = new Typecho_Widget_Helper_Form_Element_Text(
|
||||
'height', null, '340px',
|
||||
'height',
|
||||
null,
|
||||
'340px',
|
||||
_t('播放器列表最大高度'),
|
||||
_t(''));
|
||||
_t('')
|
||||
);
|
||||
$form->addInput($t);
|
||||
$t = new Typecho_Widget_Helper_Form_Element_Radio(
|
||||
'autoplay', array('true'=>_t('是'),'false'=>_t('否')),'false',
|
||||
'autoplay',
|
||||
array('true' => _t('是'),'false' => _t('否')),
|
||||
'false',
|
||||
_t('全局自动播放'),
|
||||
_t(''));
|
||||
_t('')
|
||||
);
|
||||
$form->addInput($t);
|
||||
$t = new Typecho_Widget_Helper_Form_Element_Radio(
|
||||
'mode', array('circulation'=>_t('循环'),'single'=>_t('单曲'),'order'=>_t('列表'),'random'=>_t('随机')),'circulation',
|
||||
'mode',
|
||||
array('circulation' => _t('循环'),'single' => _t('单曲'),'order' => _t('列表'),'random' => _t('随机')),
|
||||
'circulation',
|
||||
_t('全局播放模式'),
|
||||
_t(''));
|
||||
_t('')
|
||||
);
|
||||
$form->addInput($t);
|
||||
$t= new Typecho_Widget_Helper_Form_Element_Radio(
|
||||
'preload', array('auto'=>_t('自动'),'none'=>_t('不加载'),'metadata'=>_t('加载元数据')), 'auto',
|
||||
$t = new Typecho_Widget_Helper_Form_Element_Radio(
|
||||
'preload',
|
||||
array('auto' => _t('自动'),'none' => _t('不加载'),'metadata' => _t('加载元数据')),
|
||||
'auto',
|
||||
_t('预加载属性'),
|
||||
_t(''));
|
||||
_t('')
|
||||
);
|
||||
$form->addInput($t);
|
||||
$t= new Typecho_Widget_Helper_Form_Element_Radio(
|
||||
'bitrate', array('128'=>_t('流畅品质'),'192'=>_t('清晰品质'),'320'=>_t('高品质')), '192',
|
||||
|
||||
$list = array(
|
||||
'none' => _t('关闭'),
|
||||
'redis' => _t('Redis'),
|
||||
'mysql' => _t('Mysql'),
|
||||
'sqlite' => _t('SQLite')
|
||||
);
|
||||
$t = new Typecho_Widget_Helper_Form_Element_Radio(
|
||||
'cachetype',
|
||||
$list,
|
||||
'none',
|
||||
_t('缓存驱动'),
|
||||
_t('缓存歌曲解析信息,降低服务器压力')
|
||||
);
|
||||
$form->addInput($t);
|
||||
$t = new Typecho_Widget_Helper_Form_Element_Text(
|
||||
'cachehost',
|
||||
null,
|
||||
'127.0.0.1',
|
||||
_t('缓存服务地址'),
|
||||
_t('通常为 localhost, 127.0.0.1')
|
||||
);
|
||||
$form->addInput($t);
|
||||
$t = new Typecho_Widget_Helper_Form_Element_Text(
|
||||
'cacheport',
|
||||
null,
|
||||
'6379',
|
||||
_t('缓存服务端口'),
|
||||
_t('默认端口 memcache: 11211, Redis: 6379, Mysql: 3306')
|
||||
);
|
||||
$form->addInput($t);
|
||||
|
||||
$t = new Typecho_Widget_Helper_Form_Element_Radio(
|
||||
'bitrate',
|
||||
array('128' => _t('流畅品质 128K'),'192' => _t('清晰品质 192K'),'320' => _t('高品质 320K')),
|
||||
'192',
|
||||
_t('默认音质'),
|
||||
_t(''));
|
||||
_t('')
|
||||
);
|
||||
$form->addInput($t);
|
||||
$t = new Typecho_Widget_Helper_Form_Element_Text(
|
||||
'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'));
|
||||
'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 接口不被滥用,自动生成无需设置。'));
|
||||
'salt',
|
||||
null,
|
||||
Typecho_Common::randString(32),
|
||||
_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('保存时清除所有缓存'));
|
||||
'cookie',
|
||||
null,
|
||||
'',
|
||||
_t('* 网易云音乐 Cookie'),
|
||||
_t('如果您是网易云音乐的会员,可以将您的 cookie 填入此处来获取云盘等付费资源,听歌将不会计入下载次数。<br><b>如果不知道这是什么意思,忽略即可。</b>')
|
||||
);
|
||||
$form->addInput($t);
|
||||
|
||||
echo '<a href="'.Typecho_Common::url('action/metingapi', Helper::options()->index).'?do=update" target="_blank"><button class="btn" style="outline: 0">' . _t('检查并更新插件'). '</button></a>';
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,20 +174,32 @@ class Meting_Plugin extends Typecho_Widget implements Typecho_Plugin_Interface
|
||||
*/
|
||||
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 (!$is_init) {
|
||||
if (empty($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;
|
||||
if ($config['cachetype'] != 'none') {
|
||||
require_once 'driver/cache.interface.php';
|
||||
require_once 'driver/'.$config['cachetype'].'.class.php';
|
||||
try {
|
||||
$cache = new MetingCache(array(
|
||||
'host' => $config['cachehost'],
|
||||
'port' => $config['cacheport']
|
||||
));
|
||||
$cache->install();
|
||||
$cache->flush();
|
||||
} catch (Exception $e) {
|
||||
throw new Typecho_Plugin_Exception(_t($e->getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Helper::configPlugin('Meting', $config);
|
||||
}
|
||||
|
||||
public static function personalConfig(Typecho_Widget_Helper_Form $form){}
|
||||
public static function personalConfig(Typecho_Widget_Helper_Form $form)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件配置面板
|
||||
@ -135,100 +208,125 @@ class Meting_Plugin extends Typecho_Widget implements Typecho_Plugin_Interface
|
||||
* @param Typecho_Widget_Helper_Form $form 配置面板
|
||||
* @return void
|
||||
*/
|
||||
public static function header(){
|
||||
$api=Typecho_Widget::widget('Widget_Options')->plugin('Meting')->api;
|
||||
$dir=Helper::options()->pluginUrl.'/Meting/assets';
|
||||
$ver=METING_VERSION;
|
||||
public static function header()
|
||||
{
|
||||
$api = Typecho_Widget::widget('Widget_Options')->plugin('Meting')->api;
|
||||
$dir = Helper::options()->pluginUrl.'/Meting/assets';
|
||||
$ver = METING_VERSION;
|
||||
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;
|
||||
public static function footer()
|
||||
{
|
||||
$dir = Helper::options()->pluginUrl.'/Meting/assets';
|
||||
$ver = METING_VERSION;
|
||||
echo "<script type=\"text/javascript\" src=\"{$dir}/Meting.min.js?v={$ver}\"></script>\n";
|
||||
}
|
||||
|
||||
public static function playerReplace($data,$widget,$last){
|
||||
$text=empty($last)?$data:$last;
|
||||
if($widget instanceof Widget_Archive){
|
||||
$data=$text;
|
||||
$pattern=self::get_shortcode_regex(array('Meting'));
|
||||
$text=preg_replace_callback("/$pattern/",array('Meting_Plugin','parseCallback'),$data);
|
||||
public static function playerReplace($data, $widget, $last)
|
||||
{
|
||||
$text = empty($last)?$data:$last;
|
||||
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){
|
||||
$setting=self::shortcode_parse_atts(htmlspecialchars_decode($matches[3]));
|
||||
$matches[5]=htmlspecialchars_decode($matches[5]);
|
||||
$pattern=self::get_shortcode_regex(array('Music'));
|
||||
preg_match_all("/$pattern/",$matches[5],$all);
|
||||
if(sizeof($all[3]))return Meting_Plugin::parseMusic($all[3],$setting);
|
||||
public static function parseCallback($matches)
|
||||
{
|
||||
$setting = self::shortcode_parse_atts(htmlspecialchars_decode($matches[3]));
|
||||
$matches[5] = htmlspecialchars_decode($matches[5]);
|
||||
$pattern = self::get_shortcode_regex(array('Music'));
|
||||
preg_match_all("/$pattern/", $matches[5], $all);
|
||||
if (sizeof($all[3])) {
|
||||
return Meting_Plugin::parseMusic($all[3], $setting);
|
||||
}
|
||||
}
|
||||
|
||||
public static function parseMusic($matches,$setting){
|
||||
$data=array();
|
||||
$str="";
|
||||
foreach($matches as $vo){
|
||||
$t=self::shortcode_parse_atts(htmlspecialchars_decode($vo));
|
||||
$player=array(
|
||||
'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',
|
||||
public static function parseMusic($matches, $setting)
|
||||
{
|
||||
$data = array();
|
||||
$str = "";
|
||||
foreach ($matches as $vo) {
|
||||
$t = self::shortcode_parse_atts(htmlspecialchars_decode($vo));
|
||||
$player = array(
|
||||
'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',
|
||||
'listmaxheight' => Typecho_Widget::widget('Widget_Options')->plugin('Meting')->height?:'340px',
|
||||
'mode' => Typecho_Widget::widget('Widget_Options')->plugin('Meting')->mode?:'circulation',
|
||||
'mode' => Typecho_Widget::widget('Widget_Options')->plugin('Meting')->mode?:'circulation',
|
||||
);
|
||||
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;
|
||||
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-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']}\" data-pic=\"{$data['pic']}\" data-lrc=\"{$data['lrc']}\"";
|
||||
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 class=\"aplayer\" data-title=\"{$data['title']}\" data-author=\"{$data['author']}\" data-url=\"{$data['url']}\" data-pic=\"{$data['pic']}\" data-lrc=\"{$data['lrc']}\"";
|
||||
if (is_array($setting)) {
|
||||
foreach ($setting as $key => $vo) {
|
||||
$player[$key] = $vo;
|
||||
}
|
||||
}
|
||||
foreach ($player as $key => $vo) {
|
||||
$str .= " data-{$key}=\"{$vo}\"";
|
||||
}
|
||||
$str .= "></div>\n";
|
||||
}
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
public static function addButton(){
|
||||
$url=Typecho_Common::url('action/metingapi',Helper::options()->index).'?do=parse';
|
||||
$dir=Helper::options()->pluginUrl.'/Meting/assets/editer.js?v='.METING_VERSION;
|
||||
echo "<script type=\"text/javascript\">var murl='{$url}';</script>
|
||||
public static function addButton()
|
||||
{
|
||||
$url = Typecho_Common::url('action/metingapi', Helper::options()->index).'?do=parse';
|
||||
$dir = Helper::options()->pluginUrl.'/Meting/assets/editer.js?v='.METING_VERSION;
|
||||
echo "<script>var murl='{$url}';</script>
|
||||
<script type=\"text/javascript\" src=\"{$dir}\"></script>";
|
||||
}
|
||||
|
||||
# https://github.com/WordPress/WordPress/blob/master/wp-includes/shortcodes.php#L508
|
||||
private static function shortcode_parse_atts($text) {
|
||||
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) ) {
|
||||
if (preg_match_all($pattern, $text, $match, PREG_SET_ORDER)) {
|
||||
foreach ($match as $m) {
|
||||
if (!empty($m[1]))
|
||||
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]);
|
||||
} 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 ) ) {
|
||||
foreach ($atts as &$value) {
|
||||
if (false !== strpos($value, '<')) {
|
||||
if (1 !== preg_match('/^[^<]*+(?:<[^>]*+>[^<]*+)*+$/', $value)) {
|
||||
$value = '';
|
||||
}
|
||||
}
|
||||
@ -240,41 +338,19 @@ class Meting_Plugin extends Typecho_Widget implements Typecho_Plugin_Interface
|
||||
}
|
||||
|
||||
# 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) );
|
||||
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();
|
||||
$dbname=$db->getPrefix().'metingv1';
|
||||
try{
|
||||
$db->query("CREATE TABLE IF NOT EXISTS {$dbname} (
|
||||
id VARCHAR(32) PRIMARY KEY NOT NULL UNIQUE,
|
||||
value TEXT NOT NULL,
|
||||
last int NOT NULL
|
||||
)");
|
||||
}catch(Typecho_Db_Exception $e){
|
||||
$code=$e->getCode();
|
||||
throw new Typecho_Plugin_Exception('插件启用失败。错误号:'.$code);
|
||||
public static function installCheck()
|
||||
{
|
||||
if (!extension_loaded('curl')) {
|
||||
throw new Typecho_Plugin_Exception(_t('缺少 cURL 拓展'));
|
||||
}
|
||||
}
|
||||
|
||||
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}");
|
||||
}catch(Typecho_Db_Exception $e){
|
||||
$code=$e->getCode();
|
||||
throw new Typecho_Plugin_Exception('插件禁用失败。错误号:'.$code);
|
||||
if (!(extension_loaded('openssl') || extension_loaded('mcrypt'))) {
|
||||
throw new Typecho_Plugin_Exception(_t('缺少 openssl/mcrypt 拓展'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
38
README.md
38
README.md
@ -2,8 +2,7 @@
|
||||
<img src="https://user-images.githubusercontent.com/2666735/30651452-58ae6c88-9deb-11e7-9e13-6beae3f6c54c.png" alt="Meting">
|
||||
</p>
|
||||
|
||||
# APlayer for Typecho
|
||||
在 Typecho 中使用 APlayer 播放在线音乐吧~
|
||||
> 在 Typecho 中使用 APlayer 播放在线音乐吧~
|
||||
[发布页面](https://i-meto.com/meting-typecho/)
|
||||
|
||||
## 介绍
|
||||
@ -11,8 +10,9 @@
|
||||
2. 简单快捷,复制音乐详情页面网址,后台自动生成播放代码
|
||||
3. 前端 APlayer,后端 Meting 及时更新,保证兼容性及 API 高可用性
|
||||
4. 支持 MySql、SQLite 数据库
|
||||
5. 支持自定义歌曲播放
|
||||
6. **自定义 API 支持**
|
||||
5. **支持 Redis 缓存**
|
||||
6. 支持自定义歌曲播放
|
||||
7. **自定义 API 支持**
|
||||
|
||||
## 声明
|
||||
本作品仅供个人学习研究使用,请勿将其用作商业用途。
|
||||
@ -35,11 +35,11 @@
|
||||
- 歌单 http://music.163.com/#/playlist?id=436843836
|
||||
- 榜单 http://music.163.com/#/discover/toplist?id=60198
|
||||
|
||||
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
|
||||
QQ 音乐 https://y.qq.com
|
||||
- 单曲 https://y.qq.com/n/yqq/song/000jDQWP4JiB3y.html
|
||||
- 专辑 https://y.qq.com/n/yqq/album/003rytri2FHG3V.html
|
||||
- 歌手 https://y.qq.com/n/yqq/singer/003Nz2So3XXYek.html
|
||||
- 歌单 https://y.qq.com/n/yqq/playlist/1144188779.html
|
||||
|
||||
虾米音乐 http://www.xiami.com or http://h.xiami.com
|
||||
- 单曲 http://www.xiami.com/song/bf08DNT3035f
|
||||
@ -48,7 +48,7 @@ QQ 音乐 http://y.qq.com
|
||||
- 歌单 http://www.xiami.com/collect/254478782
|
||||
|
||||
酷狗音乐 http://www.kugou.com
|
||||
- 单曲 暂不支持直接解析,可直接修改短代码实现
|
||||
- 单曲 http://www.kugou.com/song/#hash=09E8DE70A24C97B92A29F6A19F3528A2
|
||||
- 专辑 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
|
||||
@ -60,9 +60,21 @@ QQ 音乐 http://y.qq.com
|
||||
- 歌单 http://music.baidu.com/songlist/364201689
|
||||
|
||||
## FAQ
|
||||
Q: 如何清除歌单、歌词缓存?
|
||||
A: 为了减少服务器压力,插件设置对歌单、歌词数据进行缓存,缓存会根据时间周期自动更新管理,无需人工干预。**如果需要强制清除,可以通过禁用再启用插件实现,不影响文章中歌曲信息**
|
||||
...
|
||||
Q: pjax 页面切换无法停止播放?
|
||||
A: 需要另外在主题的回调函数中添加
|
||||
```
|
||||
if (typeof aplayers !== 'undefined'){
|
||||
for (var i = 0; i < aplayers.length; i++) {
|
||||
try {aplayers[i].destroy()} catch(e){}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Q: 不支持混合歌单?
|
||||
A: 由于 2.0 版本重写了实现方式,旧的混合歌单将不再支持,建议通过各音乐平台创建歌单的方式添加。
|
||||
|
||||
Q: 部分歌曲失效?
|
||||
A: 可能 API 失效导致的,可以尝试点击插件升级按钮升级到最新,或填写 cookie 信息。
|
||||
|
||||
更多问题可以通过 issue 页面提交,或者通过 Telegram、邮件向我反馈
|
||||
|
||||
|
2
assets/APlayer.min.js
vendored
2
assets/APlayer.min.js
vendored
File diff suppressed because one or more lines are too long
3
assets/Meting.min.js
vendored
3
assets/Meting.min.js
vendored
@ -1,2 +1 @@
|
||||
/*! meting.aplayer.js v1.2.2 | MIT License */
|
||||
function r(e){/in/.test(document.readyState)?setTimeout("r("+e+")",9):e()}r(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.2 %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])}})});
|
||||
"use strict";console.log("\n %c MetingJS 1.0.1 %c https://github.com/metowolf/MetingJS \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 aplayers=[];document.addEventListener("DOMContentLoaded",function(){function a(a,b){var c=[],d=a.dataset;c.element=a,c.music=b,c.showlrc=c.music[0].lrc?3:0,c.narrow="true"===d.narrow,c.autoplay="true"===d.autoplay,c.mutex="false"!==d.mutex,c.mode=d.mode||"circulation",c.preload=d.preload||"auto",c.listmaxheight=d.listmaxheight||"340px",c.theme=d.theme||"#ad7a86",aplayers.push(new APlayer(c))}var b="https://api.i-meto.com/meting/api?server=:server&type=:type&id=:id&r=:r";"undefined"!=typeof meting_api&&(b=meting_api);var c=document.querySelectorAll(".aplayer"),d=!0,e=!1,f=void 0;try{for(var g,h=function(){var c=g.value,d=c.dataset.id;if(d){var e=b;e=e.replace(":server",c.dataset.server),e=e.replace(":type",c.dataset.type),e=e.replace(":id",c.dataset.id),e=e.replace(":r",Math.random());var f=new XMLHttpRequest;f.onreadystatechange=function(){if(4===f.readyState&&(200<=f.status&&300>f.status||304===f.status)){var b=JSON.parse(f.responseText);a(c,b)}},f.open("get",e,!0),f.send(null)}else{var h=[{title:c.dataset.title,author:c.dataset.author,url:c.dataset.url,pic:c.dataset.pic,lrc:c.dataset.lrc}];a(c,h)}},i=c[Symbol.iterator]();!(d=(g=i.next()).done);d=!0)h()}catch(a){e=!0,f=a}finally{try{!d&&i.return&&i.return()}finally{if(e)throw f}}},!1);
|
||||
|
@ -7,7 +7,7 @@ $(function() {
|
||||
'<div class="wmd-prompt-dialog">'+
|
||||
'<div>'+
|
||||
'<p><b>插入音乐</b></p>'+
|
||||
'<p>请在下方的输入框内输入要插入的音乐地址,如多个地址请用回车隔开'+
|
||||
'<p>请在下方的输入框内输入要插入的音乐地址'+
|
||||
'<p><input type="text"></input></p>'+
|
||||
'</div>'+
|
||||
'<form>'+
|
||||
|
8
driver/cache.interface.php
Normal file
8
driver/cache.interface.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
interface MetingCacheI
|
||||
{
|
||||
public function install();
|
||||
public function set($key, $value, $expire = 86400);
|
||||
public function get($key);
|
||||
public function flush();
|
||||
}
|
57
driver/mysql.class.php
Normal file
57
driver/mysql.class.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
class MetingCache implements MetingCacheI
|
||||
{
|
||||
private $db = null;
|
||||
public function __construct($option)
|
||||
{
|
||||
$this->db = Typecho_Db::get();
|
||||
$dbname = $this->db->getPrefix() . 'metingcache';
|
||||
$sql = "SHOW TABLES LIKE '%" . $dbname . "%'";
|
||||
if (count($this->db->fetchAll($sql)) == 0) {
|
||||
$this->install();
|
||||
} else {
|
||||
$this->db->query($this->db->delete('table.metingcache')->where('time <= ?', time()));
|
||||
}
|
||||
}
|
||||
public function install()
|
||||
{
|
||||
$sql = '
|
||||
DROP TABLE IF EXISTS `%dbname%`;
|
||||
CREATE TABLE `%dbname%` (
|
||||
`key` char(32) NOT NULL,
|
||||
`data` mediumtext,
|
||||
`time` bigint(20) DEFAULT NULL,
|
||||
PRIMARY KEY (`key`)
|
||||
) ENGINE=MyISAM DEFAULT CHARSET=%charset%';
|
||||
$dbname = $this->db->getPrefix() . 'metingcache';
|
||||
$search = array('%dbname%', '%charset%');
|
||||
$replace = array($dbname, str_replace('UTF-8', 'utf8', Helper::options()->charset));
|
||||
|
||||
$sql = str_replace($search, $replace, $sql);
|
||||
$sqls = explode(';', $sql);
|
||||
foreach ($sqls as $sql) {
|
||||
$this->db->query($sql);
|
||||
}
|
||||
}
|
||||
public function set($key, $value, $expire = 86400)
|
||||
{
|
||||
$this->db->query($this->db->insert('table.metingcache')->rows(array(
|
||||
'key' => md5($key),
|
||||
'data' => $value,
|
||||
'time' => time() + $expire
|
||||
)));
|
||||
}
|
||||
public function get($key)
|
||||
{
|
||||
$rs = $this->db->fetchRow($this->db->select('data')->from('table.metingcache')->where('key = ?', md5($key)));
|
||||
if (count($rs) == 0) {
|
||||
return false;
|
||||
} else {
|
||||
return $rs['data'];
|
||||
}
|
||||
}
|
||||
public function flush()
|
||||
{
|
||||
return $this->db->query($this->db->delete('table.metingcache'));
|
||||
}
|
||||
}
|
25
driver/redis.class.php
Normal file
25
driver/redis.class.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
class MetingCache implements MetingCacheI
|
||||
{
|
||||
private $redis = null;
|
||||
public function __construct($option)
|
||||
{
|
||||
$this->redis = new Redis();
|
||||
$this->redis->connect($option['host'], $option['port']);
|
||||
}
|
||||
public function install()
|
||||
{
|
||||
}
|
||||
public function set($key, $value, $expire = 86400)
|
||||
{
|
||||
return $this->redis->set($key, $value, $expire);
|
||||
}
|
||||
public function get($key)
|
||||
{
|
||||
return $this->redis->get($key);
|
||||
}
|
||||
public function flush()
|
||||
{
|
||||
return $this->redis->flushDb();
|
||||
}
|
||||
}
|
56
driver/sqlite.class.php
Normal file
56
driver/sqlite.class.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
class MetingCache implements MetingCacheI
|
||||
{
|
||||
private $db = null;
|
||||
public function __construct($option)
|
||||
{
|
||||
$this->db = Typecho_Db::get();
|
||||
$dbname = $this->db->getPrefix() . 'metingcache';
|
||||
$sql = "SELECT name FROM sqlite_master WHERE type='table' AND name= '" . $dbname . "'";
|
||||
if (count($this->db->fetchAll($sql)) == 0) {
|
||||
$this->install();
|
||||
} else {
|
||||
$this->db->query($this->db->delete('table.metingcache')->where('time <= ?', time()));
|
||||
}
|
||||
}
|
||||
public function install()
|
||||
{
|
||||
$sql = '
|
||||
DROP TABLE IF EXISTS `%dbname%`;
|
||||
CREATE TABLE `%dbname%` (
|
||||
`key` varchar(32) NOT NULL PRIMARY KEY,
|
||||
`data` text,
|
||||
`time` int(20) DEFAULT NULL
|
||||
);';
|
||||
$dbname = $this->db->getPrefix() . 'metingcache';
|
||||
$search = array('%dbname%');
|
||||
$replace = array($dbname);
|
||||
|
||||
$sql = str_replace($search, $replace, $sql);
|
||||
$sqls = explode(';', $sql);
|
||||
foreach ($sqls as $sql) {
|
||||
$this->db->query($sql);
|
||||
}
|
||||
}
|
||||
public function set($key, $value, $expire = 86400)
|
||||
{
|
||||
$this->db->query($this->db->insert('table.metingcache')->rows(array(
|
||||
'key' => md5($key),
|
||||
'data' => $value,
|
||||
'time' => time() + $expire
|
||||
)));
|
||||
}
|
||||
public function get($key)
|
||||
{
|
||||
$rs = $this->db->fetchRow($this->db->select('data')->from('table.metingcache')->where('key = ?', md5($key)));
|
||||
if (count($rs) == 0) {
|
||||
return false;
|
||||
} else {
|
||||
return $rs['data'];
|
||||
}
|
||||
}
|
||||
public function flush()
|
||||
{
|
||||
return $this->db->query($this->db->delete('table.metingcache'));
|
||||
}
|
||||
}
|
1843
include/Meting.php
1843
include/Meting.php
File diff suppressed because it is too large
Load Diff
12
shasum.txt
Normal file
12
shasum.txt
Normal file
@ -0,0 +1,12 @@
|
||||
87a137ef2b47308dc5815e173041b7f3afa02981d3ee1f6cd8fc5d3c1af7c71c ./Action.php
|
||||
aa3c0deef2f5b5524f9e7cbd1809d14d51e0a5eae8f21e183483c84c28b86e46 ./assets/APlayer.min.js
|
||||
6c1db3ae0b983df3e9395aa133875e64c7cb6321064c982d507fa7d82f7258f3 ./assets/editer.js
|
||||
05394bccf0b72f8a57bdefc2dc7da1da38d6ebcf39b97185a89c359f9827a556 ./assets/Meting.min.js
|
||||
c43462daad99f50a03ac36ab1f53249a0128548b0d13bacecda81812fd695c56 ./driver/cache.interface.php
|
||||
cd8087c1c5b99d5343f07b5e78279bddeca35b8d3039c8946437271120695b5f ./driver/mysql.class.php
|
||||
5bc1ce7e8b6629bd713c0c9287ac6eb3e91aae2a8f989c83028376da74876305 ./driver/redis.class.php
|
||||
1b2791f8ced874d65b89d5014756cd23ddaca347965ef6c739ae40e2a7ba6b16 ./driver/sqlite.class.php
|
||||
9439991d1f138e073c3cfeeca921539c0dc53d068ca4ec0c02e377c8397359da ./include/Meting.php
|
||||
a237e65d8cc7c5b00008db5977b28e525caeffadfee0808408034f8959c6de20 ./LICENSE
|
||||
ce6446965969e32bd3a98107e878fcef14acba6de697ae8782c3f0c3435bb448 ./Plugin.php
|
||||
b854090ea5398d0b448ec23c06e5dee12d807610a9046a4eabfd31d15c9e38c5 ./README.md
|
Loading…
Reference in New Issue
Block a user