diff --git a/src/base/searchengine.cpp b/src/base/searchengine.cpp index 624f47d5a..5bb5bd1a9 100644 --- a/src/base/searchengine.cpp +++ b/src/base/searchengine.cpp @@ -266,6 +266,21 @@ void SearchEngine::cancelSearch() } } +void SearchEngine::downloadTorrent(const QString &siteUrl, const QString &url) +{ + QProcess *downloadProcess = new QProcess(this); + downloadProcess->setEnvironment(QProcess::systemEnvironment()); + connect(downloadProcess, SIGNAL(finished(int)), this, SLOT(torrentFileDownloadFinished(int))); + m_downloaders << downloadProcess; + QStringList params { + Utils::Fs::toNativePath(engineLocation() + "/nova2dl.py"), + siteUrl, + url + }; + // Launch search + downloadProcess->start(Utils::Misc::pythonExecutable(), params, QIODevice::ReadOnly); +} + void SearchEngine::startSearch(const QString &pattern, const QString &category, const QStringList &usedPlugins) { // Search process already running or @@ -357,6 +372,21 @@ void SearchEngine::pluginDownloadFailed(const QString &url, const QString &reaso emit pluginInstallationFailed(pluginName, tr("Failed to download the plugin file. %1").arg(reason)); } +void SearchEngine::torrentFileDownloadFinished(int exitcode) +{ + QProcess *downloadProcess = static_cast(sender()); + if (exitcode == 0) { + QString line = QString::fromUtf8(downloadProcess->readAllStandardOutput()).trimmed(); + QStringList parts = line.split(' '); + if (parts.size() == 2) + emit torrentFileDownloaded(parts[0]); + } + + qDebug() << "Deleting downloadProcess"; + m_downloaders.removeOne(downloadProcess); + downloadProcess->deleteLater(); +} + // Update nova.py search plugin if necessary void SearchEngine::updateNova() { @@ -383,6 +413,12 @@ void SearchEngine::updateNova() QFile::copy(":/" + novaFolder + "/nova2.py", filePath); } + filePath = searchDir.absoluteFilePath("nova2dl.py"); + if (getPluginVersion(":/" + novaFolder + "/nova2dl.py") > getPluginVersion(filePath)) { + removePythonScriptIfExists(filePath); + QFile::copy(":/" + novaFolder + "/nova2dl.py", filePath); + } + filePath = searchDir.absoluteFilePath("fix_encoding.py"); QFile::copy(":/" + novaFolder + "/fix_encoding.py", filePath); diff --git a/src/base/searchengine.h b/src/base/searchengine.h index 084affdf3..ab27aa7e0 100644 --- a/src/base/searchengine.h +++ b/src/base/searchengine.h @@ -84,6 +84,8 @@ public: void startSearch(const QString &pattern, const QString &category, const QStringList &usedPlugins); void cancelSearch(); + void downloadTorrent(const QString &siteUrl, const QString &url); + static qreal getPluginVersion(QString filePath); static QString categoryFullName(const QString &categoryName); static QString pluginsLocation(); @@ -102,6 +104,8 @@ signals: void checkForUpdatesFinished(const QHash &updateInfo); void checkForUpdatesFailed(const QString &reason); + void torrentFileDownloaded(const QString &path); + private slots: void onTimeout(); void readSearchOutput(); @@ -110,6 +114,7 @@ private slots: void versionInfoDownloadFailed(const QString &url, const QString &reason); void pluginDownloaded(const QString &url, QString filePath); void pluginDownloadFailed(const QString &url, const QString &reason); + void torrentFileDownloadFinished(int exitcode); private: void update(); @@ -132,6 +137,7 @@ private: bool m_searchStopped; QTimer *m_searchTimeout; QByteArray m_searchResultLineTruncated; + QList m_downloaders; }; #endif // SEARCHENGINE_H diff --git a/src/gui/search/searchtab.cpp b/src/gui/search/searchtab.cpp index efadcfb89..3ead429fb 100644 --- a/src/gui/search/searchtab.cpp +++ b/src/gui/search/searchtab.cpp @@ -132,8 +132,9 @@ SearchTab::~SearchTab() void SearchTab::downloadItem(const QModelIndex &index) { QString torrentUrl = m_proxyModel->data(m_proxyModel->index(index.row(), SearchSortModel::DL_LINK)).toString(); + QString siteUrl = m_proxyModel->data(m_proxyModel->index(index.row(), SearchSortModel::ENGINE_URL)).toString(); setRowColor(index.row(), "blue"); - m_parent->downloadTorrent(torrentUrl); + m_parent->downloadTorrent(siteUrl, torrentUrl); } QHeaderView* SearchTab::header() const diff --git a/src/gui/search/searchwidget.cpp b/src/gui/search/searchwidget.cpp index 9d5009605..9c8345bf2 100644 --- a/src/gui/search/searchwidget.cpp +++ b/src/gui/search/searchwidget.cpp @@ -116,6 +116,7 @@ SearchWidget::SearchWidget(MainWindow *mainWindow) connect(m_searchEngine, SIGNAL(newSearchResults(QList)), SLOT(appendSearchResults(QList))); connect(m_searchEngine, SIGNAL(searchFinished(bool)), SLOT(searchFinished(bool))); connect(m_searchEngine, SIGNAL(searchFailed()), SLOT(searchFailed())); + connect(m_searchEngine, SIGNAL(torrentFileDownloaded(QString)), SLOT(addTorrentToSession(QString))); // Fill in category combobox fillCatCombobox(); @@ -161,6 +162,14 @@ SearchWidget::~SearchWidget() delete m_searchEngine; } +void SearchWidget::downloadTorrent(const QString &siteUrl, const QString &url) +{ + if (url.startsWith("bc://bt/", Qt::CaseInsensitive) || url.startsWith("magnet:", Qt::CaseInsensitive)) + addTorrentToSession(url); + else + m_searchEngine->downloadTorrent(siteUrl, url); +} + void SearchWidget::tab_changed(int t) { //when we switch from a tab that is not empty to another that is empty the download button @@ -187,6 +196,14 @@ void SearchWidget::selectMultipleBox(const QString &text) on_pluginsButton_clicked(); } +void SearchWidget::addTorrentToSession(const QString &source) +{ + if (AddNewTorrentDialog::isEnabled()) + AddNewTorrentDialog::show(source, this); + else + BitTorrent::Session::instance()->addTorrent(source); +} + void SearchWidget::on_pluginsButton_clicked() { PluginSelectDlg *dlg = new PluginSelectDlg(m_searchEngine, this); @@ -281,14 +298,6 @@ void SearchWidget::saveResultsColumnsWidth() Preferences::instance()->setSearchColsWidth(newWidthList.join(" ")); } -void SearchWidget::downloadTorrent(QString url) -{ - if (AddNewTorrentDialog::isEnabled()) - AddNewTorrentDialog::show(url, this); - else - BitTorrent::Session::instance()->addTorrent(url); -} - void SearchWidget::searchStarted() { // Update SearchEngine widgets @@ -391,9 +400,8 @@ void SearchWidget::on_downloadButton_clicked() //QModelIndexList selectedIndexes = currentSearchTab->getCurrentTreeView()->selectionModel()->selectedIndexes(); QModelIndexList selectedIndexes = m_allTabs.at(tabWidget->currentIndex())->getCurrentTreeView()->selectionModel()->selectedIndexes(); foreach (const QModelIndex &index, selectedIndexes) { - if (index.column() == SearchSortModel::NAME) { + if (index.column() == SearchSortModel::NAME) m_allTabs.at(tabWidget->currentIndex())->downloadItem(index); - } } } diff --git a/src/gui/search/searchwidget.h b/src/gui/search/searchwidget.h index 0098b4b7c..b39aeacb0 100644 --- a/src/gui/search/searchwidget.h +++ b/src/gui/search/searchwidget.h @@ -52,7 +52,7 @@ public: explicit SearchWidget(MainWindow *mainWindow); ~SearchWidget(); - void downloadTorrent(QString url); + void downloadTorrent(const QString &siteUrl, const QString &url); void giveFocusToSearchInput(); QTabWidget* searchTabs() const; @@ -73,6 +73,8 @@ private slots: void searchFailed(); void selectMultipleBox(const QString &text); + void addTorrentToSession(const QString &source); + void saveResultsColumnsWidth(); void fillCatCombobox(); void fillPluginComboBox(); @@ -86,7 +88,7 @@ private: SearchEngine *m_searchEngine; QPointer m_currentSearchTab; // Selected tab QPointer m_activeSearchTab; // Tab with running search - QList > m_allTabs; // To store all tabs + QList> m_allTabs; // To store all tabs MainWindow *m_mainWindow; bool m_isNewQueryString; bool m_noSearchResults; diff --git a/src/searchengine.qrc b/src/searchengine.qrc index 800b76f9c..403f0e203 100644 --- a/src/searchengine.qrc +++ b/src/searchengine.qrc @@ -1,54 +1,56 @@ - - - searchengine/nova/fix_encoding.py - searchengine/nova/helpers.py - searchengine/nova/nova2.py - searchengine/nova/novaprinter.py - searchengine/nova/socks.py - searchengine/nova/engines/btdigg.png - searchengine/nova/engines/btdigg.py - searchengine/nova/engines/demonoid.png - searchengine/nova/engines/demonoid.py - searchengine/nova/engines/extratorrent.png - searchengine/nova/engines/extratorrent.py - searchengine/nova/engines/kickasstorrents.png - searchengine/nova/engines/kickasstorrents.py - searchengine/nova/engines/legittorrents.png - searchengine/nova/engines/legittorrents.py - searchengine/nova/engines/mininova.png - searchengine/nova/engines/mininova.py - searchengine/nova/engines/piratebay.png - searchengine/nova/engines/piratebay.py - searchengine/nova/engines/torlock.png - searchengine/nova/engines/torlock.py - searchengine/nova/engines/torrentreactor.png - searchengine/nova/engines/torrentreactor.py - searchengine/nova/engines/torrentz.png - searchengine/nova/engines/torrentz.py - searchengine/nova3/helpers.py - searchengine/nova3/nova2.py - searchengine/nova3/novaprinter.py - searchengine/nova3/sgmllib3.py - searchengine/nova3/socks.py - searchengine/nova3/engines/btdigg.png - searchengine/nova3/engines/btdigg.py - searchengine/nova3/engines/demonoid.png - searchengine/nova3/engines/demonoid.py - searchengine/nova3/engines/extratorrent.png - searchengine/nova3/engines/extratorrent.py - searchengine/nova3/engines/kickasstorrents.png - searchengine/nova3/engines/kickasstorrents.py - searchengine/nova3/engines/legittorrents.png - searchengine/nova3/engines/legittorrents.py - searchengine/nova3/engines/mininova.png - searchengine/nova3/engines/mininova.py - searchengine/nova3/engines/piratebay.png - searchengine/nova3/engines/piratebay.py - searchengine/nova3/engines/torlock.png - searchengine/nova3/engines/torlock.py - searchengine/nova3/engines/torrentreactor.png - searchengine/nova3/engines/torrentreactor.py - searchengine/nova3/engines/torrentz.png - searchengine/nova3/engines/torrentz.py - + + + searchengine/nova/fix_encoding.py + searchengine/nova/helpers.py + searchengine/nova/nova2.py + searchengine/nova/novaprinter.py + searchengine/nova/socks.py + searchengine/nova/engines/btdigg.png + searchengine/nova/engines/btdigg.py + searchengine/nova/engines/demonoid.png + searchengine/nova/engines/demonoid.py + searchengine/nova/engines/extratorrent.png + searchengine/nova/engines/extratorrent.py + searchengine/nova/engines/kickasstorrents.png + searchengine/nova/engines/kickasstorrents.py + searchengine/nova/engines/legittorrents.png + searchengine/nova/engines/legittorrents.py + searchengine/nova/engines/mininova.png + searchengine/nova/engines/mininova.py + searchengine/nova/engines/piratebay.png + searchengine/nova/engines/piratebay.py + searchengine/nova/engines/torlock.png + searchengine/nova/engines/torlock.py + searchengine/nova/engines/torrentreactor.png + searchengine/nova/engines/torrentreactor.py + searchengine/nova/engines/torrentz.png + searchengine/nova/engines/torrentz.py + searchengine/nova3/helpers.py + searchengine/nova3/nova2.py + searchengine/nova3/novaprinter.py + searchengine/nova3/sgmllib3.py + searchengine/nova3/socks.py + searchengine/nova3/engines/btdigg.png + searchengine/nova3/engines/btdigg.py + searchengine/nova3/engines/demonoid.png + searchengine/nova3/engines/demonoid.py + searchengine/nova3/engines/extratorrent.png + searchengine/nova3/engines/extratorrent.py + searchengine/nova3/engines/kickasstorrents.png + searchengine/nova3/engines/kickasstorrents.py + searchengine/nova3/engines/legittorrents.png + searchengine/nova3/engines/legittorrents.py + searchengine/nova3/engines/mininova.png + searchengine/nova3/engines/mininova.py + searchengine/nova3/engines/piratebay.png + searchengine/nova3/engines/piratebay.py + searchengine/nova3/engines/torlock.png + searchengine/nova3/engines/torlock.py + searchengine/nova3/engines/torrentreactor.png + searchengine/nova3/engines/torrentreactor.py + searchengine/nova3/engines/torrentz.png + searchengine/nova3/engines/torrentz.py + searchengine/nova/nova2dl.py + searchengine/nova3/nova2dl.py + diff --git a/src/searchengine/nova/nova2dl.py b/src/searchengine/nova/nova2dl.py new file mode 100644 index 000000000..31681a268 --- /dev/null +++ b/src/searchengine/nova/nova2dl.py @@ -0,0 +1,61 @@ +#VERSION: 1.20 + +# Author: +# Christophe DUMEZ (chris@qbittorrent.org) + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import sys +import os +import glob +from helpers import download_file + +supported_engines = dict() + +engines = glob.glob(os.path.join(os.path.dirname(__file__), 'engines','*.py')) +for engine in engines: + e = engine.split(os.sep)[-1][:-3] + if len(e.strip()) == 0: continue + if e.startswith('_'): continue + try: + exec("from engines.%s import %s"%(e,e)) + exec("engine_url = %s.url"%e) + supported_engines[engine_url] = e + except: + pass + +if __name__ == '__main__': + if len(sys.argv) < 3: + raise SystemExit('./nova2dl.py engine_url download_parameter') + engine_url = sys.argv[1].strip() + download_param = sys.argv[2].strip() + if engine_url not in list(supported_engines.keys()): + raise SystemExit('./nova2dl.py: this engine_url was not recognized') + exec("engine = %s()"%supported_engines[engine_url]) + if hasattr(engine, 'download_torrent'): + engine.download_torrent(download_param) + else: + print(download_file(download_param)) + sys.exit(0) diff --git a/src/searchengine/nova3/nova2dl.py b/src/searchengine/nova3/nova2dl.py new file mode 100644 index 000000000..31681a268 --- /dev/null +++ b/src/searchengine/nova3/nova2dl.py @@ -0,0 +1,61 @@ +#VERSION: 1.20 + +# Author: +# Christophe DUMEZ (chris@qbittorrent.org) + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# * Neither the name of the author nor the names of its contributors may be +# used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +import sys +import os +import glob +from helpers import download_file + +supported_engines = dict() + +engines = glob.glob(os.path.join(os.path.dirname(__file__), 'engines','*.py')) +for engine in engines: + e = engine.split(os.sep)[-1][:-3] + if len(e.strip()) == 0: continue + if e.startswith('_'): continue + try: + exec("from engines.%s import %s"%(e,e)) + exec("engine_url = %s.url"%e) + supported_engines[engine_url] = e + except: + pass + +if __name__ == '__main__': + if len(sys.argv) < 3: + raise SystemExit('./nova2dl.py engine_url download_parameter') + engine_url = sys.argv[1].strip() + download_param = sys.argv[2].strip() + if engine_url not in list(supported_engines.keys()): + raise SystemExit('./nova2dl.py: this engine_url was not recognized') + exec("engine = %s()"%supported_engines[engine_url]) + if hasattr(engine, 'download_torrent'): + engine.download_torrent(download_param) + else: + print(download_file(download_param)) + sys.exit(0)