Implement "Sequential downloading" feature

Closes #6835.
This commit is contained in:
Vladimir Golovnev (Glassez) 2018-06-21 17:50:40 +03:00
parent a9a6b74416
commit 8d438e159c
No known key found for this signature in database
GPG key ID: 52A2C7DEE2DFA6F7
5 changed files with 226 additions and 115 deletions

View file

@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
* 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
@ -103,28 +103,46 @@ namespace
return QNetworkCookieJar::setCookiesFromUrl(cookies, url);
}
};
QNetworkRequest createNetworkRequest(const Net::DownloadRequest &downloadRequest)
{
QNetworkRequest request {downloadRequest.url()};
if (downloadRequest.userAgent().isEmpty())
request.setRawHeader("User-Agent", DEFAULT_USER_AGENT);
else
request.setRawHeader("User-Agent", downloadRequest.userAgent().toUtf8());
// Spoof HTTP Referer to allow adding torrent link from Torcache/KickAssTorrents
request.setRawHeader("Referer", request.url().toEncoded().data());
// Accept gzip
request.setRawHeader("Accept-Encoding", "gzip");
return request;
}
}
using namespace Net;
Net::DownloadManager *Net::DownloadManager::m_instance = nullptr;
DownloadManager *DownloadManager::m_instance = nullptr;
DownloadManager::DownloadManager(QObject *parent)
Net::DownloadManager::DownloadManager(QObject *parent)
: QObject(parent)
{
#ifndef QT_NO_OPENSSL
connect(&m_networkManager, &QNetworkAccessManager::sslErrors, this, &Net::DownloadManager::ignoreSslErrors);
#endif
connect(&m_networkManager, &QNetworkAccessManager::finished, this, &DownloadManager::handleReplyFinished);
connect(ProxyConfigurationManager::instance(), &ProxyConfigurationManager::proxyConfigurationChanged
, this, &DownloadManager::applyProxySettings);
m_networkManager.setCookieJar(new NetworkCookieJar(this));
}
void DownloadManager::initInstance()
void Net::DownloadManager::initInstance()
{
if (!m_instance)
m_instance = new DownloadManager;
}
void DownloadManager::freeInstance()
void Net::DownloadManager::freeInstance()
{
if (m_instance) {
delete m_instance;
@ -132,64 +150,70 @@ void DownloadManager::freeInstance()
}
}
DownloadManager *DownloadManager::instance()
Net::DownloadManager *Net::DownloadManager::instance()
{
return m_instance;
}
DownloadHandler *DownloadManager::downloadUrl(const QString &url, bool saveToFile, qint64 limit, bool handleRedirectToMagnet, const QString &userAgent)
Net::DownloadHandler *Net::DownloadManager::downloadUrl(const QString &url, bool saveToFile, qint64 limit, bool handleRedirectToMagnet, const QString &userAgent)
{
return download(DownloadRequest(url).saveToFile(saveToFile).limit(limit).handleRedirectToMagnet(handleRedirectToMagnet).userAgent(userAgent));
}
DownloadHandler *DownloadManager::download(const DownloadRequest &downloadRequest)
Net::DownloadHandler *Net::DownloadManager::download(const DownloadRequest &downloadRequest)
{
// Update proxy settings
applyProxySettings();
// Process download request
QNetworkRequest request(downloadRequest.url());
const QNetworkRequest request = createNetworkRequest(downloadRequest);
const ServiceID id = ServiceID::fromURL(request.url());
const bool isSequentialService = m_sequentialServices.contains(id);
if (!isSequentialService || !m_busyServices.contains(id)) {
qDebug("Downloading %s...", qUtf8Printable(downloadRequest.url()));
if (isSequentialService)
m_busyServices.insert(id);
return new DownloadHandler {
m_networkManager.get(request), this, downloadRequest};
}
if (downloadRequest.userAgent().isEmpty())
request.setRawHeader("User-Agent", DEFAULT_USER_AGENT);
else
request.setRawHeader("User-Agent", downloadRequest.userAgent().toUtf8());
// Spoof HTTP Referer to allow adding torrent link from Torcache/KickAssTorrents
request.setRawHeader("Referer", request.url().toEncoded().data());
qDebug("Downloading %s...", request.url().toEncoded().data());
// accept gzip
request.setRawHeader("Accept-Encoding", "gzip");
return new DownloadHandler(m_networkManager.get(request), this, downloadRequest);
auto *downloadHandler = new DownloadHandler {nullptr, this, downloadRequest};
connect(downloadHandler, &DownloadHandler::destroyed, this, [this, id, downloadHandler]()
{
m_waitingJobs[id].removeOne(downloadHandler);
});
m_waitingJobs[id].enqueue(downloadHandler);
return downloadHandler;
}
QList<QNetworkCookie> DownloadManager::cookiesForUrl(const QUrl &url) const
void Net::DownloadManager::registerSequentialService(const Net::ServiceID &serviceID)
{
m_sequentialServices.insert(serviceID);
}
QList<QNetworkCookie> Net::DownloadManager::cookiesForUrl(const QUrl &url) const
{
return m_networkManager.cookieJar()->cookiesForUrl(url);
}
bool DownloadManager::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url)
bool Net::DownloadManager::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url)
{
return m_networkManager.cookieJar()->setCookiesFromUrl(cookieList, url);
}
QList<QNetworkCookie> DownloadManager::allCookies() const
QList<QNetworkCookie> Net::DownloadManager::allCookies() const
{
return static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->allCookies();
}
void DownloadManager::setAllCookies(const QList<QNetworkCookie> &cookieList)
void Net::DownloadManager::setAllCookies(const QList<QNetworkCookie> &cookieList)
{
static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->setAllCookies(cookieList);
}
bool DownloadManager::deleteCookie(const QNetworkCookie &cookie)
bool Net::DownloadManager::deleteCookie(const QNetworkCookie &cookie)
{
return static_cast<NetworkCookieJar *>(m_networkManager.cookieJar())->deleteCookie(cookie);
}
void DownloadManager::applyProxySettings()
void Net::DownloadManager::applyProxySettings()
{
auto proxyManager = ProxyConfigurationManager::instance();
ProxyConfiguration proxyConfig = proxyManager->proxyConfiguration();
@ -210,7 +234,7 @@ void DownloadManager::applyProxySettings()
}
// Authentication?
if (proxyManager->isAuthenticationRequired()) {
qDebug("Proxy requires authentication, authenticating");
qDebug("Proxy requires authentication, authenticating...");
proxy.setUser(proxyConfig.username);
proxy.setPassword(proxyConfig.password);
}
@ -222,8 +246,23 @@ void DownloadManager::applyProxySettings()
m_networkManager.setProxy(proxy);
}
void Net::DownloadManager::handleReplyFinished(QNetworkReply *reply)
{
const ServiceID id = ServiceID::fromURL(reply->url());
auto waitingJobsIter = m_waitingJobs.find(id);
if ((waitingJobsIter == m_waitingJobs.end()) || waitingJobsIter.value().isEmpty()) {
m_busyServices.remove(id);
return;
}
DownloadHandler *handler = waitingJobsIter.value().dequeue();
qDebug("Downloading %s...", qUtf8Printable(handler->m_downloadRequest.url()));
handler->assignNetworkReply(m_networkManager.get(createNetworkRequest(handler->m_downloadRequest)));
handler->disconnect(this);
}
#ifndef QT_NO_OPENSSL
void DownloadManager::ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
void Net::DownloadManager::ignoreSslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
{
Q_UNUSED(errors)
// Ignore all SSL errors
@ -290,3 +329,18 @@ Net::DownloadRequest &Net::DownloadRequest::handleRedirectToMagnet(bool value)
m_handleRedirectToMagnet = value;
return *this;
}
Net::ServiceID Net::ServiceID::fromURL(const QUrl &url)
{
return {url.host(), url.port(80)};
}
uint Net::qHash(const ServiceID &serviceID, uint seed)
{
return ::qHash(serviceID.hostName, seed) ^ serviceID.port;
}
bool Net::operator==(const ServiceID &lhs, const ServiceID &rhs)
{
return ((lhs.hostName == rhs.hostName) && (lhs.port == rhs.port));
}