qBittorrent
searchhandler.cpp
Go to the documentation of this file.
1 /*
2  * Bittorrent Client using Qt and libtorrent.
3  * Copyright (C) 2015, 2018 Vladimir Golovnev <glassez@yandex.ru>
4  * Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * In addition, as a special exception, the copyright holders give permission to
21  * link this program with the OpenSSL project's "OpenSSL" library (or with
22  * modified versions of it that use the same license as the "OpenSSL" library),
23  * and distribute the linked executables. You must obey the GNU General Public
24  * License in all respects for all of the code used other than "OpenSSL". If you
25  * modify file(s), you may extend this exception to your version of the file(s),
26  * but you are not obligated to do so. If you do not wish to do so, delete this
27  * exception statement from your version.
28  */
29 
30 #include "searchhandler.h"
31 
32 #include <QProcess>
33 #include <QTimer>
34 #include <QVector>
35 
36 #include "base/global.h"
37 #include "base/utils/foreignapps.h"
38 #include "base/utils/fs.h"
39 #include "searchpluginmanager.h"
40 
41 namespace
42 {
44  {
53  };
54 }
55 
56 SearchHandler::SearchHandler(const QString &pattern, const QString &category, const QStringList &usedPlugins, SearchPluginManager *manager)
57  : QObject {manager}
58  , m_pattern {pattern}
59  , m_category {category}
60  , m_usedPlugins {usedPlugins}
61  , m_manager {manager}
62  , m_searchProcess {new QProcess {this}}
63  , m_searchTimeout {new QTimer {this}}
64 {
65  // Load environment variables (proxy)
66  m_searchProcess->setEnvironment(QProcess::systemEnvironment());
67 
68  const QStringList params
69  {
71  m_usedPlugins.join(','),
73  };
74 
75  // Launch search
76  m_searchProcess->setProgram(Utils::ForeignApps::pythonInfo().executableName);
77  m_searchProcess->setArguments(params + m_pattern.split(' '));
78 
79  connect(m_searchProcess, &QProcess::errorOccurred, this, &SearchHandler::processFailed);
80  connect(m_searchProcess, &QProcess::readyReadStandardOutput, this, &SearchHandler::readSearchOutput);
81  connect(m_searchProcess, qOverload<int, QProcess::ExitStatus>(&QProcess::finished)
83 
84  m_searchTimeout->setSingleShot(true);
85  connect(m_searchTimeout, &QTimer::timeout, this, &SearchHandler::cancelSearch);
86  m_searchTimeout->start(180000); // 3 min
87 
88  // deferred start allows clients to handle starting-related signals
89  QTimer::singleShot(0, this, [this]() { m_searchProcess->start(QIODevice::ReadOnly); });
90 }
91 
93 {
94  return (m_searchProcess->state() != QProcess::NotRunning);
95 }
96 
98 {
99  if ((m_searchProcess->state() == QProcess::NotRunning) || m_searchCancelled)
100  return;
101 
102 #ifdef Q_OS_WIN
103  m_searchProcess->kill();
104 #else
105  m_searchProcess->terminate();
106 #endif
107  m_searchCancelled = true;
108  m_searchTimeout->stop();
109 }
110 
111 // Slot called when QProcess is Finished
112 // QProcess can be finished for 3 reasons:
113 // Error | Stopped by user | Finished normally
114 void SearchHandler::processFinished(const int exitcode)
115 {
116  m_searchTimeout->stop();
117 
118  if (m_searchCancelled)
119  emit searchFinished(true);
120  else if ((m_searchProcess->exitStatus() == QProcess::NormalExit) && (exitcode == 0))
121  emit searchFinished(false);
122  else
123  emit searchFailed();
124 }
125 
126 // search QProcess return output as soon as it gets new
127 // stuff to read. We split it into lines and parse each
128 // line to SearchResult calling parseSearchResult().
130 {
131  QByteArray output = m_searchProcess->readAllStandardOutput();
132  output.replace('\r', "");
133 
134  QList<QByteArray> lines = output.split('\n');
135  if (!m_searchResultLineTruncated.isEmpty())
136  lines.prepend(m_searchResultLineTruncated + lines.takeFirst());
137  m_searchResultLineTruncated = lines.takeLast().trimmed();
138 
139  QVector<SearchResult> searchResultList;
140  searchResultList.reserve(lines.size());
141 
142  for (const QByteArray &line : asConst(lines))
143  {
144  SearchResult searchResult;
145  if (parseSearchResult(QString::fromUtf8(line), searchResult))
146  searchResultList << searchResult;
147  }
148 
149  if (!searchResultList.isEmpty())
150  {
151  for (const SearchResult &result : searchResultList)
152  m_results.append(result);
153  emit newSearchResults(searchResultList);
154  }
155 }
156 
158 {
159  if (!m_searchCancelled)
160  emit searchFailed();
161 }
162 
163 // Parse one line of search results list
164 // Line is in the following form:
165 // file url | file name | file size | nb seeds | nb leechers | Search engine url
166 bool SearchHandler::parseSearchResult(const QStringView line, SearchResult &searchResult)
167 {
168  const QList<QStringView> parts = line.split(u'|');
169  const int nbFields = parts.size();
170 
171  if (nbFields < (NB_PLUGIN_COLUMNS - 1)) return false; // -1 because desc_link is optional
172 
173  searchResult = SearchResult();
174  searchResult.fileUrl = parts.at(PL_DL_LINK).trimmed().toString(); // download URL
175  searchResult.fileName = parts.at(PL_NAME).trimmed().toString(); // Name
176  searchResult.fileSize = parts.at(PL_SIZE).trimmed().toLongLong(); // Size
177 
178  bool ok = false;
179 
180  searchResult.nbSeeders = parts.at(PL_SEEDS).trimmed().toLongLong(&ok); // Seeders
181  if (!ok || (searchResult.nbSeeders < 0))
182  searchResult.nbSeeders = -1;
183 
184  searchResult.nbLeechers = parts.at(PL_LEECHS).trimmed().toLongLong(&ok); // Leechers
185  if (!ok || (searchResult.nbLeechers < 0))
186  searchResult.nbLeechers = -1;
187 
188  searchResult.siteUrl = parts.at(PL_ENGINE_URL).trimmed().toString(); // Search site URL
189  if (nbFields == NB_PLUGIN_COLUMNS)
190  searchResult.descrLink = parts.at(PL_DESC_LINK).trimmed().toString(); // Description Link
191 
192  return true;
193 }
194 
196 {
197  return m_manager;
198 }
199 
200 QList<SearchResult> SearchHandler::results() const
201 {
202  return m_results;
203 }
204 
205 QString SearchHandler::pattern() const
206 {
207  return m_pattern;
208 }
SearchPluginManager * manager() const
QString pattern() const
void readSearchOutput()
const QString m_pattern
Definition: searchhandler.h:83
const QStringList m_usedPlugins
Definition: searchhandler.h:85
QTimer * m_searchTimeout
Definition: searchhandler.h:88
QList< SearchResult > m_results
Definition: searchhandler.h:91
bool parseSearchResult(QStringView line, SearchResult &searchResult)
QByteArray m_searchResultLineTruncated
Definition: searchhandler.h:89
bool isActive() const
QProcess * m_searchProcess
Definition: searchhandler.h:87
SearchHandler(const QString &pattern, const QString &category, const QStringList &usedPlugins, SearchPluginManager *manager)
const QString m_category
Definition: searchhandler.h:84
QList< SearchResult > results() const
void searchFinished(bool cancelled=false)
bool m_searchCancelled
Definition: searchhandler.h:90
SearchPluginManager * m_manager
Definition: searchhandler.h:86
void processFinished(int exitcode)
void searchFailed()
void newSearchResults(const QVector< SearchResult > &results)
static QString engineLocation()
constexpr std::add_const_t< T > & asConst(T &t) noexcept
Definition: global.h:42
PythonInfo pythonInfo()
QString toNativePath(const QString &path)
Definition: fs.cpp:64
QString descrLink
Definition: searchhandler.h:49
QString fileUrl
Definition: searchhandler.h:44
qlonglong nbLeechers
Definition: searchhandler.h:47
qlonglong fileSize
Definition: searchhandler.h:45
qlonglong nbSeeders
Definition: searchhandler.h:46
QString siteUrl
Definition: searchhandler.h:48
QString fileName
Definition: searchhandler.h:43