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>
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.
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.
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.
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.
30 #include "searchhandler.h"
36 #include "base/global.h"
37 #include "base/path.h"
38 #include "base/utils/foreignapps.h"
39 #include "base/utils/fs.h"
40 #include "searchpluginmanager.h"
44 enum SearchResultColumn
57 SearchHandler::SearchHandler(const QString
&pattern
, const QString
&category
, const QStringList
&usedPlugins
, SearchPluginManager
*manager
)
60 , m_category
{category
}
61 , m_usedPlugins
{usedPlugins
}
63 , m_searchProcess
{new QProcess
{this}}
64 , m_searchTimeout
{new QTimer
{this}}
66 // Load environment variables (proxy)
67 m_searchProcess
->setEnvironment(QProcess::systemEnvironment());
69 const QStringList params
71 (m_manager
->engineLocation() / Path(u
"nova2.py"_qs
)).toString(),
72 m_usedPlugins
.join(u
','),
77 m_searchProcess
->setProgram(Utils::ForeignApps::pythonInfo().executableName
);
78 m_searchProcess
->setArguments(params
+ m_pattern
.split(u
' '));
80 connect(m_searchProcess
, &QProcess::errorOccurred
, this, &SearchHandler::processFailed
);
81 connect(m_searchProcess
, &QProcess::readyReadStandardOutput
, this, &SearchHandler::readSearchOutput
);
82 connect(m_searchProcess
, qOverload
<int, QProcess::ExitStatus
>(&QProcess::finished
)
83 , this, &SearchHandler::processFinished
);
85 m_searchTimeout
->setSingleShot(true);
86 connect(m_searchTimeout
, &QTimer::timeout
, this, &SearchHandler::cancelSearch
);
87 m_searchTimeout
->start(180000); // 3 min
89 // deferred start allows clients to handle starting-related signals
90 QTimer::singleShot(0, this, [this]() { m_searchProcess
->start(QIODevice::ReadOnly
); });
93 bool SearchHandler::isActive() const
95 return (m_searchProcess
->state() != QProcess::NotRunning
);
98 void SearchHandler::cancelSearch()
100 if ((m_searchProcess
->state() == QProcess::NotRunning
) || m_searchCancelled
)
104 m_searchProcess
->kill();
106 m_searchProcess
->terminate();
108 m_searchCancelled
= true;
109 m_searchTimeout
->stop();
112 // Slot called when QProcess is Finished
113 // QProcess can be finished for 3 reasons:
114 // Error | Stopped by user | Finished normally
115 void SearchHandler::processFinished(const int exitcode
)
117 m_searchTimeout
->stop();
119 if (m_searchCancelled
)
120 emit
searchFinished(true);
121 else if ((m_searchProcess
->exitStatus() == QProcess::NormalExit
) && (exitcode
== 0))
122 emit
searchFinished(false);
127 // search QProcess return output as soon as it gets new
128 // stuff to read. We split it into lines and parse each
129 // line to SearchResult calling parseSearchResult().
130 void SearchHandler::readSearchOutput()
132 QByteArray output
= m_searchProcess
->readAllStandardOutput();
133 output
.replace('\r', "");
135 QList
<QByteArray
> lines
= output
.split('\n');
136 if (!m_searchResultLineTruncated
.isEmpty())
137 lines
.prepend(m_searchResultLineTruncated
+ lines
.takeFirst());
138 m_searchResultLineTruncated
= lines
.takeLast().trimmed();
140 QVector
<SearchResult
> searchResultList
;
141 searchResultList
.reserve(lines
.size());
143 for (const QByteArray
&line
: asConst(lines
))
145 SearchResult searchResult
;
146 if (parseSearchResult(QString::fromUtf8(line
), searchResult
))
147 searchResultList
<< searchResult
;
150 if (!searchResultList
.isEmpty())
152 for (const SearchResult
&result
: searchResultList
)
153 m_results
.append(result
);
154 emit
newSearchResults(searchResultList
);
158 void SearchHandler::processFailed()
160 if (!m_searchCancelled
)
164 // Parse one line of search results list
165 // Line is in the following form:
166 // file url | file name | file size | nb seeds | nb leechers | Search engine url
167 bool SearchHandler::parseSearchResult(const QStringView line
, SearchResult
&searchResult
)
169 const QList
<QStringView
> parts
= line
.split(u
'|');
170 const int nbFields
= parts
.size();
172 if (nbFields
< (NB_PLUGIN_COLUMNS
- 1)) return false; // -1 because desc_link is optional
174 searchResult
= SearchResult();
175 searchResult
.fileUrl
= parts
.at(PL_DL_LINK
).trimmed().toString(); // download URL
176 searchResult
.fileName
= parts
.at(PL_NAME
).trimmed().toString(); // Name
177 searchResult
.fileSize
= parts
.at(PL_SIZE
).trimmed().toLongLong(); // Size
181 searchResult
.nbSeeders
= parts
.at(PL_SEEDS
).trimmed().toLongLong(&ok
); // Seeders
182 if (!ok
|| (searchResult
.nbSeeders
< 0))
183 searchResult
.nbSeeders
= -1;
185 searchResult
.nbLeechers
= parts
.at(PL_LEECHS
).trimmed().toLongLong(&ok
); // Leechers
186 if (!ok
|| (searchResult
.nbLeechers
< 0))
187 searchResult
.nbLeechers
= -1;
189 searchResult
.siteUrl
= parts
.at(PL_ENGINE_URL
).trimmed().toString(); // Search site URL
190 if (nbFields
== NB_PLUGIN_COLUMNS
)
191 searchResult
.descrLink
= parts
.at(PL_DESC_LINK
).trimmed().toString(); // Description Link
196 SearchPluginManager
*SearchHandler::manager() const
201 QList
<SearchResult
> SearchHandler::results() const
206 QString
SearchHandler::pattern() const