2 * Bittorrent Client using Qt and libtorrent.
3 * Copyright (C) 2018 Vladimir Golovnev <glassez@yandex.ru>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * In addition, as a special exception, the copyright holders give permission to
20 * link this program with the OpenSSL project's "OpenSSL" library (or with
21 * modified versions of it that use the same license as the "OpenSSL" library),
22 * and distribute the linked executables. You must obey the GNU General Public
23 * License in all respects for all of the code used other than "OpenSSL". If you
24 * modify file(s), you may extend this exception to your version of the file(s),
25 * but you are not obligated to do so. If you do not wish to do so, delete this
26 * exception statement from your version.
29 #include "synccontroller.h"
33 #include <QJsonObject>
34 #include <QMetaObject>
37 #include "base/bittorrent/peeraddress.h"
38 #include "base/bittorrent/peerinfo.h"
39 #include "base/bittorrent/session.h"
40 #include "base/bittorrent/torrenthandle.h"
41 #include "base/global.h"
42 #include "base/net/geoipmanager.h"
43 #include "base/preferences.h"
44 #include "base/utils/string.h"
46 #include "freediskspacechecker.h"
47 #include "isessionmanager.h"
48 #include "serialize/serialize_torrent.h"
52 const int FREEDISKSPACE_CHECK_TIMEOUT
= 30000;
54 // Sync main data keys
55 const char KEY_SYNC_MAINDATA_QUEUEING
[] = "queueing";
56 const char KEY_SYNC_MAINDATA_REFRESH_INTERVAL
[] = "refresh_interval";
57 const char KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS
[] = "use_alt_speed_limits";
59 // Sync torrent peers keys
60 const char KEY_SYNC_TORRENT_PEERS_SHOW_FLAGS
[] = "show_flags";
63 const char KEY_PEER_CLIENT
[] = "client";
64 const char KEY_PEER_CONNECTION_TYPE
[] = "connection";
65 const char KEY_PEER_COUNTRY
[] = "country";
66 const char KEY_PEER_COUNTRY_CODE
[] = "country_code";
67 const char KEY_PEER_DOWN_SPEED
[] = "dl_speed";
68 const char KEY_PEER_FILES
[] = "files";
69 const char KEY_PEER_FLAGS
[] = "flags";
70 const char KEY_PEER_FLAGS_DESCRIPTION
[] = "flags_desc";
71 const char KEY_PEER_IP
[] = "ip";
72 const char KEY_PEER_PORT
[] = "port";
73 const char KEY_PEER_PROGRESS
[] = "progress";
74 const char KEY_PEER_RELEVANCE
[] = "relevance";
75 const char KEY_PEER_TOT_DOWN
[] = "downloaded";
76 const char KEY_PEER_TOT_UP
[] = "uploaded";
77 const char KEY_PEER_UP_SPEED
[] = "up_speed";
80 const char KEY_TRANSFER_CONNECTION_STATUS
[] = "connection_status";
81 const char KEY_TRANSFER_DHT_NODES
[] = "dht_nodes";
82 const char KEY_TRANSFER_DLDATA
[] = "dl_info_data";
83 const char KEY_TRANSFER_DLRATELIMIT
[] = "dl_rate_limit";
84 const char KEY_TRANSFER_DLSPEED
[] = "dl_info_speed";
85 const char KEY_TRANSFER_FREESPACEONDISK
[] = "free_space_on_disk";
86 const char KEY_TRANSFER_UPDATA
[] = "up_info_data";
87 const char KEY_TRANSFER_UPRATELIMIT
[] = "up_rate_limit";
88 const char KEY_TRANSFER_UPSPEED
[] = "up_info_speed";
91 const char KEY_TRANSFER_ALLTIME_DL
[] = "alltime_dl";
92 const char KEY_TRANSFER_ALLTIME_UL
[] = "alltime_ul";
93 const char KEY_TRANSFER_AVERAGE_TIME_QUEUE
[] = "average_time_queue";
94 const char KEY_TRANSFER_GLOBAL_RATIO
[] = "global_ratio";
95 const char KEY_TRANSFER_QUEUED_IO_JOBS
[] = "queued_io_jobs";
96 const char KEY_TRANSFER_READ_CACHE_HITS
[] = "read_cache_hits";
97 const char KEY_TRANSFER_READ_CACHE_OVERLOAD
[] = "read_cache_overload";
98 const char KEY_TRANSFER_TOTAL_BUFFERS_SIZE
[] = "total_buffers_size";
99 const char KEY_TRANSFER_TOTAL_PEER_CONNECTIONS
[] = "total_peer_connections";
100 const char KEY_TRANSFER_TOTAL_QUEUED_SIZE
[] = "total_queued_size";
101 const char KEY_TRANSFER_TOTAL_WASTE_SESSION
[] = "total_wasted_session";
102 const char KEY_TRANSFER_WRITE_CACHE_OVERLOAD
[] = "write_cache_overload";
104 const char KEY_FULL_UPDATE
[] = "full_update";
105 const char KEY_RESPONSE_ID
[] = "rid";
106 const char KEY_SUFFIX_REMOVED
[] = "_removed";
108 void processMap(const QVariantMap
&prevData
, const QVariantMap
&data
, QVariantMap
&syncData
);
109 void processHash(QVariantHash prevData
, const QVariantHash
&data
, QVariantMap
&syncData
, QVariantList
&removedItems
);
110 void processList(QVariantList prevData
, const QVariantList
&data
, QVariantList
&syncData
, QVariantList
&removedItems
);
111 QVariantMap
generateSyncData(int acceptedResponseId
, const QVariantMap
&data
, QVariantMap
&lastAcceptedData
, QVariantMap
&lastData
);
113 QVariantMap
getTranserInfo()
116 const auto *session
= BitTorrent::Session::instance();
118 const BitTorrent::SessionStatus
&sessionStatus
= session
->status();
119 const BitTorrent::CacheStatus
&cacheStatus
= session
->cacheStatus();
120 map
[KEY_TRANSFER_DLSPEED
] = sessionStatus
.payloadDownloadRate
;
121 map
[KEY_TRANSFER_DLDATA
] = sessionStatus
.totalPayloadDownload
;
122 map
[KEY_TRANSFER_UPSPEED
] = sessionStatus
.payloadUploadRate
;
123 map
[KEY_TRANSFER_UPDATA
] = sessionStatus
.totalPayloadUpload
;
124 map
[KEY_TRANSFER_DLRATELIMIT
] = session
->downloadSpeedLimit();
125 map
[KEY_TRANSFER_UPRATELIMIT
] = session
->uploadSpeedLimit();
127 const quint64 atd
= session
->getAlltimeDL();
128 const quint64 atu
= session
->getAlltimeUL();
129 map
[KEY_TRANSFER_ALLTIME_DL
] = atd
;
130 map
[KEY_TRANSFER_ALLTIME_UL
] = atu
;
131 map
[KEY_TRANSFER_TOTAL_WASTE_SESSION
] = sessionStatus
.totalWasted
;
132 map
[KEY_TRANSFER_GLOBAL_RATIO
] = ((atd
> 0) && (atu
> 0)) ? Utils::String::fromDouble(static_cast<qreal
>(atu
) / atd
, 2) : "-";
133 map
[KEY_TRANSFER_TOTAL_PEER_CONNECTIONS
] = sessionStatus
.peersCount
;
135 const qreal readRatio
= cacheStatus
.readRatio
;
136 map
[KEY_TRANSFER_READ_CACHE_HITS
] = (readRatio
> 0) ? Utils::String::fromDouble(100 * readRatio
, 2) : "0";
137 map
[KEY_TRANSFER_TOTAL_BUFFERS_SIZE
] = cacheStatus
.totalUsedBuffers
* 16 * 1024;
139 // num_peers is not reliable (adds up peers, which didn't even overcome tcp handshake)
140 const auto torrents
= session
->torrents();
141 const quint32 peers
= std::accumulate(torrents
.cbegin(), torrents
.cend(), 0, [](const quint32 acc
, const BitTorrent::TorrentHandle
*torrent
)
143 return (acc
+ torrent
->peersCount());
146 map
[KEY_TRANSFER_WRITE_CACHE_OVERLOAD
] = ((sessionStatus
.diskWriteQueue
> 0) && (peers
> 0)) ? Utils::String::fromDouble((100. * sessionStatus
.diskWriteQueue
) / peers
, 2) : "0";
147 map
[KEY_TRANSFER_READ_CACHE_OVERLOAD
] = ((sessionStatus
.diskReadQueue
> 0) && (peers
> 0)) ? Utils::String::fromDouble((100. * sessionStatus
.diskReadQueue
) / peers
, 2) : "0";
149 map
[KEY_TRANSFER_QUEUED_IO_JOBS
] = cacheStatus
.jobQueueLength
;
150 map
[KEY_TRANSFER_AVERAGE_TIME_QUEUE
] = cacheStatus
.averageJobTime
;
151 map
[KEY_TRANSFER_TOTAL_QUEUED_SIZE
] = cacheStatus
.queuedBytes
;
153 map
[KEY_TRANSFER_DHT_NODES
] = sessionStatus
.dhtNodes
;
154 map
[KEY_TRANSFER_CONNECTION_STATUS
] = session
->isListening()
155 ? (sessionStatus
.hasIncomingConnections
? "connected" : "firewalled")
161 // Compare two structures (prevData, data) and calculate difference (syncData).
162 // Structures encoded as map.
163 void processMap(const QVariantMap
&prevData
, const QVariantMap
&data
, QVariantMap
&syncData
)
165 // initialize output variable
168 for (auto i
= data
.cbegin(); i
!= data
.cend(); ++i
) {
169 const QString
&key
= i
.key();
170 const QVariant
&value
= i
.value();
171 QVariantList removedItems
;
173 switch (static_cast<QMetaType::Type
>(value
.type())) {
174 case QMetaType::QVariantMap
: {
176 processMap(prevData
[key
].toMap(), value
.toMap(), map
);
181 case QMetaType::QVariantHash
: {
183 processHash(prevData
[key
].toHash(), value
.toHash(), map
, removedItems
);
186 if (!removedItems
.isEmpty())
187 syncData
[key
+ KEY_SUFFIX_REMOVED
] = removedItems
;
190 case QMetaType::QVariantList
: {
192 processList(prevData
[key
].toList(), value
.toList(), list
, removedItems
);
194 syncData
[key
] = list
;
195 if (!removedItems
.isEmpty())
196 syncData
[key
+ KEY_SUFFIX_REMOVED
] = removedItems
;
199 case QMetaType::QString
:
200 case QMetaType::LongLong
:
201 case QMetaType::Float
:
203 case QMetaType::Bool
:
204 case QMetaType::Double
:
205 case QMetaType::ULongLong
:
206 case QMetaType::UInt
:
207 case QMetaType::QDateTime
:
208 if (prevData
[key
] != value
)
209 syncData
[key
] = value
;
212 Q_ASSERT_X(false, "processMap"
213 , QString("Unexpected type: %1")
214 .arg(QMetaType::typeName(static_cast<QMetaType::Type
>(value
.type())))
215 .toUtf8().constData());
220 // Compare two lists of structures (prevData, data) and calculate difference (syncData, removedItems).
221 // Structures encoded as map.
222 // Lists are encoded as hash table (indexed by structure key value) to improve ease of searching for removed items.
223 void processHash(QVariantHash prevData
, const QVariantHash
&data
, QVariantMap
&syncData
, QVariantList
&removedItems
)
225 // initialize output variables
227 removedItems
.clear();
229 if (prevData
.isEmpty()) {
230 // If list was empty before, then difference is a whole new list.
231 for (auto i
= data
.cbegin(); i
!= data
.cend(); ++i
)
232 syncData
[i
.key()] = i
.value();
235 for (auto i
= data
.cbegin(); i
!= data
.cend(); ++i
) {
236 switch (i
.value().type()) {
238 if (!prevData
.contains(i
.key())) {
239 // new list item found - append it to syncData
240 syncData
[i
.key()] = i
.value();
244 processMap(prevData
[i
.key()].toMap(), i
.value().toMap(), map
);
245 // existing list item found - remove it from prevData
246 prevData
.remove(i
.key());
248 // changed list item found - append its changes to syncData
249 syncData
[i
.key()] = map
;
257 if (!prevData
.isEmpty()) {
258 // prevData contains only items that are missing now -
259 // put them in removedItems
260 for (auto i
= prevData
.cbegin(); i
!= prevData
.cend(); ++i
)
261 removedItems
<< i
.key();
266 // Compare two lists of simple value (prevData, data) and calculate difference (syncData, removedItems).
267 void processList(QVariantList prevData
, const QVariantList
&data
, QVariantList
&syncData
, QVariantList
&removedItems
)
269 // initialize output variables
271 removedItems
.clear();
273 if (prevData
.isEmpty()) {
274 // If list was empty before, then difference is a whole new list.
278 for (const QVariant
&item
: data
) {
279 if (!prevData
.contains(item
))
280 // new list item found - append it to syncData
281 syncData
.append(item
);
283 // unchanged list item found - remove it from prevData
284 prevData
.removeOne(item
);
287 if (!prevData
.isEmpty())
288 // prevData contains only items that are missing now -
289 // put them in removedItems
290 removedItems
= prevData
;
294 QVariantMap
generateSyncData(int acceptedResponseId
, const QVariantMap
&data
, QVariantMap
&lastAcceptedData
, QVariantMap
&lastData
)
296 QVariantMap syncData
;
297 bool fullUpdate
= true;
298 int lastResponseId
= 0;
299 if (acceptedResponseId
> 0) {
300 lastResponseId
= lastData
[KEY_RESPONSE_ID
].toInt();
302 if (lastResponseId
== acceptedResponseId
)
303 lastAcceptedData
= lastData
;
305 int lastAcceptedResponseId
= lastAcceptedData
[KEY_RESPONSE_ID
].toInt();
307 if (lastAcceptedResponseId
== acceptedResponseId
) {
308 processMap(lastAcceptedData
, data
, syncData
);
314 lastAcceptedData
.clear();
316 syncData
[KEY_FULL_UPDATE
] = true;
319 lastResponseId
= (lastResponseId
% 1000000) + 1; // cycle between 1 and 1000000
321 lastData
[KEY_RESPONSE_ID
] = lastResponseId
;
322 syncData
[KEY_RESPONSE_ID
] = lastResponseId
;
328 SyncController::SyncController(ISessionManager
*sessionManager
, QObject
*parent
)
329 : APIController(sessionManager
, parent
)
331 m_freeDiskSpaceThread
= new QThread(this);
332 m_freeDiskSpaceChecker
= new FreeDiskSpaceChecker();
333 m_freeDiskSpaceChecker
->moveToThread(m_freeDiskSpaceThread
);
335 connect(m_freeDiskSpaceThread
, &QThread::finished
, m_freeDiskSpaceChecker
, &QObject::deleteLater
);
336 connect(m_freeDiskSpaceChecker
, &FreeDiskSpaceChecker::checked
, this, &SyncController::freeDiskSpaceSizeUpdated
);
338 m_freeDiskSpaceThread
->start();
340 m_freeDiskSpaceElapsedTimer
.start();
343 SyncController::~SyncController()
345 m_freeDiskSpaceThread
->quit();
346 m_freeDiskSpaceThread
->wait();
349 // The function returns the changed data from the server to synchronize with the web client.
350 // Return value is map in JSON format.
351 // Map contain the key:
352 // - "Rid": ID response
353 // Map can contain the keys:
354 // - "full_update": full data update flag
355 // - "torrents": dictionary contains information about torrents.
356 // - "torrents_removed": a list of hashes of removed torrents
357 // - "categories": map of categories info
358 // - "categories_removed": list of removed categories
359 // - "server_state": map contains information about the state of the server
360 // The keys of the 'torrents' dictionary are hashes of torrents.
361 // Each value of the 'torrents' dictionary contains map. The map can contain following keys:
362 // - "name": Torrent name
363 // - "size": Torrent size
364 // - "progress": Torrent progress
365 // - "dlspeed": Torrent download speed
366 // - "upspeed": Torrent upload speed
367 // - "priority": Torrent queue position (-1 if queuing is disabled)
368 // - "num_seeds": Torrent seeds connected to
369 // - "num_complete": Torrent seeds in the swarm
370 // - "num_leechs": Torrent leechers connected to
371 // - "num_incomplete": Torrent leechers in the swarm
372 // - "ratio": Torrent share ratio
373 // - "eta": Torrent ETA
374 // - "state": Torrent state
375 // - "seq_dl": Torrent sequential download state
376 // - "f_l_piece_prio": Torrent first last piece priority state
377 // - "completion_on": Torrent copletion time
378 // - "tracker": Torrent tracker
379 // - "dl_limit": Torrent download limit
380 // - "up_limit": Torrent upload limit
381 // - "downloaded": Amount of data downloaded
382 // - "uploaded": Amount of data uploaded
383 // - "downloaded_session": Amount of data downloaded since program open
384 // - "uploaded_session": Amount of data uploaded since program open
385 // - "amount_left": Amount of data left to download
386 // - "save_path": Torrent save path
387 // - "completed": Amount of data completed
388 // - "max_ratio": Upload max share ratio
389 // - "max_seeding_time": Upload max seeding time
390 // - "ratio_limit": Upload share ratio limit
391 // - "seeding_time_limit": Upload seeding time limit
392 // - "seen_complete": Indicates the time when the torrent was last seen complete/whole
393 // - "last_activity": Last time when a chunk was downloaded/uploaded
394 // - "total_size": Size including unwanted data
395 // Server state map may contain the following keys:
396 // - "connection_status": connection status
397 // - "dht_nodes": DHT nodes count
398 // - "dl_info_data": bytes downloaded
399 // - "dl_info_speed": download speed
400 // - "dl_rate_limit: download rate limit
401 // - "up_info_data: bytes uploaded
402 // - "up_info_speed: upload speed
403 // - "up_rate_limit: upload speed limit
404 // - "queueing": queue system usage flag
405 // - "refresh_interval": torrents table refresh interval
406 // - "free_space_on_disk": Free space on the default save path
408 // - rid (int): last response id
409 void SyncController::maindataAction()
411 const auto *session
= BitTorrent::Session::instance();
415 QVariantMap lastResponse
= sessionManager()->session()->getData(QLatin1String("syncMainDataLastResponse")).toMap();
416 QVariantMap lastAcceptedResponse
= sessionManager()->session()->getData(QLatin1String("syncMainDataLastAcceptedResponse")).toMap();
418 QVariantHash torrents
;
419 for (const BitTorrent::TorrentHandle
*torrent
: asConst(session
->torrents())) {
420 const BitTorrent::InfoHash torrentHash
= torrent
->hash();
422 QVariantMap map
= serialize(*torrent
);
423 map
.remove(KEY_TORRENT_HASH
);
425 // Calculated last activity time can differ from actual value by up to 10 seconds (this is a libtorrent issue).
426 // So we don't need unnecessary updates of last activity time in response.
427 const auto iterTorrents
= lastResponse
.find("torrents");
428 if (iterTorrents
!= lastResponse
.end()) {
429 const QVariantHash lastResponseTorrents
= iterTorrents
->toHash();
430 const auto iterHash
= lastResponseTorrents
.find(torrentHash
);
432 if (iterHash
!= lastResponseTorrents
.end()) {
433 const QVariantMap torrentData
= iterHash
->toMap();
434 const auto iterLastActivity
= torrentData
.find(KEY_TORRENT_LAST_ACTIVITY_TIME
);
436 if (iterLastActivity
!= torrentData
.end()) {
437 const int lastValue
= iterLastActivity
->toInt();
438 if (qAbs(lastValue
- map
[KEY_TORRENT_LAST_ACTIVITY_TIME
].toInt()) < 15)
439 map
[KEY_TORRENT_LAST_ACTIVITY_TIME
] = lastValue
;
444 torrents
[torrentHash
] = map
;
446 data
["torrents"] = torrents
;
448 QVariantHash categories
;
449 const auto &categoriesList
= session
->categories();
450 for (auto it
= categoriesList
.cbegin(); it
!= categoriesList
.cend(); ++it
) {
451 const QString
&key
= it
.key();
452 categories
[key
] = QVariantMap
{
454 {"savePath", it
.value()}
457 data
["categories"] = categories
;
460 for (const QString
&tag
: asConst(session
->tags()))
464 QVariantMap serverState
= getTranserInfo();
465 serverState
[KEY_TRANSFER_FREESPACEONDISK
] = getFreeDiskSpace();
466 serverState
[KEY_SYNC_MAINDATA_QUEUEING
] = session
->isQueueingSystemEnabled();
467 serverState
[KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS
] = session
->isAltGlobalSpeedLimitEnabled();
468 serverState
[KEY_SYNC_MAINDATA_REFRESH_INTERVAL
] = session
->refreshInterval();
469 data
["server_state"] = serverState
;
471 const int acceptedResponseId
{params()["rid"].toInt()};
472 setResult(QJsonObject::fromVariantMap(generateSyncData(acceptedResponseId
, data
, lastAcceptedResponse
, lastResponse
)));
474 sessionManager()->session()->setData(QLatin1String("syncMainDataLastResponse"), lastResponse
);
475 sessionManager()->session()->setData(QLatin1String("syncMainDataLastAcceptedResponse"), lastAcceptedResponse
);
479 // - hash (string): torrent hash
480 // - rid (int): last response id
481 void SyncController::torrentPeersAction()
483 auto lastResponse
= sessionManager()->session()->getData(QLatin1String("syncTorrentPeersLastResponse")).toMap();
484 auto lastAcceptedResponse
= sessionManager()->session()->getData(QLatin1String("syncTorrentPeersLastAcceptedResponse")).toMap();
486 const QString hash
{params()["hash"]};
487 const BitTorrent::TorrentHandle
*torrent
= BitTorrent::Session::instance()->findTorrent(hash
);
489 throw APIError(APIErrorType::NotFound
);
494 const QVector
<BitTorrent::PeerInfo
> peersList
= torrent
->peers();
496 #ifndef DISABLE_COUNTRIES_RESOLUTION
497 bool resolvePeerCountries
= Preferences::instance()->resolvePeerCountries();
499 bool resolvePeerCountries
= false;
502 data
[KEY_SYNC_TORRENT_PEERS_SHOW_FLAGS
] = resolvePeerCountries
;
504 for (const BitTorrent::PeerInfo
&pi
: peersList
) {
505 if (pi
.address().ip
.isNull()) continue;
508 {KEY_PEER_IP
, pi
.address().ip
.toString()},
509 {KEY_PEER_PORT
, pi
.address().port
},
510 {KEY_PEER_CLIENT
, pi
.client()},
511 {KEY_PEER_PROGRESS
, pi
.progress()},
512 {KEY_PEER_DOWN_SPEED
, pi
.payloadDownSpeed()},
513 {KEY_PEER_UP_SPEED
, pi
.payloadUpSpeed()},
514 {KEY_PEER_TOT_DOWN
, pi
.totalDownload()},
515 {KEY_PEER_TOT_UP
, pi
.totalUpload()},
516 {KEY_PEER_CONNECTION_TYPE
, pi
.connectionType()},
517 {KEY_PEER_FLAGS
, pi
.flags()},
518 {KEY_PEER_FLAGS_DESCRIPTION
, pi
.flagsDescription()},
519 {KEY_PEER_RELEVANCE
, pi
.relevance()},
520 {KEY_PEER_FILES
, torrent
->info().filesForPiece(pi
.downloadingPieceIndex()).join('\n')}
523 #ifndef DISABLE_COUNTRIES_RESOLUTION
524 if (resolvePeerCountries
) {
525 peer
[KEY_PEER_COUNTRY_CODE
] = pi
.country().toLower();
526 peer
[KEY_PEER_COUNTRY
] = Net::GeoIPManager::CountryName(pi
.country());
530 peers
[pi
.address().ip
.toString() + ':' + QString::number(pi
.address().port
)] = peer
;
532 data
["peers"] = peers
;
534 const int acceptedResponseId
{params()["rid"].toInt()};
535 setResult(QJsonObject::fromVariantMap(generateSyncData(acceptedResponseId
, data
, lastAcceptedResponse
, lastResponse
)));
537 sessionManager()->session()->setData(QLatin1String("syncTorrentPeersLastResponse"), lastResponse
);
538 sessionManager()->session()->setData(QLatin1String("syncTorrentPeersLastAcceptedResponse"), lastAcceptedResponse
);
541 qint64
SyncController::getFreeDiskSpace()
543 if (m_freeDiskSpaceElapsedTimer
.hasExpired(FREEDISKSPACE_CHECK_TIMEOUT
)) {
545 m_freeDiskSpaceElapsedTimer
.restart();
548 return m_freeDiskSpace
;
551 void SyncController::freeDiskSpaceSizeUpdated(qint64 freeSpaceSize
)
553 m_freeDiskSpace
= freeSpaceSize
;
556 void SyncController::invokeChecker() const
558 #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
559 QMetaObject::invokeMethod(m_freeDiskSpaceChecker
, &FreeDiskSpaceChecker::check
, Qt::QueuedConnection
);
561 QMetaObject::invokeMethod(m_freeDiskSpaceChecker
, "check", Qt::QueuedConnection
);