diff --git a/appinfo/application.php b/appinfo/application.php index a6142c6..efd0ea6 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -5,12 +5,16 @@ namespace OCA\NCDownloader\AppInfo; use OCA\NCDownloader\Controller\Aria2Controller; use OCA\NCDownloader\Controller\MainController; use OCA\NCDownloader\Controller\YoutubeController; +use OCA\NCDownloader\Search\Sites\bitSearch; +use OCA\NCDownloader\Search\Sites\TPB; use OCA\NCDownloader\Tools\Aria2; use OCA\NCDownloader\Tools\Helper; use OCA\NCDownloader\Tools\Settings; use OCA\NCDownloader\Tools\Youtube; use OCP\AppFramework\App; use OCP\IContainer; +use Symfony\Component\DomCrawler\Crawler; +use Symfony\Component\HttpClient\HttpClient; class Application extends App { @@ -71,6 +75,22 @@ class Application extends App $container->query('Youtube') ); }); + $container->registerService('crawler', function () { + return new Crawler(); + }); + $container->registerService('httpClient', function () { + return HttpClient::create(); + }); + $container->registerService(TPB::class, function (IContainer $container) { + $crawler = $container->query('crawler'); + $client = $container->query('httpClient'); + return new TPB($crawler, $client); + }); + $container->registerService(bitSearch::class, function (IContainer $container) { + $crawler = $container->query('crawler'); + $client = $container->query('httpClient'); + return new bitSearch($crawler, $client); + }); } private function getRealDownloadDir() diff --git a/css/style.css b/css/style.css index 179a8bb..a5502fa 100644 --- a/css/style.css +++ b/css/style.css @@ -7,6 +7,15 @@ a { text-decoration: none; } +#ncdownloader-form-wrapper .form-input-wrapper { + display : flex; + flex-flow: row nowrap; +} +#ncdownloader-form-wrapper .form-input-wrapper select, +#ncdownloader-form-wrapper .form-input-wrapper input { + justify-content: left; +} + a:hover, a:focus { color : #23527c; @@ -17,11 +26,6 @@ a:focus { outline : 5px auto -webkit-focus-ring-color; outline-offset: -2px; } - -#ncdownloader-form-wrapper input { - display: block; -} - div .number { background : none repeat scroll 0 0 #5f5853; border-radius: 999px; @@ -37,7 +41,7 @@ div .number { #ncdownloader-form-wrapper input[type=text] { padding: 0px 5px; - width : 100%; + flex:auto; } #ncdownloader-table-data .table-cell { @@ -51,9 +55,11 @@ div .number { .icon-purge { background-image: url('../img/trash.svg'); } + .icon-unpause { background-image: url('../img/play.svg'); } + #ncdownloader-action-links-container { position: relative; } diff --git a/lib/Controller/Aria2Controller.php b/lib/Controller/Aria2Controller.php index c8852a2..49ed3d3 100644 --- a/lib/Controller/Aria2Controller.php +++ b/lib/Controller/Aria2Controller.php @@ -134,11 +134,11 @@ class Aria2Controller extends Controller if (isset($resp['error'])) { return new JSONResponse($resp); } - $data = $this->prepareResp($resp); + $data = $this->transformResp($resp); $data['counter'] = $counter; return new JSONResponse($data); } - private function prepareResp($resp) + private function transformResp($resp) { $data = []; @@ -191,7 +191,9 @@ class Aria2Controller extends Controller $left = Helper::formatInterval($remaining); $numSeeders = $value['numSeeders'] ?? 0; - $extraInfo = "Seeders: $numSeeders"; + $upload = $value['uploadLength'] ?? 0; + $upload = Helper::formatBytes($upload); + $extraInfo = "Seeders: $numSeeders|Up:$upload"; // $numPeers = isset($peers['result']) ? count($peers['result']) : 0; $value['progress'] = array(sprintf("%s(%.2f%%)", $completed, $percentage), $extraInfo); $timestamp = $timestamp ?? 0; diff --git a/lib/Controller/SearchController.php b/lib/Controller/SearchController.php index 98151ce..5e55fe8 100644 --- a/lib/Controller/SearchController.php +++ b/lib/Controller/SearchController.php @@ -19,12 +19,15 @@ class SearchController extends Controller $this->appName = $appName; $this->uid = $UserId; $this->urlGenerator = \OC::$server->getURLGenerator(); + $this->search = new torrentSearch(); } public function execute() { $keyword = trim($this->request->getParam('form_input_text')); - $data = torrentSearch::go($keyword); + $site = trim($this->request->getParam('select-value-search')); + $this->search->setSite($site); + $data = $this->search->go($keyword); $resp['title'] = ['title', 'seeders', 'info', 'actions']; $resp['row'] = $data; return new JSONResponse($resp); diff --git a/lib/Search/Sites/TPB.php b/lib/Search/Sites/TPB.php index bc54c7e..3836404 100644 --- a/lib/Search/Sites/TPB.php +++ b/lib/Search/Sites/TPB.php @@ -3,7 +3,7 @@ namespace OCA\NCDownloader\Search\Sites; //The Piratebay -class TPB +class TPB implements searchBase { //html content private $content = null; diff --git a/lib/Search/Sites/bitSearch.php b/lib/Search/Sites/bitSearch.php new file mode 100644 index 0000000..d464181 --- /dev/null +++ b/lib/Search/Sites/bitSearch.php @@ -0,0 +1,65 @@ +client = $client; + $this->crawler = $crawler; + } + public function search($keyword) + { + $this->query = ['q' => trim($keyword), 'sort' => 'seeders']; + $this->searchUrl = $this->baseUrl; + //$this->setContent(file_get_contents(__DIR__ . "/BitSearch.html")); + $this->crawler->add($this->getContent()); + return $this->parse(); + } + public function setContent($content) + { + $this->content = $content; + } + public function getContent() + { + if ($this->content) { + return $this->content; + } + $response = $this->client->request('GET', $this->searchUrl, ['query' => $this->query]); + return $response->getContent(); + } + public function parse() + { + + $data = $this->crawler->filter(".w3-col.s12.mt-4 .search-result")->each(function ($node, $i) { + + if ($node->getNode(0)) { + try { + $title = $node->filter(".info h5.title")->text(); + $infoNode = $node->filter(".info .stats div"); + $count = $infoNode->count(); + $info = []; + for ($i = 0; $i < $count; $i++) { + $name = strtolower($infoNode->filter("img")->eq($i)->attr("alt")); + $info[$name] = trim($infoNode->eq($i)->text()); + } + $seeders = $info['seeder']; + $info = sprintf("%s on %s", $info['size'], $info['date']); + $magnetLink = $node->filter(".links.center-flex a:nth-child(2)")->attr("href"); + return ['title' => $title, 'data-link' => $magnetLink, 'seeders' => $seeders, 'info' => $info]; + } catch (\Exception $e) { + //echo $e->getMessage(); + } + } + + }); + return $data; + } +} diff --git a/lib/Search/Sites/searchBase.php b/lib/Search/Sites/searchBase.php new file mode 100644 index 0000000..ad4a009 --- /dev/null +++ b/lib/Search/Sites/searchBase.php @@ -0,0 +1,10 @@ +search($keyword); - self::addAction($data); + $this->container = \OC::$server->query(IServerContainer::class); + $this->site = __NAMESPACE__ . '\Sites\TPB'; + } + public function go($keyword) + { + $siteInst = $this->container->query($this->site); + $data = $siteInst->search($keyword); + $this->addAction($data); return $data; } - private static function addAction(&$data) + public function setSite($site) + { + if (strpos($site, '\\') !== false) { + $this->site = $site; + } else { + $this->site = __NAMESPACE__ . '\Sites\\' . $site; + } + } + + private function addAction(&$data) { foreach ($data as $key => &$value) { if (!$value) { diff --git a/src/inputAction.js b/src/inputAction.js index bf06600..96ef404 100644 --- a/src/inputAction.js +++ b/src/inputAction.js @@ -36,7 +36,10 @@ const createInputBox = (event, type) => { } let container; if (type === 'search') { - container = inputBox.getInstance(name, type, path).create().addSpinner(); + let selectOptions = []; + selectOptions.push({name:'bitSearch',label:'BITSEARCH',default:0}); + selectOptions.push({name:'TPB',label:'THEPIRATEBAY',selected:1}); + container = inputBox.getInstance(name, type, path).createOptions(selectOptions).create().addSpinner(); //container.appendChild(inputBox.createLoading()); } else { container = inputBox.getInstance(name, type, path).create().getContainer(); @@ -63,6 +66,8 @@ const inputHandler = (event) => { event.preventDefault(); let element = event.target; toggleSpinner(element); + let formWrapper = element.closest('form'); + let inputData = helper.getData('form-input-wrapper'); let inputValue = inputData.form_input_text; if (inputData.type !== 'search' && !helper.isURL(inputValue) && !helper.isMagnetURI(inputValue)) { @@ -82,7 +87,7 @@ const inputHandler = (event) => { if (data !== null && data.hasOwnProperty("file")) { helper.message(t("ncdownloader", "Downloading" + " " + data.file)); } - toggleSpinner(element); + toggleSpinner(element); if (data && data.title) { const tableInst = nctable.getInstance(data.title, data.row); tableInst.actionLink = false; @@ -90,7 +95,7 @@ const inputHandler = (event) => { tableInst.create(); } } - const path = inputData.path || basePath + "/new"; + const path = formWrapper.dataset.path || basePath + "/new"; let url = helper.generateUrl(path); Http.getInstance(url).setData(inputData).setHandler(function (data) { successCallback(data, element); diff --git a/src/inputBox.js b/src/inputBox.js index d72769d..6c1ac82 100644 --- a/src/inputBox.js +++ b/src/inputBox.js @@ -4,43 +4,78 @@ import helper from './helper' class inputBox { path; - constructor(name, id, path = null) { - this.name = name; + selectOptions = []; + constructor(btnName, id, path = null) { + this.btnName = btnName; this.id = id; this.path = path; } - static getInstance(name, id, path = null) { - return new inputBox(name, id, path); + static getInstance(btnName, id, path = null) { + return new inputBox(btnName, id, path); } create() { - this.container = this._createForm(); + this.formContainer = this._createForm(); this.textInput = this._createTextInput(this.id); - this.controlsContainer = this._createControlsContainer(); - this.container.appendChild(this.textInput); - this.controlsContainer.appendChild(this._createControls()); - this.container.appendChild(this.controlsContainer); + this.buttonContainer = this._createButtonContainer(); + this.formContainer.appendChild(this.textInput); + if (this.selectOptions.length !== 0) { + this.formContainer.appendChild(this._createSelect()); + } + this.buttonContainer.appendChild(this._createButton()); + this.formContainer.appendChild(this.buttonContainer); return this; } getContainer() { - return this.container; + return this.formContainer; } setPath(path) { this.path = path; return this; } - _createControlsContainer() { + _createButtonContainer() { let div = document.createElement("div"); - div.classList.add("controls-container"); + div.classList.add("button-container"); return div; } _createForm() { let container = document.createElement("form"); container.classList.add("form-input-wrapper"); container.setAttribute('id', 'form-input-wrapper'); + if (this.path) { + container.setAttribute('data-path', this.path); + } return container; } + _createSelect(id) { + id = id || this.id; + let select = document.createElement('select'); + select.setAttribute('id', "select-value-" + id); + select.setAttribute('value', ''); + select.classList.add('form-select'); + this.selectOptions.forEach(element => { + select.appendChild(element); + }); + return select; + } + + createOptions(data) { + if (!data) { + return; + } + data.forEach(element => { + let option = document.createElement('option'); + option.setAttribute('value', element.name); + let text = document.createTextNode(element.label); + option.appendChild(text); + if (element.selected) { + option.setAttribute("selected", "selected"); + } + this.selectOptions.push(option); + }); + return this; + } _createTextInput(id) { id = id || 'general'; let textInput = document.createElement('input'); @@ -48,18 +83,15 @@ class inputBox { textInput.setAttribute('id', "form_input_text"); textInput.setAttribute('data-type', id); textInput.setAttribute('value', ''); - if (this.path) { - textInput.setAttribute('data-path', this.path); - } textInput.classList.add('form-input-text'); return textInput; } - _createControls() { + _createButton() { let button = document.createElement('button'); - button.setAttribute('type', this.name); + button.setAttribute('type', this.btnName); button.setAttribute('id', 'form-input-button'); - //buttonInput.setAttribute('value', t('ncdownloader', helper.ucfirst(name))); - let text = document.createTextNode(t('ncdownloader', helper.ucfirst(this.name))); + //buttonInput.setAttribute('value', t('ncdownloader', helper.ucfirst(btnName))); + let text = document.createTextNode(t('ncdownloader', helper.ucfirst(this.btnName))); button.appendChild(text); return button; } @@ -69,8 +101,8 @@ class inputBox { let doc = parser.parseFromString(htmlString, "text/html") let element = doc.querySelector(".bs-spinner"); element.style.display = 'none'; - this.controlsContainer.appendChild(element); - return this.container; + this.buttonContainer.appendChild(element); + return this.formContainer; } }