🍒 支持memcached缓存

- 支持 memcached 缓存
 - 支持缓存检测
 - 支持歌词翻译
 - 升级 Meting 1.5.2
   - 不再强制 BC Math 扩展 #44
   - 网易云音乐支持海外主机
   - 修复 OpenSSL/mcrypt 兼容
 - 修复 QQ 音乐歌单识别 #43
 - 修复一些 bug
This commit is contained in:
metowolf 2018-03-08 14:18:32 +08:00
parent dd428ad826
commit b66f1b0957
9 changed files with 228 additions and 75 deletions

View File

@ -16,12 +16,15 @@ class Meting_Action extends Typecho_Widget implements Widget_Interface_Do
$this->on($this->request->isGet())->api();
}
private function check($a, $b)
private function check($server, $type, $id)
{
if (!in_array($a, array('netease','tencent','baidu','xiami','kugou'))) {
if (!in_array($server, array('netease','tencent','baidu','xiami','kugou'))) {
return false;
}
if (!in_array($b, array('song','album','search','artist','playlist','lrc','url','pic'))) {
if (!in_array($type, array('song','album','search','artist','playlist','lrc','url','pic'))) {
return false;
}
if (empty($id)) {
return false;
}
return true;
@ -29,22 +32,31 @@ class Meting_Action extends Typecho_Widget implements Widget_Interface_Do
private function api()
{
// 参数检查
$this->filterReferer();
$server = $this->request->get('server');
$type = $this->request->get('type');
$id = $this->request->get('id');
if (!$this->check($server, $type) || empty($id)) {
die('[]');
if (!$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);
}
if (!extension_loaded('Meting')) {
// 加载 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;
@ -57,13 +69,8 @@ class Meting_Action extends Typecho_Widget implements Widget_Interface_Do
));
}
}
$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);
}
// auth 验证
$EID = $server.'/'.$type.'/'.$id;
$salt = Typecho_Widget::widget('Widget_Options')->plugin('Meting')->salt;
@ -76,6 +83,7 @@ class Meting_Action extends Typecho_Widget implements Widget_Interface_Do
}
}
// 歌词解析
if ($type == 'lrc') {
$data = $this->cacheRead($EID);
if (empty($data)) {
@ -84,8 +92,15 @@ class Meting_Action extends Typecho_Widget implements Widget_Interface_Do
}
$data = json_decode($data, true);
header("Content-Type: application/javascript");
echo $data['lyric'];
} elseif ($type == 'pic') {
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);
@ -93,7 +108,10 @@ class Meting_Action extends Typecho_Widget implements Widget_Interface_Do
}
$data = json_decode($data, true);
$this->response->redirect($data['url']);
} elseif ($type == 'url') {
}
// 歌曲链接解析
if ($type == 'url') {
$data = $this->cacheRead($EID);
if (empty($data)) {
$rate = Typecho_Widget::widget('Widget_Options')->plugin('Meting')->bitrate;
@ -122,7 +140,10 @@ class Meting_Action extends Typecho_Widget implements Widget_Interface_Do
}
}
$this->response->redirect($url);
} else {
}
// 其它类别解析
if (in_array($type, array('song','album','search','artist','playlist'))) {
$data = $this->cacheRead($EID);
if (empty($data)) {
$data = $api->$type($id);
@ -173,6 +194,8 @@ class Meting_Action extends Typecho_Widget implements Widget_Interface_Do
$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)) {
@ -222,10 +245,59 @@ class Meting_Action extends Typecho_Widget implements Widget_Interface_Do
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 () {
@ -237,8 +309,9 @@ class Meting_Action extends Typecho_Widget implements Widget_Interface_Do
$isAdmin = $this->widget('Widget_User')->pass('administrator', true);
return $isAdmin;
}, $this);
if (!$isAdmin) {
die('非管理员,禁止操作!');
die('Forbidden!');
}
header("Content-Type: text/plain; charset=UTF-8");
@ -314,7 +387,7 @@ class Meting_Action extends Typecho_Widget implements Widget_Interface_Do
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) {
if (isset($_SERVER['HTTP_REFERER']) && parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST) !== $_SERVER['HTTP_HOST']) {
http_response_code(403);
die('[]');
}

