From 5037180a40811d905e34ce73f4e6816bb55de318 Mon Sep 17 00:00:00 2001 From: huangjx Date: Thu, 21 Oct 2021 18:18:41 +0800 Subject: [PATCH] fixed #15;added support for downloading via bittorrent files;added new class for getting download statistics --- appinfo/application.php | 8 +++- appinfo/routes.php | 1 + img/upload.svg | 1 + lib/Controller/Aria2Controller.php | 32 ++++---------- lib/Controller/MainController.php | 43 +++++++++++++++--- lib/Controller/SearchController.php | 2 +- lib/Controller/YoutubeController.php | 4 +- lib/Search/torrentSearch.php | 11 ++++- lib/Tools/Aria2.php | 36 +++++++++++++++ lib/Tools/Counters.php | 65 ++++++++++++++++++++++++++++ lib/Tools/Helper.php | 6 ++- src/http.js | 17 +++++++- 12 files changed, 190 insertions(+), 36 deletions(-) create mode 100644 img/upload.svg create mode 100644 lib/Tools/Counters.php diff --git a/appinfo/application.php b/appinfo/application.php index d6fc2aa..4a526a2 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -104,12 +104,17 @@ class Application extends App $dir = $this->settings->get('ncd_downloader_dir') ?? "/Downloads"; return $this->dataDir . $this->userFolder . $dir; } + private function getRealTorrentsDir() + { + $dir = $this->settings->get('ncd_torrents_dir') ?? "/Torrents"; + return $this->dataDir . $this->userFolder . $dir; + } private function getConfig() { //$this->config = \OC::$server->getAppConfig(); $realDownloadDir = $this->getRealDownloadDir(); - $this->torrentsDir = $this->settings->get('torrents_dir'); + $torrentsDir = $this->getRealTorrentsDir(); $aria2_dir = $this->dataDir . "/aria2"; $settings['seed_time'] = $this->settings->get("ncd_seed_time"); $settings['seed_ratio'] = $this->settings->get("ncd_seed_ratio"); @@ -119,6 +124,7 @@ class Application extends App $token = $this->settings->setType(Settings::TYPE['SYSTEM'])->get('ncd_rpctoken'); $config = [ 'dir' => $realDownloadDir, + 'torrents_dir' => $torrentsDir, 'conf_dir' => $aria2_dir, 'token' => $token, 'settings' => $settings, diff --git a/appinfo/routes.php b/appinfo/routes.php index 65d187d..89b02e5 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -3,6 +3,7 @@ return [ 'routes' => [ ['name' => 'Main#Index', 'url' => '/', 'verb' => 'GET'], + ['name' => 'Main#Upload', 'url' => '/upload', 'verb' => 'POST'], ['name' => 'main#Download', 'url' => '/new', 'verb' => 'POST'], ['name' => 'Aria2#Action', 'url' => '/aria2/{path}', 'verb' => 'POST'], ['name' => 'Aria2#getStatus', 'url' => '/status/{path}', 'verb' => 'POST'], diff --git a/img/upload.svg b/img/upload.svg new file mode 100644 index 0000000..4691578 --- /dev/null +++ b/img/upload.svg @@ -0,0 +1 @@ + diff --git a/lib/Controller/Aria2Controller.php b/lib/Controller/Aria2Controller.php index b2816f0..1427fd7 100644 --- a/lib/Controller/Aria2Controller.php +++ b/lib/Controller/Aria2Controller.php @@ -3,6 +3,7 @@ namespace OCA\NCDownloader\Controller; use OCA\NCDownloader\Tools\Aria2; use OCA\NCDownloader\Tools\DBConn; +use OCA\NCDownloader\Tools\Counters; use OCA\NCDownloader\Tools\folderScan; use OCA\NCDownloader\Tools\Helper; use OCA\NCDownloader\Tools\Settings; @@ -38,6 +39,8 @@ class Aria2Controller extends Controller $this->aria2 = $aria2; $this->aria2->init(); $this->dbconn = new DBConn(); + $this->counters = new Counters($aria2, $this->dbconn,$UserId); + } /** * @NoAdminRequired @@ -98,7 +101,7 @@ class Aria2Controller extends Controller return ['message' => $this->l10n->t("DONE!"), 'status' => 1]; } if (is_string($result)) { - return ['message' => $this->l10n->t($result), 'status' => 1]; + return ['message' => $this->l10n->t("DONE!"), 'status' => 1]; } } else { return ['error' => $this->l10n->t("FAILED!"), 'status' => 0]; @@ -140,7 +143,7 @@ class Aria2Controller extends Controller public function getStatus($path) { //$path = $this->request->getRequestUri(); - $counter = $this->getCounters(); + $counter = $this->counters->getCounters(); folderScan::sync(); switch (strtolower($path)) { case "active": @@ -269,6 +272,9 @@ class Aria2Controller extends Controller if (empty($resp)) { return $data; } + if (isset($resp['error'])) { + return $resp; + } $data = array_filter($resp, function ($value) { $gid = $value['following'] ?? $value['gid']; @@ -277,26 +283,4 @@ class Aria2Controller extends Controller return $data; } - private function getCounters() - { - return [ - 'active' => $this->getCounter(), - 'waiting' => $this->getCounter('tellWaiting'), - 'complete' => $this->getCounter('tellStopped'), - 'fail' => $this->getCounter('tellFail'), - ]; - } - private function getCounter($action = 'tellActive') - { - if ($action === 'tellActive') { - $data = $this->aria2->{$action}([]); - } else { - $data = $this->aria2->{$action}($this->minmax); - } - - if (!is_array($data)) { - return 0; - } - return count($this->filterData($data)); - } } diff --git a/lib/Controller/MainController.php b/lib/Controller/MainController.php index 8f2605b..d1cb57b 100644 --- a/lib/Controller/MainController.php +++ b/lib/Controller/MainController.php @@ -3,6 +3,7 @@ namespace OCA\NCDownloader\Controller; use OCA\NCDownloader\Tools\Aria2; +use OCA\NCDownloader\Tools\Counters; use OCA\NCDownloader\Tools\DBConn; use OCA\NCDownloader\Tools\Helper; use OCP\AppFramework\Controller; @@ -33,6 +34,7 @@ class MainController extends Controller $this->aria2 = $aria2; $this->aria2->init(); $this->dbconn = new DBConn(); + $this->counters = new Counters($aria2, $this->dbconn, $UserId); } /** * @NoAdminRequired @@ -43,24 +45,24 @@ class MainController extends Controller // $str = \OC::$server->getDatabaseConnection()->getInner()->getPrefix(); //$config = \OC::$server->getAppConfig(); OC_Util::addScript($this->appName, 'app'); - // OC_Util::addStyle($this->appName, 'style'); - // OC_Util::addStyle($this->appName, 'table'); + // OC_Util::addStyle($this->appName, 'table'); $params = array(); $params['aria2_running'] = $this->aria2->isRunning(); $params['aria2_installed'] = $this->aria2->isInstalled(); $params['youtube_installed'] = (bool) Helper::findBinaryPath('youtube-dl'); + $params['counter'] = $this->counters->getCounters(); $response = new TemplateResponse($this->appName, 'Index', $params); return $response; } - /** + /** * @NoAdminRequired * @NoCSRFRequired */ public function Download() { - $url = trim($this->request->getParam('form_input_text')); + $url = trim($this->request->getParam('text-input-value')); //$type = trim($this->request->getParam('type')); $resp = $this->_download($url); return new JSONResponse($resp); @@ -89,8 +91,39 @@ class MainController extends Controller 'data' => serialize(['link' => $url]), ]; $this->dbconn->save($data); - $resp = ['message' => $filename, 'result' => $result,'file' => $filename]; + $resp = ['message' => $filename, 'result' => $result, 'file' => $filename]; return $resp; } + /** + * @NoAdminRequired + * @NoCSRFRequired + */ + public function Upload() + { + if (is_uploaded_file($file = $_FILES['torrentfile']['tmp_name'])) { + $file = $this->aria2->getTorrentsDir() . '/' . Helper::cleanString($_FILES['torrentfile']['name']); + + move_uploaded_file($_FILES['torrentfile']['tmp_name'], $file); + + $result = $this->aria2->btDownload($file); + if (!$result) { + return ['error' => 'failed to download the file for some reason!']; + } + if (isset($result['error'])) { + return $result; + } + + $data = [ + 'uid' => $this->uid, + 'gid' => $result['gid'], + 'type' => Helper::DOWNLOADTYPE['ARIA2'], + 'filename' => $result['filename'] ?? 'unknown', + 'timestamp' => time(), + ]; + $this->dbconn->save($data); + $resp = ['message' => $result['filename'], 'result' => $result['gid'], 'file' => $result['filename']]; + } + return new JSONResponse($resp); + } } diff --git a/lib/Controller/SearchController.php b/lib/Controller/SearchController.php index afe94d3..cfd3169 100644 --- a/lib/Controller/SearchController.php +++ b/lib/Controller/SearchController.php @@ -27,7 +27,7 @@ class SearchController extends Controller */ public function execute() { - $keyword = trim($this->request->getParam('form_input_text')); + $keyword = trim($this->request->getParam('text-input-value')); $site = trim($this->request->getParam('select-value-search')); $this->search->setSite($site); $data = $this->search->go($keyword); diff --git a/lib/Controller/YoutubeController.php b/lib/Controller/YoutubeController.php index 1f7a239..2cc5ae7 100644 --- a/lib/Controller/YoutubeController.php +++ b/lib/Controller/YoutubeController.php @@ -77,9 +77,9 @@ class YoutubeController extends Controller public function Download() { $params = array(); - $url = trim($this->request->getParam('form_input_text')); + $url = trim($this->request->getParam('text-input-value')); $yt = $this->youtube; - $yt->audioOnly = (bool) $this->request->getParam('audioOnly'); + $yt->audioOnly = (bool) $this->request->getParam('audio-only'); if (!$yt->isInstalled()) { return new JSONResponse($this->installYTD()); } diff --git a/lib/Search/torrentSearch.php b/lib/Search/torrentSearch.php index 5bf6aa5..6a7499b 100644 --- a/lib/Search/torrentSearch.php +++ b/lib/Search/torrentSearch.php @@ -3,12 +3,15 @@ namespace OCA\NCDownloader\Search; require __DIR__ . "/../../vendor/autoload.php"; +use OCP\AppFramework\QueryException; use OCP\IServerContainer; +use Symfony\Component\HttpClient\Exception\ClientException; class torrentSearch { public $container; private $site = null; + private $defaultSite = __NAMESPACE__ . '\Sites\TPB'; public function __construct() { $this->container = \OC::$server->query(IServerContainer::class); @@ -16,7 +19,13 @@ class torrentSearch } public function go($keyword) { - $siteInst = $this->container->query($this->site); + try { + $siteInst = $this->container->query($this->site); + } catch (QueryException $e) { + $siteInst = $this->container->query($this->defaultSite); + } catch (ClientException $e) { + return ['message', $e->getMessage()]; + } $data = $siteInst->search($keyword); $this->addAction($data); return $data; diff --git a/lib/Tools/Aria2.php b/lib/Tools/Aria2.php index 6b9166a..69f1876 100644 --- a/lib/Tools/Aria2.php +++ b/lib/Tools/Aria2.php @@ -30,6 +30,7 @@ class Aria2 'host' => '127.0.0.1', 'port' => 6800, 'dir' => '/tmp/Downloads', + 'torrents_dir' => '/tmp/Torrents', 'token' => null, 'conf_dir' => '/tmp/aria2', 'completeHook' => $_SERVER['DOCUMENT_ROOT'] . "/apps/ncdownloader/hooks/completeHook.sh", @@ -43,6 +44,7 @@ class Aria2 $this->bin = Helper::findBinaryPath('aria2c'); } $this->setDownloadDir($dir); + $this->setTorrentsDir($torrents_dir); if (!empty($settings)) { foreach ($settings as $key => $value) { $this->setOption($key, $value); @@ -114,6 +116,18 @@ class Aria2 { return $this->options; } + public function setTorrentsDir($dir) + { + $this->torrentsDir = $dir; + if (!is_dir($dir)) { + mkdir($dir, 0755, true); + } + return $this; + } + public function getTorrentsDir() + { + return $this->torrentsDir; + } public function setDownloadDir($dir) { $this->setOption('dir', $dir); @@ -185,12 +199,14 @@ class Aria2 public function __call($name, $args) { $this->methodName = $name; + $data = array(); if (isset($args[0]) && is_array($args[0]) && count($args) == 1 && strtolower($name) !== "adduri") { $args = reset($args); } switch ($name) { case "addUri": + case "addTorrent": array_push($args, $this->options); break; case "tellActive": @@ -286,6 +302,26 @@ class Aria2 return false; } + public function btDownload($file) + { + if ($data = file_get_contents($file)) { + $filename = Helper::getBasicFilename($file); + $torrent = base64_encode($data); + $resp = $this->addTorrent($torrent, []); + }else{ + return ['error' => "no valid torrents file!"]; + } + if (isset($resp['error'])) { + return $resp; + } + + if (isset($resp['result']) && is_string($gid = $resp['result'])) { + return ['gid' => $gid, 'filename' => $filename]; + } + + return false; + } + public function getDefaults() { return [ diff --git a/lib/Tools/Counters.php b/lib/Tools/Counters.php new file mode 100644 index 0000000..6fb0c47 --- /dev/null +++ b/lib/Tools/Counters.php @@ -0,0 +1,65 @@ +aria2 = $aria2; + $this->dbconn = $dbconn; + $this->uid = $uid; + } + public function getCounters() + { + return [ + 'active' => $this->getCounter(), + 'waiting' => $this->getCounter('tellWaiting'), + 'complete' => $this->getCounter('tellStopped'), + 'fail' => $this->getCounter('tellFail'), + 'youtube-dl' => $this->getCounter('youtube-dl'), + ]; + } + private function getCounter($action = 'tellActive') + { + if ($action === 'youtube-dl') { + $data = $this->dbconn->getYoutubeByUid($this->uid); + } else if ($action === 'tellActive') { + $data = $this->aria2->{$action}([]); + } else { + $data = $this->aria2->{$action}($this->minmax); + } + + if (!is_array($data) && count($data) < 1) { + return 0; + } + if ($action !== 'youtube-dl') { + $data = $this->filterData($data); + } + return count($data); + } + + private function filterData($resp) + { + + $data = []; + if (empty($resp)) { + return $data; + } + if (isset($resp['error'])) { + return $resp; + } + + $data = array_filter($resp, function ($value) { + $gid = $value['following'] ?? $value['gid']; + return (bool) ($this->dbconn->getUidByGid($gid) === $this->uid); + }); + + return $data; + } +} diff --git a/lib/Tools/Helper.php b/lib/Tools/Helper.php index 210dc34..b65ddd0 100644 --- a/lib/Tools/Helper.php +++ b/lib/Tools/Helper.php @@ -124,7 +124,7 @@ class Helper '/[’‘‹›‚]/u' => '', // Literally a single quote '/[“”«»„]/u' => '', // Double quote '/ /' => '_', // nonbreaking space(equiv. to 0x160) - '/[^a-z0-9_\s.-]/i' => '_', + // '/[^a-z0-9_\s.-]/i' => '_', ); return preg_replace(array_keys($replace), array_values($replace), $string); } @@ -286,5 +286,9 @@ class Helper { return (bool) self::findBinaryPath('ffmpeg'); } + // filename without extension + public static function getBasicFilename($path){ + return pathinfo($path, PATHINFO_FILENAME); + } } diff --git a/src/http.js b/src/http.js index b08eb00..cdfdab9 100644 --- a/src/http.js +++ b/src/http.js @@ -14,12 +14,16 @@ const Http = class { this.data = data return this } + setDataType($value) { + this.dataType = $value; + } send() { let token = this.getToken(); this.xhr.open(this.method, this.url); this.xhr.setRequestHeader('requesttoken', token) this.xhr.setRequestHeader('OCS-APIREQUEST', 'true') - this.xhr.setRequestHeader('Content-Type', this.dataType); + if (this.dataType) + this.xhr.setRequestHeader('Content-Type', this.dataType); let callback = this.handler; this.xhr.onload = () => { if (typeof callback === 'function') @@ -47,6 +51,17 @@ const Http = class { this.errorHandler = handler return this; } + upload(file) { + const fd = new FormData(); + this.xhr.open(this.method, this.url, true); + let callback = this.handler; + this.xhr.onload = () => { + if (typeof callback === 'function') + callback(JSON.parse(this.xhr.response)); + } + fd.append('torrentfile', file); + return this.xhr.send(fd); + } } export default Http \ No newline at end of file