sledgehammer999
11 years ago
16 changed files with 1014 additions and 847 deletions
@ -0,0 +1,93 @@ |
|||||||
|
#include "alertdispatcher.h" |
||||||
|
|
||||||
|
#include <boost/bind.hpp> |
||||||
|
#include <boost/make_shared.hpp> |
||||||
|
#include <QMutexLocker> |
||||||
|
|
||||||
|
QAlertDispatcher::QAlertDispatcher(libtorrent::session *session, QObject* parent) |
||||||
|
: QObject(parent) |
||||||
|
, session(session) |
||||||
|
, current_tag(new QAtomicPointer<QAlertDispatcher>(this)) |
||||||
|
, event_posted(false) { |
||||||
|
session->set_alert_dispatch(boost::bind(&QAlertDispatcher::dispatch, current_tag, _1)); |
||||||
|
} |
||||||
|
|
||||||
|
QAlertDispatcher::~QAlertDispatcher() { |
||||||
|
// When QAlertDispatcher is destoyed, libtorrent still can call
|
||||||
|
// QAlertDispatcher::dispatch a few times after destruction. This is
|
||||||
|
// handled by passing a "tag". A tag is a object that references QAlertDispatch.
|
||||||
|
// Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag
|
||||||
|
// and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called
|
||||||
|
// with invalid tag it simply discard an alert.
|
||||||
|
|
||||||
|
{ |
||||||
|
QMutexLocker lock(&(alerts_mutex)); |
||||||
|
*current_tag = 0; |
||||||
|
current_tag.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
typedef boost::function<void (std::auto_ptr<libtorrent::alert>)> dispatch_function_t; |
||||||
|
session->set_alert_dispatch(dispatch_function_t()); |
||||||
|
} |
||||||
|
|
||||||
|
void QAlertDispatcher::getPendingAlertsNoWait(std::deque<libtorrent::alert*>& out) { |
||||||
|
Q_ASSERT(out.empty()); |
||||||
|
|
||||||
|
QMutexLocker lock(&(alerts_mutex)); |
||||||
|
std::swap(alerts, out); |
||||||
|
event_posted = false; |
||||||
|
} |
||||||
|
|
||||||
|
void QAlertDispatcher::getPendingAlerts(std::deque<libtorrent::alert*>& out) { |
||||||
|
assert(out.empty()); |
||||||
|
|
||||||
|
QMutexLocker lock(&(alerts_mutex)); |
||||||
|
|
||||||
|
while (alerts.empty()) |
||||||
|
alerts_condvar.wait(&(alerts_mutex)); |
||||||
|
|
||||||
|
std::swap(alerts, out); |
||||||
|
event_posted = false; |
||||||
|
} |
||||||
|
|
||||||
|
void QAlertDispatcher::dispatch(QSharedPointer<QAtomicPointer<QAlertDispatcher> > tag, |
||||||
|
std::auto_ptr<libtorrent::alert> alert_ptr) { |
||||||
|
QAlertDispatcher* that = *tag; |
||||||
|
if (!that) |
||||||
|
return; |
||||||
|
|
||||||
|
QMutexLocker lock(&(that->alerts_mutex)); |
||||||
|
|
||||||
|
that = *tag; |
||||||
|
if (!that) |
||||||
|
return; |
||||||
|
|
||||||
|
bool was_empty = that->alerts.empty(); |
||||||
|
|
||||||
|
that->alerts.push_back(alert_ptr.get()); |
||||||
|
alert_ptr.release(); |
||||||
|
|
||||||
|
if (was_empty) |
||||||
|
that->alerts_condvar.wakeAll(); |
||||||
|
|
||||||
|
that->enqueueToMainThread(); |
||||||
|
|
||||||
|
Q_ASSERT(that->current_tag == tag); |
||||||
|
} |
||||||
|
|
||||||
|
void QAlertDispatcher::enqueueToMainThread() { |
||||||
|
if (!event_posted) { |
||||||
|
event_posted = true; |
||||||
|
QMetaObject::invokeMethod(this, "deliverSignal", Qt::QueuedConnection); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void QAlertDispatcher::deliverSignal() { |
||||||
|
emit alertsReceived(); |
||||||
|
|
||||||
|
QMutexLocker lock(&(alerts_mutex)); |
||||||
|
event_posted = false; |
||||||
|
|
||||||
|
if (!alerts.empty()) |
||||||
|
enqueueToMainThread(); |
||||||
|
} |
@ -0,0 +1,43 @@ |
|||||||
|
#ifndef ALERTDISPATCHER_H |
||||||
|
#define ALERTDISPATCHER_H |
||||||
|
|
||||||
|
#include <QObject> |
||||||
|
#include <QMutex> |
||||||
|
#include <QWaitCondition> |
||||||
|
#include <QAtomicPointer> |
||||||
|
#include <QSharedPointer> |
||||||
|
#include <libtorrent/session.hpp> |
||||||
|
|
||||||
|
class QAlertDispatcher : public QObject |
||||||
|
{ |
||||||
|
Q_OBJECT |
||||||
|
Q_DISABLE_COPY(QAlertDispatcher) |
||||||
|
|
||||||
|
public: |
||||||
|
QAlertDispatcher(libtorrent::session *session, QObject* parent); |
||||||
|
~QAlertDispatcher(); |
||||||
|
|
||||||
|
void getPendingAlertsNoWait(std::deque<libtorrent::alert*>&); |
||||||
|
void getPendingAlerts(std::deque<libtorrent::alert*>&); |
||||||
|
|
||||||
|
signals: |
||||||
|
void alertsReceived(); |
||||||
|
|
||||||
|
private: |
||||||
|
static void dispatch(QSharedPointer<QAtomicPointer<QAlertDispatcher> >, |
||||||
|
std::auto_ptr<libtorrent::alert>); |
||||||
|
void enqueueToMainThread(); |
||||||
|
|
||||||
|
private slots: |
||||||
|
void deliverSignal(); |
||||||
|
|
||||||
|
private: |
||||||
|
libtorrent::session *session; |
||||||
|
QMutex alerts_mutex; |
||||||
|
QWaitCondition alerts_condvar; |
||||||
|
std::deque<libtorrent::alert*> alerts; |
||||||
|
QSharedPointer<QAtomicPointer<QAlertDispatcher> > current_tag; |
||||||
|
bool event_posted; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // ALERTDISPATCHER_H
|
@ -0,0 +1,95 @@ |
|||||||
|
#include "torrentstatistics.h" |
||||||
|
|
||||||
|
#include <QDateTime> |
||||||
|
|
||||||
|
#include <libtorrent/session.hpp> |
||||||
|
|
||||||
|
#include "qbtsession.h" |
||||||
|
#include "qinisettings.h" |
||||||
|
|
||||||
|
TorrentStatistics::TorrentStatistics(QBtSession* session, QObject* parent) |
||||||
|
: QObject(parent) |
||||||
|
, m_session(session) |
||||||
|
, m_sessionUL(0) |
||||||
|
, m_sessionDL(0) |
||||||
|
, m_lastWrite(0) |
||||||
|
, m_dirty(false) { |
||||||
|
loadStats(); |
||||||
|
connect(&m_timer, SIGNAL(timeout()), this, SLOT(gatherStats())); |
||||||
|
m_timer.start(60 * 1000); |
||||||
|
} |
||||||
|
|
||||||
|
TorrentStatistics::~TorrentStatistics() { |
||||||
|
if (m_dirty) |
||||||
|
m_lastWrite = 0; |
||||||
|
saveStats(); |
||||||
|
} |
||||||
|
|
||||||
|
quint64 TorrentStatistics::getAlltimeDL() const { |
||||||
|
return m_alltimeDL + m_sessionDL; |
||||||
|
} |
||||||
|
|
||||||
|
quint64 TorrentStatistics::getAlltimeUL() const { |
||||||
|
return m_alltimeUL + m_sessionUL; |
||||||
|
} |
||||||
|
|
||||||
|
void TorrentStatistics::gatherStats() { |
||||||
|
libtorrent::session_status ss = m_session->getSessionStatus(); |
||||||
|
if (ss.total_download > m_sessionDL) { |
||||||
|
m_sessionDL = ss.total_download; |
||||||
|
m_dirty = true; |
||||||
|
} |
||||||
|
if (ss.total_upload > m_sessionUL) { |
||||||
|
m_sessionUL = ss.total_upload; |
||||||
|
m_dirty = true; |
||||||
|
} |
||||||
|
|
||||||
|
saveStats(); |
||||||
|
} |
||||||
|
|
||||||
|
void TorrentStatistics::saveStats() const { |
||||||
|
if (!(m_dirty && (QDateTime::currentMSecsSinceEpoch() - m_lastWrite >= 15*60*1000) )) |
||||||
|
return; |
||||||
|
QIniSettings s("qBittorrent", "qBittorrent-data"); |
||||||
|
QVariantHash v; |
||||||
|
v.insert("AlltimeDL", m_alltimeDL + m_sessionDL); |
||||||
|
v.insert("AlltimeUL", m_alltimeUL + m_sessionUL); |
||||||
|
s.setValue("Stats/AllStats", v); |
||||||
|
m_dirty = false; |
||||||
|
m_lastWrite = QDateTime::currentMSecsSinceEpoch(); |
||||||
|
} |
||||||
|
|
||||||
|
void TorrentStatistics::loadStats() { |
||||||
|
// Temp code. Versions v3.1.4 and v3.1.5 saved the data in the qbittorrent.ini file.
|
||||||
|
// This code reads the data from there, writes it to the new file, and removes the keys
|
||||||
|
// from the old file. This code should be removed after some time has passed.
|
||||||
|
// e.g. When we reach v3.3.0
|
||||||
|
QIniSettings s_old; |
||||||
|
QIniSettings s("qBittorrent", "qBittorrent-data"); |
||||||
|
QVariantHash v; |
||||||
|
|
||||||
|
// Let's test if the qbittorrent.ini holds the key
|
||||||
|
if (s_old.contains("Stats/AllStats")) { |
||||||
|
v = s_old.value("Stats/AllStats").toHash(); |
||||||
|
m_dirty = true; |
||||||
|
|
||||||
|
// If the user has used qbt > 3.1.5 and then reinstalled/used
|
||||||
|
// qbt < 3.1.6, there will be stats in qbittorrent-data.ini too
|
||||||
|
// so we need to merge those 2.
|
||||||
|
if (s.contains("Stats/AllStats")) { |
||||||
|
QVariantHash tmp = s.value("Stats/AllStats").toHash(); |
||||||
|
v["AlltimeDL"] = v["AlltimeDL"].toULongLong() + tmp["AlltimeDL"].toULongLong(); |
||||||
|
v["AlltimeUL"] = v["AlltimeUL"].toULongLong() + tmp["AlltimeUL"].toULongLong(); |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
v = s.value("Stats/AllStats").toHash(); |
||||||
|
|
||||||
|
m_alltimeDL = v["AlltimeDL"].toULongLong(); |
||||||
|
m_alltimeUL = v["AlltimeUL"].toULongLong(); |
||||||
|
|
||||||
|
if (m_dirty) { |
||||||
|
saveStats(); |
||||||
|
s_old.remove("Stats/AllStats"); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
#ifndef TORRENTSTATISTICS_H |
||||||
|
#define TORRENTSTATISTICS_H |
||||||
|
|
||||||
|
#include <QObject> |
||||||
|
#include <QTimer> |
||||||
|
|
||||||
|
class QBtSession; |
||||||
|
|
||||||
|
class TorrentStatistics : QObject |
||||||
|
{ |
||||||
|
Q_OBJECT |
||||||
|
Q_DISABLE_COPY(TorrentStatistics) |
||||||
|
|
||||||
|
public: |
||||||
|
TorrentStatistics(QBtSession* session, QObject* parent = 0); |
||||||
|
~TorrentStatistics(); |
||||||
|
|
||||||
|
quint64 getAlltimeDL() const; |
||||||
|
quint64 getAlltimeUL() const; |
||||||
|
|
||||||
|
private slots: |
||||||
|
void gatherStats(); |
||||||
|
|
||||||
|
private: |
||||||
|
void saveStats() const; |
||||||
|
void loadStats(); |
||||||
|
|
||||||
|
private: |
||||||
|
QBtSession* m_session; |
||||||
|
// Will overflow at 15.9 EiB
|
||||||
|
quint64 m_alltimeUL; |
||||||
|
quint64 m_alltimeDL; |
||||||
|
qint64 m_sessionUL; |
||||||
|
qint64 m_sessionDL; |
||||||
|
mutable qint64 m_lastWrite; |
||||||
|
mutable bool m_dirty; |
||||||
|
|
||||||
|
QTimer m_timer; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // TORRENTSTATISTICS_H
|
Loading…
Reference in new issue