mirror of
https://github.com/MoePlayer/APlayer-Typecho.git
synced 2024-12-21 20:30:24 +08:00
[realse] v1.0.0
This commit is contained in:
parent
50fa1483be
commit
99cdf4a43f
215
Action.php
Normal file
215
Action.php
Normal file
@ -0,0 +1,215 @@
|
||||
<?php
|
||||
if(!defined('__TYPECHO_ROOT_DIR__'))exit;
|
||||
if (!extension_loaded('Meting'))include_once 'include/Meting.php';
|
||||
|
||||
class Meting_Action extends Typecho_Widget implements Widget_Interface_Do {
|
||||
|
||||
public function execute(){}
|
||||
|
||||
public function action(){
|
||||
$this->on($this->request->is('do=js'))->js();
|
||||
$this->on($this->request->is('do=url'))->url();
|
||||
$this->on($this->request->is('do=pic'))->pic();
|
||||
$this->on($this->request->is('do=lrc'))->lrc();
|
||||
$this->on($this->request->is('do=parse'))->shortcode();
|
||||
}
|
||||
|
||||
private function shortcode(){
|
||||
$urls=$this->request->get('data');
|
||||
$urls=explode("\n",$urls);
|
||||
echo "[Meting]\n";
|
||||
foreach($urls as $url){
|
||||
$url=trim($url);
|
||||
if($url=="")continue;
|
||||
$server='音乐平台';$id='编号';$type='类型';
|
||||
if(strpos($url,'163.com')!==false){
|
||||
$server='netease';
|
||||
if(preg_match('/playlist\?id=(\d+)/i',$url,$id))list($id,$type)=array($id[1],'playlist');
|
||||
elseif(preg_match('/album\?id=(\d+)/i',$url,$id))list($id,$type)=array($id[1],'album');
|
||||
elseif(preg_match('/song\?id=(\d+)/i',$url,$id))list($id,$type)=array($id[1],'song');
|
||||
elseif(preg_match('/artist\?id=(\d+)/i',$url,$id))list($id,$type)=array($id[1],'artist');
|
||||
}
|
||||
elseif(strpos($url,'qq.com')!==false){
|
||||
$server='tencent';
|
||||
if(preg_match('/playlist\/([^\.]*)/i',$url,$id))list($id,$type)=array($id[1],'playlist');
|
||||
elseif(preg_match('/album\/([^\.]*)/i',$url,$id))list($id,$type)=array($id[1],'album');
|
||||
elseif(preg_match('/song\/([^\.]*)/i',$url,$id))list($id,$type)=array($id[1],'song');
|
||||
elseif(preg_match('/singer\/([^\.]*)/i',$url,$id))list($id,$type)=array($id[1],'artist');
|
||||
}
|
||||
elseif(strpos($url,'xiami.com')!==false){
|
||||
$server='xiami';
|
||||
if(preg_match('/collect\/([^\.]*)/i',$url,$id))list($id,$type)=array($id[1],'playlist');
|
||||
elseif(preg_match('/album\/([^\.]*)/i',$url,$id))list($id,$type)=array($id[1],'album');
|
||||
elseif(preg_match('/song\/([^\.]*)/i',$url,$id))list($id,$type)=array($id[1],'song');
|
||||
elseif(preg_match('/artist\/([^\.]*)/i',$url,$id))list($id,$type)=array($id[1],'artist');
|
||||
|
||||
if(!preg_match('/^\d*$/i',$id,$t)){
|
||||
$data=self::curl($url);
|
||||
preg_match('/'.$type.'\/(\d+)/i',$data,$id);
|
||||
$id=$id[1];
|
||||
}
|
||||
}
|
||||
elseif(strpos($url,'kugou.com')!==false){
|
||||
$server='kugou';
|
||||
if(preg_match('/special\/single\/(\d+)/i',$url,$id))list($id,$type)=array($id[1],'playlist');
|
||||
elseif(preg_match('/album\/single\/(\d+)/i',$url,$id))list($id,$type)=array($id[1],'album');
|
||||
elseif(preg_match('/singer\/home\/(\d+)/i',$url,$id))list($id,$type)=array($id[1],'artist');
|
||||
}
|
||||
elseif(strpos($url,'baidu.com')!==false){
|
||||
$server='baidu';
|
||||
if(preg_match('/songlist\/(\d+)/i',$url,$id))list($id,$type)=array($id[1],'playlist');
|
||||
elseif(preg_match('/album\/(\d+)/i',$url,$id))list($id,$type)=array($id[1],'album');
|
||||
elseif(preg_match('/song\/(\d+)/i',$url,$id))list($id,$type)=array($id[1],'song');
|
||||
elseif(preg_match('/artist\/(\d+)/i',$url,$id))list($id,$type)=array($id[1],'artist');
|
||||
}
|
||||
else list($id,$type)=array($url,'search');
|
||||
echo '[Music server="'.$server.'" id="'.$id.'" type="'.$type.'"/]'."\n";
|
||||
}
|
||||
echo "[/Meting]";
|
||||
}
|
||||
|
||||
private function curl($url){
|
||||
$curl=curl_init();
|
||||
curl_setopt($curl,CURLOPT_URL,$url);
|
||||
curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);
|
||||
curl_setopt($curl,CURLOPT_CONNECTTIMEOUT,10);
|
||||
curl_setopt($curl,CURLOPT_TIMEOUT,10);
|
||||
curl_setopt($curl,CURLOPT_REFERER,$url);
|
||||
$result=curl_exec($curl);
|
||||
curl_close($curl);
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function js(){
|
||||
$PID=$this->request->get('id');
|
||||
$data=$this->request->get('d');
|
||||
$data=json_decode(base64_decode($data),1);
|
||||
$setting=$this->request->get('s');
|
||||
$setting=json_decode(base64_decode($setting),1);
|
||||
$music=array();
|
||||
foreach($data as $vo){
|
||||
$EID=md5('js/'.$vo['server'].'/'.$vo['type'].'/'.$vo['id']);
|
||||
$t=self::cacheRead($EID,60*60*24*3);
|
||||
if(!$t){
|
||||
$API=(new Meting($vo['server']))->format(true);
|
||||
$t=call_user_func_array(array($API,$vo['type']),array($vo['id']));
|
||||
$t=json_decode($t,1);
|
||||
self::cacheWrite($EID,$t);
|
||||
}
|
||||
$music=array_merge($music,$t);
|
||||
}
|
||||
$player=array(
|
||||
'theme' => $setting['theme']?:Typecho_Widget::widget('Widget_Options')->plugin('Meting')->theme?:'red',
|
||||
'preload' => $setting['preload']?:Typecho_Widget::widget('Widget_Options')->plugin('Meting')->preload?:'auto',
|
||||
'autoplay' => $setting['autoplay']?:Typecho_Widget::widget('Widget_Options')->plugin('Meting')->autoplay?:'false',
|
||||
'height' => $setting['height']?:Typecho_Widget::widget('Widget_Options')->plugin('Meting')->height?:'340px',
|
||||
'mode' => $setting['mode']?:Typecho_Widget::widget('Widget_Options')->plugin('Meting')->mode?:'circulation',
|
||||
'music' => array(),
|
||||
);
|
||||
foreach($music as $vo){
|
||||
$URI=Typecho_Common::url('MetingAPI?site='.$vo['source'],Helper::options()->index);
|
||||
$player['music'][]=array(
|
||||
'title'=>$vo['name'],
|
||||
'author'=>implode(' / ',$vo['artist']),
|
||||
'url'=>$URI.'&do=url&id='.$vo['url_id'],
|
||||
'pic'=>$URI.'&do=pic&id='.$vo['pic_id'],
|
||||
'lrc'=>$URI.'&do=lrc&id='.$vo['lyric_id'],
|
||||
);
|
||||
}
|
||||
if(sizeof($player['music'])==1)$player['music']=$player['music'][0];
|
||||
$player['music']=json_encode($player['music']);
|
||||
|
||||
header('content-type:application/javascript');
|
||||
echo"
|
||||
var Meting{$PID} = new APlayer({
|
||||
element: document.getElementById('MetingPlayer'+{$PID}),
|
||||
autoplay: ".$player['autoplay'].",
|
||||
preload: \"".$player['preload']."\",
|
||||
showlrc: 3,
|
||||
mutex: true,
|
||||
mode: '".$player['mode']."',
|
||||
theme: \"".$player['theme']."\",
|
||||
music: ".$player['music'].",
|
||||
listmaxheight: '".$player['height']."',
|
||||
});
|
||||
";
|
||||
}
|
||||
|
||||
private function url(){
|
||||
$id=$this->request->get('id');
|
||||
$site=$this->request->get('site');
|
||||
$rate=Typecho_Widget::widget('Widget_Options')->plugin('Meting')->bitrate;
|
||||
|
||||
$cachekey="url/{$site}/{$id}";
|
||||
$data=self::cacheRead($cachekey,60*15);
|
||||
if(!$data){
|
||||
$data=(new Meting($site))->url($id,$rate);
|
||||
$data=json_decode($data,1);
|
||||
self::cacheWrite($cachekey,$data);
|
||||
}
|
||||
if(empty($data['url']))$data['url']='https://oc1pe0tot.qnssl.com/copyright.m4a';
|
||||
$this->response->redirect($data['url']);
|
||||
}
|
||||
|
||||
private function pic(){
|
||||
$id=$this->request->get('id');
|
||||
$site=$this->request->get('site');
|
||||
|
||||
$cachekey="pic/{$site}/{$id}";
|
||||
$data=self::cacheRead($cachekey,60*60*24*30);
|
||||
if(!$data){
|
||||
$data=(new Meting($site))->pic($id,90);
|
||||
$data=json_decode($data,1);
|
||||
self::cacheWrite($cachekey,$data);
|
||||
}
|
||||
$this->response->redirect($data['url']);
|
||||
}
|
||||
|
||||
private function lrc(){
|
||||
$id=$this->request->get('id');
|
||||
$site=$this->request->get('site');
|
||||
|
||||
$cachekey="lrc/{$site}/{$id}";
|
||||
$data=self::cacheRead($cachekey,60*60*24*10);
|
||||
if(!$data){
|
||||
$data=(new Meting($site))->lyric($id);
|
||||
$data=json_decode($data,1);
|
||||
self::cacheWrite($cachekey,$data);
|
||||
}
|
||||
|
||||
switch($site){
|
||||
case 'netease':$text=$data['lrc']['lyric'];break;
|
||||
case 'tencent':$text=base64_decode($data['lyric']);break;
|
||||
case 'xiami':$text=$data['lyric'];break;
|
||||
case 'kugou':$text=$data['lyric'];break;
|
||||
case 'baidu':$text=$data['lrcContent'];break;
|
||||
}
|
||||
if(strlen($text)==0)$text='[00:00.00]无歌词';
|
||||
echo $text;
|
||||
}
|
||||
|
||||
private function cacheWrite($k,$v){
|
||||
if(!is_array($v))return;
|
||||
$db=Typecho_Db::get();
|
||||
$prefix=$db->getPrefix();
|
||||
$insert=$db->insert($prefix.'meting')->rows(array('id'=>sha1($k),'value'=>serialize($v),'date'=>time()));
|
||||
return $db->query($insert);
|
||||
}
|
||||
|
||||
private function cacheRead($k,$t=60*60){
|
||||
$db=Typecho_Db::get();
|
||||
$prefix=$db->getPrefix();
|
||||
$query= $db->select('value','date')->from($prefix.'meting')->where('id=?',sha1($k));
|
||||
$result = $db->fetchAll($query);
|
||||
if(sizeof($result)){
|
||||
if(time()-$result[0]['date']>$t){
|
||||
$delete = $db->delete('value','date')->from($prefix.'meting')->where('date<?',time()-$t);
|
||||
$db->query($delete);
|
||||
return false;
|
||||
}
|
||||
return unserialize($result[0]['value']);
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
}
|
293
Plugin.php
Normal file
293
Plugin.php
Normal file
@ -0,0 +1,293 @@
|
||||
<?php
|
||||
if(!defined('__TYPECHO_ROOT_DIR__'))exit;
|
||||
|
||||
/**
|
||||
* Meting for Typecho
|
||||
*
|
||||
* @package Meting
|
||||
* @author METO
|
||||
* @version 1.0.0
|
||||
* @dependence 13.12.12-*
|
||||
* @link https://github.com/metowolf/Meting
|
||||
*
|
||||
*/
|
||||
|
||||
define('METING_VERSION','1.0.0');
|
||||
|
||||
class Meting_Plugin extends Typecho_Widget implements Typecho_Plugin_Interface
|
||||
{
|
||||
protected static $PID = 0;
|
||||
/**
|
||||
* 激活插件方法,如果激活失败,直接抛出异常
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
* @throws Typecho_Plugin_Exception
|
||||
*/
|
||||
public static function activate(){
|
||||
Meting_Plugin::install();
|
||||
Helper::addRoute("Meting_Route","/MetingAPI","Meting_Action",'action');
|
||||
Typecho_Plugin::factory('Widget_Abstract_Contents')->filter =array('Meting_Plugin','playerFilter');
|
||||
Typecho_Plugin::factory('Widget_Abstract_Contents')->contentEx=array('Meting_Plugin','playerReplace');
|
||||
Typecho_Plugin::factory('Widget_Abstract_Contents')->excerptEx=array('Meting_Plugin','playerReplace');
|
||||
Typecho_Plugin::factory('Widget_Archive')->header=array('Meting_Plugin','header');
|
||||
Typecho_Plugin::factory('Widget_Archive')->footer=array('Meting_Plugin','footer');
|
||||
Typecho_Plugin::factory('admin/write-post.php')->bottom = array('Meting_Plugin', 'addButton');
|
||||
Typecho_Plugin::factory('admin/write-page.php')->bottom = array('Meting_Plugin', 'addButton');
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用插件方法,如果禁用失败,直接抛出异常
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return void
|
||||
* @throws Typecho_Plugin_Exception
|
||||
*/
|
||||
public static function deactivate(){
|
||||
Meting_Plugin::uninstall();
|
||||
Helper::removeRoute("Meting_Route");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件配置面板
|
||||
*
|
||||
* @access public
|
||||
* @param Typecho_Widget_Helper_Form $form 配置面板
|
||||
* @return void
|
||||
*/
|
||||
public static function config(Typecho_Widget_Helper_Form $form){
|
||||
$t = new Typecho_Widget_Helper_Form_Element_Text(
|
||||
'theme', null, '#e9e9e9',
|
||||
_t('播放器颜色'),
|
||||
_t('播放器默认的主题颜色,支持如 #372e21、#75c、red,该设定会被[Meting]标签中的theme属性覆盖,默认为 #e9e9e9'));
|
||||
$form->addInput($t);
|
||||
$t = new Typecho_Widget_Helper_Form_Element_Text(
|
||||
'height', null, '340px',
|
||||
_t('播放器列表最大高度'),
|
||||
_t(''));
|
||||
$form->addInput($t);
|
||||
$t = new Typecho_Widget_Helper_Form_Element_Radio(
|
||||
'autoplay', array('true'=>_t('是'),'false'=>_t('否')),'false',
|
||||
_t('全局自动播放'),
|
||||
_t(''));
|
||||
$form->addInput($t);
|
||||
$t = new Typecho_Widget_Helper_Form_Element_Radio(
|
||||
'mode', array('circulation'=>_t('循环'),'single'=>_t('单曲'),'order'=>_t('列表'),'random'=>_t('随机')),'circulation',
|
||||
_t('全局播放模式'),
|
||||
_t(''));
|
||||
$form->addInput($t);
|
||||
$t= new Typecho_Widget_Helper_Form_Element_Radio(
|
||||
'preload', array('auto'=>_t('自动'),'none'=>_t('不加载'),'metadata'=>_t('加载元数据')), 'auto',
|
||||
_t('预加载属性'),
|
||||
_t(''));
|
||||
$form->addInput($t);
|
||||
$t= new Typecho_Widget_Helper_Form_Element_Radio(
|
||||
'bitrate', array('128'=>_t('流畅品质'),'192'=>_t('清晰品质'),'320'=>_t('高品质')), '192',
|
||||
_t('默认音质'),
|
||||
_t(''));
|
||||
$form->addInput($t);
|
||||
}
|
||||
|
||||
public static function personalConfig(Typecho_Widget_Helper_Form $form){}
|
||||
|
||||
public static function render(){}
|
||||
|
||||
/**
|
||||
* 获取插件配置面板
|
||||
*
|
||||
* @access public
|
||||
* @param Typecho_Widget_Helper_Form $form 配置面板
|
||||
* @return void
|
||||
*/
|
||||
public static function header(){
|
||||
$dir=Helper::options()->pluginUrl.'/Meting/assets/';
|
||||
echo "<!-- Meting ".METING_VERSION." Start -->\n<script type=\"text/javascript\" src=\"{$dir}APlayer.min.js\"></script>\n<!-- Meting End -->";
|
||||
}
|
||||
|
||||
public static function footer(){}
|
||||
|
||||
public static function getPID(){
|
||||
return ++self::$PID;
|
||||
}
|
||||
|
||||
public static function playerFilter($data){
|
||||
return $data;
|
||||
}
|
||||
|
||||
public static function playerReplace($data,$widget,$last){
|
||||
$text=$last?:$data;
|
||||
if($widget instanceof Widget_Archive){
|
||||
$data=$text;
|
||||
$pattern=self::get_shortcode_regex(array('Meting'));
|
||||
$text=preg_replace_callback("/$pattern/",array('Meting_Plugin','parseCallback'),$data);
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
public static function parseCallback($matches){
|
||||
$t=self::shortcode_parse_atts(htmlspecialchars_decode($matches[3]));
|
||||
$setting=base64_encode(json_encode($t));
|
||||
|
||||
$matches[5]=htmlspecialchars_decode($matches[5]);
|
||||
$pattern=self::get_shortcode_regex(array('Music'));
|
||||
preg_match_all("/$pattern/",$matches[5],$all);
|
||||
$data=array();
|
||||
foreach($all[3] as $vo){
|
||||
$t=self::shortcode_parse_atts(htmlspecialchars_decode($vo));
|
||||
if(!in_array($t['server'],['netease','tencent','xiami','baidu','kugou']))continue;
|
||||
if(!in_array($t['type'],['search','album','playlist','artist','song']))continue;
|
||||
$data[]=$t;
|
||||
}
|
||||
$id=self::getPID();
|
||||
$dir=Typecho_Common::url('MetingAPI',Helper::options()->index);
|
||||
$data=base64_encode(json_encode($data));
|
||||
return <<<EOF
|
||||
<div id="MetingPlayer{$id}" class="aplayer" /></div>
|
||||
<script type="text/javascript" src="{$dir}?do=js&s={$setting}&d={$data}&id={$id}" async defer></script>
|
||||
|
||||
EOF;
|
||||
}
|
||||
|
||||
public static function addButton(){
|
||||
$url=Typecho_Common::url('MetingAPI?do=parse',Helper::options()->index);
|
||||
echo <<<EOF
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
if($('#wmd-button-row').length>0)$('#wmd-button-row').append('<li class="wmd-spacer wmd-spacer1" id="wmd-spacer5"></li><li class="wmd-button" id="wmd-music-button" style="" title="插入音乐">音乐</li>');
|
||||
$(document).on('click', '#wmd-music-button', function() {
|
||||
$('body').append(
|
||||
'<div id="MetingPanel">'+
|
||||
'<div class="wmd-prompt-background" style="position: absolute; top: 0px; z-index: 1000; opacity: 0.5; height: 875px; left: 0px; width: 100%;"></div>'+
|
||||
'<div class="wmd-prompt-dialog">'+
|
||||
'<div>'+
|
||||
'<p><b>插入音乐</b></p>'+
|
||||
'<p>请在下方的输入框内输入要插入的音乐地址,如多个地址请用回车隔开'+
|
||||
'<p><textarea type="text" rows="5" cols="40"></textarea></p>'+
|
||||
'</div>'+
|
||||
'<form>'+
|
||||
'<button type="button" class="btn btn-s primary" id="ok">确定</button>'+
|
||||
'<button type="button" class="btn btn-s" id="cancel">取消</button>'+
|
||||
'</form>'+
|
||||
'</div>'+
|
||||
'</div>');
|
||||
$('.wmd-prompt-dialog textarea').val('http://').select();
|
||||
});
|
||||
$(document).on('click','#cancel',function() {
|
||||
$('#MetingPanel').remove();
|
||||
$('textarea').focus();
|
||||
});
|
||||
$(document).on('click','#ok',function() {
|
||||
callback=$.ajax({
|
||||
type:'POST',
|
||||
url:"{$url}",
|
||||
data:{data:$('.wmd-prompt-dialog textarea').val()},
|
||||
async:false
|
||||
});
|
||||
$('#MetingPanel').remove();
|
||||
myField = document.getElementById('text');
|
||||
if (document.selection) {
|
||||
myField.focus();
|
||||
sel = document.selection.createRange();
|
||||
sel.text = callback.responseText;
|
||||
myField.focus();
|
||||
}
|
||||
else if (myField.selectionStart || myField.selectionStart == '0') {
|
||||
var startPos = myField.selectionStart;
|
||||
var endPos = myField.selectionEnd;
|
||||
var cursorPos = startPos;
|
||||
myField.value = myField.value.substring(0, startPos)
|
||||
+ callback.responseText
|
||||
+ myField.value.substring(endPos, myField.value.length);
|
||||
cursorPos += callback.responseText.length;
|
||||
myField.focus();
|
||||
myField.selectionStart = cursorPos;
|
||||
myField.selectionEnd = cursorPos;
|
||||
}
|
||||
else{
|
||||
myField.value += callback.responseText;
|
||||
myField.focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
EOF;
|
||||
}
|
||||
|
||||
# https://github.com/WordPress/WordPress/blob/master/wp-includes/shortcodes.php#L508
|
||||
private static function shortcode_parse_atts($text) {
|
||||
$atts = array();
|
||||
$pattern = '/([\w-]+)\s*=\s*"([^"]*)"(?:\s|$)|([\w-]+)\s*=\s*\'([^\']*)\'(?:\s|$)|([\w-]+)\s*=\s*([^\s\'"]+)(?:\s|$)|"([^"]*)"(?:\s|$)|(\S+)(?:\s|$)/';
|
||||
$text = preg_replace("/[\x{00a0}\x{200b}]+/u", " ", $text);
|
||||
if ( preg_match_all($pattern, $text, $match, PREG_SET_ORDER) ) {
|
||||
foreach ($match as $m) {
|
||||
if (!empty($m[1]))
|
||||
$atts[strtolower($m[1])] = stripcslashes($m[2]);
|
||||
elseif (!empty($m[3]))
|
||||
$atts[strtolower($m[3])] = stripcslashes($m[4]);
|
||||
elseif (!empty($m[5]))
|
||||
$atts[strtolower($m[5])] = stripcslashes($m[6]);
|
||||
elseif (isset($m[7]) && strlen($m[7]))
|
||||
$atts[] = stripcslashes($m[7]);
|
||||
elseif (isset($m[8]))
|
||||
$atts[] = stripcslashes($m[8]);
|
||||
}
|
||||
foreach( $atts as &$value ) {
|
||||
if ( false !== strpos( $value, '<' ) ) {
|
||||
if ( 1 !== preg_match( '/^[^<]*+(?:<[^>]*+>[^<]*+)*+$/', $value ) ) {
|
||||
$value = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$atts = ltrim($text);
|
||||
}
|
||||
return $atts;
|
||||
}
|
||||
|
||||
# https://github.com/WordPress/WordPress/blob/master/wp-includes/shortcodes.php#L254
|
||||
private static function get_shortcode_regex( $tagnames = null ) {
|
||||
$tagregexp = join( '|', array_map('preg_quote', $tagnames) );
|
||||
return '\[(\[?)('.$tagregexp.')(?![\w-])([^\]\/]*(?:\/(?!\])[^\]\/]*)*?)(?:(\/)\]|\](?:([^\[]*+(?:\[(?!\/\2\])[^\[]*+)*+)\[\/\2\])?)(\]?)';
|
||||
}
|
||||
|
||||
public static function install(){
|
||||
$db=Typecho_Db::get();
|
||||
$prefix=$db->getPrefix();
|
||||
$scripts=file_get_contents(__DIR__.'/include/install.sql');
|
||||
$scripts=str_replace('typecho_',$prefix,$scripts);
|
||||
$scripts=explode(';', $scripts);
|
||||
try{
|
||||
foreach($scripts as $script){
|
||||
$script=trim($script);
|
||||
if($script){
|
||||
$db->query($script,Typecho_Db::WRITE);
|
||||
}
|
||||
}
|
||||
}catch(Typecho_Db_Exception $e){
|
||||
$code=$e->getCode();
|
||||
if($code=='42S01')return;
|
||||
throw new Typecho_Plugin_Exception('数据表建立失败,插件启用失败。错误号: '.$code);
|
||||
}
|
||||
}
|
||||
|
||||
public static function uninstall(){
|
||||
$db=Typecho_Db::get();
|
||||
$prefix=$db->getPrefix();
|
||||
$scripts=file_get_contents(__DIR__.'/include/uninstall.sql');
|
||||
$scripts=str_replace('typecho_',$prefix,$scripts);
|
||||
$scripts=explode(';', $scripts);
|
||||
try{
|
||||
foreach($scripts as $script){
|
||||
$script=trim($script);
|
||||
if($script){
|
||||
$db->query($script,Typecho_Db::WRITE);
|
||||
}
|
||||
}
|
||||
}catch(Typecho_Db_Exception $e){
|
||||
$code=$e->getCode();
|
||||
if($code=='42S01')return;
|
||||
throw new Typecho_Plugin_Exception('数据表清空失败,插件禁用失败。错误号: '.$code);
|
||||
}
|
||||
}
|
||||
}
|
64
README.md
Normal file
64
README.md
Normal file
@ -0,0 +1,64 @@
|
||||
# Meting for Typecho
|
||||
在 Typecho 中使用 APlayer 播放在线音乐吧~
|
||||
[在线演示](http://demo.i-meto.com)
|
||||
|
||||
## 介绍
|
||||
1. 支持国内五大音乐平台(网易云、QQ、虾米、百度、酷狗)的单曲/专辑/歌单播放
|
||||
2. 简单快捷,复制音乐详情页面网址,后台自动生成播放代码
|
||||
3. **支持不同音乐平台歌曲混合播放**
|
||||
4. 前端 Aplayer,后端 Meting 及时更新,保证兼容性及 API 高可用性
|
||||
|
||||
## 声明
|
||||
本作品仅供个人学习研究使用,请勿将其用作商业用途。
|
||||
**!!切勿使用本插件代码下载版权保护音乐!!**
|
||||
|
||||
## 安装
|
||||
1. 在本页面右上角点击 Download ZIP 下载压缩包
|
||||
2. 上传到 /usr/plugins 目录
|
||||
3. **修改文件夹名为 Meting**
|
||||
4. 后台启用插件
|
||||
|
||||
## 使用
|
||||
在文章编辑页面,点击编辑器上的 **音乐** 按钮,在弹出的窗口中输入音乐地址(见支持列表),最后点击确定即可
|
||||
**歌曲混合特性:** 允许添加多个音乐地址,每行一个,插件会自动将所有歌曲合并在同一个歌单进行播放。
|
||||
|
||||
## 支持列表
|
||||
网易云音乐 http://music.163.com
|
||||
- 单曲 http://music.163.com/#/song?id=424474911
|
||||
- 专辑 http://music.163.com/#/album?id=34808540
|
||||
- 歌手 http://music.163.com/#/artist?id=3681
|
||||
- 歌单 http://music.163.com/#/playlist?id=436843836
|
||||
|
||||
QQ 音乐 http://y.qq.com
|
||||
- 单曲 https://y.qq.com/portal/song/000jDQWP4JiB3y.html
|
||||
- 专辑 https://y.qq.com/portal/album/003rytri2FHG3V.html
|
||||
- 歌手 https://y.qq.com/portal/singer/003Nz2So3XXYek.html
|
||||
- 歌单 https://y.qq.com/portal/playlist/1144188779.html
|
||||
|
||||
虾米音乐 http://www.xiami.com or http://h.xiami.com
|
||||
- 单曲 http://www.xiami.com/song/bf08DNT3035f
|
||||
- 专辑 http://www.xiami.com/album/ddOZW6a10eb
|
||||
- 歌手 http://www.xiami.com/artist/be6yda0f8
|
||||
- 歌单 http://www.xiami.com/collect/254478782
|
||||
|
||||
酷狗音乐 http://www.kugou.com
|
||||
- 单曲 暂不支持直接解析,可直接修改短代码实现
|
||||
- 专辑 http://www.kugou.com/yy/album/single/1645030.html
|
||||
- 歌手 http://www.kugou.com/yy/singer/home/3520.html
|
||||
- 歌单 http://www.kugou.com/yy/special/single/119859.html
|
||||
|
||||
百度音乐 http://music.baidu.com/
|
||||
- 单曲 http://music.baidu.com/song/268275324
|
||||
- 专辑 http://music.baidu.com/album/268275533
|
||||
- 歌手 http://music.baidu.com/artist/1219
|
||||
- 歌单 http://music.baidu.com/songlist/364201689
|
||||
|
||||
## FAQ
|
||||
Q: 如何清除歌单、歌词缓存?
|
||||
A: 为了减少服务器压力,插件设置对歌单、歌词数据进行缓存,缓存会根据时间周期自动更新管理,无需人工干预。**如果需要强制清除,可以通过禁用再启用插件实现,不影响文章中歌曲信息**
|
||||
...
|
||||
|
||||
更多问题可以通过 issue 页面提交,或者通过 Telegram、邮件向我反馈
|
||||
|
||||
## LICENSE
|
||||
Meting-Typecho-Plugin is under the MIT license.
|
3
assets/APlayer.min.js
vendored
Normal file
3
assets/APlayer.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
874
include/Meting.php
Normal file
874
include/Meting.php
Normal file
@ -0,0 +1,874 @@
|
||||
<?php
|
||||
/*!
|
||||
* Meting music framework
|
||||
* https://i-meto.com
|
||||
* Version 1.0.1
|
||||
*
|
||||
* Copyright 2016, METO Sheel <i@i-meto.com>
|
||||
* Released under the MIT license
|
||||
*/
|
||||
|
||||
class Meting
|
||||
{
|
||||
protected $_SITE;
|
||||
protected $_TEMP;
|
||||
protected $_FORMAT = false;
|
||||
|
||||
function __construct($v='netease'){
|
||||
self::site($v);
|
||||
}
|
||||
|
||||
public function site($v){
|
||||
$this->_SITE=$v;
|
||||
}
|
||||
|
||||
public function format($v = true){
|
||||
$this->_FORMAT=$v;
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function curl($API){
|
||||
if(isset($API['encode']))$API=call_user_func_array(array($this,$API['encode']),array($API));
|
||||
|
||||
$BASE=self::curlset();
|
||||
$curl=curl_init();
|
||||
if($API['method']=='POST'){
|
||||
if(is_array($API['body']))$API['body']=http_build_query($API['body']);
|
||||
curl_setopt($curl,CURLOPT_URL,$API['url']);
|
||||
curl_setopt($curl,CURLOPT_POSTFIELDS,$API['body']);
|
||||
curl_setopt($curl,CURLOPT_POST,1);
|
||||
}
|
||||
elseif($API['method']=='GET'){
|
||||
if(isset($API['body']))$API['url']=$API['url'].'?'.http_build_query($API['body']);
|
||||
curl_setopt($curl,CURLOPT_URL,$API['url']);
|
||||
}
|
||||
curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);
|
||||
curl_setopt($curl,CURLOPT_CONNECTTIMEOUT,10);
|
||||
curl_setopt($curl,CURLOPT_TIMEOUT,10);
|
||||
curl_setopt($curl,CURLOPT_COOKIE,$BASE['cookie']);
|
||||
curl_setopt($curl,CURLOPT_REFERER,$BASE['referer']);
|
||||
curl_setopt($curl,CURLOPT_USERAGENT,$BASE['useragent']);
|
||||
$result=curl_exec($curl);
|
||||
curl_close($curl);
|
||||
|
||||
if(isset($API['decode']))$result=call_user_func_array(array($this,$API['decode']),array($result));
|
||||
|
||||
if($this->_FORMAT){
|
||||
if(isset($API['format'])){
|
||||
$result=json_decode($result,1);
|
||||
$result=self::clean($result,$API['format']);
|
||||
$result=json_encode($result);
|
||||
}
|
||||
$this->_FORMAT=false;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function pickup($array,$rule){
|
||||
$t=explode('#',$rule);
|
||||
foreach($t as $vo){
|
||||
if($array==null)return null;
|
||||
$array=$array[$vo];
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
private function clean($raw,$rule){
|
||||
if(!empty($rule))$raw=self::pickup($raw,$rule);
|
||||
if($raw==null)$raw=array();
|
||||
elseif(!isset($raw[0]))$raw=array($raw);
|
||||
$result=array_map(array($this,'format_'.$this->_SITE),$raw);
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function search($keyword,$page=1,$limit=30){
|
||||
$API=array(
|
||||
'netease' => array(
|
||||
'method' => 'POST',
|
||||
'url' => 'http://music.163.com/api/linux/forward',
|
||||
'body' => array(
|
||||
'method' => 'POST',
|
||||
'params' => array(
|
||||
's' => $keyword,
|
||||
'type' => 1,
|
||||
'limit' => $limit,
|
||||
'total' => 'true',
|
||||
'offset' => $page-1,
|
||||
),
|
||||
'url' => 'http://music.163.com/api/cloudsearch/pc',
|
||||
),
|
||||
'encode' => 'netease_AESECB',
|
||||
'format' => 'result#songs',
|
||||
),
|
||||
'tencent' => array(
|
||||
'method' => 'GET',
|
||||
'url' => 'https://c.y.qq.com/soso/fcgi-bin/search_cp',
|
||||
'body' => array(
|
||||
'p' => $page,
|
||||
'n' => $limit,
|
||||
'w' => $keyword,
|
||||
'aggr' => 1,
|
||||
'lossless' => 1,
|
||||
'cr' => 1,
|
||||
),
|
||||
'decode' => 'jsonp2json',
|
||||
'format' => 'data#song#list',
|
||||
),
|
||||
'xiami' => array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://api.xiami.com/web',
|
||||
'body' => array(
|
||||
'v' => '2.0',
|
||||
'app_key' => '1',
|
||||
'key' => $keyword,
|
||||
'page' => $page,
|
||||
'limit' => $limit,
|
||||
'r' => 'search/songs',
|
||||
),
|
||||
'format' => 'data#songs',
|
||||
),
|
||||
'kugou' => array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://mobilecdn.kugou.com/api/v3/search/song',
|
||||
'body' => array(
|
||||
'iscorrect' => 1,
|
||||
'pagesize' => $limit,
|
||||
'plat' => 20,
|
||||
'sver' => 3,
|
||||
'showtype' => 14,
|
||||
'page' => $page,
|
||||
'keyword' => $keyword,
|
||||
),
|
||||
'format' => 'data#info',
|
||||
),
|
||||
'baidu' => array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting',
|
||||
'body' => array(
|
||||
'method' => 'baidu.ting.search.merge',
|
||||
'isNew' => 1,
|
||||
'query' => $keyword,
|
||||
'page_size' => $limit,
|
||||
'page_no' => $page,
|
||||
'type' => 0,
|
||||
'format' => 'json',
|
||||
'from' => 'ios',
|
||||
'channel' => '(null)',
|
||||
'cuid' => 'appstore',
|
||||
'from' => 'ios',
|
||||
'version' => '5.9.5',
|
||||
),
|
||||
'format' => 'result#song_info#song_list',
|
||||
),
|
||||
);
|
||||
return self::curl($API[$this->_SITE]);
|
||||
}
|
||||
|
||||
public function song($id){
|
||||
$API=array(
|
||||
'netease' => array(
|
||||
'method' => 'POST',
|
||||
'url' => 'http://music.163.com/api/linux/forward',
|
||||
'body' => array(
|
||||
'method' => 'POST',
|
||||
'params' => array(
|
||||
'c' => '[{"id":'.$id.'}]',
|
||||
),
|
||||
'url' => 'http://music.163.com/api/v3/song/detail',
|
||||
),
|
||||
'encode' => 'netease_AESECB',
|
||||
'format' => 'songs',
|
||||
),
|
||||
'tencent'=>array(
|
||||
'method' => 'GET',
|
||||
'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_play_single_song.fcg',
|
||||
'body' => array(
|
||||
'songmid' => $id,
|
||||
'format' => 'json',
|
||||
),
|
||||
'decode' => 'tencent_singlesong',
|
||||
'format' => 'data',
|
||||
),
|
||||
'xiami'=>array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://api.xiami.com/web',
|
||||
'body' => array(
|
||||
'v' => '2.0',
|
||||
'app_key' => '1',
|
||||
'id' => $id,
|
||||
'r' => 'song/detail',
|
||||
),
|
||||
'format' => 'data#song',
|
||||
),
|
||||
'kugou'=>array(
|
||||
'method' => 'POST',
|
||||
'url' => 'http://m.kugou.com/app/i/getSongInfo.php?',
|
||||
'body' => array(
|
||||
"cmd" => "playInfo",
|
||||
"hash" => $id,
|
||||
"from" => "mkugou",
|
||||
),
|
||||
'format' => '',
|
||||
),
|
||||
'baidu' => array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting',
|
||||
'body' => array(
|
||||
'method' => 'baidu.ting.song.play',
|
||||
'songid' => $id,
|
||||
'format' => 'json',
|
||||
'from' => 'ios',
|
||||
'channel' => '(null)',
|
||||
'cuid' => 'appstore',
|
||||
'from' => 'ios',
|
||||
'version' => '5.9.5',
|
||||
),
|
||||
'format' => 'songinfo',
|
||||
),
|
||||
);
|
||||
return self::curl($API[$this->_SITE]);
|
||||
}
|
||||
|
||||
public function album($id){
|
||||
$API=array(
|
||||
'netease'=>array(
|
||||
'method' => 'POST',
|
||||
'url' => 'http://music.163.com/api/linux/forward',
|
||||
'body' => array(
|
||||
'method' => 'GET',
|
||||
'params' => array(
|
||||
'id' => $id,
|
||||
),
|
||||
'url' => 'http://music.163.com/api/v1/album/'.$id,
|
||||
),
|
||||
'encode' => 'netease_AESECB',
|
||||
'format' => 'songs',
|
||||
),
|
||||
'tencent'=>array(
|
||||
'method' => 'GET',
|
||||
'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_album_info_cp.fcg',
|
||||
'body' => array(
|
||||
'albummid' => $id,
|
||||
),
|
||||
'format' => 'data#list',
|
||||
),
|
||||
'xiami'=>array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://api.xiami.com/web',
|
||||
'body' => array(
|
||||
'v' => '2.0',
|
||||
'app_key' => '1',
|
||||
'id' => $id,
|
||||
'r' => 'album/detail',
|
||||
),
|
||||
'format' => 'data#songs',
|
||||
),
|
||||
'kugou'=>array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://mobilecdn.kugou.com/api/v3/album/song',
|
||||
'body' => array(
|
||||
'albumid' => $id,
|
||||
'plat' => 2,
|
||||
'page' => 1,
|
||||
'pagesize' => -1,
|
||||
'version' => 8400,
|
||||
),
|
||||
'format' => 'data#info',
|
||||
),
|
||||
'baidu' => array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting',
|
||||
'body' => array(
|
||||
'method' => 'baidu.ting.album.getAlbumInfo',
|
||||
'album_id' => $id,
|
||||
'format' => 'json',
|
||||
'from' => 'ios',
|
||||
'channel' => '(null)',
|
||||
'cuid' => 'appstore',
|
||||
'from' => 'ios',
|
||||
'version' => '5.9.5',
|
||||
),
|
||||
'format' => 'songlist',
|
||||
),
|
||||
);
|
||||
return self::curl($API[$this->_SITE]);
|
||||
}
|
||||
|
||||
public function artist($id,$limit=50){
|
||||
$API=array(
|
||||
'netease'=>array(
|
||||
'method' => 'POST',
|
||||
'url' => 'http://music.163.com/api/linux/forward',
|
||||
'body' => array(
|
||||
'method' => 'GET',
|
||||
'params' => array(
|
||||
'top' => $limit,
|
||||
"id" => $id,
|
||||
"ext" => "true",
|
||||
),
|
||||
'url' => 'http://music.163.com/api/v1/artist/'.$id,
|
||||
),
|
||||
'encode' => 'netease_AESECB',
|
||||
'format' => 'hotSongs',
|
||||
),
|
||||
'tencent'=>array(
|
||||
'method' => 'GET',
|
||||
'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg',
|
||||
'body' => array(
|
||||
'singermid' => $id,
|
||||
'begin' => 0,
|
||||
'num' => $limit,
|
||||
),
|
||||
'format' => 'data#list',
|
||||
),
|
||||
'xiami' => array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://api.xiami.com/web',
|
||||
'body' => array(
|
||||
'v' => '2.0',
|
||||
'app_key' => '1',
|
||||
'id' => $id,
|
||||
'limit' => $limit,
|
||||
'page' => 1,
|
||||
'r' => 'artist/hot-songs',
|
||||
),
|
||||
'format' => 'data',
|
||||
),
|
||||
'kugou'=>array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://mobilecdn.kugou.com/api/v3/singer/song',
|
||||
'body' => array(
|
||||
'singerid' => $id,
|
||||
'page' => 1,
|
||||
'plat' => 0,
|
||||
'pagesize' => $limit,
|
||||
'version' => 8400,
|
||||
),
|
||||
'format' => 'data#info',
|
||||
),
|
||||
'baidu' => array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting',
|
||||
'body' => array(
|
||||
'method' => 'baidu.ting.artist.getSongList',
|
||||
'tinguid' => $id,
|
||||
'limits' => $limit,
|
||||
'format' => 'json',
|
||||
'from' => 'ios',
|
||||
'channel' => '(null)',
|
||||
'cuid' => 'appstore',
|
||||
'from' => 'ios',
|
||||
'version' => '5.9.5',
|
||||
),
|
||||
'format' => 'songlist',
|
||||
),
|
||||
);
|
||||
return self::curl($API[$this->_SITE]);
|
||||
}
|
||||
|
||||
public function playlist($id){
|
||||
$API=array(
|
||||
'netease'=>array(
|
||||
'method' => 'POST',
|
||||
'url' => 'http://music.163.com/api/linux/forward',
|
||||
'body' => array(
|
||||
'method' => 'POST',
|
||||
'params' => array(
|
||||
'id' => $id,
|
||||
"n" => 1000,
|
||||
),
|
||||
'url' => 'http://music.163.com/api/v3/playlist/detail',
|
||||
),
|
||||
'encode' => 'netease_AESECB',
|
||||
'format' => 'playlist#tracks',
|
||||
),
|
||||
'tencent'=>array(
|
||||
'method' => 'GET',
|
||||
'url' => 'https://c.y.qq.com/qzone/fcg-bin/fcg_ucc_getcdinfo_byids_cp.fcg',
|
||||
'body' => array(
|
||||
'disstid' => $id,
|
||||
'utf8' => 1,
|
||||
'type' => 1,
|
||||
),
|
||||
'decode' => 'jsonp2json',
|
||||
'format' => 'cdlist#0#songlist',
|
||||
),
|
||||
'xiami'=>array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://api.xiami.com/web',
|
||||
'body' => array(
|
||||
'v' => '2.0',
|
||||
'app_key' => '1',
|
||||
'id' => $id,
|
||||
'r' => 'collect/detail',
|
||||
),
|
||||
'format' => 'data#songs',
|
||||
),
|
||||
'kugou'=>array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://mobilecdn.kugou.com/api/v3/special/song',
|
||||
'body' => array(
|
||||
'specialid' => $id,
|
||||
'page' => 1,
|
||||
'plat' => 2,
|
||||
'pagesize' => -1,
|
||||
'version' => 8400,
|
||||
),
|
||||
'format' => 'data#info',
|
||||
),
|
||||
'baidu' => array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting',
|
||||
'body' => array(
|
||||
'method' => 'baidu.ting.diy.gedanInfo',
|
||||
'listid' => $id,
|
||||
'format' => 'json',
|
||||
'from' => 'ios',
|
||||
'channel' => '(null)',
|
||||
'cuid' => 'appstore',
|
||||
'from' => 'ios',
|
||||
'version' => '5.9.5',
|
||||
),
|
||||
'format' => 'content',
|
||||
),
|
||||
);
|
||||
return self::curl($API[$this->_SITE]);
|
||||
}
|
||||
|
||||
public function url($id,$br=320){
|
||||
$API=array(
|
||||
'netease'=>array(
|
||||
'method' => 'POST',
|
||||
'url' => 'http://music.163.com/api/linux/forward',
|
||||
'body' => array(
|
||||
'method' => 'POST',
|
||||
'params' => array(
|
||||
'ids' => array($id),
|
||||
'br' => $br*1000,
|
||||
),
|
||||
'url' => 'http://music.163.com/api/song/enhance/player/url',
|
||||
),
|
||||
'encode' => 'netease_AESECB',
|
||||
'decode' => 'netease_url',
|
||||
),
|
||||
'tencent'=>array(
|
||||
'method' => 'GET',
|
||||
'url' => 'https://c.y.qq.com/v8/fcg-bin/fcg_play_single_song.fcg',
|
||||
'body' => array(
|
||||
'songmid' => $id,
|
||||
'format' => 'json',
|
||||
),
|
||||
'decode' => 'tencent_url',
|
||||
),
|
||||
'xiami'=>array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://www.xiami.com/song/playlist/id/'.$id.'/object_name/default/object_id/0/cat/json',
|
||||
'body' => array(),
|
||||
'decode' => 'xiami_url',
|
||||
),
|
||||
'kugou'=>array(
|
||||
'method' => 'POST',
|
||||
'url' => 'http://media.store.kugou.com/v1/get_res_privilege',
|
||||
'body' => array(
|
||||
"relate" => 1,
|
||||
"userid" => 0,
|
||||
"vip" => 0,
|
||||
"appid" => 1390,
|
||||
"token" => "",
|
||||
"behavior" => "download",
|
||||
"clientver" => "1",
|
||||
"resource" => array(array(
|
||||
"id" => 0,
|
||||
"type" => "audio",
|
||||
"hash" => $id,
|
||||
)),
|
||||
),
|
||||
'encode' => 'kugou_json',
|
||||
'decode' => 'kugou_url',
|
||||
),
|
||||
'baidu' => array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://music.baidu.com/data/music/fmlink',
|
||||
'body' => array(
|
||||
'songIds' => $id,
|
||||
'rate' => $br,
|
||||
'type' => 'mp3',
|
||||
),
|
||||
'decode' => 'baidu_url',
|
||||
),
|
||||
);
|
||||
$this->_temp['br']=$br;
|
||||
return self::curl($API[$this->_SITE]);
|
||||
}
|
||||
|
||||
public function lyric($id){
|
||||
$API=array(
|
||||
'netease'=>array(
|
||||
'method' => 'POST',
|
||||
'url' => 'http://music.163.com/api/linux/forward',
|
||||
'body' => array(
|
||||
'method' => 'POST',
|
||||
'params' => array(
|
||||
'id' => $id,
|
||||
'os' => 'linux',
|
||||
'lv' => -1,
|
||||
'kv' => -1,
|
||||
'tv' => -1,
|
||||
),
|
||||
'url' => 'http://music.163.com/api/song/lyric',
|
||||
),
|
||||
'encode' => 'netease_AESECB',
|
||||
),
|
||||
'tencent'=>array(
|
||||
'method' => 'GET',
|
||||
'url' => 'https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric.fcg',
|
||||
'body' => array(
|
||||
'songmid' => $id,
|
||||
'nobase64' => 0,
|
||||
),
|
||||
'decode' => 'jsonp2json',
|
||||
),
|
||||
'xiami'=>array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://api.xiami.com/web',
|
||||
'body' => array(
|
||||
'v' => '2.0',
|
||||
'app_key' => '1',
|
||||
'id' => $id,
|
||||
'r' => 'song/detail',
|
||||
),
|
||||
'decode' => 'xiami_lyric',
|
||||
),
|
||||
'kugou'=>array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://m.kugou.com/app/i/krc.php',
|
||||
'body' => array(
|
||||
'keyword' => '%20-%20',
|
||||
'timelength' => 1000000,
|
||||
'cmd' => 100,
|
||||
'hash' => $id,
|
||||
),
|
||||
'decode' => 'kugou_lyric'
|
||||
),
|
||||
'baidu' => array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://tingapi.ting.baidu.com/v1/restserver/ting',
|
||||
'body' => array(
|
||||
'method' => 'baidu.ting.song.lry',
|
||||
'songid' => $id,
|
||||
'format' => 'json',
|
||||
'from' => 'ios',
|
||||
'channel' => '(null)',
|
||||
'cuid' => 'appstore',
|
||||
'from' => 'ios',
|
||||
'version' => '5.9.5',
|
||||
),
|
||||
),
|
||||
);
|
||||
return self::curl($API[$this->_SITE]);
|
||||
}
|
||||
|
||||
public function pic($id,$size=300){
|
||||
switch($this->_SITE){
|
||||
case 'netease':
|
||||
$url='https://p3.music.126.net/'.self::netease_pickey($id).'/'.$id.'.jpg?param='.$size.'z'.$size.'&quality=100';
|
||||
break;
|
||||
case 'tencent':
|
||||
$url='https://y.gtimg.cn/music/photo_new/T002R'.$size.'x'.$size.'M000'.$id.'.jpg?max_age=2592000';
|
||||
break;
|
||||
case 'xiami':
|
||||
$data=$this->format(false)->song($id);
|
||||
$url=json_decode($data,1)['data']['song']['logo'];
|
||||
$url=str_replace(['_1.','http:','img.'],['.','https:','pic.'],$url).'@'.$size.'h_'.$size.'w_90q_1c.webp';
|
||||
break;
|
||||
case 'kugou':
|
||||
$API=array(
|
||||
'method' =>'GET',
|
||||
'url' => 'http://tools.mobile.kugou.com/api/v1/singer_header/get_by_hash',
|
||||
'body' => array(
|
||||
'hash' => $id,
|
||||
'size' => 400,
|
||||
'format' => 'json',
|
||||
),
|
||||
);
|
||||
$data=$this->curl($API);
|
||||
$url=json_decode($data,1)['url'];
|
||||
break;
|
||||
case 'baidu':
|
||||
$data=self::song($id);
|
||||
$data=json_decode($data,1);
|
||||
$url=$data['songinfo']['pic_big']?:$data['songinfo']['pic_small'];
|
||||
}
|
||||
$arr=array('url'=>$url);
|
||||
return json_encode($arr);
|
||||
}
|
||||
|
||||
private function curlset(){
|
||||
$BASE=array(
|
||||
'netease'=>array(
|
||||
'referer' => 'http://music.163.com/',
|
||||
'cookie' => 'os=linux; appver=1.0.0.1026; osver=Ubuntu%2016.10; MUSIC_U=78d411095f4b022667bc8ec49e9a44cca088df057d987f5feaf066d37458e41c4a7d9447977352cf27ea9fee03f6ec4441049cea1c6bb9b6; __remember_me=true',
|
||||
'useragent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.30 Safari/537.36',
|
||||
),
|
||||
'tencent'=>array(
|
||||
'referer' => 'http://y.qq.com/portal/player.html',
|
||||
'cookie' => 'qqmusic_uin=12345678; qqmusic_key=12345678; qqmusic_fromtag=30; ts_last=y.qq.com/portal/player.html;',
|
||||
'useragent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.30 Safari/537.36',
|
||||
),
|
||||
'xiami'=>array(
|
||||
'referer' => 'http://h.xiami.com/',
|
||||
'cookie' => 'user_from=2;XMPLAYER_addSongsToggler=0;XMPLAYER_isOpen=0;_xiamitoken=123456789;',
|
||||
'useragent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.30 Safari/537.36',
|
||||
),
|
||||
'kugou'=>array(
|
||||
'referer' => 'http://www.kugou.com/webkugouplayer/flash/webKugou.swf',
|
||||
'cookie' => '_WCMID=123456789',
|
||||
'useragent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.30 Safari/537.36',
|
||||
),
|
||||
'baidu'=>array(
|
||||
'referer' => 'http://ting.baidu.com/',
|
||||
'cookie' => 'BAIDUID=123456789',
|
||||
'useragent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.30 Safari/537.36',
|
||||
),
|
||||
);
|
||||
return $BASE[$this->_SITE];
|
||||
}
|
||||
|
||||
/**
|
||||
* 乱七八糟的函数,加密解密...
|
||||
* 正在努力重构这些代码 TAT
|
||||
*/
|
||||
private function netease_AESECB($API){
|
||||
$KEY='7246674226682325323F5E6544673A51';
|
||||
$body=json_encode($API['body']);
|
||||
$body=openssl_encrypt($body,'aes-128-ecb',hex2bin($KEY));
|
||||
$body=strtoupper(bin2hex(base64_decode($body)));
|
||||
|
||||
$API['body']=array(
|
||||
'eparams'=>$body,
|
||||
);
|
||||
return $API;
|
||||
}
|
||||
private function kugou_json($API){
|
||||
$API['body']=json_encode($API['body']);
|
||||
return $API;
|
||||
}
|
||||
private function jsonp2json($jsonp){
|
||||
if($jsonp[0] !== '[' && $jsonp[0] !== '{') {
|
||||
$jsonp = substr($jsonp, strpos($jsonp, '('));
|
||||
}
|
||||
return trim($jsonp,'();');
|
||||
}
|
||||
private function tencent_singlesong($result){
|
||||
$result=json_decode($result,1);
|
||||
$data=$result['data'][0];
|
||||
$t=array(
|
||||
'songmid' => $data['mid'],
|
||||
'songname' => $data['name'],
|
||||
'albummid' => $data['album']['mid'],
|
||||
);
|
||||
foreach($t as $key=>$vo)$result['data'][0][$key]=$vo;
|
||||
return json_encode($result);
|
||||
}
|
||||
private function netease_pickey($id){
|
||||
$magic=str_split('3go8&$8*3*3h0k(2)2');
|
||||
$song_id=str_split($id);
|
||||
for($i=0;$i<count($song_id);$i++)$song_id[$i]=chr(ord($song_id[$i])^ord($magic[$i%count($magic)]));
|
||||
$result=base64_encode(md5(implode('',$song_id),1));
|
||||
$result=str_replace(['/','+'],['_','-'],$result);
|
||||
return $result;
|
||||
}
|
||||
/**
|
||||
* URL - 歌曲地址转换函数
|
||||
* 用于返回不高于指定 bitRate 的歌曲地址(默认规范化)
|
||||
*/
|
||||
private function netease_url($result){
|
||||
$data=json_decode($result,1);
|
||||
$url=array(
|
||||
'url' => str_replace('http:','https:',$data['data'][0]['url']),
|
||||
'br' => $data['data'][0]['br']/1000,
|
||||
);
|
||||
return json_encode($url);
|
||||
}
|
||||
private function tencent_url($result){
|
||||
$data=json_decode($result,1);
|
||||
$GUID=mt_rand()%10000000000;
|
||||
$API=array(
|
||||
'method' => 'GET',
|
||||
'url' => 'https://c.y.qq.com/base/fcgi-bin/fcg_musicexpress.fcg',
|
||||
'body' => array(
|
||||
'json' => 3,
|
||||
'guid' => $GUID,
|
||||
),
|
||||
'decode' => 'jsonp2json',
|
||||
);
|
||||
$KEY=json_decode($this->curl($API),1)['key'];
|
||||
|
||||
$type=array(
|
||||
'size_320mp3'=>array(320,'M800','mp3'),
|
||||
'size_128mp3'=>array(128,'M500','mp3'),
|
||||
'size_96aac'=>array(96,'C400','m4a'),
|
||||
'size_48aac'=>array(48,'C200','m4a'),
|
||||
);
|
||||
foreach($type as $key=>$vo){
|
||||
if($data['data'][0]['file'][$key]&&$vo[0]<=$this->_temp['br']){
|
||||
$url=array(
|
||||
'url' => 'http://dl.stream.qqmusic.qq.com/'.$vo[1].$data['data'][0]['file']['media_mid'].'.'.$vo[2].'?vkey='.$KEY.'&guid='.$GUID.'&uid=0&fromtag=30',
|
||||
'br' => $vo[0],
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return json_encode($url);
|
||||
}
|
||||
private function xiami_url($result){
|
||||
$data=json_decode($result,1);
|
||||
$max=0;
|
||||
foreach($data['data']['trackList'][0]['allAudios'] as $vo){
|
||||
if($vo['rate']<=$this->_temp['br']&&$vo['rate']>$max){
|
||||
$max=$vo['rate'];
|
||||
$url=array(
|
||||
'url' => str_replace('http:','https:',$vo['filePath']),
|
||||
'br' => $vo['rate'],
|
||||
);
|
||||
}
|
||||
}
|
||||
return json_encode($url);
|
||||
}
|
||||
private function kugou_url($result){
|
||||
$data=json_decode($result,1);
|
||||
|
||||
$max=0;
|
||||
$url=array();
|
||||
foreach($data['data'][0]['relate_goods'] as $vo){
|
||||
if($vo['info']['bitrate']<=$this->_temp['br']&&$vo['info']['bitrate']>$max){
|
||||
$API=array(
|
||||
'method' => 'GET',
|
||||
'url' => 'http://trackercdn.kugou.com/i/v2/',
|
||||
'body' => array(
|
||||
'hash' => $vo['hash'],
|
||||
'key' => md5($vo['hash'].'kgcloudv2'),
|
||||
'pid' => 1,
|
||||
'behavior' => 'play',
|
||||
'cmd' => '23',
|
||||
'version' => 8400,
|
||||
),
|
||||
);
|
||||
$t=json_decode($this->curl($API),1);
|
||||
if(isset($t['url'])){
|
||||
$max=$t['bitRate']/1000;
|
||||
$url=array(
|
||||
'url' => $t['url'],
|
||||
'br' => $t['bitRate']/1000,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return json_encode($url);
|
||||
}
|
||||
private function baidu_url($result){
|
||||
$data=json_decode($result,1);
|
||||
$url=array(
|
||||
'url' => $data['data']['songList'][0]['songLink'],
|
||||
'br' => $data['data']['songList'][0]['rate'],
|
||||
);
|
||||
$url['url']=str_replace('http://yinyueshiting.baidu.com','https://gss0.bdstatic.com/y0s1hSulBw92lNKgpU_Z2jR7b2w6buu',$url['url']);
|
||||
return json_encode($url);
|
||||
}
|
||||
/**
|
||||
* 歌词处理模块
|
||||
* 用于规范化歌词输出
|
||||
*/
|
||||
private function xiami_lyric($result){
|
||||
$result=json_decode($result,1);
|
||||
$API=array(
|
||||
'method' => 'GET',
|
||||
'url' => $result['data']['song']['lyric'],
|
||||
);
|
||||
$data=$this->curl($API);
|
||||
$data=preg_replace('/<\d{1,8}>/','',$data);
|
||||
$arr=array(
|
||||
'lyric' => $data,
|
||||
);
|
||||
return json_encode($arr);
|
||||
}
|
||||
private function kugou_lyric($result){
|
||||
$arr=array(
|
||||
'lyric' => $result,
|
||||
);
|
||||
return json_encode($arr);
|
||||
}
|
||||
/**
|
||||
* Format - 规范化函数
|
||||
* 用于统一返回的参数,可用 ->format() 一次性开关开启
|
||||
*/
|
||||
private function format_netease($data){
|
||||
$result=array(
|
||||
'id' => $data['id'],
|
||||
'name' => $data['name'],
|
||||
'artist' => array(),
|
||||
'pic_id' => $data['al']['pic_str']?:$data['al']['pic'],
|
||||
'url_id' => $data['id'],
|
||||
'lyric_id' => $data['id'],
|
||||
'source' => 'netease',
|
||||
);
|
||||
if(isset($data['al']['picUrl'])){
|
||||
preg_match('/\/(\d+)\./',$data['al']['picUrl'],$match);
|
||||
$result['pic_id']=$match[1];
|
||||
}
|
||||
foreach($data['ar'] as $vo)$result['artist'][]=$vo['name'];
|
||||
return $result;
|
||||
}
|
||||
private function format_tencent($data){
|
||||
if(isset($data['musicData']))$data=$data['musicData'];
|
||||
$result=array(
|
||||
'id' => $data['songmid'],
|
||||
'name' => $data['songname'],
|
||||
'artist' => array(),
|
||||
'pic_id' => $data['albummid'],
|
||||
'url_id' => $data['songmid'],
|
||||
'lyric_id' => $data['songmid'],
|
||||
'source' => 'tencent',
|
||||
);
|
||||
foreach($data['singer'] as $vo)$result['artist'][]=$vo['name'];
|
||||
return $result;
|
||||
}
|
||||
private function format_xiami($data){
|
||||
$result=array(
|
||||
'id' => $data['song_id'],
|
||||
'name' => $data['song_name'],
|
||||
'artist' => explode(';',$data['singers']?:$data['artist_name']),
|
||||
'pic_id' => $data['song_id'],
|
||||
'url_id' => $data['song_id'],
|
||||
'lyric_id' => $data['song_id'],
|
||||
'source' => 'xiami',
|
||||
);
|
||||
return $result;
|
||||
}
|
||||
private function format_kugou($data){
|
||||
$result=array(
|
||||
'id' => $data['hash'],
|
||||
'name' => "",
|
||||
'artist' => array(),
|
||||
'url_id' => $data['hash'],
|
||||
'pic_id' => $data['hash'],
|
||||
'lyric_id' => $data['hash'],
|
||||
'source' => 'kugou',
|
||||
);
|
||||
list($result['artist'],$result['name'])=explode(' - ',$data['filename']?:$data['fileName']);
|
||||
$result['artist']=explode('、',$result['artist']);
|
||||
return $result;
|
||||
}
|
||||
private function format_baidu($data){
|
||||
$result=array(
|
||||
'id' => $data['song_id'],
|
||||
'name' => $data['title'],
|
||||
'artist' => explode(',',$data['author']),
|
||||
'pic_id' => $data['song_id'],
|
||||
'url_id' => $data['song_id'],
|
||||
'lyric_id' => $data['song_id'],
|
||||
'source' => 'baidu',
|
||||
);
|
||||
return $result;
|
||||
}
|
||||
}
|
6
include/install.sql
Normal file
6
include/install.sql
Normal file
@ -0,0 +1,6 @@
|
||||
CREATE TABLE typecho_meting (
|
||||
id binary(40) NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
date int(11) NOT NULL
|
||||
)ENGINE=MYISAM DEFAULT CHARSET=utf8;
|
||||
ALTER TABLE typecho_meting ADD UNIQUE(id);
|
1
include/uninstall.sql
Normal file
1
include/uninstall.sql
Normal file
@ -0,0 +1 @@
|
||||
TRUNCATE typecho_meting;
|
Loading…
Reference in New Issue
Block a user