mirror of
https://gitee.com/ledc/IYUUAutoReseed
synced 2026-02-14 09:25:27 +00:00
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,5 +1,8 @@
|
||||
/app/torrent
|
||||
/app/config/config.php
|
||||
/torrent
|
||||
/config/config.php
|
||||
/php-7.2.12-nts
|
||||
/*.bat
|
||||
/*.sh
|
||||
/*.sh
|
||||
/vendor
|
||||
.idea
|
||||
.php_cs.cache
|
||||
596
app/AutoReseed.php
Normal file
596
app/AutoReseed.php
Normal file
@@ -0,0 +1,596 @@
|
||||
<?php
|
||||
/**
|
||||
* IYUUAutoReseed自动辅种
|
||||
*/
|
||||
namespace IYUU;
|
||||
|
||||
use Curl\Curl;
|
||||
use IYUU\Client\AbstractClientInterface;
|
||||
use IYUU\Client\qBittorrent\qBittorrent;
|
||||
use IYUU\Client\Transmission\TransmissionRPC;
|
||||
use IYUU\Library\IFile;
|
||||
use IYUU\Library\Oauth;
|
||||
|
||||
/**
|
||||
* IYUUAutoReseed自动辅种类
|
||||
*/
|
||||
class AutoReseed
|
||||
{
|
||||
/**
|
||||
* 版本号
|
||||
* @var string
|
||||
*/
|
||||
const VER = '20191224.1010';
|
||||
/**
|
||||
* RPC连接池
|
||||
* @var array
|
||||
*/
|
||||
public static $links = array();
|
||||
/**
|
||||
* 客户端配置
|
||||
* @var array
|
||||
*/
|
||||
public static $clients = array();
|
||||
/**
|
||||
* 不辅种的站点 'ourbits','hdchina'
|
||||
* @var array
|
||||
*/
|
||||
public static $noReseed = [];
|
||||
/**
|
||||
* 缓存路径
|
||||
* @var string
|
||||
*/
|
||||
public static $cacheDir = TORRENT_PATH.'cache'.DS;
|
||||
public static $cacheHash = TORRENT_PATH.'cachehash'.DS;
|
||||
/**
|
||||
* API接口配置
|
||||
* @var string
|
||||
* @var array
|
||||
*/
|
||||
public static $apiUrl = 'http://iyuu.cn:2122';
|
||||
public static $endpoints = array(
|
||||
'add' => '/api/add',
|
||||
'update' => '/api/update',
|
||||
'reseed' => '/api/reseed',
|
||||
'login' => '/login',
|
||||
);
|
||||
/**
|
||||
* 退出状态码
|
||||
* @var int
|
||||
*/
|
||||
public static $ExitCode = 0;
|
||||
/**
|
||||
* 客户端转移做种 状态码[请把transmission配置为第一个客户端]
|
||||
* @var array
|
||||
*/
|
||||
public static $move = null;
|
||||
/**
|
||||
* 微信消息体
|
||||
* @var array
|
||||
*/
|
||||
public static $wechatMsg = array(
|
||||
'hashCount' => 0, // 提交给服务器的hash总数
|
||||
'sitesCount' => 0, // 可辅种站点总数
|
||||
'reseedCount' => 0, // 返回的总数据
|
||||
'reseedSuccess' => 0, // 成功:辅种成功(会加入缓存,哪怕种子在校验中,下次也会过滤)
|
||||
'reseedError' => 0, // 错误:辅种失败(可以重试)
|
||||
'reseedRepeat' => 0, // 重复:客户端已做种
|
||||
'reseedSkip' => 0, // 跳过:因未设置passkey,而跳过
|
||||
'reseedPass' => 0, // 忽略:因上次成功添加、存在缓存,而跳过
|
||||
);
|
||||
/**
|
||||
* 初始化
|
||||
* @return void
|
||||
*/
|
||||
public static function init()
|
||||
{
|
||||
global $configALL;
|
||||
self::$clients = isset($configALL['default']['clients']) && $configALL['default']['clients'] ? $configALL['default']['clients'] : array();
|
||||
echo "程序正在初始化运行参数... \n";
|
||||
// 递归删除上次历史记录
|
||||
IFile::rmdir(self::$cacheDir, true);
|
||||
// 建立目录
|
||||
IFile::mkdir(self::$cacheDir);
|
||||
IFile::mkdir(self::$cacheHash);
|
||||
// 连接全局客户端
|
||||
self::links();
|
||||
// 合作站点自动注册鉴权
|
||||
Oauth::login(self::$apiUrl . self::$endpoints['login']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接远端RPC服务器
|
||||
*
|
||||
* @param string
|
||||
* @return bool
|
||||
*/
|
||||
public static function links()
|
||||
{
|
||||
if (empty(self::$links)) {
|
||||
foreach (self::$clients as $k => $v) {
|
||||
// 跳过未配置的客户端
|
||||
if (empty($v['username']) || empty($v['password'])) {
|
||||
unset(self::$clients[$k]);
|
||||
echo "clients_".$k." 用户名或密码未配置,已跳过 \n\n";
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
switch ($v['type']) {
|
||||
case 'transmission':
|
||||
$client = new TransmissionRPC($v['host'], $v['username'], $v['password']);
|
||||
break;
|
||||
case 'qBittorrent':
|
||||
$client = new qBittorrent($v['host'], $v['username'], $v['password']);
|
||||
break;
|
||||
default:
|
||||
echo '[ERROR] '.$v['type'];
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
/** @var AbstractClientInterface $client */
|
||||
self::$links[$k]['type'] = $v['type'];
|
||||
self::$links[$k]['rpc'] = $client;
|
||||
$result = $client->status();
|
||||
|
||||
print $v['type'].':'.$v['host']." Rpc连接 [{$result->result}] \n";
|
||||
// 检查是否转移种子的做种客户端?
|
||||
if (isset($v['move']) && $v['move']) {
|
||||
self::$move = array($k,$v['type']);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
echo '[ERROR] ' . $e->getMessage() . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* 从客户端获取种子的哈希列表
|
||||
* @var array
|
||||
*/
|
||||
public static function get()
|
||||
{
|
||||
$hashArray = array();
|
||||
foreach (self::$clients as $k => $v) {
|
||||
$result = array();
|
||||
$res = $info_hash = array();
|
||||
$json = $sha1 = '';
|
||||
try {
|
||||
switch ($v['type']) {
|
||||
case 'transmission':
|
||||
$ids = $fields = array();
|
||||
#$fields = array( "id", "status", "name", "hashString", "downloadDir", "torrentFile" );
|
||||
$fields = array( "id", "status", "hashString", "downloadDir");
|
||||
$result = self::$links[$k]['rpc']->get($ids, $fields);
|
||||
if (empty($result->result) || $result->result != 'success') {
|
||||
// 获取种子列表 失败
|
||||
echo "获取种子列表失败,原因可能是transmission暂时无响应,请稍后重试! \n";
|
||||
break;
|
||||
}
|
||||
if (empty($result->arguments)) {
|
||||
echo "未获取到需要辅种的数据,请多多保种,然后重试! \n";
|
||||
break;
|
||||
}
|
||||
// 对象转数组
|
||||
$res = object_array($result->arguments->torrents);
|
||||
// 过滤,只保留正常做种
|
||||
$res = array_filter($res, "filterStatus");
|
||||
// 提取数组:hashString
|
||||
$info_hash = array_column($res, 'hashString');
|
||||
// 升序排序
|
||||
sort($info_hash);
|
||||
// 微信模板消息 统计
|
||||
self::$wechatMsg['hashCount'] += count($info_hash);
|
||||
$json = json_encode($info_hash, JSON_UNESCAPED_UNICODE);
|
||||
// 去重 应该从文件读入,防止重复提交
|
||||
$sha1 = sha1($json);
|
||||
if (isset($hashArray['sha1']) && (in_array($sha1, $hashArray['sha1']) != false)) {
|
||||
break;
|
||||
}
|
||||
// 组装返回数据
|
||||
$hashArray['hash']['clients_'.$k] = $json;
|
||||
$hashArray['sha1'][] = $sha1;
|
||||
// 变换数组:hashString为键
|
||||
self::$links[$k]['hash'] = array_column($res, "downloadDir", 'hashString');
|
||||
#p(self::$links[$k]['hash']);exit;
|
||||
break;
|
||||
case 'qBittorrent':
|
||||
$result = self::$links[$k]['rpc']->torrentList();
|
||||
$res = json_decode($result, true);
|
||||
if (empty($res)) {
|
||||
echo "未获取到需要辅种的数据,请多多保种,然后重试! \n";
|
||||
break;
|
||||
}
|
||||
#p($res);exit;
|
||||
// 过滤,只保留正常做种
|
||||
$res = array_filter($res, "qbfilterStatus");
|
||||
// 提取数组:hashString
|
||||
$info_hash = array_column($res, 'hash');
|
||||
// 升序排序
|
||||
sort($info_hash);
|
||||
// 微信模板消息 统计
|
||||
self::$wechatMsg['hashCount'] += count($info_hash);
|
||||
$json = json_encode($info_hash, JSON_UNESCAPED_UNICODE);
|
||||
// 去重 应该从文件读入,防止重复提交
|
||||
$sha1 = sha1($json);
|
||||
if (isset($hashArray['sha1']) && (in_array($sha1, $hashArray['sha1']) != false)) {
|
||||
break;
|
||||
}
|
||||
// 组装返回数据
|
||||
$hashArray['hash']['clients_'.$k] = $json;
|
||||
$hashArray['sha1'][] = $sha1;
|
||||
// 变换数组:hash为键
|
||||
self::$links[$k]['hash'] = array_column($res, "save_path", 'hash');
|
||||
#p(self::$links[$k]['hash']);exit;
|
||||
break;
|
||||
default:
|
||||
echo '[ERROR] '.$v['type'];
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
// 是否执行转移种子做种客户端?
|
||||
if (self::$move != null && (empty($v['move']))) {
|
||||
self::move($res, $v['type']);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
echo '[ERROR] ' . $e->getMessage() . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
return $hashArray;
|
||||
}
|
||||
/**
|
||||
* @brief 添加下载任务
|
||||
* @param string $torrent 种子元数据
|
||||
* @param string $save_path 保存路径
|
||||
* @return bool
|
||||
*/
|
||||
public static function add($rpcKey, $torrent, $save_path = '', $extra_options = array())
|
||||
{
|
||||
try {
|
||||
$is_url = false;
|
||||
if ((strpos($torrent, 'http://')===0) || (strpos($torrent, 'https://')===0) || (strpos($torrent, 'magnet:?xt=urn:btih:')===0)) {
|
||||
$is_url = true;
|
||||
}
|
||||
// 下载服务器类型
|
||||
$type = self::$links[$rpcKey]['type'];
|
||||
// 判断
|
||||
switch ($type) {
|
||||
case 'transmission':
|
||||
$extra_options['paused'] = true;
|
||||
if ($is_url) {
|
||||
$result = self::$links[$rpcKey]['rpc']->add($torrent, $save_path, $extra_options); // 种子URL添加下载任务
|
||||
} else {
|
||||
$result = self::$links[$rpcKey]['rpc']->add_metainfo($torrent, $save_path, $extra_options); // 种子元数据添加下载任务
|
||||
}
|
||||
if (isset($result->result) && $result->result == 'success') {
|
||||
$id = $name = '';
|
||||
if (isset($result->arguments->torrent_duplicate)) {
|
||||
$id = $result->arguments->torrent_duplicate->id;
|
||||
$name = $result->arguments->torrent_duplicate->name;
|
||||
} elseif (isset($result->arguments->torrent_added)) {
|
||||
$id = $result->arguments->torrent_added->id;
|
||||
$name = $result->arguments->torrent_added->name;
|
||||
}
|
||||
print "********RPC添加下载任务成功 [{$result->result}] (id=$id) \n";
|
||||
if ($is_url) {
|
||||
print "种子:".$torrent. "\n";
|
||||
}
|
||||
print "名字:".$name."\n\n";
|
||||
return true;
|
||||
} else {
|
||||
$errmsg = isset($result->result) ? $result->result : '未知错误,请稍后重试!';
|
||||
print "-----RPC添加种子任务,失败 [{$errmsg}] \n";
|
||||
if ($is_url) {
|
||||
print "种子:".$torrent. "\n";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'qBittorrent':
|
||||
$extra_options['paused'] = 'true';
|
||||
$extra_options['autoTMM'] = 'false'; //关闭自动种子管理
|
||||
if ($is_url) {
|
||||
$result = self::$links[$rpcKey]['rpc']->add($torrent, $save_path, $extra_options); // 种子URL添加下载任务
|
||||
} else {
|
||||
$extra_options['name'] = 'torrents';
|
||||
$extra_options['filename'] = rand(1, 4294967200).'.torrent';
|
||||
$result = self::$links[$rpcKey]['rpc']->add_metainfo($torrent, $save_path, $extra_options); // 种子元数据添加下载任务
|
||||
}
|
||||
if ($result === 'Ok.') {
|
||||
print "********RPC添加下载任务成功 [{$result}] \n\n";
|
||||
return true;
|
||||
} else {
|
||||
print "-----RPC添加种子任务,失败 [{$result}] \n\n";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
echo '[ERROR] '.$type;
|
||||
break;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
echo '[ERROR] ' . $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 正常做种的种子在各下载器的互相转移
|
||||
*/
|
||||
public static function move($torrent=array(), $type = 'qBittorrent')
|
||||
{
|
||||
switch ($type) {
|
||||
case 'transmission':
|
||||
break;
|
||||
case 'qBittorrent':
|
||||
foreach ($torrent as $k => $v) {
|
||||
// 路径转换
|
||||
#$v['save_path'] = '/volume3' . $v['save_path']; // docker路径转换
|
||||
self::add(self::$move[0], $v['magnet_uri'], $v['save_path']);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
echo '[ERROR] '.$type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 提交种子hash给远端API,用来获取辅种数据
|
||||
* @param array $hashArray 种子hash数组
|
||||
* @return
|
||||
*/
|
||||
public static function call($hashArray = array())
|
||||
{
|
||||
global $configALL;
|
||||
$resArray = $sites = array();
|
||||
$curl = new Curl();
|
||||
$curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
|
||||
// 签名
|
||||
$hashArray['timestamp'] = time();
|
||||
// 爱语飞飞token
|
||||
$hashArray['sign'] = Oauth::getSign();
|
||||
$hashArray['version'] = self::VER;
|
||||
// 写日志
|
||||
if (true) {
|
||||
// 文件句柄
|
||||
$resource = fopen(self::$cacheDir.'hashString.txt', "wb");
|
||||
// 成功:返回写入字节数,失败返回false
|
||||
$worldsnum = fwrite($resource, p($hashArray, false));
|
||||
fclose($resource);
|
||||
}
|
||||
// 发起请求
|
||||
echo "正在提交辅种信息…… \n";
|
||||
$res = $curl->post(self::$apiUrl . self::$endpoints['reseed'], $hashArray);
|
||||
$resArray = json_decode($res->response, true);
|
||||
// 写日志
|
||||
if (true) {
|
||||
// 文件句柄
|
||||
$resource = fopen(self::$cacheDir.'reseed.txt', "wb");
|
||||
// 成功:返回写入字节数,失败返回false
|
||||
$worldsnum = fwrite($resource, p($resArray, false));
|
||||
fclose($resource);
|
||||
}
|
||||
// 判断返回值
|
||||
if (isset($resArray['errmsg']) && ($resArray['errmsg'] == 'ok')) {
|
||||
echo "辅种信息提交成功!!! \n\n";
|
||||
} else {
|
||||
$errmsg = isset($resArray['errmsg']) ? $resArray['errmsg'] : '远端服务器无响应,请稍后重试!';
|
||||
echo '-----辅种失败,原因:' .$errmsg. " \n\n";
|
||||
exit(1);
|
||||
}
|
||||
// 可辅种站点信息列表
|
||||
$sites = $resArray['sites'];
|
||||
self::$wechatMsg['sitesCount'] = count($sites);
|
||||
#p($sites);
|
||||
// 按客户端循环辅种 开始
|
||||
foreach (self::$links as $k => $v) {
|
||||
$reseed = $infohash_Dir = array();
|
||||
// info_hash 对应的下载目录
|
||||
$infohash_Dir = self::$links[$k]['hash'];
|
||||
if (empty($resArray['clients_'.$k])) {
|
||||
echo "clients_".$k."没有查询到可辅种数据 \n\n";
|
||||
continue;
|
||||
}
|
||||
#p($infohash_Dir);
|
||||
// 当前客户端辅种数据
|
||||
$reseed = $resArray['clients_'.$k];
|
||||
foreach ($reseed as $info_hash => $vv) {
|
||||
// 当前种子哈希对应的目录
|
||||
$downloadDir = $infohash_Dir[$info_hash];
|
||||
foreach ($vv['torrent'] as $id => $value) {
|
||||
// 匹配的辅种数据累加
|
||||
self::$wechatMsg['reseedCount']++;
|
||||
// 站点id
|
||||
$sitesID = $value['sid'];
|
||||
$url = $_url = '';
|
||||
$download_page = $details_url = '';
|
||||
// 页面规则
|
||||
$download_page = str_replace('{}', $value['torrent_id'], $sites[$sitesID]['download_page']);
|
||||
$_url = 'https://' .$sites[$sitesID]['base_url']. '/' .$download_page;
|
||||
if (empty($configALL[$sites[$sitesID]['site']]['passkey'])) {
|
||||
echo '-------因当前' .$sites[$sitesID]['site']. '站点未设置passkey,已跳过!!' . "\n\n";
|
||||
self::$wechatMsg['reseedSkip']++;
|
||||
continue;
|
||||
}
|
||||
// 种子URL组合方式区分
|
||||
switch ($sites[$sitesID]['site']) {
|
||||
case 'ttg':
|
||||
$url = $_url."/". $configALL[$sites[$sitesID]['site']]['passkey'];
|
||||
break;
|
||||
case 'm-team':
|
||||
$ip_type = '';
|
||||
if (isset($configALL[$sites[$sitesID]['site']]['ip_type'])) {
|
||||
$ip_type = $configALL[$sites[$sitesID]['site']]['ip_type'] == 'ipv6' ? '&ipv6=1' : '';
|
||||
}
|
||||
$url = $_url."&passkey=". $configALL[$sites[$sitesID]['site']]['passkey'] . $ip_type. "&https=1";
|
||||
break;
|
||||
case 'moecat':
|
||||
$ip_type = '';
|
||||
if (isset($configALL[$sites[$sitesID]['site']]['ip_type'])) {
|
||||
$ip_type = $configALL[$sites[$sitesID]['site']]['ip_type'] == 'ipv6' ? '&ipv6=1' : '';
|
||||
}
|
||||
$url = $_url."&passkey=". $configALL[$sites[$sitesID]['site']]['passkey'] . $ip_type. "&https=1";
|
||||
break;
|
||||
default:
|
||||
$url = $_url."&passkey=". $configALL[$sites[$sitesID]['site']]['passkey'];
|
||||
break;
|
||||
}
|
||||
/**
|
||||
* 检查站点是否可以辅种
|
||||
*/
|
||||
// 判断是否具有VIP或特殊权限?
|
||||
$is_vip = isset($configALL[$sites[$sitesID]['site']]['is_vip']) && $configALL[$sites[$sitesID]['site']]['is_vip'] ? 1 : 0;
|
||||
if ((in_array($sites[$sitesID]['site'], self::$noReseed)==false) || $is_vip) {
|
||||
/**
|
||||
* 可以辅种
|
||||
*/
|
||||
if (isset($infohash_Dir[$value['info_hash']])) {
|
||||
// 与客户端现有种子重复
|
||||
echo '-------与客户端现有种子重复:'.$_url."\n\n";
|
||||
self::$wechatMsg['reseedRepeat']++;
|
||||
continue;
|
||||
} else {
|
||||
// 判断上次是否成功添加?
|
||||
if (is_file(self::$cacheHash . $value['info_hash'].'.txt')) {
|
||||
echo '-------当前种子上次辅种已成功添加,已跳过!'.$_url."\n\n";
|
||||
self::$wechatMsg['reseedPass']++;
|
||||
continue;
|
||||
}
|
||||
// 种子元数据获取
|
||||
switch ($sites[$sitesID]['site']) {
|
||||
case 'hdchina':
|
||||
if (empty($configALL[$sites[$sitesID]['site']]['cookie'])) {
|
||||
echo '-------因当前' .$sites[$sitesID]['site']. '站点未设置cookie,已跳过!!' . "\n\n";
|
||||
self::$wechatMsg['reseedSkip']++;
|
||||
break;
|
||||
}
|
||||
if (isset($configALL[$sites[$sitesID]['site']]['limit'])) {
|
||||
echo "当前站点触发人机验证,已加入排除列表 \n";
|
||||
}
|
||||
$cookie = isset($configALL[$sites[$sitesID]['site']]['cookie']) ? $configALL[$sites[$sitesID]['site']]['cookie'] : '';
|
||||
$userAgent = $configALL['default']['userAgent'];
|
||||
// 拼接URL
|
||||
$details_page = str_replace('{}', $value['torrent_id'], 'details.php?id={}&hit=1');
|
||||
$details_url = 'https://' .$sites[$sitesID]['base_url']. '/' .$details_page;
|
||||
$details_html = download($details_url, $cookie, $userAgent);
|
||||
print "种子详情页:".$details_url. "\n";
|
||||
// 提取种子下载地址
|
||||
$download_page = str_replace('{}', '', $sites[$sitesID]['download_page']);
|
||||
$offset = strpos($details_html, $download_page);
|
||||
$urlTemp = substr($details_html, $offset, 50);
|
||||
// 种子地址
|
||||
$_url = substr($urlTemp, 0, strpos($urlTemp, '">'));
|
||||
$_url = 'https://' .$sites[$sitesID]['base_url']. '/' . $_url;
|
||||
print "种子下载页:".$_url. "\n";
|
||||
$url = download($_url, $cookie, $userAgent);
|
||||
if (strpos($url, '系统检测到过多的种子下载请求') != false) {
|
||||
echo "触发人机验证 \n";
|
||||
ff($sites[$sitesID]['site']. ' 触发人机验证,请重新设置!');
|
||||
self::$noReseed[] = 'hdchina';
|
||||
$configALL[$sites[$sitesID]['site']]['limit'] = 1;
|
||||
}
|
||||
break;
|
||||
case 'hdcity':
|
||||
if (empty($configALL[$sites[$sitesID]['site']]['cookie'])) {
|
||||
echo '-------因当前' .$sites[$sitesID]['site']. '站点未设置cookie,已跳过!!' . "\n\n";
|
||||
self::$wechatMsg['reseedSkip']++;
|
||||
break;
|
||||
}
|
||||
$cookie = isset($configALL[$sites[$sitesID]['site']]['cookie']) ? $configALL[$sites[$sitesID]['site']]['cookie'] : '';
|
||||
$userAgent = $configALL['default']['userAgent'];
|
||||
print "种子:".$_url. "\n";
|
||||
if (isset($configALL[$sites[$sitesID]['site']]['cuhash'])) {
|
||||
// 已获取cuhash
|
||||
# code...
|
||||
} else {
|
||||
// 获取cuhash
|
||||
$html = download('https://' .$sites[$sitesID]['base_url']. '/pt', $cookie, $userAgent);
|
||||
// 提取种子下载地址
|
||||
$offset = strpos($html, 'cuhash=');
|
||||
$len = strlen('cuhash=');
|
||||
$cuhashTemp = substr($html, $offset+$len, 40);
|
||||
$configALL[$sites[$sitesID]['site']]['cuhash'] = substr($cuhashTemp, 0, strpos($cuhashTemp, '"'));
|
||||
}
|
||||
$url = $_url."&cuhash=". $configALL[$sites[$sitesID]['site']]['cuhash'];
|
||||
// 城市下载种子时会302转向
|
||||
$url = download($url, $cookie, $userAgent);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// 把拼接的种子URL,推送给下载器
|
||||
$ret = false;
|
||||
// 成功返回:true
|
||||
$ret = self::add($k, $url, $downloadDir);
|
||||
// 添加成功的种子,以infohash为文件名,写入缓存
|
||||
if ($ret) {
|
||||
// 成功的种子
|
||||
// 文件句柄
|
||||
$resource = fopen(self::$cacheHash . $value['info_hash'].'.txt', "wb");
|
||||
// 成功:返回写入字节数,失败返回false
|
||||
$worldsnum = fwrite($resource, $url);
|
||||
fclose($resource);
|
||||
self::$wechatMsg['reseedSuccess']++;
|
||||
continue;
|
||||
} else {
|
||||
// 失败的种子
|
||||
// 站点类型判断
|
||||
switch ($sites[$sitesID]['site']) {
|
||||
case 'hdcity':
|
||||
echo '当前' .$sites[$sitesID]['site']. '站点是配置cuhash(不是passkey),添加成功说明配置正确!如果添加任务失败,请查阅常见问题!!' . "\n";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// 失败累加
|
||||
self::$wechatMsg['reseedError']++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* 不辅种
|
||||
*/
|
||||
echo '-------已跳过不辅种的站点:'.$_url."\n\n";
|
||||
// 写入日志文件,供用户手动辅种
|
||||
if (!isset($infohash_Dir[$value['info_hash']])) {
|
||||
// 站点类型判断
|
||||
switch ($sites[$sitesID]['site']) {
|
||||
case 'hdchina':
|
||||
$url = $_url;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// 文件句柄
|
||||
$resource = fopen(self::$cacheDir . $sites[$sitesID]['site'].'.txt', 'a');
|
||||
// 成功:返回写入字节数,失败返回false
|
||||
$worldsnum = fwrite($resource, 'clients_'.$k."\n".$downloadDir."\n".$url."\n".$details_url."\n\n");
|
||||
fclose($resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 按客户端循环辅种 结束
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static function wechatMessage()
|
||||
{
|
||||
$br = "\r\n";
|
||||
$text = 'IYUU自动辅种-统计报表';
|
||||
$desp = '总做种:'.self::$wechatMsg['hashCount'] . ' [客户端正在做种的hash总数]' .$br;
|
||||
$desp .= '返回数据:'.self::$wechatMsg['reseedCount']. ' [服务器返回的可辅种数据]' .$br;
|
||||
$desp .= '支持站点:'.self::$wechatMsg['sitesCount']. ' [当前支持自动辅种的站点数量]' .$br;
|
||||
$desp .= '成功:'.self::$wechatMsg['reseedSuccess']. ' [辅种成功,会把hash加入缓存]' .$br;
|
||||
$desp .= '失败:'.self::$wechatMsg['reseedError']. ' [下载器下载种子失败或网络超时引起,可以重试]' .$br;
|
||||
$desp .= '重复:'.self::$wechatMsg['reseedRepeat']. ' [客户端已做种]' .$br;
|
||||
$desp .= '跳过:'.self::$wechatMsg['reseedSkip']. ' [未设置passkey]' .$br;
|
||||
$desp .= '忽略:'.self::$wechatMsg['reseedPass']. ' [成功添加存在缓存]' .$br;
|
||||
return ff($text, $desp);
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Rhilip
|
||||
* Date: 2019/4/21
|
||||
* Time: 21:12
|
||||
*/
|
||||
class Bencode
|
||||
{
|
||||
/**
|
||||
* Decodes a BEncoded string to the following values:
|
||||
* - Dictionary (starts with d, ends with e)
|
||||
* - List (starts with l, ends with e
|
||||
* - Integer (starts with i, ends with e
|
||||
* - String (starts with number denoting number of characters followed by : and then the string)
|
||||
*
|
||||
* @see https://wiki.theory.org/index.php/BitTorrentSpecification
|
||||
*
|
||||
* @param string $data
|
||||
* @param int $pos
|
||||
* @return mixed
|
||||
*/
|
||||
public static function decode($data, &$pos = 0)
|
||||
{
|
||||
$start_decode = ($pos === 0);
|
||||
if ($data[$pos] === 'd') {
|
||||
$pos++;
|
||||
$return = [];
|
||||
while ($data[$pos] !== 'e') {
|
||||
$key = self::decode($data, $pos);
|
||||
$value = self::decode($data, $pos);
|
||||
if ($key === null || $value === null) {
|
||||
break;
|
||||
}
|
||||
if (!is_string($key)) {
|
||||
throw new RuntimeException('Invalid key type, must be string: ' . gettype($key));
|
||||
}
|
||||
$return[$key] = $value;
|
||||
}
|
||||
ksort($return);
|
||||
$pos++;
|
||||
} elseif ($data[$pos] === 'l') {
|
||||
$pos++;
|
||||
$return = [];
|
||||
while ($data[$pos] !== 'e') {
|
||||
$value = self::decode($data, $pos);
|
||||
$return[] = $value;
|
||||
}
|
||||
$pos++;
|
||||
} elseif ($data[$pos] === 'i') {
|
||||
$pos++;
|
||||
$digits = strpos($data, 'e', $pos) - $pos;
|
||||
$return = substr($data, $pos, $digits);
|
||||
if ($return === '-0') {
|
||||
throw new RuntimeException('Cannot have integer value -0');
|
||||
}
|
||||
$multiplier = 1;
|
||||
if ($return[0] === '-') {
|
||||
$multiplier = -1;
|
||||
$return = substr($return, 1);
|
||||
}
|
||||
if (!ctype_digit($return)) {
|
||||
throw new RuntimeException('Cannot have non-digit values in integer number: ' . $return);
|
||||
}
|
||||
$return = $multiplier * ((int)$return);
|
||||
$pos += $digits + 1;
|
||||
} else {
|
||||
$digits = strpos($data, ':', $pos) - $pos;
|
||||
$len = (int)substr($data, $pos, $digits);
|
||||
$pos += ($digits + 1);
|
||||
$return = substr($data, $pos, $len);
|
||||
$pos += $len;
|
||||
}
|
||||
if ($start_decode) {
|
||||
if ($pos !== strlen($data)) {
|
||||
throw new RuntimeException('Could not fully decode bencode string');
|
||||
}
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
/**
|
||||
* @param mixed $data
|
||||
* @return string
|
||||
*/
|
||||
public static function encode($data)
|
||||
{
|
||||
if (is_array($data)) {
|
||||
$return = '';
|
||||
$check = -1;
|
||||
$list = true;
|
||||
foreach ($data as $key => $value) {
|
||||
if ($key !== ++$check) {
|
||||
$list = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($list) {
|
||||
$return .= 'l';
|
||||
foreach ($data as $value) {
|
||||
$return .= self::encode($value);
|
||||
}
|
||||
} else {
|
||||
$return .= 'd';
|
||||
foreach ($data as $key => $value) {
|
||||
$return .= self::encode(strval($key));
|
||||
$return .= self::encode($value);
|
||||
}
|
||||
}
|
||||
$return .= 'e';
|
||||
} elseif (is_integer($data)) {
|
||||
$return = 'i' . $data . 'e';
|
||||
} else {
|
||||
$return = strlen($data) . ':' . $data;
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
/**
|
||||
* Given a path to a file, decode the contents of it
|
||||
*
|
||||
* @param string $path
|
||||
* @return mixed
|
||||
*/
|
||||
public static function load($path)
|
||||
{
|
||||
if (is_file($path)) {
|
||||
return self::decode(file_get_contents($path));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Given a path for a file, encode the contents of it
|
||||
*
|
||||
* @param string $path
|
||||
* @param $data
|
||||
* @return mixed
|
||||
*/
|
||||
public static function dump($path, $data)
|
||||
{
|
||||
return file_put_contents($path, self::encode($data));
|
||||
}
|
||||
}
|
||||
@@ -1,313 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* 调试函数
|
||||
*/
|
||||
function p($data, $echo=true){
|
||||
$str='******************************'."\n";
|
||||
// 如果是boolean或者null直接显示文字;否则print
|
||||
if (is_bool($data)) {
|
||||
$show_data=$data ? 'true' : 'false';
|
||||
}elseif (is_null($data)) {
|
||||
$show_data='null';
|
||||
}else{
|
||||
$show_data=print_r($data,true);
|
||||
}
|
||||
$str.=$show_data;
|
||||
$str.="\n".'******************************'."\n";
|
||||
if($echo){
|
||||
echo $str;
|
||||
return null;
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信推送Server酱
|
||||
*/
|
||||
function sc($text='', $desp='')
|
||||
{
|
||||
global $configALL;
|
||||
$token = $configALL['sc.ftqq.com'];
|
||||
$desp = ($desp=='')?date("Y-m-d H:i:s") :$desp;
|
||||
$postdata = http_build_query(array(
|
||||
'text' => $text,
|
||||
'desp' => $desp
|
||||
));
|
||||
$opts = array('http' => array(
|
||||
'method' => 'POST',
|
||||
'header' => 'Content-type: application/x-www-form-urlencoded',
|
||||
'content' => $postdata
|
||||
));
|
||||
$context = stream_context_create($opts);
|
||||
$result = file_get_contents('http://sc.ftqq.com/'.$token.'.send', false, $context);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信推送 爱语飞飞
|
||||
*/
|
||||
function ff($text='', $desp='')
|
||||
{
|
||||
global $configALL;
|
||||
$token = $configALL['iyuu.cn'];
|
||||
$desp = ($desp=='')?date("Y-m-d H:i:s") :$desp;
|
||||
$postdata = http_build_query(array(
|
||||
'text' => $text,
|
||||
'desp' => $desp
|
||||
));
|
||||
$opts = array('http' => array(
|
||||
'method' => 'POST',
|
||||
'header' => 'Content-type: application/x-www-form-urlencoded',
|
||||
'content' => $postdata
|
||||
));
|
||||
$context = stream_context_create($opts);
|
||||
$result = file_get_contents('http://iyuu.cn/'.$token.'.send', false, $context);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信推送 爱语飞飞
|
||||
* @param array $torrent 种子数组
|
||||
Array
|
||||
(
|
||||
[id] => 118632
|
||||
[h1] => CCTV5+ 2019 ATP Men's Tennis Final 20191115B HDTV 1080i H264-HDSTV
|
||||
[title] => 央视体育赛事频道 2019年ATP男子网球年终总决赛 单打小组赛 纳达尔VS西西帕斯 20191115[优惠剩余时间:4时13分]
|
||||
[details] => https://xxx.me/details.php?id=118632
|
||||
[download] => https://xxx.me/download.php?id=118632
|
||||
[filename] => 118632.torrent
|
||||
[type] => 0
|
||||
[sticky] => 1
|
||||
[time] => Array
|
||||
(
|
||||
[0] => "2019-11-16 20:41:53">4时13分
|
||||
[1] => "2019-11-16 14:41:53">1时<br />46分
|
||||
)
|
||||
[comments] => 0
|
||||
[size] => 5232.64MB
|
||||
[seeders] => 69
|
||||
[leechers] => 10
|
||||
[completed] => 93
|
||||
[percentage] => 100%
|
||||
[owner] => 匿名
|
||||
)
|
||||
*/
|
||||
function send($site = '', $torrent = array())
|
||||
{
|
||||
$br = "\r\n";
|
||||
$text = $site. ' 免费:' .$torrent['filename']. ',添加成功';
|
||||
$desp = '主标题:'.$torrent['h1'] . $br;
|
||||
|
||||
if ( isset($torrent['title']) ) {
|
||||
$desp .= '副标题:'.$torrent['title']. $br;
|
||||
}
|
||||
if ( isset($torrent['size']) ) {
|
||||
$desp .= '大小:'.$torrent['size']. $br;
|
||||
}
|
||||
if ( isset($torrent['seeders']) ) {
|
||||
$desp .= '做种数:'.$torrent['seeders']. $br;
|
||||
}
|
||||
if ( isset($torrent['leechers']) ) {
|
||||
$desp .= '下载数:'.$torrent['leechers']. $br;
|
||||
}
|
||||
if ( isset($torrent['owner']) ) {
|
||||
$desp .= '发布者:'.$torrent['owner']. $br;
|
||||
}
|
||||
return ff($text, $desp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 下载种子
|
||||
* @param string $url 种子URL
|
||||
* @param string $cookies 模拟登陆的cookie
|
||||
* @return mixed 返回的数据
|
||||
*/
|
||||
function download($url, $cookies, $useragent, $method = 'GET')
|
||||
{
|
||||
$header = array(
|
||||
"Content-Type:application/x-www-form-urlencoded",
|
||||
'User-Agent: '.$useragent);
|
||||
$ch = curl_init();
|
||||
if($method === 'POST'){
|
||||
curl_setopt($ch, CURLOPT_POST, true );
|
||||
}
|
||||
if(stripos($url, 'https://') !== FALSE) {
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
|
||||
curl_setopt($ch, CURLOPT_SSLVERSION, 1);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER,$header);
|
||||
curl_setopt($ch, CURLOPT_COOKIE,$cookies);
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT,60);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT,600);
|
||||
$data = curl_exec($ch);
|
||||
$status = curl_getinfo($ch);
|
||||
curl_close($ch);
|
||||
if (isset($status['http_code']) && $status['http_code'] == 200) {
|
||||
return $data;
|
||||
}
|
||||
if (isset($status['http_code']) && $status['http_code'] == 302) {
|
||||
return download($status['redirect_url'], $cookies, $useragent);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 文件大小格式化为MB
|
||||
* @param string $from 文件大小
|
||||
* @return int 单位MB
|
||||
*/
|
||||
function convertToMB($from){
|
||||
$number=substr($from,0,-2);
|
||||
switch(strtoupper(substr($from,-2))){
|
||||
case "KB":
|
||||
return $number/1024;
|
||||
case "MB":
|
||||
return $number;
|
||||
case "GB":
|
||||
return $number*pow(1024,1);
|
||||
case "TB":
|
||||
return $number*pow(1024,2);
|
||||
case "PB":
|
||||
return $number*pow(1024,3);
|
||||
default:
|
||||
return $from;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 种子过滤器
|
||||
* @param string $site 站点标识
|
||||
* @param array $torrent 种子数组
|
||||
* Array
|
||||
(
|
||||
[id] => 118632
|
||||
[h1] => CCTV5+ 2019 ATP Men's Tennis Final 20191115B HDTV 1080i H264-HDSTV
|
||||
[title] => 央视体育赛事频道 2019年ATP男子网球年终总决赛 单打小组赛 纳达尔VS西西帕斯 20191115[优惠剩余时间:4时13分]
|
||||
[details] => https://xxx.me/details.php?id=118632
|
||||
[download] => https://xxx.me/download.php?id=118632
|
||||
[filename] => 118632.torrent
|
||||
[type] => 0
|
||||
[sticky] => 1
|
||||
[time] => Array
|
||||
(
|
||||
[0] => "2019-11-16 20:41:53">4时13分
|
||||
[1] => "2019-11-16 14:41:53">1时<br />46分
|
||||
)
|
||||
[comments] => 0
|
||||
[size] => 5232.64MB
|
||||
[seeders] => 69
|
||||
[leechers] => 10
|
||||
[completed] => 93
|
||||
[percentage] => 100%
|
||||
[owner] => 匿名
|
||||
)
|
||||
* @return bool 或 string false不过滤
|
||||
*/
|
||||
function filter($site = '', $torrent = array()){
|
||||
global $configALL;
|
||||
$config = $configALL[$site];
|
||||
$filter = array();
|
||||
// 读取配置
|
||||
if (isset($configALL['default']['filter']) || isset($config['filter'])) {
|
||||
$filter = isset($config['filter']) && $config['filter'] ? $config['filter'] : $configALL['default']['filter'];
|
||||
}else {
|
||||
return false;
|
||||
}
|
||||
$filename = $torrent['filename'];
|
||||
|
||||
// 兼容性
|
||||
if ( empty($torrent['size']) ) {
|
||||
return false;
|
||||
}
|
||||
// 大小过滤
|
||||
$size = convertToMB($torrent['size']);
|
||||
$min = isset($filter['size']['min']) ? convertToMB($filter['size']['min']) : 0;
|
||||
$max = isset($filter['size']['max']) ? convertToMB($filter['size']['max']) : 2097152; //默认 2097152MB = 2TB
|
||||
if ($min > $size || $size > $max) {
|
||||
return $filename. ' ' .$size. 'MB,被大小过滤';
|
||||
}
|
||||
|
||||
// 兼容性
|
||||
if ( empty($torrent['seeders']) ) {
|
||||
return false;
|
||||
}
|
||||
// 种子数过滤
|
||||
$seeders = $torrent['seeders'];
|
||||
$min = isset($filter['seeders']['min']) ? $filter['seeders']['min'] : 1; //默认 1
|
||||
$max = isset($filter['seeders']['max']) ? $filter['seeders']['max'] : 3; //默认 3
|
||||
if ($min > $seeders || $seeders > $max) {
|
||||
return $filename. ' 当前做种' .$seeders. '人,被过滤';
|
||||
}
|
||||
|
||||
// 兼容性
|
||||
if ( empty($torrent['leechers']) ) {
|
||||
return false;
|
||||
}
|
||||
// 下载数过滤
|
||||
$leechers = $torrent['leechers'];
|
||||
$min = isset($filter['leechers']['min']) ? $filter['leechers']['min'] : 0; //默认
|
||||
$max = isset($filter['leechers']['max']) ? $filter['leechers']['max'] : 30000; //默认
|
||||
if ($min > $leechers || $leechers > $max) {
|
||||
return $filename. ' 当前下载' .$leechers. '人,被过滤';
|
||||
}
|
||||
|
||||
// 兼容性
|
||||
if ( empty($torrent['completed']) ) {
|
||||
return false;
|
||||
}
|
||||
// 完成数过滤
|
||||
$completed = $torrent['completed'];
|
||||
$min = isset($filter['completed']['min']) ? $filter['completed']['min'] : 0; //默认
|
||||
$max = isset($filter['completed']['max']) ? $filter['completed']['max'] : 30000; //默认
|
||||
if ($min > $completed || $completed > $max) {
|
||||
return $filename. ' 已完成数' .$completed. '人,被过滤';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 奇数
|
||||
*/
|
||||
function oddFilter($var)
|
||||
{
|
||||
// 返回$var最后一个二进制位,
|
||||
// 为1则保留(奇数的二进制的最后一位肯定是1)
|
||||
return($var & 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 偶数
|
||||
*/
|
||||
function evenFilter($var)
|
||||
{
|
||||
// 返回$var最后一个二进制位,
|
||||
// 为0则保留(偶数的二进制的最后一位肯定是0)
|
||||
return(!($var & 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发布员签名
|
||||
* 注意:同时配置iyuu.cn与secret时,优先使用secret。
|
||||
*/
|
||||
function sign( $timestamp ){
|
||||
global $configALL;
|
||||
// 爱语飞飞
|
||||
$token = isset($configALL['iyuu.cn']) && $configALL['iyuu.cn'] ? $configALL['iyuu.cn'] : '';
|
||||
// 鉴权
|
||||
$token = isset($configALL['secret']) && $configALL['secret'] ? $configALL['secret'] : $token;
|
||||
return sha1($timestamp . $token);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 分离token中的用户uid
|
||||
* token算法:IYUU + uid + T + sha1(openid+time+盐)
|
||||
* @param string $token 用户请求token
|
||||
*/
|
||||
function getUid($token){
|
||||
//验证是否IYUU开头,strpos($token,'T')<15,token总长度小于60(40+10+5)
|
||||
return (strlen($token)<60)&&(strpos($token,'IYUU')===0)&&(strpos($token,'T')<15) ? substr($token,4,strpos($token,'T')-4): false;
|
||||
}
|
||||
@@ -1,332 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief 文件处理
|
||||
* @version 0.6
|
||||
*/
|
||||
|
||||
/**
|
||||
* @class IFile
|
||||
* @brief IFile 文件处理类
|
||||
*/
|
||||
class IFile
|
||||
{
|
||||
private $resource = null; //文件资源句柄
|
||||
|
||||
/**
|
||||
* @brief 构造函数,打开资源流,并独占锁定
|
||||
* @param String $fileName 文件路径名
|
||||
* @param String $mode 操作方式,默认为读操作,可供选择的项为:r,r+,w+,w+,a,a+
|
||||
* @note $mod,'r' 只读方式打开,将文件指针指向文件头
|
||||
* 'r+' 读写方式打开,将文件指针指向文件头
|
||||
* 'w' 写入方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
|
||||
* 'w+' 读写方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
|
||||
* 'a' 写入方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
|
||||
* 'a+' 读写方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
|
||||
*/
|
||||
function __construct($fileName,$mode='r')
|
||||
{
|
||||
$dirName = dirname($fileName);
|
||||
$baseName = basename($fileName);
|
||||
|
||||
//检查并创建文件夹
|
||||
self::mkdir($dirName);
|
||||
|
||||
$this->resource = fopen($fileName,$mode.'b');
|
||||
if($this->resource)
|
||||
{
|
||||
flock($this->resource,LOCK_EX);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取文件内容
|
||||
* @return String 文件内容
|
||||
*/
|
||||
public function read()
|
||||
{
|
||||
$content = null;
|
||||
while(!feof($this->resource))
|
||||
{
|
||||
$content.= fread($this->resource,1024);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 文件写入操作
|
||||
* @param String $content 要写入的文件内容
|
||||
* @return Int or false 写入的字符数; false:写入失败;
|
||||
*/
|
||||
public function write($content)
|
||||
{
|
||||
$worldsnum = fwrite($this->resource,$content);
|
||||
$this->save();
|
||||
return is_bool($worldsnum) ? false : $worldsnum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空目录下的所有文件
|
||||
* @return bool false:失败; true:成功;
|
||||
*/
|
||||
public static function clearDir($dir)
|
||||
{
|
||||
if($dir[0] != '.' && is_dir($dir) && is_writable($dir))
|
||||
{
|
||||
$dirRes = opendir($dir);
|
||||
while( false !== ($fileName = readdir($dirRes)) )
|
||||
{
|
||||
if($fileName[0] !== '.')
|
||||
{
|
||||
$fullpath = $dir.'/'.$fileName;
|
||||
if(is_file($fullpath))
|
||||
{
|
||||
self::unlink($fullpath);
|
||||
}
|
||||
else
|
||||
{
|
||||
self::clearDir($fullpath);
|
||||
rmdir($fullpath);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($dirRes);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取文件信息
|
||||
* @param String $fileName 文件路径
|
||||
* @return array or null array:文件信息; null:文件不存在;
|
||||
*/
|
||||
public static function getInfo($fileName)
|
||||
{
|
||||
if(is_file($fileName))
|
||||
return stat($fileName);
|
||||
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建文件夹
|
||||
* @param String $path 路径
|
||||
* @param int $chmod 文件夹权限
|
||||
* @note $chmod 参数不能是字符串(加引号),否则linux会出现权限问题
|
||||
*/
|
||||
public static function mkdir($path,$chmod=0777)
|
||||
{
|
||||
return is_dir($path) or (self::mkdir(dirname($path),$chmod) and mkdir($path,$chmod));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 复制文件
|
||||
* @param String $from 源文件路径
|
||||
* @param String $to 目标文件路径
|
||||
* @param String $mod 操作模式,c:复制(默认); x:剪切(删除$from文件)
|
||||
* @return bool 操作结果 true:成功; false:失败;
|
||||
*/
|
||||
public static function copy($from,$to,$mode = 'c')
|
||||
{
|
||||
$dir = dirname($to);
|
||||
|
||||
//创建目录
|
||||
self::mkdir($dir);
|
||||
|
||||
copy($from,$to);
|
||||
|
||||
if(is_file($to))
|
||||
{
|
||||
if($mode == 'x')
|
||||
{
|
||||
self::unlink($from);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 删除文件
|
||||
* @param String $fileName 文件路径
|
||||
* @return bool 操作结果 false:删除失败;
|
||||
*/
|
||||
public static function unlink($fileName)
|
||||
{
|
||||
if(is_file($fileName) && is_writable($fileName))
|
||||
{
|
||||
return unlink($fileName);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 删除$dir文件夹 或者 其下所有文件
|
||||
* @param String $dir 文件路径
|
||||
* @param bool $recursive 是否强制删除,如果强制删除则递归删除该目录下的全部文件,默认为false
|
||||
* @return bool true:删除成功; false:删除失败;
|
||||
*/
|
||||
public static function rmdir($dir,$recursive = false)
|
||||
{
|
||||
if(is_dir($dir) && is_writable($dir))
|
||||
{
|
||||
//强制删除
|
||||
if($recursive == true)
|
||||
{
|
||||
self::clearDir($dir);
|
||||
return self::rmdir($dir,false);
|
||||
}
|
||||
|
||||
//非强制删除
|
||||
else
|
||||
{
|
||||
if(rmdir($dir))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取文件类型
|
||||
* @param String $fileName 文件名
|
||||
* @return String $filetype 文件类型
|
||||
* @note 如果文件不存在,返回false,如果文件后缀名不在识别列表之内,返回NULL,对于docx及elsx格式文档识别在会出现识别为ZIP格式的错误,这是office2007的bug目前尚未修复,请谨慎使用
|
||||
*/
|
||||
public static function getFileType($fileName)
|
||||
{
|
||||
$filetype = null;
|
||||
if(!is_file($fileName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
$fileRes = fopen($fileName,"rb");
|
||||
if(!$fileRes)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
$bin= fread($fileRes, 2);
|
||||
fclose($fileRes);
|
||||
|
||||
if($bin != null)
|
||||
{
|
||||
$strInfo = unpack("C2chars", $bin);
|
||||
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
|
||||
$typelist = self::getTypeList();
|
||||
foreach($typelist as $val)
|
||||
{
|
||||
if(strtolower($val[0]) == strtolower($typeCode))
|
||||
{
|
||||
if($val[0] == 8075)
|
||||
{
|
||||
return array('zip','docx','xlsx');
|
||||
}
|
||||
else
|
||||
{
|
||||
return $val[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $filetype;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取文件类型映射关系
|
||||
* @return array 文件类型映射关系数组
|
||||
*/
|
||||
public static function getTypeList()
|
||||
{
|
||||
return array(
|
||||
array('255216','jpg'),
|
||||
array('13780','png'),
|
||||
array('7173','gif'),
|
||||
array('6677','bmp'),
|
||||
array('6063','xml'),
|
||||
array('60104','html'),
|
||||
array('208207','xls/doc'),
|
||||
array('8075','zip'),
|
||||
array('8075','docx'),
|
||||
array('8075','xlsx'),
|
||||
array("8297","rar"),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取文件大小
|
||||
* @param String $fileName 文件名
|
||||
* @return Int 文件大小的字节数,如果文件无效则返回 NULL
|
||||
*/
|
||||
public static function getFileSize($fileName)
|
||||
{
|
||||
return is_file($fileName) ? filesize($fileName):null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检测文件夹是否为空
|
||||
* @param String $dir 路径地址
|
||||
* @return bool true:$dir为空目录; false:$dir为非空目录;
|
||||
*/
|
||||
public static function isEmptyDir($dir)
|
||||
{
|
||||
if(is_dir($dir))
|
||||
{
|
||||
$isEmpty = true;
|
||||
$dirRes = opendir($dir);
|
||||
while(false !== ($fileName = readdir($dirRes)))
|
||||
{
|
||||
if($fileName!='.' && $fileName!='..')
|
||||
{
|
||||
$isEmpty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir($dirRes);
|
||||
return $isEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 释放文件锁定
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
flock($this->resource,LOCK_UN);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取文件扩展名
|
||||
* @param String $fileName 文件名
|
||||
* @return String 文件后缀名
|
||||
*/
|
||||
public static function getFileSuffix($fileName)
|
||||
{
|
||||
$fileInfoArray = pathinfo($fileName);
|
||||
return $fileInfoArray['extension'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 析构函数,释放文件连接句柄
|
||||
*/
|
||||
function __destruct()
|
||||
{
|
||||
if(is_resource($this->resource))
|
||||
{
|
||||
fclose($this->resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,368 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Rpc操作类
|
||||
*/
|
||||
class Rpc
|
||||
{
|
||||
/**
|
||||
* 版本号
|
||||
* @var string
|
||||
*/
|
||||
const VER = '0.0.1';
|
||||
|
||||
// 下载种子的请求类型 GET POST
|
||||
public static $method = 'GET';
|
||||
// RPC连接池
|
||||
public static $links = array();
|
||||
/**
|
||||
* cookie
|
||||
*/
|
||||
public static $cookies = '';
|
||||
/**
|
||||
* 浏览器 User-Agent
|
||||
*/
|
||||
public static $userAgent = '';
|
||||
/**
|
||||
* passkey
|
||||
*/
|
||||
public static $passkey = '';
|
||||
/**
|
||||
* 客户端配置
|
||||
*/
|
||||
public static $clients = '';
|
||||
/**
|
||||
* 监控目录
|
||||
*/
|
||||
public static $watch = '';
|
||||
/**
|
||||
* 种子存放路径
|
||||
*/
|
||||
public static $torrentDir = '';
|
||||
/**
|
||||
* 工作模式
|
||||
*/
|
||||
public static $workingMode = '';
|
||||
// 站点标识
|
||||
public static $site = '';
|
||||
/**
|
||||
* 负载均衡 控制变量
|
||||
*/
|
||||
public static $RPC_Key = 0;
|
||||
/**
|
||||
* 退出状态码
|
||||
*/
|
||||
public static $ExitCode = 0;
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
public static function init($site = '', $method = 'GET'){
|
||||
global $configALL;
|
||||
|
||||
self::$site = $site;
|
||||
self::$method = strtoupper($method);
|
||||
|
||||
$config = $configALL[$site];
|
||||
self::$cookies = $config['cookie'];
|
||||
self::$userAgent = isset($config['userAgent']) && $config['userAgent'] ? $config['userAgent'] : $configALL['default']['userAgent'];
|
||||
self::$clients = isset($config['clients']) && $config['clients'] ? $config['clients'] : $configALL['default']['clients'];
|
||||
self::$workingMode = isset($config['workingMode']) && $config['workingMode'] ? $config['workingMode'] : 0;
|
||||
$watch = isset($config['watch']) && $config['watch'] ? $config['watch'] : $configALL['default']['watch'];
|
||||
self::$watch = rtrim($watch,'/') . DS;
|
||||
self::$torrentDir = TORRENT_PATH . $site . DS;
|
||||
// 建立目录
|
||||
IFile::mkdir(self::$torrentDir);
|
||||
|
||||
self::links();
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接远端RPC服务器
|
||||
*
|
||||
* @param string
|
||||
* @return array
|
||||
*/
|
||||
public static function links()
|
||||
{
|
||||
if(self::$workingMode === 1 && empty(self::$links)){
|
||||
foreach ( self::$clients as $k => $v ){
|
||||
// 跳过未配置的客户端
|
||||
if (empty($v['username']) || empty( $v['password'])) {
|
||||
unset(self::$clients[$k]);
|
||||
echo "clients_".$k." 用户名或密码未配置,已跳过 \n\n";
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
switch($v['type']){
|
||||
case 'transmission':
|
||||
self::$links[$k]['rpc'] = new TransmissionRPC($v['host'], $v['username'], $v['password']);
|
||||
$result = self::$links[$k]['rpc']->sstats();
|
||||
print $v['type'].':'.$v['host']." Rpc连接成功 [{$result->result}] \n";
|
||||
break;
|
||||
case 'qBittorrent':
|
||||
self::$links[$k]['rpc'] = new qBittorrent($v['host'], $v['username'], $v['password']);
|
||||
$result = self::$links[$k]['rpc']->appVersion();
|
||||
print $v['type'].':'.$v['host']." Rpc连接成功 [{$result}] \n";
|
||||
break;
|
||||
case 'uTorrent':
|
||||
self::$links[$k]['rpc'] = new uTorrent($v['host'], $v['username'], $v['password']);
|
||||
$result = self::$links[$k]['rpc']->getBuild();
|
||||
print $v['type'].':'.$v['host']." Rpc连接 [{$result}] \n";
|
||||
break;
|
||||
default:
|
||||
echo '[ERROR] '.$v['type'];
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
self::$links[$k]['type'] = $v['type'];
|
||||
self::$links[$k]['downloadDir'] = $v['downloadDir'];
|
||||
} catch (Exception $e) {
|
||||
echo '[ERROR] ' . $e->getMessage() . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加下载任务
|
||||
* @param string $torrent 种子元数据
|
||||
* @param string $save_path 保存路径
|
||||
* @return bool
|
||||
*/
|
||||
public static function add($torrent, $save_path = '', $extra_options = array())
|
||||
{
|
||||
switch( (int)self::$workingMode ){
|
||||
case 0: // watch默认工作模式
|
||||
// 复制到watch目录
|
||||
copy($torrent,$save_path);
|
||||
if(is_file($save_path)){
|
||||
print "********watch模式,下载任务添加成功 \n\n";
|
||||
return true;
|
||||
}else {
|
||||
print "-----watch模式,下载任务添加失败!!! \n\n";
|
||||
}
|
||||
break;
|
||||
case 1: //负载均衡模式
|
||||
try
|
||||
{
|
||||
$is_url = false;
|
||||
if( (strpos($torrent,'http://')===0) || (strpos($torrent,'https://')===0) || (strpos($torrent,'magnet:?xt=urn:btih:')===0) ){
|
||||
$is_url = true;
|
||||
}
|
||||
// 负载均衡
|
||||
$rpcKey = self::$RPC_Key;
|
||||
echo '选中:负载均衡'.$rpcKey."\n";
|
||||
self::rpcSelect();
|
||||
// 调试
|
||||
#p($result);
|
||||
// 下载服务器类型 判断
|
||||
$type = self::$links[$rpcKey]['type'];
|
||||
switch($type){
|
||||
case 'transmission':
|
||||
if( $is_url ){
|
||||
echo 'add';
|
||||
$result = self::$links[$rpcKey]['rpc']->add( $torrent, self::$links[$rpcKey]['downloadDir'], $extra_options ); // 种子URL添加下载任务
|
||||
}else{
|
||||
echo 'add_metainfo';
|
||||
$result = self::$links[$rpcKey]['rpc']->add_metainfo( $torrent, self::$links[$rpcKey]['downloadDir'], $extra_options ); // 种子文件添加下载任务
|
||||
}
|
||||
$id = $name = '';
|
||||
if( isset($result->arguments->torrent_duplicate) ){
|
||||
$id = $result->arguments->torrent_duplicate->id;
|
||||
$name = $result->arguments->torrent_duplicate->name;
|
||||
}elseif( isset($result->arguments->torrent_added) ){
|
||||
$id = $result->arguments->torrent_added->id;
|
||||
$name = $result->arguments->torrent_added->name;
|
||||
}
|
||||
if(!$id){
|
||||
print "-----RPC添加种子任务,失败 [{$result->result}] \n\n";
|
||||
}else{
|
||||
print "********RPC添加下载任务成功 [{$result->result}] (id=$id) \n\n";
|
||||
// 新添加的任务,开始
|
||||
self::$links[$rpcKey]['rpc']->start( $id );
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 'qBittorrent':
|
||||
if( $is_url ){
|
||||
echo 'add';
|
||||
$result = self::$links[$rpcKey]['rpc']->add( $torrent, self::$links[$rpcKey]['downloadDir'], $extra_options ); // 种子URL添加下载任务
|
||||
}else{
|
||||
echo 'add_metainfo';
|
||||
$result = self::$links[$rpcKey]['rpc']->add_metainfo( $torrent, self::$links[$rpcKey]['downloadDir'], $extra_options ); // 种子文件添加下载任务
|
||||
}
|
||||
if ($result === 'Ok.') {
|
||||
print "********RPC添加下载任务成功 [{$result}] \n\n";
|
||||
return true;
|
||||
} else {
|
||||
print "-----RPC添加种子任务,失败 [{$result}] \n\n";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
echo '[ERROR] '.$type;
|
||||
break;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
die('[ERROR] ' . $e->getMessage() . PHP_EOL);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
echo "\n\n";
|
||||
# 暂未开放
|
||||
break;
|
||||
default:
|
||||
echo "\n\n";
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 负载均衡 选择算法
|
||||
*
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
public static function rpcSelect()
|
||||
{
|
||||
$clientsConut = count(self::$clients);
|
||||
if( $clientsConut > 1 ){
|
||||
if( $clientsConut > (self::$RPC_Key+1) ){
|
||||
self::$RPC_Key++;
|
||||
}else{
|
||||
self::$RPC_Key = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief 种子处理函数
|
||||
* @param array $data 种子数组
|
||||
* Array
|
||||
(
|
||||
[id] => 118632
|
||||
[h1] => CCTV5+ 2019 ATP Men's Tennis Final 20191115B HDTV 1080i H264-HDxxx
|
||||
[title] => 央视体育赛事频道 2019年ATP男子网球年终总决赛 单打小组赛 纳达尔VS西西帕斯 20191115[优惠剩余时间:4时13分]
|
||||
[details] => https://XXX.me/details.php?id=118632
|
||||
[download] => https://XXX.me/download.php?id=118632
|
||||
[filename] => 118632.torrent
|
||||
[type] => 0
|
||||
[sticky] => 1
|
||||
[time] => Array
|
||||
(
|
||||
[0] => "2019-11-16 20:41:53">4时13分
|
||||
[1] => "2019-11-16 14:41:53">1时<br />46分
|
||||
)
|
||||
[comments] => 0
|
||||
[size] => 5232.64MB
|
||||
[seeders] => 69
|
||||
[leechers] => 10
|
||||
[completed] => 93
|
||||
[percentage] => 100%
|
||||
[owner] => 匿名
|
||||
)
|
||||
* @return
|
||||
*/
|
||||
public static function call($data = array())
|
||||
{
|
||||
foreach ($data as $key => $value) {
|
||||
// 控制台打印
|
||||
echo '主标题:'.$value['h1']."\n";
|
||||
echo '副标题:'.$value['title']."\n";
|
||||
echo '详情页:'.$value['details']."\n";
|
||||
if ( $value['type'] != 0 ) {
|
||||
echo "-----非免费,已忽略! \n\n";
|
||||
continue;
|
||||
}
|
||||
if ( isset($value['hr']) && ($value['hr'] == 1) ) {
|
||||
echo "-----HR种子,已忽略! \n\n";
|
||||
continue;
|
||||
}
|
||||
// 下载任务的可选参数
|
||||
$extra_options = array();
|
||||
// 保存的文件名
|
||||
$filename = $value['id'] . '.torrent';
|
||||
// 默认watch工作模式,复制到此目录
|
||||
$to = self::$watch . $filename;
|
||||
// 种子完整存放路径
|
||||
$torrentFile = self::$torrentDir . $filename;
|
||||
if(is_file($torrentFile)){
|
||||
$fileSize = filesize($torrentFile); //失败会返回false 或 0(0代表上次下载失败)
|
||||
if ( !empty($fileSize) ) {
|
||||
//种子已经存在
|
||||
echo '-----存在旧种子:'.$filename."\n\n";
|
||||
continue;
|
||||
}
|
||||
// 删除下载错误的文件
|
||||
IFile::unlink($torrentFile);
|
||||
}
|
||||
|
||||
// 调用过滤函数
|
||||
$isFilter = filter(self::$site, $value);
|
||||
if ( is_string( $isFilter ) ) {
|
||||
echo "-----" .$isFilter. "\n\n";
|
||||
continue;
|
||||
}
|
||||
//种子不存在
|
||||
echo '正在下载新种子... '.$value['download']." \n";
|
||||
// 创建文件、下载种子以二进制写入
|
||||
$content = '';
|
||||
$content = download($value['download'], self::$cookies, self::$userAgent, self::$method);
|
||||
#p($content);
|
||||
// 文件句柄
|
||||
$resource = fopen($torrentFile, "wb");
|
||||
// 成功:返回写入字节数,失败返回false
|
||||
$worldsnum = fwrite($resource, $content);
|
||||
// 关闭
|
||||
fclose($resource);
|
||||
// 判断
|
||||
if(is_bool($worldsnum)){
|
||||
print "种子下载失败!!! \n\n";
|
||||
IFile::unlink($torrentFile);
|
||||
continue;
|
||||
}else{
|
||||
print "成功下载种子" . $filename . ',共计:' . $worldsnum . "字节 \n";
|
||||
sleep(mt_rand(2,10));
|
||||
$ret = false;
|
||||
$rpcKey = self::$RPC_Key;
|
||||
switch( (int)self::$workingMode ){
|
||||
case 0: //默认工作模式
|
||||
$ret = self::add($torrentFile, $to);
|
||||
break;
|
||||
case 1: //负载均衡模式
|
||||
$type = self::$links[$rpcKey]['type'];
|
||||
// 下载服务器类型
|
||||
switch ($type) {
|
||||
case 'transmission':
|
||||
# code...
|
||||
break;
|
||||
case 'qBittorrent':
|
||||
$extra_options['name'] = 'torrents';
|
||||
$extra_options['filename'] = $filename;
|
||||
$extra_options['autoTMM'] = 'false'; //关闭自动种子管理
|
||||
break;
|
||||
default:
|
||||
# code...
|
||||
break;
|
||||
}
|
||||
// 种子文件添加下载任务
|
||||
$ret = self::add($content, $to, $extra_options);
|
||||
break;
|
||||
case 2:
|
||||
echo "\n\n";
|
||||
# 暂未开放
|
||||
break;
|
||||
default:
|
||||
echo "\n\n";
|
||||
break;
|
||||
}
|
||||
global $configALL;
|
||||
if( isset($configALL['iyuu.cn']) && ($ret === true) ){
|
||||
send(self::$site, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,708 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Transmission bittorrent client/daemon RPC communication class
|
||||
* Copyright (C) 2010 Johan Adriaans <johan.adriaans@gmail.com>,
|
||||
* Bryce Chidester <bryce@cobryce.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* PHP version specific information
|
||||
* version_compare() (PHP 4 >= 4.1.0, PHP 5)
|
||||
* ctype_digit() (PHP 4 >= 4.0.4, PHP 5)
|
||||
* stream_context_create (PHP 4 >= 4.3.0, PHP 5)
|
||||
* PHP Class support (PHP 5) (PHP 4 might work, untested)
|
||||
*/
|
||||
|
||||
/**
|
||||
* A friendly little version check...
|
||||
*/
|
||||
if ( version_compare( PHP_VERSION, TransmissionRPC::MIN_PHPVER, '<' ) )
|
||||
die( "The TransmissionRPC class requires PHP version {TransmissionRPC::TRANSMISSIONRPC_MIN_PHPVER} or above." . PHP_EOL );
|
||||
|
||||
/**
|
||||
* Transmission bittorrent client/daemon RPC communication class
|
||||
*
|
||||
* Usage example:
|
||||
* <code>
|
||||
* $rpc = new TransmissionRPC($rpc_url);
|
||||
* $result = $rpc->add_file( $url_or_path_to_torrent, $target_folder );
|
||||
* </code>
|
||||
*
|
||||
*/
|
||||
class TransmissionRPC
|
||||
{
|
||||
/**
|
||||
* User agent used in all http communication
|
||||
*/
|
||||
const HTTP_UA = 'TransmissionRPC for PHP/0.3';
|
||||
|
||||
/**
|
||||
* Minimum PHP version required
|
||||
* 5.2.10 implemented the required http stream ignore_errors option
|
||||
*/
|
||||
const MIN_PHPVER = '5.2.10';
|
||||
|
||||
/**
|
||||
* The URL to the bittorent client you want to communicate with
|
||||
* the port (default: 9091) can be set in you Transmission preferences
|
||||
* @var string
|
||||
*/
|
||||
public $url = '';
|
||||
|
||||
/**
|
||||
* If your Transmission RPC requires authentication, supply username here
|
||||
* @var string
|
||||
*/
|
||||
public $username = '';
|
||||
|
||||
/**
|
||||
* If your Transmission RPC requires authentication, supply password here
|
||||
* @var string
|
||||
*/
|
||||
public $password = '';
|
||||
|
||||
/**
|
||||
* Return results as an array, or an object (default)
|
||||
* @var bool
|
||||
*/
|
||||
public $return_as_array = false;
|
||||
|
||||
/**
|
||||
* Print debugging information, default is off
|
||||
* @var bool
|
||||
*/
|
||||
public $debug = false;
|
||||
|
||||
/**
|
||||
* Transmission RPC version
|
||||
* @var int
|
||||
*/
|
||||
protected $rpc_version = 0;
|
||||
|
||||
/**
|
||||
* Transmission uses a session id to prevent CSRF attacks
|
||||
* @var string
|
||||
*/
|
||||
protected $session_id = '';
|
||||
|
||||
/**
|
||||
* Default values for stream context
|
||||
* @var array
|
||||
*/
|
||||
private $default_context_opts = array( 'http' => array(
|
||||
'user_agent' => self::HTTP_UA,
|
||||
'timeout' => '60', // Don't want to be too slow
|
||||
'ignore_errors' => true, // Leave the error parsing/handling to the code
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Constants for torrent status
|
||||
*/
|
||||
const TR_STATUS_STOPPED = 0;
|
||||
const TR_STATUS_CHECK_WAIT = 1;
|
||||
const TR_STATUS_CHECK = 2;
|
||||
const TR_STATUS_DOWNLOAD_WAIT = 3;
|
||||
const TR_STATUS_DOWNLOAD = 4;
|
||||
const TR_STATUS_SEED_WAIT = 5;
|
||||
const TR_STATUS_SEED = 6;
|
||||
|
||||
const RPC_LT_14_TR_STATUS_CHECK_WAIT = 1;
|
||||
const RPC_LT_14_TR_STATUS_CHECK = 2;
|
||||
const RPC_LT_14_TR_STATUS_DOWNLOAD = 4;
|
||||
const RPC_LT_14_TR_STATUS_SEED = 8;
|
||||
const RPC_LT_14_TR_STATUS_STOPPED = 16;
|
||||
|
||||
/**
|
||||
* Start one or more torrents
|
||||
*
|
||||
* @param int|array ids A list of transmission torrent ids
|
||||
*/
|
||||
public function start ( $ids )
|
||||
{
|
||||
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
|
||||
$request = array( "ids" => $ids );
|
||||
return $this->request( "torrent-start", $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop one or more torrents
|
||||
*
|
||||
* @param int|array ids A list of transmission torrent ids
|
||||
*/
|
||||
public function stop ( $ids )
|
||||
{
|
||||
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
|
||||
$request = array( "ids" => $ids );
|
||||
return $this->request( "torrent-stop", $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Reannounce one or more torrents
|
||||
*
|
||||
* @param int|array ids A list of transmission torrent ids
|
||||
*/
|
||||
public function reannounce ( $ids )
|
||||
{
|
||||
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
|
||||
$request = array( "ids" => $ids );
|
||||
return $this->request( "torrent-reannounce", $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify one or more torrents
|
||||
*
|
||||
* @param int|array ids A list of transmission torrent ids
|
||||
*/
|
||||
public function verify ( $ids )
|
||||
{
|
||||
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
|
||||
$request = array( "ids" => $ids );
|
||||
return $this->request( "torrent-verify", $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information on torrents in transmission, if the ids parameter is
|
||||
* empty all torrents will be returned. The fields array can be used to return certain
|
||||
* fields. Default fields are: "id", "name", "status", "doneDate", "haveValid", "totalSize".
|
||||
* See https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt for available fields
|
||||
*
|
||||
* @param array fields An array of return fields
|
||||
* @param int|array ids A list of transmission torrent ids
|
||||
*
|
||||
Request:
|
||||
{
|
||||
"arguments": {
|
||||
"fields": [ "id", "name", "totalSize" ],
|
||||
"ids": [ 7, 10 ]
|
||||
},
|
||||
"method": "torrent-get",
|
||||
"tag": 39693
|
||||
}
|
||||
|
||||
Response:
|
||||
{
|
||||
"arguments": {
|
||||
"torrents": [
|
||||
{
|
||||
"id": 10,
|
||||
"name": "Fedora x86_64 DVD",
|
||||
"totalSize": 34983493932,
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"name": "Ubuntu x86_64 DVD",
|
||||
"totalSize", 9923890123,
|
||||
}
|
||||
]
|
||||
},
|
||||
"result": "success",
|
||||
"tag": 39693
|
||||
}
|
||||
*/
|
||||
public function get ( $ids = array(), $fields = array() )
|
||||
{
|
||||
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
|
||||
if ( count( $fields ) == 0 ) $fields = array( "id", "name", "status", "doneDate", "haveValid", "totalSize" ); // Defaults
|
||||
$request = array(
|
||||
"fields" => $fields,
|
||||
"ids" => $ids
|
||||
);
|
||||
return $this->request( "torrent-get", $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set properties on one or more torrents, available fields are:
|
||||
* "bandwidthPriority" | number this torrent's bandwidth tr_priority_t
|
||||
* "downloadLimit" | number maximum download speed (in K/s)
|
||||
* "downloadLimited" | boolean true if "downloadLimit" is honored
|
||||
* "files-wanted" | array indices of file(s) to download
|
||||
* "files-unwanted" | array indices of file(s) to not download
|
||||
* "honorsSessionLimits" | boolean true if session upload limits are honored
|
||||
* "ids" | array torrent list, as described in 3.1
|
||||
* "location" | string new location of the torrent's content
|
||||
* "peer-limit" | number maximum number of peers
|
||||
* "priority-high" | array indices of high-priority file(s)
|
||||
* "priority-low" | array indices of low-priority file(s)
|
||||
* "priority-normal" | array indices of normal-priority file(s)
|
||||
* "seedRatioLimit" | double session seeding ratio
|
||||
* "seedRatioMode" | number which ratio to use. See tr_ratiolimit
|
||||
* "uploadLimit" | number maximum upload speed (in K/s)
|
||||
* "uploadLimited" | boolean true if "uploadLimit" is honored
|
||||
* See https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt for more information
|
||||
*
|
||||
* @param array arguments An associative array of arguments to set
|
||||
* @param int|array ids A list of transmission torrent ids
|
||||
*/
|
||||
public function set ( $ids = array(), $arguments = array() )
|
||||
{
|
||||
// See https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt for available fields
|
||||
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
|
||||
if ( !isset( $arguments['ids'] ) ) $arguments['ids'] = $ids; // Any $ids given in $arguments overrides the method parameter
|
||||
return $this->request( "torrent-set", $arguments );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new torrent
|
||||
*
|
||||
* Available extra options:
|
||||
* key | value type & description
|
||||
* ---------------------+-------------------------------------------------
|
||||
* "download-dir" | string path to download the torrent to
|
||||
* "filename" | string filename or URL of the .torrent file
|
||||
* "metainfo" | string base64-encoded .torrent content
|
||||
* "paused" | boolean if true, don't start the torrent
|
||||
* "peer-limit" | number maximum number of peers
|
||||
* "bandwidthPriority" | number torrent's bandwidth tr_priority_t
|
||||
* "files-wanted" | array indices of file(s) to download
|
||||
* "files-unwanted" | array indices of file(s) to not download
|
||||
* "priority-high" | array indices of high-priority file(s)
|
||||
* "priority-low" | array indices of low-priority file(s)
|
||||
* "priority-normal" | array indices of normal-priority file(s)
|
||||
*
|
||||
* Either "filename" OR "metainfo" MUST be included.
|
||||
* All other arguments are optional.
|
||||
*
|
||||
* @param torrent_location The URL or path to the torrent file
|
||||
* @param save_path Folder to save torrent in
|
||||
* @param extra options Optional extra torrent options
|
||||
*/
|
||||
public function add_file ( $torrent_location, $save_path = '', $extra_options = array() )
|
||||
{
|
||||
if(!empty($save_path)) $extra_options['download-dir'] = $save_path;
|
||||
$extra_options['filename'] = $torrent_location;
|
||||
|
||||
return $this->request( "torrent-add", $extra_options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a torrent using the raw torrent data
|
||||
*
|
||||
* @param torrent_metainfo The raw, unencoded contents (metainfo) of a torrent
|
||||
* @param save_path Folder to save torrent in
|
||||
* @param extra options Optional extra torrent options
|
||||
*/
|
||||
public function add_metainfo ( $torrent_metainfo, $save_path = '', $extra_options = array() )
|
||||
{
|
||||
$extra_options['download-dir'] = $save_path;
|
||||
$extra_options['metainfo'] = base64_encode( $torrent_metainfo );
|
||||
|
||||
return $this->request( "torrent-add", $extra_options );
|
||||
}
|
||||
|
||||
/* Add a new torrent using a file path or a URL (For backwards compatibility)
|
||||
* @param torrent_location The URL or path to the torrent file
|
||||
* @param save_path Folder to save torrent in
|
||||
* @param extra options Optional extra torrent options
|
||||
*/
|
||||
public function add ( $torrent_location, $save_path = '', $extra_options = array() )
|
||||
{
|
||||
return $this->add_file( $torrent_location, $save_path, $extra_options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove torrent from transmission
|
||||
*
|
||||
* @param bool delete_local_data Also remove local data?
|
||||
* @param int|array ids A list of transmission torrent ids
|
||||
*/
|
||||
public function remove ( $ids, $delete_local_data = false )
|
||||
{
|
||||
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
|
||||
$request = array(
|
||||
"ids" => $ids,
|
||||
"delete-local-data" => $delete_local_data
|
||||
);
|
||||
return $this->request( "torrent-remove", $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* Move local storage location
|
||||
*
|
||||
* @param int|array ids A list of transmission torrent ids
|
||||
* @param string target_location The new storage location
|
||||
* @param string move_existing_data Move existing data or scan new location for available data
|
||||
*/
|
||||
public function move ( $ids, $target_location, $move_existing_data = true )
|
||||
{
|
||||
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $ids to an array if only a single id was passed
|
||||
$request = array(
|
||||
"ids" => $ids,
|
||||
"location" => $target_location,
|
||||
"move" => $move_existing_data
|
||||
);
|
||||
return $this->request( "torrent-set-location", $request );
|
||||
}
|
||||
|
||||
/**
|
||||
* 3.7. Renaming a Torrent's Path
|
||||
*
|
||||
* Method name: "torrent-rename-path"
|
||||
*
|
||||
* For more information on the use of this function, see the transmission.h
|
||||
* documentation of tr_torrentRenamePath(). In particular, note that if this
|
||||
* call succeeds you'll want to update the torrent's "files" and "name" field
|
||||
* with torrent-get.
|
||||
*
|
||||
* Request arguments:
|
||||
*
|
||||
* string | value type & description
|
||||
* ---------------------------------+-------------------------------------------------
|
||||
* "ids" | array the torrent torrent list, as described in 3.1
|
||||
* | (must only be 1 torrent)
|
||||
* "path" | string the path to the file or folder that will be renamed
|
||||
* "name" | string the file or folder's new name
|
||||
|
||||
* Response arguments: "path", "name", and "id", holding the torrent ID integer
|
||||
*
|
||||
* @param int|array ids A 1-element list of transmission torrent ids
|
||||
* @param string path The path to the file or folder that will be renamed
|
||||
* @param string name The file or folder's new name
|
||||
*/
|
||||
public function rename ( $ids, $path, $name )
|
||||
{
|
||||
if ( !is_array( $ids ) ) $ids = array( $ids ); // Convert $id to an array if only a single id was passed
|
||||
if ( count( $ids ) !== 1 ) {
|
||||
throw new TransmissionRPCException( 'A single id is accepted', TransmissionRPCException::E_INVALIDARG );
|
||||
}
|
||||
|
||||
$request = array(
|
||||
"ids" => $ids,
|
||||
"path" => $path,
|
||||
"name" => $name
|
||||
);
|
||||
return $this->request( "torrent-rename-path", $request );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve session statistics
|
||||
*
|
||||
* @returns array of statistics
|
||||
*/
|
||||
public function sstats ( )
|
||||
{
|
||||
return $this->request( "session-stats", array() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all session variables
|
||||
*
|
||||
* @returns array of session information
|
||||
*/
|
||||
public function sget ( )
|
||||
{
|
||||
return $this->request( "session-get", array() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set session variable(s)
|
||||
*
|
||||
* @param array of session variables to set
|
||||
*/
|
||||
public function sset ( $arguments )
|
||||
{
|
||||
return $this->request( "session-set", $arguments );
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the interpretation of the torrent status
|
||||
*
|
||||
* @param int The integer "torrent status"
|
||||
* @returns string The translated meaning
|
||||
*/
|
||||
public function getStatusString ( $intstatus )
|
||||
{
|
||||
if($this->rpc_version < 14){
|
||||
if( $intstatus == self::RPC_LT_14_TR_STATUS_CHECK_WAIT )
|
||||
return "Waiting to verify local files";
|
||||
if( $intstatus == self::RPC_LT_14_TR_STATUS_CHECK )
|
||||
return "Verifying local files";
|
||||
if( $intstatus == self::RPC_LT_14_TR_STATUS_DOWNLOAD )
|
||||
return "Downloading";
|
||||
if( $intstatus == self::RPC_LT_14_TR_STATUS_SEED )
|
||||
return "Seeding";
|
||||
if( $intstatus == self::RPC_LT_14_TR_STATUS_STOPPED )
|
||||
return "Stopped";
|
||||
}else{
|
||||
if( $intstatus == self::TR_STATUS_CHECK_WAIT )
|
||||
return "Waiting to verify local files";
|
||||
if( $intstatus == self::TR_STATUS_CHECK )
|
||||
return "Verifying local files";
|
||||
if( $intstatus == self::TR_STATUS_DOWNLOAD )
|
||||
return "Downloading";
|
||||
if( $intstatus == self::TR_STATUS_SEED )
|
||||
return "Seeding";
|
||||
if( $intstatus == self::TR_STATUS_STOPPED )
|
||||
return "Stopped";
|
||||
if( $intstatus == self::TR_STATUS_SEED_WAIT )
|
||||
return "Queued for seeding";
|
||||
if( $intstatus == self::TR_STATUS_DOWNLOAD_WAIT )
|
||||
return "Queued for download";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Here be dragons (Internal methods)
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Clean up the request array. Removes any empty fields from the request
|
||||
*
|
||||
* @param array array The request associative array to clean
|
||||
* @returns array The cleaned array
|
||||
*/
|
||||
protected function cleanRequestData ( $array )
|
||||
{
|
||||
if ( !is_array( $array ) || count( $array ) == 0 ) return null; // Nothing to clean
|
||||
setlocale( LC_NUMERIC, 'en_US.utf8' ); // Override the locale - if the system locale is wrong, then 12.34 will encode as 12,34 which is invalid JSON
|
||||
foreach ( $array as $index => $value )
|
||||
{
|
||||
if( is_object( $value ) ) $array[$index] = $value->toArray(); // Convert objects to arrays so they can be JSON encoded
|
||||
if( is_array( $value ) ) $array[$index] = $this->cleanRequestData( $value ); // Recursion
|
||||
if( empty( $value ) && $value !== 0 ) // Remove empty members
|
||||
{
|
||||
unset( $array[$index] );
|
||||
continue; // Skip the rest of the tests - they may re-add the element.
|
||||
}
|
||||
if( is_numeric( $value ) ) $array[$index] = $value+0; // Force type-casting for proper JSON encoding (+0 is a cheap way to maintain int/float/etc)
|
||||
if( is_bool( $value ) ) $array[$index] = ( $value ? 1 : 0); // Store boolean values as 0 or 1
|
||||
if( is_string( $value ) ) {
|
||||
if ( mb_detect_encoding($value,"auto") !== 'UTF-8' ) {
|
||||
$array[$index] = mb_convert_encoding($value, "UTF-8");
|
||||
//utf8_encode( $value ); // Make sure all data is UTF-8 encoded for Transmission
|
||||
}
|
||||
}
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up the result object. Replaces all minus(-) characters in the object properties with underscores
|
||||
* and converts any object with any all-digit property names to an array.
|
||||
*
|
||||
* @param object The request result to clean
|
||||
* @returns array The cleaned object
|
||||
*/
|
||||
protected function cleanResultObject ( $object )
|
||||
{
|
||||
// Prepare and cast object to array
|
||||
$return_as_array = false;
|
||||
$array = $object;
|
||||
if ( !is_array( $array ) ) $array = (array) $array;
|
||||
foreach ( $array as $index => $value )
|
||||
{
|
||||
if( is_array( $array[$index] ) || is_object( $array[$index] ) )
|
||||
{
|
||||
$array[$index] = $this->cleanResultObject( $array[$index] ); // Recursion
|
||||
}
|
||||
if ( strstr( $index, '-' ) )
|
||||
{
|
||||
$valid_index = str_replace( '-', '_', $index );
|
||||
$array[$valid_index] = $array[$index];
|
||||
unset( $array[$index] );
|
||||
$index = $valid_index;
|
||||
}
|
||||
// Might be an array, check index for digits, if so, an array should be returned
|
||||
if ( ctype_digit( (string) $index ) ) { $return_as_array = true; }
|
||||
if ( empty( $value ) ) unset( $array[$index] );
|
||||
}
|
||||
// Return array cast to object
|
||||
return $return_as_array ? $array : (object) $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行 rpc 请求
|
||||
*
|
||||
* @param $method 请求类型/方法, 详见 $this->allowMethods
|
||||
* @param array $arguments 附加参数, 可选
|
||||
* @return mixed
|
||||
*/
|
||||
protected function request($method, $arguments = array())
|
||||
{
|
||||
// Check the parameters
|
||||
if ( !is_scalar( $method ) )
|
||||
throw new TransmissionRPCException( 'Method name has no scalar value', TransmissionRPCException::E_INVALIDARG );
|
||||
if ( !is_array( $arguments ) )
|
||||
throw new TransmissionRPCException( 'Arguments must be given as array', TransmissionRPCException::E_INVALIDARG );
|
||||
|
||||
$arguments = $this->cleanRequestData( $arguments ); // Sanitize input
|
||||
|
||||
// Grab the X-Transmission-Session-Id if we don't have it already
|
||||
if( !$this->session_id )
|
||||
if( !$this->GetSessionID() )
|
||||
throw new TransmissionRPCException( 'Unable to acquire X-Transmission-Session-Id', TransmissionRPCException::E_SESSIONID );
|
||||
|
||||
$data = array(
|
||||
'method' => $method,
|
||||
'arguments' => $arguments
|
||||
);
|
||||
|
||||
$header = array(
|
||||
'Content-Type: application/json',
|
||||
'Authorization: Basic '.base64_encode(sprintf("%s:%s", $this->username, $this->password)),
|
||||
'X-Transmission-Session-Id: '.$this->session_id
|
||||
);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $this->url);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
|
||||
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
||||
curl_setopt($ch, CURLOPT_USERPWD, $this->username.':'.$this->password);
|
||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 600);
|
||||
$content = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if (!$content) $content = json_encode(array('result' => 'failed'));
|
||||
return $this->return_as_array ? json_decode( $content, true ) : $this->cleanResultObject( json_decode( $content ) ); // Return the sanitized result
|
||||
}
|
||||
/**
|
||||
* Performs an empty GET on the Transmission RPC to get the X-Transmission-Session-Id
|
||||
* and store it in $this->session_id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetSessionID()
|
||||
{
|
||||
if( !$this->url )
|
||||
throw new TransmissionRPCException( "Class must be initialized before GetSessionID() can be called.", TransmissionRPCException::E_INVALIDARG );
|
||||
|
||||
// Setup the context
|
||||
$contextopts = $this->default_context_opts; // Start with the defaults
|
||||
|
||||
// Make sure it's blank/empty (reset)
|
||||
$this->session_id = null;
|
||||
|
||||
// Setup authentication (if provided)
|
||||
if ( $this->username && $this->password )
|
||||
$contextopts['http']['header'] = sprintf( "Authorization: Basic %s\r\n", base64_encode( $this->username.':'.$this->password ) );
|
||||
|
||||
if( $this->debug ) echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Stream context created with options:".
|
||||
PHP_EOL . print_r( $contextopts, true );
|
||||
|
||||
$context = stream_context_create( $contextopts ); // Create the context for this request
|
||||
if ( ! $fp = @fopen( $this->url, 'r', false, $context ) ) // Open a filepointer to the data, and use fgets to get the result
|
||||
throw new TransmissionRPCException( 'Unable to connect to '.$this->url, TransmissionRPCException::E_CONNECTION );
|
||||
|
||||
// Check the response (headers etc)
|
||||
$stream_meta = stream_get_meta_data( $fp );
|
||||
fclose( $fp );
|
||||
if( $this->debug ) echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Stream meta info: ".
|
||||
PHP_EOL . print_r( $stream_meta, true );
|
||||
if( $stream_meta['timed_out'] )
|
||||
throw new TransmissionRPCException( "Timed out connecting to {$this->url}", TransmissionRPCException::E_CONNECTION );
|
||||
if( substr( $stream_meta['wrapper_data'][0], 9, 3 ) == "401" )
|
||||
throw new TransmissionRPCException( "Invalid username/password.", TransmissionRPCException::E_AUTHENTICATION );
|
||||
elseif( substr( $stream_meta['wrapper_data'][0], 9, 3 ) == "409" ) // This is what we're hoping to find
|
||||
{
|
||||
// Loop through the returned headers and extract the X-Transmission-Session-Id
|
||||
foreach( $stream_meta['wrapper_data'] as $header )
|
||||
{
|
||||
if( strpos( $header, 'X-Transmission-Session-Id: ' ) === 0 )
|
||||
{
|
||||
if( $this->debug ) echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Session-Id header: ".
|
||||
PHP_EOL . print_r( $header, true );
|
||||
$this->session_id = trim( substr( $header, 27 ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( ! $this->session_id ) { // Didn't find a session_id
|
||||
throw new TransmissionRPCException( "Unable to retrieve X-Transmission-Session-Id", TransmissionRPCException::E_SESSIONID );
|
||||
}
|
||||
} else {
|
||||
throw new TransmissionRPCException( "Unexpected response from Transmission RPC: ".$stream_meta['wrapper_data'][0] );
|
||||
}
|
||||
return $this->session_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the connection parameters
|
||||
*
|
||||
* TODO: Sanitize username, password, and URL
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
*/
|
||||
public function __construct( $url = 'http://localhost:9091/transmission/rpc', $username = null, $password = null, $return_as_array = false )
|
||||
{
|
||||
// server URL
|
||||
$this->url = $url;
|
||||
|
||||
// Username & password
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
|
||||
// Get the Transmission RPC_version
|
||||
$this->rpc_version = self::sget()->arguments->rpc_version;
|
||||
|
||||
// Return As Array
|
||||
$this->return_as_array = $return_as_array;
|
||||
|
||||
// Reset X-Transmission-Session-Id so we (re)fetch one
|
||||
$this->session_id = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the type of exception the TransmissionRPC class will throw
|
||||
*/
|
||||
class TransmissionRPCException extends Exception
|
||||
{
|
||||
/**
|
||||
* Exception: Invalid arguments
|
||||
*/
|
||||
const E_INVALIDARG = -1;
|
||||
|
||||
/**
|
||||
* Exception: Invalid Session-Id
|
||||
*/
|
||||
const E_SESSIONID = -2;
|
||||
|
||||
/**
|
||||
* Exception: Error while connecting
|
||||
*/
|
||||
const E_CONNECTION = -3;
|
||||
|
||||
/**
|
||||
* Exception: Error 401 returned, unauthorized
|
||||
*/
|
||||
const E_AUTHENTICATION = -4;
|
||||
|
||||
/**
|
||||
* Exception constructor
|
||||
*/
|
||||
public function __construct( $message = null, $code = 0, Exception $previous = null )
|
||||
{
|
||||
// PHP version 5.3.0 and above support Exception linking
|
||||
if ( version_compare( PHP_VERSION, '5.3.0', '>=' ) )
|
||||
parent::__construct( $message, $code, $previous );
|
||||
else
|
||||
parent::__construct( $message, $code );
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
@@ -1,255 +0,0 @@
|
||||
<?php
|
||||
define("UTORRENT_TORRENT_HASH", 0);
|
||||
define("UTORRENT_TORRENT_STATUS", 1);
|
||||
define("UTORRENT_TORRENT_NAME", 2);
|
||||
define("UTORRENT_TORRENT_SIZE", 3);
|
||||
define("UTORRENT_TORRENT_PROGRESS", 4);
|
||||
define("UTORRENT_TORRENT_DOWNLOADED", 5);
|
||||
define("UTORRENT_TORRENT_UPLOADED", 6);
|
||||
define("UTORRENT_TORRENT_RATIO", 7);
|
||||
define("UTORRENT_TORRENT_UPSPEED", 8);
|
||||
define("UTORRENT_TORRENT_DOWNSPEED", 9);
|
||||
define("UTORRENT_TORRENT_ETA", 10);
|
||||
define("UTORRENT_TORRENT_LABEL", 11);
|
||||
define("UTORRENT_TORRENT_PEERS_CONNECTED", 12);
|
||||
define("UTORRENT_TORRENT_PEERS_SWARM", 13);
|
||||
define("UTORRENT_TORRENT_SEEDS_CONNECTED", 14);
|
||||
define("UTORRENT_TORRENT_SEEDS_SWARM", 15);
|
||||
define("UTORRENT_TORRENT_AVAILABILITY", 16);
|
||||
define("UTORRENT_TORRENT_QUEUE_POSITION", 17);
|
||||
define("UTORRENT_TORRENT_REMAINING", 18);
|
||||
define("UTORRENT_FILEPRIORITY_HIGH", 3);
|
||||
define("UTORRENT_FILEPRIORITY_NORMAL", 2);
|
||||
define("UTORRENT_FILEPRIORITY_LOW", 1);
|
||||
define("UTORRENT_FILEPRIORITY_SKIP", 0);
|
||||
define("UTORRENT_TYPE_INTEGER", 0);
|
||||
define("UTORRENT_TYPE_BOOLEAN", 1);
|
||||
define("UTORRENT_TYPE_STRING", 2);
|
||||
define("UTORRENT_STATUS_STARTED", 1);
|
||||
define("UTORRENT_STATUS_CHECKED", 2);
|
||||
define("UTORRENT_STATUS_START_AFTER_CHECK", 4);
|
||||
|
||||
class uTorrent {
|
||||
// class static variables
|
||||
private static $base = "%s/gui/%s";
|
||||
|
||||
// member variables
|
||||
public $host;
|
||||
public $user;
|
||||
public $pass;
|
||||
|
||||
protected $token;
|
||||
protected $guid;
|
||||
|
||||
// constructor
|
||||
function __construct($host = "", $user = "", $pass = "") {
|
||||
$this->host = rtrim($host,'/');
|
||||
$this->user = $user;
|
||||
$this->pass = $pass;
|
||||
|
||||
if (!$this->getToken()) {
|
||||
//handle error here, don't know how to best do this yet
|
||||
die('could not get token');
|
||||
}
|
||||
}
|
||||
|
||||
// performs request
|
||||
private function makeRequest($request, $decode = true, $options = array()) {
|
||||
$request = preg_replace('/^\?/', '?token='.$this->token . '&', $request);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt_array($ch, $options);
|
||||
curl_setopt($ch, CURLOPT_URL, sprintf(self::$base, $this->host, $request));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_USERPWD, $this->user.":".$this->pass);
|
||||
curl_setopt($ch, CURLOPT_COOKIE, "GUID=".$this->guid);
|
||||
|
||||
$req = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return ($decode ? json_decode($req, true) : $req);
|
||||
}
|
||||
|
||||
// implodes given parameter with glue, whether it is an array or not
|
||||
private function paramImplode($glue, $param) {
|
||||
return $glue.implode($glue, is_array($param) ? $param : array($param));
|
||||
}
|
||||
|
||||
// gets token, returns true on success
|
||||
private function getToken() {
|
||||
$url = sprintf(self::$base, $this->host, 'token.html');
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_USERPWD, $this->user.":".$this->pass);
|
||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||
$output = curl_exec($ch);
|
||||
$info = curl_getinfo($ch);
|
||||
curl_close($ch);
|
||||
|
||||
$headers = substr($output, 0, $info['header_size']);
|
||||
|
||||
if (preg_match("@Set-Cookie: GUID=([^;]+);@i", $headers, $matches)) {
|
||||
$this->guid = $matches[1];
|
||||
}
|
||||
|
||||
if (preg_match('/<div id=\'token\'.+>(.*)<\/div>/', $output, $m)) {
|
||||
$this->token = $m[1];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// returns the uTorrent build number
|
||||
public function getBuild(){
|
||||
$json = $this->makeRequest("?");
|
||||
return $json['build'];
|
||||
}
|
||||
|
||||
// returns an array of files for the specified torrent hash
|
||||
// TODO:
|
||||
// - (when implemented in API) allow multiple hashes to be specified
|
||||
public function getFiles($hash) {
|
||||
$json = $this->makeRequest("?action=getfiles&hash=".$hash);
|
||||
return $json['files'];
|
||||
}
|
||||
|
||||
// returns an array of all labels
|
||||
public function getLabels(){
|
||||
$json = $this->makeRequest("?list=1");
|
||||
return $json['label'];
|
||||
}
|
||||
|
||||
// returns an array of the properties for the specified torrent hash
|
||||
// TODO:
|
||||
// - (when implemented in API) allow multiple hashes to be specified
|
||||
public function getProperties($hash) {
|
||||
$json = $this->makeRequest("?action=getprops&hash=".$hash);
|
||||
return $json['props'];
|
||||
}
|
||||
|
||||
// returns an array of all settings
|
||||
public function getSettings() {
|
||||
$json = $this->makeRequest("?action=getsettings");
|
||||
return $json['settings'];
|
||||
}
|
||||
|
||||
// returns an array of all torrent jobs and related information
|
||||
public function getTorrents() {
|
||||
$json = $this->makeRequest("?list=1");
|
||||
return $json['torrents'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the RSS favourites/filters
|
||||
* @return model\Filter[]
|
||||
*/
|
||||
public function getRSSFilters() {
|
||||
$json = $this->makeRequest("?list=1");
|
||||
$filters = array();
|
||||
foreach ($json['rssfilters'] as $filter) {
|
||||
$filters[] = model\Filter::fromData($filter);
|
||||
}
|
||||
return $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an RSS filter as retrieved from getRSSFilters
|
||||
* @param \uTorrent\model\Filter $filter
|
||||
*/
|
||||
public function setRSSFilter(model\Filter $filter) {
|
||||
$request = array_merge(array('action' => 'filter-update'), $filter->toParams());
|
||||
return $this->makeRequest('?'.http_build_query($request));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new RSS filter
|
||||
* Requires a utorrent > 2.2.1 (not sure which version exactly)
|
||||
* @param \uTorrent\model\Filter $filter
|
||||
* @return int ID of the new filter
|
||||
*/
|
||||
public function addRSSFilter(model\Filter $filter) {
|
||||
$filter->filterId = -1;
|
||||
$resp = $this->setRSSFilter($filter);
|
||||
if (!empty($resp['filter_ident'])) {
|
||||
return $resp['filter_ident'];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// returns true if WebUI server is online and enabled, false otherwise
|
||||
public function is_online() {
|
||||
return is_array($this->makeRequest("?"));
|
||||
}
|
||||
|
||||
// sets the properties for the specified torrent hash
|
||||
// TODO:
|
||||
// - allow multiple hashes, properties, and values to be set simultaneously
|
||||
public function setProperties($hash, $property, $value) {
|
||||
$this->makeRequest("?action=setprops&hash=".$hash."&s=".$property."&v=".$value, false);
|
||||
}
|
||||
|
||||
// sets the priorities for the specified files in the specified torrent hash
|
||||
public function setPriority($hash, $files, $priority) {
|
||||
$this->makeRequest("?action=setprio&hash=".$hash."&p=".$priority.$this->paramImplode("&f=", $files), false);
|
||||
}
|
||||
|
||||
// sets the settings
|
||||
// TODO:
|
||||
// - allow multiple settings and values to be set simultaneously
|
||||
public function setSetting($setting, $value) {
|
||||
$this->makeRequest("?action=setsetting&s=".$setting."&v=".$value, false);
|
||||
}
|
||||
|
||||
// add a file to the list
|
||||
public function torrentAdd($filename, &$estring = false) {
|
||||
$split = explode(":", $filename, 2);
|
||||
if (count($split) > 1 && (stristr("|http|https|file|magnet|", "|".$split[0]."|") !== false)) {
|
||||
$this->makeRequest("?action=add-url&s=".urlencode($filename), false);
|
||||
}
|
||||
elseif (file_exists($filename)) {
|
||||
$json = $this->makeRequest("?action=add-file", true, array(CURLOPT_POSTFIELDS => array("torrent_file" => "@".realpath($filename))));
|
||||
|
||||
if (isset($json['error'])) {
|
||||
if ($estring !== false) $estring = $json['error'];
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
if ($estring !== false) $estring = "File doesn't exist!";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// force start the specified torrent hashes
|
||||
public function torrentForceStart($hash) {
|
||||
$this->makeRequest("?action=forcestart".$this->paramImplode("&hash=", $hash), false);
|
||||
}
|
||||
|
||||
// pause the specified torrent hashes
|
||||
public function torrentPause($hash) {
|
||||
$this->makeRequest("?action=pause".$this->paramImplode("&hash=", $hash), false);
|
||||
}
|
||||
|
||||
// recheck the specified torrent hashes
|
||||
public function torrentRecheck($hash) {
|
||||
$this->makeRequest("?action=recheck".$this->paramImplode("&hash=", $hash), false);
|
||||
}
|
||||
|
||||
// start the specified torrent hashes
|
||||
public function torrentStart($hash) {
|
||||
$this->makeRequest("?action=start".$this->paramImplode("&hash=", $hash), false);
|
||||
}
|
||||
|
||||
// stop the specified torrent hashes
|
||||
public function torrentStop($hash) {
|
||||
$this->makeRequest("?action=stop".$this->paramImplode("&hash=", $hash), false);
|
||||
}
|
||||
|
||||
// remove the specified torrent hashes (and data, if $data is set to true)
|
||||
public function torrentRemove($hash, $data = false) {
|
||||
$this->makeRequest("?action=".($data ? "removedata" : "remove").$this->paramImplode("&hash=", $hash), false);
|
||||
}
|
||||
}
|
||||
21
app/Client/AbstractClientInterface.php
Normal file
21
app/Client/AbstractClientInterface.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Rhilip
|
||||
* Date: 1/17/2020
|
||||
* Time: 2020
|
||||
*/
|
||||
|
||||
namespace IYUU\Client;
|
||||
|
||||
interface AbstractClientInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* 查询Bittorrent客户端状态
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function status();
|
||||
|
||||
}
|
||||
769
app/Client/Transmission/TransmissionRPC.php
Normal file
769
app/Client/Transmission/TransmissionRPC.php
Normal file
@@ -0,0 +1,769 @@
|
||||
<?php
|
||||
/**
|
||||
* Transmission bittorrent client/daemon RPC communication class
|
||||
* Copyright (C) 2010 Johan Adriaans <johan.adriaans@gmail.com>,
|
||||
* Bryce Chidester <bryce@cobryce.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* PHP version specific information
|
||||
* version_compare() (PHP 4 >= 4.1.0, PHP 5)
|
||||
* ctype_digit() (PHP 4 >= 4.0.4, PHP 5)
|
||||
* stream_context_create (PHP 4 >= 4.3.0, PHP 5)
|
||||
* PHP Class support (PHP 5) (PHP 4 might work, untested)
|
||||
*/
|
||||
|
||||
namespace IYUU\Client\Transmission;
|
||||
|
||||
use IYUU\Client\AbstractClientInterface;
|
||||
|
||||
/**
|
||||
* A friendly little version check...
|
||||
*/
|
||||
if (version_compare(PHP_VERSION, TransmissionRPC::MIN_PHPVER, '<')) {
|
||||
die("The TransmissionRPC class requires PHP version {TransmissionRPC::TRANSMISSIONRPC_MIN_PHPVER} or above." . PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmission bittorrent client/daemon RPC communication class
|
||||
*
|
||||
* Usage example:
|
||||
* <code>
|
||||
* $rpc = new TransmissionRPC($rpc_url);
|
||||
* $result = $rpc->add_file( $url_or_path_to_torrent, $target_folder );
|
||||
* </code>
|
||||
*
|
||||
*/
|
||||
class TransmissionRPC implements AbstractClientInterface
|
||||
{
|
||||
/**
|
||||
* User agent used in all http communication
|
||||
*/
|
||||
const HTTP_UA = 'TransmissionRPC for PHP/0.3';
|
||||
|
||||
/**
|
||||
* Minimum PHP version required
|
||||
* 5.2.10 implemented the required http stream ignore_errors option
|
||||
*/
|
||||
const MIN_PHPVER = '5.2.10';
|
||||
|
||||
/**
|
||||
* The URL to the bittorent client you want to communicate with
|
||||
* the port (default: 9091) can be set in you Transmission preferences
|
||||
* @var string
|
||||
*/
|
||||
public $url = '';
|
||||
|
||||
/**
|
||||
* If your Transmission RPC requires authentication, supply username here
|
||||
* @var string
|
||||
*/
|
||||
public $username = '';
|
||||
|
||||
/**
|
||||
* If your Transmission RPC requires authentication, supply password here
|
||||
* @var string
|
||||
*/
|
||||
public $password = '';
|
||||
|
||||
/**
|
||||
* Return results as an array, or an object (default)
|
||||
* @var bool
|
||||
*/
|
||||
public $return_as_array = false;
|
||||
|
||||
/**
|
||||
* Print debugging information, default is off
|
||||
* @var bool
|
||||
*/
|
||||
public $debug = false;
|
||||
|
||||
/**
|
||||
* Transmission RPC version
|
||||
* @var int
|
||||
*/
|
||||
protected $rpc_version = 0;
|
||||
|
||||
/**
|
||||
* Transmission uses a session id to prevent CSRF attacks
|
||||
* @var string
|
||||
*/
|
||||
protected $session_id = '';
|
||||
|
||||
/**
|
||||
* Default values for stream context
|
||||
* @var array
|
||||
*/
|
||||
private $default_context_opts = array('http' => array(
|
||||
'user_agent' => self::HTTP_UA,
|
||||
'timeout' => '60', // Don't want to be too slow
|
||||
'ignore_errors' => true, // Leave the error parsing/handling to the code
|
||||
)
|
||||
);
|
||||
|
||||
/**
|
||||
* Constants for torrent status
|
||||
*/
|
||||
const TR_STATUS_STOPPED = 0;
|
||||
const TR_STATUS_CHECK_WAIT = 1;
|
||||
const TR_STATUS_CHECK = 2;
|
||||
const TR_STATUS_DOWNLOAD_WAIT = 3;
|
||||
const TR_STATUS_DOWNLOAD = 4;
|
||||
const TR_STATUS_SEED_WAIT = 5;
|
||||
const TR_STATUS_SEED = 6;
|
||||
|
||||
const RPC_LT_14_TR_STATUS_CHECK_WAIT = 1;
|
||||
const RPC_LT_14_TR_STATUS_CHECK = 2;
|
||||
const RPC_LT_14_TR_STATUS_DOWNLOAD = 4;
|
||||
const RPC_LT_14_TR_STATUS_SEED = 8;
|
||||
const RPC_LT_14_TR_STATUS_STOPPED = 16;
|
||||
|
||||
/**
|
||||
* Takes the connection parameters
|
||||
*
|
||||
* TODO: Sanitize username, password, and URL
|
||||
*
|
||||
* @param string $url
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
*/
|
||||
public function __construct($url = 'http://localhost:9091/transmission/rpc', $username = null, $password = null, $return_as_array = false)
|
||||
{
|
||||
// server URL
|
||||
$this->url = $url;
|
||||
|
||||
// Username & password
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
|
||||
// Get the Transmission RPC_version
|
||||
$this->rpc_version = self::sget()->arguments->rpc_version;
|
||||
|
||||
// Return As Array
|
||||
$this->return_as_array = $return_as_array;
|
||||
|
||||
// Reset X-Transmission-Session-Id so we (re)fetch one
|
||||
$this->session_id = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start one or more torrents
|
||||
*
|
||||
* @param int|array ids A list of transmission torrent ids
|
||||
* @return mixed
|
||||
* @throws TransmissionRPCException
|
||||
*/
|
||||
public function start($ids)
|
||||
{
|
||||
if (!is_array($ids)) {
|
||||
$ids = array($ids);
|
||||
} // Convert $ids to an array if only a single id was passed
|
||||
$request = array("ids" => $ids);
|
||||
return $this->request("torrent-start", $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop one or more torrents
|
||||
*
|
||||
* @param int|array ids A list of transmission torrent ids
|
||||
* @return mixed
|
||||
* @throws TransmissionRPCException
|
||||
*/
|
||||
public function stop($ids)
|
||||
{
|
||||
if (!is_array($ids)) {
|
||||
$ids = array($ids);
|
||||
} // Convert $ids to an array if only a single id was passed
|
||||
$request = array("ids" => $ids);
|
||||
return $this->request("torrent-stop", $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reannounce one or more torrents
|
||||
*
|
||||
* @param int|array ids A list of transmission torrent ids
|
||||
* @return mixed
|
||||
* @throws TransmissionRPCException
|
||||
*/
|
||||
public function reannounce($ids)
|
||||
{
|
||||
if (!is_array($ids)) {
|
||||
$ids = array($ids);
|
||||
} // Convert $ids to an array if only a single id was passed
|
||||
$request = array("ids" => $ids);
|
||||
return $this->request("torrent-reannounce", $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify one or more torrents
|
||||
*
|
||||
* @param int|array ids A list of transmission torrent ids
|
||||
* @return mixed
|
||||
* @throws TransmissionRPCException
|
||||
*/
|
||||
public function verify($ids)
|
||||
{
|
||||
if (!is_array($ids)) {
|
||||
$ids = array($ids);
|
||||
} // Convert $ids to an array if only a single id was passed
|
||||
$request = array("ids" => $ids);
|
||||
return $this->request("torrent-verify", $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get information on torrents in transmission, if the ids parameter is
|
||||
* empty all torrents will be returned. The fields array can be used to return certain
|
||||
* fields. Default fields are: "id", "name", "status", "doneDate", "haveValid", "totalSize".
|
||||
* See https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt for available fields
|
||||
*
|
||||
* @param array fields An array of return fields
|
||||
* @param int|array ids A list of transmission torrent ids
|
||||
*
|
||||
* Request:
|
||||
* {
|
||||
* "arguments": {
|
||||
* "fields": [ "id", "name", "totalSize" ],
|
||||
* "ids": [ 7, 10 ]
|
||||
* },
|
||||
* "method": "torrent-get",
|
||||
* "tag": 39693
|
||||
* }
|
||||
*
|
||||
* Response:
|
||||
* {
|
||||
* "arguments": {
|
||||
* "torrents": [
|
||||
* {
|
||||
* "id": 10,
|
||||
* "name": "Fedora x86_64 DVD",
|
||||
* "totalSize": 34983493932,
|
||||
* },
|
||||
* {
|
||||
* "id": 7,
|
||||
* "name": "Ubuntu x86_64 DVD",
|
||||
* "totalSize", 9923890123,
|
||||
* }
|
||||
* ]
|
||||
* },
|
||||
* "result": "success",
|
||||
* "tag": 39693
|
||||
* }
|
||||
* @return mixed
|
||||
* @throws TransmissionRPCException
|
||||
*/
|
||||
public function get($ids = array(), $fields = array())
|
||||
{
|
||||
if (!is_array($ids)) {
|
||||
$ids = array($ids);
|
||||
} // Convert $ids to an array if only a single id was passed
|
||||
if (count($fields) == 0) {
|
||||
$fields = array("id", "name", "status", "doneDate", "haveValid", "totalSize");
|
||||
} // Defaults
|
||||
$request = array(
|
||||
"fields" => $fields,
|
||||
"ids" => $ids
|
||||
);
|
||||
return $this->request("torrent-get", $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set properties on one or more torrents, available fields are:
|
||||
* "bandwidthPriority" | number this torrent's bandwidth tr_priority_t
|
||||
* "downloadLimit" | number maximum download speed (in K/s)
|
||||
* "downloadLimited" | boolean true if "downloadLimit" is honored
|
||||
* "files-wanted" | array indices of file(s) to download
|
||||
* "files-unwanted" | array indices of file(s) to not download
|
||||
* "honorsSessionLimits" | boolean true if session upload limits are honored
|
||||
* "ids" | array torrent list, as described in 3.1
|
||||
* "location" | string new location of the torrent's content
|
||||
* "peer-limit" | number maximum number of peers
|
||||
* "priority-high" | array indices of high-priority file(s)
|
||||
* "priority-low" | array indices of low-priority file(s)
|
||||
* "priority-normal" | array indices of normal-priority file(s)
|
||||
* "seedRatioLimit" | double session seeding ratio
|
||||
* "seedRatioMode" | number which ratio to use. See tr_ratiolimit
|
||||
* "uploadLimit" | number maximum upload speed (in K/s)
|
||||
* "uploadLimited" | boolean true if "uploadLimit" is honored
|
||||
* See https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt for more information
|
||||
*
|
||||
* @param array arguments An associative array of arguments to set
|
||||
* @param int|array ids A list of transmission torrent ids
|
||||
* @return mixed
|
||||
* @throws TransmissionRPCException
|
||||
*/
|
||||
public function set($ids = array(), $arguments = array())
|
||||
{
|
||||
// See https://github.com/transmission/transmission/blob/2.9x/extras/rpc-spec.txt for available fields
|
||||
if (!is_array($ids)) {
|
||||
$ids = array($ids);
|
||||
} // Convert $ids to an array if only a single id was passed
|
||||
if (!isset($arguments['ids'])) {
|
||||
$arguments['ids'] = $ids;
|
||||
} // Any $ids given in $arguments overrides the method parameter
|
||||
return $this->request("torrent-set", $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new torrent
|
||||
*
|
||||
* Available extra options:
|
||||
* key | value type & description
|
||||
* ---------------------+-------------------------------------------------
|
||||
* "download-dir" | string path to download the torrent to
|
||||
* "filename" | string filename or URL of the .torrent file
|
||||
* "metainfo" | string base64-encoded .torrent content
|
||||
* "paused" | boolean if true, don't start the torrent
|
||||
* "peer-limit" | number maximum number of peers
|
||||
* "bandwidthPriority" | number torrent's bandwidth tr_priority_t
|
||||
* "files-wanted" | array indices of file(s) to download
|
||||
* "files-unwanted" | array indices of file(s) to not download
|
||||
* "priority-high" | array indices of high-priority file(s)
|
||||
* "priority-low" | array indices of low-priority file(s)
|
||||
* "priority-normal" | array indices of normal-priority file(s)
|
||||
*
|
||||
* Either "filename" OR "metainfo" MUST be included.
|
||||
* All other arguments are optional.
|
||||
*
|
||||
* @param string $torrent_location The URL or path to the torrent file
|
||||
* @param string $save_path Folder to save torrent in
|
||||
* @param array $extra_options Optional extra torrent options
|
||||
* @return mixed
|
||||
* @throws TransmissionRPCException
|
||||
*/
|
||||
public function add_file($torrent_location, $save_path = '', $extra_options = array())
|
||||
{
|
||||
if (!empty($save_path)) {
|
||||
$extra_options['download-dir'] = $save_path;
|
||||
}
|
||||
$extra_options['filename'] = $torrent_location;
|
||||
|
||||
return $this->request("torrent-add", $extra_options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a torrent using the raw torrent data
|
||||
*
|
||||
* @param string $torrent_metainfo The raw, unencoded contents (metainfo) of a torrent
|
||||
* @param string $save_path Folder to save torrent in
|
||||
* @param array $extra_options Optional extra torrent options
|
||||
* @return mixed
|
||||
* @throws TransmissionRPCException
|
||||
*/
|
||||
public function add_metainfo($torrent_metainfo, $save_path = '', $extra_options = array())
|
||||
{
|
||||
$extra_options['download-dir'] = $save_path;
|
||||
$extra_options['metainfo'] = base64_encode($torrent_metainfo);
|
||||
|
||||
return $this->request("torrent-add", $extra_options);
|
||||
}
|
||||
|
||||
/* Add a new torrent using a file path or a URL (For backwards compatibility)
|
||||
* @param torrent_location The URL or path to the torrent file
|
||||
* @param save_path Folder to save torrent in
|
||||
* @param extra options Optional extra torrent options
|
||||
*/
|
||||
public function add($torrent_location, $save_path = '', $extra_options = array())
|
||||
{
|
||||
return $this->add_file($torrent_location, $save_path, $extra_options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove torrent from transmission
|
||||
*
|
||||
* @param bool delete_local_data Also remove local data?
|
||||
* @param int|array ids A list of transmission torrent ids
|
||||
* @return mixed
|
||||
* @throws TransmissionRPCException
|
||||
*/
|
||||
public function remove($ids, $delete_local_data = false)
|
||||
{
|
||||
if (!is_array($ids)) {
|
||||
$ids = array($ids);
|
||||
} // Convert $ids to an array if only a single id was passed
|
||||
$request = array(
|
||||
"ids" => $ids,
|
||||
"delete-local-data" => $delete_local_data
|
||||
);
|
||||
return $this->request("torrent-remove", $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move local storage location
|
||||
*
|
||||
* @param int|array ids A list of transmission torrent ids
|
||||
* @param string target_location The new storage location
|
||||
* @param string move_existing_data Move existing data or scan new location for available data
|
||||
* @return mixed
|
||||
* @throws TransmissionRPCException
|
||||
*/
|
||||
public function move($ids, $target_location, $move_existing_data = true)
|
||||
{
|
||||
if (!is_array($ids)) {
|
||||
$ids = array($ids);
|
||||
} // Convert $ids to an array if only a single id was passed
|
||||
$request = array(
|
||||
"ids" => $ids,
|
||||
"location" => $target_location,
|
||||
"move" => $move_existing_data
|
||||
);
|
||||
return $this->request("torrent-set-location", $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* 3.7. Renaming a Torrent's Path
|
||||
*
|
||||
* Method name: "torrent-rename-path"
|
||||
*
|
||||
* For more information on the use of this function, see the transmission.h
|
||||
* documentation of tr_torrentRenamePath(). In particular, note that if this
|
||||
* call succeeds you'll want to update the torrent's "files" and "name" field
|
||||
* with torrent-get.
|
||||
*
|
||||
* Request arguments:
|
||||
*
|
||||
* string | value type & description
|
||||
* ---------------------------------+-------------------------------------------------
|
||||
* "ids" | array the torrent torrent list, as described in 3.1
|
||||
* | (must only be 1 torrent)
|
||||
* "path" | string the path to the file or folder that will be renamed
|
||||
* "name" | string the file or folder's new name
|
||||
* Response arguments: "path", "name", and "id", holding the torrent ID integer
|
||||
*
|
||||
* @param int|array ids A 1-element list of transmission torrent ids
|
||||
* @param string path The path to the file or folder that will be renamed
|
||||
* @param string name The file or folder's new name
|
||||
* @return mixed
|
||||
* @throws TransmissionRPCException
|
||||
*/
|
||||
public function rename($ids, $path, $name)
|
||||
{
|
||||
if (!is_array($ids)) {
|
||||
$ids = array($ids);
|
||||
} // Convert $id to an array if only a single id was passed
|
||||
if (count($ids) !== 1) {
|
||||
throw new TransmissionRPCException('A single id is accepted', TransmissionRPCException::E_INVALIDARG);
|
||||
}
|
||||
|
||||
$request = array(
|
||||
"ids" => $ids,
|
||||
"path" => $path,
|
||||
"name" => $name
|
||||
);
|
||||
return $this->request("torrent-rename-path", $request);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve session statistics
|
||||
*
|
||||
* @returns array of statistics
|
||||
*/
|
||||
public function sstats()
|
||||
{
|
||||
return $this->request("session-stats", array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all session variables
|
||||
*
|
||||
* @returns array of session information
|
||||
*/
|
||||
public function sget()
|
||||
{
|
||||
return $this->request("session-get", array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set session variable(s)
|
||||
*
|
||||
* @param array of session variables to set
|
||||
* @return mixed
|
||||
* @throws TransmissionRPCException
|
||||
*/
|
||||
public function sset($arguments)
|
||||
{
|
||||
return $this->request("session-set", $arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the interpretation of the torrent status
|
||||
*
|
||||
* @param int The integer "torrent status"
|
||||
* @returns string The translated meaning
|
||||
* @return string
|
||||
*/
|
||||
public function getStatusString($intstatus)
|
||||
{
|
||||
if ($this->rpc_version < 14) {
|
||||
if ($intstatus == self::RPC_LT_14_TR_STATUS_CHECK_WAIT) {
|
||||
return "Waiting to verify local files";
|
||||
}
|
||||
if ($intstatus == self::RPC_LT_14_TR_STATUS_CHECK) {
|
||||
return "Verifying local files";
|
||||
}
|
||||
if ($intstatus == self::RPC_LT_14_TR_STATUS_DOWNLOAD) {
|
||||
return "Downloading";
|
||||
}
|
||||
if ($intstatus == self::RPC_LT_14_TR_STATUS_SEED) {
|
||||
return "Seeding";
|
||||
}
|
||||
if ($intstatus == self::RPC_LT_14_TR_STATUS_STOPPED) {
|
||||
return "Stopped";
|
||||
}
|
||||
} else {
|
||||
if ($intstatus == self::TR_STATUS_CHECK_WAIT) {
|
||||
return "Waiting to verify local files";
|
||||
}
|
||||
if ($intstatus == self::TR_STATUS_CHECK) {
|
||||
return "Verifying local files";
|
||||
}
|
||||
if ($intstatus == self::TR_STATUS_DOWNLOAD) {
|
||||
return "Downloading";
|
||||
}
|
||||
if ($intstatus == self::TR_STATUS_SEED) {
|
||||
return "Seeding";
|
||||
}
|
||||
if ($intstatus == self::TR_STATUS_STOPPED) {
|
||||
return "Stopped";
|
||||
}
|
||||
if ($intstatus == self::TR_STATUS_SEED_WAIT) {
|
||||
return "Queued for seeding";
|
||||
}
|
||||
if ($intstatus == self::TR_STATUS_DOWNLOAD_WAIT) {
|
||||
return "Queued for download";
|
||||
}
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Here be dragons (Internal methods)
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Clean up the request array. Removes any empty fields from the request
|
||||
*
|
||||
* @param array array The request associative array to clean
|
||||
* @returns array The cleaned array
|
||||
* @return array|null
|
||||
*/
|
||||
protected function cleanRequestData($array)
|
||||
{
|
||||
if (!is_array($array) || count($array) == 0) {
|
||||
return null;
|
||||
} // Nothing to clean
|
||||
setlocale(LC_NUMERIC, 'en_US.utf8'); // Override the locale - if the system locale is wrong, then 12.34 will encode as 12,34 which is invalid JSON
|
||||
foreach ($array as $index => $value) {
|
||||
if (is_object($value)) {
|
||||
$array[$index] = $value->toArray();
|
||||
} // Convert objects to arrays so they can be JSON encoded
|
||||
if (is_array($value)) {
|
||||
$array[$index] = $this->cleanRequestData($value);
|
||||
} // Recursion
|
||||
if (empty($value) && $value !== 0) { // Remove empty members
|
||||
unset($array[$index]);
|
||||
continue; // Skip the rest of the tests - they may re-add the element.
|
||||
}
|
||||
if (is_numeric($value)) {
|
||||
$array[$index] = $value + 0;
|
||||
} // Force type-casting for proper JSON encoding (+0 is a cheap way to maintain int/float/etc)
|
||||
if (is_bool($value)) {
|
||||
$array[$index] = ($value ? 1 : 0);
|
||||
} // Store boolean values as 0 or 1
|
||||
if (is_string($value)) {
|
||||
if (mb_detect_encoding($value, "auto") !== 'UTF-8') {
|
||||
$array[$index] = mb_convert_encoding($value, "UTF-8");
|
||||
//utf8_encode( $value ); // Make sure all data is UTF-8 encoded for Transmission
|
||||
}
|
||||
}
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up the result object. Replaces all minus(-) characters in the object properties with underscores
|
||||
* and converts any object with any all-digit property names to an array.
|
||||
*
|
||||
* @param object The request result to clean
|
||||
* @returns array The cleaned object
|
||||
* @return array|object
|
||||
*/
|
||||
protected function cleanResultObject($object)
|
||||
{
|
||||
// Prepare and cast object to array
|
||||
$return_as_array = false;
|
||||
$array = $object;
|
||||
if (!is_array($array)) {
|
||||
$array = (array)$array;
|
||||
}
|
||||
foreach ($array as $index => $value) {
|
||||
if (is_array($array[$index]) || is_object($array[$index])) {
|
||||
$array[$index] = $this->cleanResultObject($array[$index]); // Recursion
|
||||
}
|
||||
if (strstr($index, '-')) {
|
||||
$valid_index = str_replace('-', '_', $index);
|
||||
$array[$valid_index] = $array[$index];
|
||||
unset($array[$index]);
|
||||
$index = $valid_index;
|
||||
}
|
||||
// Might be an array, check index for digits, if so, an array should be returned
|
||||
if (ctype_digit((string)$index)) {
|
||||
$return_as_array = true;
|
||||
}
|
||||
if (empty($value)) {
|
||||
unset($array[$index]);
|
||||
}
|
||||
}
|
||||
// Return array cast to object
|
||||
return $return_as_array ? $array : (object)$array;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行 rpc 请求
|
||||
*
|
||||
* @param string $method 请求类型/方法, 详见 $this->allowMethods
|
||||
* @param array $arguments 附加参数, 可选
|
||||
* @return mixed
|
||||
* @throws TransmissionRPCException
|
||||
*/
|
||||
protected function request($method, $arguments = array())
|
||||
{
|
||||
// Check the parameters
|
||||
if (!is_scalar($method)) {
|
||||
throw new TransmissionRPCException('Method name has no scalar value', TransmissionRPCException::E_INVALIDARG);
|
||||
}
|
||||
if (!is_array($arguments)) {
|
||||
throw new TransmissionRPCException('Arguments must be given as array', TransmissionRPCException::E_INVALIDARG);
|
||||
}
|
||||
|
||||
$arguments = $this->cleanRequestData($arguments); // Sanitize input
|
||||
|
||||
// Grab the X-Transmission-Session-Id if we don't have it already
|
||||
if (!$this->session_id) {
|
||||
if (!$this->GetSessionID()) {
|
||||
throw new TransmissionRPCException('Unable to acquire X-Transmission-Session-Id', TransmissionRPCException::E_SESSIONID);
|
||||
}
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'method' => $method,
|
||||
'arguments' => $arguments
|
||||
);
|
||||
|
||||
$header = array(
|
||||
'Content-Type: application/json',
|
||||
'Authorization: Basic ' . base64_encode(sprintf("%s:%s", $this->username, $this->password)),
|
||||
'X-Transmission-Session-Id: ' . $this->session_id
|
||||
);
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $this->url);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
|
||||
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
|
||||
curl_setopt($ch, CURLOPT_USERPWD, $this->username . ':' . $this->password);
|
||||
curl_setopt($ch, CURLOPT_HEADER, false);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 600);
|
||||
$content = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if (!$content) {
|
||||
$content = json_encode(array('result' => 'failed'));
|
||||
}
|
||||
return $this->return_as_array ? json_decode($content, true) : $this->cleanResultObject(json_decode($content)); // Return the sanitized result
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an empty GET on the Transmission RPC to get the X-Transmission-Session-Id
|
||||
* and store it in $this->session_id
|
||||
*
|
||||
* @return string
|
||||
* @throws TransmissionRPCException
|
||||
*/
|
||||
public function GetSessionID()
|
||||
{
|
||||
if (!$this->url) {
|
||||
throw new TransmissionRPCException("Class must be initialized before GetSessionID() can be called.", TransmissionRPCException::E_INVALIDARG);
|
||||
}
|
||||
|
||||
// Setup the context
|
||||
$contextopts = $this->default_context_opts; // Start with the defaults
|
||||
|
||||
// Make sure it's blank/empty (reset)
|
||||
$this->session_id = null;
|
||||
|
||||
// Setup authentication (if provided)
|
||||
if ($this->username && $this->password) {
|
||||
$contextopts['http']['header'] = sprintf("Authorization: Basic %s\r\n", base64_encode($this->username . ':' . $this->password));
|
||||
}
|
||||
|
||||
if ($this->debug) {
|
||||
echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Stream context created with options:" .
|
||||
PHP_EOL . print_r($contextopts, true);
|
||||
}
|
||||
|
||||
$context = stream_context_create($contextopts); // Create the context for this request
|
||||
if (!$fp = @fopen($this->url, 'r', false, $context)) { // Open a filepointer to the data, and use fgets to get the result
|
||||
throw new TransmissionRPCException('Unable to connect to ' . $this->url, TransmissionRPCException::E_CONNECTION);
|
||||
}
|
||||
|
||||
// Check the response (headers etc)
|
||||
$stream_meta = stream_get_meta_data($fp);
|
||||
fclose($fp);
|
||||
if ($this->debug) {
|
||||
echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Stream meta info: " .
|
||||
PHP_EOL . print_r($stream_meta, true);
|
||||
}
|
||||
if ($stream_meta['timed_out']) {
|
||||
throw new TransmissionRPCException("Timed out connecting to {$this->url}", TransmissionRPCException::E_CONNECTION);
|
||||
}
|
||||
if (substr($stream_meta['wrapper_data'][0], 9, 3) == "401") {
|
||||
throw new TransmissionRPCException("Invalid username/password.", TransmissionRPCException::E_AUTHENTICATION);
|
||||
} elseif (substr($stream_meta['wrapper_data'][0], 9, 3) == "409") { // This is what we're hoping to find
|
||||
// Loop through the returned headers and extract the X-Transmission-Session-Id
|
||||
foreach ($stream_meta['wrapper_data'] as $header) {
|
||||
if (strpos($header, 'X-Transmission-Session-Id: ') === 0) {
|
||||
if ($this->debug) {
|
||||
echo "TRANSMISSIONRPC_DEBUG:: GetSessionID():: Session-Id header: " .
|
||||
PHP_EOL . print_r($header, true);
|
||||
}
|
||||
$this->session_id = trim(substr($header, 27));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$this->session_id) { // Didn't find a session_id
|
||||
throw new TransmissionRPCException("Unable to retrieve X-Transmission-Session-Id", TransmissionRPCException::E_SESSIONID);
|
||||
}
|
||||
} else {
|
||||
throw new TransmissionRPCException("Unexpected response from Transmission RPC: " . $stream_meta['wrapper_data'][0]);
|
||||
}
|
||||
return $this->session_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function status()
|
||||
{
|
||||
return $this->sstats();
|
||||
}
|
||||
}
|
||||
48
app/Client/Transmission/TransmissionRPCException.php
Normal file
48
app/Client/Transmission/TransmissionRPCException.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Rhilip
|
||||
* Date: 1/17/2020
|
||||
* Time: 2020
|
||||
*/
|
||||
|
||||
namespace IYUU\Client\Transmission;
|
||||
|
||||
/**
|
||||
* This is the type of exception the TransmissionRPC class will throw
|
||||
*/
|
||||
class TransmissionRPCException extends \Exception
|
||||
{
|
||||
/**
|
||||
* Exception: Invalid arguments
|
||||
*/
|
||||
const E_INVALIDARG = -1;
|
||||
|
||||
/**
|
||||
* Exception: Invalid Session-Id
|
||||
*/
|
||||
const E_SESSIONID = -2;
|
||||
|
||||
/**
|
||||
* Exception: Error while connecting
|
||||
*/
|
||||
const E_CONNECTION = -3;
|
||||
|
||||
/**
|
||||
* Exception: Error 401 returned, unauthorized
|
||||
*/
|
||||
const E_AUTHENTICATION = -4;
|
||||
|
||||
/**
|
||||
* Exception constructor
|
||||
*/
|
||||
public function __construct($message = null, $code = 0, \Exception $previous = null)
|
||||
{
|
||||
// PHP version 5.3.0 and above support Exception linking
|
||||
if (version_compare(PHP_VERSION, '5.3.0', '>=')) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
} else {
|
||||
parent::__construct($message, $code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
<?php
|
||||
namespace IYUU\Client\qBittorrent;
|
||||
|
||||
use Curl\Curl;
|
||||
use IYUU\Client\AbstractClientInterface;
|
||||
|
||||
/**
|
||||
* https://github.com/qbittorrent/qBittorrent/wiki/Web-API-Documentation
|
||||
*/
|
||||
class qBittorrent
|
||||
class qBittorrent implements AbstractClientInterface
|
||||
{
|
||||
private $debug;
|
||||
private $url;
|
||||
@@ -35,7 +39,7 @@ class qBittorrent
|
||||
'1' => null,
|
||||
'2' => '/api/v2/app/setPreferences'
|
||||
],
|
||||
'defaultSavePath' => [
|
||||
'defaultSavePath' => [
|
||||
'1' => null,
|
||||
'2' => '/api/v2/app/defaultSavePath'
|
||||
],
|
||||
@@ -72,7 +76,7 @@ class qBittorrent
|
||||
public function __construct($url='', $username='', $password='', $api_version = 2, $debug = false)
|
||||
{
|
||||
$this->debug = $debug;
|
||||
$this->url = rtrim($url,'/');
|
||||
$this->url = rtrim($url, '/');
|
||||
$this->username = $username;
|
||||
$this->password = $password;
|
||||
$this->api_version = $api_version;
|
||||
@@ -130,29 +134,33 @@ class qBittorrent
|
||||
*/
|
||||
public function add($torrent_url, $save_path = '', $extra_options = array())
|
||||
{
|
||||
if(!empty($save_path)) $extra_options['savepath'] = $save_path;
|
||||
if (!empty($save_path)) {
|
||||
$extra_options['savepath'] = $save_path;
|
||||
}
|
||||
$extra_options['urls'] = $torrent_url;
|
||||
#$extra_options['skip_checking'] = 'true'; //跳校验
|
||||
// 关键 上传文件流 multipart/form-data【严格按照api文档编写】
|
||||
$post_data = $this->buildUrls($extra_options);
|
||||
#p($post_data);
|
||||
// 设置请求头
|
||||
$this->curl->setHeader('Content-Type','multipart/form-data; boundary='.$this->delimiter);
|
||||
$this->curl->setHeader('Content-Length',strlen($post_data));
|
||||
$this->curl->setHeader('Content-Type', 'multipart/form-data; boundary='.$this->delimiter);
|
||||
$this->curl->setHeader('Content-Length', strlen($post_data));
|
||||
return $this->postData('torrent_add', $post_data);
|
||||
}
|
||||
|
||||
public function add_metainfo($torrent_metainfo, $save_path = '', $extra_options = array())
|
||||
public function add_metainfo($torrent_metainfo, $save_path = '', $extra_options = array())
|
||||
{
|
||||
if(!empty($save_path)) $extra_options['savepath'] = $save_path;
|
||||
if (!empty($save_path)) {
|
||||
$extra_options['savepath'] = $save_path;
|
||||
}
|
||||
$extra_options['torrents'] = $torrent_metainfo;
|
||||
#$extra_options['skip_checking'] = 'true'; //跳校验
|
||||
// 关键 上传文件流 multipart/form-data【严格按照api文档编写】
|
||||
$post_data = $this->buildData($extra_options);
|
||||
#p($post_data);
|
||||
// 设置请求头
|
||||
$this->curl->setHeader('Content-Type','multipart/form-data; boundary='.$this->delimiter);
|
||||
$this->curl->setHeader('Content-Length',strlen($post_data));
|
||||
$this->curl->setHeader('Content-Type', 'multipart/form-data; boundary='.$this->delimiter);
|
||||
$this->curl->setHeader('Content-Length', strlen($post_data));
|
||||
return $this->postData('torrent_add', $post_data);
|
||||
}
|
||||
|
||||
@@ -250,7 +258,8 @@ class qBittorrent
|
||||
* 拼接种子urls multipart/form-data
|
||||
* https://github.com/qbittorrent/qBittorrent/wiki/Web-API-Documentation#add-new-torrent
|
||||
*/
|
||||
private function buildUrls($param){
|
||||
private function buildUrls($param)
|
||||
{
|
||||
$this->delimiter = uniqid();
|
||||
$eol = "\r\n";
|
||||
$data = '';
|
||||
@@ -267,7 +276,8 @@ class qBittorrent
|
||||
* 拼接种子上传文件流 multipart/form-data
|
||||
* https://github.com/qbittorrent/qBittorrent/wiki/Web-API-Documentation#add-new-torrent
|
||||
*/
|
||||
private function buildData($param){
|
||||
private function buildData($param)
|
||||
{
|
||||
$this->delimiter = uniqid();
|
||||
$eol = "\r\n";
|
||||
$data = '';
|
||||
@@ -285,4 +295,12 @@ class qBittorrent
|
||||
$data .= "--" . $this->delimiter . "--" . $eol;
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function status()
|
||||
{
|
||||
return $this->appVersion();
|
||||
}
|
||||
}
|
||||
301
app/Library/IFile.php
Normal file
301
app/Library/IFile.php
Normal file
@@ -0,0 +1,301 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief 文件处理
|
||||
* @version 0.6
|
||||
*/
|
||||
|
||||
namespace IYUU\Library;
|
||||
|
||||
/**
|
||||
* @class IFile
|
||||
* @brief IFile 文件处理类
|
||||
*/
|
||||
class IFile
|
||||
{
|
||||
private $resource = null; //文件资源句柄
|
||||
|
||||
/**
|
||||
* @brief 构造函数,打开资源流,并独占锁定
|
||||
* @param String $fileName 文件路径名
|
||||
* @param String $mode 操作方式,默认为读操作,可供选择的项为:r,r+,w+,w+,a,a+
|
||||
* @note $mod,'r' 只读方式打开,将文件指针指向文件头
|
||||
* 'r+' 读写方式打开,将文件指针指向文件头
|
||||
* 'w' 写入方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
|
||||
* 'w+' 读写方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
|
||||
* 'a' 写入方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
|
||||
* 'a+' 读写方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
|
||||
*/
|
||||
public function __construct($fileName, $mode='r')
|
||||
{
|
||||
$dirName = dirname($fileName);
|
||||
$baseName = basename($fileName);
|
||||
|
||||
//检查并创建文件夹
|
||||
self::mkdir($dirName);
|
||||
|
||||
$this->resource = fopen($fileName, $mode.'b');
|
||||
if ($this->resource) {
|
||||
flock($this->resource, LOCK_EX);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取文件内容
|
||||
* @return String 文件内容
|
||||
*/
|
||||
public function read()
|
||||
{
|
||||
$content = null;
|
||||
while (!feof($this->resource)) {
|
||||
$content.= fread($this->resource, 1024);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 文件写入操作
|
||||
* @param String $content 要写入的文件内容
|
||||
* @return Int or false 写入的字符数; false:写入失败;
|
||||
*/
|
||||
public function write($content)
|
||||
{
|
||||
$worldsnum = fwrite($this->resource, $content);
|
||||
$this->save();
|
||||
return is_bool($worldsnum) ? false : $worldsnum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空目录下的所有文件
|
||||
* @return bool false:失败; true:成功;
|
||||
*/
|
||||
public static function clearDir($dir)
|
||||
{
|
||||
if ($dir[0] != '.' && is_dir($dir) && is_writable($dir)) {
|
||||
$dirRes = opendir($dir);
|
||||
while (false !== ($fileName = readdir($dirRes))) {
|
||||
if ($fileName[0] !== '.') {
|
||||
$fullpath = $dir.'/'.$fileName;
|
||||
if (is_file($fullpath)) {
|
||||
self::unlink($fullpath);
|
||||
} else {
|
||||
self::clearDir($fullpath);
|
||||
rmdir($fullpath);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($dirRes);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取文件信息
|
||||
* @param String $fileName 文件路径
|
||||
* @return array or null array:文件信息; null:文件不存在;
|
||||
*/
|
||||
public static function getInfo($fileName)
|
||||
{
|
||||
if (is_file($fileName)) {
|
||||
return stat($fileName);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建文件夹
|
||||
* @param String $path 路径
|
||||
* @param int $chmod 文件夹权限
|
||||
* @note $chmod 参数不能是字符串(加引号),否则linux会出现权限问题
|
||||
*/
|
||||
public static function mkdir($path, $chmod=0777)
|
||||
{
|
||||
return is_dir($path) or (self::mkdir(dirname($path), $chmod) and mkdir($path, $chmod));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 复制文件
|
||||
* @param String $from 源文件路径
|
||||
* @param String $to 目标文件路径
|
||||
* @param String $mod 操作模式,c:复制(默认); x:剪切(删除$from文件)
|
||||
* @return bool 操作结果 true:成功; false:失败;
|
||||
*/
|
||||
public static function copy($from, $to, $mode = 'c')
|
||||
{
|
||||
$dir = dirname($to);
|
||||
|
||||
//创建目录
|
||||
self::mkdir($dir);
|
||||
|
||||
copy($from, $to);
|
||||
|
||||
if (is_file($to)) {
|
||||
if ($mode == 'x') {
|
||||
self::unlink($from);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 删除文件
|
||||
* @param String $fileName 文件路径
|
||||
* @return bool 操作结果 false:删除失败;
|
||||
*/
|
||||
public static function unlink($fileName)
|
||||
{
|
||||
if (is_file($fileName) && is_writable($fileName)) {
|
||||
return unlink($fileName);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 删除$dir文件夹 或者 其下所有文件
|
||||
* @param String $dir 文件路径
|
||||
* @param bool $recursive 是否强制删除,如果强制删除则递归删除该目录下的全部文件,默认为false
|
||||
* @return bool true:删除成功; false:删除失败;
|
||||
*/
|
||||
public static function rmdir($dir, $recursive = false)
|
||||
{
|
||||
if (is_dir($dir) && is_writable($dir)) {
|
||||
//强制删除
|
||||
if ($recursive == true) {
|
||||
self::clearDir($dir);
|
||||
return self::rmdir($dir, false);
|
||||
}
|
||||
|
||||
//非强制删除
|
||||
else {
|
||||
if (rmdir($dir)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取文件类型
|
||||
* @param String $fileName 文件名
|
||||
* @return String $filetype 文件类型
|
||||
* @note 如果文件不存在,返回false,如果文件后缀名不在识别列表之内,返回NULL,对于docx及elsx格式文档识别在会出现识别为ZIP格式的错误,这是office2007的bug目前尚未修复,请谨慎使用
|
||||
*/
|
||||
public static function getFileType($fileName)
|
||||
{
|
||||
$filetype = null;
|
||||
if (!is_file($fileName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$fileRes = fopen($fileName, "rb");
|
||||
if (!$fileRes) {
|
||||
return false;
|
||||
}
|
||||
$bin= fread($fileRes, 2);
|
||||
fclose($fileRes);
|
||||
|
||||
if ($bin != null) {
|
||||
$strInfo = unpack("C2chars", $bin);
|
||||
$typeCode = intval($strInfo['chars1'].$strInfo['chars2']);
|
||||
$typelist = self::getTypeList();
|
||||
foreach ($typelist as $val) {
|
||||
if (strtolower($val[0]) == strtolower($typeCode)) {
|
||||
if ($val[0] == 8075) {
|
||||
return array('zip','docx','xlsx');
|
||||
} else {
|
||||
return $val[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $filetype;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取文件类型映射关系
|
||||
* @return array 文件类型映射关系数组
|
||||
*/
|
||||
public static function getTypeList()
|
||||
{
|
||||
return array(
|
||||
array('255216','jpg'),
|
||||
array('13780','png'),
|
||||
array('7173','gif'),
|
||||
array('6677','bmp'),
|
||||
array('6063','xml'),
|
||||
array('60104','html'),
|
||||
array('208207','xls/doc'),
|
||||
array('8075','zip'),
|
||||
array('8075','docx'),
|
||||
array('8075','xlsx'),
|
||||
array("8297","rar"),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取文件大小
|
||||
* @param String $fileName 文件名
|
||||
* @return Int 文件大小的字节数,如果文件无效则返回 NULL
|
||||
*/
|
||||
public static function getFileSize($fileName)
|
||||
{
|
||||
return is_file($fileName) ? filesize($fileName):null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检测文件夹是否为空
|
||||
* @param String $dir 路径地址
|
||||
* @return bool true:$dir为空目录; false:$dir为非空目录;
|
||||
*/
|
||||
public static function isEmptyDir($dir)
|
||||
{
|
||||
if (is_dir($dir)) {
|
||||
$isEmpty = true;
|
||||
$dirRes = opendir($dir);
|
||||
while (false !== ($fileName = readdir($dirRes))) {
|
||||
if ($fileName!='.' && $fileName!='..') {
|
||||
$isEmpty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir($dirRes);
|
||||
return $isEmpty;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 释放文件锁定
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
flock($this->resource, LOCK_UN);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取文件扩展名
|
||||
* @param String $fileName 文件名
|
||||
* @return String 文件后缀名
|
||||
*/
|
||||
public static function getFileSuffix($fileName)
|
||||
{
|
||||
$fileInfoArray = pathinfo($fileName);
|
||||
return $fileInfoArray['extension'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 析构函数,释放文件连接句柄
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if (is_resource($this->resource)) {
|
||||
fclose($this->resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,12 @@
|
||||
/**
|
||||
* IYUU用户注册、认证
|
||||
*/
|
||||
namespace IYUU\Library;
|
||||
|
||||
use Curl\Curl;
|
||||
class Oauth{
|
||||
|
||||
class Oauth
|
||||
{
|
||||
// 合作的站点
|
||||
public static $sites = ['ourbits'];
|
||||
// 爱语飞飞token
|
||||
@@ -17,13 +21,14 @@ class Oauth{
|
||||
/**
|
||||
* 初始化配置
|
||||
*/
|
||||
public static function init(){
|
||||
public static function init()
|
||||
{
|
||||
global $configALL;
|
||||
foreach (self::$sites as $name) {
|
||||
if (isset($configALL[$name]['passkey']) && $configALL[$name]['passkey'] && isset($configALL[$name]['id']) && $configALL[$name]['id'] ) {
|
||||
if (isset($configALL[$name]['passkey']) && $configALL[$name]['passkey'] && isset($configALL[$name]['id']) && $configALL[$name]['id']) {
|
||||
self::$token = self::getSign();
|
||||
self::$user_id = $configALL[$name]['id'];
|
||||
self::$passkey = sha1( $configALL[$name]['passkey'] ); // 避免泄露用户passkey秘钥
|
||||
self::$passkey = sha1($configALL[$name]['passkey']); // 避免泄露用户passkey秘钥
|
||||
self::$site = $name;
|
||||
return true;
|
||||
}
|
||||
@@ -35,7 +40,8 @@ class Oauth{
|
||||
/**
|
||||
* 从配置文件内读取爱语飞飞token作为鉴权参数
|
||||
*/
|
||||
public static function getSign(){
|
||||
public static function getSign()
|
||||
{
|
||||
global $configALL;
|
||||
// 爱语飞飞
|
||||
$token = isset($configALL['iyuu.cn']) && $configALL['iyuu.cn'] ? $configALL['iyuu.cn'] : '';
|
||||
@@ -51,9 +57,10 @@ class Oauth{
|
||||
* 作用:在服务器端实现微信用户与合作站点用户id的关联
|
||||
* 参数:爱语飞飞token + 合作站点用户id + sha1(合作站点密钥passkey) + 合作站点标识
|
||||
*/
|
||||
public static function login($apiUrl = ''){
|
||||
public static function login($apiUrl = '')
|
||||
{
|
||||
$is_oauth = self::init();
|
||||
if ( $is_oauth ) {
|
||||
if ($is_oauth) {
|
||||
$curl = new Curl();
|
||||
$curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
|
||||
$data = [
|
||||
@@ -66,6 +73,6 @@ class Oauth{
|
||||
p($res->response);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,362 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* 技术讨论及后续更新,请加入QQ群!!!!!!!
|
||||
群名称:IYUU自动辅种交流
|
||||
QQ群号:859882209
|
||||
* 手动配置方法,请查看:https://www.iyuu.cn/archives/324/
|
||||
*/
|
||||
return array(
|
||||
// 1.【必须配置】爱语飞飞 微信通知,请访问https://iyuu.cn 用微信扫码申请
|
||||
'iyuu.cn' => 'IYUU',
|
||||
// 2.server酱 微信通知配置
|
||||
'sc.ftqq.com' => '',
|
||||
// 3.发布员鉴权
|
||||
'secret' => '',
|
||||
// 4.全局默认配置
|
||||
'default' => array(
|
||||
// 5.【必须配置】浏览器UA,打开http://demo.iyuu.cn 复制过来即可
|
||||
'userAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36',
|
||||
// 6.【自动辅种必须配置】全局客户端设置(条目不够可以复制)
|
||||
'clients' => array(
|
||||
// 全局客户端设置 开始
|
||||
# 开始
|
||||
array(
|
||||
'type' => 'transmission', // 支持:transmission、qBittorrent
|
||||
'host' => 'http://127.0.0.1:9091/transmission/rpc', // 警告!注意:transmission/rpc这段别动,你只需要修改 127.0.0.1:9091
|
||||
'username' => '',
|
||||
'password' => '',
|
||||
),
|
||||
# 结束
|
||||
# 开始
|
||||
array(
|
||||
'type' => 'qBittorrent', // 支持:transmission、qBittorrent
|
||||
'host' => 'http://www.baidu.com:8083',
|
||||
'username' => '',
|
||||
'password' => '',
|
||||
),
|
||||
# 结束
|
||||
// 全局客户端设置 结束
|
||||
),
|
||||
'move' =>array(
|
||||
'type' => 2, // 0保持不变,1减,2加, 3直接替换
|
||||
'path' =>array(
|
||||
'/sda1' => '/volume1',
|
||||
),
|
||||
),
|
||||
'CONNECTTIMEOUT'=> 60,
|
||||
'TIMEOUT' => 600,
|
||||
),
|
||||
/**
|
||||
* 以下为各站点的独立配置(互不影响、互不冲突)
|
||||
* 自动辅种:需要配置各站的passkey(没有配置passkey的站点会自动跳过)
|
||||
*/
|
||||
// m-team 序号:1
|
||||
'm-team' => array(
|
||||
// 14.m-team的cookie 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => 'tp=',
|
||||
// 15.m-team的passkey 【必须配置】
|
||||
'passkey' => '',
|
||||
// 种子Tracker的IP地址选择 可选:ipv4,ipv6
|
||||
'ip_type' => 'ipv4',
|
||||
),
|
||||
// keepfrds 序号:2
|
||||
'keepfrds' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// ourbits 序号:3
|
||||
'ourbits' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
'id' => 0, // 用户ID
|
||||
'is_vip' => 0, // 是否具有VIP或特殊权限?0 普通,1 VIP
|
||||
),
|
||||
// HDSky 序号:4
|
||||
'hdsky' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// pter 序号:5
|
||||
'pter' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// tjupt 序号:6
|
||||
'tjupt' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hdhome 序号:7
|
||||
'hdhome' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// btschool 序号:8
|
||||
'btschool' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// PTHome 序号:9
|
||||
'pthome' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hddolby 序号:10
|
||||
'hddolby' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// TorrentCCF 序号:11
|
||||
'torrentccf' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// PTMSG 序号:12
|
||||
'ptmsg' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// MoeCat 序号:13
|
||||
'moecat' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
// 种子Tracker的IP地址选择 可选:ipv4,ipv6
|
||||
'ip_type' => 'ipv4',
|
||||
),
|
||||
// totheglory 序号:14
|
||||
'ttg' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// nanyangpt 序号:15
|
||||
'nanyangpt' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// springsunday.net 序号:16
|
||||
'ssd' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// yingk 序号:17
|
||||
'yingk' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hdcity 序号:18
|
||||
'hdcity' => array(
|
||||
// 必须配置
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置cuhash
|
||||
'passkey' => '',
|
||||
),
|
||||
// 52pt.site 序号:19
|
||||
'52pt' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// brobits.cc 序号:20
|
||||
'brobits' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// www.beitai.pt 序号:21
|
||||
'beitai' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// pt.eastgame.org 序号:22
|
||||
'eastgame' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// pt.soulvoice.club 序号:23
|
||||
'soulvoice' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// chdbits 序号:24
|
||||
'chdbits' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// leaguehd 序号:25
|
||||
'leaguehd' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// ptsbao.club 序号:26
|
||||
'ptsbao' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hdchina 序号:27
|
||||
'hdchina' => array(
|
||||
// 必须配置
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hdarea 序号:28
|
||||
'hdarea' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hdtime 序号:29
|
||||
'hdtime' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// 1ptba 序号:30
|
||||
'1ptba' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hd4fans 序号:31
|
||||
'hd4fans' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hdbug 序号:32
|
||||
'hdbug' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// opencd 序号:33 皇后
|
||||
'opencd' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hdstreet 序号:34
|
||||
'hdstreet' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// joyhd 序号:35
|
||||
'joyhd' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// dmhy 序号:36 幼儿园
|
||||
'dmhy' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// upxin 序号:37
|
||||
'upxin' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// oshen 序号:38
|
||||
'oshen' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// discfan 序号:39 港知堂
|
||||
'discfan' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hdzone 序号:40
|
||||
'hdzone' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// cnscg 序号:41 圣城
|
||||
'cnscg' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// nicept 序号:42 老师
|
||||
'nicept' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hdbd 序号:43 伊甸园
|
||||
'hdbd' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
|
||||
// 配置文件结束
|
||||
);
|
||||
@@ -1 +0,0 @@
|
||||
<?php return '20191224.1010';
|
||||
319
app/helper.php
Normal file
319
app/helper.php
Normal file
@@ -0,0 +1,319 @@
|
||||
<?php
|
||||
/**
|
||||
* 调试函数
|
||||
* @param $data
|
||||
* @param bool $echo
|
||||
* @return string|null
|
||||
*/
|
||||
function p($data, $echo=true)
|
||||
{
|
||||
$str='******************************'."\n";
|
||||
// 如果是boolean或者null直接显示文字;否则print
|
||||
if (is_bool($data)) {
|
||||
$show_data=$data ? 'true' : 'false';
|
||||
} elseif (is_null($data)) {
|
||||
$show_data='null';
|
||||
} else {
|
||||
$show_data=print_r($data, true);
|
||||
}
|
||||
$str.=$show_data;
|
||||
$str.="\n".'******************************'."\n";
|
||||
if ($echo) {
|
||||
echo $str;
|
||||
return null;
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信推送Server酱
|
||||
* @param string $text
|
||||
* @param string $desp
|
||||
* @return false|string
|
||||
*/
|
||||
function sc($text='', $desp='')
|
||||
{
|
||||
global $configALL;
|
||||
$token = $configALL['sc.ftqq.com'];
|
||||
$desp = ($desp=='')?date("Y-m-d H:i:s") :$desp;
|
||||
$postdata = http_build_query(array(
|
||||
'text' => $text,
|
||||
'desp' => $desp
|
||||
));
|
||||
$opts = array('http' => array(
|
||||
'method' => 'POST',
|
||||
'header' => 'Content-type: application/x-www-form-urlencoded',
|
||||
'content' => $postdata
|
||||
));
|
||||
$context = stream_context_create($opts);
|
||||
$result = file_get_contents('http://sc.ftqq.com/'.$token.'.send', false, $context);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信推送 爱语飞飞
|
||||
* @param string $text
|
||||
* @param string $desp
|
||||
* @return false|string
|
||||
*/
|
||||
function ff($text='', $desp='')
|
||||
{
|
||||
global $configALL;
|
||||
$token = $configALL['iyuu.cn'];
|
||||
$desp = ($desp=='')?date("Y-m-d H:i:s") :$desp;
|
||||
$postdata = http_build_query(array(
|
||||
'text' => $text,
|
||||
'desp' => $desp
|
||||
));
|
||||
$opts = array('http' => array(
|
||||
'method' => 'POST',
|
||||
'header' => 'Content-type: application/x-www-form-urlencoded',
|
||||
'content' => $postdata
|
||||
));
|
||||
$context = stream_context_create($opts);
|
||||
$result = file_get_contents('http://iyuu.cn/'.$token.'.send', false, $context);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信推送 爱语飞飞
|
||||
* @param string $site
|
||||
* @param array $torrent 种子数组
|
||||
* Array
|
||||
* (
|
||||
* [id] => 118632
|
||||
* [h1] => CCTV5+ 2019 ATP Men's Tennis Final 20191115B HDTV 1080i H264-HDSTV
|
||||
* [title] => 央视体育赛事频道 2019年ATP男子网球年终总决赛 单打小组赛 纳达尔VS西西帕斯 20191115[优惠剩余时间:4时13分]
|
||||
* [details] => https://xxx.me/details.php?id=118632
|
||||
* [download] => https://xxx.me/download.php?id=118632
|
||||
* [filename] => 118632.torrent
|
||||
* [type] => 0
|
||||
* [sticky] => 1
|
||||
* [time] => Array
|
||||
* (
|
||||
* [0] => "2019-11-16 20:41:53">4时13分
|
||||
* [1] => "2019-11-16 14:41:53">1时<br />46分
|
||||
* )
|
||||
* [comments] => 0
|
||||
* [size] => 5232.64MB
|
||||
* [seeders] => 69
|
||||
* [leechers] => 10
|
||||
* [completed] => 93
|
||||
* [percentage] => 100%
|
||||
* [owner] => 匿名
|
||||
* )
|
||||
* @return false|string
|
||||
*/
|
||||
function send($site = '', $torrent = array())
|
||||
{
|
||||
$br = "\r\n";
|
||||
$text = $site. ' 免费:' .$torrent['filename']. ',添加成功';
|
||||
$desp = '主标题:'.$torrent['h1'] . $br;
|
||||
|
||||
if (isset($torrent['title'])) {
|
||||
$desp .= '副标题:'.$torrent['title']. $br;
|
||||
}
|
||||
if (isset($torrent['size'])) {
|
||||
$desp .= '大小:'.$torrent['size']. $br;
|
||||
}
|
||||
if (isset($torrent['seeders'])) {
|
||||
$desp .= '做种数:'.$torrent['seeders']. $br;
|
||||
}
|
||||
if (isset($torrent['leechers'])) {
|
||||
$desp .= '下载数:'.$torrent['leechers']. $br;
|
||||
}
|
||||
if (isset($torrent['owner'])) {
|
||||
$desp .= '发布者:'.$torrent['owner']. $br;
|
||||
}
|
||||
return ff($text, $desp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 下载种子
|
||||
* @param string $url 种子URL
|
||||
* @param string $cookies 模拟登陆的cookie
|
||||
* @param $useragent
|
||||
* @param string $method
|
||||
* @return mixed 返回的数据
|
||||
*/
|
||||
function download($url, $cookies, $useragent, $method = 'GET')
|
||||
{
|
||||
$header = array(
|
||||
"Content-Type:application/x-www-form-urlencoded",
|
||||
'User-Agent: '.$useragent);
|
||||
$ch = curl_init();
|
||||
if ($method === 'POST') {
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
}
|
||||
if (stripos($url, 'https://') !== false) {
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_SSLVERSION, 1);
|
||||
}
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
|
||||
curl_setopt($ch, CURLOPT_COOKIE, $cookies);
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 60);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 600);
|
||||
$data = curl_exec($ch);
|
||||
$status = curl_getinfo($ch);
|
||||
curl_close($ch);
|
||||
if (isset($status['http_code']) && $status['http_code'] == 200) {
|
||||
return $data;
|
||||
}
|
||||
if (isset($status['http_code']) && $status['http_code'] == 302) {
|
||||
return download($status['redirect_url'], $cookies, $useragent);
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 文件大小格式化为MB
|
||||
* @param string $from 文件大小
|
||||
* @return int 单位MB
|
||||
*/
|
||||
function convertToMB($from)
|
||||
{
|
||||
$number=substr($from, 0, -2);
|
||||
switch (strtoupper(substr($from, -2))) {
|
||||
case "KB":
|
||||
return $number/1024;
|
||||
case "MB":
|
||||
return $number;
|
||||
case "GB":
|
||||
return $number*pow(1024, 1);
|
||||
case "TB":
|
||||
return $number*pow(1024, 2);
|
||||
case "PB":
|
||||
return $number*pow(1024, 3);
|
||||
default:
|
||||
return $from;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 种子过滤器
|
||||
* @param string $site 站点标识
|
||||
* @param array $torrent 种子数组
|
||||
* Array
|
||||
(
|
||||
[id] => 118632
|
||||
[h1] => CCTV5+ 2019 ATP Men's Tennis Final 20191115B HDTV 1080i H264-HDSTV
|
||||
[title] => 央视体育赛事频道 2019年ATP男子网球年终总决赛 单打小组赛 纳达尔VS西西帕斯 20191115[优惠剩余时间:4时13分]
|
||||
[details] => https://xxx.me/details.php?id=118632
|
||||
[download] => https://xxx.me/download.php?id=118632
|
||||
[filename] => 118632.torrent
|
||||
[type] => 0
|
||||
[sticky] => 1
|
||||
[time] => Array
|
||||
(
|
||||
[0] => "2019-11-16 20:41:53">4时13分
|
||||
[1] => "2019-11-16 14:41:53">1时<br />46分
|
||||
)
|
||||
[comments] => 0
|
||||
[size] => 5232.64MB
|
||||
[seeders] => 69
|
||||
[leechers] => 10
|
||||
[completed] => 93
|
||||
[percentage] => 100%
|
||||
[owner] => 匿名
|
||||
)
|
||||
* @return bool 或 string false不过滤
|
||||
*/
|
||||
function filter($site = '', $torrent = array())
|
||||
{
|
||||
global $configALL;
|
||||
$config = $configALL[$site];
|
||||
$filter = array();
|
||||
// 读取配置
|
||||
if (isset($configALL['default']['filter']) || isset($config['filter'])) {
|
||||
$filter = isset($config['filter']) && $config['filter'] ? $config['filter'] : $configALL['default']['filter'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
$filename = $torrent['filename'];
|
||||
|
||||
// 兼容性
|
||||
if (empty($torrent['size'])) {
|
||||
return false;
|
||||
}
|
||||
// 大小过滤
|
||||
$size = convertToMB($torrent['size']);
|
||||
$min = isset($filter['size']['min']) ? convertToMB($filter['size']['min']) : 0;
|
||||
$max = isset($filter['size']['max']) ? convertToMB($filter['size']['max']) : 2097152; //默认 2097152MB = 2TB
|
||||
if ($min > $size || $size > $max) {
|
||||
return $filename. ' ' .$size. 'MB,被大小过滤';
|
||||
}
|
||||
|
||||
// 兼容性
|
||||
if (empty($torrent['seeders'])) {
|
||||
return false;
|
||||
}
|
||||
// 种子数过滤
|
||||
$seeders = $torrent['seeders'];
|
||||
$min = isset($filter['seeders']['min']) ? $filter['seeders']['min'] : 1; //默认 1
|
||||
$max = isset($filter['seeders']['max']) ? $filter['seeders']['max'] : 3; //默认 3
|
||||
if ($min > $seeders || $seeders > $max) {
|
||||
return $filename. ' 当前做种' .$seeders. '人,被过滤';
|
||||
}
|
||||
|
||||
// 兼容性
|
||||
if (empty($torrent['leechers'])) {
|
||||
return false;
|
||||
}
|
||||
// 下载数过滤
|
||||
$leechers = $torrent['leechers'];
|
||||
$min = isset($filter['leechers']['min']) ? $filter['leechers']['min'] : 0; //默认
|
||||
$max = isset($filter['leechers']['max']) ? $filter['leechers']['max'] : 30000; //默认
|
||||
if ($min > $leechers || $leechers > $max) {
|
||||
return $filename. ' 当前下载' .$leechers. '人,被过滤';
|
||||
}
|
||||
|
||||
// 兼容性
|
||||
if (empty($torrent['completed'])) {
|
||||
return false;
|
||||
}
|
||||
// 完成数过滤
|
||||
$completed = $torrent['completed'];
|
||||
$min = isset($filter['completed']['min']) ? $filter['completed']['min'] : 0; //默认
|
||||
$max = isset($filter['completed']['max']) ? $filter['completed']['max'] : 30000; //默认
|
||||
if ($min > $completed || $completed > $max) {
|
||||
return $filename. ' 已完成数' .$completed. '人,被过滤';
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* transmission过滤函数,只保留正常做种
|
||||
*/
|
||||
function filterStatus($v)
|
||||
{
|
||||
return isset($v['status']) && $v['status']===6;
|
||||
}
|
||||
|
||||
/**
|
||||
* qBittorrent过滤函数,只保留正常做种
|
||||
*/
|
||||
function qbfilterStatus($v)
|
||||
{
|
||||
if (isset($v['state']) && in_array($v['state'], array('uploading','stalledUP','pausedUP','queuedUP','checkingUP','forcedUP'))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//PHP stdClass Object转array
|
||||
function object_array($array)
|
||||
{
|
||||
if (is_object($array)) {
|
||||
$array = (array)$array;
|
||||
}
|
||||
if (is_array($array)) {
|
||||
foreach ($array as $key=>$value) {
|
||||
$array[$key] = object_array($value);
|
||||
}
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
42
app/init.php
42
app/init.php
@@ -1,42 +0,0 @@
|
||||
<?php
|
||||
//----------------------------------
|
||||
// 公共入口文件
|
||||
//----------------------------------
|
||||
// 定义目录
|
||||
defined('ROOT_PATH') or define("ROOT_PATH", dirname(__DIR__));
|
||||
defined('APP_PATH') or define('APP_PATH', __DIR__);
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
define('TORRENT_PATH', APP_PATH.DS.'torrent'.DS);
|
||||
|
||||
// 严格开发模式
|
||||
error_reporting( E_ALL );
|
||||
#ini_set('display_errors', 1);
|
||||
|
||||
// 永不超时
|
||||
ini_set('max_execution_time', 0);
|
||||
set_time_limit(0);
|
||||
// 内存限制,如果外面设置的内存比 /etc/php/php-cli.ini 大,就不要设置了
|
||||
if (intval(ini_get("memory_limit")) < 1024)
|
||||
{
|
||||
ini_set('memory_limit', '1024M');
|
||||
}
|
||||
|
||||
if( PHP_SAPI != 'cli' )
|
||||
{
|
||||
exit("You must run the CLI environment\n");
|
||||
}
|
||||
|
||||
// 设置时区
|
||||
date_default_timezone_set('Asia/Shanghai');
|
||||
|
||||
// 系统配置
|
||||
if( file_exists( APP_PATH."/config/config.php" ) )
|
||||
{
|
||||
// 配置(全局变量)
|
||||
$configALL = require_once APP_PATH."/config/config.php";
|
||||
}else{
|
||||
// 示例配置
|
||||
$configALL = require_once APP_PATH . '/config/config.sample.php';
|
||||
}
|
||||
|
||||
require_once ROOT_PATH . '/vendor/autoload.php';
|
||||
@@ -1,10 +1,15 @@
|
||||
{
|
||||
"require": {
|
||||
"owner888/phpspider": "^2.1",
|
||||
"curl/curl": "^2.2"
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-curl": "*",
|
||||
"owner888/phpspider": "^2.1",
|
||||
"curl/curl": "^2.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"IYUU\\": "app/"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap":["app/Class"],
|
||||
"files": ["app/Class/Function.php"]
|
||||
}
|
||||
"files": ["app/helper.php"]
|
||||
}
|
||||
}
|
||||
|
||||
362
config/config.sample.php
Normal file
362
config/config.sample.php
Normal file
@@ -0,0 +1,362 @@
|
||||
<?php
|
||||
/**
|
||||
* 技术讨论及后续更新,请加入QQ群!!!!!!!
|
||||
群名称:IYUU自动辅种交流
|
||||
QQ群号:859882209
|
||||
* 手动配置方法,请查看:https://www.iyuu.cn/archives/324/
|
||||
*/
|
||||
return array(
|
||||
// 1.【必须配置】爱语飞飞 微信通知,请访问https://iyuu.cn 用微信扫码申请
|
||||
'iyuu.cn' => 'IYUU',
|
||||
// 2.server酱 微信通知配置
|
||||
'sc.ftqq.com' => '',
|
||||
// 3.发布员鉴权
|
||||
'secret' => '',
|
||||
// 4.全局默认配置
|
||||
'default' => array(
|
||||
// 5.【必须配置】浏览器UA,打开http://demo.iyuu.cn 复制过来即可
|
||||
'userAgent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36',
|
||||
// 6.【自动辅种必须配置】全局客户端设置(条目不够可以复制)
|
||||
'clients' => array(
|
||||
// 全局客户端设置 开始
|
||||
# 开始
|
||||
array(
|
||||
'type' => 'transmission', // 支持:transmission、qBittorrent
|
||||
'host' => 'http://127.0.0.1:9091/transmission/rpc', // 警告!注意:transmission/rpc这段别动,你只需要修改 127.0.0.1:9091
|
||||
'username' => '',
|
||||
'password' => '',
|
||||
),
|
||||
# 结束
|
||||
# 开始
|
||||
array(
|
||||
'type' => 'qBittorrent', // 支持:transmission、qBittorrent
|
||||
'host' => 'http://www.baidu.com:8083',
|
||||
'username' => '',
|
||||
'password' => '',
|
||||
),
|
||||
# 结束
|
||||
// 全局客户端设置 结束
|
||||
),
|
||||
'move' =>array(
|
||||
'type' => 2, // 0保持不变,1减,2加, 3直接替换
|
||||
'path' =>array(
|
||||
'/sda1' => '/volume1',
|
||||
),
|
||||
),
|
||||
'CONNECTTIMEOUT'=> 60,
|
||||
'TIMEOUT' => 600,
|
||||
),
|
||||
/**
|
||||
* 以下为各站点的独立配置(互不影响、互不冲突)
|
||||
* 自动辅种:需要配置各站的passkey(没有配置passkey的站点会自动跳过)
|
||||
*/
|
||||
// m-team 序号:1
|
||||
'm-team' => array(
|
||||
// 14.m-team的cookie 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => 'tp=',
|
||||
// 15.m-team的passkey 【必须配置】
|
||||
'passkey' => '',
|
||||
// 种子Tracker的IP地址选择 可选:ipv4,ipv6
|
||||
'ip_type' => 'ipv4',
|
||||
),
|
||||
// keepfrds 序号:2
|
||||
'keepfrds' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// ourbits 序号:3
|
||||
'ourbits' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
'id' => 0, // 用户ID
|
||||
'is_vip' => 0, // 是否具有VIP或特殊权限?0 普通,1 VIP
|
||||
),
|
||||
// HDSky 序号:4
|
||||
'hdsky' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// pter 序号:5
|
||||
'pter' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// tjupt 序号:6
|
||||
'tjupt' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hdhome 序号:7
|
||||
'hdhome' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// btschool 序号:8
|
||||
'btschool' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// PTHome 序号:9
|
||||
'pthome' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hddolby 序号:10
|
||||
'hddolby' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// TorrentCCF 序号:11
|
||||
'torrentccf' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// PTMSG 序号:12
|
||||
'ptmsg' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// MoeCat 序号:13
|
||||
'moecat' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
// 种子Tracker的IP地址选择 可选:ipv4,ipv6
|
||||
'ip_type' => 'ipv4',
|
||||
),
|
||||
// totheglory 序号:14
|
||||
'ttg' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// nanyangpt 序号:15
|
||||
'nanyangpt' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// springsunday.net 序号:16
|
||||
'ssd' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// yingk 序号:17
|
||||
'yingk' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hdcity 序号:18
|
||||
'hdcity' => array(
|
||||
// 必须配置
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置cuhash
|
||||
'passkey' => '',
|
||||
),
|
||||
// 52pt.site 序号:19
|
||||
'52pt' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// brobits.cc 序号:20
|
||||
'brobits' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// www.beitai.pt 序号:21
|
||||
'beitai' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// pt.eastgame.org 序号:22
|
||||
'eastgame' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// pt.soulvoice.club 序号:23
|
||||
'soulvoice' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// chdbits 序号:24
|
||||
'chdbits' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// leaguehd 序号:25
|
||||
'leaguehd' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// ptsbao.club 序号:26
|
||||
'ptsbao' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hdchina 序号:27
|
||||
'hdchina' => array(
|
||||
// 必须配置
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hdarea 序号:28
|
||||
'hdarea' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hdtime 序号:29
|
||||
'hdtime' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// 1ptba 序号:30
|
||||
'1ptba' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hd4fans 序号:31
|
||||
'hd4fans' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hdbug 序号:32
|
||||
'hdbug' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// opencd 序号:33 皇后
|
||||
'opencd' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hdstreet 序号:34
|
||||
'hdstreet' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// joyhd 序号:35
|
||||
'joyhd' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// dmhy 序号:36 幼儿园
|
||||
'dmhy' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// upxin 序号:37
|
||||
'upxin' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// oshen 序号:38
|
||||
'oshen' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// discfan 序号:39 港知堂
|
||||
'discfan' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hdzone 序号:40
|
||||
'hdzone' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// cnscg 序号:41 圣城
|
||||
'cnscg' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// nicept 序号:42 老师
|
||||
'nicept' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
// hdbd 序号:43 伊甸园
|
||||
'hdbd' => array(
|
||||
// 如果需要用下载免费种脚本,须配置(只是自动辅种,可以不配置此项)
|
||||
'cookie' => '',
|
||||
// 如果需要自动辅种,必须配置
|
||||
'passkey' => '',
|
||||
),
|
||||
|
||||
// 配置文件结束
|
||||
);
|
||||
50
init.php
Normal file
50
init.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
//----------------------------------
|
||||
// 公共入口文件
|
||||
//----------------------------------
|
||||
// 定义目录
|
||||
use IYUU\AutoReseed;
|
||||
|
||||
defined('ROOT_PATH') or define("ROOT_PATH", __DIR__);
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
define('TORRENT_PATH', ROOT_PATH.DS.'torrent'.DS);
|
||||
|
||||
// 严格开发模式
|
||||
error_reporting(E_ALL);
|
||||
#ini_set('display_errors', 1);
|
||||
|
||||
// 永不超时
|
||||
ini_set('max_execution_time', 0);
|
||||
set_time_limit(0);
|
||||
|
||||
// 内存限制,如果外面设置的内存比 /etc/php/php-cli.ini 大,就不要设置了
|
||||
if (intval(ini_get("memory_limit")) < 1024) {
|
||||
ini_set('memory_limit', '1024M');
|
||||
}
|
||||
|
||||
if (PHP_SAPI != 'cli') {
|
||||
exit("You must run the CLI environment\n");
|
||||
}
|
||||
|
||||
// 设置时区
|
||||
date_default_timezone_set('Asia/Shanghai');
|
||||
|
||||
// 系统配置
|
||||
if (file_exists(ROOT_PATH."/config/config.php")) {
|
||||
// 配置(全局变量)
|
||||
$configALL = require_once ROOT_PATH . "/config/config.php";
|
||||
} else {
|
||||
// 示例配置
|
||||
$configALL = require_once ROOT_PATH . '/config/config.sample.php';
|
||||
}
|
||||
|
||||
require_once ROOT_PATH . '/vendor/autoload.php';
|
||||
|
||||
AutoReseed::init();
|
||||
$hashArray = AutoReseed::get();
|
||||
if (AutoReseed::$move != null) {
|
||||
echo "种子移动完毕,请重新编辑配置,再尝试辅种! \n\n";
|
||||
exit;
|
||||
}
|
||||
AutoReseed::call($hashArray);
|
||||
AutoReseed::wechatMessage();
|
||||
626
iyuu.cn.php
626
iyuu.cn.php
@@ -1,626 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* IYUUAutoReseed自动辅种
|
||||
*/
|
||||
use Curl\Curl;
|
||||
require_once __DIR__ . '/app/init.php';
|
||||
iyuuAutoReseed::init();
|
||||
$hashArray = iyuuAutoReseed::get();
|
||||
if ( iyuuAutoReseed::$move != null ) {
|
||||
echo "种子移动完毕,请重新编辑配置,再尝试辅种! \n\n";
|
||||
exit;
|
||||
}
|
||||
iyuuAutoReseed::call($hashArray);
|
||||
iyuuAutoReseed::wechatMessage();
|
||||
/**
|
||||
* IYUUAutoReseed自动辅种类
|
||||
*/
|
||||
class iyuuAutoReseed
|
||||
{
|
||||
/**
|
||||
* 版本号
|
||||
* @var string
|
||||
*/
|
||||
const VER = '0.1.0';
|
||||
/**
|
||||
* RPC连接池
|
||||
* @var array
|
||||
*/
|
||||
public static $links = array();
|
||||
/**
|
||||
* 客户端配置
|
||||
* @var array
|
||||
*/
|
||||
public static $clients = array();
|
||||
/**
|
||||
* 不辅种的站点 'ourbits','hdchina'
|
||||
* @var array
|
||||
*/
|
||||
public static $noReseed = [];
|
||||
/**
|
||||
* 缓存路径
|
||||
* @var string
|
||||
*/
|
||||
public static $cacheDir = TORRENT_PATH.'cache'.DS;
|
||||
public static $cacheHash = TORRENT_PATH.'cachehash'.DS;
|
||||
/**
|
||||
* API接口配置
|
||||
* @var string
|
||||
* @var array
|
||||
*/
|
||||
public static $apiUrl = 'http://iyuu.cn:2122';
|
||||
public static $endpoints = array(
|
||||
'add' => '/api/add',
|
||||
'update' => '/api/update',
|
||||
'reseed' => '/api/reseed',
|
||||
'login' => '/login',
|
||||
);
|
||||
/**
|
||||
* 退出状态码
|
||||
* @var int
|
||||
*/
|
||||
public static $ExitCode = 0;
|
||||
/**
|
||||
* 客户端转移做种 状态码[请把transmission配置为第一个客户端]
|
||||
* @var array
|
||||
*/
|
||||
public static $move = null;
|
||||
/**
|
||||
* 微信消息体
|
||||
* @var array
|
||||
*/
|
||||
public static $wechatMsg = array(
|
||||
'hashCount' => 0, // 提交给服务器的hash总数
|
||||
'sitesCount' => 0, // 可辅种站点总数
|
||||
'reseedCount' => 0, // 返回的总数据
|
||||
'reseedSuccess' => 0, // 成功:辅种成功(会加入缓存,哪怕种子在校验中,下次也会过滤)
|
||||
'reseedError' => 0, // 错误:辅种失败(可以重试)
|
||||
'reseedRepeat' => 0, // 重复:客户端已做种
|
||||
'reseedSkip' => 0, // 跳过:因未设置passkey,而跳过
|
||||
'reseedPass' => 0, // 忽略:因上次成功添加、存在缓存,而跳过
|
||||
);
|
||||
/**
|
||||
* 初始化
|
||||
* @return void
|
||||
*/
|
||||
public static function init(){
|
||||
global $configALL;
|
||||
self::$clients = isset($configALL['default']['clients']) && $configALL['default']['clients'] ? $configALL['default']['clients'] : array();
|
||||
echo "程序正在初始化运行参数... \n";
|
||||
// 递归删除上次历史记录
|
||||
IFile::rmdir(self::$cacheDir, true);
|
||||
// 建立目录
|
||||
IFile::mkdir(self::$cacheDir);
|
||||
IFile::mkdir(self::$cacheHash);
|
||||
// 连接全局客户端
|
||||
self::links();
|
||||
// 合作站点自动注册鉴权
|
||||
Oauth::login(self::$apiUrl . self::$endpoints['login']);
|
||||
}
|
||||
/**
|
||||
* 连接远端RPC服务器
|
||||
*
|
||||
* @param string
|
||||
* @return bool
|
||||
*/
|
||||
public static function links()
|
||||
{
|
||||
if(empty(self::$links)){
|
||||
foreach ( self::$clients as $k => $v ){
|
||||
// 跳过未配置的客户端
|
||||
if (empty($v['username']) || empty( $v['password'])) {
|
||||
unset(self::$clients[$k]);
|
||||
echo "clients_".$k." 用户名或密码未配置,已跳过 \n\n";
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
switch($v['type']){
|
||||
case 'transmission':
|
||||
self::$links[$k]['rpc'] = new TransmissionRPC($v['host'], $v['username'], $v['password']);
|
||||
$result = self::$links[$k]['rpc']->sstats();
|
||||
print $v['type'].':'.$v['host']." Rpc连接 [{$result->result}] \n";
|
||||
break;
|
||||
case 'qBittorrent':
|
||||
self::$links[$k]['rpc'] = new qBittorrent($v['host'], $v['username'], $v['password']);
|
||||
$result = self::$links[$k]['rpc']->appVersion();
|
||||
print $v['type'].':'.$v['host']." Rpc连接 [{$result}] \n";
|
||||
break;
|
||||
default:
|
||||
echo '[ERROR] '.$v['type'];
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
self::$links[$k]['type'] = $v['type'];
|
||||
// 检查是否转移种子的做种客户端?
|
||||
if ( isset($v['move']) && $v['move'] ) {
|
||||
self::$move = array($k,$v['type']);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo '[ERROR] ' . $e->getMessage() . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* 从客户端获取种子的哈希列表
|
||||
* @var array
|
||||
*/
|
||||
public static function get(){
|
||||
$hashArray = array();
|
||||
foreach ( self::$clients as $k => $v ){
|
||||
$result = array();
|
||||
$res = $info_hash = array();
|
||||
$json = $sha1 = '';
|
||||
try
|
||||
{
|
||||
switch($v['type']){
|
||||
case 'transmission':
|
||||
$ids = $fields = array();
|
||||
#$fields = array( "id", "status", "name", "hashString", "downloadDir", "torrentFile" );
|
||||
$fields = array( "id", "status", "hashString", "downloadDir");
|
||||
$result = self::$links[$k]['rpc']->get($ids, $fields);
|
||||
if ( empty($result->result) || $result->result != 'success' ){
|
||||
// 获取种子列表 失败
|
||||
echo "获取种子列表失败,原因可能是transmission暂时无响应,请稍后重试! \n";
|
||||
break;
|
||||
}
|
||||
if( empty($result->arguments) ){
|
||||
echo "未获取到需要辅种的数据,请多多保种,然后重试! \n";
|
||||
break;
|
||||
}
|
||||
// 对象转数组
|
||||
$res = object_array($result->arguments->torrents);
|
||||
// 过滤,只保留正常做种
|
||||
$res = array_filter($res, "filterStatus");
|
||||
// 提取数组:hashString
|
||||
$info_hash = array_column($res, 'hashString');
|
||||
// 升序排序
|
||||
sort($info_hash);
|
||||
// 微信模板消息 统计
|
||||
self::$wechatMsg['hashCount'] += count($info_hash);
|
||||
$json = json_encode($info_hash, JSON_UNESCAPED_UNICODE);
|
||||
// 去重 应该从文件读入,防止重复提交
|
||||
$sha1 = sha1( $json );
|
||||
if ( isset($hashArray['sha1']) && (in_array($sha1, $hashArray['sha1']) != false) ) {
|
||||
break;
|
||||
}
|
||||
// 组装返回数据
|
||||
$hashArray['hash']['clients_'.$k] = $json;
|
||||
$hashArray['sha1'][] = $sha1;
|
||||
// 变换数组:hashString为键
|
||||
self::$links[$k]['hash'] = array_column($res, "downloadDir", 'hashString');
|
||||
#p(self::$links[$k]['hash']);exit;
|
||||
break;
|
||||
case 'qBittorrent':
|
||||
$result = self::$links[$k]['rpc']->torrentList();
|
||||
$res = json_decode($result,true);
|
||||
if ( empty($res) ) {
|
||||
echo "未获取到需要辅种的数据,请多多保种,然后重试! \n";
|
||||
break;
|
||||
}
|
||||
#p($res);exit;
|
||||
// 过滤,只保留正常做种
|
||||
$res = array_filter($res, "qbfilterStatus");
|
||||
// 提取数组:hashString
|
||||
$info_hash = array_column($res, 'hash');
|
||||
// 升序排序
|
||||
sort($info_hash);
|
||||
// 微信模板消息 统计
|
||||
self::$wechatMsg['hashCount'] += count($info_hash);
|
||||
$json = json_encode($info_hash, JSON_UNESCAPED_UNICODE);
|
||||
// 去重 应该从文件读入,防止重复提交
|
||||
$sha1 = sha1( $json );
|
||||
if ( isset($hashArray['sha1']) && (in_array($sha1, $hashArray['sha1']) != false) ) {
|
||||
break;
|
||||
}
|
||||
// 组装返回数据
|
||||
$hashArray['hash']['clients_'.$k] = $json;
|
||||
$hashArray['sha1'][] = $sha1;
|
||||
// 变换数组:hash为键
|
||||
self::$links[$k]['hash'] = array_column($res, "save_path", 'hash');
|
||||
#p(self::$links[$k]['hash']);exit;
|
||||
break;
|
||||
default:
|
||||
echo '[ERROR] '.$v['type'];
|
||||
exit(1);
|
||||
break;
|
||||
}
|
||||
// 是否执行转移种子做种客户端?
|
||||
if ( self::$move != null && (empty($v['move'])) ) {
|
||||
self::move($res, $v['type']);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo '[ERROR] ' . $e->getMessage() . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
return $hashArray;
|
||||
}
|
||||
/**
|
||||
* @brief 添加下载任务
|
||||
* @param string $torrent 种子元数据
|
||||
* @param string $save_path 保存路径
|
||||
* @return bool
|
||||
*/
|
||||
public static function add($rpcKey, $torrent, $save_path = '', $extra_options = array())
|
||||
{
|
||||
try
|
||||
{
|
||||
$is_url = false;
|
||||
if( (strpos($torrent,'http://')===0) || (strpos($torrent,'https://')===0) || (strpos($torrent,'magnet:?xt=urn:btih:')===0) ){
|
||||
$is_url = true;
|
||||
}
|
||||
// 下载服务器类型
|
||||
$type = self::$links[$rpcKey]['type'];
|
||||
// 判断
|
||||
switch( $type ){
|
||||
case 'transmission':
|
||||
$extra_options['paused'] = true;
|
||||
if( $is_url ){
|
||||
$result = self::$links[$rpcKey]['rpc']->add( $torrent, $save_path, $extra_options ); // 种子URL添加下载任务
|
||||
} else{
|
||||
$result = self::$links[$rpcKey]['rpc']->add_metainfo( $torrent, $save_path, $extra_options ); // 种子元数据添加下载任务
|
||||
}
|
||||
if(isset($result->result) && $result->result == 'success'){
|
||||
$id = $name = '';
|
||||
if( isset($result->arguments->torrent_duplicate) ){
|
||||
$id = $result->arguments->torrent_duplicate->id;
|
||||
$name = $result->arguments->torrent_duplicate->name;
|
||||
}elseif( isset($result->arguments->torrent_added) ){
|
||||
$id = $result->arguments->torrent_added->id;
|
||||
$name = $result->arguments->torrent_added->name;
|
||||
}
|
||||
print "********RPC添加下载任务成功 [{$result->result}] (id=$id) \n";
|
||||
if( $is_url ){
|
||||
print "种子:".$torrent. "\n";
|
||||
}
|
||||
print "名字:".$name."\n\n";
|
||||
return true;
|
||||
}else{
|
||||
$errmsg = isset($result->result) ? $result->result : '未知错误,请稍后重试!';
|
||||
print "-----RPC添加种子任务,失败 [{$errmsg}] \n";
|
||||
if( $is_url ){
|
||||
print "种子:".$torrent. "\n";
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'qBittorrent':
|
||||
$extra_options['paused'] = 'true';
|
||||
$extra_options['autoTMM'] = 'false'; //关闭自动种子管理
|
||||
if( $is_url ){
|
||||
$result = self::$links[$rpcKey]['rpc']->add( $torrent, $save_path, $extra_options ); // 种子URL添加下载任务
|
||||
} else{
|
||||
$extra_options['name'] = 'torrents';
|
||||
$extra_options['filename'] = rand(1,4294967200).'.torrent';
|
||||
$result = self::$links[$rpcKey]['rpc']->add_metainfo( $torrent, $save_path, $extra_options ); // 种子元数据添加下载任务
|
||||
}
|
||||
if ($result === 'Ok.') {
|
||||
print "********RPC添加下载任务成功 [{$result}] \n\n";
|
||||
return true;
|
||||
} else {
|
||||
print "-----RPC添加种子任务,失败 [{$result}] \n\n";
|
||||
}
|
||||
break;
|
||||
default:
|
||||
echo '[ERROR] '.$type;
|
||||
break;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo '[ERROR] ' . $e->getMessage() . PHP_EOL;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 正常做种的种子在各下载器的互相转移
|
||||
*/
|
||||
public static function move($torrent=array(), $type = 'qBittorrent'){
|
||||
switch($type){
|
||||
case 'transmission':
|
||||
break;
|
||||
case 'qBittorrent':
|
||||
foreach ($torrent as $k => $v) {
|
||||
// 路径转换
|
||||
#$v['save_path'] = '/volume3' . $v['save_path']; // docker路径转换
|
||||
self::add(self::$move[0], $v['magnet_uri'], $v['save_path'] );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
echo '[ERROR] '.$type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @brief 提交种子hash给远端API,用来获取辅种数据
|
||||
* @param array $hashArray 种子hash数组
|
||||
* @return
|
||||
*/
|
||||
public static function call($hashArray = array())
|
||||
{
|
||||
global $configALL;
|
||||
$resArray = $sites = array();
|
||||
$curl = new Curl();
|
||||
$curl->setOpt(CURLOPT_SSL_VERIFYPEER, false);
|
||||
// 签名
|
||||
$hashArray['timestamp'] = time();
|
||||
// 爱语飞飞token
|
||||
$hashArray['sign'] = Oauth::getSign();
|
||||
$hashArray['version'] = self::VER;
|
||||
// 写日志
|
||||
if (true) {
|
||||
// 文件句柄
|
||||
$resource = fopen(self::$cacheDir.'hashString.txt', "wb");
|
||||
// 成功:返回写入字节数,失败返回false
|
||||
$worldsnum = fwrite($resource, p($hashArray, false));
|
||||
fclose($resource);
|
||||
}
|
||||
// 发起请求
|
||||
echo "正在提交辅种信息…… \n";
|
||||
$res = $curl->post(self::$apiUrl . self::$endpoints['reseed'], $hashArray);
|
||||
$resArray = json_decode($res->response, true);
|
||||
// 写日志
|
||||
if(true){
|
||||
// 文件句柄
|
||||
$resource = fopen(self::$cacheDir.'reseed.txt', "wb");
|
||||
// 成功:返回写入字节数,失败返回false
|
||||
$worldsnum = fwrite($resource, p($resArray, false));
|
||||
fclose($resource);
|
||||
}
|
||||
// 判断返回值
|
||||
if ( isset($resArray['errmsg']) && ($resArray['errmsg'] == 'ok') ) {
|
||||
echo "辅种信息提交成功!!! \n\n";
|
||||
}else{
|
||||
$errmsg = isset($resArray['errmsg']) ? $resArray['errmsg'] : '远端服务器无响应,请稍后重试!';
|
||||
echo '-----辅种失败,原因:' .$errmsg. " \n\n";
|
||||
exit(1);
|
||||
}
|
||||
// 可辅种站点信息列表
|
||||
$sites = $resArray['sites'];
|
||||
self::$wechatMsg['sitesCount'] = count($sites);
|
||||
#p($sites);
|
||||
// 按客户端循环辅种 开始
|
||||
foreach (self::$links as $k => $v) {
|
||||
$reseed = $infohash_Dir = array();
|
||||
// info_hash 对应的下载目录
|
||||
$infohash_Dir = self::$links[$k]['hash'];
|
||||
if (empty($resArray['clients_'.$k])) {
|
||||
echo "clients_".$k."没有查询到可辅种数据 \n\n";
|
||||
continue;
|
||||
}
|
||||
#p($infohash_Dir);
|
||||
// 当前客户端辅种数据
|
||||
$reseed = $resArray['clients_'.$k];
|
||||
foreach ($reseed as $info_hash => $vv) {
|
||||
// 当前种子哈希对应的目录
|
||||
$downloadDir = $infohash_Dir[$info_hash];
|
||||
foreach ($vv['torrent'] as $id => $value) {
|
||||
// 匹配的辅种数据累加
|
||||
self::$wechatMsg['reseedCount']++;
|
||||
// 站点id
|
||||
$sitesID = $value['sid'];
|
||||
$url = $_url = '';
|
||||
$download_page = $details_url = '';
|
||||
// 页面规则
|
||||
$download_page = str_replace('{}', $value['torrent_id'], $sites[$sitesID]['download_page']);
|
||||
$_url = 'https://' .$sites[$sitesID]['base_url']. '/' .$download_page;
|
||||
if ( empty($configALL[$sites[$sitesID]['site']]['passkey']) ) {
|
||||
echo '-------因当前' .$sites[$sitesID]['site']. '站点未设置passkey,已跳过!!' . "\n\n";
|
||||
self::$wechatMsg['reseedSkip']++;
|
||||
continue;
|
||||
}
|
||||
// 种子URL组合方式区分
|
||||
switch ($sites[$sitesID]['site']) {
|
||||
case 'ttg':
|
||||
$url = $_url."/". $configALL[$sites[$sitesID]['site']]['passkey'];
|
||||
break;
|
||||
case 'm-team':
|
||||
$ip_type = '';
|
||||
if (isset($configALL[$sites[$sitesID]['site']]['ip_type'])) {
|
||||
$ip_type = $configALL[$sites[$sitesID]['site']]['ip_type'] == 'ipv6' ? '&ipv6=1' : '';
|
||||
}
|
||||
$url = $_url."&passkey=". $configALL[$sites[$sitesID]['site']]['passkey'] . $ip_type. "&https=1";
|
||||
break;
|
||||
case 'moecat':
|
||||
$ip_type = '';
|
||||
if (isset($configALL[$sites[$sitesID]['site']]['ip_type'])) {
|
||||
$ip_type = $configALL[$sites[$sitesID]['site']]['ip_type'] == 'ipv6' ? '&ipv6=1' : '';
|
||||
}
|
||||
$url = $_url."&passkey=". $configALL[$sites[$sitesID]['site']]['passkey'] . $ip_type. "&https=1";
|
||||
break;
|
||||
default:
|
||||
$url = $_url."&passkey=". $configALL[$sites[$sitesID]['site']]['passkey'];
|
||||
break;
|
||||
}
|
||||
/**
|
||||
* 检查站点是否可以辅种
|
||||
*/
|
||||
// 判断是否具有VIP或特殊权限?
|
||||
$is_vip = isset($configALL[$sites[$sitesID]['site']]['is_vip']) && $configALL[$sites[$sitesID]['site']]['is_vip'] ? 1 : 0;
|
||||
if ( (in_array($sites[$sitesID]['site'], self::$noReseed)==false) || $is_vip ) {
|
||||
/**
|
||||
* 可以辅种
|
||||
*/
|
||||
if ( isset($infohash_Dir[$value['info_hash']]) ) {
|
||||
// 与客户端现有种子重复
|
||||
echo '-------与客户端现有种子重复:'.$_url."\n\n";
|
||||
self::$wechatMsg['reseedRepeat']++;
|
||||
continue;
|
||||
}else{
|
||||
// 判断上次是否成功添加?
|
||||
if ( is_file(self::$cacheHash . $value['info_hash'].'.txt') ) {
|
||||
echo '-------当前种子上次辅种已成功添加,已跳过!'.$_url."\n\n";
|
||||
self::$wechatMsg['reseedPass']++;
|
||||
continue;
|
||||
}
|
||||
// 种子元数据获取
|
||||
switch ($sites[$sitesID]['site']) {
|
||||
case 'hdchina':
|
||||
if ( empty($configALL[$sites[$sitesID]['site']]['cookie']) ) {
|
||||
echo '-------因当前' .$sites[$sitesID]['site']. '站点未设置cookie,已跳过!!' . "\n\n";
|
||||
self::$wechatMsg['reseedSkip']++;
|
||||
break;
|
||||
}
|
||||
if ( isset($configALL[$sites[$sitesID]['site']]['limit']) ) {
|
||||
echo "当前站点触发人机验证,已加入排除列表 \n";
|
||||
}
|
||||
$cookie = isset($configALL[$sites[$sitesID]['site']]['cookie']) ? $configALL[$sites[$sitesID]['site']]['cookie'] : '';
|
||||
$userAgent = $configALL['default']['userAgent'];
|
||||
// 拼接URL
|
||||
$details_page = str_replace('{}', $value['torrent_id'], 'details.php?id={}&hit=1');
|
||||
$details_url = 'https://' .$sites[$sitesID]['base_url']. '/' .$details_page;
|
||||
$details_html = download($details_url, $cookie, $userAgent);
|
||||
print "种子详情页:".$details_url. "\n";
|
||||
// 提取种子下载地址
|
||||
$download_page = str_replace('{}', '', $sites[$sitesID]['download_page']);
|
||||
$offset = strpos($details_html, $download_page);
|
||||
$urlTemp = substr($details_html, $offset, 50);
|
||||
// 种子地址
|
||||
$_url = substr($urlTemp,0,strpos($urlTemp,'">'));
|
||||
$_url = 'https://' .$sites[$sitesID]['base_url']. '/' . $_url;
|
||||
print "种子下载页:".$_url. "\n";
|
||||
$url = download($_url, $cookie, $userAgent);
|
||||
if(strpos($url,'系统检测到过多的种子下载请求') != false){
|
||||
echo "触发人机验证 \n";
|
||||
ff($sites[$sitesID]['site']. ' 触发人机验证,请重新设置!');
|
||||
self::$noReseed[] = 'hdchina';
|
||||
$configALL[$sites[$sitesID]['site']]['limit'] = 1;
|
||||
}
|
||||
break;
|
||||
case 'hdcity':
|
||||
if ( empty($configALL[$sites[$sitesID]['site']]['cookie']) ) {
|
||||
echo '-------因当前' .$sites[$sitesID]['site']. '站点未设置cookie,已跳过!!' . "\n\n";
|
||||
self::$wechatMsg['reseedSkip']++;
|
||||
break;
|
||||
}
|
||||
$cookie = isset($configALL[$sites[$sitesID]['site']]['cookie']) ? $configALL[$sites[$sitesID]['site']]['cookie'] : '';
|
||||
$userAgent = $configALL['default']['userAgent'];
|
||||
print "种子:".$_url. "\n";
|
||||
if ( isset($configALL[$sites[$sitesID]['site']]['cuhash']) ) {
|
||||
// 已获取cuhash
|
||||
# code...
|
||||
}else {
|
||||
// 获取cuhash
|
||||
$html = download('https://' .$sites[$sitesID]['base_url']. '/pt', $cookie, $userAgent);
|
||||
// 提取种子下载地址
|
||||
$offset = strpos($html,'cuhash=');
|
||||
$len = strlen('cuhash=');
|
||||
$cuhashTemp = substr($html,$offset+$len,40);
|
||||
$configALL[$sites[$sitesID]['site']]['cuhash'] = substr($cuhashTemp,0,strpos($cuhashTemp,'"'));
|
||||
}
|
||||
$url = $_url."&cuhash=". $configALL[$sites[$sitesID]['site']]['cuhash'];
|
||||
// 城市下载种子时会302转向
|
||||
$url = download($url, $cookie, $userAgent);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// 把拼接的种子URL,推送给下载器
|
||||
$ret = false;
|
||||
// 成功返回:true
|
||||
$ret = self::add($k, $url, $downloadDir);
|
||||
// 添加成功的种子,以infohash为文件名,写入缓存
|
||||
if ($ret) {
|
||||
// 成功的种子
|
||||
// 文件句柄
|
||||
$resource = fopen(self::$cacheHash . $value['info_hash'].'.txt', "wb");
|
||||
// 成功:返回写入字节数,失败返回false
|
||||
$worldsnum = fwrite($resource, $url);
|
||||
fclose($resource);
|
||||
self::$wechatMsg['reseedSuccess']++;
|
||||
continue;
|
||||
}else{
|
||||
// 失败的种子
|
||||
// 站点类型判断
|
||||
switch ($sites[$sitesID]['site']) {
|
||||
case 'hdcity':
|
||||
echo '当前' .$sites[$sitesID]['site']. '站点是配置cuhash(不是passkey),添加成功说明配置正确!如果添加任务失败,请查阅常见问题!!' . "\n";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// 失败累加
|
||||
self::$wechatMsg['reseedError']++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
/**
|
||||
* 不辅种
|
||||
*/
|
||||
echo '-------已跳过不辅种的站点:'.$_url."\n\n";
|
||||
// 写入日志文件,供用户手动辅种
|
||||
if ( !isset($infohash_Dir[$value['info_hash']]) ) {
|
||||
// 站点类型判断
|
||||
switch ($sites[$sitesID]['site']) {
|
||||
case 'hdchina':
|
||||
$url = $_url;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// 文件句柄
|
||||
$resource = fopen(self::$cacheDir . $sites[$sitesID]['site'].'.txt', 'a');
|
||||
// 成功:返回写入字节数,失败返回false
|
||||
$worldsnum = fwrite($resource, 'clients_'.$k."\n".$downloadDir."\n".$url."\n".$details_url."\n\n");
|
||||
fclose($resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 按客户端循环辅种 结束
|
||||
}
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static function wechatMessage(){
|
||||
$br = "\r\n";
|
||||
$text = 'IYUU自动辅种-统计报表';
|
||||
$desp = '总做种:'.self::$wechatMsg['hashCount'] . ' [客户端正在做种的hash总数]' .$br;
|
||||
$desp .= '返回数据:'.self::$wechatMsg['reseedCount']. ' [服务器返回的可辅种数据]' .$br;
|
||||
$desp .= '支持站点:'.self::$wechatMsg['sitesCount']. ' [当前支持自动辅种的站点数量]' .$br;
|
||||
$desp .= '成功:'.self::$wechatMsg['reseedSuccess']. ' [辅种成功,会把hash加入缓存]' .$br;
|
||||
$desp .= '失败:'.self::$wechatMsg['reseedError']. ' [下载器下载种子失败或网络超时引起,可以重试]' .$br;
|
||||
$desp .= '重复:'.self::$wechatMsg['reseedRepeat']. ' [客户端已做种]' .$br;
|
||||
$desp .= '跳过:'.self::$wechatMsg['reseedSkip']. ' [未设置passkey]' .$br;
|
||||
$desp .= '忽略:'.self::$wechatMsg['reseedPass']. ' [成功添加存在缓存]' .$br;
|
||||
return ff($text, $desp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* transmission过滤函数,只保留正常做种
|
||||
*/
|
||||
function filterStatus( $v ){
|
||||
return isset($v['status']) && $v['status']===6;
|
||||
}
|
||||
|
||||
/**
|
||||
* qBittorrent过滤函数,只保留正常做种
|
||||
*/
|
||||
function qbfilterStatus( $v ){
|
||||
if( isset($v['state']) && in_array($v['state'], array('uploading','stalledUP','pausedUP','queuedUP','checkingUP','forcedUP')) ){
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
//PHP stdClass Object转array
|
||||
function object_array($array) {
|
||||
if(is_object($array)) {
|
||||
$array = (array)$array;
|
||||
}
|
||||
if(is_array($array)) {
|
||||
foreach($array as $key=>$value) {
|
||||
$array[$key] = object_array($value);
|
||||
}
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
// 对象转数组
|
||||
function object2array(&$object) {
|
||||
return json_decode( json_encode( $object ), true );
|
||||
}
|
||||
7
vendor/autoload.php
vendored
7
vendor/autoload.php
vendored
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit902220bdd481fe56c25750cdf0255dd6::getLoader();
|
||||
445
vendor/composer/ClassLoader.php
vendored
445
vendor/composer/ClassLoader.php
vendored
@@ -1,445 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see http://www.php-fig.org/psr/psr-0/
|
||||
* @see http://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
// PSR-4
|
||||
private $prefixLengthsPsr4 = array();
|
||||
private $prefixDirsPsr4 = array();
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
private $prefixesPsr0 = array();
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
private $useIncludePath = false;
|
||||
private $classMap = array();
|
||||
private $classMapAuthoritative = false;
|
||||
private $missingClasses = array();
|
||||
private $apcuPrefix;
|
||||
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $classMap Class to filename map
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return bool|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*/
|
||||
function includeFile($file)
|
||||
{
|
||||
include $file;
|
||||
}
|
||||
21
vendor/composer/LICENSE
vendored
21
vendor/composer/LICENSE
vendored
@@ -1,21 +0,0 @@
|
||||
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
17
vendor/composer/autoload_classmap.php
vendored
17
vendor/composer/autoload_classmap.php
vendored
@@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Bencode' => $baseDir . '/app/Class/Bencode.php',
|
||||
'IFile' => $baseDir . '/app/Class/IFile.php',
|
||||
'Oauth' => $baseDir . '/app/Class/Oauth.php',
|
||||
'Rpc' => $baseDir . '/app/Class/Rpc.php',
|
||||
'TransmissionRPC' => $baseDir . '/app/Class/TransmissionRPC.php',
|
||||
'TransmissionRPCException' => $baseDir . '/app/Class/TransmissionRPC.php',
|
||||
'qBittorrent' => $baseDir . '/app/Class/qBittorrent.php',
|
||||
'uTorrent' => $baseDir . '/app/Class/uTorrent.php',
|
||||
);
|
||||
10
vendor/composer/autoload_files.php
vendored
10
vendor/composer/autoload_files.php
vendored
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_files.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'818e4acb2b433594ec9a26bacb71084d' => $baseDir . '/app/Class/Function.php',
|
||||
);
|
||||
10
vendor/composer/autoload_namespaces.php
vendored
10
vendor/composer/autoload_namespaces.php
vendored
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Curl' => array($vendorDir . '/curl/curl/src'),
|
||||
);
|
||||
10
vendor/composer/autoload_psr4.php
vendored
10
vendor/composer/autoload_psr4.php
vendored
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'phpspider\\' => array($vendorDir . '/owner888/phpspider'),
|
||||
);
|
||||
70
vendor/composer/autoload_real.php
vendored
70
vendor/composer/autoload_real.php
vendored
@@ -1,70 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit902220bdd481fe56c25750cdf0255dd6
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit902220bdd481fe56c25750cdf0255dd6', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit902220bdd481fe56c25750cdf0255dd6', 'loadClassLoader'));
|
||||
|
||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||
if ($useStaticLoader) {
|
||||
require_once __DIR__ . '/autoload_static.php';
|
||||
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit902220bdd481fe56c25750cdf0255dd6::getInitializer($loader));
|
||||
} else {
|
||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->set($namespace, $path);
|
||||
}
|
||||
|
||||
$map = require __DIR__ . '/autoload_psr4.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->setPsr4($namespace, $path);
|
||||
}
|
||||
|
||||
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||
if ($classMap) {
|
||||
$loader->addClassMap($classMap);
|
||||
}
|
||||
}
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
if ($useStaticLoader) {
|
||||
$includeFiles = Composer\Autoload\ComposerStaticInit902220bdd481fe56c25750cdf0255dd6::$files;
|
||||
} else {
|
||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||
}
|
||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||
composerRequire902220bdd481fe56c25750cdf0255dd6($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
|
||||
function composerRequire902220bdd481fe56c25750cdf0255dd6($fileIdentifier, $file)
|
||||
{
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
require $file;
|
||||
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
}
|
||||
}
|
||||
58
vendor/composer/autoload_static.php
vendored
58
vendor/composer/autoload_static.php
vendored
@@ -1,58 +0,0 @@
|
||||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInit902220bdd481fe56c25750cdf0255dd6
|
||||
{
|
||||
public static $files = array (
|
||||
'818e4acb2b433594ec9a26bacb71084d' => __DIR__ . '/../..' . '/app/Class/Function.php',
|
||||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'p' =>
|
||||
array (
|
||||
'phpspider\\' => 10,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'phpspider\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/owner888/phpspider',
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixesPsr0 = array (
|
||||
'C' =>
|
||||
array (
|
||||
'Curl' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/curl/curl/src',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
public static $classMap = array (
|
||||
'Bencode' => __DIR__ . '/../..' . '/app/Class/Bencode.php',
|
||||
'IFile' => __DIR__ . '/../..' . '/app/Class/IFile.php',
|
||||
'Oauth' => __DIR__ . '/../..' . '/app/Class/Oauth.php',
|
||||
'Rpc' => __DIR__ . '/../..' . '/app/Class/Rpc.php',
|
||||
'TransmissionRPC' => __DIR__ . '/../..' . '/app/Class/TransmissionRPC.php',
|
||||
'TransmissionRPCException' => __DIR__ . '/../..' . '/app/Class/TransmissionRPC.php',
|
||||
'qBittorrent' => __DIR__ . '/../..' . '/app/Class/qBittorrent.php',
|
||||
'uTorrent' => __DIR__ . '/../..' . '/app/Class/uTorrent.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInit902220bdd481fe56c25750cdf0255dd6::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInit902220bdd481fe56c25750cdf0255dd6::$prefixDirsPsr4;
|
||||
$loader->prefixesPsr0 = ComposerStaticInit902220bdd481fe56c25750cdf0255dd6::$prefixesPsr0;
|
||||
$loader->classMap = ComposerStaticInit902220bdd481fe56c25750cdf0255dd6::$classMap;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
}
|
||||
119
vendor/composer/installed.json
vendored
119
vendor/composer/installed.json
vendored
@@ -1,119 +0,0 @@
|
||||
[
|
||||
{
|
||||
"name": "curl/curl",
|
||||
"version": "2.2.0",
|
||||
"version_normalized": "2.2.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-mod/curl.git",
|
||||
"reference": "d22086dd2eee5ca02e4c29b9a5bdf3645bfdbbff"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-mod/curl/zipball/d22086dd2eee5ca02e4c29b9a5bdf3645bfdbbff",
|
||||
"reference": "d22086dd2eee5ca02e4c29b9a5bdf3645bfdbbff",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"php": "^5.6 | ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.7",
|
||||
"squizlabs/php_codesniffer": "~2.1"
|
||||
},
|
||||
"time": "2018-12-04T19:47:03+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Curl": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Hassan Amouhzi",
|
||||
"email": "hassan@anezi.net",
|
||||
"homepage": "http://hassan.amouhzi.com"
|
||||
},
|
||||
{
|
||||
"name": "php-curl-class",
|
||||
"homepage": "https://github.com/php-curl-class"
|
||||
},
|
||||
{
|
||||
"name": "user52",
|
||||
"homepage": "https://github.com/user52"
|
||||
}
|
||||
],
|
||||
"description": "cURL class for PHP",
|
||||
"homepage": "https://github.com/php-mod/curl",
|
||||
"keywords": [
|
||||
"curl",
|
||||
"dot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "owner888/phpspider",
|
||||
"version": "v2.1.6",
|
||||
"version_normalized": "2.1.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/owner888/phpspider.git",
|
||||
"reference": "e6021148adec201418c16ba26f39bc013ba5b4d9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/owner888/phpspider/zipball/e6021148adec201418c16ba26f39bc013ba5b4d9",
|
||||
"reference": "e6021148adec201418c16ba26f39bc013ba5b4d9",
|
||||
"shasum": "",
|
||||
"mirrors": [
|
||||
{
|
||||
"url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%",
|
||||
"preferred": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-pcntl、ext-redis": "For better performance. "
|
||||
},
|
||||
"time": "2018-08-15T08:04:29+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"phpspider\\": "./"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Seatle Yang",
|
||||
"email": "seatle@foxmail.com",
|
||||
"homepage": "http://www.phpspider.org",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "The PHPSpider Framework.",
|
||||
"homepage": "http://www.phpspider.org",
|
||||
"keywords": [
|
||||
"framework",
|
||||
"phpspider"
|
||||
]
|
||||
}
|
||||
]
|
||||
11
vendor/curl/curl/.gitignore
vendored
11
vendor/curl/curl/.gitignore
vendored
@@ -1,11 +0,0 @@
|
||||
vendor/*
|
||||
*.orig
|
||||
.buildpath
|
||||
.project
|
||||
.settings/*
|
||||
.idea/*
|
||||
composer.lock
|
||||
*~
|
||||
tests/phpunit_report/*
|
||||
/.settings/
|
||||
/.php_cs.cache
|
||||
113
vendor/curl/curl/.gitlab-ci.yml
vendored
113
vendor/curl/curl/.gitlab-ci.yml
vendored
@@ -1,113 +0,0 @@
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
|
||||
build-test-server:
|
||||
image: docker:latest
|
||||
stage: build
|
||||
services:
|
||||
- docker:dind
|
||||
script:
|
||||
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
|
||||
- docker build --pull -t "$CI_REGISTRY_IMAGE:server-test" tests/server
|
||||
- docker push "$CI_REGISTRY_IMAGE:server-test"
|
||||
only:
|
||||
changes:
|
||||
- tests/server
|
||||
|
||||
tests-php5.6:
|
||||
image: alpine:3.7
|
||||
stage: test
|
||||
services:
|
||||
- name: "$CI_REGISTRY_IMAGE:server-test"
|
||||
alias: server_test
|
||||
script:
|
||||
- apk add --no-cache php5-cli php5-curl php5-gd php5-phar php5-json php5-openssl php5-dom php5-xml php5-zlib
|
||||
- ln -s /usr/bin/php5 /usr/bin/php
|
||||
- php --version
|
||||
- if [ ! -f composer.phar ]; then DOWLOAD_COMPOSER=1 ; fi;
|
||||
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" ; fi;
|
||||
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "if (hash_file('sha384', 'composer-setup.php') === '93b54496392c062774670ac18b134c3b3a95e5a5e5c8f1a9f115f203b75bf9a129d5daa8ba6a13e2cc8a1da0806388a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" ; fi;
|
||||
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php composer-setup.php ; fi;
|
||||
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "unlink('composer-setup.php');" ; fi;
|
||||
- php composer.phar install
|
||||
- vendor/bin/phpcs --warning-severity=0 --standard=PSR2 src
|
||||
- vendor/bin/phpunit
|
||||
cache:
|
||||
key: php5.6
|
||||
paths:
|
||||
- composer.phar
|
||||
- vendor
|
||||
|
||||
tests-php7.0:
|
||||
image: alpine:3.5
|
||||
stage: test
|
||||
services:
|
||||
- name: "$CI_REGISTRY_IMAGE:server-test"
|
||||
alias: server_test
|
||||
script:
|
||||
- apk add --no-cache php7 php7-curl php7-gd php7-phar php7-json php7-openssl php7-dom php7-mbstring
|
||||
- ln -s /usr/bin/php7 /usr/bin/php
|
||||
- php --version
|
||||
- if [ ! -f composer.phar ]; then DOWLOAD_COMPOSER=1 ; fi;
|
||||
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" ; fi;
|
||||
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "if (hash_file('sha384', 'composer-setup.php') === '93b54496392c062774670ac18b134c3b3a95e5a5e5c8f1a9f115f203b75bf9a129d5daa8ba6a13e2cc8a1da0806388a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" ; fi;
|
||||
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php composer-setup.php ; fi;
|
||||
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "unlink('composer-setup.php');" ; fi;
|
||||
- php composer.phar install
|
||||
- vendor/bin/phpcs --warning-severity=0 --standard=PSR2 src
|
||||
- nohup php -S localhost:8000 -t tests/server/php-curl-test > phpd.log 2>&1 &
|
||||
- vendor/bin/phpunit
|
||||
cache:
|
||||
key: php7.0
|
||||
paths:
|
||||
- composer.phar
|
||||
- vendor
|
||||
|
||||
tests-php7.1:
|
||||
image: alpine:3.7
|
||||
stage: test
|
||||
services:
|
||||
- name: "$CI_REGISTRY_IMAGE:server-test"
|
||||
alias: server_test
|
||||
script:
|
||||
- apk add --no-cache php7-cli php7-curl php7-gd php7-phar php7-json php7-openssl php7-dom php7-simplexml php7-tokenizer php7-mbstring php7-xml
|
||||
- php --version
|
||||
- if [ ! -f composer.phar ]; then DOWLOAD_COMPOSER=1 ; fi;
|
||||
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" ; fi;
|
||||
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "if (hash_file('sha384', 'composer-setup.php') === '93b54496392c062774670ac18b134c3b3a95e5a5e5c8f1a9f115f203b75bf9a129d5daa8ba6a13e2cc8a1da0806388a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" ; fi;
|
||||
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php composer-setup.php ; fi;
|
||||
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "unlink('composer-setup.php');" ; fi;
|
||||
- php composer.phar install
|
||||
- vendor/bin/phpcs --warning-severity=0 --standard=PSR2 src
|
||||
- nohup php -S localhost:8000 -t tests/server/php-curl-test > phpd.log 2>&1 &
|
||||
- vendor/bin/phpunit
|
||||
cache:
|
||||
key: php7.1
|
||||
paths:
|
||||
- composer.phar
|
||||
- vendor
|
||||
|
||||
tests-php7.2:
|
||||
image: alpine:3.8
|
||||
stage: test
|
||||
services:
|
||||
- name: "$CI_REGISTRY_IMAGE:server-test"
|
||||
alias: server_test
|
||||
script:
|
||||
- apk add --no-cache php7-cli php7-curl php7-gd php7-phar php7-json php7-openssl php7-dom php7-simplexml php7-tokenizer php7-mbstring php7-xml
|
||||
- php --version
|
||||
- if [ ! -f composer.phar ]; then DOWLOAD_COMPOSER=1 ; fi;
|
||||
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" ; fi;
|
||||
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "if (hash_file('sha384', 'composer-setup.php') === '93b54496392c062774670ac18b134c3b3a95e5a5e5c8f1a9f115f203b75bf9a129d5daa8ba6a13e2cc8a1da0806388a8') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" ; fi;
|
||||
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php composer-setup.php ; fi;
|
||||
- if [ -n "$DOWLOAD_COMPOSER" ] ; then php -r "unlink('composer-setup.php');" ; fi;
|
||||
- php composer.phar install
|
||||
- vendor/bin/phpcs --warning-severity=0 --standard=PSR2 src
|
||||
- nohup php -S localhost:8000 -t tests/server/php-curl-test > phpd.log 2>&1 &
|
||||
- vendor/bin/phpunit
|
||||
cache:
|
||||
key: php7.2
|
||||
paths:
|
||||
- composer.phar
|
||||
- vendor
|
||||
20
vendor/curl/curl/LICENSE
vendored
20
vendor/curl/curl/LICENSE
vendored
@@ -1,20 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 php-mod
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
125
vendor/curl/curl/README.md
vendored
125
vendor/curl/curl/README.md
vendored
@@ -1,125 +0,0 @@
|
||||
# PHP Curl Class
|
||||
|
||||
This library provides an object-oriented wrapper of the PHP cURL extension.
|
||||
|
||||
If you have questions or problems with installation or usage [create an Issue](https://github.com/php-mod/curl/issues).
|
||||
|
||||
## Installation
|
||||
|
||||
In order to install this library via composer run the following command in the console:
|
||||
|
||||
```sh
|
||||
composer require curl/curl
|
||||
```
|
||||
|
||||
or add the package manually to your composer.json file in the require section:
|
||||
|
||||
```json
|
||||
"curl/curl": "^2.0"
|
||||
```
|
||||
|
||||
## Usage examples
|
||||
|
||||
```php
|
||||
$curl = new Curl\Curl();
|
||||
$curl->get('http://www.example.com/');
|
||||
```
|
||||
|
||||
```php
|
||||
$curl = new Curl\Curl();
|
||||
$curl->get('http://www.example.com/search', array(
|
||||
'q' => 'keyword',
|
||||
));
|
||||
```
|
||||
|
||||
```php
|
||||
$curl = new Curl\Curl();
|
||||
$curl->post('http://www.example.com/login/', array(
|
||||
'username' => 'myusername',
|
||||
'password' => 'mypassword',
|
||||
));
|
||||
```
|
||||
|
||||
```php
|
||||
$curl = new Curl\Curl();
|
||||
$curl->setBasicAuthentication('username', 'password');
|
||||
$curl->setUserAgent('');
|
||||
$curl->setReferrer('');
|
||||
$curl->setHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
$curl->setCookie('key', 'value');
|
||||
$curl->get('http://www.example.com/');
|
||||
|
||||
if ($curl->error) {
|
||||
echo $curl->error_code;
|
||||
}
|
||||
else {
|
||||
echo $curl->response;
|
||||
}
|
||||
|
||||
var_dump($curl->request_headers);
|
||||
var_dump($curl->response_headers);
|
||||
```
|
||||
|
||||
```php
|
||||
$curl = new Curl\Curl();
|
||||
$curl->setOpt(CURLOPT_RETURNTRANSFER, TRUE);
|
||||
$curl->setOpt(CURLOPT_SSL_VERIFYPEER, FALSE);
|
||||
$curl->get('https://encrypted.example.com/');
|
||||
```
|
||||
|
||||
```php
|
||||
$curl = new Curl\Curl();
|
||||
$curl->put('http://api.example.com/user/', array(
|
||||
'first_name' => 'Zach',
|
||||
'last_name' => 'Borboa',
|
||||
));
|
||||
```
|
||||
|
||||
```php
|
||||
$curl = new Curl\Curl();
|
||||
$curl->patch('http://api.example.com/profile/', array(
|
||||
'image' => '@path/to/file.jpg',
|
||||
));
|
||||
```
|
||||
|
||||
```php
|
||||
$curl = new Curl\Curl();
|
||||
$curl->delete('http://api.example.com/user/', array(
|
||||
'id' => '1234',
|
||||
));
|
||||
```
|
||||
|
||||
```php
|
||||
$curl->close();
|
||||
```
|
||||
|
||||
```php
|
||||
// Example access to curl object.
|
||||
curl_set_opt($curl->curl, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1');
|
||||
curl_close($curl->curl);
|
||||
```
|
||||
|
||||
```php
|
||||
// Example of downloading a file or any other content
|
||||
$curl = new Curl\Curl();
|
||||
// open the file where the request response should be written
|
||||
$file_handle = fopen($target_file, 'w+');
|
||||
// pass it to the curl resource
|
||||
$curl->setOpt(CURLOPT_FILE, $file_handle);
|
||||
// do any type of request
|
||||
$curl->get('https://github.com');
|
||||
// disable writing to file
|
||||
$curl->setOpt(CURLOPT_FILE, null);
|
||||
// close the file for writing
|
||||
fclose($file_handle);
|
||||
```
|
||||
|
||||
|
||||
## Testing
|
||||
|
||||
In order to test the library:
|
||||
|
||||
1. Create a fork
|
||||
2. Clone the fork to your machine
|
||||
3. Install the depencies `composer install`
|
||||
4. Run the unit tests `./vendor/bin/phpunit tests`
|
||||
36
vendor/curl/curl/composer.json
vendored
36
vendor/curl/curl/composer.json
vendored
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"name": "curl/curl",
|
||||
"description": "cURL class for PHP",
|
||||
"keywords": ["dot", "curl"],
|
||||
"homepage": "https://github.com/php-mod/curl",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "php-curl-class",
|
||||
"homepage": "https://github.com/php-curl-class"
|
||||
},
|
||||
{
|
||||
"name": "Hassan Amouhzi",
|
||||
"email": "hassan@anezi.net",
|
||||
"homepage": "http://hassan.amouhzi.com"
|
||||
},
|
||||
{
|
||||
"name": "user52",
|
||||
"homepage": "https://github.com/user52"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^5.6 | ^7.0",
|
||||
"ext-curl": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.7",
|
||||
"squizlabs/php_codesniffer": "~2.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Curl": "src/"
|
||||
}
|
||||
}
|
||||
}
|
||||
24
vendor/curl/curl/phpunit.xml.dist
vendored
24
vendor/curl/curl/phpunit.xml.dist
vendored
@@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="true"
|
||||
verbose="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
>
|
||||
<php>
|
||||
<ini name="display_errors" value="on"/>
|
||||
</php>
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="PHP MP4Box Tests Suite">
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
</phpunit>
|
||||
719
vendor/curl/curl/src/Curl/Curl.php
vendored
719
vendor/curl/curl/src/Curl/Curl.php
vendored
@@ -1,719 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Curl;
|
||||
|
||||
/**
|
||||
* An object-oriented wrapper of the PHP cURL extension.
|
||||
*
|
||||
* This library requires to have the php cURL extensions installed:
|
||||
* https://php.net/manual/curl.setup.php
|
||||
*
|
||||
* Example of making a get request with parameters:
|
||||
*
|
||||
* ```php
|
||||
* $curl = new Curl\Curl();
|
||||
* $curl->get('http://www.example.com/search', array(
|
||||
* 'q' => 'keyword',
|
||||
* ));
|
||||
* ```
|
||||
*
|
||||
* Example post request with post data:
|
||||
*
|
||||
* ```php
|
||||
* $curl = new Curl\Curl();
|
||||
* $curl->post('http://www.example.com/login/', array(
|
||||
* 'username' => 'myusername',
|
||||
* 'password' => 'mypassword',
|
||||
* ));
|
||||
* ```
|
||||
*
|
||||
* @see https://php.net/manual/curl.setup.php
|
||||
*/
|
||||
class Curl
|
||||
{
|
||||
// The HTTP authentication method(s) to use.
|
||||
|
||||
/**
|
||||
* @var string Type AUTH_BASIC
|
||||
*/
|
||||
const AUTH_BASIC = CURLAUTH_BASIC;
|
||||
|
||||
/**
|
||||
* @var string Type AUTH_DIGEST
|
||||
*/
|
||||
const AUTH_DIGEST = CURLAUTH_DIGEST;
|
||||
|
||||
/**
|
||||
* @var string Type AUTH_GSSNEGOTIATE
|
||||
*/
|
||||
const AUTH_GSSNEGOTIATE = CURLAUTH_GSSNEGOTIATE;
|
||||
|
||||
/**
|
||||
* @var string Type AUTH_NTLM
|
||||
*/
|
||||
const AUTH_NTLM = CURLAUTH_NTLM;
|
||||
|
||||
/**
|
||||
* @var string Type AUTH_ANY
|
||||
*/
|
||||
const AUTH_ANY = CURLAUTH_ANY;
|
||||
|
||||
/**
|
||||
* @var string Type AUTH_ANYSAFE
|
||||
*/
|
||||
const AUTH_ANYSAFE = CURLAUTH_ANYSAFE;
|
||||
|
||||
/**
|
||||
* @var string The user agent name which is set when making a request
|
||||
*/
|
||||
const USER_AGENT = 'PHP Curl/1.9 (+https://github.com/php-mod/curl)';
|
||||
|
||||
private $_cookies = array();
|
||||
|
||||
private $_headers = array();
|
||||
|
||||
/**
|
||||
* @var resource Contains the curl resource created by `curl_init()` function
|
||||
*/
|
||||
public $curl;
|
||||
|
||||
/**
|
||||
* @var bool Whether an error occured or not
|
||||
*/
|
||||
public $error = false;
|
||||
|
||||
/**
|
||||
* @var int Contains the error code of the curren request, 0 means no error happend
|
||||
*/
|
||||
public $error_code = 0;
|
||||
|
||||
/**
|
||||
* @var string If the curl request failed, the error message is contained
|
||||
*/
|
||||
public $error_message = null;
|
||||
|
||||
/**
|
||||
* @var bool Whether an error occured or not
|
||||
*/
|
||||
public $curl_error = false;
|
||||
|
||||
/**
|
||||
* @var int Contains the error code of the curren request, 0 means no error happend.
|
||||
* @see https://curl.haxx.se/libcurl/c/libcurl-errors.html
|
||||
*/
|
||||
public $curl_error_code = 0;
|
||||
|
||||
/**
|
||||
* @var string If the curl request failed, the error message is contained
|
||||
*/
|
||||
public $curl_error_message = null;
|
||||
|
||||
/**
|
||||
* @var bool Whether an error occured or not
|
||||
*/
|
||||
public $http_error = false;
|
||||
|
||||
/**
|
||||
* @var int Contains the status code of the current processed request.
|
||||
*/
|
||||
public $http_status_code = 0;
|
||||
|
||||
/**
|
||||
* @var string If the curl request failed, the error message is contained
|
||||
*/
|
||||
public $http_error_message = null;
|
||||
|
||||
/**
|
||||
* @var string|array TBD (ensure type) Contains the request header informations
|
||||
*/
|
||||
public $request_headers = null;
|
||||
|
||||
/**
|
||||
* @var string|array TBD (ensure type) Contains the response header informations
|
||||
*/
|
||||
public $response_headers = array();
|
||||
|
||||
/**
|
||||
* @var string Contains the response from the curl request
|
||||
*/
|
||||
public $response = null;
|
||||
|
||||
/**
|
||||
* @var bool Whether the current section of response headers is after 'HTTP/1.1 100 Continue'
|
||||
*/
|
||||
protected $response_header_continue = false;
|
||||
|
||||
/**
|
||||
* Constructor ensures the available curl extension is loaded.
|
||||
*
|
||||
* @throws \ErrorException
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if (!extension_loaded('curl')) {
|
||||
throw new \ErrorException('The cURL extensions is not loaded, make sure you have installed the cURL extension: https://php.net/manual/curl.setup.php');
|
||||
}
|
||||
|
||||
$this->init();
|
||||
}
|
||||
|
||||
// private methods
|
||||
|
||||
/**
|
||||
* Initializer for the curl resource.
|
||||
*
|
||||
* Is called by the __construct() of the class or when the curl request is reseted.
|
||||
* @return self
|
||||
*/
|
||||
private function init()
|
||||
{
|
||||
$this->curl = curl_init();
|
||||
$this->setUserAgent(self::USER_AGENT);
|
||||
$this->setOpt(CURLINFO_HEADER_OUT, true);
|
||||
$this->setOpt(CURLOPT_HEADER, false);
|
||||
$this->setOpt(CURLOPT_RETURNTRANSFER, true);
|
||||
$this->setOpt(CURLOPT_HEADERFUNCTION, array($this, 'addResponseHeaderLine'));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle writing the response headers
|
||||
*
|
||||
* @param resource $curl The current curl resource
|
||||
* @param string $header_line A line from the list of response headers
|
||||
*
|
||||
* @return int Returns the length of the $header_line
|
||||
*/
|
||||
public function addResponseHeaderLine($curl, $header_line)
|
||||
{
|
||||
$trimmed_header = trim($header_line, "\r\n");
|
||||
|
||||
if ($trimmed_header === "") {
|
||||
$this->response_header_continue = false;
|
||||
} elseif (strtolower($trimmed_header) === 'http/1.1 100 continue') {
|
||||
$this->response_header_continue = true;
|
||||
} elseif (!$this->response_header_continue) {
|
||||
$this->response_headers[] = $trimmed_header;
|
||||
}
|
||||
|
||||
return strlen($header_line);
|
||||
}
|
||||
|
||||
// protected methods
|
||||
|
||||
/**
|
||||
* Execute the curl request based on the respectiv settings.
|
||||
*
|
||||
* @return int Returns the error code for the current curl request
|
||||
*/
|
||||
protected function exec()
|
||||
{
|
||||
$this->response_headers = array();
|
||||
$this->response = curl_exec($this->curl);
|
||||
$this->curl_error_code = curl_errno($this->curl);
|
||||
$this->curl_error_message = curl_error($this->curl);
|
||||
$this->curl_error = !($this->curl_error_code === 0);
|
||||
$this->http_status_code = curl_getinfo($this->curl, CURLINFO_HTTP_CODE);
|
||||
$this->http_error = in_array(floor($this->http_status_code / 100), array(4, 5));
|
||||
$this->error = $this->curl_error || $this->http_error;
|
||||
$this->error_code = $this->error ? ($this->curl_error ? $this->curl_error_code : $this->http_status_code) : 0;
|
||||
$this->request_headers = preg_split('/\r\n/', curl_getinfo($this->curl, CURLINFO_HEADER_OUT), null, PREG_SPLIT_NO_EMPTY);
|
||||
$this->http_error_message = $this->error ? (isset($this->response_headers['0']) ? $this->response_headers['0'] : '') : '';
|
||||
$this->error_message = $this->curl_error ? $this->curl_error_message : $this->http_error_message;
|
||||
|
||||
return $this->error_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|object|string $data
|
||||
*/
|
||||
protected function preparePayload($data)
|
||||
{
|
||||
$this->setOpt(CURLOPT_POST, true);
|
||||
|
||||
if (is_array($data) || is_object($data)) {
|
||||
$skip = false;
|
||||
foreach ($data as $key => $value) {
|
||||
// If a value is an instance of CurlFile skip the http_build_query
|
||||
// see issue https://github.com/php-mod/curl/issues/46
|
||||
// suggestion from: https://stackoverflow.com/a/36603038/4611030
|
||||
if ($value instanceof \CurlFile) {
|
||||
$skip = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$skip) {
|
||||
$data = http_build_query($data);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setOpt(CURLOPT_POSTFIELDS, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set auth options for the current request.
|
||||
*
|
||||
* Available auth types are:
|
||||
*
|
||||
* + self::AUTH_BASIC
|
||||
* + self::AUTH_DIGEST
|
||||
* + self::AUTH_GSSNEGOTIATE
|
||||
* + self::AUTH_NTLM
|
||||
* + self::AUTH_ANY
|
||||
* + self::AUTH_ANYSAFE
|
||||
*
|
||||
* @param int $httpauth The type of authentication
|
||||
*/
|
||||
protected function setHttpAuth($httpauth)
|
||||
{
|
||||
$this->setOpt(CURLOPT_HTTPAUTH, $httpauth);
|
||||
}
|
||||
|
||||
// public methods
|
||||
|
||||
/**
|
||||
* @deprecated calling exec() directly is discouraged
|
||||
*/
|
||||
public function _exec()
|
||||
{
|
||||
return $this->exec();
|
||||
}
|
||||
|
||||
// functions
|
||||
|
||||
/**
|
||||
* Make a get request with optional data.
|
||||
*
|
||||
* The get request has no body data, the data will be correctly added to the $url with the http_build_query() method.
|
||||
*
|
||||
* @param string $url The url to make the get request for
|
||||
* @param array $data Optional arguments who are part of the url
|
||||
* @return self
|
||||
*/
|
||||
public function get($url, $data = array())
|
||||
{
|
||||
if (count($data) > 0) {
|
||||
$this->setOpt(CURLOPT_URL, $url.'?'.http_build_query($data));
|
||||
} else {
|
||||
$this->setOpt(CURLOPT_URL, $url);
|
||||
}
|
||||
$this->setOpt(CURLOPT_HTTPGET, true);
|
||||
$this->exec();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a post request with optional post data.
|
||||
*
|
||||
* @param string $url The url to make the post request
|
||||
* @param array $data Post data to pass to the url
|
||||
* @return self
|
||||
*/
|
||||
public function post($url, $data = array())
|
||||
{
|
||||
$this->setOpt(CURLOPT_URL, $url);
|
||||
$this->preparePayload($data);
|
||||
$this->exec();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a put request with optional data.
|
||||
*
|
||||
* The put request data can be either sent via payload or as get paramters of the string.
|
||||
*
|
||||
* @param string $url The url to make the put request
|
||||
* @param array $data Optional data to pass to the $url
|
||||
* @param bool $payload Whether the data should be transmitted trough payload or as get parameters of the string
|
||||
* @return self
|
||||
*/
|
||||
public function put($url, $data = array(), $payload = false)
|
||||
{
|
||||
if (! empty($data)) {
|
||||
if ($payload === false) {
|
||||
$url .= '?'.http_build_query($data);
|
||||
} else {
|
||||
$this->preparePayload($data);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setOpt(CURLOPT_URL, $url);
|
||||
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'PUT');
|
||||
$this->exec();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a patch request with optional data.
|
||||
*
|
||||
* The patch request data can be either sent via payload or as get paramters of the string.
|
||||
*
|
||||
* @param string $url The url to make the patch request
|
||||
* @param array $data Optional data to pass to the $url
|
||||
* @param bool $payload Whether the data should be transmitted trough payload or as get parameters of the string
|
||||
* @return self
|
||||
*/
|
||||
public function patch($url, $data = array(), $payload = false)
|
||||
{
|
||||
if (! empty($data)) {
|
||||
if ($payload === false) {
|
||||
$url .= '?'.http_build_query($data);
|
||||
} else {
|
||||
$this->preparePayload($data);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setOpt(CURLOPT_URL, $url);
|
||||
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'PATCH');
|
||||
$this->exec();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a delete request with optional data.
|
||||
*
|
||||
* @param string $url The url to make the delete request
|
||||
* @param array $data Optional data to pass to the $url
|
||||
* @param bool $payload Whether the data should be transmitted trough payload or as get parameters of the string
|
||||
* @return self
|
||||
*/
|
||||
public function delete($url, $data = array(), $payload = false)
|
||||
{
|
||||
if (! empty($data)) {
|
||||
if ($payload === false) {
|
||||
$url .= '?'.http_build_query($data);
|
||||
} else {
|
||||
$this->preparePayload($data);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setOpt(CURLOPT_URL, $url);
|
||||
$this->setOpt(CURLOPT_CUSTOMREQUEST, 'DELETE');
|
||||
$this->exec();
|
||||
return $this;
|
||||
}
|
||||
|
||||
// setters
|
||||
|
||||
/**
|
||||
* Pass basic auth data.
|
||||
*
|
||||
* If the the rquested url is secured by an httaccess basic auth mechanism you can use this method to provided the auth data.
|
||||
*
|
||||
* ```php
|
||||
* $curl = new Curl();
|
||||
* $curl->setBasicAuthentication('john', 'doe');
|
||||
* $curl->get('http://example.com/secure.php');
|
||||
* ```
|
||||
*
|
||||
* @param string $username The username for the authentification
|
||||
* @param string $password The password for the given username for the authentification
|
||||
* @return self
|
||||
*/
|
||||
public function setBasicAuthentication($username, $password)
|
||||
{
|
||||
$this->setHttpAuth(self::AUTH_BASIC);
|
||||
$this->setOpt(CURLOPT_USERPWD, $username.':'.$password);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide optional header informations.
|
||||
*
|
||||
* In order to pass optional headers by key value pairing:
|
||||
*
|
||||
* ```php
|
||||
* $curl = new Curl();
|
||||
* $curl->setHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
* $curl->get('http://example.com/request.php');
|
||||
* ```
|
||||
*
|
||||
* @param string $key The header key
|
||||
* @param string $value The value for the given header key
|
||||
* @return self
|
||||
*/
|
||||
public function setHeader($key, $value)
|
||||
{
|
||||
$this->_headers[$key] = $key.': '.$value;
|
||||
$this->setOpt(CURLOPT_HTTPHEADER, array_values($this->_headers));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a User Agent.
|
||||
*
|
||||
* In order to provide you cusomtized user agent name you can use this method.
|
||||
*
|
||||
* ```php
|
||||
* $curl = new Curl();
|
||||
* $curl->setUserAgent('My John Doe Agent 1.0');
|
||||
* $curl->get('http://example.com/request.php');
|
||||
* ```
|
||||
*
|
||||
* @param string $useragent The name of the user agent to set for the current request
|
||||
* @return self
|
||||
*/
|
||||
public function setUserAgent($useragent)
|
||||
{
|
||||
$this->setOpt(CURLOPT_USERAGENT, $useragent);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Call setReferer() instead
|
||||
*/
|
||||
public function setReferrer($referrer)
|
||||
{
|
||||
$this->setReferer($referrer);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the HTTP referer header.
|
||||
*
|
||||
* The $referer informations can help identify the requested client where the requested was made.
|
||||
*
|
||||
* @param string $referer An url to pass and will be set as referer header
|
||||
* @return self
|
||||
*/
|
||||
public function setReferer($referer)
|
||||
{
|
||||
$this->setOpt(CURLOPT_REFERER, $referer);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set contents of HTTP Cookie header.
|
||||
*
|
||||
* @param string $key The name of the cookie
|
||||
* @param string $value The value for the provided cookie name
|
||||
* @return self
|
||||
*/
|
||||
public function setCookie($key, $value)
|
||||
{
|
||||
$this->_cookies[$key] = $value;
|
||||
$this->setOpt(CURLOPT_COOKIE, http_build_query($this->_cookies, '', '; '));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set customized curl options.
|
||||
*
|
||||
* To see a full list of options: http://php.net/curl_setopt
|
||||
*
|
||||
* @see http://php.net/curl_setopt
|
||||
*
|
||||
* @param int $option The curl option constante e.g. `CURLOPT_AUTOREFERER`, `CURLOPT_COOKIESESSION`
|
||||
* @param mixed $value The value to pass for the given $option
|
||||
*/
|
||||
public function setOpt($option, $value)
|
||||
{
|
||||
return curl_setopt($this->curl, $option, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get customized curl options.
|
||||
*
|
||||
* To see a full list of options: http://php.net/curl_getinfo
|
||||
*
|
||||
* @see http://php.net/curl_getinfo
|
||||
*
|
||||
* @param int $option The curl option constante e.g. `CURLOPT_AUTOREFERER`, `CURLOPT_COOKIESESSION`
|
||||
* @param mixed $value The value to check for the given $option
|
||||
*/
|
||||
public function getOpt($option)
|
||||
{
|
||||
return curl_getinfo($this->curl, $option);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the endpoint set for curl
|
||||
*
|
||||
* @see http://php.net/curl_getinfo
|
||||
*
|
||||
* @return string of endpoint
|
||||
*/
|
||||
public function getEndpoint()
|
||||
{
|
||||
return $this->getOpt(CURLINFO_EFFECTIVE_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable verbositiy.
|
||||
*
|
||||
* @todo As to keep naming convention it should be renamed to `setVerbose()`
|
||||
*
|
||||
* @param string $on
|
||||
* @return self
|
||||
*/
|
||||
public function verbose($on = true)
|
||||
{
|
||||
$this->setOpt(CURLOPT_VERBOSE, $on);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all curl options.
|
||||
*
|
||||
* In order to make multiple requests with the same curl object all settings requires to be reset.
|
||||
* @return self
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->close();
|
||||
$this->_cookies = array();
|
||||
$this->_headers = array();
|
||||
$this->error = false;
|
||||
$this->error_code = 0;
|
||||
$this->error_message = null;
|
||||
$this->curl_error = false;
|
||||
$this->curl_error_code = 0;
|
||||
$this->curl_error_message = null;
|
||||
$this->http_error = false;
|
||||
$this->http_status_code = 0;
|
||||
$this->http_error_message = null;
|
||||
$this->request_headers = null;
|
||||
$this->response_headers = array();
|
||||
$this->response = null;
|
||||
$this->init();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closing the current open curl resource.
|
||||
* @return self
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if (is_resource($this->curl)) {
|
||||
curl_close($this->curl);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the connection when the Curl object will be destroyed.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Was an 'info' header returned.
|
||||
* @return bool
|
||||
*/
|
||||
public function isInfo()
|
||||
{
|
||||
return $this->http_status_code >= 100 && $this->http_status_code < 200;
|
||||
}
|
||||
|
||||
/**
|
||||
* Was an 'OK' response returned.
|
||||
* @return bool
|
||||
*/
|
||||
public function isSuccess()
|
||||
{
|
||||
return $this->http_status_code >= 200 && $this->http_status_code < 300;
|
||||
}
|
||||
|
||||
/**
|
||||
* Was a 'redirect' returned.
|
||||
* @return bool
|
||||
*/
|
||||
public function isRedirect()
|
||||
{
|
||||
return $this->http_status_code >= 300 && $this->http_status_code < 400;
|
||||
}
|
||||
|
||||
/**
|
||||
* Was an 'error' returned (client error or server error).
|
||||
* @return bool
|
||||
*/
|
||||
public function isError()
|
||||
{
|
||||
return $this->http_status_code >= 400 && $this->http_status_code < 600;
|
||||
}
|
||||
|
||||
/**
|
||||
* Was a 'client error' returned.
|
||||
* @return bool
|
||||
*/
|
||||
public function isClientError()
|
||||
{
|
||||
return $this->http_status_code >= 400 && $this->http_status_code < 500;
|
||||
}
|
||||
|
||||
/**
|
||||
* Was a 'server error' returned.
|
||||
* @return bool
|
||||
*/
|
||||
public function isServerError()
|
||||
{
|
||||
return $this->http_status_code >= 500 && $this->http_status_code < 600;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific response header key or all values from the response headers array.
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* ```php
|
||||
* $curl = (new Curl())->get('http://example.com');
|
||||
*
|
||||
* echo $curl->getResponseHeaders('Content-Type');
|
||||
* ```
|
||||
*
|
||||
* Or in order to dump all keys with the given values use:
|
||||
*
|
||||
* ```php
|
||||
* $curl = (new Curl())->get('http://example.com');
|
||||
*
|
||||
* var_dump($curl->getResponseHeaders());
|
||||
* ```
|
||||
*
|
||||
* @param string $headerKey Optional key to get from the array.
|
||||
* @return bool|string
|
||||
* @since 1.9
|
||||
*/
|
||||
public function getResponseHeaders($headerKey = null)
|
||||
{
|
||||
$headers = array();
|
||||
$headerKey = strtolower($headerKey);
|
||||
|
||||
foreach ($this->response_headers as $header) {
|
||||
$parts = explode(":", $header, 2);
|
||||
|
||||
$key = isset($parts[0]) ? $parts[0] : null;
|
||||
$value = isset($parts[1]) ? $parts[1] : null;
|
||||
|
||||
$headers[trim(strtolower($key))] = trim($value);
|
||||
}
|
||||
|
||||
if ($headerKey) {
|
||||
return isset($headers[$headerKey]) ? $headers[$headerKey] : false;
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
|
||||
public function getResponse()
|
||||
{
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
public function getErrorCode()
|
||||
{
|
||||
return $this->curl_error_code;
|
||||
}
|
||||
|
||||
public function getErrorMessage()
|
||||