mirror of
https://github.com/MoePlayer/APlayer-Typecho.git
synced 2024-12-21 20:30:24 +08:00
00f8c05a65
- 升级 MetingJS 1.1.1 - 增强接口安全性 - 修复虾米音乐 HTTPS
397 lines
15 KiB
PHP
397 lines
15 KiB
PHP
<?php
|
|
if (!defined('__TYPECHO_ROOT_DIR__')) {
|
|
exit;
|
|
}
|
|
|
|
class Meting_Action extends Typecho_Widget implements Widget_Interface_Do
|
|
{
|
|
public function execute()
|
|
{
|
|
}
|
|
|
|
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($server, $type, $id)
|
|
{
|
|
if (!in_array($server, array('netease','tencent','baidu','xiami','kugou'))) {
|
|
return false;
|
|
}
|
|
if (!in_array($type, array('song','album','search','artist','playlist','lrc','url','pic'))) {
|
|
return false;
|
|
}
|
|
if (empty($id)) {
|
|
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, $id)) {
|
|
http_response_code(403);
|
|
die();
|
|
}
|
|
|
|
// 加载 Meting 模块
|
|
if (!extension_loaded('Meting')) {
|
|
include_once 'include/Meting.php';
|
|
}
|
|
$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);
|
|
}
|
|
|
|
// 加载 Meting Cache 模块
|
|
if (!extension_loaded('MetingCache')) {
|
|
$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
|
|
));
|
|
}
|
|
}
|
|
|
|
// auth 验证
|
|
$EID = $server.$type.$id;
|
|
$salt = Typecho_Widget::widget('Widget_Options')->plugin('Meting')->salt;
|
|
|
|
if (!empty($salt)) {
|
|
$auth1 = md5($salt.$EID.$salt);
|
|
$auth2 = $this->request->get('auth');
|
|
if (strcmp($auth1, $auth2)) {
|
|
http_response_code(403);
|
|
die();
|
|
}
|
|
}
|
|
|
|
// 歌词解析
|
|
if ($type == 'lrc') {
|
|
$data = $this->cacheRead($EID);
|
|
if (empty($data)) {
|
|
$data = $api->lyric($id);
|
|
$this->cacheWrite($EID, $data, 86400);
|
|
}
|
|
$data = json_decode($data, true);
|
|
header("Content-Type: application/javascript");
|
|
if (!empty($data['tlyric'])) {
|
|
echo $this->lrctran($data['lyric'], $data['tlyric']);
|
|
} else {
|
|
echo $data['lyric'];
|
|
}
|
|
}
|
|
|
|
// 专辑图片解析
|
|
if ($type == 'pic') {
|
|
$data = $this->cacheRead($EID);
|
|
if (empty($data)) {
|
|
$data = $api->pic($id, 90);
|
|
$this->cacheWrite($EID, $data, 86400);
|
|
}
|
|
$data = json_decode($data, true);
|
|
$this->response->redirect($data['url']);
|
|
}
|
|
|
|
// 歌曲链接解析
|
|
if ($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'];
|
|
|
|
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 ($server == 'xiami') {
|
|
$url = str_replace('http://', 'https://', $url);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
// 其它类别解析
|
|
if (in_array($type, array('song','album','search','artist','playlist'))) {
|
|
$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);
|
|
|
|
$music = array();
|
|
foreach ($data as $vo) {
|
|
$music[] = array(
|
|
'name' => $vo['name'],
|
|
'artist' => implode(' / ', $vo['artist']),
|
|
'url' => $url.'?server='.$vo['source'].'&type=url&id='.$vo['url_id'].'&auth='.md5($salt.$vo['source'].'url'.$vo['url_id'].$salt),
|
|
'cover' => $url.'?server='.$vo['source'].'&type=pic&id='.$vo['pic_id'].'&auth='.md5($salt.$vo['source'].'pic'.$vo['pic_id'].$salt),
|
|
'lrc' => $url.'?server='.$vo['source'].'&type=lrc&id='.$vo['lyric_id'].'&auth='.md5($salt.$vo['source'].'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('/playsquare\/([^\.]*)/i', $url, $id)) {
|
|
list($id, $type) = array($id[1],'playlist');
|
|
} elseif (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('/#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;
|
|
}
|
|
if (is_array($id)) {
|
|
$id = '';
|
|
}
|
|
die("[Meting]\n[Music server=\"{$server}\" id=\"{$id}\" type=\"{$type}\"/]\n[/Meting]\n");
|
|
|
|
}
|
|
|
|
private function lrctrim($lyrics)
|
|
{
|
|
$result = "";
|
|
$lyrics = explode("\n", $lyrics);
|
|
$data = array();
|
|
foreach ($lyrics as $key => $lyric) {
|
|
preg_match('/\[(\d{2}):(\d{2}[\.:]?\d*)]/', $lyric, $lrcTimes);
|
|
$lrcText = preg_replace('/\[(\d{2}):(\d{2}[\.:]?\d*)]/', '', $lyric);
|
|
if (empty($lrcTimes)) {
|
|
continue;
|
|
}
|
|
$lrcTimes = intval($lrcTimes[1]) * 60000 + intval(floatval($lrcTimes[2]) * 1000);
|
|
$lrcText = preg_replace('/\s\s+/', ' ', $lrcText);
|
|
$lrcText = trim($lrcText);
|
|
$data[] = array($lrcTimes, $key, $lrcText);
|
|
}
|
|
sort($data);
|
|
return $data;
|
|
}
|
|
|
|
private function lrctran($lyric, $tlyric)
|
|
{
|
|
$lyric = $this->lrctrim($lyric);
|
|
$tlyric = $this->lrctrim($tlyric);
|
|
$len1 = count($lyric);
|
|
$len2 = count($tlyric);
|
|
$result = "";
|
|
for ($i=0,$j=0; $i<$len1&&$j<$len2; $i++) {
|
|
while ($lyric[$i][0]>$tlyric[$j][0]&&$j+1<$len2) {
|
|
$j++;
|
|
}
|
|
if ($lyric[$i][0] == $tlyric[$j][0]) {
|
|
$tlyric[$j][2] = str_replace('/', '', $tlyric[$j][2]);
|
|
if (!empty($tlyric[$j][2])) {
|
|
$lyric[$i][2] .= " ({$tlyric[$j][2]})";
|
|
}
|
|
$j++;
|
|
}
|
|
}
|
|
for ($i=0; $i<$len1; $i++) {
|
|
$t = $lyric[$i][0];
|
|
$result .= sprintf("[%02d:%02d.%03d]%s\n", $t/60000, $t%60000/1000, $t%1000, $lyric[$i][2]);
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
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('Forbidden!');
|
|
}
|
|
|
|
header("Content-Type: text/plain; charset=UTF-8");
|
|
|
|
$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) ||
|
|
!hash_equals(hash('sha256', file_get_contents(__DIR__.'/'.$filename)), $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, $t)
|
|
{
|
|
if (!isset($this->cache)) {
|
|
return;
|
|
}
|
|
return $this->cache->set($k, $v, $t);
|
|
}
|
|
|
|
private function cacheRead($k)
|
|
{
|
|
if (!isset($this->cache)) {
|
|
return false;
|
|
}
|
|
return $this->cache->get($k);
|
|
}
|
|
|
|
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']) && parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST) !== $_SERVER['HTTP_HOST']) {
|
|
http_response_code(403);
|
|
die('[]');
|
|
}
|
|
}
|
|
}
|