WebAPI: Provide announce stats within "sync" data

This commit is contained in:
Vladimir Golovnev (Glassez)
2025-02-27 20:12:52 +03:00
parent e10fb40a48
commit e309b17732
3 changed files with 132 additions and 25 deletions

View File

@@ -120,6 +120,28 @@ namespace
const QString KEY_FULL_UPDATE = u"full_update"_s;
const QString KEY_RESPONSE_ID = u"rid"_s;
const QString KEY_TORRENT_HAS_TRACKER_WARNING = u"has_tracker_warning"_s;
const QString KEY_TORRENT_HAS_TRACKER_ERROR = u"has_tracker_error"_s;
const QString KEY_TORRENT_HAS_OTHER_ANNOUNCE_ERROR = u"has_other_announce_error"_s;
QStringList asStrings(const QSet<BitTorrent::TorrentID> &torrentIDs)
{
QStringList result;
result.reserve(torrentIDs.size());
for (const BitTorrent::TorrentID &torrentID : torrentIDs)
result.emplaceBack(torrentID.toString());
return result;
}
bool hasWarningMessage(const BitTorrent::TrackerEntryStatus &status)
{
return std::ranges::any_of(status.endpoints, [](const BitTorrent::TrackerEndpointStatus &endpointEntry)
{
return (endpointEntry.state == BitTorrent::TrackerEndpointState::Working) && !endpointEntry.message.isEmpty();
});
}
QVariantMap processMap(const QVariantMap &prevData, const QVariantMap &data);
std::pair<QVariantMap, QVariantList> processHash(QVariantHash prevData, const QVariantHash &data);
std::pair<QVariantList, QVariantList> processList(QVariantList prevData, const QVariantList &data);
@@ -384,6 +406,39 @@ namespace
return QJsonObject::fromVariantMap(syncData);
}
void addAnnounceStats(QVariantMap &serializedTorrent, const BitTorrent::Torrent *torrent)
{
bool hasTrackerWarning = false;
bool hasTrackerError = false;
bool hasOtherAnnounceError = false;
for (const BitTorrent::TrackerEntryStatus &status : asConst(torrent->trackers()))
{
switch (status.state)
{
case BitTorrent::TrackerEndpointState::Working:
if (!hasTrackerWarning && hasWarningMessage(status))
hasTrackerWarning = true;
break;
case BitTorrent::TrackerEndpointState::TrackerError:
hasTrackerError = true;
break;
case BitTorrent::TrackerEndpointState::NotWorking:
case BitTorrent::TrackerEndpointState::Unreachable:
hasOtherAnnounceError = true;
break;
default:
break;
}
if (hasTrackerWarning && hasTrackerError && hasOtherAnnounceError)
break;
}
serializedTorrent[KEY_TORRENT_HAS_TRACKER_WARNING] = hasTrackerWarning;
serializedTorrent[KEY_TORRENT_HAS_TRACKER_ERROR] = hasTrackerError;
serializedTorrent[KEY_TORRENT_HAS_OTHER_ANNOUNCE_ERROR] = hasOtherAnnounceError;
}
}
SyncController::SyncController(IApplication *app, QObject *parent)
@@ -488,6 +543,7 @@ void SyncController::maindataAction()
connect(btSession, &BitTorrent::Session::trackersAdded, this, &SyncController::onTorrentTrackersChanged);
connect(btSession, &BitTorrent::Session::trackersRemoved, this, &SyncController::onTorrentTrackersChanged);
connect(btSession, &BitTorrent::Session::trackersChanged, this, &SyncController::onTorrentTrackersChanged);
connect(btSession, &BitTorrent::Session::trackerEntryStatusesUpdated, this, &SyncController::onTorrentTrackerEntryStatusesUpdated);
}
const int acceptedID = params()[u"rid"_s].toInt();
@@ -526,6 +582,7 @@ void SyncController::makeMaindataSnapshot()
QVariantMap serializedTorrent = serialize(*torrent);
serializedTorrent.remove(KEY_TORRENT_ID);
addAnnounceStats(serializedTorrent, torrent);
for (const BitTorrent::TrackerEntryStatus &status : asConst(torrent->trackers()))
m_knownTrackers[status.url].insert(torrentID);
@@ -547,14 +604,8 @@ void SyncController::makeMaindataSnapshot()
for (const Tag &tag : asConst(session->tags()))
m_maindataSnapshot.tags.append(tag.toString());
for (auto trackersIter = m_knownTrackers.cbegin(); trackersIter != m_knownTrackers.cend(); ++trackersIter)
{
QStringList torrentIDs;
for (const BitTorrent::TorrentID &torrentID : asConst(trackersIter.value()))
torrentIDs.append(torrentID.toString());
m_maindataSnapshot.trackers[trackersIter.key()] = torrentIDs;
}
for (const auto &[tracker, torrentIDs] : m_knownTrackers.asKeyValueRange())
m_maindataSnapshot.trackers[tracker] = asStrings(torrentIDs);
m_maindataSnapshot.serverState = getTransferInfo();
m_maindataSnapshot.serverState[KEY_TRANSFER_FREESPACEONDISK] = m_freeDiskSpace;
@@ -579,8 +630,12 @@ QJsonObject SyncController::generateMaindataSyncData(const int id, const bool fu
for (const BitTorrent::TorrentID &torrentID : asConst(m_updatedTorrents))
m_maindataSyncBuf.removedTorrents.removeOne(torrentID.toString());
for (const BitTorrent::TorrentID &torrentID : asConst(m_removedTorrents))
m_maindataSyncBuf.torrents.remove(torrentID.toString());
{
const QString torrentIDStr = torrentID.toString();
m_maindataSyncBuf.torrents.remove(torrentIDStr);
}
for (const QString &tracker : asConst(m_updatedTrackers))
m_maindataSyncBuf.removedTrackers.removeOne(tracker);
@@ -635,30 +690,64 @@ QJsonObject SyncController::generateMaindataSyncData(const int id, const bool fu
QVariantMap serializedTorrent = serialize(*torrent);
serializedTorrent.remove(KEY_TORRENT_ID);
auto &torrentSnapshot = m_maindataSnapshot.torrents[torrentID.toString()];
const QString torrentIDStr = torrentID.toString();
auto &torrentSnapshot = m_maindataSnapshot.torrents[torrentIDStr];
if (m_announcedTorrents.contains(torrentID))
{
addAnnounceStats(serializedTorrent, torrent);
}
else
{
serializedTorrent[KEY_TORRENT_HAS_TRACKER_WARNING] = torrentSnapshot[KEY_TORRENT_HAS_TRACKER_WARNING];
serializedTorrent[KEY_TORRENT_HAS_TRACKER_ERROR] = torrentSnapshot[KEY_TORRENT_HAS_TRACKER_ERROR];
serializedTorrent[KEY_TORRENT_HAS_OTHER_ANNOUNCE_ERROR] = torrentSnapshot[KEY_TORRENT_HAS_OTHER_ANNOUNCE_ERROR];
}
if (const QVariantMap syncData = processMap(torrentSnapshot, serializedTorrent); !syncData.isEmpty())
{
m_maindataSyncBuf.torrents[torrentID.toString()] = syncData;
m_maindataSyncBuf.torrents[torrentIDStr] = syncData;
torrentSnapshot = serializedTorrent;
}
}
for (const BitTorrent::TorrentID &torrentID : asConst(m_announcedTorrents))
{
if (m_updatedTorrents.contains(torrentID))
continue;
const BitTorrent::Torrent *torrent = session->getTorrent(torrentID);
Q_ASSERT(torrent);
const QString torrentIDStr = torrentID.toString();
auto &torrentSnapshot = m_maindataSnapshot.torrents[torrentIDStr];
// Only announce stats are changed so don't need to serialize torrent again
QVariantMap serializedTorrent = torrentSnapshot;
addAnnounceStats(serializedTorrent, torrent);
if (const QVariantMap syncData = processMap(torrentSnapshot, serializedTorrent); !syncData.isEmpty())
{
m_maindataSyncBuf.torrents[torrentIDStr] = syncData;
torrentSnapshot = serializedTorrent;
}
}
m_updatedTorrents.clear();
m_announcedTorrents.clear();
for (const BitTorrent::TorrentID &torrentID : asConst(m_removedTorrents))
{
m_maindataSyncBuf.removedTorrents.append(torrentID.toString());
m_maindataSnapshot.torrents.remove(torrentID.toString());
const QString torrentIDStr = torrentID.toString();
m_maindataSyncBuf.removedTorrents.append(torrentIDStr);
m_maindataSnapshot.torrents.remove(torrentIDStr);
}
m_removedTorrents.clear();
for (const QString &tracker : asConst(m_updatedTrackers))
{
const QSet<BitTorrent::TorrentID> torrentIDs = m_knownTrackers[tracker];
QStringList serializedTorrentIDs;
serializedTorrentIDs.reserve(torrentIDs.size());
for (const BitTorrent::TorrentID &torrentID : torrentIDs)
serializedTorrentIDs.append(torrentID.toString());
const QStringList serializedTorrentIDs = asStrings(m_knownTrackers[tracker]);
m_maindataSyncBuf.trackers[tracker] = serializedTorrentIDs;
m_maindataSnapshot.trackers[tracker] = serializedTorrentIDs;
@@ -847,6 +936,7 @@ void SyncController::onTorrentAdded(BitTorrent::Torrent *torrent)
m_removedTorrents.remove(torrentID);
m_updatedTorrents.insert(torrentID);
m_announcedTorrents.insert(torrentID);
for (const BitTorrent::TrackerEntryStatus &status : asConst(torrent->trackers()))
{
@@ -860,6 +950,7 @@ void SyncController::onTorrentAboutToBeRemoved(BitTorrent::Torrent *torrent)
{
const BitTorrent::TorrentID torrentID = torrent->id();
m_announcedTorrents.remove(torrentID);
m_updatedTorrents.remove(torrentID);
m_removedTorrents.insert(torrentID);
@@ -899,6 +990,7 @@ void SyncController::onTorrentMetadataReceived(BitTorrent::Torrent *torrent)
void SyncController::onTorrentStopped(BitTorrent::Torrent *torrent)
{
m_updatedTorrents.insert(torrent->id());
m_announcedTorrents.insert(torrent->id());
}
void SyncController::onTorrentStarted(BitTorrent::Torrent *torrent)
@@ -981,4 +1073,12 @@ void SyncController::onTorrentTrackersChanged(BitTorrent::Torrent *torrent)
m_removedTrackers.remove(currentTracker);
}
}
m_announcedTorrents.insert(torrentID);
}
void SyncController::onTorrentTrackerEntryStatusesUpdated(const BitTorrent::Torrent *torrent
, [[maybe_unused]] const QHash<QString, BitTorrent::TrackerEntryStatus> &updatedTrackers)
{
m_announcedTorrents.insert(torrent->id());
}