View File

@ -8,13 +8,13 @@ if (!defined('__TYPECHO_ROOT_DIR__')) {
*
* @package APlayer for Typecho | Meting
* @author METO
* @version 2.1.0
* @version 2.1.1
* @dependence 14.10.10-*
* @link https://github.com/MoePlayer/APlayer-Typecho
*
*/
define('METING_VERSION', '2.1.0');
define('METING_VERSION', '2.1.1');
class Meting_Plugin extends Typecho_Widget implements Typecho_Plugin_Interface
{
@ -103,7 +103,8 @@ class Meting_Plugin extends Typecho_Widget implements Typecho_Plugin_Interface
$list = array(
'none' => _t('关闭'),
'redis' => _t('Redis'),
'mysql' => _t('Mysql'),
'memcached' => _t('Memcached'),
'mysql' => _t('MySQL'),
'sqlite' => _t('SQLite')
);
$t = new Typecho_Widget_Helper_Form_Element_Radio(
@ -187,6 +188,7 @@ class Meting_Plugin extends Typecho_Widget implements Typecho_Plugin_Interface
'port' => $config['cacheport']
));
$cache->install();
$cache->check();
$cache->flush();
} catch (Exception $e) {
throw new Typecho_Plugin_Exception(_t($e->getMessage()));

View File

@ -5,4 +5,5 @@ interface MetingCacheI
public function set($key, $value, $expire = 86400);
public function get($key);
public function flush();
public function check();
}

View File

@ -0,0 +1,35 @@
<?php
class MetingCache implements MetingCacheI
{
private $memcached = null;
public function __construct($option)
{
$this->memcached = new Memcached();
$this->memcached->addServer($option['host'], $option['port']);
assert($this->memcached->getVersion()!==false);
}
public function install()
{
}
public function set($key, $value, $expire = 86400)
{
return $this->memcached->set($key, $value, $expire);
}
public function get($key)
{
return $this->memcached->get($key);
}
public function flush()
{
return $this->memcached->flush();
}
public function check()
{
$number = uniqid();
$this->set('check', $number, 60);
$cache = $this->get('check');
if ($number != $cache) {
throw new Exception('Cache Test Fall!');
}
}
}

View File

@ -54,4 +54,13 @@ CREATE TABLE `%dbname%` (
{
return $this->db->query($this->db->delete('table.metingcache'));
}
public function check()
{
$number = uniqid();
$this->set('check', $number, 60);
$cache = $this->get('check');
if ($number != $cache) {
throw new Exception('Cache Test Fall!');
}
}
}

View File

@ -22,4 +22,13 @@ class MetingCache implements MetingCacheI
{
return $this->redis->flushDb();
}
public function check()
{
$number = uniqid();
$this->set('check', $number, 60);
$cache = $this->get('check');
if ($number != $cache) {
throw new Exception('Cache Test Fall!');
}
}
}

View File

@ -53,4 +53,13 @@ CREATE TABLE `%dbname%` (
{
return $this->db->query($this->db->delete('table.metingcache'));
}
public function check()
{
$number = uniqid();
$this->set('check', $number, 60);
$cache = $this->get('check');
if ($number != $cache) {
throw new Exception('Cache Test Fall!');
}
}
}

View File

