diff --git a/src/webui/CMakeLists.txt b/src/webui/CMakeLists.txt index 51cdfe489..610774a72 100644 --- a/src/webui/CMakeLists.txt +++ b/src/webui/CMakeLists.txt @@ -4,7 +4,6 @@ add_library(qbt_webui STATIC api/apierror.h api/appcontroller.h api/authcontroller.h - api/freediskspacechecker.h api/isessionmanager.h api/logcontroller.h api/rsscontroller.h @@ -13,6 +12,7 @@ add_library(qbt_webui STATIC api/torrentscontroller.h api/transfercontroller.h api/serialize/serialize_torrent.h + freediskspacechecker.h webapplication.h webui.h @@ -21,7 +21,6 @@ add_library(qbt_webui STATIC api/apierror.cpp api/appcontroller.cpp api/authcontroller.cpp - api/freediskspacechecker.cpp api/logcontroller.cpp api/rsscontroller.cpp api/searchcontroller.cpp @@ -29,6 +28,7 @@ add_library(qbt_webui STATIC api/torrentscontroller.cpp api/transfercontroller.cpp api/serialize/serialize_torrent.cpp + freediskspacechecker.cpp webapplication.cpp webui.cpp ) diff --git a/src/webui/api/synccontroller.cpp b/src/webui/api/synccontroller.cpp index 830614b28..e484b213e 100644 --- a/src/webui/api/synccontroller.cpp +++ b/src/webui/api/synccontroller.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include "base/algorithm.h" #include "base/bittorrent/cachestatus.h" @@ -50,13 +49,10 @@ #include "base/preferences.h" #include "base/utils/string.h" #include "apierror.h" -#include "freediskspacechecker.h" #include "serialize/serialize_torrent.h" namespace { - const int FREEDISKSPACE_CHECK_TIMEOUT = 30000; - // Sync main data keys const QString KEY_SYNC_MAINDATA_QUEUEING = u"queueing"_s; const QString KEY_SYNC_MAINDATA_REFRESH_INTERVAL = u"refresh_interval"_s; @@ -391,8 +387,11 @@ namespace SyncController::SyncController(IApplication *app, QObject *parent) : APIController(app, parent) { - invokeChecker(); - m_freeDiskSpaceElapsedTimer.start(); +} + +void SyncController::updateFreeDiskSpace(const qint64 freeDiskSpace) +{ + m_freeDiskSpace = freeDiskSpace; } // The function returns the changed data from the server to synchronize with the web client. @@ -552,7 +551,7 @@ void SyncController::makeMaindataSnapshot() } m_maindataSnapshot.serverState = getTransferInfo(); - m_maindataSnapshot.serverState[KEY_TRANSFER_FREESPACEONDISK] = getFreeDiskSpace(); + m_maindataSnapshot.serverState[KEY_TRANSFER_FREESPACEONDISK] = m_freeDiskSpace; m_maindataSnapshot.serverState[KEY_SYNC_MAINDATA_QUEUEING] = session->isQueueingSystemEnabled(); m_maindataSnapshot.serverState[KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS] = session->isAltGlobalSpeedLimitEnabled(); m_maindataSnapshot.serverState[KEY_SYNC_MAINDATA_REFRESH_INTERVAL] = session->refreshInterval(); @@ -661,7 +660,7 @@ QJsonObject SyncController::generateMaindataSyncData(const int id, const bool fu m_removedTrackers.clear(); QVariantMap serverState = getTransferInfo(); - serverState[KEY_TRANSFER_FREESPACEONDISK] = getFreeDiskSpace(); + serverState[KEY_TRANSFER_FREESPACEONDISK] = m_freeDiskSpace; serverState[KEY_SYNC_MAINDATA_QUEUEING] = session->isQueueingSystemEnabled(); serverState[KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS] = session->isAltGlobalSpeedLimitEnabled(); serverState[KEY_SYNC_MAINDATA_REFRESH_INTERVAL] = session->refreshInterval(); @@ -782,34 +781,6 @@ void SyncController::torrentPeersAction() setResult(generateSyncData(acceptedResponseId, data, m_lastAcceptedPeersResponse, m_lastPeersResponse)); } -qint64 SyncController::getFreeDiskSpace() -{ - if (m_freeDiskSpaceElapsedTimer.hasExpired(FREEDISKSPACE_CHECK_TIMEOUT)) - invokeChecker(); - - return m_freeDiskSpace; -} - -void SyncController::invokeChecker() -{ - if (m_isFreeDiskSpaceCheckerRunning) - return; - - auto *freeDiskSpaceChecker = new FreeDiskSpaceChecker; - connect(freeDiskSpaceChecker, &FreeDiskSpaceChecker::checked, this, [this](const qint64 freeSpaceSize) - { - m_freeDiskSpace = freeSpaceSize; - m_isFreeDiskSpaceCheckerRunning = false; - m_freeDiskSpaceElapsedTimer.restart(); - }); - connect(freeDiskSpaceChecker, &FreeDiskSpaceChecker::checked, freeDiskSpaceChecker, &QObject::deleteLater); - m_isFreeDiskSpaceCheckerRunning = true; - QThreadPool::globalInstance()->start([freeDiskSpaceChecker] - { - freeDiskSpaceChecker->check(); - }); -} - void SyncController::onCategoryAdded(const QString &categoryName) { m_removedCategories.remove(categoryName); diff --git a/src/webui/api/synccontroller.h b/src/webui/api/synccontroller.h index a77487f38..2123f625d 100644 --- a/src/webui/api/synccontroller.h +++ b/src/webui/api/synccontroller.h @@ -28,22 +28,17 @@ #pragma once -#include -#include #include +#include #include "base/bittorrent/infohash.h" #include "apicontroller.h" -class QThread; - namespace BitTorrent { class Torrent; } -class FreeDiskSpaceChecker; - class SyncController : public APIController { Q_OBJECT @@ -54,14 +49,14 @@ public: explicit SyncController(IApplication *app, QObject *parent = nullptr); +public slots: + void updateFreeDiskSpace(qint64 freeDiskSpace); + private slots: void maindataAction(); void torrentPeersAction(); private: - qint64 getFreeDiskSpace(); - void invokeChecker(); - void makeMaindataSnapshot(); QJsonObject generateMaindataSyncData(int id, bool fullUpdate); @@ -85,8 +80,6 @@ private: void onTorrentTrackersChanged(BitTorrent::Torrent *torrent); qint64 m_freeDiskSpace = 0; - QElapsedTimer m_freeDiskSpaceElapsedTimer; - bool m_isFreeDiskSpaceCheckerRunning = false; QVariantMap m_lastPeersResponse; QVariantMap m_lastAcceptedPeersResponse; diff --git a/src/webui/api/freediskspacechecker.cpp b/src/webui/freediskspacechecker.cpp similarity index 85% rename from src/webui/api/freediskspacechecker.cpp rename to src/webui/freediskspacechecker.cpp index 1d1abbe6f..23939fc97 100644 --- a/src/webui/api/freediskspacechecker.cpp +++ b/src/webui/freediskspacechecker.cpp @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2023 Vladimir Golovnev * Copyright (C) 2018 Thomas Piccirello * * This program is free software; you can redistribute it and/or @@ -31,8 +32,13 @@ #include "base/bittorrent/session.h" #include "base/utils/fs.h" +qint64 FreeDiskSpaceChecker::lastResult() const +{ + return m_lastResult; +} + void FreeDiskSpaceChecker::check() { - const qint64 freeDiskSpace = Utils::Fs::freeDiskSpaceOnPath(BitTorrent::Session::instance()->savePath()); - emit checked(freeDiskSpace); + m_lastResult = Utils::Fs::freeDiskSpaceOnPath(BitTorrent::Session::instance()->savePath()); + emit checked(m_lastResult); } diff --git a/src/webui/api/freediskspacechecker.h b/src/webui/freediskspacechecker.h similarity index 92% rename from src/webui/api/freediskspacechecker.h rename to src/webui/freediskspacechecker.h index a472f35cc..31be38b34 100644 --- a/src/webui/api/freediskspacechecker.h +++ b/src/webui/freediskspacechecker.h @@ -1,5 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2023 Vladimir Golovnev * Copyright (C) 2018 Thomas Piccirello * * This program is free software; you can redistribute it and/or @@ -38,9 +39,14 @@ class FreeDiskSpaceChecker final : public QObject public: using QObject::QObject; + qint64 lastResult() const; + public slots: void check(); signals: void checked(qint64 freeSpaceSize); + +private: + qint64 m_lastResult = 0; }; diff --git a/src/webui/webapplication.cpp b/src/webui/webapplication.cpp index 38a8daa05..be1c19972 100644 --- a/src/webui/webapplication.cpp +++ b/src/webui/webapplication.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2014, 2022 Vladimir Golovnev + * Copyright (C) 2014, 2022-2023 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -29,16 +29,20 @@ #include "webapplication.h" #include +#include #include #include #include #include #include +#include #include #include #include #include +#include +#include #include #include "base/algorithm.h" @@ -60,6 +64,7 @@ #include "api/synccontroller.h" #include "api/torrentscontroller.h" #include "api/transfercontroller.h" +#include "freediskspacechecker.h" const int MAX_ALLOWED_FILESIZE = 10 * 1024 * 1024; const QString DEFAULT_SESSION_COOKIE_NAME = u"SID"_s; @@ -68,6 +73,10 @@ const QString WWW_FOLDER = u":/www"_s; const QString PUBLIC_FOLDER = u"/public"_s; const QString PRIVATE_FOLDER = u"/private"_s; +using namespace std::chrono_literals; + +const std::chrono::seconds FREEDISKSPACE_CHECK_TIMEOUT = 30s; + namespace { QStringMap parseCookie(const QStringView cookieStr) @@ -147,6 +156,9 @@ WebApplication::WebApplication(IApplication *app, QObject *parent) , ApplicationComponent(app) , m_cacheID {QString::number(Utils::Random::rand(), 36)} , m_authController {new AuthController(this, app, this)} + , m_workerThread {new QThread} + , m_freeDiskSpaceChecker {new FreeDiskSpaceChecker} + , m_freeDiskSpaceCheckingTimer {new QTimer(this)} { declarePublicAPI(u"auth/login"_s); @@ -163,6 +175,16 @@ WebApplication::WebApplication(IApplication *app, QObject *parent) } m_sessionCookieName = DEFAULT_SESSION_COOKIE_NAME; } + + m_freeDiskSpaceChecker->moveToThread(m_workerThread.get()); + connect(m_workerThread.get(), &QThread::finished, m_freeDiskSpaceChecker, &QObject::deleteLater); + m_workerThread->start(); + + m_freeDiskSpaceCheckingTimer->setInterval(FREEDISKSPACE_CHECK_TIMEOUT); + m_freeDiskSpaceCheckingTimer->setSingleShot(true); + connect(m_freeDiskSpaceCheckingTimer, &QTimer::timeout, m_freeDiskSpaceChecker, &FreeDiskSpaceChecker::check); + connect(m_freeDiskSpaceChecker, &FreeDiskSpaceChecker::checked, m_freeDiskSpaceCheckingTimer, qOverload<>(&QTimer::start)); + QMetaObject::invokeMethod(m_freeDiskSpaceChecker, &FreeDiskSpaceChecker::check); } WebApplication::~WebApplication() @@ -690,14 +712,18 @@ void WebApplication::sessionStart() }); m_currentSession = new WebSession(generateSid(), app()); + m_sessions[m_currentSession->id()] = m_currentSession; + m_currentSession->registerAPIController(u"app"_s); m_currentSession->registerAPIController(u"log"_s); m_currentSession->registerAPIController(u"rss"_s); m_currentSession->registerAPIController(u"search"_s); - m_currentSession->registerAPIController(u"sync"_s); m_currentSession->registerAPIController(u"torrents"_s); m_currentSession->registerAPIController(u"transfer"_s); - m_sessions[m_currentSession->id()] = m_currentSession; + + auto *syncController = m_currentSession->registerAPIController(u"sync"_s); + syncController->updateFreeDiskSpace(m_freeDiskSpaceChecker->lastResult()); + connect(m_freeDiskSpaceChecker, &FreeDiskSpaceChecker::checked, syncController, &SyncController::updateFreeDiskSpace); QNetworkCookie cookie {m_sessionCookieName.toLatin1(), m_currentSession->id().toUtf8()}; cookie.setHttpOnly(true); diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index 8d9fbc9fd..95313583a 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2014, 2017, 2022 Vladimir Golovnev + * Copyright (C) 2014, 2017, 2022-2023 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -49,13 +49,17 @@ #include "base/http/types.h" #include "base/path.h" #include "base/utils/net.h" +#include "base/utils/thread.h" #include "base/utils/version.h" #include "api/isessionmanager.h" inline const Utils::Version<3, 2> API_VERSION {2, 9, 3}; +class QTimer; + class APIController; class AuthController; +class FreeDiskSpaceChecker; class WebApplication; class WebSession final : public QObject, public ApplicationComponent, public ISession @@ -69,10 +73,12 @@ public: void updateTimestamp(); template - void registerAPIController(const QString &scope) + T *registerAPIController(const QString &scope) { static_assert(std::is_base_of_v, "Class should be derived from APIController."); - m_apiControllers[scope] = new T(app(), this); + auto *controller = new T(app(), this); + m_apiControllers[scope] = controller; + return controller; } APIController *getAPIController(const QString &scope) const; @@ -97,11 +103,6 @@ public: Http::Response processRequest(const Http::Request &request, const Http::Environment &env) override; - QString clientId() const override; - WebSession *session() override; - void sessionStart() override; - void sessionEnd() override; - const Http::Request &request() const; const Http::Environment &env() const; @@ -109,6 +110,11 @@ public: void setPasswordHash(const QByteArray &passwordHash); private: + QString clientId() const override; + WebSession *session() override; + void sessionStart() override; + void sessionEnd() override; + void doProcessRequest(); void configure(); @@ -244,4 +250,8 @@ private: QHostAddress m_clientAddress; QVector m_prebuiltHeaders; + + Utils::Thread::UniquePtr m_workerThread; + FreeDiskSpaceChecker *m_freeDiskSpaceChecker = nullptr; + QTimer *m_freeDiskSpaceCheckingTimer = nullptr; }; diff --git a/src/webui/webui.pri b/src/webui/webui.pri index 1f1135d1a..8d7213569 100644 --- a/src/webui/webui.pri +++ b/src/webui/webui.pri @@ -3,7 +3,6 @@ HEADERS += \ $$PWD/api/apierror.h \ $$PWD/api/appcontroller.h \ $$PWD/api/authcontroller.h \ - $$PWD/api/freediskspacechecker.h \ $$PWD/api/isessionmanager.h \ $$PWD/api/logcontroller.h \ $$PWD/api/rsscontroller.h \ @@ -12,6 +11,7 @@ HEADERS += \ $$PWD/api/torrentscontroller.h \ $$PWD/api/transfercontroller.h \ $$PWD/api/serialize/serialize_torrent.h \ + $$PWD/freediskspacechecker.h \ $$PWD/webapplication.h \ $$PWD/webui.h @@ -20,7 +20,6 @@ SOURCES += \ $$PWD/api/apierror.cpp \ $$PWD/api/appcontroller.cpp \ $$PWD/api/authcontroller.cpp \ - $$PWD/api/freediskspacechecker.cpp \ $$PWD/api/logcontroller.cpp \ $$PWD/api/rsscontroller.cpp \ $$PWD/api/searchcontroller.cpp \ @@ -28,6 +27,7 @@ SOURCES += \ $$PWD/api/torrentscontroller.cpp \ $$PWD/api/transfercontroller.cpp \ $$PWD/api/serialize/serialize_torrent.cpp \ + $$PWD/freediskspacechecker.cpp \ $$PWD/webapplication.cpp \ $$PWD/webui.cpp