mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2025-12-20 23:47:23 -06:00
Propagate error messages from search engine
This will be useful for development and debugging issues. The messages will be outputted to both console and qbt logger. Note that having stderr messages does not always mean the operation failed but there might be some (non-fatal) issues. PR #23293. Closes #6553. Closes #22381. Closes #23052.
This commit is contained in:
@@ -28,9 +28,11 @@
|
|||||||
|
|
||||||
#include "searchdownloadhandler.h"
|
#include "searchdownloadhandler.h"
|
||||||
|
|
||||||
|
#include <QtLogging>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
|
#include "base/logger.h"
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
#include "base/utils/foreignapps.h"
|
#include "base/utils/foreignapps.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
@@ -38,6 +40,8 @@
|
|||||||
|
|
||||||
SearchDownloadHandler::SearchDownloadHandler(const QString &pluginName, const QString &url, SearchPluginManager *manager)
|
SearchDownloadHandler::SearchDownloadHandler(const QString &pluginName, const QString &url, SearchPluginManager *manager)
|
||||||
: QObject(manager)
|
: QObject(manager)
|
||||||
|
, m_pluginName {pluginName}
|
||||||
|
, m_url {url}
|
||||||
, m_manager {manager}
|
, m_manager {manager}
|
||||||
, m_downloadProcess {new QProcess(this)}
|
, m_downloadProcess {new QProcess(this)}
|
||||||
{
|
{
|
||||||
@@ -58,10 +62,17 @@ SearchDownloadHandler::SearchDownloadHandler(const QString &pluginName, const QS
|
|||||||
m_downloadProcess->start(Utils::ForeignApps::pythonInfo().executablePath.data(), params, QIODevice::ReadOnly);
|
m_downloadProcess->start(Utils::ForeignApps::pythonInfo().executablePath.data(), params, QIODevice::ReadOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchDownloadHandler::downloadProcessFinished(int exitcode)
|
void SearchDownloadHandler::downloadProcessFinished(const int exitcode)
|
||||||
{
|
{
|
||||||
QString path;
|
const auto errMsg = QString::fromUtf8(m_downloadProcess->readAllStandardError()).trimmed();
|
||||||
|
if (!errMsg.isEmpty())
|
||||||
|
{
|
||||||
|
qWarning("%s", qUtf8Printable(errMsg));
|
||||||
|
LogMsg(tr("Error occurred when downloading torrent via search engine. Engine: \"%1\". URL: \"%2\". Error: \"%3\".")
|
||||||
|
.arg(m_pluginName, m_url, errMsg), Log::WARNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString path;
|
||||||
if ((exitcode == 0) && (m_downloadProcess->exitStatus() == QProcess::NormalExit))
|
if ((exitcode == 0) && (m_downloadProcess->exitStatus() == QProcess::NormalExit))
|
||||||
{
|
{
|
||||||
const QString line = QString::fromUtf8(m_downloadProcess->readAllStandardOutput()).trimmed();
|
const QString line = QString::fromUtf8(m_downloadProcess->readAllStandardOutput()).trimmed();
|
||||||
@@ -70,5 +81,5 @@ void SearchDownloadHandler::downloadProcessFinished(int exitcode)
|
|||||||
path = parts[0].toString();
|
path = parts[0].toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
emit downloadFinished(path);
|
emit downloadFinished(path, errMsg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
class QProcess;
|
class QProcess;
|
||||||
|
|
||||||
@@ -44,11 +45,13 @@ class SearchDownloadHandler : public QObject
|
|||||||
SearchDownloadHandler(const QString &pluginName, const QString &url, SearchPluginManager *manager);
|
SearchDownloadHandler(const QString &pluginName, const QString &url, SearchPluginManager *manager);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void downloadFinished(const QString &path);
|
void downloadFinished(const QString &path, const QString &errorMessage);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void downloadProcessFinished(int exitcode);
|
void downloadProcessFinished(int exitcode);
|
||||||
|
|
||||||
|
QString m_pluginName;
|
||||||
|
QString m_url;
|
||||||
SearchPluginManager *m_manager = nullptr;
|
SearchPluginManager *m_manager = nullptr;
|
||||||
QProcess *m_downloadProcess = nullptr;
|
QProcess *m_downloadProcess = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -31,12 +31,14 @@
|
|||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
|
#include <QtLogging>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QMetaObject>
|
#include <QMetaObject>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
|
#include "base/logger.h"
|
||||||
#include "base/path.h"
|
#include "base/path.h"
|
||||||
#include "base/utils/bytearray.h"
|
#include "base/utils/bytearray.h"
|
||||||
#include "base/utils/foreignapps.h"
|
#include "base/utils/foreignapps.h"
|
||||||
@@ -59,6 +61,26 @@ namespace
|
|||||||
PL_PUB_DATE,
|
PL_PUB_DATE,
|
||||||
NB_PLUGIN_COLUMNS
|
NB_PLUGIN_COLUMNS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
QString toString(const QProcess::ProcessError error)
|
||||||
|
{
|
||||||
|
switch (error)
|
||||||
|
{
|
||||||
|
case QProcess::FailedToStart:
|
||||||
|
return SearchHandler::tr("Process failed to start");
|
||||||
|
case QProcess::Crashed:
|
||||||
|
return SearchHandler::tr("Process crashed");
|
||||||
|
case QProcess::Timedout:
|
||||||
|
return SearchHandler::tr("Process timed out");
|
||||||
|
case QProcess::WriteError:
|
||||||
|
return SearchHandler::tr("Process write error");
|
||||||
|
case QProcess::ReadError:
|
||||||
|
return SearchHandler::tr("Process read error");
|
||||||
|
case QProcess::UnknownError:
|
||||||
|
return SearchHandler::tr("Process unknown error");
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
SearchHandler::SearchHandler(const QString &pattern, const QString &category, const QStringList &usedPlugins, SearchPluginManager *manager)
|
SearchHandler::SearchHandler(const QString &pattern, const QString &category, const QStringList &usedPlugins, SearchPluginManager *manager)
|
||||||
@@ -86,7 +108,16 @@ SearchHandler::SearchHandler(const QString &pattern, const QString &category, co
|
|||||||
};
|
};
|
||||||
m_searchProcess->setArguments(params + m_pattern.split(u' '));
|
m_searchProcess->setArguments(params + m_pattern.split(u' '));
|
||||||
|
|
||||||
connect(m_searchProcess, &QProcess::errorOccurred, this, &SearchHandler::processFailed);
|
connect(m_searchProcess, &QProcess::errorOccurred, this, [this](const QProcess::ProcessError error)
|
||||||
|
{
|
||||||
|
if (!m_searchCancelled)
|
||||||
|
{
|
||||||
|
const auto errMsg = toString(error);
|
||||||
|
LogMsg(tr("Search process failed. Search query: \"%1\". Category: \"%2\". Engines: \"%3\". Error: \"%4\".")
|
||||||
|
.arg(m_pattern, m_category, m_usedPlugins.join(u", "), errMsg), Log::WARNING);
|
||||||
|
emit searchFailed(errMsg);
|
||||||
|
}
|
||||||
|
});
|
||||||
connect(m_searchProcess, &QProcess::readyReadStandardOutput, this, &SearchHandler::readSearchOutput);
|
connect(m_searchProcess, &QProcess::readyReadStandardOutput, this, &SearchHandler::readSearchOutput);
|
||||||
connect(m_searchProcess, qOverload<int, QProcess::ExitStatus>(&QProcess::finished)
|
connect(m_searchProcess, qOverload<int, QProcess::ExitStatus>(&QProcess::finished)
|
||||||
, this, &SearchHandler::processFinished);
|
, this, &SearchHandler::processFinished);
|
||||||
@@ -127,12 +158,20 @@ void SearchHandler::processFinished(const int exitcode)
|
|||||||
{
|
{
|
||||||
m_searchTimeout->stop();
|
m_searchTimeout->stop();
|
||||||
|
|
||||||
|
const auto errMsg = QString::fromUtf8(m_searchProcess->readAllStandardError()).trimmed();
|
||||||
|
if (!errMsg.isEmpty())
|
||||||
|
{
|
||||||
|
qWarning("%s", qUtf8Printable(errMsg));
|
||||||
|
LogMsg(tr("Error occurred in search engine. Search query: \"%1\". Category: \"%2\". Engines: \"%3\". Error: \"%4\".")
|
||||||
|
.arg(m_pattern, m_category, m_usedPlugins.join(u", "), errMsg), Log::WARNING);
|
||||||
|
}
|
||||||
|
|
||||||
if (m_searchCancelled)
|
if (m_searchCancelled)
|
||||||
emit searchFinished(true);
|
emit searchFinished(true);
|
||||||
else if ((m_searchProcess->exitStatus() == QProcess::NormalExit) && (exitcode == 0))
|
else if ((m_searchProcess->exitStatus() == QProcess::NormalExit) && (exitcode == 0))
|
||||||
emit searchFinished(false);
|
emit searchFinished(false);
|
||||||
else
|
else
|
||||||
emit searchFailed();
|
emit searchFailed(errMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// search QProcess return output as soon as it gets new
|
// search QProcess return output as soon as it gets new
|
||||||
@@ -161,12 +200,6 @@ void SearchHandler::readSearchOutput()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchHandler::processFailed()
|
|
||||||
{
|
|
||||||
if (!m_searchCancelled)
|
|
||||||
emit searchFailed();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse one line of search results list
|
// Parse one line of search results list
|
||||||
// Line is in the following form:
|
// Line is in the following form:
|
||||||
// file url | file name | file size | nb seeds | nb leechers | Search engine url
|
// file url | file name | file size | nb seeds | nb leechers | Search engine url
|
||||||
|
|||||||
@@ -74,12 +74,11 @@ public:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void searchFinished(bool cancelled = false);
|
void searchFinished(bool cancelled = false);
|
||||||
void searchFailed();
|
void searchFailed(const QString &errorMessage);
|
||||||
void newSearchResults(const QList<SearchResult> &results);
|
void newSearchResults(const QList<SearchResult> &results);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void readSearchOutput();
|
void readSearchOutput();
|
||||||
void processFailed();
|
|
||||||
void processFinished(int exitcode);
|
void processFinished(int exitcode);
|
||||||
bool parseSearchResult(QByteArrayView line, SearchResult &searchResult);
|
bool parseSearchResult(QByteArrayView line, SearchResult &searchResult);
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#include <QtLogging>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
#include <QDomDocument>
|
#include <QDomDocument>
|
||||||
@@ -559,6 +560,13 @@ void SearchPluginManager::update()
|
|||||||
nova.start(Utils::ForeignApps::pythonInfo().executablePath.data(), params, QIODevice::ReadOnly);
|
nova.start(Utils::ForeignApps::pythonInfo().executablePath.data(), params, QIODevice::ReadOnly);
|
||||||
nova.waitForFinished();
|
nova.waitForFinished();
|
||||||
|
|
||||||
|
if (const auto errMsg = QString::fromUtf8(nova.readAllStandardError()).trimmed()
|
||||||
|
; !errMsg.isEmpty())
|
||||||
|
{
|
||||||
|
qWarning("%s", qUtf8Printable(errMsg));
|
||||||
|
LogMsg(tr("Error occurred when fetching search engine capabilities. Error: \"%1\".").arg(errMsg), Log::WARNING);
|
||||||
|
}
|
||||||
|
|
||||||
const auto capabilities = QString::fromUtf8(nova.readAllStandardOutput());
|
const auto capabilities = QString::fromUtf8(nova.readAllStandardOutput());
|
||||||
QDomDocument xmlDoc;
|
QDomDocument xmlDoc;
|
||||||
if (!xmlDoc.setContent(capabilities))
|
if (!xmlDoc.setContent(capabilities))
|
||||||
|
|||||||
@@ -64,10 +64,12 @@ namespace
|
|||||||
return QApplication::palette().color(QPalette::Disabled, QPalette::WindowText);
|
return QApplication::palette().color(QPalette::Disabled, QPalette::WindowText);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString statusText(SearchJobWidget::Status st)
|
QString statusText(const SearchJobWidget::Status st)
|
||||||
{
|
{
|
||||||
switch (st)
|
switch (st)
|
||||||
{
|
{
|
||||||
|
case SearchJobWidget::Status::Ready:
|
||||||
|
break;
|
||||||
case SearchJobWidget::Status::Ongoing:
|
case SearchJobWidget::Status::Ongoing:
|
||||||
return SearchJobWidget::tr("Searching...");
|
return SearchJobWidget::tr("Searching...");
|
||||||
case SearchJobWidget::Status::Finished:
|
case SearchJobWidget::Status::Finished:
|
||||||
@@ -78,9 +80,8 @@ namespace
|
|||||||
return SearchJobWidget::tr("An error occurred during search...");
|
return SearchJobWidget::tr("An error occurred during search...");
|
||||||
case SearchJobWidget::Status::NoResults:
|
case SearchJobWidget::Status::NoResults:
|
||||||
return SearchJobWidget::tr("Search returned no results");
|
return SearchJobWidget::tr("Search returned no results");
|
||||||
default:
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,7 +403,7 @@ void SearchJobWidget::copyField(const int column) const
|
|||||||
QApplication::clipboard()->setText(list.join(u'\n'));
|
QApplication::clipboard()->setText(list.join(u'\n'));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchJobWidget::setStatus(Status value)
|
void SearchJobWidget::setStatus(const Status value)
|
||||||
{
|
{
|
||||||
if (m_status == value)
|
if (m_status == value)
|
||||||
return;
|
return;
|
||||||
@@ -426,9 +427,12 @@ void SearchJobWidget::downloadTorrent(const QModelIndex &rowIndex, const AddTorr
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
SearchDownloadHandler *downloadHandler = SearchPluginManager::instance()->downloadTorrent(engineName, torrentUrl);
|
SearchDownloadHandler *downloadHandler = SearchPluginManager::instance()->downloadTorrent(engineName, torrentUrl);
|
||||||
connect(downloadHandler, &SearchDownloadHandler::downloadFinished
|
connect(downloadHandler, &SearchDownloadHandler::downloadFinished, this
|
||||||
, this, [this, option](const QString &source) { addTorrentToSession(source, option); });
|
, [this, downloadHandler, option](const QString &source, [[maybe_unused]] const QString &errorMessage)
|
||||||
connect(downloadHandler, &SearchDownloadHandler::downloadFinished, downloadHandler, &SearchDownloadHandler::deleteLater);
|
{
|
||||||
|
addTorrentToSession(source, option);
|
||||||
|
downloadHandler->deleteLater();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setRowVisited(rowIndex.row());
|
setRowVisited(rowIndex.row());
|
||||||
@@ -631,7 +635,7 @@ void SearchJobWidget::searchFinished(bool cancelled)
|
|||||||
setStatus(Status::Finished);
|
setStatus(Status::Finished);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchJobWidget::searchFailed()
|
void SearchJobWidget::searchFailed([[maybe_unused]] const QString &errorMessage)
|
||||||
{
|
{
|
||||||
setStatus(Status::Error);
|
setStatus(Status::Error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ private:
|
|||||||
void contextMenuEvent(QContextMenuEvent *event) override;
|
void contextMenuEvent(QContextMenuEvent *event) override;
|
||||||
void onItemDoubleClicked(const QModelIndex &index);
|
void onItemDoubleClicked(const QModelIndex &index);
|
||||||
void searchFinished(bool cancelled);
|
void searchFinished(bool cancelled);
|
||||||
void searchFailed();
|
void searchFailed(const QString &errorMessage);
|
||||||
void appendSearchResults(const QList<SearchResult> &results);
|
void appendSearchResults(const QList<SearchResult> &results);
|
||||||
void updateResultsCount();
|
void updateResultsCount();
|
||||||
void setStatus(Status value);
|
void setStatus(Status value);
|
||||||
|
|||||||
@@ -114,8 +114,8 @@ void SearchController::startAction()
|
|||||||
|
|
||||||
const auto id = generateSearchId();
|
const auto id = generateSearchId();
|
||||||
const std::shared_ptr<SearchHandler> searchHandler {SearchPluginManager::instance()->startSearch(pattern, category, pluginsToUse)};
|
const std::shared_ptr<SearchHandler> searchHandler {SearchPluginManager::instance()->startSearch(pattern, category, pluginsToUse)};
|
||||||
QObject::connect(searchHandler.get(), &SearchHandler::searchFinished, this, [id, this]() { m_activeSearches.remove(id); });
|
connect(searchHandler.get(), &SearchHandler::searchFinished, this, [this, id] { m_activeSearches.remove(id); });
|
||||||
QObject::connect(searchHandler.get(), &SearchHandler::searchFailed, this, [id, this]() { m_activeSearches.remove(id); });
|
connect(searchHandler.get(), &SearchHandler::searchFailed, this, [this, id]([[maybe_unused]] const QString &errorMessage) { m_activeSearches.remove(id); });
|
||||||
|
|
||||||
m_searchHandlers.insert(id, searchHandler);
|
m_searchHandlers.insert(id, searchHandler);
|
||||||
|
|
||||||
@@ -235,8 +235,8 @@ void SearchController::downloadTorrentAction()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
SearchDownloadHandler *downloadHandler = SearchPluginManager::instance()->downloadTorrent(pluginName, torrentUrl);
|
SearchDownloadHandler *downloadHandler = SearchPluginManager::instance()->downloadTorrent(pluginName, torrentUrl);
|
||||||
connect(downloadHandler, &SearchDownloadHandler::downloadFinished
|
connect(downloadHandler, &SearchDownloadHandler::downloadFinished, this
|
||||||
, this, [this, downloadHandler](const QString &source)
|
, [this, downloadHandler](const QString &source, [[maybe_unused]] const QString &errorMessage)
|
||||||
{
|
{
|
||||||
app()->addTorrentManager()->addTorrent(source);
|
app()->addTorrentManager()->addTorrent(source);
|
||||||
downloadHandler->deleteLater();
|
downloadHandler->deleteLater();
|
||||||
|
|||||||
Reference in New Issue
Block a user