Repository: qbittorrent/qBittorrent
Pull request #22407: Show free disk space in status bar
Author: login: glassez
Labels: name: GUI
description: GUI-related issues/changes
name: Core
Created: 2025-03-09T11:24:09Z | Merged: 2025-03-13T11:47:10Z

Closes #19607.

![20250309_141549](https://github.com/user-attachments/assets/1675a256-e68c-4709-9d61-f5700b328f9c)


![20250309_141658](https://github.com/user-attachments/assets/69ec87b9-69b6-464c-b1b9-ae74c512a892)



Linked issue #19607: Always show free space in desktop client bottom bar like we do on the WebUI
### Suggestion

Always show free space in desktop client bottom bar like we do on the WebUI

### Use case

The desktop client should always show free space like the WebUI does. 

### Extra info/examples/attachments

_No response_
Linked issue comments: user: login: Pentaphon
created_at: 2024-07-01T05:34:05Z
body: @glassez thoughts on getting this feature in 5.x? This feature is long overdue.  It just has to match how it looks on the WebUI. 
author_association: NONE
user: login: glassez
created_at: 2024-07-01T06:17:18Z
body: > @glassez thoughts on getting this feature in 5.x? This feature is long overdue. It just has to match how it looks on the WebUI.

Personally, I have never liked such a "feature". There are more suitable ways to find out details about free space, etc. (e.g. file managers, desktop and taskbar widgets).
author_association: MEMBER
user: login: Pentaphon
created_at: 2024-07-01T06:43:22Z
body: > Personally, I have never liked such a "feature". There are more suitable ways to find out details about free space, etc. (e.g. file managers, desktop and taskbar widgets).

Fair enough. I do think it is useful to see your free space on the client itself before you add another torrent though because a lot of people do not use widgets on their desktop and I'd argue that a torrent client deals with such big files that showing the free space on a torrent client makes it "feel" like it is a part of your existing file manager. Thanks for responding so quickly, even if we ended up not totally agreeing. I really appreciate the attention you give to this project. 
author_association: NONE
user: login: glassez
created_at: 2024-07-01T09:46:58Z
body: > I do think it is useful to see your free space on the client itself before you add another torrent

It is shown in Add new torrent dialog.
author_association: MEMBER
user: login: Pentaphon
created_at: 2024-07-01T10:44:58Z
body: > It is shown in Add new torrent dialog.

Yes, I'm aware. I know it makes more sense to have it on the WebUI than the desktop client but I just prefer to always have it being shown on the main window, especially when using a smaller device like a laptop with limited storage. 
author_association: NONE
user: login: Pentaphon
created_at: 2025-02-16T18:18:03Z
body: @glassez do you think this will make it into 5.1 or is 5.2 more realistic?
author_association: NONE
user: login: glassez
created_at: 2025-03-09T11:24:36Z
body: #22407
author_association: MEMBER

Diff:
diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt
index 5d2f9cf0e3be..7998d35019f5 100644
--- a/src/base/CMakeLists.txt
+++ b/src/base/CMakeLists.txt
@@ -54,6 +54,7 @@ add_library(qbt_base STATIC
     concepts/stringable.h
     digest32.h
     exceptions.h
+    freediskspacechecker.h
     global.h
     http/connection.h
     http/httperror.h
@@ -159,6 +160,7 @@ add_library(qbt_base STATIC
     bittorrent/trackerentry.cpp
     bittorrent/trackerentrystatus.cpp
     exceptions.cpp
+    freediskspacechecker.cpp
     http/connection.cpp
     http/httperror.cpp
     http/requestparser.cpp
diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h
index f5c06217208f..4e40833fdd13 100644
--- a/src/base/bittorrent/session.h
+++ b/src/base/bittorrent/session.h
@@ -1,6 +1,6 @@
 /*
  * Bittorrent Client using Qt and libtorrent.
- * Copyright (C) 2015-2024  Vladimir Golovnev <glassez@yandex.ru>
+ * Copyright (C) 2015-2025  Vladimir Golovnev <glassez@yandex.ru>
  * Copyright (C) 2006  Christophe Dumez <chris@qbittorrent.org>
  *
  * This program is free software; you can redistribute it and/or
@@ -479,6 +479,8 @@ namespace BitTorrent
         virtual QString lastExternalIPv4Address() const = 0;
         virtual QString lastExternalIPv6Address() const = 0;
 
+        virtual qint64 freeDiskSpace() const = 0;
+
     signals:
         void startupProgressUpdated(int progress);
         void addTorrentFailed(const InfoHash &infoHash, const QString &reason);
@@ -519,5 +521,6 @@ namespace BitTorrent
         void trackerSuccess(Torrent *torrent, const QString &tracker);
         void trackerWarning(Torrent *torrent, const QString &tracker);
         void trackerEntryStatusesUpdated(Torrent *torrent, const QHash<QString, TrackerEntryStatus> &updatedTrackers);
+        void freeDiskSpaceChecked(qint64 result);
     };
 }
diff --git a/src/base/bittorrent/sessionimpl.cpp b/src/base/bittorrent/sessionimpl.cpp
index 09df5d1e7ee1..961039355376 100644
--- a/src/base/bittorrent/sessionimpl.cpp
+++ b/src/base/bittorrent/sessionimpl.cpp
@@ -1,6 +1,6 @@
 /*
  * Bittorrent Client using Qt and libtorrent.
- * Copyright (C) 2015-2024  Vladimir Golovnev <glassez@yandex.ru>
+ * Copyright (C) 2015-2025  Vladimir Golovnev <glassez@yandex.ru>
  * Copyright (C) 2006  Christophe Dumez <chris@qbittorrent.org>
  *
  * This program is free software; you can redistribute it and/or
@@ -76,6 +76,7 @@
 #include <QUuid>
 
 #include "base/algorithm.h"
+#include "base/freediskspacechecker.h"
 #include "base/global.h"
 #include "base/logger.h"
 #include "base/net/downloadmanager.h"
@@ -115,6 +116,7 @@ using namespace BitTorrent;
 
 const Path CATEGORIES_FILE_NAME {u"categories.json"_s};
 const int MAX_PROCESSING_RESUMEDATA_COUNT = 50;
+const std::chrono::seconds FREEDISKSPACE_CHECK_TIMEOUT = 30s;
 
 namespace
 {
@@ -540,6 +542,8 @@ SessionImpl::SessionImpl(QObject *parent)
     , m_ioThread {new QThread}
     , m_asyncWorker {new QThreadPool(this)}
     , m_recentErroredTorrentsTimer {new QTimer(this)}
+    , m_freeDiskSpaceChecker {new FreeDiskSpaceChecker(savePath())}
+    , m_freeDiskSpaceCheckingTimer {new QTimer(this)}
 {
     // It is required to perform async access to libtorrent sequentially
     m_asyncWorker->setMaxThreadCount(1);
@@ -600,6 +604,18 @@ SessionImpl::SessionImpl(QObject *parent)
         , &Net::ProxyConfigurationManager::proxyConfigurationChanged
         , this, &SessionImpl::configureDeferred);
 
+    m_freeDiskSpaceChecker->moveToThread(m_ioThread.get());
+    connect(m_ioThread.get(), &QThread::finished, m_freeDiskSpaceChecker, &QObject::deleteLater);
+    m_freeDiskSpaceCheckingTimer->setInterval(FREEDISKSPACE_CHECK_TIMEOUT);
+    m_freeDiskSpaceCheckingTimer->setSingleShot(true);
+    connect(m_freeDiskSpaceCheckingTimer, &QTimer::timeout, m_freeDiskSpaceChecker, &FreeDiskSpaceChecker::check);
+    connect(m_freeDiskSpaceChecker, &FreeDiskSpaceChecker::checked, this, [this](const qint64 value)
+    {
+        m_freeDiskSpace = value;
+        m_freeDiskSpaceCheckingTimer->start();
+        emit freeDiskSpaceChecked(m_freeDiskSpace);
+    });
+
     m_fileSearcher = new FileSearcher;
     m_fileSearcher->moveToThread(m_ioThread.get());
     connect(m_ioThread.get(), &QThread::finished, m_fileSearcher, &QObject::deleteLater);
@@ -613,6 +629,8 @@ SessionImpl::SessionImpl(QObject *parent)
     m_ioThread->setObjectName("SessionImpl m_ioThread");
     m_ioThread->start();
 
+    QMetaObject::invokeMethod(m_freeDiskSpaceChecker, &FreeDiskSpaceChecker::check);
+
     initMetrics();
     loadStatistics();
 
@@ -3266,6 +3284,14 @@ void SessionImpl::setSavePath(const Path &path)
     m_savePath = newPath;
     for (TorrentImpl *const torrent : asConst(m_torrents))
         torrent->handleCategoryOptionsChanged();
+
+    m_freeDiskSpace = -1;
+    m_freeDiskSpaceCheckingTimer->stop();
+    QMetaObject::invokeMethod(m_freeDiskSpaceChecker, [checker = m_freeDiskSpaceChecker, pathToCheck = m_savePath]
+    {
+        checker->setPathToCheck(pathToCheck);
+        checker->check();
+    });
 }
 
 void SessionImpl::setDownloadPath(const Path &path)
@@ -5113,6 +5139,11 @@ QString SessionImpl::lastExternalIPv6Address() const
     return m_lastExternalIPv6Address;
 }
 
+qint64 SessionImpl::freeDiskSpace() const
+{
+    return m_freeDiskSpace;
+}
+
 bool SessionImpl::isListening() const
 {
     return m_nativeSessionExtension->isSessionListening();
diff --git a/src/base/bittorrent/sessionimpl.h b/src/base/bittorrent/sessionimpl.h
index 960f0cd99e80..1dbe647b2a73 100644
--- a/src/base/bittorrent/sessionimpl.h
+++ b/src/base/bittorrent/sessionimpl.h
@@ -1,6 +1,6 @@
 /*
  * Bittorrent Client using Qt and libtorrent.
- * Copyright (C) 2015-2024  Vladimir Golovnev <glassez@yandex.ru>
+ * Copyright (C) 2015-2025  Vladimir Golovnev <glassez@yandex.ru>
  * Copyright (C) 2006  Christophe Dumez <chris@qbittorrent.org>
  *
  * This program is free software; you can redistribute it and/or
@@ -64,6 +64,7 @@ class QUrl;
 class BandwidthScheduler;
 class FileSearcher;
 class FilterParserThread;
+class FreeDiskSpaceChecker;
 class NativeSessionExtension;
 
 namespace BitTorrent
@@ -448,6 +449,8 @@ namespace BitTorrent
         QString lastExternalIPv4Address() const override;
         QString lastExternalIPv6Address() const override;
 
+        qint64 freeDiskSpace() const override;
+
         // Torrent interface
         void handleTorrentResumeDataRequested(const TorrentImpl *torrent);
         void handleTorrentShareLimitChanged(TorrentImpl *torrent);
@@ -850,6 +853,10 @@ namespace BitTorrent
 
         QList<TorrentImpl *> m_pendingFinishedTorrents;
 
+        FreeDiskSpaceChecker *m_freeDiskSpaceChecker = nullptr;
+        QTimer *m_freeDiskSpaceCheckingTimer = nullptr;
+        qint64 m_freeDiskSpace = -1;
+
         friend void Session::initInstance();
         friend void Session::freeInstance();
         friend Session *Session::instance();
diff --git a/src/webui/freediskspacechecker.cpp b/src/base/freediskspacechecker.cpp
similarity index 78%
rename from src/webui/freediskspacechecker.cpp
rename to src/base/freediskspacechecker.cpp
index 23939fc97aff..ace9dcf9674f 100644
--- a/src/webui/freediskspacechecker.cpp
+++ b/src/base/freediskspacechecker.cpp
@@ -1,6 +1,6 @@
 /*
  * Bittorrent Client using Qt and libtorrent.
- * Copyright (C) 2023  Vladimir Golovnev <glassez@yandex.ru>
+ * Copyright (C) 2023-2025  Vladimir Golovnev <glassez@yandex.ru>
  * Copyright (C) 2018  Thomas Piccirello <thomas.piccirello@gmail.com>
  *
  * This program is free software; you can redistribute it and/or
@@ -29,16 +29,24 @@
 
 #include "freediskspacechecker.h"
 
-#include "base/bittorrent/session.h"
 #include "base/utils/fs.h"
 
-qint64 FreeDiskSpaceChecker::lastResult() const
+FreeDiskSpaceChecker::FreeDiskSpaceChecker(const Path &pathToCheck)
+    : m_pathToCheck {pathToCheck}
 {
-    return m_lastResult;
+}
+
+Path FreeDiskSpaceChecker::pathToCheck() const
+{
+    return m_pathToCheck;
+}
+
+void FreeDiskSpaceChecker::setPathToCheck(const Path &newPathToCheck)
+{
+    m_pathToCheck = newPathToCheck;
 }
 
 void FreeDiskSpaceChecker::check()
 {
-    m_lastResult = Utils::Fs::freeDiskSpaceOnPath(BitTorrent::Session::instance()->savePath());
-    emit checked(m_lastResult);
+    emit checked(Utils::Fs::freeDiskSpaceOnPath(m_pathToCheck));
 }
diff --git a/src/webui/freediskspacechecker.h b/src/base/freediskspacechecker.h
similarity index 87%
rename from src/webui/freediskspacechecker.h
rename to src/base/freediskspacechecker.h
index 31be38b34444..950ffc0260c8 100644
--- a/src/webui/freediskspacechecker.h
+++ b/src/base/freediskspacechecker.h
@@ -1,6 +1,6 @@
 /*
  * Bittorrent Client using Qt and libtorrent.
- * Copyright (C) 2023  Vladimir Golovnev <glassez@yandex.ru>
+ * Copyright (C) 2023-2025  Vladimir Golovnev <glassez@yandex.ru>
  * Copyright (C) 2018  Thomas Piccirello <thomas.piccirello@gmail.com>
  *
  * This program is free software; you can redistribute it and/or
@@ -31,15 +31,18 @@
 
 #include <QObject>
 
+#include "base/path.h"
+
 class FreeDiskSpaceChecker final : public QObject
 {
     Q_OBJECT
     Q_DISABLE_COPY_MOVE(FreeDiskSpaceChecker)
 
 public:
-    using QObject::QObject;
+    FreeDiskSpaceChecker(const Path &pathToCheck);
 
-    qint64 lastResult() const;
+    Path pathToCheck() const;
+    void setPathToCheck(const Path &newPathToCheck);
 
 public slots:
     void check();
@@ -48,5 +51,5 @@ public slots:
     void checked(qint64 freeSpaceSize);
 
 private:
-    qint64 m_lastResult = 0;
+    Path m_pathToCheck;
 };
diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp
index 4cf0e9bef5ea..439226b96b79 100644
--- a/src/base/preferences.cpp
+++ b/src/base/preferences.cpp
@@ -359,6 +359,19 @@ void Preferences::setStatusbarDisplayed(const bool displayed)
     setValue(u"Preferences/General/StatusbarDisplayed"_s, displayed);
 }
 
+bool Preferences::isStatusbarFreeDiskSpaceDisplayed() const
+{
+    return value(u"Preferences/General/StatusbarFreeDiskSpaceDisplayed"_s, false);
+}
+
+void Preferences::setStatusbarFreeDiskSpaceDisplayed(const bool displayed)
+{
+    if (displayed == isStatusbarFreeDiskSpaceDisplayed())
+        return;
+
+    setValue(u"Preferences/General/StatusbarFreeDiskSpaceDisplayed"_s, displayed);
+}
+
 bool Preferences::isStatusbarExternalIPDisplayed() const
 {
     return value(u"Preferences/General/StatusbarExternalIPDisplayed"_s, false);
diff --git a/src/base/preferences.h b/src/base/preferences.h
index 95cb873d7a60..8a4d23300df5 100644
--- a/src/base/preferences.h
+++ b/src/base/preferences.h
@@ -119,6 +119,8 @@ class Preferences final : public QObject
     void setHideZeroComboValues(int n);
     bool isStatusbarDisplayed() const;
     void setStatusbarDisplayed(bool displayed);
+    bool isStatusbarFreeDiskSpaceDisplayed() const;
+    void setStatusbarFreeDiskSpaceDisplayed(bool displayed);
     bool isStatusbarExternalIPDisplayed() const;
     void setStatusbarExternalIPDisplayed(bool displayed);
     bool isToolbarDisplayed() const;
diff --git a/src/gui/optionsdialog.cpp b/src/gui/optionsdialog.cpp
index 3527e4db9ed5..809db94cccf7 100644
--- a/src/gui/optionsdialog.cpp
+++ b/src/gui/optionsdialog.cpp
@@ -355,6 +355,7 @@ void OptionsDialog::loadBehaviorTabOptions()
     // Groupbox's check state  must be initialized after some of its children if they are manually enabled/disabled
     m_ui->checkFileLog->setChecked(app()->isFileLoggerEnabled());
 
+    m_ui->checkBoxFreeDiskSpaceStatusBar->setChecked(pref->isStatusbarFreeDiskSpaceDisplayed());
     m_ui->checkBoxExternalIPStatusBar->setChecked(pref->isStatusbarExternalIPDisplayed());
     m_ui->checkBoxPerformanceWarning->setChecked(session->isPerformanceWarningEnabled());
 
@@ -443,6 +444,7 @@ void OptionsDialog::loadBehaviorTabOptions()
     connect(m_ui->spinFileLogAge, qSpinBoxValueChanged, this, &ThisType::enableApplyButton);
     connect(m_ui->comboFileLogAgeType, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton);
 
+    connect(m_ui->checkBoxFreeDiskSpaceStatusBar, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
     connect(m_ui->checkBoxExternalIPStatusBar, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
     connect(m_ui->checkBoxPerformanceWarning, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
 }
@@ -536,6 +538,7 @@ void OptionsDialog::saveBehaviorTabOptions() const
 
     app()->setStartUpWindowState(m_ui->windowStateComboBox->currentData().value<WindowState>());
 
+    pref->setStatusbarFreeDiskSpaceDisplayed(m_ui->checkBoxFreeDiskSpaceStatusBar->isChecked());
     pref->setStatusbarExternalIPDisplayed(m_ui->checkBoxExternalIPStatusBar->isChecked());
     session->setPerformanceWarningEnabled(m_ui->checkBoxPerformanceWarning->isChecked());
 }
diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui
index 71906f13b547..49311f6020e3 100644
--- a/src/gui/optionsdialog.ui
+++ b/src/gui/optionsdialog.ui
@@ -819,6 +819,13 @@
               </layout>
              </widget>
             </item>
+            <item>
+             <widget class="QCheckBox" name="checkBoxFreeDiskSpaceStatusBar">
+              <property name="text">
+               <string>Show free disk space in status bar</string>
+              </property>
+             </widget>
+            </item>
             <item>
              <widget class="QCheckBox" name="checkBoxExternalIPStatusBar">
               <property name="text">
diff --git a/src/gui/statusbar.cpp b/src/gui/statusbar.cpp
index 9f40708087cb..d881ee24b535 100644
--- a/src/gui/statusbar.cpp
+++ b/src/gui/statusbar.cpp
@@ -44,6 +44,19 @@
 #include "uithememanager.h"
 #include "utils.h"
 
+namespace
+{
+    QWidget *createSeparator(QWidget *parent)
+    {
+        QFrame *separator = new QFrame(parent);
+        separator->setFrameStyle(QFrame::VLine);
+    #ifndef Q_OS_MACOS
+        separator->setFrameShadow(QFrame::Raised);
+    #endif
+        return separator;
+    }
+}
+
 StatusBar::StatusBar(QWidget *parent)
     : QStatusBar(parent)
 {
@@ -87,11 +100,17 @@ StatusBar::StatusBar(QWidget *parent)
     m_upSpeedLbl->setStyleSheet(u"text-align:left;"_s);
     m_upSpeedLbl->setMinimumWidth(200);
 
+    m_freeDiskSpaceLbl = new QLabel(tr("Free space: N/A"));
+    m_freeDiskSpaceLbl->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
+    m_freeDiskSpaceSeparator = createSeparator(m_freeDiskSpaceLbl);
+
     m_lastExternalIPsLbl = new QLabel(tr("External IP: N/A"));
     m_lastExternalIPsLbl->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
+    m_lastExternalIPsSeparator = createSeparator(m_lastExternalIPsLbl);
 
     m_DHTLbl = new QLabel(tr("DHT: %1 nodes").arg(0), this);
     m_DHTLbl->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
+    m_DHTSeparator = createSeparator(m_DHTLbl);
 
     m_altSpeedsBtn = new QPushButton(this);
     m_altSpeedsBtn->setFlat(true);
@@ -113,52 +132,42 @@ StatusBar::StatusBar(QWidget *parent)
     m_connecStatusLblIcon->setMaximumWidth(Utils::Gui::largeIconSize().width());
     m_altSpeedsBtn->setMaximumWidth(Utils::Gui::largeIconSize().width());
 
-    QFrame *statusSep1 = new QFrame(this);
-    statusSep1->setFrameStyle(QFrame::VLine);
-#ifndef Q_OS_MACOS
-    statusSep1->setFrameShadow(QFrame::Raised);
-#endif
-    QFrame *statusSep2 = new QFrame(this);
-    statusSep2->setFrameStyle(QFrame::VLine);
-#ifndef Q_OS_MACOS
-    statusSep2->setFrameShadow(QFrame::Raised);
-#endif
-    QFrame *statusSep3 = new QFrame(this);
-    statusSep3->setFrameStyle(QFrame::VLine);
-#ifndef Q_OS_MACOS
-    statusSep3->setFrameShadow(QFrame::Raised);
-#endif
-    QFrame *statusSep4 = new QFrame(this);
-    statusSep4->setFrameStyle(QFrame::VLine);
-#ifndef Q_OS_MACOS
-    statusSep4->setFrameShadow(QFrame::Raised);
-#endif
-    QFrame *statusSep5 = new QFrame(this);
-    statusSep5->setFrameStyle(QFrame::VLine);
-#ifndef Q_OS_MACOS
-    statusSep5->setFrameShadow(QFrame::Raised);
-#endif
+    layout->addWidget(m_freeDiskSpaceLbl);
+    layout->addWidget(m_freeDiskSpaceSeparator);
+
     layout->addWidget(m_lastExternalIPsLbl);
-    layout->addWidget(statusSep1);
+    layout->addWidget(m_lastExternalIPsSeparator);
+
     layout->addWidget(m_DHTLbl);
-    layout->addWidget(statusSep2);
+    layout->addWidget(m_DHTSeparator);
+
     layout->addWidget(m_connecStatusLblIcon);
-    layout->addWidget(statusSep3);
+    layout->addWidget(createSeparator(m_connecStatusLblIcon));
+
     layout->addWidget(m_altSpeedsBtn);
-    layout->addWidget(statusSep4);
+    layout->addWidget(createSeparator(m_altSpeedsBtn));
+
     layout->addWidget(m_dlSpeedLbl);
-    layout->addWidget(statusSep5);
+    layout->addWidget(createSeparator(m_dlSpeedLbl));
+
     layout->addWidget(m_upSpeedLbl);
 
     addPermanentWidget(container);
     setStyleSheet(u"QWidget {margin: 0;}"_s);
     container->adjustSize();
     adjustSize();
+    updateFreeDiskSpaceVisibility();
     updateExternalAddressesVisibility();
     // Is DHT enabled
-    m_DHTLbl->setVisible(session->isDHTEnabled());
+    const bool isDHTVisible = session->isDHTEnabled();
+    m_DHTLbl->setVisible(isDHTVisible);
+    m_DHTSeparator->setVisible(isDHTVisible);
     refresh();
     connect(session, &BitTorrent::Session::statsUpdated, this, &StatusBar::refresh);
+
+    updateFreeDiskSpaceLabel(session->freeDiskSpace());
+    connect(session, &BitTorrent::Session::freeDiskSpaceChecked, this, &StatusBar::updateFreeDiskSpaceLabel);
+
     connect(Preferences::instance(), &Preferences::changed, this, &StatusBar::optionsSaved);
 }
 
@@ -216,15 +225,28 @@ void StatusBar::updateDHTNodesNumber()
     if (BitTorrent::Session::instance()->isDHTEnabled())
     {
         m_DHTLbl->setVisible(true);
-        m_DHTLbl->setText(tr("DHT: %1 nodes")
-                          .arg(BitTorrent::Session::instance()->status().dhtNodes));
+        m_DHTSeparator->setVisible(true);
+        m_DHTLbl->setText(tr("DHT: %1 nodes").arg(BitTorrent::Session::instance()->status().dhtNodes));
     }
     else
     {
         m_DHTLbl->setVisible(false);
+        m_DHTSeparator->setVisible(false);
     }
 }
 
+void StatusBar::updateFreeDiskSpaceLabel(const qint64 value)
+{
+    m_freeDiskSpaceLbl->setText(tr("Free space: ") + Utils::Misc::friendlyUnit(value));
+}
+
+void StatusBar::updateFreeDiskSpaceVisibility()
+{
+    const bool isVisible = Preferences::instance()->isStatusbarFreeDiskSpaceDisplayed();
+    m_freeDiskSpaceLbl->setVisible(isVisible);
+    m_freeDiskSpaceSeparator->setVisible(isVisible);
+}
+
 void StatusBar::updateExternalAddressesLabel()
 {
     const QString lastExternalIPv4Address = BitTorrent::Session::instance()->lastExternalIPv4Address();
@@ -244,7 +266,9 @@ void StatusBar::updateExternalAddressesLabel()
 
 void StatusBar::updateExternalAddressesVisibility()
 {
-    m_lastExternalIPsLbl->setVisible(Preferences::instance()->isStatusbarExternalIPDisplayed());
+    const bool isVisible = Preferences::instance()->isStatusbarExternalIPDisplayed();
+    m_lastExternalIPsLbl->setVisible(isVisible);
+    m_lastExternalIPsSeparator->setVisible(isVisible);
 }
 
 void StatusBar::updateSpeedLabels()
@@ -300,5 +324,6 @@ void StatusBar::capSpeed()
 
 void StatusBar::optionsSaved()
 {
+    updateFreeDiskSpaceVisibility();
     updateExternalAddressesVisibility();
 }
diff --git a/src/gui/statusbar.h b/src/gui/statusbar.h
index d468467079ae..27fde54d1871 100644
--- a/src/gui/statusbar.h
+++ b/src/gui/statusbar.h
@@ -63,14 +63,20 @@ private slots:
 private:
     void updateConnectionStatus();
     void updateDHTNodesNumber();
+    void updateFreeDiskSpaceLabel(qint64 value);
+    void updateFreeDiskSpaceVisibility();
     void updateExternalAddressesLabel();
     void updateExternalAddressesVisibility();
     void updateSpeedLabels();
 
     QPushButton *m_dlSpeedLbl = nullptr;
     QPushButton *m_upSpeedLbl = nullptr;
+    QLabel *m_freeDiskSpaceLbl = nullptr;
+    QWidget *m_freeDiskSpaceSeparator = nullptr;
     QLabel *m_lastExternalIPsLbl = nullptr;
+    QWidget *m_lastExternalIPsSeparator = nullptr;
     QLabel *m_DHTLbl = nullptr;
+    QWidget *m_DHTSeparator = nullptr;
     QPushButton *m_connecStatusLblIcon = nullptr;
     QPushButton *m_altSpeedsBtn = nullptr;
 };
diff --git a/src/webui/CMakeLists.txt b/src/webui/CMakeLists.txt
index 9dfc12831cef..c8234023998b 100644
--- a/src/webui/CMakeLists.txt
+++ b/src/webui/CMakeLists.txt
@@ -13,7 +13,6 @@ add_library(qbt_webui STATIC
     api/torrentscontroller.h
     api/transfercontroller.h
     api/serialize/serialize_torrent.h
-    freediskspacechecker.h
     webapplication.h
     webui.h
 
@@ -30,7 +29,6 @@ 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/webapplication.cpp b/src/webui/webapplication.cpp
index 0f556f3f36a0..42098bc6767b 100644
--- a/src/webui/webapplication.cpp
+++ b/src/webui/webapplication.cpp
@@ -1,6 +1,6 @@
 /*
  * Bittorrent Client using Qt and libtorrent.
- * Copyright (C) 2014-2024  Vladimir Golovnev <glassez@yandex.ru>
+ * Copyright (C) 2014-2025  Vladimir Golovnev <glassez@yandex.ru>
  * Copyright (C) 2024  Radu Carpa <radu.carpa@cern.ch>
  *
  * This program is free software; you can redistribute it and/or
@@ -43,10 +43,10 @@
 #include <QNetworkCookie>
 #include <QRegularExpression>
 #include <QThread>
-#include <QTimer>
 #include <QUrl>
 
 #include "base/algorithm.h"
+#include "base/bittorrent/session.h"
 #include "base/bittorrent/torrentcreationmanager.h"
 #include "base/http/httperror.h"
 #include "base/logger.h"
@@ -67,7 +67,6 @@
 #include "api/torrentcreatorcontroller.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;
@@ -76,10 +75,6 @@ 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)
@@ -161,9 +156,6 @@ WebApplication::WebApplication(IApplication *app, QObject *parent)
     : ApplicationComponent(app, parent)
     , 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)}
     , m_torrentCreationManager {new BitTorrent::TorrentCreationManager(app, this)}
 {
     declarePublicAPI(u"auth/login"_s);
@@ -181,17 +173,6 @@ 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->setObjectName("WebApplication m_workerThread");
-    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()
@@ -739,9 +720,10 @@ void WebApplication::sessionStart()
     m_currentSession->registerAPIController(u"torrents"_s, new TorrentsController(app(), m_currentSession));
     m_currentSession->registerAPIController(u"transfer"_s, new TransferController(app(), m_currentSession));
 
+    const auto *btSession = BitTorrent::Session::instance();
     auto *syncController = new SyncController(app(), m_currentSession);
-    syncController->updateFreeDiskSpace(m_freeDiskSpaceChecker->lastResult());
-    connect(m_freeDiskSpaceChecker, &FreeDiskSpaceChecker::checked, syncController, &SyncController::updateFreeDiskSpace);
+    syncController->updateFreeDiskSpace(btSession->freeDiskSpace());
+    connect(btSession, &BitTorrent::Session::freeDiskSpaceChecked, syncController, &SyncController::updateFreeDiskSpace);
     m_currentSession->registerAPIController(u"sync"_s, syncController);
 
     QNetworkCookie cookie {m_sessionCookieName.toLatin1(), m_currentSession->id().toLatin1()};
diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h
index e30d5f349e69..295919e221b8 100644
--- a/src/webui/webapplication.h
+++ b/src/webui/webapplication.h
@@ -1,6 +1,6 @@
 /*
  * Bittorrent Client using Qt and libtorrent.
- * Copyright (C) 2014-2024  Vladimir Golovnev <glassez@yandex.ru>
+ * Copyright (C) 2014-2025  Vladimir Golovnev <glassez@yandex.ru>
  * Copyright (C) 2024  Radu Carpa <radu.carpa@cern.ch>
  *
  * This program is free software; you can redistribute it and/or
@@ -50,17 +50,13 @@
 #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, 11, 4};
 
-class QTimer;
-
 class APIController;
 class AuthController;
-class FreeDiskSpaceChecker;
 class WebApplication;
 
 namespace BitTorrent
@@ -259,8 +255,5 @@ class WebApplication final : public ApplicationComponent<QObject>
 
     QList<Http::Header> m_prebuiltHeaders;
 
-    Utils::Thread::UniquePtr m_workerThread;
-    FreeDiskSpaceChecker *m_freeDiskSpaceChecker = nullptr;
-    QTimer *m_freeDiskSpaceCheckingTimer = nullptr;
     BitTorrent::TorrentCreationManager *m_torrentCreationManager = nullptr;
 };


Reviews:
user: login: thalieht
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-03-09T13:36:50Z
user: login: xavier2k6
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-03-11T19:22:41Z
user: login: Chocobo1
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-12T18:04:46Z
user: login: glassez
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-13T05:42:33Z
user: login: Chocobo1
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-13T06:37:39Z

Review comments:
diff_hunk: @@ -67,7 +68,6 @@
 #include "api/torrentcreatorcontroller.h"
 #include "api/torrentscontroller.h"
 #include "api/transfercontroller.h"
-#include "freediskspacechecker.h"
path: src/webui/webapplication.cpp
user: login: Chocobo1
body: It seems `QTimer` header can also be removed.
created_at: 2025-03-12T18:04:01Z
author_association: MEMBER
diff_hunk: @@ -67,7 +68,6 @@
 #include "api/torrentcreatorcontroller.h"
 #include "api/torrentscontroller.h"
 #include "api/transfercontroller.h"
-#include "freediskspacechecker.h"
path: src/webui/webapplication.cpp
user: login: glassez
body: > It seems `QTimer` header can also be removed.

You're right. Done.
created_at: 2025-03-13T05:42:33Z
author_association: MEMBER

PR comments:
user: login: xavier2k6
created_at: 2025-03-09T23:04:28Z
body: ~Doesn't work for me, shows "Unknown" (checked using portable mode)~

Oh I see, it actually only shows the local/main drive available space - it doesn't show secondary drives/external USB connected or NAS mounted etc

![Screenshot 2025-03-09 231841](https://github.com/user-attachments/assets/8e55ac5b-b71f-454c-b0d6-dd6ccb8f838d)

![Screenshot 2025-03-09 231729](https://github.com/user-attachments/assets/eb323993-1eee-41f6-9ab0-d78a21c09d79)

![Screenshot 2025-03-09 232159](https://github.com/user-attachments/assets/56e532a7-dcf2-4863-9982-2d6ce66e3c2a)

author_association: MEMBER
user: login: xavier2k6
created_at: 2025-03-11T17:53:31Z
body: Not related to this PR but the status bar doesn't truly reflect the "free space" for different drives.....consistently just shows "free space" of one drive.
author_association: MEMBER
user: login: glassez
created_at: 2025-03-11T18:01:51Z
body: > Not related to this PR but the status bar doesn't truly reflect the "free space" for different drives.....consistently just shows "free space" of one drive.

It shows free space at default save path as it was initially designed.
author_association: MEMBER
user: login: xavier2k6
created_at: 2025-03-11T18:24:57Z
body: >It shows free space at default save path as it was initially designed.

I would've thought/expected it to change to reflect "free space" of each save path when scrolling through transfer list of various drive(s)
author_association: MEMBER
user: login: xavier2k6
created_at: 2025-03-11T18:27:14Z
body: Maybe there should be a "free space" `column` in transfer list instead of in the `status bar`
author_association: MEMBER
user: login: glassez
created_at: 2025-03-11T18:37:20Z
body: @xavier2k6 
The purpose of this PR is simply to port in main UI what is already in the WebUI. If you want something more, then you should create a separate Feature request.
author_association: MEMBER
user: login: glassez
created_at: 2025-03-11T18:41:19Z
body: > Maybe there should be a "free space" `column` in transfer list instead of in the `status bar`

IMO, it looks too "heavy" both from the UI and implementation perspective.
author_association: MEMBER