mirror of
https://github.com/qbittorrent/qBittorrent
synced 2025-08-19 21:03:30 -07:00
parent
be3eefd8de
commit
91b2687032
1 changed files with 42 additions and 36 deletions
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2018-2023 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2018-2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
@ -28,8 +28,6 @@
|
||||||
|
|
||||||
#include "synccontroller.h"
|
#include "synccontroller.h"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QMetaObject>
|
#include <QMetaObject>
|
||||||
|
@ -119,9 +117,9 @@ namespace
|
||||||
const QString KEY_FULL_UPDATE = u"full_update"_s;
|
const QString KEY_FULL_UPDATE = u"full_update"_s;
|
||||||
const QString KEY_RESPONSE_ID = u"rid"_s;
|
const QString KEY_RESPONSE_ID = u"rid"_s;
|
||||||
|
|
||||||
void processMap(const QVariantMap &prevData, const QVariantMap &data, QVariantMap &syncData);
|
QVariantMap processMap(const QVariantMap &prevData, const QVariantMap &data);
|
||||||
void processHash(QVariantHash prevData, const QVariantHash &data, QVariantMap &syncData, QVariantList &removedItems);
|
std::pair<QVariantMap, QVariantList> processHash(QVariantHash prevData, const QVariantHash &data);
|
||||||
void processList(QVariantList prevData, const QVariantList &data, QVariantList &syncData, QVariantList &removedItems);
|
std::pair<QVariantList, QVariantList> processList(QVariantList prevData, const QVariantList &data);
|
||||||
QJsonObject generateSyncData(int acceptedResponseId, const QVariantMap &data, QVariantMap &lastAcceptedData, QVariantMap &lastData);
|
QJsonObject generateSyncData(int acceptedResponseId, const QVariantMap &data, QVariantMap &lastAcceptedData, QVariantMap &lastData);
|
||||||
|
|
||||||
QVariantMap getTransferInfo()
|
QVariantMap getTransferInfo()
|
||||||
|
@ -171,31 +169,28 @@ namespace
|
||||||
|
|
||||||
// Compare two structures (prevData, data) and calculate difference (syncData).
|
// Compare two structures (prevData, data) and calculate difference (syncData).
|
||||||
// Structures encoded as map.
|
// Structures encoded as map.
|
||||||
void processMap(const QVariantMap &prevData, const QVariantMap &data, QVariantMap &syncData)
|
QVariantMap processMap(const QVariantMap &prevData, const QVariantMap &data)
|
||||||
{
|
{
|
||||||
// initialize output variable
|
// initialize output variable
|
||||||
syncData.clear();
|
QVariantMap syncData;
|
||||||
|
|
||||||
for (auto i = data.cbegin(); i != data.cend(); ++i)
|
for (auto i = data.cbegin(); i != data.cend(); ++i)
|
||||||
{
|
{
|
||||||
const QString &key = i.key();
|
const QString &key = i.key();
|
||||||
const QVariant &value = i.value();
|
const QVariant &value = i.value();
|
||||||
QVariantList removedItems;
|
|
||||||
|
|
||||||
switch (value.userType())
|
switch (value.userType())
|
||||||
{
|
{
|
||||||
case QMetaType::QVariantMap:
|
case QMetaType::QVariantMap:
|
||||||
{
|
{
|
||||||
QVariantMap map;
|
const QVariantMap map = processMap(prevData[key].toMap(), value.toMap());
|
||||||
processMap(prevData[key].toMap(), value.toMap(), map);
|
|
||||||
if (!map.isEmpty())
|
if (!map.isEmpty())
|
||||||
syncData[key] = map;
|
syncData[key] = map;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case QMetaType::QVariantHash:
|
case QMetaType::QVariantHash:
|
||||||
{
|
{
|
||||||
QVariantMap map;
|
const auto [map, removedItems] = processHash(prevData[key].toHash(), value.toHash());
|
||||||
processHash(prevData[key].toHash(), value.toHash(), map, removedItems);
|
|
||||||
if (!map.isEmpty())
|
if (!map.isEmpty())
|
||||||
syncData[key] = map;
|
syncData[key] = map;
|
||||||
if (!removedItems.isEmpty())
|
if (!removedItems.isEmpty())
|
||||||
|
@ -204,8 +199,7 @@ namespace
|
||||||
break;
|
break;
|
||||||
case QMetaType::QVariantList:
|
case QMetaType::QVariantList:
|
||||||
{
|
{
|
||||||
QVariantList list;
|
const auto [list, removedItems] = processList(prevData[key].toList(), value.toList());
|
||||||
processList(prevData[key].toList(), value.toList(), list, removedItems);
|
|
||||||
if (!list.isEmpty())
|
if (!list.isEmpty())
|
||||||
syncData[key] = list;
|
syncData[key] = list;
|
||||||
if (!removedItems.isEmpty())
|
if (!removedItems.isEmpty())
|
||||||
|
@ -228,21 +222,22 @@ namespace
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Q_ASSERT_X(false, "processMap"
|
Q_ASSERT_X(false, "processMap"
|
||||||
, u"Unexpected type: %1"_s
|
, u"Unexpected type: %1"_s.arg(QString::fromLatin1(value.metaType().name()))
|
||||||
.arg(QString::fromLatin1(value.metaType().name()))
|
.toUtf8().constData());
|
||||||
.toUtf8().constData());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return syncData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare two lists of structures (prevData, data) and calculate difference (syncData, removedItems).
|
// Compare two lists of structures (prevData, data) and calculate difference (syncData, removedItems).
|
||||||
// Structures encoded as map.
|
// Structures encoded as map.
|
||||||
// Lists are encoded as hash table (indexed by structure key value) to improve ease of searching for removed items.
|
// Lists are encoded as hash table (indexed by structure key value) to improve ease of searching for removed items.
|
||||||
void processHash(QVariantHash prevData, const QVariantHash &data, QVariantMap &syncData, QVariantList &removedItems)
|
std::pair<QVariantMap, QVariantList> processHash(QVariantHash prevData, const QVariantHash &data)
|
||||||
{
|
{
|
||||||
// initialize output variables
|
// initialize output variables
|
||||||
syncData.clear();
|
std::pair<QVariantMap, QVariantList> result;
|
||||||
removedItems.clear();
|
auto &[syncData, removedItems] = result;
|
||||||
|
|
||||||
if (prevData.isEmpty())
|
if (prevData.isEmpty())
|
||||||
{
|
{
|
||||||
|
@ -264,8 +259,7 @@ namespace
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
QVariantMap map;
|
const QVariantMap map = processMap(prevData[i.key()].toMap(), i.value().toMap());
|
||||||
processMap(prevData[i.key()].toMap(), i.value().toMap(), map);
|
|
||||||
// existing list item found - remove it from prevData
|
// existing list item found - remove it from prevData
|
||||||
prevData.remove(i.key());
|
prevData.remove(i.key());
|
||||||
if (!map.isEmpty())
|
if (!map.isEmpty())
|
||||||
|
@ -283,9 +277,7 @@ namespace
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
QVariantList list;
|
const auto [list, removedList] = processList(prevData[i.key()].toList(), i.value().toList());
|
||||||
QVariantList removedList;
|
|
||||||
processList(prevData[i.key()].toList(), i.value().toList(), list, removedList);
|
|
||||||
// existing list item found - remove it from prevData
|
// existing list item found - remove it from prevData
|
||||||
prevData.remove(i.key());
|
prevData.remove(i.key());
|
||||||
if (!list.isEmpty() || !removedList.isEmpty())
|
if (!list.isEmpty() || !removedList.isEmpty())
|
||||||
|
@ -309,14 +301,16 @@ namespace
|
||||||
removedItems << i.key();
|
removedItems << i.key();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare two lists of simple value (prevData, data) and calculate difference (syncData, removedItems).
|
// Compare two lists of simple value (prevData, data) and calculate difference (syncData, removedItems).
|
||||||
void processList(QVariantList prevData, const QVariantList &data, QVariantList &syncData, QVariantList &removedItems)
|
std::pair<QVariantList, QVariantList> processList(QVariantList prevData, const QVariantList &data)
|
||||||
{
|
{
|
||||||
// initialize output variables
|
// initialize output variables
|
||||||
syncData.clear();
|
std::pair<QVariantList, QVariantList> result;
|
||||||
removedItems.clear();
|
auto &[syncData, removedItems] = result;
|
||||||
|
|
||||||
if (prevData.isEmpty())
|
if (prevData.isEmpty())
|
||||||
{
|
{
|
||||||
|
@ -346,6 +340,8 @@ namespace
|
||||||
removedItems = prevData;
|
removedItems = prevData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject generateSyncData(int acceptedResponseId, const QVariantMap &data, QVariantMap &lastAcceptedData, QVariantMap &lastData)
|
QJsonObject generateSyncData(int acceptedResponseId, const QVariantMap &data, QVariantMap &lastAcceptedData, QVariantMap &lastData)
|
||||||
|
@ -373,7 +369,7 @@ namespace
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
processMap(lastAcceptedData, data, syncData);
|
syncData = processMap(lastAcceptedData, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int responseId = (lastResponseId % 1000000) + 1; // cycle between 1 and 1000000
|
const int responseId = (lastResponseId % 1000000) + 1; // cycle between 1 and 1000000
|
||||||
|
@ -595,8 +591,11 @@ QJsonObject SyncController::generateMaindataSyncData(const int id, const bool fu
|
||||||
category.insert(u"name"_s, categoryName);
|
category.insert(u"name"_s, categoryName);
|
||||||
|
|
||||||
auto &categorySnapshot = m_maindataSnapshot.categories[categoryName];
|
auto &categorySnapshot = m_maindataSnapshot.categories[categoryName];
|
||||||
processMap(categorySnapshot, category, m_maindataSyncBuf.categories[categoryName]);
|
if (const QVariantMap syncData = processMap(categorySnapshot, category); !syncData.isEmpty())
|
||||||
categorySnapshot = category;
|
{
|
||||||
|
m_maindataSyncBuf.categories[categoryName] = syncData;
|
||||||
|
categorySnapshot = category;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_updatedCategories.clear();
|
m_updatedCategories.clear();
|
||||||
|
|
||||||
|
@ -630,8 +629,12 @@ QJsonObject SyncController::generateMaindataSyncData(const int id, const bool fu
|
||||||
serializedTorrent.remove(KEY_TORRENT_ID);
|
serializedTorrent.remove(KEY_TORRENT_ID);
|
||||||
|
|
||||||
auto &torrentSnapshot = m_maindataSnapshot.torrents[torrentID.toString()];
|
auto &torrentSnapshot = m_maindataSnapshot.torrents[torrentID.toString()];
|
||||||
processMap(torrentSnapshot, serializedTorrent, m_maindataSyncBuf.torrents[torrentID.toString()]);
|
|
||||||
torrentSnapshot = serializedTorrent;
|
if (const QVariantMap syncData = processMap(torrentSnapshot, serializedTorrent); !syncData.isEmpty())
|
||||||
|
{
|
||||||
|
m_maindataSyncBuf.torrents[torrentID.toString()] = syncData;
|
||||||
|
torrentSnapshot = serializedTorrent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_updatedTorrents.clear();
|
m_updatedTorrents.clear();
|
||||||
|
|
||||||
|
@ -668,8 +671,11 @@ QJsonObject SyncController::generateMaindataSyncData(const int id, const bool fu
|
||||||
serverState[KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS] = session->isAltGlobalSpeedLimitEnabled();
|
serverState[KEY_SYNC_MAINDATA_USE_ALT_SPEED_LIMITS] = session->isAltGlobalSpeedLimitEnabled();
|
||||||
serverState[KEY_SYNC_MAINDATA_REFRESH_INTERVAL] = session->refreshInterval();
|
serverState[KEY_SYNC_MAINDATA_REFRESH_INTERVAL] = session->refreshInterval();
|
||||||
serverState[KEY_SYNC_MAINDATA_USE_SUBCATEGORIES] = session->isSubcategoriesEnabled();
|
serverState[KEY_SYNC_MAINDATA_USE_SUBCATEGORIES] = session->isSubcategoriesEnabled();
|
||||||
processMap(m_maindataSnapshot.serverState, serverState, m_maindataSyncBuf.serverState);
|
if (const QVariantMap syncData = processMap(m_maindataSnapshot.serverState, serverState); !syncData.isEmpty())
|
||||||
m_maindataSnapshot.serverState = serverState;
|
{
|
||||||
|
m_maindataSyncBuf.serverState = syncData;
|
||||||
|
m_maindataSnapshot.serverState = serverState;
|
||||||
|
}
|
||||||
|
|
||||||
QJsonObject syncData;
|
QJsonObject syncData;
|
||||||
syncData[KEY_RESPONSE_ID] = id;
|
syncData[KEY_RESPONSE_ID] = id;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue