diff --git a/app/AutoReseed.php b/app/AutoReseed.php index 173299a..dbddb79 100644 --- a/app/AutoReseed.php +++ b/app/AutoReseed.php @@ -5,9 +5,7 @@ namespace IYUU; use Curl\Curl; -use IYUU\Client\AbstractClientInterface; -use IYUU\Client\qBittorrent\qBittorrent; -use IYUU\Client\Transmission\TransmissionRPC; +use IYUU\Client\AbstractClient; use IYUU\Library\IFile; use IYUU\Library\Oauth; use IYUU\Library\Table; @@ -26,27 +24,31 @@ class AutoReseed * RPC连接池 * @var array */ - public static $links = []; + public static $links = array(); /** * 客户端配置 * @var array */ - public static $clients = []; + public static $clients = array(); + /** + * 站点列表 + */ + public static $sites = array(); /** * 不辅种的站点 'ourbits','hdchina' * @var array */ - public static $noReseed = []; + public static $noReseed = array(); /** * 不转移的站点 'hdarea','hdbd' * @var array */ - public static $noMove = ['hdarea']; + public static $noMove = array('hdarea'); /** * cookie检查 * @var array */ - public static $cookieCheck = ['hdchina','hdcity']; + public static $cookieCheck = array('hdchina','hdcity'); /** * 缓存路径 * @var string @@ -141,6 +143,7 @@ class AutoReseed $sites = isset($rs['data']['sites']) && $rs['data']['sites'] ? $rs['data']['sites'] : false; // 数据写入本地 if ($sites) { + self::$sites = array_column($sites, null, 'id'); $json = array_column($sites, null, 'site'); ksort($json); $json = json_encode($json, JSON_UNESCAPED_UNICODE); @@ -184,24 +187,13 @@ class AutoReseed foreach (self::$clients as $k => $v) { // 跳过未配置的客户端 if (empty($v['username']) || empty($v['password'])) { - unset(self::$clients[$k]); + self::$links[$k] = array(); echo "clients_".$k." 用户名或密码未配置,已跳过".PHP_EOL.PHP_EOL; 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 '[Links ERROR] '.$v['type']; - exit(1); - break; - } - /** @var AbstractClientInterface $client */ + $client = AbstractClient::create($v); + self::$links[$k]['BT_backup'] = isset($v['BT_backup']) && $v['BT_backup'] ? $v['BT_backup'] : ''; self::$links[$k]['type'] = $v['type']; self::$links[$k]['rpc'] = $client; $result = $client->status(); @@ -218,104 +210,7 @@ class AutoReseed } 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暂时无响应,请稍后重试!".PHP_EOL; - break; - } - if (empty($result->arguments->torrents)) { - echo "未获取到数据,请多多保种,然后重试!".PHP_EOL; - break; - } - // 对象转数组 - $res = object_array($result->arguments->torrents); - // 过滤,只保留正常做种 - $res = array_filter($res, "filterStatus"); - if (empty($res)) { - echo "未获取到需要辅种的数据,请多多保种,然后重试!".PHP_EOL; - break; - } - // 提取数组: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 "未获取到数据,请多多保种,然后重试!".PHP_EOL; - break; - } - // 过滤,只保留正常做种 - $res = array_filter($res, "qbfilterStatus"); - if (empty($res)) { - echo "未获取到需要辅种的数据,请多多保种,然后重试!".PHP_EOL; - break; - } - // 提取数组: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 '[get ERROR] '.$v['type'] . PHP_EOL; - exit(1); - break; - } - } catch (\Exception $e) { - echo '[get ERROR] ' . $e->getMessage() . PHP_EOL; - exit(1); - } - } - return $hashArray; - } + /** * @brief 添加下载任务 * @param string $torrent 种子元数据 @@ -371,7 +266,8 @@ class AutoReseed $result = self::$links[$rpcKey]['rpc']->add($torrent, $save_path, $extra_options); // 种子URL添加下载任务 } else { $extra_options['name'] = 'torrents'; - $extra_options['filename'] = rand(1, 4294967200).'.torrent'; + $rand = mt_rand(10, 42949672); + $extra_options['filename'] = intval($rand).'.torrent'; $result = self::$links[$rpcKey]['rpc']->add_metainfo($torrent, $save_path, $extra_options); // 种子元数据添加下载任务 } if ($is_url) { @@ -395,71 +291,73 @@ class AutoReseed } /** * @brief 提交种子hash给远端API,用来获取辅种数据 - * @param array $hashArray 种子hash数组 * @return */ - public static function call($hashArray = array()) + public static function call() { - $resArray = array(); - // 签名 - $hashArray['sign'] = Oauth::getSign(); - $hashArray['timestamp'] = time(); - $hashArray['version'] = self::VER; - // 写请求日志 - wlog($hashArray, 'hashString'); if (self::$move!==null) { - self::move($hashArray); + self::move(); } - self::reseed($hashArray); + self::reseed(); self::wechatMessage(); } /** * IYUUAutoReseed辅种 */ - public static function reseed($hashArray = array()) + public static function reseed() { global $configALL; - $sites = array(); - // 前置过滤 - if (self::$move!==null && self::$move[1]==2) { - foreach ($hashArray['hash'] as $key => $json) { - if ($key != 'clients_'.self::$move[0]) { - $hashArray['hash'][$key] = '[]'; - } - } - } - echo "正在提交辅种信息……".PHP_EOL; - $res = self::$curl->post(self::$apiUrl . self::$endpoints['reseed'], $hashArray); - $res = json_decode($res->response, true); - $resArray = isset($res['data']) && $res['data'] ? $res['data'] : array(); - // 写返回日志 - wlog($res, 'reseed'); - // 判断返回值 - if (isset($resArray['errmsg']) && ($resArray['errmsg'] == 'ok')) { - echo "辅种信息提交成功!!!".PHP_EOL.PHP_EOL; - } else { - $errmsg = isset($res['msg']) && $res['msg'] ? $res['msg'] : '远端服务器无响应,请稍后重试!'; - echo '-----辅种失败,原因:' .$errmsg.PHP_EOL.PHP_EOL; - exit(1); - } - // 可辅种站点信息 - $sites = $resArray['sites']; - #p($sites); // 支持站点数量 - self::$wechatMsg['sitesCount'] = count($sites); + self::$wechatMsg['sitesCount'] = count(self::$sites); + $sites = self::$sites; // 按客户端循环辅种 开始 foreach (self::$links as $k => $v) { - if (empty($resArray['clients_'.$k])) { - echo "clients_".$k."没有查询到可辅种数据".PHP_EOL.PHP_EOL; + if (empty($v)) { + echo "clients_".$k." 用户名或密码未配置,已跳过".PHP_EOL.PHP_EOL; + continue; + } + // 过滤无需辅种的客户端 + if (self::$move!==null && self::$move[1]==2) { + echo "clients_".$k." 根据设置无需辅种,已跳过!"; + continue; + } + echo "正在从下载器 clients_".$k." 获取种子哈希……".PHP_EOL; + $hashArray = self::$links[$k]['rpc']->getList(); + if (empty($hashArray)) { + // 失败 + continue; + } else { + $infohash_Dir = $hashArray['hashString']; + #p($infohash_Dir); + unset($hashArray['hashString']); + // 签名 + $hashArray['sign'] = Oauth::getSign(); + $hashArray['timestamp'] = time(); + $hashArray['version'] = self::VER; + // 写请求日志 + wlog($hashArray, 'hashString'.$k); + self::$wechatMsg['hashCount'] +=count($infohash_Dir); + } + echo "正在向服务器提交 clients_".$k." 种子哈希……".PHP_EOL; + $res = self::$curl->post(self::$apiUrl . self::$endpoints['infohash'], $hashArray); + $res = json_decode($res->response, true); + // 写返回日志 + wlog($res, 'reseed'.$k); + $reseed = isset($res['data']) && $res['data'] ? $res['data'] : array(); + if (empty($reseed)) { + echo "clients_".$k." 没有查询到可辅种数据".PHP_EOL.PHP_EOL; + continue; + } + // 判断返回值 + if (empty($res['msg'])) { + echo "clients_".$k." 辅种数据下载成功!!!".PHP_EOL.PHP_EOL; + } else { + $errmsg = isset($res['msg']) && $res['msg'] ? $res['msg'] : '远端服务器无响应,请稍后重试!'; + echo '-----辅种失败,原因:' .$errmsg.PHP_EOL.PHP_EOL; continue; } - $reseed = $infohash_Dir = array(); - // info_hash与下载目录对应表 - $infohash_Dir = self::$links[$k]['hash']; - #p($infohash_Dir); // 当前客户端可辅种数据 - $reseed = $resArray['clients_'.$k]; foreach ($reseed as $info_hash => $vv) { // 当前种子哈希对应的目录 $downloadDir = $infohash_Dir[$info_hash]; @@ -535,6 +433,9 @@ class AutoReseed $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); + if (strpos($details_html, '没有该ID的种子') != false) { + # code... 错误通知 + } print "种子详情页:".$details_url.PHP_EOL; // 提取种子下载地址 $download_page = str_replace('{}', '', $sites[$sitesID]['download_page']); @@ -554,7 +455,6 @@ class AutoReseed sleep(1); } while (--$t > 0); ff($siteName. '站点,辅种时触发第一次下载提示!'); - $configALL[$siteName]['limit'] = 1; self::$noReseed[] = 'hdchina'; } if (strpos($url, '系统检测到过多的种子下载请求') != false) { @@ -646,223 +546,116 @@ class AutoReseed /** * IYUUAutoReseed做种客户端转移 */ - public static function move($hashArray = array()) + public static function move() { global $configALL; - $sites = array(); - // 前置过滤:2020年1月26日15:52:41 - self::hashFilter($hashArray['hash']); - // 发起请求 - echo "正在提交转移信息……".PHP_EOL; - $res = self::$curl->post(self::$apiUrl . self::$endpoints['move'], $hashArray); - $res = json_decode($res->response, true); - $resArray = isset($res['data']) && $res['data'] ? $res['data'] : array(); - // 写日志 - wlog($res, 'move'); - // 判断返回值 - if (isset($resArray['errmsg']) && ($resArray['errmsg'] == 'ok')) { - echo "转移数据返回:成功!!!".PHP_EOL.PHP_EOL; - } else { - $errmsg = isset($res['msg']) && $res['msg'] ? $res['msg'] : '远端服务器无响应,请稍后重试!'; - echo '-----转移请求失败,原因:' .$errmsg.PHP_EOL.PHP_EOL; - exit(1); - } - // 可辅种站点信息 - $sites = $resArray['sites']; - // 支持站点数量 - #self::$wechatMsg['sitesCount'] = count($sites); - // 按客户端移动 开始 + $sites = self::$sites; foreach (self::$links as $k => $v) { - if (self::$move!=null && self::$move[0] == $k) { + if (self::$move[0] == $k) { echo "clients_".$k."是目标转移客户端,避免冲突,已跳过!".PHP_EOL.PHP_EOL; continue; } - if (empty($resArray['clients_'.$k])) { - echo "clients_".$k."没有查询到可转移数据".PHP_EOL.PHP_EOL; + echo "正在从下载器 clients_".$k." 获取种子哈希……".PHP_EOL; + $hashArray = self::$links[$k]['rpc']->getList($move); + #p($move);exit; + if (empty($hashArray)) { + // 失败 continue; + } else { + $infohash_Dir = $hashArray['hashString']; + // 写日志 + wlog($hashArray, 'move'.$k); } - $infohash_Dir = $move = array(); - // info_hash与下载目录对应表 - $infohash_Dir = self::$links[$k]['hash']; - // 当前客户端可辅种数据 - $move = $resArray['clients_'.$k]; - foreach ($move as $info_hash => $value) { - $_url = $url = ''; - $download_page = $details_url = ''; - // 匹配的辅种数据累加 - #self::$wechatMsg['reseedCount']++; - // 当前种子哈希对应的目录 - $downloadDir = $infohash_Dir[$info_hash]; - // 站点id - $sitesID = $value['sid']; - // 站点名 - $siteName = $sites[$sitesID]['site']; - // 页面规则 - $download_page = str_replace('{}', $value['torrent_id'], $sites[$sitesID]['download_page']); - $_url = 'https://' .$sites[$sitesID]['base_url']. '/' .$download_page; - echo "clients_".$k."正在转移... {$siteName}".PHP_EOL; - /** - * 前置检测 - */ - // passkey检测 - if (empty($configALL[$siteName]['passkey'])) { - echo '-------因当前' .$siteName. "站点未设置passkey,已跳过!!".PHP_EOL.PHP_EOL; - #self::$wechatMsg['reseedSkip']++; - continue; - } - // cookie检测 - if (in_array($siteName, self::$cookieCheck) && empty($configALL[$siteName]['cookie'])) { - echo '-------因当前' .$siteName. '站点未设置cookie,已跳过!!' .PHP_EOL.PHP_EOL; - #self::$wechatMsg['reseedSkip']++; - continue; - } - // 流控检测 - if (isset($configALL[$siteName]['limit'])) { - echo "-------因当前" .$siteName. "站点触发流控,已跳过!! {$_url}".PHP_EOL.PHP_EOL; - // 流控日志 - if ($siteName == 'hdchina') { - $details_page = str_replace('{}', $value['torrent_id'], 'details.php?id={}&hit=1'); - $_url = 'https://' .$sites[$sitesID]['base_url']. '/' .$details_page; - } - wlog('clients_'.$k.PHP_EOL.$downloadDir.PHP_EOL."-------因当前" .$siteName. "站点触发流控,已跳过!! {$_url}".PHP_EOL.PHP_EOL, 'MoveLimit'); - #self::$wechatMsg['reseedSkip']++; - continue; - } - // 历史转移检测 - if (is_file(self::$cacheMove . $info_hash.'.txt')) { - echo '-------当前种子上次已成功转移,已跳过! '.$_url.PHP_EOL.PHP_EOL; - #self::$wechatMsg['reseedPass']++; - continue; - } - // 不转移的站点检测 - if (in_array($siteName, self::$noMove)) { - echo '-------已跳过不转移的站点 ' .$siteName. '!! ' .PHP_EOL.PHP_EOL; - #self::$wechatMsg['reseedSkip']++; - continue; - } - /** - * 种子URL组合方式区分 - */ - $url = self::getTorrentUrl($siteName, $_url); - /** - * 转移核心 - */ - // 特殊站点:推送给下载器种子元数据 - switch ($siteName) { - case 'hdchina': - $cookie = isset($configALL[$siteName]['cookie']) ? $configALL[$siteName]['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.PHP_EOL; - // 提取种子下载地址 - $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.PHP_EOL; - $url = download($_url, $cookie, $userAgent); - if (strpos($url, '系统检测到过多的种子下载请求') != false) { - echo "当前站点触发人机验证,已加入排除列表".PHP_EOL; - ff($siteName. '站点,辅种时触发人机验证!'); - $configALL[$siteName]['limit'] = 1; - } - break; - case 'hdcity': - $cookie = isset($configALL[$siteName]['cookie']) ? $configALL[$siteName]['cookie'] : ''; - $userAgent = $configALL['default']['userAgent']; - print "种子:".$_url.PHP_EOL; - if (isset($configALL[$siteName]['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[$siteName]['cuhash'] = substr($cuhashTemp, 0, strpos($cuhashTemp, '"')); - } - $url = $_url."&cuhash=". $configALL[$siteName]['cuhash']; - // 城市下载种子时会302转向 - $url = download($url, $cookie, $userAgent); - break; - default: - // 默认站点:推送给下载器种子URL链接 - break; - } - echo "种子URL已推送给下载器,下载器正在下载种子...".PHP_EOL; - // 实际路径与相对路径之间互转 + + // 前置过滤:移除转移成功的hash + $rs = self::hashFilter($infohash_Dir); + if ($rs) { + echo "clients_".$k." 全部转移成功,本次无需转移!".PHP_EOL.PHP_EOL; + continue; + } + + foreach ($infohash_Dir as $info_hash => $downloadDir) { + // 做种实际路径与相对路径之间互转 echo '转换前:'.$downloadDir.PHP_EOL; $downloadDir = self::pathReplace($downloadDir); echo '转换后:'.$downloadDir.PHP_EOL; if (is_null($downloadDir)) { die("全局配置的move数组内,路径转换参数配置错误,请重新配置!!!".PHP_EOL); } - $ret = false; - // 把拼接的种子URL,推送给下载器 - // 成功返回:true - $ret = self::add(self::$move[0], $url, $downloadDir); - // 按站点规范日志内容 - switch ($siteName) { - case 'hdchina': - $url = $details_url; + + // 种子扩展参数 + $extra_options = array(); + $path = self::$links[$k]['BT_backup']; + // 待删除种子 + $torrentDelete = ''; + + // 获取种子原文件的实际路径 + + switch ($v['type']) { + case 'transmission': + $torrentPath = $move[$info_hash]['torrentFile']; + $torrentDelete = $move[$info_hash]['id']; + // 脚本与tr不在一起的兼容性处理 + if (!is_file($torrentPath)) { + $torrentPath = str_replace("\\", "/", $torrentPath); + $torrentPath = $path . strrchr($torrentPath, '/'); + } break; - case 'hdcity': - $url = $_url; + case 'qBittorrent': + if (empty($path)) { + die("clients_".$k." 未设置种子的BT_backup目录,无法完成转移!"); + } + $torrentPath = $path .DS. $info_hash . '.torrent'; + $torrentDelete = $info_hash; + $extra_options['skip_checking'] = 'true'; //跳校验 break; default: + # code... break; } + + // 判断种子原文件是否存在 + if (!is_file($torrentPath)) { + die("clients_".$k." 的种子文件{$torrentPath}不存在,无法完成转移!"); + } + echo '存在种子:'.$torrentPath.PHP_EOL; + $torrent = file_get_contents($torrentPath); + // 正式开始转移 + echo "种子已推送给下载器,正在转移做种...".PHP_EOL; + $ret = false; + // 成功返回:true + $ret = self::add(self::$move[0], $torrent, $downloadDir, $extra_options); + /** * 转移成功的种子写日志 */ + $log = $info_hash.PHP_EOL.$torrentPath.PHP_EOL.$downloadDir.PHP_EOL.PHP_EOL; if ($ret) { + // 删除做种,不删资源 + #self::$links[$k]['rpc']->delete($torrentDelete); + // 转移成功的种子,以infohash为文件名,写入缓存 - wlog($url.PHP_EOL, $info_hash, self::$cacheMove); - wlog($url.PHP_EOL, 'MoveSuccess'); - // 成功累加 - #self::$wechatMsg['reseedSuccess']++; - continue; + wlog($log, $info_hash, self::$cacheMove); + wlog($log, 'MoveSuccess'.$k); } else { // 失败的种子 - wlog($url.PHP_EOL, 'MoveError'); - // 失败累加 - #self::$wechatMsg['reseedError']++; - continue; + wlog($log, 'MoveError'.$k); } - } + } } - // 按客户端循环辅种 结束 } /** * 过滤已转移的种子hash */ - public static function hashFilter(&$hash = array()) + public static function hashFilter(&$infohash_Dir = array()) { - foreach ($hash as $client => $json) { - $data = array(); - $data = json_decode($json, true); - if (empty($data)) { - continue; - } - foreach ($data as $key => $info_hash) { - if (is_file(self::$cacheMove . $info_hash.'.txt')) { - echo '-------当前种子上次已成功转移,前置过滤已跳过! ' .PHP_EOL.PHP_EOL; - unset($data[$key]); - } - } - if ($data) { - $data = array_values($data); + foreach ($infohash_Dir as $info_hash => $dir) { + if (is_file(self::$cacheMove . $info_hash.'.txt')) { + unset($infohash_Dir[$info_hash]); + echo '-------当前种子上次已成功转移,前置过滤已跳过! ' .PHP_EOL.PHP_EOL; } - $hash[$client] = json_encode($data, JSON_UNESCAPED_UNICODE); } - return $hash; + return empty($infohash_Dir) ? true : false; } /** * 实际路径与相对路径之间互相转换 diff --git a/app/Client/AbstractClient.php b/app/Client/AbstractClient.php new file mode 100644 index 0000000..7a0e406 --- /dev/null +++ b/app/Client/AbstractClient.php @@ -0,0 +1,63 @@ + + * Date: 2020-2-14 + * Time: 21:31:49 + */ + +namespace IYUU\Client; + +abstract class AbstractClient +{ + /** + * 公共方法:创建客户端实例 + */ + public static function create($config = array()) + { + $type = $config['type']; + $host = $config['host']; + $username = $config['username']; + $password = $config['password']; + + $className = "IYUU\Client\\" . $type . "\\" . $type; + if (class_exists($className)) { + echo $type." 客户端正在实例化!".PHP_EOL; + return new $className($host, $username, $password); + } else { + die($className.' 客户端不存在'); + } + } + + /** + * 查询Bittorrent客户端状态 + * + * @return string + */ + abstract public function status(); + + /** + * 获取种子列表 + * @return array( + 'hash' => string json, + 'sha1' => string, + 'hashString '=> array + ) + */ + abstract public function getList(&$move = array()); + + /** + * 添加种子连接 + */ + abstract public function add($torrent_url, $save_path = '', $extra_options = array()); + + /** + * 添加种子原数据 + */ + abstract public function add_metainfo($torrent_url, $save_path = '', $extra_options = array()); + + /** + * 删除种子 + */ + abstract public function delete($hash, $deleteFiles = false); +} diff --git a/app/Client/AbstractClientInterface.php b/app/Client/AbstractClientInterface.php deleted file mode 100644 index de4bec3..0000000 --- a/app/Client/AbstractClientInterface.php +++ /dev/null @@ -1,20 +0,0 @@ - * */ -class TransmissionRPC implements AbstractClientInterface +class Transmission extends AbstractClient { /** * 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 @@ -79,12 +73,6 @@ class TransmissionRPC implements AbstractClientInterface */ 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 @@ -139,29 +127,15 @@ class TransmissionRPC implements AbstractClientInterface /** * 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) + public function __construct($url = 'http://127.0.0.1:9091/transmission/rpc', $username = null, $password = null) { - // 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; } /** @@ -175,7 +149,7 @@ class TransmissionRPC implements AbstractClientInterface { 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); } @@ -191,7 +165,7 @@ class TransmissionRPC implements AbstractClientInterface { 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); } @@ -207,7 +181,7 @@ class TransmissionRPC implements AbstractClientInterface { 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); } @@ -223,7 +197,7 @@ class TransmissionRPC implements AbstractClientInterface { 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); } @@ -358,6 +332,16 @@ class TransmissionRPC implements AbstractClientInterface 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); + } + /** * Add a torrent using the raw torrent data * @@ -377,16 +361,6 @@ class TransmissionRPC implements AbstractClientInterface 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 * @@ -395,7 +369,7 @@ class TransmissionRPC implements AbstractClientInterface * @return mixed * @throws TransmissionRPCException */ - public function remove($ids, $delete_local_data = false) + public function delete($ids, $delete_local_data = false) { if (!is_array($ids)) { $ids = array($ids); @@ -594,7 +568,8 @@ class TransmissionRPC implements AbstractClientInterface $array[$index] = ($value ? 1 : 0); } // Store boolean values as 0 or 1 if (is_string($value)) { - if (mb_detect_encoding($value, "auto") !== 'UTF-8') { + $type = mb_detect_encoding($value, "auto"); + if ($type !== 'UTF-8') { $array[$index] = mb_convert_encoding($value, "UTF-8"); //utf8_encode( $value ); // Make sure all data is UTF-8 encoded for Transmission } @@ -603,50 +578,12 @@ class TransmissionRPC implements AbstractClientInterface 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 + * @return array * @throws TransmissionRPCException */ protected function request($method, $arguments = array()) @@ -694,9 +631,9 @@ class TransmissionRPC implements AbstractClientInterface curl_close($ch); if (!$content) { - $content = json_encode(array('result' => 'failed')); + $content = array('result' => 'failed'); } - return $this->return_as_array ? json_decode($content, true) : $this->cleanResultObject(json_decode($content)); // Return the sanitized result + return json_decode($content, true); } /** @@ -767,10 +704,56 @@ class TransmissionRPC implements AbstractClientInterface } /** - * @inheritDoc + * 抽象方法,子类实现 */ public function status() { - return isset($this->sstats()->result) ? $this->sstats()->result : 'error'; + $rs = $this->sstats(); + return isset($rs['result']) ? $rs['result'] : 'error'; + } + + /** + * 抽象方法,子类实现 + */ + public function getList(&$move = array()) + { + $ids = array(); + $fields = array( "id", "status", "name", "hashString", "downloadDir", "torrentFile" ); + $res = $this->get($ids, $fields); + if (isset($res['result']) && $res['result'] == 'success') { + // 成功 + } else { + // 失败 + echo "获取种子列表失败,可能transmission暂时无响应,请稍后重试!".PHP_EOL; + return array(); + } + if (empty($res['arguments']['torrents'])) { + echo "未获取到正常做种数据,请多保种,然后重试!".PHP_EOL; + return array(); + } + $res = $res['arguments']['torrents']; + // 过滤,只保留正常做种 + $res = array_filter($res, function ($v) { + return isset($v['status']) && $v['status']===6; + }, ARRAY_FILTER_USE_BOTH); + + if (empty($res)) { + echo "未获取到正常做种数据,请多保种,然后重试!".PHP_EOL; + return array(); + } + // 提取数组:hashString + $info_hash = array_column($res, 'hashString'); + // 升序排序 + sort($info_hash); + $json = json_encode($info_hash, JSON_UNESCAPED_UNICODE); + // 去重 应该从文件读入,防止重复提交 + $sha1 = sha1($json); + // 组装返回数据 + $hashArray['hash'] = $json; + $hashArray['sha1'] = $sha1; + // 变换数组:hashString为键 + $hashArray['hashString'] = array_column($res, "downloadDir", 'hashString'); + $move = array_column($res, null, 'hashString'); + return $hashArray; } } diff --git a/app/Client/qBittorrent/qBittorrent.php b/app/Client/qBittorrent/qBittorrent.php index b5439b5..0b3ea0e 100644 --- a/app/Client/qBittorrent/qBittorrent.php +++ b/app/Client/qBittorrent/qBittorrent.php @@ -2,12 +2,12 @@ namespace IYUU\Client\qBittorrent; use Curl\Curl; -use IYUU\Client\AbstractClientInterface; +use IYUU\Client\AbstractClient; /** * https://github.com/qbittorrent/qBittorrent/wiki/Web-API-Documentation */ -class qBittorrent implements AbstractClientInterface +class qBittorrent extends AbstractClient { private $debug; private $url; @@ -164,11 +164,6 @@ class qBittorrent implements AbstractClientInterface return $this->postData('torrent_add', $post_data); } - public function torrentDelete($hash='', $deleteFiles = false) - { - return $this->postData('torrent_delete', ['hashes' => $hash, 'deleteFiles' => $deleteFiles ? 'true':'false']); - } - public function torrentDeleteAll($deleteFiles = false) { $torrents = json_decode($this->torrentList()); @@ -301,10 +296,56 @@ class qBittorrent implements AbstractClientInterface } /** - * @inheritDoc + * 抽象方法,子类实现 */ public function status() { return $this->appVersion(); } + + /** + * 抽象方法,子类实现 + */ + public function getList(&$move = array()) + { + $result = $this->getData('torrent_list'); + $res = json_decode($result, true); + if (empty($res)) { + echo "获取种子列表失败,可能qBittorrent暂时无响应,请稍后重试!".PHP_EOL; + return array(); + } + // 过滤,只保留正常做种 + $res = array_filter($res, function ($v) { + if (isset($v['state']) && in_array($v['state'], array('uploading','stalledUP','pausedUP','queuedUP','checkingUP','forcedUP'))) { + return true; + } + return false; + }, ARRAY_FILTER_USE_BOTH); + + if (empty($res)) { + echo "未获取到正常做种数据,请多保种,然后重试!".PHP_EOL; + return array(); + } + // 提取数组:hashString + $info_hash = array_column($res, 'hash'); + // 升序排序 + sort($info_hash); + $json = json_encode($info_hash, JSON_UNESCAPED_UNICODE); + // 去重 应该从文件读入,防止重复提交 + $sha1 = sha1($json); + // 组装返回数据 + $hashArray['hash'] = $json; + $hashArray['sha1'] = $sha1; + // 变换数组:hashString为键 + $hashArray['hashString'] = array_column($res, "save_path", 'hash'); + return $hashArray; + } + + /** + * 抽象方法,子类实现 + */ + public function delete($hash='', $deleteFiles = false) + { + return $this->postData('torrent_delete', ['hashes' => $hash, 'deleteFiles' => $deleteFiles ? 'true':'false']); + } } diff --git a/app/helper.php b/app/helper.php index 06196ea..0a5d259 100644 --- a/app/helper.php +++ b/app/helper.php @@ -343,24 +343,6 @@ function wlog($data='', $name = '', $path = '') @fclose($file_pointer); return $worldsnum; } -/** - * 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) diff --git a/iyuu.php b/iyuu.php index 928396c..33c27fd 100644 --- a/iyuu.php +++ b/iyuu.php @@ -4,6 +4,5 @@ use IYUU\AutoReseed; echo microtime(true).' IYUU自动辅种正在初始化...'.PHP_EOL; AutoReseed::init(); -$hashArray = AutoReseed::get(); -AutoReseed::call($hashArray); +AutoReseed::call(); exit(0);