Request device/directory completion
This commit is contained in:
parent
4aa6d4457b
commit
447928a018
|
@ -10,7 +10,7 @@ set(META_APP_CATEGORIES "System;Utility;Network;FileTransfer")
|
||||||
set(META_GUI_OPTIONAL false)
|
set(META_GUI_OPTIONAL false)
|
||||||
set(META_VERSION_MAJOR 0)
|
set(META_VERSION_MAJOR 0)
|
||||||
set(META_VERSION_MINOR 7)
|
set(META_VERSION_MINOR 7)
|
||||||
set(META_VERSION_PATCH 2)
|
set(META_VERSION_PATCH 3)
|
||||||
set(META_VERSION_EXACT_SONAME ON)
|
set(META_VERSION_EXACT_SONAME ON)
|
||||||
|
|
||||||
project(${META_PROJECT_NAME})
|
project(${META_PROJECT_NAME})
|
||||||
|
|
|
@ -56,6 +56,7 @@ SyncthingConnection::SyncthingConnection(const QString &syncthingUrl, const QByt
|
||||||
, m_status(SyncthingStatus::Disconnected)
|
, m_status(SyncthingStatus::Disconnected)
|
||||||
, m_keepPolling(false)
|
, m_keepPolling(false)
|
||||||
, m_reconnecting(false)
|
, m_reconnecting(false)
|
||||||
|
, m_requestCompletion(true)
|
||||||
, m_lastEventId(0)
|
, m_lastEventId(0)
|
||||||
, m_autoReconnectTries(0)
|
, m_autoReconnectTries(0)
|
||||||
, m_totalIncomingTraffic(unknownTraffic)
|
, m_totalIncomingTraffic(unknownTraffic)
|
||||||
|
@ -671,6 +672,11 @@ void SyncthingConnection::continueConnecting()
|
||||||
requestErrors();
|
requestErrors();
|
||||||
for (const SyncthingDir &dir : m_dirs) {
|
for (const SyncthingDir &dir : m_dirs) {
|
||||||
requestDirStatus(dir.id);
|
requestDirStatus(dir.id);
|
||||||
|
if (m_requestCompletion) {
|
||||||
|
for (const QString &devId : dir.deviceIds) {
|
||||||
|
requestCompletion(devId, dir.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// since config and status could be read successfully, let's poll for events
|
// since config and status could be read successfully, let's poll for events
|
||||||
m_lastEventId = 0;
|
m_lastEventId = 0;
|
||||||
|
@ -771,11 +777,25 @@ void SyncthingConnection::requestDirStatus(const QString &dirId)
|
||||||
{
|
{
|
||||||
QUrlQuery query;
|
QUrlQuery query;
|
||||||
query.addQueryItem(QStringLiteral("folder"), dirId);
|
query.addQueryItem(QStringLiteral("folder"), dirId);
|
||||||
QNetworkReply *reply = requestData(QStringLiteral("db/status"), query);
|
auto *const reply = requestData(QStringLiteral("db/status"), query);
|
||||||
reply->setProperty("dirId", dirId);
|
reply->setProperty("dirId", dirId);
|
||||||
QObject::connect(reply, &QNetworkReply::finished, this, &SyncthingConnection::readDirStatus);
|
QObject::connect(reply, &QNetworkReply::finished, this, &SyncthingConnection::readDirStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Requests completion for \a devId and \a dirId asynchronously.
|
||||||
|
*/
|
||||||
|
void SyncthingConnection::requestCompletion(const QString &devId, const QString &dirId)
|
||||||
|
{
|
||||||
|
QUrlQuery query;
|
||||||
|
query.addQueryItem(QStringLiteral("device"), devId);
|
||||||
|
query.addQueryItem(QStringLiteral("folder"), dirId);
|
||||||
|
auto *const reply = requestData(QStringLiteral("db/completion"), query);
|
||||||
|
reply->setProperty("devId", devId);
|
||||||
|
reply->setProperty("dirId", dirId);
|
||||||
|
QObject::connect(reply, &QNetworkReply::finished, this, &SyncthingConnection::readCompletion);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Requests device statistics asynchronously.
|
* \brief Requests device statistics asynchronously.
|
||||||
*/
|
*/
|
||||||
|
@ -1436,6 +1456,8 @@ void SyncthingConnection::readEvents()
|
||||||
readItemStarted(eventTime, eventData);
|
readItemStarted(eventTime, eventData);
|
||||||
} else if (eventType == QLatin1String("ItemFinished")) {
|
} else if (eventType == QLatin1String("ItemFinished")) {
|
||||||
readItemFinished(eventTime, eventData);
|
readItemFinished(eventTime, eventData);
|
||||||
|
} else if (eventType == QLatin1String("RemoteIndexUpdated")) {
|
||||||
|
readRemoteIndexUpdated(eventTime, eventData);
|
||||||
} else if (eventType == QLatin1String("ConfigSaved")) {
|
} else if (eventType == QLatin1String("ConfigSaved")) {
|
||||||
requestConfig(); // just consider current config as invalidated
|
requestConfig(); // just consider current config as invalidated
|
||||||
}
|
}
|
||||||
|
@ -1700,7 +1722,7 @@ void SyncthingConnection::readItemStarted(DateTime eventTime, const QJsonObject
|
||||||
*/
|
*/
|
||||||
void SyncthingConnection::readItemFinished(DateTime eventTime, const QJsonObject &eventData)
|
void SyncthingConnection::readItemFinished(DateTime eventTime, const QJsonObject &eventData)
|
||||||
{
|
{
|
||||||
const QString dir(eventData.value(QStringLiteral("folder")).toString());
|
const auto dir(eventData.value(QLatin1String("folder")).toString());
|
||||||
if (dir.isEmpty()) {
|
if (dir.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1728,6 +1750,40 @@ void SyncthingConnection::readItemFinished(DateTime eventTime, const QJsonObject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Reads results of requestEvents().
|
||||||
|
*/
|
||||||
|
void SyncthingConnection::readRemoteIndexUpdated(DateTime eventTime, const QJsonObject &eventData)
|
||||||
|
{
|
||||||
|
// ignore those events if we're not updating completion automatically
|
||||||
|
if (!m_requestCompletion) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find dev/dir
|
||||||
|
const auto devId(eventData.value(QLatin1String("device")).toString());
|
||||||
|
const auto dirId(eventData.value(QLatin1String("folder")).toString());
|
||||||
|
if (dirId.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int index;
|
||||||
|
auto *const dirInfo = findDirInfo(dirId, index);
|
||||||
|
if (!dirInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignore event if we don't share the directory with the device
|
||||||
|
if (!dirInfo->deviceIds.contains(devId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// request completion again if out-of-date
|
||||||
|
const auto &completion = dirInfo->completionByDevice[devId];
|
||||||
|
if (completion.lastUpdate < eventTime) {
|
||||||
|
requestCompletion(devId, dirId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Reads results of rescan().
|
* \brief Reads results of rescan().
|
||||||
*/
|
*/
|
||||||
|
@ -1896,6 +1952,52 @@ bool SyncthingConnection::readDirSummary(DateTime eventTime, const QJsonObject &
|
||||||
return stateChanged;
|
return stateChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Reads data from requestCompletion().
|
||||||
|
*/
|
||||||
|
void SyncthingConnection::readCompletion()
|
||||||
|
{
|
||||||
|
auto *const reply = static_cast<QNetworkReply *>(sender());
|
||||||
|
const auto devId(reply->property("devId").toString());
|
||||||
|
const auto dirId(reply->property("dirId").toString());
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
switch (reply->error()) {
|
||||||
|
case QNetworkReply::NoError: {
|
||||||
|
// determine relevant dev/dir
|
||||||
|
int index;
|
||||||
|
SyncthingDir *const dir = findDirInfo(dirId, index);
|
||||||
|
// discard status for unknown dirs
|
||||||
|
if (!dir) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse JSON
|
||||||
|
QJsonParseError jsonError;
|
||||||
|
const auto response(reply->readAll());
|
||||||
|
const auto replyDoc(QJsonDocument::fromJson(response, &jsonError));
|
||||||
|
if (jsonError.error != QJsonParseError::NoError) {
|
||||||
|
emitError(tr("Unable to parse completion for device/directory %1/%2: ").arg(devId, dirId), jsonError, reply, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// update the relevant completion info
|
||||||
|
const auto replyObj(replyDoc.object());
|
||||||
|
auto &completion = dir->completionByDevice[devId];
|
||||||
|
completion.lastUpdate = DateTime::gmtNow();
|
||||||
|
completion.percentage = replyObj.value(QLatin1String("completion")).toInt();
|
||||||
|
completion.globalBytes = toUInt64(replyObj.value(QLatin1String("globalBytes")));
|
||||||
|
completion.neededBytes = toUInt64(replyObj.value(QLatin1String("needBytes")));
|
||||||
|
completion.neededItems = toUInt64(replyObj.value(QLatin1String("needItems")));
|
||||||
|
completion.neededDeletes = toUInt64(replyObj.value(QLatin1String("needDeletes")));
|
||||||
|
emit dirStatusChanged(*dir, index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
emitError(tr("Unable to request completion for device/directory %1/%2: ").arg(devId, dirId), SyncthingErrorCategory::SpecificRequest, reply);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Sets the connection status. Ensures statusChanged() is emitted.
|
* \brief Sets the connection status. Ensures statusChanged() is emitted.
|
||||||
* \param status Specifies the status; should be either SyncthingStatus::Disconnected, SyncthingStatus::Reconnecting, or
|
* \param status Specifies the status; should be either SyncthingStatus::Disconnected, SyncthingStatus::Reconnecting, or
|
||||||
|
|
|
@ -69,6 +69,7 @@ class LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingConnection : public QObject {
|
||||||
Q_PROPERTY(bool connected READ isConnected NOTIFY statusChanged)
|
Q_PROPERTY(bool connected READ isConnected NOTIFY statusChanged)
|
||||||
Q_PROPERTY(bool hasUnreadNotifications READ hasUnreadNotifications)
|
Q_PROPERTY(bool hasUnreadNotifications READ hasUnreadNotifications)
|
||||||
Q_PROPERTY(bool hasOutOfSyncDirs READ hasOutOfSyncDirs)
|
Q_PROPERTY(bool hasOutOfSyncDirs READ hasOutOfSyncDirs)
|
||||||
|
Q_PROPERTY(bool requestingCompletionEnabled READ isRequestingCompletionEnabled WRITE setRequestingCompletionEnabled)
|
||||||
Q_PROPERTY(int trafficPollInterval READ trafficPollInterval WRITE setTrafficPollInterval)
|
Q_PROPERTY(int trafficPollInterval READ trafficPollInterval WRITE setTrafficPollInterval)
|
||||||
Q_PROPERTY(int devStatsPollInterval READ devStatsPollInterval WRITE setDevStatsPollInterval)
|
Q_PROPERTY(int devStatsPollInterval READ devStatsPollInterval WRITE setDevStatsPollInterval)
|
||||||
Q_PROPERTY(QString configDir READ configDir NOTIFY configDirChanged)
|
Q_PROPERTY(QString configDir READ configDir NOTIFY configDirChanged)
|
||||||
|
@ -97,6 +98,8 @@ public:
|
||||||
bool isConnected() const;
|
bool isConnected() const;
|
||||||
bool hasUnreadNotifications() const;
|
bool hasUnreadNotifications() const;
|
||||||
bool hasOutOfSyncDirs() const;
|
bool hasOutOfSyncDirs() const;
|
||||||
|
bool isRequestingCompletionEnabled() const;
|
||||||
|
void setRequestingCompletionEnabled(bool requestingCompletionEnabled);
|
||||||
int trafficPollInterval() const;
|
int trafficPollInterval() const;
|
||||||
void setTrafficPollInterval(int trafficPollInterval);
|
void setTrafficPollInterval(int trafficPollInterval);
|
||||||
int devStatsPollInterval() const;
|
int devStatsPollInterval() const;
|
||||||
|
@ -180,6 +183,7 @@ private Q_SLOTS:
|
||||||
void requestClearingErrors();
|
void requestClearingErrors();
|
||||||
void requestDirStatistics();
|
void requestDirStatistics();
|
||||||
void requestDirStatus(const QString &dirId);
|
void requestDirStatus(const QString &dirId);
|
||||||
|
void requestCompletion(const QString &devId, const QString &dirId);
|
||||||
|
|
||||||
void requestDeviceStatistics();
|
void requestDeviceStatistics();
|
||||||
void requestEvents();
|
void requestEvents();
|
||||||
|
@ -203,6 +207,7 @@ private Q_SLOTS:
|
||||||
void readDeviceEvent(ChronoUtilities::DateTime eventTime, const QString &eventType, const QJsonObject &eventData);
|
void readDeviceEvent(ChronoUtilities::DateTime eventTime, const QString &eventType, const QJsonObject &eventData);
|
||||||
void readItemStarted(ChronoUtilities::DateTime eventTime, const QJsonObject &eventData);
|
void readItemStarted(ChronoUtilities::DateTime eventTime, const QJsonObject &eventData);
|
||||||
void readItemFinished(ChronoUtilities::DateTime eventTime, const QJsonObject &eventData);
|
void readItemFinished(ChronoUtilities::DateTime eventTime, const QJsonObject &eventData);
|
||||||
|
void readRemoteIndexUpdated(ChronoUtilities::DateTime eventTime, const QJsonObject &eventData);
|
||||||
void readRescan();
|
void readRescan();
|
||||||
void readDevPauseResume();
|
void readDevPauseResume();
|
||||||
void readDirPauseResume();
|
void readDirPauseResume();
|
||||||
|
@ -210,6 +215,7 @@ private Q_SLOTS:
|
||||||
void readShutdown();
|
void readShutdown();
|
||||||
void readDirStatus();
|
void readDirStatus();
|
||||||
bool readDirSummary(ChronoUtilities::DateTime eventTime, const QJsonObject &summary, SyncthingDir &dirInfo, int index);
|
bool readDirSummary(ChronoUtilities::DateTime eventTime, const QJsonObject &summary, SyncthingDir &dirInfo, int index);
|
||||||
|
void readCompletion();
|
||||||
|
|
||||||
void continueConnecting();
|
void continueConnecting();
|
||||||
void continueReconnecting();
|
void continueReconnecting();
|
||||||
|
@ -238,6 +244,7 @@ private:
|
||||||
SyncthingStatus m_status;
|
SyncthingStatus m_status;
|
||||||
bool m_keepPolling;
|
bool m_keepPolling;
|
||||||
bool m_reconnecting;
|
bool m_reconnecting;
|
||||||
|
bool m_requestCompletion;
|
||||||
int m_lastEventId;
|
int m_lastEventId;
|
||||||
QTimer m_trafficPollTimer;
|
QTimer m_trafficPollTimer;
|
||||||
QTimer m_devStatsPollTimer;
|
QTimer m_devStatsPollTimer;
|
||||||
|
@ -352,6 +359,24 @@ inline bool SyncthingConnection::hasUnreadNotifications() const
|
||||||
return m_unreadNotifications;
|
return m_unreadNotifications;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Returns whether completion for all directories of all devices should be requested automatically.
|
||||||
|
* \remarks Completion can be requested manually using requestCompletion().
|
||||||
|
*/
|
||||||
|
inline bool SyncthingConnection::isRequestingCompletionEnabled() const
|
||||||
|
{
|
||||||
|
return m_requestCompletion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Sets whether completion for all directories of all devices should be requested automatically.
|
||||||
|
* \remarks Completion can be requested manually using requestCompletion().
|
||||||
|
*/
|
||||||
|
inline void SyncthingConnection::setRequestingCompletionEnabled(bool requestingCompletionEnabled)
|
||||||
|
{
|
||||||
|
m_requestCompletion = requestingCompletionEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief Considers all notifications as read; hence might trigger a status update.
|
* \brief Considers all notifications as read; hence might trigger a status update.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -51,6 +51,15 @@ struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingItemDownloadProgress {
|
||||||
static constexpr unsigned int syncthingBlockSize = 128 * 1024;
|
static constexpr unsigned int syncthingBlockSize = 128 * 1024;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingCompletion {
|
||||||
|
ChronoUtilities::DateTime lastUpdate;
|
||||||
|
quint64 globalBytes = 0;
|
||||||
|
quint64 neededBytes = 0;
|
||||||
|
quint64 neededItems = 0;
|
||||||
|
quint64 neededDeletes = 0;
|
||||||
|
int percentage = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingDir {
|
struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingDir {
|
||||||
SyncthingDir(const QString &id = QString(), const QString &label = QString(), const QString &path = QString());
|
SyncthingDir(const QString &id = QString(), const QString &label = QString(), const QString &path = QString());
|
||||||
bool assignStatus(const QString &statusStr, ChronoUtilities::DateTime time);
|
bool assignStatus(const QString &statusStr, ChronoUtilities::DateTime time);
|
||||||
|
@ -75,6 +84,7 @@ struct LIB_SYNCTHING_CONNECTOR_EXPORT SyncthingDir {
|
||||||
int completionPercentage = 0;
|
int completionPercentage = 0;
|
||||||
int scanningPercentage = 0;
|
int scanningPercentage = 0;
|
||||||
double scanningRate = 0;
|
double scanningRate = 0;
|
||||||
|
std::map<QString, SyncthingCompletion> completionByDevice;
|
||||||
QString globalError;
|
QString globalError;
|
||||||
std::vector<SyncthingItemError> itemErrors;
|
std::vector<SyncthingItemError> itemErrors;
|
||||||
std::vector<SyncthingItemError> previousItemErrors;
|
std::vector<SyncthingItemError> previousItemErrors;
|
||||||
|
|
Loading…
Reference in New Issue