mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2026-01-02 21:52:32 -06:00
Improve Search handling
Legacy SearchEngine class really has three roles: 1. Manage search plugins, 2. Handle the search job, and 3. Handle the download of the torrent file using the search plugin. Now it is splitted into 3 classes: SearchManager, SearchHandler and SearchDownloadHandler. Search GUI is also improved.
This commit is contained in:
66
src/base/search/searchdownloadhandler.cpp
Normal file
66
src/base/search/searchdownloadhandler.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2018 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "searchdownloadhandler.h"
|
||||
|
||||
#include <QProcess>
|
||||
|
||||
#include "../utils/fs.h"
|
||||
#include "../utils/misc.h"
|
||||
#include "searchpluginmanager.h"
|
||||
|
||||
SearchDownloadHandler::SearchDownloadHandler(const QString &siteUrl, const QString &url, SearchPluginManager *manager)
|
||||
: QObject {manager}
|
||||
, m_manager {manager}
|
||||
, m_downloadProcess {new QProcess {this}}
|
||||
{
|
||||
m_downloadProcess->setEnvironment(QProcess::systemEnvironment());
|
||||
connect(m_downloadProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished)
|
||||
, this, &SearchDownloadHandler::downloadProcessFinished);
|
||||
const QStringList params {
|
||||
Utils::Fs::toNativePath(m_manager->engineLocation() + "/nova2dl.py"),
|
||||
siteUrl,
|
||||
url
|
||||
};
|
||||
// Launch search
|
||||
m_downloadProcess->start(Utils::Misc::pythonExecutable(), params, QIODevice::ReadOnly);
|
||||
}
|
||||
|
||||
void SearchDownloadHandler::downloadProcessFinished(int exitcode)
|
||||
{
|
||||
QString path;
|
||||
|
||||
if ((exitcode == 0) && (m_downloadProcess->exitStatus() == QProcess::NormalExit)) {
|
||||
const QString line = QString::fromUtf8(m_downloadProcess->readAllStandardOutput()).trimmed();
|
||||
const QVector<QStringRef> parts = line.splitRef(' ');
|
||||
if (parts.size() == 2)
|
||||
path = parts[0].toString();
|
||||
}
|
||||
|
||||
emit downloadFinished(path);
|
||||
}
|
||||
53
src/base/search/searchdownloadhandler.h
Normal file
53
src/base/search/searchdownloadhandler.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2018 Vladimir Golovnev <glassez@yandex.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QProcess;
|
||||
class SearchPluginManager;
|
||||
|
||||
class SearchDownloadHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(SearchDownloadHandler)
|
||||
|
||||
friend class SearchPluginManager;
|
||||
|
||||
SearchDownloadHandler(const QString &siteUrl, const QString &url, SearchPluginManager *manager);
|
||||
|
||||
signals:
|
||||
void downloadFinished(const QString &path);
|
||||
|
||||
private:
|
||||
void downloadProcessFinished(int exitcode);
|
||||
|
||||
SearchPluginManager *m_manager;
|
||||
QProcess *m_downloadProcess;
|
||||
};
|
||||
199
src/base/search/searchhandler.cpp
Normal file
199
src/base/search/searchhandler.cpp
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "searchhandler.h"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QTimer>
|
||||
|
||||
#include "../utils/fs.h"
|
||||
#include "../utils/misc.h"
|
||||
#include "searchpluginmanager.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
enum SearchResultColumn
|
||||
{
|
||||
PL_DL_LINK,
|
||||
PL_NAME,
|
||||
PL_SIZE,
|
||||
PL_SEEDS,
|
||||
PL_LEECHS,
|
||||
PL_ENGINE_URL,
|
||||
PL_DESC_LINK,
|
||||
NB_PLUGIN_COLUMNS
|
||||
};
|
||||
}
|
||||
|
||||
SearchHandler::SearchHandler(const QString &pattern, const QString &category, const QStringList &usedPlugins, SearchPluginManager *manager)
|
||||
: QObject {manager}
|
||||
, m_pattern {pattern}
|
||||
, m_category {category}
|
||||
, m_usedPlugins {usedPlugins}
|
||||
, m_manager {manager}
|
||||
, m_searchProcess {new QProcess {this}}
|
||||
, m_searchTimeout {new QTimer {this}}
|
||||
{
|
||||
// Load environment variables (proxy)
|
||||
m_searchProcess->setEnvironment(QProcess::systemEnvironment());
|
||||
|
||||
const QStringList params {
|
||||
Utils::Fs::toNativePath(m_manager->engineLocation() + "/nova2.py"),
|
||||
m_usedPlugins.join(","),
|
||||
m_category
|
||||
};
|
||||
|
||||
// Launch search
|
||||
m_searchProcess->setProgram(Utils::Misc::pythonExecutable());
|
||||
m_searchProcess->setArguments(params + m_pattern.split(" "));
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
|
||||
connect(m_searchProcess, &QProcess::errorOccurred, this, &SearchHandler::processFailed);
|
||||
#else
|
||||
connect(m_searchProcess, static_cast<void(QProcess::*)(QProcess::ProcessError)>(&QProcess::error)
|
||||
, this, &SearchHandler::processFailed);
|
||||
#endif
|
||||
connect(m_searchProcess, &QProcess::readyReadStandardOutput, this, &SearchHandler::readSearchOutput);
|
||||
connect(m_searchProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished)
|
||||
, this, &SearchHandler::processFinished);
|
||||
|
||||
m_searchTimeout->setSingleShot(true);
|
||||
connect(m_searchTimeout, &QTimer::timeout, this, &SearchHandler::cancelSearch);
|
||||
m_searchTimeout->start(180000); // 3 min
|
||||
|
||||
// deferred start allows clients to handle starting-related signals
|
||||
QTimer::singleShot(0, this, [this]() { m_searchProcess->start(QIODevice::ReadOnly); });
|
||||
}
|
||||
|
||||
bool SearchHandler::isActive() const
|
||||
{
|
||||
return (m_searchProcess->state() != QProcess::NotRunning);
|
||||
}
|
||||
|
||||
void SearchHandler::cancelSearch()
|
||||
{
|
||||
if ((m_searchProcess->state() == QProcess::NotRunning) || m_searchCancelled)
|
||||
return;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
m_searchProcess->kill();
|
||||
#else
|
||||
m_searchProcess->terminate();
|
||||
#endif
|
||||
m_searchCancelled = true;
|
||||
m_searchTimeout->stop();
|
||||
}
|
||||
|
||||
// Slot called when QProcess is Finished
|
||||
// QProcess can be finished for 3 reasons:
|
||||
// Error | Stopped by user | Finished normally
|
||||
void SearchHandler::processFinished(int exitcode)
|
||||
{
|
||||
m_searchTimeout->stop();
|
||||
|
||||
if (m_searchCancelled)
|
||||
emit searchFinished(true);
|
||||
else if ((m_searchProcess->exitStatus() == QProcess::NormalExit) && (exitcode == 0))
|
||||
emit searchFinished(false);
|
||||
else
|
||||
emit searchFailed();
|
||||
}
|
||||
|
||||
// search QProcess return output as soon as it gets new
|
||||
// stuff to read. We split it into lines and parse each
|
||||
// line to SearchResult calling parseSearchResult().
|
||||
void SearchHandler::readSearchOutput()
|
||||
{
|
||||
QByteArray output = m_searchProcess->readAllStandardOutput();
|
||||
output.replace("\r", "");
|
||||
QList<QByteArray> lines = output.split('\n');
|
||||
if (!m_searchResultLineTruncated.isEmpty())
|
||||
lines.prepend(m_searchResultLineTruncated + lines.takeFirst());
|
||||
m_searchResultLineTruncated = lines.takeLast().trimmed();
|
||||
|
||||
QList<SearchResult> searchResultList;
|
||||
foreach (const QByteArray &line, lines) {
|
||||
SearchResult searchResult;
|
||||
if (parseSearchResult(QString::fromUtf8(line), searchResult))
|
||||
searchResultList << searchResult;
|
||||
}
|
||||
|
||||
if (!searchResultList.isEmpty()) {
|
||||
m_results.append(searchResultList);
|
||||
emit newSearchResults(searchResultList);
|
||||
}
|
||||
}
|
||||
|
||||
void SearchHandler::processFailed()
|
||||
{
|
||||
if (!m_searchCancelled)
|
||||
emit searchFailed();
|
||||
}
|
||||
|
||||
// Parse one line of search results list
|
||||
// Line is in the following form:
|
||||
// file url | file name | file size | nb seeds | nb leechers | Search engine url
|
||||
bool SearchHandler::parseSearchResult(const QString &line, SearchResult &searchResult)
|
||||
{
|
||||
const QStringList parts = line.split("|");
|
||||
const int nbFields = parts.size();
|
||||
if (nbFields < (NB_PLUGIN_COLUMNS - 1)) return false; // -1 because desc_link is optional
|
||||
|
||||
searchResult = SearchResult();
|
||||
searchResult.fileUrl = parts.at(PL_DL_LINK).trimmed(); // download URL
|
||||
searchResult.fileName = parts.at(PL_NAME).trimmed(); // Name
|
||||
searchResult.fileSize = parts.at(PL_SIZE).trimmed().toLongLong(); // Size
|
||||
bool ok = false;
|
||||
searchResult.nbSeeders = parts.at(PL_SEEDS).trimmed().toLongLong(&ok); // Seeders
|
||||
if (!ok || (searchResult.nbSeeders < 0))
|
||||
searchResult.nbSeeders = -1;
|
||||
searchResult.nbLeechers = parts.at(PL_LEECHS).trimmed().toLongLong(&ok); // Leechers
|
||||
if (!ok || (searchResult.nbLeechers < 0))
|
||||
searchResult.nbLeechers = -1;
|
||||
searchResult.siteUrl = parts.at(PL_ENGINE_URL).trimmed(); // Search site URL
|
||||
if (nbFields == NB_PLUGIN_COLUMNS)
|
||||
searchResult.descrLink = parts.at(PL_DESC_LINK).trimmed(); // Description Link
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SearchPluginManager *SearchHandler::manager() const
|
||||
{
|
||||
return m_manager;
|
||||
}
|
||||
|
||||
QList<SearchResult> SearchHandler::results() const
|
||||
{
|
||||
return m_results;
|
||||
}
|
||||
|
||||
QString SearchHandler::pattern() const
|
||||
{
|
||||
return m_pattern;
|
||||
}
|
||||
90
src/base/search/searchhandler.h
Normal file
90
src/base/search/searchhandler.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
|
||||
class QProcess;
|
||||
class QTimer;
|
||||
|
||||
struct SearchResult
|
||||
{
|
||||
QString fileName;
|
||||
QString fileUrl;
|
||||
qlonglong fileSize;
|
||||
qlonglong nbSeeders;
|
||||
qlonglong nbLeechers;
|
||||
QString siteUrl;
|
||||
QString descrLink;
|
||||
};
|
||||
|
||||
class SearchPluginManager;
|
||||
|
||||
class SearchHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(SearchHandler)
|
||||
|
||||
friend class SearchPluginManager;
|
||||
|
||||
SearchHandler(const QString &pattern, const QString &category
|
||||
, const QStringList &usedPlugins, SearchPluginManager *manager);
|
||||
|
||||
public:
|
||||
bool isActive() const;
|
||||
QString pattern() const;
|
||||
SearchPluginManager *manager() const;
|
||||
QList<SearchResult> results() const;
|
||||
|
||||
void cancelSearch();
|
||||
|
||||
signals:
|
||||
void searchFinished(bool cancelled = false);
|
||||
void searchFailed();
|
||||
void newSearchResults(const QList<SearchResult> &results);
|
||||
|
||||
private:
|
||||
void readSearchOutput();
|
||||
void processFailed();
|
||||
void processFinished(int exitcode);
|
||||
bool parseSearchResult(const QString &line, SearchResult &searchResult);
|
||||
|
||||
const QString m_pattern;
|
||||
const QString m_category;
|
||||
const QStringList m_usedPlugins;
|
||||
SearchPluginManager *m_manager;
|
||||
QProcess *m_searchProcess;
|
||||
QTimer *m_searchTimeout;
|
||||
QByteArray m_searchResultLineTruncated;
|
||||
bool m_searchCancelled = false;
|
||||
QList<SearchResult> m_results;
|
||||
};
|
||||
535
src/base/search/searchpluginmanager.cpp
Normal file
535
src/base/search/searchpluginmanager.cpp
Normal file
@@ -0,0 +1,535 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#include "searchpluginmanager.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QDomDocument>
|
||||
#include <QDomElement>
|
||||
#include <QDomNode>
|
||||
#include <QList>
|
||||
#include <QProcess>
|
||||
|
||||
#include "base/logger.h"
|
||||
#include "base/net/downloadmanager.h"
|
||||
#include "base/net/downloadhandler.h"
|
||||
#include "base/preferences.h"
|
||||
#include "base/profile.h"
|
||||
#include "base/utils/fs.h"
|
||||
#include "base/utils/misc.h"
|
||||
#include "searchdownloadhandler.h"
|
||||
#include "searchhandler.h"
|
||||
|
||||
static inline void removePythonScriptIfExists(const QString &scriptPath)
|
||||
{
|
||||
Utils::Fs::forceRemove(scriptPath);
|
||||
Utils::Fs::forceRemove(scriptPath + "c");
|
||||
}
|
||||
|
||||
const QHash<QString, QString> SearchPluginManager::m_categoryNames {
|
||||
{"all", QT_TRANSLATE_NOOP("SearchEngine", "All categories")},
|
||||
{"movies", QT_TRANSLATE_NOOP("SearchEngine", "Movies")},
|
||||
{"tv", QT_TRANSLATE_NOOP("SearchEngine", "TV shows")},
|
||||
{"music", QT_TRANSLATE_NOOP("SearchEngine", "Music")},
|
||||
{"games", QT_TRANSLATE_NOOP("SearchEngine", "Games")},
|
||||
{"anime", QT_TRANSLATE_NOOP("SearchEngine", "Anime")},
|
||||
{"software", QT_TRANSLATE_NOOP("SearchEngine", "Software")},
|
||||
{"pictures", QT_TRANSLATE_NOOP("SearchEngine", "Pictures")},
|
||||
{"books", QT_TRANSLATE_NOOP("SearchEngine", "Books")}
|
||||
};
|
||||
|
||||
SearchPluginManager::SearchPluginManager()
|
||||
: m_updateUrl(QString("http://searchplugins.qbittorrent.org/%1/engines/").arg(Utils::Misc::pythonVersion() >= 3 ? "nova3" : "nova"))
|
||||
{
|
||||
updateNova();
|
||||
update();
|
||||
}
|
||||
|
||||
SearchPluginManager::~SearchPluginManager()
|
||||
{
|
||||
qDeleteAll(m_plugins);
|
||||
}
|
||||
|
||||
QStringList SearchPluginManager::allPlugins() const
|
||||
{
|
||||
return m_plugins.keys();
|
||||
}
|
||||
|
||||
QStringList SearchPluginManager::enabledPlugins() const
|
||||
{
|
||||
QStringList plugins;
|
||||
foreach (const PluginInfo *plugin, m_plugins.values()) {
|
||||
if (plugin->enabled)
|
||||
plugins << plugin->name;
|
||||
}
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
||||
QStringList SearchPluginManager::supportedCategories() const
|
||||
{
|
||||
QStringList result;
|
||||
foreach (const PluginInfo *plugin, m_plugins.values()) {
|
||||
if (plugin->enabled) {
|
||||
foreach (QString cat, plugin->supportedCategories) {
|
||||
if (!result.contains(cat))
|
||||
result << cat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PluginInfo *SearchPluginManager::pluginInfo(const QString &name) const
|
||||
{
|
||||
return m_plugins.value(name);
|
||||
}
|
||||
|
||||
void SearchPluginManager::enablePlugin(const QString &name, bool enabled)
|
||||
{
|
||||
PluginInfo *plugin = m_plugins.value(name, 0);
|
||||
if (plugin) {
|
||||
plugin->enabled = enabled;
|
||||
// Save to Hard disk
|
||||
Preferences *const pref = Preferences::instance();
|
||||
QStringList disabledPlugins = pref->getSearchEngDisabled();
|
||||
if (enabled)
|
||||
disabledPlugins.removeAll(name);
|
||||
else if (!disabledPlugins.contains(name))
|
||||
disabledPlugins.append(name);
|
||||
pref->setSearchEngDisabled(disabledPlugins);
|
||||
|
||||
emit pluginEnabled(name, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
// Updates shipped plugin
|
||||
void SearchPluginManager::updatePlugin(const QString &name)
|
||||
{
|
||||
installPlugin(QString("%1%2.py").arg(m_updateUrl).arg(name));
|
||||
}
|
||||
|
||||
// Install or update plugin from file or url
|
||||
void SearchPluginManager::installPlugin(const QString &source)
|
||||
{
|
||||
qDebug("Asked to install plugin at %s", qUtf8Printable(source));
|
||||
|
||||
if (Utils::Misc::isUrl(source)) {
|
||||
using namespace Net;
|
||||
DownloadHandler *handler = DownloadManager::instance()->downloadUrl(source, true);
|
||||
connect(handler, static_cast<void (DownloadHandler::*)(const QString &, const QString &)>(&DownloadHandler::downloadFinished)
|
||||
, this, &SearchPluginManager::pluginDownloaded);
|
||||
connect(handler, &DownloadHandler::downloadFailed, this, &SearchPluginManager::pluginDownloadFailed);
|
||||
}
|
||||
else {
|
||||
QString path = source;
|
||||
if (path.startsWith("file:", Qt::CaseInsensitive))
|
||||
path = QUrl(path).toLocalFile();
|
||||
|
||||
QString pluginName = Utils::Fs::fileName(path);
|
||||
pluginName.chop(pluginName.size() - pluginName.lastIndexOf("."));
|
||||
|
||||
if (!path.endsWith(".py", Qt::CaseInsensitive))
|
||||
emit pluginInstallationFailed(pluginName, tr("Unknown search engine plugin file format."));
|
||||
else
|
||||
installPlugin_impl(pluginName, path);
|
||||
}
|
||||
}
|
||||
|
||||
void SearchPluginManager::installPlugin_impl(const QString &name, const QString &path)
|
||||
{
|
||||
PluginVersion newVersion = getPluginVersion(path);
|
||||
qDebug() << "Version to be installed:" << newVersion;
|
||||
|
||||
PluginInfo *plugin = pluginInfo(name);
|
||||
if (plugin && !(plugin->version < newVersion)) {
|
||||
qDebug("Apparently update is not needed, we have a more recent version");
|
||||
emit pluginUpdateFailed(name, tr("A more recent version of this plugin is already installed."));
|
||||
return;
|
||||
}
|
||||
|
||||
// Process with install
|
||||
QString destPath = pluginPath(name);
|
||||
bool updated = false;
|
||||
if (QFile::exists(destPath)) {
|
||||
// Backup in case install fails
|
||||
QFile::copy(destPath, destPath + ".bak");
|
||||
Utils::Fs::forceRemove(destPath);
|
||||
Utils::Fs::forceRemove(destPath + "c");
|
||||
updated = true;
|
||||
}
|
||||
// Copy the plugin
|
||||
QFile::copy(path, destPath);
|
||||
// Update supported plugins
|
||||
update();
|
||||
// Check if this was correctly installed
|
||||
if (!m_plugins.contains(name)) {
|
||||
// Remove broken file
|
||||
Utils::Fs::forceRemove(destPath);
|
||||
if (updated) {
|
||||
// restore backup
|
||||
QFile::copy(destPath + ".bak", destPath);
|
||||
Utils::Fs::forceRemove(destPath + ".bak");
|
||||
// Update supported plugins
|
||||
update();
|
||||
emit pluginUpdateFailed(name, tr("Plugin is not supported."));
|
||||
}
|
||||
else {
|
||||
emit pluginInstallationFailed(name, tr("Plugin is not supported."));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Install was successful, remove backup
|
||||
if (updated)
|
||||
Utils::Fs::forceRemove(destPath + ".bak");
|
||||
}
|
||||
}
|
||||
|
||||
bool SearchPluginManager::uninstallPlugin(const QString &name)
|
||||
{
|
||||
// remove it from hard drive
|
||||
QDir pluginsFolder(pluginsLocation());
|
||||
QStringList filters;
|
||||
filters << name + ".*";
|
||||
QStringList files = pluginsFolder.entryList(filters, QDir::Files, QDir::Unsorted);
|
||||
QString file;
|
||||
foreach (file, files)
|
||||
Utils::Fs::forceRemove(pluginsFolder.absoluteFilePath(file));
|
||||
// Remove it from supported engines
|
||||
delete m_plugins.take(name);
|
||||
|
||||
emit pluginUninstalled(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SearchPluginManager::updateIconPath(PluginInfo * const plugin)
|
||||
{
|
||||
if (!plugin) return;
|
||||
QString iconPath = QString("%1/%2.png").arg(pluginsLocation()).arg(plugin->name);
|
||||
if (QFile::exists(iconPath)) {
|
||||
plugin->iconPath = iconPath;
|
||||
}
|
||||
else {
|
||||
iconPath = QString("%1/%2.ico").arg(pluginsLocation()).arg(plugin->name);
|
||||
if (QFile::exists(iconPath))
|
||||
plugin->iconPath = iconPath;
|
||||
}
|
||||
}
|
||||
|
||||
void SearchPluginManager::checkForUpdates()
|
||||
{
|
||||
// Download version file from update server
|
||||
using namespace Net;
|
||||
DownloadHandler *handler = DownloadManager::instance()->downloadUrl(m_updateUrl + "versions.txt");
|
||||
connect(handler, static_cast<void (DownloadHandler::*)(const QString &, const QByteArray &)>(&DownloadHandler::downloadFinished)
|
||||
, this, &SearchPluginManager::versionInfoDownloaded);
|
||||
connect(handler, &DownloadHandler::downloadFailed, this, &SearchPluginManager::versionInfoDownloadFailed);
|
||||
}
|
||||
|
||||
SearchDownloadHandler *SearchPluginManager::downloadTorrent(const QString &siteUrl, const QString &url)
|
||||
{
|
||||
return new SearchDownloadHandler {siteUrl, url, this};
|
||||
}
|
||||
|
||||
SearchHandler *SearchPluginManager::startSearch(const QString &pattern, const QString &category, const QStringList &usedPlugins)
|
||||
{
|
||||
// No search pattern entered
|
||||
Q_ASSERT(!pattern.isEmpty());
|
||||
|
||||
return new SearchHandler {pattern, category, usedPlugins, this};
|
||||
}
|
||||
|
||||
QString SearchPluginManager::categoryFullName(const QString &categoryName)
|
||||
{
|
||||
return tr(m_categoryNames.value(categoryName).toUtf8().constData());
|
||||
}
|
||||
|
||||
QString SearchPluginManager::pluginFullName(const QString &pluginName)
|
||||
{
|
||||
return pluginInfo(pluginName) ? pluginInfo(pluginName)->fullName : QString();
|
||||
}
|
||||
|
||||
QString SearchPluginManager::pluginsLocation()
|
||||
{
|
||||
return QString("%1/engines").arg(engineLocation());
|
||||
}
|
||||
|
||||
QString SearchPluginManager::engineLocation()
|
||||
{
|
||||
QString folder = "nova";
|
||||
if (Utils::Misc::pythonVersion() >= 3)
|
||||
folder = "nova3";
|
||||
const QString location = Utils::Fs::expandPathAbs(specialFolderLocation(SpecialFolder::Data) + folder);
|
||||
QDir locationDir(location);
|
||||
if (!locationDir.exists())
|
||||
locationDir.mkpath(locationDir.absolutePath());
|
||||
return location;
|
||||
}
|
||||
|
||||
void SearchPluginManager::versionInfoDownloaded(const QString &url, const QByteArray &data)
|
||||
{
|
||||
Q_UNUSED(url)
|
||||
parseVersionInfo(data);
|
||||
}
|
||||
|
||||
void SearchPluginManager::versionInfoDownloadFailed(const QString &url, const QString &reason)
|
||||
{
|
||||
Q_UNUSED(url)
|
||||
emit checkForUpdatesFailed(tr("Update server is temporarily unavailable. %1").arg(reason));
|
||||
}
|
||||
|
||||
void SearchPluginManager::pluginDownloaded(const QString &url, QString filePath)
|
||||
{
|
||||
filePath = Utils::Fs::fromNativePath(filePath);
|
||||
|
||||
QString pluginName = Utils::Fs::fileName(url);
|
||||
pluginName.chop(pluginName.size() - pluginName.lastIndexOf(".")); // Remove extension
|
||||
installPlugin_impl(pluginName, filePath);
|
||||
Utils::Fs::forceRemove(filePath);
|
||||
}
|
||||
|
||||
void SearchPluginManager::pluginDownloadFailed(const QString &url, const QString &reason)
|
||||
{
|
||||
QString pluginName = url.split('/').last();
|
||||
pluginName.replace(".py", "", Qt::CaseInsensitive);
|
||||
if (pluginInfo(pluginName))
|
||||
emit pluginUpdateFailed(pluginName, tr("Failed to download the plugin file. %1").arg(reason));
|
||||
else
|
||||
emit pluginInstallationFailed(pluginName, tr("Failed to download the plugin file. %1").arg(reason));
|
||||
}
|
||||
|
||||
// Update nova.py search plugin if necessary
|
||||
void SearchPluginManager::updateNova()
|
||||
{
|
||||
qDebug("Updating nova");
|
||||
|
||||
// create nova directory if necessary
|
||||
QDir searchDir(engineLocation());
|
||||
QString novaFolder = Utils::Misc::pythonVersion() >= 3 ? "searchengine/nova3" : "searchengine/nova";
|
||||
QFile packageFile(searchDir.absoluteFilePath("__init__.py"));
|
||||
packageFile.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
packageFile.close();
|
||||
if (!searchDir.exists("engines"))
|
||||
searchDir.mkdir("engines");
|
||||
Utils::Fs::removeDirRecursive(searchDir.absoluteFilePath("__pycache__"));
|
||||
|
||||
QFile packageFile2(searchDir.absolutePath() + "/engines/__init__.py");
|
||||
packageFile2.open(QIODevice::WriteOnly | QIODevice::Text);
|
||||
packageFile2.close();
|
||||
|
||||
// Copy search plugin files (if necessary)
|
||||
QString filePath = searchDir.absoluteFilePath("nova2.py");
|
||||
if (getPluginVersion(":/" + novaFolder + "/nova2.py") > getPluginVersion(filePath)) {
|
||||
removePythonScriptIfExists(filePath);
|
||||
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);
|
||||
|
||||
filePath = searchDir.absoluteFilePath("novaprinter.py");
|
||||
if (getPluginVersion(":/" + novaFolder + "/novaprinter.py") > getPluginVersion(filePath)) {
|
||||
removePythonScriptIfExists(filePath);
|
||||
QFile::copy(":/" + novaFolder + "/novaprinter.py", filePath);
|
||||
}
|
||||
|
||||
filePath = searchDir.absoluteFilePath("helpers.py");
|
||||
if (getPluginVersion(":/" + novaFolder + "/helpers.py") > getPluginVersion(filePath)) {
|
||||
removePythonScriptIfExists(filePath);
|
||||
QFile::copy(":/" + novaFolder + "/helpers.py", filePath);
|
||||
}
|
||||
|
||||
filePath = searchDir.absoluteFilePath("socks.py");
|
||||
removePythonScriptIfExists(filePath);
|
||||
QFile::copy(":/" + novaFolder + "/socks.py", filePath);
|
||||
|
||||
if (novaFolder.endsWith("nova")) {
|
||||
filePath = searchDir.absoluteFilePath("fix_encoding.py");
|
||||
removePythonScriptIfExists(filePath);
|
||||
QFile::copy(":/" + novaFolder + "/fix_encoding.py", filePath);
|
||||
}
|
||||
else if (novaFolder.endsWith("nova3")) {
|
||||
filePath = searchDir.absoluteFilePath("sgmllib3.py");
|
||||
removePythonScriptIfExists(filePath);
|
||||
QFile::copy(":/" + novaFolder + "/sgmllib3.py", filePath);
|
||||
}
|
||||
|
||||
QDir destDir(pluginsLocation());
|
||||
Utils::Fs::removeDirRecursive(destDir.absoluteFilePath("__pycache__"));
|
||||
}
|
||||
|
||||
void SearchPluginManager::update()
|
||||
{
|
||||
QProcess nova;
|
||||
nova.setEnvironment(QProcess::systemEnvironment());
|
||||
QStringList params;
|
||||
params << Utils::Fs::toNativePath(engineLocation() + "/nova2.py");
|
||||
params << "--capabilities";
|
||||
nova.start(Utils::Misc::pythonExecutable(), params, QIODevice::ReadOnly);
|
||||
nova.waitForStarted();
|
||||
nova.waitForFinished();
|
||||
|
||||
QString capabilities = QString(nova.readAll());
|
||||
QDomDocument xmlDoc;
|
||||
if (!xmlDoc.setContent(capabilities)) {
|
||||
qWarning() << "Could not parse Nova search engine capabilities, msg: " << capabilities.toLocal8Bit().data();
|
||||
qWarning() << "Error: " << nova.readAllStandardError().constData();
|
||||
return;
|
||||
}
|
||||
|
||||
QDomElement root = xmlDoc.documentElement();
|
||||
if (root.tagName() != "capabilities") {
|
||||
qWarning() << "Invalid XML file for Nova search engine capabilities, msg: " << capabilities.toLocal8Bit().data();
|
||||
return;
|
||||
}
|
||||
|
||||
for (QDomNode engineNode = root.firstChild(); !engineNode.isNull(); engineNode = engineNode.nextSibling()) {
|
||||
QDomElement engineElem = engineNode.toElement();
|
||||
if (!engineElem.isNull()) {
|
||||
QString pluginName = engineElem.tagName();
|
||||
|
||||
PluginInfo *plugin = new PluginInfo;
|
||||
plugin->name = pluginName;
|
||||
plugin->version = getPluginVersion(pluginPath(pluginName));
|
||||
plugin->fullName = engineElem.elementsByTagName("name").at(0).toElement().text();
|
||||
plugin->url = engineElem.elementsByTagName("url").at(0).toElement().text();
|
||||
|
||||
foreach (QString cat, engineElem.elementsByTagName("categories").at(0).toElement().text().split(" ")) {
|
||||
cat = cat.trimmed();
|
||||
if (!cat.isEmpty())
|
||||
plugin->supportedCategories << cat;
|
||||
}
|
||||
|
||||
QStringList disabledEngines = Preferences::instance()->getSearchEngDisabled();
|
||||
plugin->enabled = !disabledEngines.contains(pluginName);
|
||||
|
||||
updateIconPath(plugin);
|
||||
|
||||
if (!m_plugins.contains(pluginName)) {
|
||||
m_plugins[pluginName] = plugin;
|
||||
emit pluginInstalled(pluginName);
|
||||
}
|
||||
else if (m_plugins[pluginName]->version != plugin->version) {
|
||||
delete m_plugins.take(pluginName);
|
||||
m_plugins[pluginName] = plugin;
|
||||
emit pluginUpdated(pluginName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SearchPluginManager::parseVersionInfo(const QByteArray &info)
|
||||
{
|
||||
qDebug("Checking if update is needed");
|
||||
|
||||
QHash<QString, PluginVersion> updateInfo;
|
||||
bool dataCorrect = false;
|
||||
QList<QByteArray> lines = info.split('\n');
|
||||
foreach (QByteArray line, lines) {
|
||||
line = line.trimmed();
|
||||
if (line.isEmpty()) continue;
|
||||
if (line.startsWith("#")) continue;
|
||||
|
||||
QList<QByteArray> list = line.split(' ');
|
||||
if (list.size() != 2) continue;
|
||||
|
||||
QString pluginName = QString(list.first());
|
||||
if (!pluginName.endsWith(":")) continue;
|
||||
|
||||
pluginName.chop(1); // remove trailing ':'
|
||||
PluginVersion version = PluginVersion::tryParse(list.last(), {});
|
||||
if (version == PluginVersion()) continue;
|
||||
|
||||
dataCorrect = true;
|
||||
if (isUpdateNeeded(pluginName, version)) {
|
||||
qDebug("Plugin: %s is outdated", qUtf8Printable(pluginName));
|
||||
updateInfo[pluginName] = version;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dataCorrect)
|
||||
emit checkForUpdatesFailed(tr("An incorrect update info received."));
|
||||
else
|
||||
emit checkForUpdatesFinished(updateInfo);
|
||||
}
|
||||
|
||||
bool SearchPluginManager::isUpdateNeeded(QString pluginName, PluginVersion newVersion) const
|
||||
{
|
||||
PluginInfo *plugin = pluginInfo(pluginName);
|
||||
if (!plugin) return true;
|
||||
|
||||
PluginVersion oldVersion = plugin->version;
|
||||
qDebug() << "IsUpdate needed? to be installed:" << newVersion << ", already installed:" << oldVersion;
|
||||
return (newVersion > oldVersion);
|
||||
}
|
||||
|
||||
QString SearchPluginManager::pluginPath(const QString &name)
|
||||
{
|
||||
return QString("%1/%2.py").arg(pluginsLocation()).arg(name);
|
||||
}
|
||||
|
||||
PluginVersion SearchPluginManager::getPluginVersion(QString filePath)
|
||||
{
|
||||
QFile plugin(filePath);
|
||||
if (!plugin.exists()) {
|
||||
qDebug("%s plugin does not exist, returning 0.0", qUtf8Printable(filePath));
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!plugin.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
return {};
|
||||
|
||||
const PluginVersion invalidVersion;
|
||||
|
||||
PluginVersion version;
|
||||
while (!plugin.atEnd()) {
|
||||
QByteArray line = plugin.readLine();
|
||||
if (line.startsWith("#VERSION: ")) {
|
||||
line = line.split(' ').last().trimmed();
|
||||
version = PluginVersion::tryParse(line, invalidVersion);
|
||||
if (version == invalidVersion) {
|
||||
LogMsg(tr("Search plugin '%1' contains invalid version string ('%2')")
|
||||
.arg(Utils::Fs::fileName(filePath)).arg(QString::fromUtf8(line)), Log::MsgType::WARNING);
|
||||
}
|
||||
else {
|
||||
qDebug() << "plugin" << filePath << "version: " << version;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return version;
|
||||
}
|
||||
115
src/base/search/searchpluginmanager.h
Normal file
115
src/base/search/searchpluginmanager.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Bittorrent Client using Qt and libtorrent.
|
||||
* Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
|
||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give permission to
|
||||
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||
* and distribute the linked executables. You must obey the GNU General Public
|
||||
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||
* modify file(s), you may extend this exception to your version of the file(s),
|
||||
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
* exception statement from your version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QHash>
|
||||
#include <QMetaType>
|
||||
#include <QObject>
|
||||
|
||||
#include "base/utils/version.h"
|
||||
|
||||
using PluginVersion = Utils::Version<unsigned short, 2>;
|
||||
Q_DECLARE_METATYPE(PluginVersion)
|
||||
|
||||
struct PluginInfo
|
||||
{
|
||||
QString name;
|
||||
PluginVersion version;
|
||||
QString fullName;
|
||||
QString url;
|
||||
QStringList supportedCategories;
|
||||
QString iconPath;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
class SearchDownloadHandler;
|
||||
class SearchHandler;
|
||||
|
||||
class SearchPluginManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(SearchPluginManager)
|
||||
|
||||
public:
|
||||
SearchPluginManager();
|
||||
~SearchPluginManager() override;
|
||||
|
||||
QStringList allPlugins() const;
|
||||
QStringList enabledPlugins() const;
|
||||
QStringList supportedCategories() const;
|
||||
PluginInfo *pluginInfo(const QString &name) const;
|
||||
|
||||
void enablePlugin(const QString &name, bool enabled = true);
|
||||
void updatePlugin(const QString &name);
|
||||
void installPlugin(const QString &source);
|
||||
bool uninstallPlugin(const QString &name);
|
||||
static void updateIconPath(PluginInfo * const plugin);
|
||||
void checkForUpdates();
|
||||
|
||||
SearchHandler *startSearch(const QString &pattern, const QString &category, const QStringList &usedPlugins);
|
||||
SearchDownloadHandler *downloadTorrent(const QString &siteUrl, const QString &url);
|
||||
|
||||
static PluginVersion getPluginVersion(QString filePath);
|
||||
static QString categoryFullName(const QString &categoryName);
|
||||
QString pluginFullName(const QString &pluginName);
|
||||
static QString pluginsLocation();
|
||||
static QString engineLocation();
|
||||
|
||||
signals:
|
||||
void pluginEnabled(const QString &name, bool enabled);
|
||||
void pluginInstalled(const QString &name);
|
||||
void pluginInstallationFailed(const QString &name, const QString &reason);
|
||||
void pluginUninstalled(const QString &name);
|
||||
void pluginUpdated(const QString &name);
|
||||
void pluginUpdateFailed(const QString &name, const QString &reason);
|
||||
|
||||
void checkForUpdatesFinished(const QHash<QString, PluginVersion> &updateInfo);
|
||||
void checkForUpdatesFailed(const QString &reason);
|
||||
|
||||
private:
|
||||
void update();
|
||||
void updateNova();
|
||||
void parseVersionInfo(const QByteArray &info);
|
||||
void installPlugin_impl(const QString &name, const QString &path);
|
||||
bool isUpdateNeeded(QString pluginName, PluginVersion newVersion) const;
|
||||
|
||||
void versionInfoDownloaded(const QString &url, const QByteArray &data);
|
||||
void versionInfoDownloadFailed(const QString &url, const QString &reason);
|
||||
void pluginDownloaded(const QString &url, QString filePath);
|
||||
void pluginDownloadFailed(const QString &url, const QString &reason);
|
||||
|
||||
static QString pluginPath(const QString &name);
|
||||
|
||||
static const QHash<QString, QString> m_categoryNames;
|
||||
|
||||
const QString m_updateUrl;
|
||||
|
||||
QHash<QString, PluginInfo*> m_plugins;
|
||||
};
|
||||
Reference in New Issue
Block a user