From c5d7b62473887c337c249ac7b3bd6420286c7e83 Mon Sep 17 00:00:00 2001 From: jNullj <15849761+jNullj@users.noreply.github.com> Date: Fri, 19 Jan 2024 19:38:16 +0200 Subject: [PATCH] Delay subsequent requests to the same host PR #19801. Closes #8350. --- src/base/net/downloadmanager.cpp | 9 ++++--- src/base/net/downloadmanager.h | 8 ++++-- src/base/rss/rss_feed.cpp | 9 ++++++- src/base/rss/rss_feed.h | 2 ++ src/base/rss/rss_folder.cpp | 7 +++++ src/base/rss/rss_folder.h | 2 ++ src/base/rss/rss_item.h | 2 ++ src/base/rss/rss_session.cpp | 15 +++++++++++ src/base/rss/rss_session.h | 7 +++++ src/gui/optionsdialog.cpp | 5 ++++ src/gui/optionsdialog.ui | 28 +++++++++++++++++--- src/webui/api/appcontroller.cpp | 4 +++ src/webui/www/private/views/preferences.html | 10 +++++++ 13 files changed, 98 insertions(+), 10 deletions(-) diff --git a/src/base/net/downloadmanager.cpp b/src/base/net/downloadmanager.cpp index 2d4a4e747..fdb48f6aa 100644 --- a/src/base/net/downloadmanager.cpp +++ b/src/base/net/downloadmanager.cpp @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Jonathan Ketchker * Copyright (C) 2015-2024 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * @@ -49,6 +50,8 @@ #include "downloadhandlerimpl.h" #include "proxyconfigurationmanager.h" +using namespace std::chrono_literals; + namespace { // Disguise as Firefox to avoid web server banning @@ -188,9 +191,9 @@ Net::DownloadHandler *Net::DownloadManager::download(const DownloadRequest &down return downloadHandler; } -void Net::DownloadManager::registerSequentialService(const Net::ServiceID &serviceID) +void Net::DownloadManager::registerSequentialService(const Net::ServiceID &serviceID, const std::chrono::seconds delay) { - m_sequentialServices.insert(serviceID); + m_sequentialServices.insert(serviceID, delay); } QList Net::DownloadManager::cookiesForUrl(const QUrl &url) const @@ -309,7 +312,7 @@ void Net::DownloadManager::processRequest(DownloadHandlerImpl *downloadHandler) QNetworkReply *reply = m_networkManager->get(request); connect(reply, &QNetworkReply::finished, this, [this, serviceID = ServiceID::fromURL(downloadHandler->url())] { - QTimer::singleShot(0, this, [this, serviceID] { processWaitingJobs(serviceID); }); + QTimer::singleShot(m_sequentialServices.value(serviceID, 0s), this, [this, serviceID] { processWaitingJobs(serviceID); }); }); downloadHandler->assignNetworkReply(reply); } diff --git a/src/base/net/downloadmanager.h b/src/base/net/downloadmanager.h index cef3bff50..8f74c2f9d 100644 --- a/src/base/net/downloadmanager.h +++ b/src/base/net/downloadmanager.h @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Jonathan Ketchker * Copyright (C) 2015-2024 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * @@ -29,6 +30,8 @@ #pragma once +#include + #include #include #include @@ -137,7 +140,7 @@ namespace Net template void download(const DownloadRequest &downloadRequest, bool useProxy, Context context, Func &&slot); - void registerSequentialService(const ServiceID &serviceID); + void registerSequentialService(const ServiceID &serviceID, std::chrono::seconds delay = std::chrono::seconds(0)); QList cookiesForUrl(const QUrl &url) const; bool setCookiesFromUrl(const QList &cookieList, const QUrl &url); @@ -161,7 +164,8 @@ namespace Net QNetworkAccessManager *m_networkManager = nullptr; QNetworkProxy m_proxy; - QSet m_sequentialServices; + // m_sequentialServices value is delay for same host requests + QHash m_sequentialServices; QSet m_busyServices; QHash> m_waitingJobs; }; diff --git a/src/base/rss/rss_feed.cpp b/src/base/rss/rss_feed.cpp index a707851dc..ac3515308 100644 --- a/src/base/rss/rss_feed.cpp +++ b/src/base/rss/rss_feed.cpp @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Jonathan Ketchker * Copyright (C) 2015-2022 Vladimir Golovnev * Copyright (C) 2010 Christophe Dumez * Copyright (C) 2010 Arnaud Demaiziere @@ -98,7 +99,7 @@ Feed::Feed(const QUuid &uid, const QString &url, const QString &path, Session *s else connect(m_session, &Session::processingStateChanged, this, &Feed::handleSessionProcessingEnabledChanged); - Net::DownloadManager::instance()->registerSequentialService(Net::ServiceID::fromURL(m_url)); + Net::DownloadManager::instance()->registerSequentialService(Net::ServiceID::fromURL(m_url), m_session->fetchDelay()); load(); } @@ -159,6 +160,12 @@ void Feed::refresh() emit stateChanged(this); } +void Feed::updateFetchDelay() +{ + // Update delay values for registered sequential services + Net::DownloadManager::instance()->registerSequentialService(Net::ServiceID::fromURL(m_url), m_session->fetchDelay()); +} + QUuid Feed::uid() const { return m_uid; diff --git a/src/base/rss/rss_feed.h b/src/base/rss/rss_feed.h index 491985c2d..678ff39c1 100644 --- a/src/base/rss/rss_feed.h +++ b/src/base/rss/rss_feed.h @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Jonathan Ketchker * Copyright (C) 2015-2022 Vladimir Golovnev * Copyright (C) 2010 Christophe Dumez * Copyright (C) 2010 Arnaud Demaiziere @@ -75,6 +76,7 @@ namespace RSS int unreadCount() const override; void markAsRead() override; void refresh() override; + void updateFetchDelay() override; QUuid uid() const; QString url() const; diff --git a/src/base/rss/rss_folder.cpp b/src/base/rss/rss_folder.cpp index 252f4b94c..ccf06e0ef 100644 --- a/src/base/rss/rss_folder.cpp +++ b/src/base/rss/rss_folder.cpp @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Jonathan Ketchker * Copyright (C) 2017 Vladimir Golovnev * Copyright (C) 2010 Christophe Dumez * Copyright (C) 2010 Arnaud Demaiziere @@ -91,6 +92,12 @@ void Folder::refresh() item->refresh(); } +void Folder::updateFetchDelay() +{ + for (Item *item : asConst(items())) + item->updateFetchDelay(); +} + QList Folder::items() const { return m_items; diff --git a/src/base/rss/rss_folder.h b/src/base/rss/rss_folder.h index 3441e3a02..13b4ce150 100644 --- a/src/base/rss/rss_folder.h +++ b/src/base/rss/rss_folder.h @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Jonathan Ketchker * Copyright (C) 2017 Vladimir Golovnev * Copyright (C) 2010 Christophe Dumez * Copyright (C) 2010 Arnaud Demaiziere @@ -54,6 +55,7 @@ namespace RSS int unreadCount() const override; void markAsRead() override; void refresh() override; + void updateFetchDelay() override; QList items() const; diff --git a/src/base/rss/rss_item.h b/src/base/rss/rss_item.h index df7c6ff7b..9dcd6f005 100644 --- a/src/base/rss/rss_item.h +++ b/src/base/rss/rss_item.h @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Jonathan Ketchker * Copyright (C) 2017 Vladimir Golovnev * Copyright (C) 2010 Christophe Dumez * Copyright (C) 2010 Arnaud Demaiziere @@ -52,6 +53,7 @@ namespace RSS virtual int unreadCount() const = 0; virtual void markAsRead() = 0; virtual void refresh() = 0; + virtual void updateFetchDelay() = 0; QString path() const; QString name() const; diff --git a/src/base/rss/rss_session.cpp b/src/base/rss/rss_session.cpp index ec4959eeb..1aabc7ba3 100644 --- a/src/base/rss/rss_session.cpp +++ b/src/base/rss/rss_session.cpp @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Jonathan Ketchker * Copyright (C) 2017 Vladimir Golovnev * Copyright (C) 2010 Christophe Dumez * Copyright (C) 2010 Arnaud Demaiziere @@ -62,6 +63,7 @@ QPointer Session::m_instance = nullptr; Session::Session() : m_storeProcessingEnabled(u"RSS/Session/EnableProcessing"_s) , m_storeRefreshInterval(u"RSS/Session/RefreshInterval"_s, 30) + , m_storeFetchDelay(u"RSS/Session/FetchDelay"_s, 2) , m_storeMaxArticlesPerFeed(u"RSS/Session/MaxArticlesPerFeed"_s, 50) , m_workingThread(new QThread) { @@ -525,6 +527,19 @@ void Session::setRefreshInterval(const int refreshInterval) } } +std::chrono::seconds Session::fetchDelay() const +{ + return std::chrono::seconds(m_storeFetchDelay); +} + +void Session::setFetchDelay(const std::chrono::seconds delay) +{ + if (delay == fetchDelay()) + return; + m_storeFetchDelay = static_cast(delay.count()); + rootFolder()->updateFetchDelay(); +} + QThread *Session::workingThread() const { return m_workingThread.get(); diff --git a/src/base/rss/rss_session.h b/src/base/rss/rss_session.h index 9fee14c1e..97241c56f 100644 --- a/src/base/rss/rss_session.h +++ b/src/base/rss/rss_session.h @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Jonathan Ketchker * Copyright (C) 2017 Vladimir Golovnev * Copyright (C) 2010 Christophe Dumez * Copyright (C) 2010 Arnaud Demaiziere @@ -68,6 +69,8 @@ * 3. Feed is JSON object (keys are property names, values are property values; 'uid' and 'url' are required) */ +#include + #include #include #include @@ -114,6 +117,9 @@ namespace RSS int refreshInterval() const; void setRefreshInterval(int refreshInterval); + std::chrono::seconds fetchDelay() const; + void setFetchDelay(std::chrono::seconds delay); + nonstd::expected addFolder(const QString &path); nonstd::expected addFeed(const QString &url, const QString &path); nonstd::expected setFeedURL(const QString &path, const QString &url); @@ -161,6 +167,7 @@ namespace RSS CachedSettingValue m_storeProcessingEnabled; CachedSettingValue m_storeRefreshInterval; + CachedSettingValue m_storeFetchDelay; CachedSettingValue m_storeMaxArticlesPerFeed; Utils::Thread::UniquePtr m_workingThread; AsyncFileStorage *m_confFileStorage = nullptr; diff --git a/src/gui/optionsdialog.cpp b/src/gui/optionsdialog.cpp index d0643688d..79c80bb94 100644 --- a/src/gui/optionsdialog.cpp +++ b/src/gui/optionsdialog.cpp @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Jonathan Ketchker * Copyright (C) 2023 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * @@ -29,6 +30,7 @@ #include "optionsdialog.h" +#include #include #include @@ -1193,6 +1195,7 @@ void OptionsDialog::loadRSSTabOptions() m_ui->checkRSSEnable->setChecked(rssSession->isProcessingEnabled()); m_ui->spinRSSRefreshInterval->setValue(rssSession->refreshInterval()); + m_ui->spinRSSFetchDelay->setValue(rssSession->fetchDelay().count()); m_ui->spinRSSMaxArticlesPerFeed->setValue(rssSession->maxArticlesPerFeed()); m_ui->checkRSSAutoDownloaderEnable->setChecked(autoDownloader->isProcessingEnabled()); m_ui->textSmartEpisodeFilters->setPlainText(autoDownloader->smartEpisodeFilters().join(u'\n')); @@ -1209,6 +1212,7 @@ void OptionsDialog::loadRSSTabOptions() connect(m_ui->textSmartEpisodeFilters, &QPlainTextEdit::textChanged, this, &OptionsDialog::enableApplyButton); connect(m_ui->checkSmartFilterDownloadRepacks, &QCheckBox::toggled, this, &OptionsDialog::enableApplyButton); connect(m_ui->spinRSSRefreshInterval, qSpinBoxValueChanged, this, &OptionsDialog::enableApplyButton); + connect(m_ui->spinRSSFetchDelay, qSpinBoxValueChanged, this, &OptionsDialog::enableApplyButton); connect(m_ui->spinRSSMaxArticlesPerFeed, qSpinBoxValueChanged, this, &OptionsDialog::enableApplyButton); } @@ -1219,6 +1223,7 @@ void OptionsDialog::saveRSSTabOptions() const rssSession->setProcessingEnabled(m_ui->checkRSSEnable->isChecked()); rssSession->setRefreshInterval(m_ui->spinRSSRefreshInterval->value()); + rssSession->setFetchDelay(std::chrono::seconds(m_ui->spinRSSFetchDelay->value())); rssSession->setMaxArticlesPerFeed(m_ui->spinRSSMaxArticlesPerFeed->value()); autoDownloader->setProcessingEnabled(m_ui->checkRSSAutoDownloaderEnable->isChecked()); autoDownloader->setSmartEpisodeFilters(m_ui->textSmartEpisodeFilters->toPlainText().split(u'\n', Qt::SkipEmptyParts)); diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui index 7d1d95910..2f0b1d7e6 100644 --- a/src/gui/optionsdialog.ui +++ b/src/gui/optionsdialog.ui @@ -3155,19 +3155,22 @@ Disable encryption: Only connect to peers without protocol encryption - + + + sec + 2147483646 - 100 + 2 - + - Maximum number of articles per feed: + Same host request delay: @@ -3200,6 +3203,23 @@ Disable encryption: Only connect to peers without protocol encryption + + + + 2147483646 + + + 100 + + + + + + + Maximum number of articles per feed: + + + diff --git a/src/webui/api/appcontroller.cpp b/src/webui/api/appcontroller.cpp index 49070f5ee..a785fc3f5 100644 --- a/src/webui/api/appcontroller.cpp +++ b/src/webui/api/appcontroller.cpp @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2024 Jonathan Ketchker * Copyright (C) 2018 Vladimir Golovnev * Copyright (C) 2006-2012 Christophe Dumez * Copyright (C) 2006-2012 Ishan Arora @@ -321,6 +322,7 @@ void AppController::preferencesAction() // RSS settings data[u"rss_refresh_interval"_s] = RSS::Session::instance()->refreshInterval(); + data[u"rss_fetch_delay"_s] = static_cast(RSS::Session::instance()->fetchDelay().count()); data[u"rss_max_articles_per_feed"_s] = RSS::Session::instance()->maxArticlesPerFeed(); data[u"rss_processing_enabled"_s] = RSS::Session::instance()->isProcessingEnabled(); data[u"rss_auto_downloading_enabled"_s] = RSS::AutoDownloader::instance()->isProcessingEnabled(); @@ -870,6 +872,8 @@ void AppController::setPreferencesAction() if (hasKey(u"rss_refresh_interval"_s)) RSS::Session::instance()->setRefreshInterval(it.value().toInt()); + if (hasKey(u"rss_fetch_delay"_s)) + RSS::Session::instance()->setFetchDelay(std::chrono::seconds(it.value().toLongLong())); if (hasKey(u"rss_max_articles_per_feed"_s)) RSS::Session::instance()->setMaxArticlesPerFeed(it.value().toInt()); if (hasKey(u"rss_processing_enabled"_s)) diff --git a/src/webui/www/private/views/preferences.html b/src/webui/www/private/views/preferences.html index 48a959d31..a25c4bad7 100644 --- a/src/webui/www/private/views/preferences.html +++ b/src/webui/www/private/views/preferences.html @@ -737,6 +737,14 @@   QBT_TR( min)QBT_TR[CONTEXT=OptionsDialog] + + + + + +   QBT_TR( sec)QBT_TR[CONTEXT=OptionsDialog] + + @@ -2227,6 +2235,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD // RSS Tab $('enable_fetching_rss_feeds_checkbox').setProperty('checked', pref.rss_processing_enabled); $('feed_refresh_interval').setProperty('value', pref.rss_refresh_interval); + $('feedFetchDelay').setProperty('value', perf.rss_fetch_delay); $('maximum_article_number').setProperty('value', pref.rss_max_articles_per_feed); $('enable_auto_downloading_rss_torrents_checkbox').setProperty('checked', pref.rss_auto_downloading_enabled); $('downlock_repack_proper_episodes').setProperty('checked', pref.rss_download_repack_proper_episodes); @@ -2640,6 +2649,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD // RSS Tab settings.set('rss_processing_enabled', $('enable_fetching_rss_feeds_checkbox').getProperty('checked')); settings.set('rss_refresh_interval', $('feed_refresh_interval').getProperty('value')); + settings.set('rss_fetch_delay', $('feedFetchDelay').getProperties('value')); settings.set('rss_max_articles_per_feed', $('maximum_article_number').getProperty('value')); settings.set('rss_auto_downloading_enabled', $('enable_auto_downloading_rss_torrents_checkbox').getProperty('checked')); settings.set('rss_download_repack_proper_episodes', $('downlock_repack_proper_episodes').getProperty('checked'));