diff --git a/Makefile b/Makefile index efe5ae6..ae9e224 100644 --- a/Makefile +++ b/Makefile @@ -113,20 +113,21 @@ dist: source: rm -rf $(source_build_directory) mkdir -p $(source_build_directory) - tar cvzf $(source_package_name).tar.gz ../$(app_name) \ + tar cvzf $(source_package_name).tar.gz \ --exclude-vcs \ --exclude="../$(app_name)/build" \ --exclude="../$(app_name)/js/node_modules" \ --exclude="../$(app_name)/node_modules" \ --exclude="../$(app_name)/*.log" \ --exclude="../$(app_name)/js/*.log" \ + ../$(app_name) # Builds the source package for the app store, ignores php and js tests .PHONY: appstore appstore: rm -rf $(appstore_build_directory) mkdir -p $(appstore_build_directory) - tar cvzf $(appstore_package_name).tar.gz ../$(app_name) \ + tar cvzf $(appstore_package_name).tar.gz \ --exclude-vcs \ --exclude="../$(app_name)/build" \ --exclude="../$(app_name)/tests" \ @@ -142,12 +143,14 @@ appstore: --exclude="../$(app_name)/js/bower.json" \ --exclude="../$(app_name)/js/karma.*" \ --exclude="../$(app_name)/js/protractor.*" \ + --exclude="../$(app_name)/node_modules" \ --exclude="../$(app_name)/package.json" \ --exclude="../$(app_name)/bower.json" \ --exclude="../$(app_name)/karma.*" \ --exclude="../$(app_name)/protractor\.*" \ --exclude="../$(app_name)/.*" \ --exclude="../$(app_name)/js/.*" \ + ../$(app_name) .PHONY: test test: composer diff --git a/appinfo/application.php b/appinfo/application.php index 40694a2..6cd7107 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -19,11 +19,9 @@ class Application extends App public function __construct(array $urlParams = array()) { parent::__construct('ncdownloader', $urlParams); - $user = \OC::$server->getUserSession()->getUser(); + $user = Helper::getUser(); $this->uid = ($user) ? $user->getUID() : ''; $this->settings = new Settings($this->uid); - $this->dataDir = \OC::$server->getSystemConfig()->getValue('datadirectory'); - $this->appPath = \OC::$server->getAppManager()->getAppPath('ncdownloader'); $this->userFolder = Helper::getUserFolder($this->uid); $container = $this->getContainer(); $container->registerService('UserId', function (IContainer $container) { @@ -31,15 +29,11 @@ class Application extends App }); $container->registerService('Aria2', function (IContainer $container) { - return new Aria2($this->getConfig()); + return new Aria2(Helper::getAria2Config($this->uid)); }); $container->registerService('Youtube', function (IContainer $container) { - $config = [ - 'binary' => $this->settings->setType(Settings::TYPE['SYSTEM'])->get("ncd_yt_binary"), - 'downloadDir' => $this->getRealDownloadDir(), - 'settings' => $this->settings->setType(Settings::TYPE['USER'])->getYoutube(), - ]; + $config = Helper::getYoutubeConfig($this->uid); return new Youtube($config); }); @@ -100,42 +94,4 @@ class Application extends App } } - private function getRealDownloadDir() - { - - //relative nextcloud user path - $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(); - $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"); - if (is_array($customSettings = $this->settings->getAria2())) { - $settings = array_merge($customSettings, $settings); - } - $token = $this->settings->setType(Settings::TYPE['SYSTEM'])->get('ncd_rpctoken'); - $config = [ - 'dir' => $realDownloadDir, - 'torrents_dir' => $torrentsDir, - 'conf_dir' => $aria2_dir, - 'token' => $token, - 'settings' => $settings, - 'binary' => $this->settings->setType(Settings::TYPE['SYSTEM'])->get('ncd_aria2_binary'), - 'startHook' => $this->appPath . "/hooks/startHook.sh", - 'completeHook' => $this->appPath . "/hooks/completeHook.sh", - ]; - return $config; - } - } diff --git a/appinfo/info.xml b/appinfo/info.xml index b592c1f..20a6df6 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -8,7 +8,7 @@ Search for torrents within the app from mutiple BT sites; Control Aria2 and manage download tasks from the web; download videos from 700+ video sites(youtube,youku,vimo,dailymotion,twitter,facebook and the likes - 0.9.1 + 0.9.16 agpl jiaxinhuang NCDownloader diff --git a/appinfo/routes.php b/appinfo/routes.php index 700fc17..c6a5ec2 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -7,7 +7,7 @@ return [ ['name' => 'main#Download', 'url' => '/new', 'verb' => 'POST'], ['name' => 'Aria2#Action', 'url' => '/aria2/{path}', 'verb' => 'POST'], ['name' => 'Aria2#getStatus', 'url' => '/status/{path}', 'verb' => 'POST'], - ['name' => 'Aria2#Update', 'url' => '/update', 'verb' => 'GET'], + ['name' => 'Main#scanFolder', 'url' => '/scanfolder', 'verb' => 'GET'], ['name' => 'Youtube#Index', 'url' => '/youtube/get', 'verb' => 'POST'], ['name' => 'Youtube#Download', 'url' => '/youtube/new', 'verb' => 'POST'], ['name' => 'Youtube#Delete', 'url' => '/youtube/delete', 'verb' => 'POST'], diff --git a/lib/Controller/Aria2Controller.php b/lib/Controller/Aria2Controller.php index 1951872..2fa121b 100644 --- a/lib/Controller/Aria2Controller.php +++ b/lib/Controller/Aria2Controller.php @@ -6,7 +6,6 @@ use OCA\NCDownloader\Tools\Counters; use OCA\NCDownloader\Tools\DbHelper; use OCA\NCDownloader\Tools\folderScan; use OCA\NCDownloader\Tools\Helper; -use OCA\NCDownloader\Tools\Settings; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\JSONResponse; use OCP\Files\IRootFolder; @@ -32,8 +31,7 @@ class Aria2Controller extends Controller $this->l10n = $IL10N; $this->rootFolder = $rootFolder; $this->urlGenerator = \OC::$server->getURLGenerator(); - $this->settings = new Settings($UserId); - $this->downloadDir = $this->settings->get('ncd_downloader_dir') ?? "/Downloads"; + $this->downloadDir = Helper::getDownloadDir(); OC_Util::setupFS(); //$this->config = \OC::$server->getAppConfig(); $this->aria2 = $aria2; @@ -122,15 +120,6 @@ class Aria2Controller extends Controller $data = $this->aria2->start(); return $data; } - /** - * @NoAdminRequired - * @NoCSRFRequired - */ - public function Update() - { - $resp = folderScan::create()->scan(); - return new JSONResponse($resp); - } private function createActionItem($name, $path) { @@ -235,7 +224,7 @@ class Aria2Controller extends Controller $tmp = []; $actions = []; $filename = sprintf('%s', $folderLink, $filename); - $fileInfo = sprintf(' %s | %s', $extra ? $extra['link'] : 'nolink', $total, date("Y-m-d H:i:s", $timestamp)); + $fileInfo = sprintf(' %s | %s', $extra["link"] ?? 'nolink', $total, date("Y-m-d H:i:s", $timestamp)); $tmp['filename'] = array($filename, $fileInfo); if ($this->aria2->methodName === "tellStopped") { diff --git a/lib/Controller/MainController.php b/lib/Controller/MainController.php index 572447d..066926c 100644 --- a/lib/Controller/MainController.php +++ b/lib/Controller/MainController.php @@ -11,10 +11,11 @@ use OCA\NCDownloader\Tools\Youtube; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\TemplateResponse; -//use OCP\Files\IRootFolder; use OCP\IL10N; +//use OCP\Files\IRootFolder; use OCP\IRequest; use OC_Util; +use OCA\NCDownloader\Tools\folderScan; class MainController extends Controller { @@ -27,6 +28,7 @@ class MainController extends Controller public function __construct($appName, IRequest $request, $UserId, IL10N $IL10N, Aria2 $aria2, Youtube $youtube) { + parent::__construct($appName, $request); $this->appName = $appName; $this->uid = $UserId; @@ -38,10 +40,9 @@ class MainController extends Controller $this->dbconn = new DbHelper(); $this->counters = new Counters($aria2, $this->dbconn, $UserId); $this->youtube = $youtube; - $this->settings = new Settings($this->uid); $this->isAdmin = \OC_User::isAdminUser($this->uid); - $this->hideError = $this->settings->get("ncd_hide_errors", false); - $this->disable_bt_nonadmin = $this->settings->setType($this->settings::TYPE['SYSTEM'])->get("ncd_disable_bt", false); + $this->hideError = Helper::getSettings("ncd_hide_errors", false); + $this->disable_bt_nonadmin = Helper::getSettings("ncd_disable_bt", false, Settings::TYPE["SYSTEM"]); $this->accessDenied = $this->l10n->t("Sorry,only admin users can download files via BT!"); } /** @@ -56,7 +57,6 @@ class MainController extends Controller // OC_Util::addStyle($this->appName, 'table'); $params = $this->buildParams(); $response = new TemplateResponse($this->appName, 'Index', $params); - return $response; } @@ -124,6 +124,10 @@ class MainController extends Controller */ public function Download() { + $dlDir = $this->aria2->getDownloadDir(); + if (!is_writable($dlDir)) { + return new JSONResponse(['error' => sprintf("%s is not writable", $dlDir)]); + } $url = trim($this->request->getParam('text-input-value')); if (Helper::isMagnet($url)) { if ($this->disable_bt_nonadmin && !($this->isAdmin)) { @@ -192,4 +196,14 @@ class MainController extends Controller return new JSONResponse($resp); } + /** + * @NoAdminRequired + * @NoCSRFRequired + */ + public function scanFolder() + { + $resp = folderScan::create()->scan(); + return new JSONResponse($resp); + } + } diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 91f4a89..06c8af4 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -111,7 +111,7 @@ class SettingsController extends Controller { $saved = json_decode($this->settings->get("custom_youtube_dl_settings"), 1); $params = $this->request->getParams(); - foreach ($data as $key => $value) { + foreach ($params as $key => $value) { unset($saved[$key]); } $resp = $this->settings->save("custom_youtube_dl_settings", json_encode($saved)); diff --git a/lib/Controller/YoutubeController.php b/lib/Controller/YoutubeController.php index a023525..ebc2cbd 100644 --- a/lib/Controller/YoutubeController.php +++ b/lib/Controller/YoutubeController.php @@ -5,7 +5,6 @@ use OCA\NCDownloader\Tools\Aria2; use OCA\NCDownloader\Tools\DbHelper; use OCA\NCDownloader\Tools\folderScan; use OCA\NCDownloader\Tools\Helper; -use OCA\NCDownloader\Tools\Settings; use OCA\NCDownloader\Tools\Youtube; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\JSONResponse; @@ -27,8 +26,7 @@ class YoutubeController extends Controller $this->uid = $UserId; $this->urlGenerator = \OC::$server->getURLGenerator(); $this->l10n = $IL10N; - $this->settings = new Settings($UserId); - $this->downloadDir = $this->settings->get('ncd_downloader_dir') ?? "/Downloads"; + $this->downloadDir = Helper::getDownloadDir(); $this->dbconn = new DbHelper(); $this->youtube = $youtube; $this->aria2 = $aria2; @@ -77,7 +75,10 @@ class YoutubeController extends Controller */ public function Download() { - $params = array(); + $dlDir = $this->youtube->getDownloadDir(); + if (!is_writable($dlDir)) { + return new JSONResponse(['error' => sprintf("%s is not writable", $dlDir)]); + } $url = trim($this->request->getParam('text-input-value')); $yt = $this->youtube; if (in_array($this->request->getParam('extension'), $this->audio_extensions)) { @@ -119,7 +120,7 @@ class YoutubeController extends Controller } $row = $this->dbconn->getByGid($gid); - $data = $this->dbconn->getExtra($value["data"]);; + $data = $this->dbconn->getExtra($row["data"]); if (!isset($data['pid'])) { if ($this->dbconn->deleteByGid($gid)) { $msg = sprintf("%s is deleted from database!", $gid); @@ -204,9 +205,9 @@ class YoutubeController extends Controller private function installYTD() { try { - $filename = Helper::getFileName($yt->installUrl()); - $yt->setDownloadDir($this->dataDir . "/bin"); - $resp = $this->Save($yt->installUrl(), $filename); + $filename = Helper::getFileName($this->installUrl()); + $this->setDownloadDir($this->dataDir . "/bin"); + $resp = $this->Save($this->installUrl(), $filename); return $resp; } catch (\Exception $e) { return ['error' => $e->getMessage()]; diff --git a/lib/Search/Sites/TPB.php b/lib/Search/Sites/TPB.php index d17b1bc..15b265b 100644 --- a/lib/Search/Sites/TPB.php +++ b/lib/Search/Sites/TPB.php @@ -33,7 +33,6 @@ class TPB extends searchBase implements searchInterface if ($this->content) { return $this->content; } - $content; try { $response = $this->client->request('GET', $this->searchUrl); $content = $response->getContent(); diff --git a/lib/Search/Sites/bitSearch.php b/lib/Search/Sites/bitSearch.php index a275f32..8e18002 100644 --- a/lib/Search/Sites/bitSearch.php +++ b/lib/Search/Sites/bitSearch.php @@ -37,7 +37,6 @@ class bitSearch extends searchBase implements searchInterface if ($this->content) { return $this->content; } - $content; try { $response = $this->client->request('GET', $this->searchUrl, ['query' => $this->query]); $content = $response->getContent(); diff --git a/lib/Settings/Personal.php b/lib/Settings/Personal.php index c289834..3293be5 100644 --- a/lib/Settings/Personal.php +++ b/lib/Settings/Personal.php @@ -34,7 +34,7 @@ class Personal implements ISettings { */ public function getForm() { $parameters = [ - "ncd_downloader_dir" => $this->settings->get("ncd_downloader_dir"), + "ncd_downloader_dir" => Helper::getDownloadDir(), "ncd_torrents_dir" => $this->settings->get("ncd_torrents_dir"), "ncd_seed_ratio" => $this->settings->get("ncd_seed_ratio"), 'ncd_seed_time_unit' => $this->settings->get("ncd_seed_time_unit"), diff --git a/lib/Tools/Aria2.php b/lib/Tools/Aria2.php index 14dda87..af730d7 100644 --- a/lib/Tools/Aria2.php +++ b/lib/Tools/Aria2.php @@ -97,10 +97,14 @@ class Aria2 private function configure() { - if (!is_dir($this->confDir)) { + if ($this->confDir && !is_dir($this->confDir)) { mkdir($this->confDir, 0755, true); } - if (!is_dir($dir = $this->getDownloadDir())) { + $dir = ""; + if (($dir = $this->getDownloadDir()) && !is_dir($dir)) { + mkdir($dir, 0755, true); + } + if (($dir = $this->getTorrentsDir()) && !is_dir($dir)) { mkdir($dir, 0755, true); } $this->followTorrent(true); @@ -123,12 +127,9 @@ class Aria2 public function setTorrentsDir($dir) { $this->torrentsDir = $dir; - if (!is_dir($dir)) { - mkdir($dir, 0755, true); - } return $this; } - public function getTorrentsDir() + public function getTorrentsDir(): string { return $this->torrentsDir; } @@ -136,12 +137,9 @@ class Aria2 { $this->setOption('dir', $dir); $this->downloadDir = $dir; - if (!is_dir($dir)) { - mkdir($dir, 0755, true); - } return $this; } - public function getDownloadDir() + public function getDownloadDir(): string { return $this->downloadDir; } diff --git a/lib/Tools/DbHelper.php b/lib/Tools/DbHelper.php index 6019864..143f452 100644 --- a/lib/Tools/DbHelper.php +++ b/lib/Tools/DbHelper.php @@ -67,6 +67,7 @@ class DbHelper ->andWhere('type = :type') ->setParameter('uid', $uid) ->setParameter('type', Helper::DOWNLOADTYPE['YOUTUBE-DL']) + ->orderBy('id', 'DESC') ->execute(); return $qb->fetchAll(); } @@ -132,7 +133,12 @@ class DbHelper public function getExtra($data) { if ($this->getDBType() == "pgsql" && is_resource($data)) { - $extra = pg_unescape_bytea(stream_get_contents($data)); + if (function_exists("pg_unescape_bytea")) { + $extra = pg_unescape_bytea(stream_get_contents($data)); + } + else { + $extra = stream_get_contents($data); + } return unserialize($extra); } return unserialize($data); diff --git a/lib/Tools/Helper.php b/lib/Tools/Helper.php index fd6641a..dfb7af3 100644 --- a/lib/Tools/Helper.php +++ b/lib/Tools/Helper.php @@ -2,15 +2,19 @@ namespace OCA\NCDownloader\Tools; +use Exception; use OCA\NCDownloader\Search\Sites\searchInterface; use OCA\NCDownloader\Tools\aria2Options; +use OCA\NCDownloader\Tools\Settings; +use OCP\IUser; use OC\Files\Filesystem; +use OC_Util; class Helper { public const DOWNLOADTYPE = ['ARIA2' => 1, 'YOUTUBE-DL' => 2, 'OTHERS' => 3]; public const STATUS = ['ACTIVE' => 1, 'PAUSED' => 2, 'COMPLETE' => 3, 'WAITING' => 4, 'ERROR' => 5]; - const MAXLEN = 255; + const MAXFILELEN = 255; public static function isUrl($URL) { @@ -61,8 +65,8 @@ class Helper } public static function clipFilename($filename) { - if (($len = strlen($filename)) > 64) { - return substr($filename, $len - 64); + if (($len = strlen($filename)) > self::MAXFILELEN) { + return substr($filename, $len - self::MAXFILELEN); } return $filename; } @@ -73,7 +77,7 @@ class Helper } else { $filename = self::getUrlPath($url); } - return substr($filename, 0, self::MAXLEN); + return substr($filename, 0, self::MAXFILELEN); } public static function formatBytes($size, $precision = 2) { @@ -135,6 +139,9 @@ class Helper public static function debug($msg) { + if (is_array($msg)) { + $msg = implode(",", $msg); + } $logger = \OC::$server->getLogger(); $logger->error($msg, ['app' => 'ncdownloader']); } @@ -279,14 +286,13 @@ class Helper ]; return $titles[$type]; } - // the relative home folder of a nextcloud user - public static function getUserFolder($uid = null) + // the relative home folder of a nextcloud user,e.g. /admin/files + public static function getUserFolder($uid = null): string { if (!empty($rootFolder = Filesystem::getRoot())) { return $rootFolder; } else if (isset($uid)) { return "/" . $uid . "/files"; - } return ''; } @@ -374,4 +380,124 @@ class Helper return $sites; } + public static function getMountPoints(): ?array + { + return Filesystem::getMountPoints("/"); + } + + public static function getDataDir(): string + { + return \OC::$server->getSystemConfig()->getValue('datadirectory'); + } + + public static function getLocalFolder(string $path): string + { + if (self::getUID()) { + OC_Util::setupFS(); + return Filesystem::getLocalFolder($path); + } + return ""; + } + + public static function getRealDownloadDir($uid = null): string + { + $dlDir = self::getDownloadDir(); + return self::getLocalFolder($dlDir); + } + public static function getRealTorrentsDir($uid = null): string + { + $dir = self::getSettings('ncd_torrents_dir', "/Torrents"); + return self::getLocalFolder($dir); + } + + public static function getUser(): ?IUser + { + return \OC::$server->getUserSession()->getUser(); + } + + public static function getUID(): string + { + $user = self::getUser(); + $uid = $user ? $user->getUID() : ""; + return $uid; + } + + public static function getSettings($key, $default = null, int $type = Settings::TYPE['USER']) + { + $settings = self::newSettings(); + return $settings->setType($type)->get($key, $default); + } + + public static function newSettings($uid = null) + { + $uid = $uid ?? self::getUID(); + return Settings::create($uid); + } + + public static function getYoutubeConfig($uid = null): array + { + $config = [ + 'binary' => self::getSettings("ncd_yt_binary", null, Settings::TYPE['SYSTEM']), + 'downloadDir' => Helper::getRealDownloadDir(), + 'settings' => self::newSettings()->getYoutube(), + ]; + return $config; + } + + public static function getAria2Config($uid = null): array + { + $options = []; + $uid = $uid ?? self::getUID(); + $settings = self::newSettings($uid); + $realDownloadDir = Helper::getRealDownloadDir($uid); + $torrentsDir = Helper::getRealTorrentsDir($uid); + $appPath = self::getAppPath(); + $dataDir = self::getDataDir(); + $aria2_dir = $dataDir . "/aria2"; + $options['seed_time'] = $settings->get("ncd_seed_time"); + $options['seed_ratio'] = $settings->get("ncd_seed_ratio"); + if (is_array($customSettings = $settings->getAria2())) { + $options = array_merge($customSettings, $options); + } + $token = $settings->setType(Settings::TYPE['SYSTEM'])->get('ncd_rpctoken'); + $config = [ + 'dir' => $realDownloadDir, + 'torrents_dir' => $torrentsDir, + 'conf_dir' => $aria2_dir, + 'token' => $token, + 'settings' => $options, + 'binary' => $settings->setType(Settings::TYPE['SYSTEM'])->get('ncd_aria2_binary'), + 'startHook' => $appPath . "/hooks/startHook.sh", + 'completeHook' => $appPath . "/hooks/completeHook.sh", + ]; + return $config; + } + + public static function getAppPath(): string + { + return \OC::$server->getAppManager()->getAppPath('ncdownloader'); + } + public static function folderUpdated(string $dir): bool + { + if (!file_exists($dir)) { + return false; + } + $checkFile = $dir . "/.lastmodified"; + if (!file_exists($checkFile)) { + $time = \filemtime($dir); + file_put_contents($checkFile, $time); + return false; + } + $lastModified = (int) file_get_contents($checkFile); + $time = \filemtime($dir); + if ($time > $lastModified) { + file_put_contents($checkFile, $time); + return true; + } + return false; + } + public static function getDownloadDir(): string + { + return self::getSettings('ncd_downloader_dir', "/Downloads"); + } } diff --git a/lib/Tools/Settings.php b/lib/Tools/Settings.php index 631f499..18e9578 100644 --- a/lib/Tools/Settings.php +++ b/lib/Tools/Settings.php @@ -7,17 +7,19 @@ use OC\AllConfig; class Settings extends AllConfig { //@config OC\AppConfig - private $config; + private $appConfig; //@OC\SystemConfig private $sysConfig; //@OC\AllConfig private $allConfig; - + private $user; + private $appName; //type of settings (system = 1 or app =2) private $type; - public const TYPE = ['SYSTEM' => 0x001, 'USER' => 0x010, 'APP' => 0x100]; + private static $instance = null; + public const TYPE = ['SYSTEM' => 1, 'USER' => 2, 'APP' => 3]; public function __construct($user = null) { $this->appConfig = \OC::$server->getAppConfig(); @@ -29,6 +31,14 @@ class Settings extends AllConfig //$this->connAdapter = \OC::$server->getDatabaseConnection(); //$this->conn = $this->connAdapter->getInner(); } + public static function create($user = null) + { + + if (!self::$instance) { + self::$instance = new static($user); + } + return self::$instance; + } public function setType($type) { $this->type = $type; @@ -104,141 +114,4 @@ class Settings extends AllConfig } return $value; } - -} - -class customSettings -{ - private $name = null; - private $dbType = 0; - private $table = 'ncdownloader_settings'; - private $uid = null; - const PGSQL = 1, MYSQL = 2, SQL = 3; - /* @var OC\DB\ConnectionAdapter */ - private $connAdapter; - /* @var OC\DB\Connection */ - private $conn; - private $type = 2; //personal = 2,admin =1 - - public function __construct() - { - if (\OC::$server->getConfig()->getSystemValue('dbtype') == 'pgsql') { - $this->dbType = PGSQL; - } - $this->connAdapter = \OC::$server->getDatabaseConnection(); - $this->conn = $this->connAdapter->getInner(); - - $this->prefixTable(); - } - - private function prefixTable() - { - $this->table = '*PREFIX*' . $this->table; - return $this->table; - } - - public function set($name, $value) - { - if ($this->have($name)) { - $this->update($name, $value); - } else { - $this->insert($name, $value); - } - } - public function setType($type) - { - $this->type = $type; - } - - public function get($name) - { - - if (isset($this->uid)) { - $sql = sprintf("SELECT value FROM %s WHERE uid = ? AND name = ? LIMIT 1", $this->table); - $query = \OC_DB::prepare($sql); - $result = $query->execute(array($this->uid, $name)); - } else { - $sql = sprintf("SELECT value FROM %s WHERE name = ? LIMIT 1", $this->table); - $query = \OC_DB::prepare($sql); - $result = $query->execute(array($name)); - } - if ($query->rowCount() == 1) { - return $result->fetchOne(); - } - return null; - } - - public function setUID($uid) - { - $this->uid = $uid; - } - - public function setTable($table) - { - $this->table = $table; - } - public function getTable() - { - return $this->table; - } - - public function have($name) - { - if (isset($this->uid)) { - $sql = sprintf("SELECT value FROM %s WHERE uid = ? AND name = ? AND type = ? LIMIT 1", $this->table); - $query = \OC_DB::prepare($sql); - $query->execute(array($name, $this->uid, $this->type)); - } else { - $sql = sprintf("SELECT value FROM %s WHERE name = ? AND type = ? LIMIT 1", $this->table); - $query = \OC_DB::prepare($sql); - $query->execute(array($name, $this->type)); - } - - if ($query->rowCount() == 1) { - return true; - } - return false; - } - - public function getAll() - { - $sql = 'SELECT `name`, `value` FROM `*PREFIX*' . $this->table . '`' - . (!is_null($this->uid) ? ' WHERE `UID` = ?' : ''); - if ($this->DbType == 1) { - $sql = 'SELECT "name", "value" FROM *PREFIX*' . $this->table . '' - . (!is_null($this->uid) ? ' WHERE "uid" = ?' : ''); - } - $query = \OC_DB::prepare($sql); - - if (!is_null($this->uid)) { - return $query->execute(array($this->uid)); - } else { - return $query->execute(); - } - } - - public function update($value) - { - if (isset($this->uid)) { - $sql = sprintf("UPDATE %s SET value = ? WHERE name = ? AND type = ? AND uid = ?", $this->table); - //OCP\DB\IPreparedStatement - $query = \OC_DB::prepare($sql); - $query->execute(array($value, $name, $this->type, $this->uid)); - } else { - $sql = sprintf("UPDATE %s SET value = ? WHERE name = ? AND type = ?", $this->table); - //OCP\DB\IPreparedStatement - $query = \OC_DB::prepare($sql); - $query->execute(array($value, $name, $this->type)); - } - - } - - public function insert($name, $value) - { - $sql = sprintf("INSERT INTO %s (name,value,type,uid) VALUES(?,?,?,?)", $this->table); - //OCP\DB\IPreparedStatement - $query = \OC_DB::prepare($sql); - $query->execute(array($name, $value, $this->type, $this->uid)); - - } } diff --git a/lib/Tools/Youtube.php b/lib/Tools/Youtube.php index 912df4e..cedfae1 100644 --- a/lib/Tools/Youtube.php +++ b/lib/Tools/Youtube.php @@ -104,9 +104,9 @@ class Youtube return $this; } - public static function create() + public static function create($options) { - return new self(); + return new self($options); } public function setDownloadDir($dir) @@ -116,7 +116,7 @@ class Youtube public function getDownloadDir() { - return $this->getDownloadDir; + return $this->downloadDir; } public function prependOption(string $option) diff --git a/lib/Tools/YoutubeHelper.php b/lib/Tools/YoutubeHelper.php index 5ffaadd..6f94801 100644 --- a/lib/Tools/YoutubeHelper.php +++ b/lib/Tools/YoutubeHelper.php @@ -66,7 +66,9 @@ class YoutubeHelper if ($file) { $extra = serialize($extra); if($this->dbconn->getDBType() == "pgsql"){ - $extra = pg_escape_bytea($extra); + if (function_exists("pg_escape_bytea")) { + $extra = pg_escape_bytea($extra); + } } $data = [ 'uid' => $this->user, diff --git a/lib/Tools/folderScan.php b/lib/Tools/folderScan.php index b347855..98c2d4a 100644 --- a/lib/Tools/folderScan.php +++ b/lib/Tools/folderScan.php @@ -2,7 +2,6 @@ namespace OCA\NCDownloader\Tools; use OCA\NCDownloader\Tools\Helper; -use OCA\NCDownloader\Tools\Settings; use OC\Files\Utils\Scanner; use \OCP\EventDispatcher\IEventDispatcher; @@ -10,19 +9,17 @@ class folderScan { private $user; private $path; + private $realDir; public function __construct($path = null, $user = null) { - $this->user = $user ?? \OC::$server->getUserSession()->getUser()->getUID(); + $this->user = $user ?? Helper::getUID(); $this->path = $path ?? $this->getDefaultPath(); - $this->realDir = \OC::$server->getSystemConfig()->getValue('datadirectory') . "/" . $this->path; + $this->realDir = $realDir ?? Helper::getLocalFolder($this->path); } public function getDefaultPath() { - $settings = new Settings($this->user); - $rootFolder = Helper::getUserFolder($this->user); - $downloadDir = $settings->get('ncd_downloader_dir') ?? "/Downloads"; - return $rootFolder . "/" . ltrim($downloadDir, '/\\'); + return Helper::getUserFolder() . Helper::getDownloadDir(); } public static function create($path = null, $user = null) { @@ -42,7 +39,7 @@ class folderScan private function update() { - if (!(self::folderUpdated($this->realDir))) { + if (!(Helper::folderUpdated($this->realDir))) { return ['message' => "no change"]; } $this->scan(); @@ -55,34 +52,15 @@ class folderScan $this->scanner = new Scanner($this->user, \OC::$server->getDatabaseConnection(), \OC::$server->query(IEventDispatcher::class), $this->logger); try { $this->scanner->scan($this->path); - return ['status' => 'OK', 'path' => $this->path]; - } catch (ForbiddenException $e) { + return ['status' => true, 'path' => $this->path]; + } catch (\OCP\Files\ForbiddenException $e) { $this->logger->warning("Make sure you're running the scan command only as the user the web server runs as"); + } catch (\OCP\Files\NotFoundException $e) { + $this->logger->warning("Path for the scan command not found: " . $e->getMessage()); } catch (\Exception $e) { - $this->logger->warning("Exception during scan: " . $e->getMessage() . $e->getTraceAsString()); } - return ['status' => $e->getMessage(), 'path' => $this->path]; - - } - public static function folderUpdated($dir) - { - if (!file_exists($dir)) { - return false; - } - $checkFile = $dir . "/.lastmodified"; - if (!file_exists($checkFile)) { - $time = \filemtime($dir); - file_put_contents($checkFile, $time); - return false; - } - $lastModified = (int) file_get_contents($checkFile); - $time = \filemtime($dir); - if ($time > $lastModified) { - file_put_contents($checkFile, $time); - return true; - } - return false; + return ['status' => false, 'path' => $this->path]; } //update only folder is modified diff --git a/src/App.vue b/src/App.vue index 2e5080e..0514638 100644 --- a/src/App.vue +++ b/src/App.vue @@ -56,19 +56,25 @@ export default { let element = event.target; let formWrapper = element.closest("form"); let formData = helper.getData(formWrapper); - let inputValue = formData["text-input-value"]; + let inputValue = formData["text-input-value"].trim(); let message; - if (formData.type === "youtube-dl") { - formData["extension"] = ""; - if (formData["select-value-extension"] !== "defaultext") { - formData["extension"] = formData["select-value-extension"]; - } - message = helper.t("Download task started!"); - } if (!helper.isURL(inputValue) && !helper.isMagnetURI(inputValue)) { helper.error(t("ncdownloader", inputValue + " is Invalid")); return; } + if (formData.type === "youtube-dl") { + formData["extension"] = ""; + + if (formData["select-value-extension"] !== "defaultext") { + formData["extension"] = formData["select-value-extension"]; + } + message = helper.t("Download task started!"); + helper.pollingYoutube(); + helper.setContentTableType("youtube-dl-downloads"); + } else { + helper.polling(); + helper.setContentTableType("active-downloads"); + } if (message) { helper.info(message); } @@ -90,7 +96,7 @@ export default { vm.$data.loading = 0; return; } - helper.enabledPolling = 0; + helper.disablePolling(); contentTable.getInstance().loading(); let url = formWrapper.getAttribute("action"); diff --git a/src/actions/updatePage.js b/src/actions/updatePage.js index 4aea671..3b584ec 100644 --- a/src/actions/updatePage.js +++ b/src/actions/updatePage.js @@ -1,50 +1,40 @@ import helper from '../utils/helper' import eventHandler from '../lib/eventHandler'; -import Http from '../lib/http' - -const basePath = "/apps/ncdownloader/status/"; -const tableContainer = ".table"; export default { run: function () { - const clickHandler = (event, type) => { + const clickHandler = (event) => { + event.stopPropagation(); event.preventDefault(); - helper.hideDownload(); - let container = document.querySelector(tableContainer); - let currentType = container.getAttribute("type"); - let path = basePath + type; - if (type === "youtube-dl") { - path = "/apps/ncdownloader/youtube/get"; - } - let name = type + "-downloads"; + let element = event.target; + //helper.hideDownload(); + let currentType = helper.getContentTableType(); + let path = element.getAttribute("path"); + let name = element.getAttribute("id"); //avoid repeated click - if (currentType === name && helper.enabledPolling) { + if (currentType === name && helper.isPolling()) { return; } - helper.enabledPolling = 1; - //$(tableContainer).removeClass().addClass("table " + name); - container.setAttribute("type", name); - container.className = "table " + name; - let delay = 15000; - if (['active', 'youtube-dl'].includes(type)) { - delay = 1500; + helper.setContentTableType(name); + let delay; + if (!['active-downloads', 'youtube-dl-downloads'].includes(name)) { + delay = 15000; + } + if (name === "youtube-dl-downloads") { + helper.pollingYoutube(); + } else { + helper.polling(delay, path); } - helper.loop(helper.refresh, delay, ...[path]) }; - eventHandler.add("click",".waiting-downloads a",event => clickHandler(event, 'waiting')); - eventHandler.add("click",".complete-downloads a",event => clickHandler(event, 'complete')); - eventHandler.add("click",".active-downloads a",event => clickHandler(event, 'active')); - eventHandler.add("click",".fail-downloads a",event => clickHandler(event, 'fail')); - eventHandler.add("click",".youtube-dl-downloads a",event => clickHandler(event, 'youtube-dl')); - eventHandler.add("click", "#ncdownloader-table-wrapper",".download-file-folder", function (event) { + eventHandler.add("click", ".download-queue a", event => clickHandler(event)); + eventHandler.add("click", "#ncdownloader-table-wrapper", ".download-file-folder", function (event) { event.stopPropagation(); - const path = "/apps/ncdownloader/update"; - let url = helper.generateUrl(path); - Http.getInstance(url).setMethod('GET').send(); + event.preventDefault(); + let ele = event.target; + let url = ele.getAttribute("href"); + helper.scanFolder().then(() => { + helper.redirect(url); + }); }); - helper.polling(function (url) { - url = helper.generateUrl(url); - Http.getInstance(url).setMethod('GET').send(); - }, 60000, "/apps/ncdownloader/update"); } } \ No newline at end of file diff --git a/src/lib/eventHandler.ts b/src/lib/eventHandler.ts index bebabcd..81ad419 100644 --- a/src/lib/eventHandler.ts +++ b/src/lib/eventHandler.ts @@ -14,23 +14,26 @@ const eventHandler = { }); return; } - let el = document.querySelector(target); - if (!el) { + let items = document.querySelectorAll(target); + if (!items) { return; } - el.addEventListener(eventType, function (e) { - let element = e.target as HTMLElement; - if (element === this && selector === target) { - callback.call(element, e); - return; - } - for (; element && element != this; element = element.parentElement) { - if (typeof selector === "string" && element.matches(selector)) { + items.forEach(el => { + el.addEventListener(eventType, function (e) { + let element = e.target as HTMLElement; + if (element === this && selector === target) { callback.call(element, e); - break; + return; } - } - }); + for (; element && element != this; element = element.parentElement) { + if (typeof selector === "string" && element.matches(selector)) { + callback.call(element, e); + break; + } + } + }); + }) + }, remove: function (element: target, eventType: string, callback: callback) { diff --git a/src/lib/polling.ts b/src/lib/polling.ts new file mode 100644 index 0000000..0b4dedf --- /dev/null +++ b/src/lib/polling.ts @@ -0,0 +1,49 @@ +type callback = (...args: any[]) => void + +class Polling { + private static instance: Polling; + private timeoutID: number; + private delay: number = 1500; + private enabled: boolean = false; + constructor() { + this.enabled = false; + } + static create(): Polling { + this.instance = this.instance || new Polling(); + return this.instance; + } + + enable() { + this.enabled = true; + return this; + } + disable() { + this.enabled = false; + return this; + } + isEnabled() { + return this.enabled; + } + setDelay(time: number = 1500): Polling { + this.delay = time; + return this; + } + + run(callback: callback, ...args: any[]) { + this.clear().enable() + callback(...args); + let timeoutHandler = () => { + if (this.enabled) { + this.run(callback, ...args); + } + } + this.timeoutID = window.setTimeout(timeoutHandler, this.delay); + } + clear() { + if (this.timeoutID) + window.clearTimeout(this.timeoutID); + return this; + } +} + +export default Polling; \ No newline at end of file diff --git a/src/utils/helper.js b/src/utils/helper.js index aba3181..f343a21 100644 --- a/src/utils/helper.js +++ b/src/utils/helper.js @@ -6,7 +6,7 @@ import "toastify-js/src/toastify.css" import { translate as t, translatePlural as n } from '@nextcloud/l10n' import contentTable from '../lib/contentTable'; import Http from '../lib/http' - +import Polling from "../lib/polling"; const helper = { vue: {}, addVue(name, object) { @@ -16,12 +16,45 @@ const helper = { return helper.vue[name]; }, generateUrl: generateUrl, - loop(callback, delay, ...args) { - callback(...args); - clearTimeout(helper.timeoutID); - this.polling(callback, delay, ...args); + isPolling() { + return Polling.create().isEnabled(); + }, + enabePolling() { + Polling.create().enable(); + }, + disablePolling() { + Polling.create().disable().clear(); + }, + polling(delay = 1500, path) { + Polling.create().setDelay(delay).run(helper.refresh, path); + }, + scanFolder(path = "/apps/ncdownloader/scanfolder") { + let url = helper.generateUrl(path); + return new Promise((resolve) => { + Http.getInstance(url).setMethod('GET').setHandler(function (data) { + resolve(data.status); + }).send(); + }); + }, + pollingFolder(delay = 1500) { + Polling.create().setDelay(delay).run(helper.scanFolder); + }, + pollingYoutube(delay = 1500) { + Polling.create().setDelay(delay).run(helper.refresh, "/apps/ncdownloader/youtube/get"); + }, + refresh(path) { + path = path || "/apps/ncdownloader/status/active"; + let url = helper.generateUrl(path); + Http.getInstance(url).setHandler(function (data) { + if (data && data.row) { + contentTable.getInstance(data.title, data.row).create(); + } else { + contentTable.getInstance().noData(); + } + if (data.counter) + helper.updateCounter(data.counter); + }).send(); }, - enabledPolling: 0, trim(string, char) { return string.split(char).filter(Boolean).join(char) }, @@ -32,15 +65,6 @@ const helper = { ucfirst(string) { return string.charAt(0).toUpperCase() + string.slice(1) }, - polling(callback, delay, ...args) { - self = this; - helper.timeoutID = setTimeout(function () { - if (self.enabledPolling) { - callback(...args); - self.polling(callback, delay, ...args); - } - }, delay); - }, isURL(url) { let regex = '^((https?|ftp)://)([a-z0-9-]+\.)?(?:[-a-zA-Z0-9()@:%_\+.~#?&/=]+)$'; const pattern = new RegExp(regex, 'i'); @@ -115,19 +139,6 @@ const helper = { counter.innerHTML = '
' + data[key] + '
'; } }, - refresh(path) { - path = path || "/apps/ncdownloader/status/active"; - let url = helper.generateUrl(path); - Http.getInstance(url).setHandler(function (data) { - if (data && data.row) { - contentTable.getInstance(data.title, data.row).create(); - } else { - contentTable.getInstance().noData(); - } - if (data.counter) - helper.updateCounter(data.counter); - }).send(); - }, html2DOM: function (htmlString) { const parser = new window.DOMParser(); let doc = parser.parseFromString(htmlString, "text/html") @@ -253,6 +264,18 @@ const helper = { resetSearch: function (vm) { vm.$data.loading = 0; contentTable.getInstance([], []).clear(); + }, + redirect(url) { + window.location.href = url; + }, + getContentTableType() { + let container = document.getElementById("ncdownloader-table-wrapper"); + return container.getAttribute("type"); + }, + setContentTableType(name) { + let container = document.getElementById("ncdownloader-table-wrapper"); + container.setAttribute("type", name); + container.className = "table " + name; } } diff --git a/templates/Navigation.php b/templates/Navigation.php index 643b931..e23ef4d 100644 --- a/templates/Navigation.php +++ b/templates/Navigation.php @@ -1,5 +1,12 @@ "active", "label" => "Active Downloads", "id" => "active-downloads", "path" => "/apps/ncdownloader/status/active"], + ["name" => "waiting", "label" => "Waiting Downloads", "id" => "waiting-downloads", "path" => "/apps/ncdownloader/status/waiting"], + ["name" => "fail", "label" => "Failed Downloads", "id" => "failed-downloads", "path" => "/apps/ncdownloader/status/fail"], + ["name" => "complete", "label" => "Complete Downloads", "id" => "complete-downloads", "path" => "/apps/ncdownloader/status/complete"], + ["name" => "youtube-dl", "label" => "Youtube-dl Downloads", "id" => "youtube-dl-downloads", "path" => "/apps/ncdownloader/youtube/get"], +]; ?>
@@ -33,71 +40,21 @@ extract($_);