@ -3,7 +3,7 @@
* Meting music framework
* https://i-meto.com
* https://github.com/metowolf/Meting
* Version 1.5.0.
* Version 1.5.2.
*
* Copyright 2018, METO Sheel <i@i-meto.com>
* Released under the MIT license
@ -13,8 +13,7 @@ namespace Metowolf;
class Meting
{
const VERSION = '1.5.0';
const DEFAULT_TIMEOUT = 30;
const VERSION = '1.5.2';
public $raw;
public $data;
@ -24,6 +23,13 @@ class Meting
public $server;
public $format = false;
public $header = array(
'Accept' => '*/*',
'Accept-Encoding' => 'gzip, deflate',
'Accept-Language' => 'zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4',
'Connection' => 'keep-alive',
'Content-Type' => 'application/x-www-form-urlencoded',
);
public function __construct($value = 'netease')
{
@ -34,17 +40,14 @@ class Meting
{
$suppose = array('netease', 'tencent', 'xiami', 'kugou', 'baidu');
$this->server = in_array($value, $suppose) ? $value : 'netease';
$data = $this->curlset();
$this->cookie = $data['cookie'];
$this->referer = $data['referer'];
$this->useragent = $data['useragent'];
$this->header = $this->curlset();
return $this;
}
public function cookie($value)
{
$this->cookie = $value;
$this->header['Cookie'] = $value;
return $this;
}
@ -86,14 +89,17 @@ class Meting
return $this->data;
}
private function curl($url, $payload = null, $header = 0)
private function curl($url, $payload = null, $headerOnly = 0)
{
$header = array_map(function ($k, $v) {
return $k.': '.$v;
}, array_keys($this->header), $this->header);
$curl = curl_init();
if (!is_null($payload)) {
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, is_array($payload) ? http_build_query($payload) : $payload);
}
curl_setopt($curl, CURLOPT_HEADER, $header);
curl_setopt($curl, CURLOPT_HEADER, $headerOnly);
curl_setopt($curl, CURLOPT_TIMEOUT, 20);
curl_setopt($curl, CURLOPT_ENCODING, 'gzip');
curl_setopt($curl, CURLOPT_IPRESOLVE, 1);
@ -101,16 +107,7 @@ class Meting
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_COOKIE, $this->cookie);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'Accept: */*',
'Accept-Encoding: gzip, deflate',
'Accept-Language: zh-CN,zh;q=0.8,gl;q=0.6,zh-TW;q=0.4',
'Connection: keep-alive',
'Content-Type: application/x-www-form-urlencoded',
'Referer: '.$this->referer,
'User-Agent: '.$this->useragent,
));
curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
for ($i = 0; $i < 3; $i++) {
$this->raw = curl_exec($curl);
$this->info = curl_getinfo($curl);
@ -771,33 +768,33 @@ class Meting
switch ($this->server) {
case 'netease':
return array(
'referer' => 'https://music.163.com/',
'cookie' => 'os=pc; osver=Microsoft-Windows-10-Professional-build-10586-64bit; appver=2.0.3.131777; channel=netease; __remember_me=true',
'useragent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
'Referer' => 'https://music.163.com/',
'Cookie' => 'os=pc; osver=Microsoft-Windows-10-Professional-build-10586-64bit; appver=2.0.3.131777; channel=netease; __remember_me=true',
'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36',
'X-Real-IP' => long2ip(mt_rand(1884815360, 1884890111)),
);
case 'tencent':
return array(
'referer' => 'https://y.qq.com/portal/player.html',
'cookie' => 'pgv_pvi=22038528; pgv_si=s3156287488; pgv_pvid=5535248600; yplayer_open=1; ts_last=y.qq.com/portal/player.html; ts_uid=4847550686; yq_index=0; qqmusic_fromtag=66; player_exist=1',
'useragent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36',
'Referer' => 'https://y.qq.com/portal/player.html',
'Cookie' => 'pgv_pvi=22038528; pgv_si=s3156287488; pgv_pvid=5535248600; yplayer_open=1; ts_last=y.qq.com/portal/player.html; ts_uid=4847550686; yq_index=0; qqmusic_fromtag=66; player_exist=1',
'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36',
);
case 'xiami':
return array(
'referer' => 'http://h5api.m.xiami.com/',
'cookie' => '_m_h5_tk=15d3402511a022796d88b249f83fb968_1511163656929; _m_h5_tk_enc=b6b3e64d81dae577fc314b5c5692df3c',
'useragent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) XIAMI-MUSIC/3.0.9 Chrome/56.0.2924.87 Electron/1.6.11 Safari/537.36',
'Referer' => 'http://h5api.m.xiami.com/',
'Cookie' => '_m_h5_tk=15d3402511a022796d88b249f83fb968_1511163656929; _m_h5_tk_enc=b6b3e64d81dae577fc314b5c5692df3c',
'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) XIAMI-MUSIC/3.0.9 Chrome/56.0.2924.87 Electron/1.6.11 Safari/537.36',
);
case 'kugou':
return array(
'referer' => 'http://www.kugou.com/webkugouplayer/flash/webKugou.swf',
'cookie' => 'kg_mid='.$this->getRandomHex(32),
'useragent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36',
'Referer' => 'http://www.kugou.com/webkugouplayer/flash/webKugou.swf',
'Cookie' => 'kg_mid='.$this->getRandomHex(32),
'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36',
);
case 'baidu':
return array(
'referer' => '',
'cookie' => 'BAIDUID='.$this->getRandomHex(32).':FG=1',
'useragent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) baidu-music/1.0.2 Chrome/56.0.2924.87 Electron/1.6.11 Safari/537.36',
'Cookie' => 'BAIDUID='.$this->getRandomHex(32).':FG=1',
'User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) baidu-music/1.0.2 Chrome/56.0.2924.87 Electron/1.6.11 Safari/537.36',
);
}
}
@ -852,17 +849,34 @@ class Meting
$pubkey = '65537';
$nonce = '0CoJUm6Qyw8W8jud';
$vi = '0102030405060708';
$skey = $this->getRandomHex(16);
if (extension_loaded('bcmath')) {
$skey = $this->getRandomHex(16);
} else {
$skey = 'B3v3kH4vRPWRJFfH';
}
$body = json_encode($api['body']);
$body = openssl_encrypt($body, 'aes-128-cbc', $nonce, false, $vi);
$body = openssl_encrypt($body, 'aes-128-cbc', $skey, false, $vi);
$skey = strrev(utf8_encode($skey));
$skey = $this->bchexdec($this->str2hex($skey));
$skey = bcpowmod($skey, $pubkey, $modulus);
$skey = $this->bcdechex($skey);
$skey = str_pad($skey, 256, '0', STR_PAD_LEFT);
if (function_exists('openssl_encrypt')) {
$body = openssl_encrypt($body, 'aes-128-cbc', $nonce, false, $vi);
$body = openssl_encrypt($body, 'aes-128-cbc', $skey, false, $vi);
} else {
$pad = 16 - (strlen($body) % 16);
$body = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $nonce, $body.str_repeat(chr($pad), $pad), MCRYPT_MODE_CBC, $vi));
$pad = 16 - (strlen($body) % 16);
$body = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $skey, $body.str_repeat(chr($pad), $pad), MCRYPT_MODE_CBC, $vi));
}
if (extension_loaded('bcmath')) {
$skey = strrev(utf8_encode($skey));
$skey = $this->bchexdec($this->str2hex($skey));
$skey = bcpowmod($skey, $pubkey, $modulus);
$skey = $this->bcdechex($skey);
$skey = str_pad($skey, 256, '0', STR_PAD_LEFT);
} else {
$skey = '85302b818aea19b68db899c25dac229412d9bba9b3fcfe4f714dc016bc1686fc446a08844b1f8327fd9cb623cc189be00c5a365ac835e93d4858ee66f43fdc59e32aaed3ef24f0675d70172ef688d376a4807228c55583fe5bac647d10ecef15220feef61477c28cae8406f6f9896ed329d6db9f88757e31848a6c2ce2f94308';
}
$api['url'] = str_replace('/api/', '/weapi/', $api['url']);
$api['body'] = array(
@ -883,8 +897,8 @@ class Meting
if (function_exists('openssl_encrypt')) {
$data = openssl_encrypt($data, 'aes-128-cbc', $key, false, $vi);
} else {
$PAD = 16 - (strlen($data) % 16);
$data = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data.str_repeat(chr($PAD), $PAD), MCRYPT_MODE_CBC, $vi));
$pad = 16 - (strlen($data) % 16);
$data = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $data.str_repeat(chr($pad), $pad), MCRYPT_MODE_CBC, $vi));
}
$api['body']['e'] = $data;
@ -896,7 +910,7 @@ class Meting
{
$data = $this->curl('http://h5api.m.xiami.com/h5/mtop.alimusic.search.searchservice.searchsongs/1.0/?appKey=12574478&t=1511168684000&dataType=json&data=%7B%22requestStr%22%3A%22%7B%5C%22model%5C%22%3A%7B%5C%22key%5C%22%3A%5C%22Dangerous+Woman%5C%22%2C%5C%22pagingVO%5C%22%3A%7B%5C%22page%5C%22%3A1%2C%5C%22pageSize%5C%22%3A30%7D%7D%7D%22%7D&api=mtop.alimusic.search.searchservice.searchsongs&v=1.0&type=originaljson&sign=f6c99a429e9ef703ea955f7cd113a467', null, 1);
preg_match_all('/_m_h5[^;]+/', $data->raw, $match);
$this->cookie = $match[0][0].'; '.$match[0][1];
$this->header['Cookie'] = $match[0][0].'; '.$match[0][1];
$data = json_encode(array(
'requestStr' => json_encode(array(
'header' => array(
@ -906,7 +920,7 @@ class Meting
)),
));
$appkey = '12574478';
$cookie = $this->cookie;
$cookie = $this->header['Cookie'];
preg_match('/_m_h5_tk=([^_]+)/', $cookie, $match);
$token = $match[1];
$t = time() * 1000;

View File

@ -1,12 +1,13 @@
9f5d01ca9d67ca647883e1c0958573265294e33f7b7b4dff04e9b0bdb8dbb9c5 ./Action.php
0ee891ceb5f6a8d4cd94ef256abd7887f2e08ff90eed087a5553b70575cbe69d ./Action.php
aa3c0deef2f5b5524f9e7cbd1809d14d51e0a5eae8f21e183483c84c28b86e46 ./assets/APlayer.min.js
6c1db3ae0b983df3e9395aa133875e64c7cb6321064c982d507fa7d82f7258f3 ./assets/editer.js
efd4f31c32ad090a109c0fb30a04c007093aa3d304e26adcc2cb75153812ecb1 ./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
9129314ad52bf9a010e5a0287b0609595d2a85cb95e08f3ad68463107c82bc42 ./driver/cache.interface.php
59561acde0f21b536d3511591eca46a338db8df836ed260b0c21cb06e68b1043 ./driver/memcached.class.php
e83fbbfc27352142f610b9cf5600b510ee44deaf42f390893250fdc79adc98ba ./driver/mysql.class.php
a8521663ed1f5d4a1cf37290081155e1682fb5aa9a0f0e07e0f7b83cc98c0a00 ./driver/redis.class.php
12f663ca0f4515566448ac6cd96304df038224eb10b8aafb66e49f051495fe98 ./driver/sqlite.class.php
8f730be87f6605716d86925d3066b6fe0df5c8ae1b09f7229b4070fdd1962510 ./include/Meting.php
a237e65d8cc7c5b00008db5977b28e525caeffadfee0808408034f8959c6de20 ./LICENSE
ce6446965969e32bd3a98107e878fcef14acba6de697ae8782c3f0c3435bb448 ./Plugin.php
211c2f194dba153b3fd34f74b3961eb8d7e4d29801327c3010e226ec97706a81 ./Plugin.php
0d5007fa131cc51053b5239c449136eb5a6047dce856b9eabe9e01e6c18a505d ./README.md