mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-01-22 12:34:19 +00:00
Improved ETA calculation
This commit is contained in:
parent
a48ef3f94f
commit
d124c24e89
@ -1,5 +1,6 @@
|
|||||||
* Unreleased - Christophe Dumez <chris@qbittorrent.org> - v2.6.0
|
* Unreleased - Christophe Dumez <chris@qbittorrent.org> - v2.6.0
|
||||||
- FEATURE: Use system icons (Linux, Qt >= 4.6)
|
- FEATURE: Use system icons (Linux, Qt >= 4.6)
|
||||||
|
- FEATURE: Improved ETA calculation
|
||||||
- FEATURE: Simplify program preferences
|
- FEATURE: Simplify program preferences
|
||||||
- COSMETIC: Same deletion confirmation dialog in the GUI and Web UI
|
- COSMETIC: Same deletion confirmation dialog in the GUI and Web UI
|
||||||
- COSMETIC: Simplified the top toolbar
|
- COSMETIC: Simplified the top toolbar
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
|
|
||||||
#include "smtp.h"
|
#include "smtp.h"
|
||||||
#include "filesystemwatcher.h"
|
#include "filesystemwatcher.h"
|
||||||
|
#include "torrentspeedmonitor.h"
|
||||||
#include "qbtsession.h"
|
#include "qbtsession.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "downloadthread.h"
|
#include "downloadthread.h"
|
||||||
@ -143,12 +144,17 @@ QBtSession::QBtSession()
|
|||||||
connect(m_scanFolders, SIGNAL(torrentsAdded(QStringList&)), this, SLOT(addTorrentsFromScanFolder(QStringList&)));
|
connect(m_scanFolders, SIGNAL(torrentsAdded(QStringList&)), this, SLOT(addTorrentsFromScanFolder(QStringList&)));
|
||||||
// Apply user settings to Bittorrent session
|
// Apply user settings to Bittorrent session
|
||||||
configureSession();
|
configureSession();
|
||||||
|
// Torrent speed monitor
|
||||||
|
m_speedMonitor = new TorrentSpeedMonitor(this);
|
||||||
|
m_speedMonitor->start();
|
||||||
qDebug("* BTSession constructed");
|
qDebug("* BTSession constructed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main destructor
|
// Main destructor
|
||||||
QBtSession::~QBtSession() {
|
QBtSession::~QBtSession() {
|
||||||
qDebug("BTSession destructor IN");
|
qDebug("BTSession destructor IN");
|
||||||
|
delete m_speedMonitor;
|
||||||
|
qDebug("Deleted the torrent speed monitor");
|
||||||
// Do some BT related saving
|
// Do some BT related saving
|
||||||
#if LIBTORRENT_VERSION_MINOR < 15
|
#if LIBTORRENT_VERSION_MINOR < 15
|
||||||
saveDHTEntry();
|
saveDHTEntry();
|
||||||
@ -172,8 +178,6 @@ QBtSession::~QBtSession() {
|
|||||||
// HTTP Server
|
// HTTP Server
|
||||||
if(httpServer)
|
if(httpServer)
|
||||||
delete httpServer;
|
delete httpServer;
|
||||||
if(timerETA)
|
|
||||||
delete timerETA;
|
|
||||||
qDebug("BTSession destructor OUT");
|
qDebug("BTSession destructor OUT");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -598,66 +602,6 @@ void QBtSession::useAlternativeSpeedsLimit(bool alternative) {
|
|||||||
emit alternativeSpeedsModeChanged(alternative);
|
emit alternativeSpeedsModeChanged(alternative);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QBtSession::takeETASamples() {
|
|
||||||
bool change = false;;
|
|
||||||
foreach(const QString &hash, ETA_samples.keys()) {
|
|
||||||
const QTorrentHandle h = getTorrentHandle(hash);
|
|
||||||
if(h.is_valid() && !h.is_paused() && !h.is_seed()) {
|
|
||||||
QList<int> samples = ETA_samples.value(h.hash(), QList<int>());
|
|
||||||
if(samples.size() >= MAX_SAMPLES)
|
|
||||||
samples.removeFirst();
|
|
||||||
samples.append(h.download_payload_rate());
|
|
||||||
ETA_samples[h.hash()] = samples;
|
|
||||||
change = true;
|
|
||||||
} else {
|
|
||||||
ETA_samples.remove(hash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!change && timerETA) {
|
|
||||||
delete timerETA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This algorithm was inspired from KTorrent - http://www.ktorrent.org
|
|
||||||
// Calculate the ETA using a combination of several algorithms:
|
|
||||||
// GASA: Global Average Speed Algorithm
|
|
||||||
// CSA: Current Speed Algorithm
|
|
||||||
// WINX: Window of X Algorithm
|
|
||||||
qlonglong QBtSession::getETA(QString hash) {
|
|
||||||
const QTorrentHandle h = getTorrentHandle(hash);
|
|
||||||
if(!h.is_valid() || h.state() != torrent_status::downloading || !h.active_time())
|
|
||||||
return -1;
|
|
||||||
// See if the torrent is going to be completed soon
|
|
||||||
const qulonglong bytes_left = h.actual_size() - h.total_wanted_done();
|
|
||||||
if(h.actual_size() > 10485760L) { // Size > 10MiB
|
|
||||||
if(h.progress() >= (float)0.99 && bytes_left < 10485760L) { // Progress>99% but less than 10MB left.
|
|
||||||
// Compute by taking samples
|
|
||||||
if(!ETA_samples.contains(h.hash())) {
|
|
||||||
ETA_samples[h.hash()] = QList<int>();
|
|
||||||
}
|
|
||||||
if(!timerETA) {
|
|
||||||
timerETA = new QTimer(this);
|
|
||||||
connect(timerETA, SIGNAL(timeout()), this, SLOT(takeETASamples()));
|
|
||||||
timerETA->start();
|
|
||||||
} else {
|
|
||||||
const QList<int> samples = ETA_samples.value(h.hash(), QList<int>());
|
|
||||||
const int nb_samples = samples.size();
|
|
||||||
if(nb_samples > 3) {
|
|
||||||
long sum_samples = 0;
|
|
||||||
foreach(const int val, samples) {
|
|
||||||
sum_samples += val;
|
|
||||||
}
|
|
||||||
// Use WINX
|
|
||||||
return (qlonglong)(((double)bytes_left) / (((double)sum_samples) / ((double)nb_samples)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Normal case: Use GASA
|
|
||||||
double avg_speed = (double)h.all_time_download() / h.active_time();
|
|
||||||
return (qlonglong) floor((double) (bytes_left) / avg_speed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the torrent handle, given its hash
|
// Return the torrent handle, given its hash
|
||||||
QTorrentHandle QBtSession::getTorrentHandle(QString hash) const{
|
QTorrentHandle QBtSession::getTorrentHandle(QString hash) const{
|
||||||
return QTorrentHandle(s->find_torrent(misc::QStringToSha1(hash)));
|
return QTorrentHandle(s->find_torrent(misc::QStringToSha1(hash)));
|
||||||
@ -2593,3 +2537,8 @@ void QBtSession::drop()
|
|||||||
m_instance = 0;
|
m_instance = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qlonglong QBtSession::getETA(const QString &hash) const
|
||||||
|
{
|
||||||
|
return m_speedMonitor->getETA(hash);
|
||||||
|
}
|
||||||
|
@ -59,6 +59,7 @@ class FilterParserThread;
|
|||||||
class HttpServer;
|
class HttpServer;
|
||||||
class BandwidthScheduler;
|
class BandwidthScheduler;
|
||||||
class ScanFoldersModel;
|
class ScanFoldersModel;
|
||||||
|
class TorrentSpeedMonitor;
|
||||||
|
|
||||||
class QBtSession : public QObject {
|
class QBtSession : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -86,7 +87,6 @@ public:
|
|||||||
//int getMaximumActiveDownloads() const;
|
//int getMaximumActiveDownloads() const;
|
||||||
//int getMaximumActiveTorrents() const;
|
//int getMaximumActiveTorrents() const;
|
||||||
int loadTorrentPriority(QString hash);
|
int loadTorrentPriority(QString hash);
|
||||||
qlonglong getETA(QString hash);
|
|
||||||
inline QStringList getConsoleMessages() const { return consoleMessages; }
|
inline QStringList getConsoleMessages() const { return consoleMessages; }
|
||||||
inline QStringList getPeerBanMessages() const { return peerBanMessages; }
|
inline QStringList getPeerBanMessages() const { return peerBanMessages; }
|
||||||
inline libtorrent::session* getSession() const { return s; }
|
inline libtorrent::session* getSession() const { return s; }
|
||||||
@ -108,6 +108,7 @@ public slots:
|
|||||||
void startUpTorrents();
|
void startUpTorrents();
|
||||||
void recheckTorrent(QString hash);
|
void recheckTorrent(QString hash);
|
||||||
void useAlternativeSpeedsLimit(bool alternative);
|
void useAlternativeSpeedsLimit(bool alternative);
|
||||||
|
qlonglong getETA(const QString& hash) const;
|
||||||
/* Needed by Web UI */
|
/* Needed by Web UI */
|
||||||
void pauseAllTorrents();
|
void pauseAllTorrents();
|
||||||
void pauseTorrent(QString hash);
|
void pauseTorrent(QString hash);
|
||||||
@ -171,7 +172,6 @@ protected slots:
|
|||||||
void addTorrentsFromScanFolder(QStringList&);
|
void addTorrentsFromScanFolder(QStringList&);
|
||||||
void readAlerts();
|
void readAlerts();
|
||||||
void processBigRatios();
|
void processBigRatios();
|
||||||
void takeETASamples();
|
|
||||||
void exportTorrentFiles(QString path);
|
void exportTorrentFiles(QString path);
|
||||||
void saveTempFastResumeData();
|
void saveTempFastResumeData();
|
||||||
void sendNotificationEmail(QTorrentHandle h);
|
void sendNotificationEmail(QTorrentHandle h);
|
||||||
@ -243,9 +243,6 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
QString defaultSavePath;
|
QString defaultSavePath;
|
||||||
QString defaultTempPath;
|
QString defaultTempPath;
|
||||||
// ETA Computation
|
|
||||||
QPointer<QTimer> timerETA;
|
|
||||||
QHash<QString, QList<int> > ETA_samples;
|
|
||||||
// IP filtering
|
// IP filtering
|
||||||
QPointer<FilterParserThread> filterParser;
|
QPointer<FilterParserThread> filterParser;
|
||||||
QString filterPath;
|
QString filterPath;
|
||||||
@ -259,6 +256,7 @@ private:
|
|||||||
#endif
|
#endif
|
||||||
// Tracker
|
// Tracker
|
||||||
QPointer<QTracker> m_tracker;
|
QPointer<QTracker> m_tracker;
|
||||||
|
TorrentSpeedMonitor *m_speedMonitor;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3,10 +3,12 @@ INCLUDEPATH += $$PWD
|
|||||||
HEADERS += $$PWD/qbtsession.h \
|
HEADERS += $$PWD/qbtsession.h \
|
||||||
$$PWD/qtorrenthandle.h \
|
$$PWD/qtorrenthandle.h \
|
||||||
$$PWD/bandwidthscheduler.h \
|
$$PWD/bandwidthscheduler.h \
|
||||||
$$PWD/trackerinfos.h
|
$$PWD/trackerinfos.h \
|
||||||
|
qtlibtorrent/torrentspeedmonitor.h
|
||||||
|
|
||||||
SOURCES += $$PWD/qbtsession.cpp \
|
SOURCES += $$PWD/qbtsession.cpp \
|
||||||
$$PWD/qtorrenthandle.cpp
|
$$PWD/qtorrenthandle.cpp \
|
||||||
|
qtlibtorrent/torrentspeedmonitor.cpp
|
||||||
|
|
||||||
!contains(DEFINES, DISABLE_GUI) {
|
!contains(DEFINES, DISABLE_GUI) {
|
||||||
HEADERS += $$PWD/torrentmodel.h
|
HEADERS += $$PWD/torrentmodel.h
|
||||||
|
84
src/qtlibtorrent/torrentspeedmonitor.cpp
Normal file
84
src/qtlibtorrent/torrentspeedmonitor.cpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#include <QMutexLocker>
|
||||||
|
#include "torrentspeedmonitor.h"
|
||||||
|
#include "qbtsession.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
using namespace libtorrent;
|
||||||
|
|
||||||
|
TorrentSpeedMonitor::TorrentSpeedMonitor(QBtSession* session) :
|
||||||
|
QThread(session), m_abort(false), m_session(session)
|
||||||
|
{
|
||||||
|
connect(m_session, SIGNAL(deletedTorrent(QString)), SLOT(removeSamples(QString)));
|
||||||
|
connect(m_session, SIGNAL(pausedTorrent(QTorrentHandle)), SLOT(removeSamples(QTorrentHandle)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TorrentSpeedMonitor::~TorrentSpeedMonitor() {
|
||||||
|
m_abort = true;
|
||||||
|
m_abortCond.wakeOne();
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentSpeedMonitor::run()
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
mutex.lock();
|
||||||
|
getSamples();
|
||||||
|
m_abortCond.wait(&mutex, 1000);
|
||||||
|
mutex.unlock();
|
||||||
|
} while(!m_abort);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpeedSample::addSample(int s)
|
||||||
|
{
|
||||||
|
m_speedSamples << s;
|
||||||
|
if(m_speedSamples.size() > max_samples)
|
||||||
|
m_speedSamples.removeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
float SpeedSample::average() const
|
||||||
|
{
|
||||||
|
if(m_speedSamples.empty()) return 0;
|
||||||
|
qlonglong sum = 0;
|
||||||
|
foreach (int s, m_speedSamples) {
|
||||||
|
sum += s;
|
||||||
|
}
|
||||||
|
return sum/static_cast<float>(m_speedSamples.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpeedSample::clear()
|
||||||
|
{
|
||||||
|
m_speedSamples.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentSpeedMonitor::removeSamples(const QString &hash)
|
||||||
|
{
|
||||||
|
m_samples.remove(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentSpeedMonitor::removeSamples(const QTorrentHandle& h) {
|
||||||
|
try {
|
||||||
|
m_samples.remove(h.hash());
|
||||||
|
} catch(invalid_handle&){}
|
||||||
|
}
|
||||||
|
|
||||||
|
qlonglong TorrentSpeedMonitor::getETA(const QString &hash) const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&mutex);
|
||||||
|
QTorrentHandle h = m_session->getTorrentHandle(hash);
|
||||||
|
if(h.is_paused() || !m_samples.contains(hash)) return -1;
|
||||||
|
const float speed_average = m_samples.value(hash).average();
|
||||||
|
if(speed_average == 0) return -1;
|
||||||
|
return (h.total_wanted() - h.total_done()) / speed_average;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentSpeedMonitor::getSamples()
|
||||||
|
{
|
||||||
|
const std::vector<torrent_handle> torrents = m_session->getSession()->get_torrents();
|
||||||
|
std::vector<torrent_handle>::const_iterator it;
|
||||||
|
for(it = torrents.begin(); it != torrents.end(); it++) {
|
||||||
|
try {
|
||||||
|
if(!it->is_paused())
|
||||||
|
m_samples[misc::toQString(it->info_hash())].addSample(it->status().download_payload_rate);
|
||||||
|
} catch(invalid_handle&){}
|
||||||
|
}
|
||||||
|
}
|
59
src/qtlibtorrent/torrentspeedmonitor.h
Normal file
59
src/qtlibtorrent/torrentspeedmonitor.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#ifndef TORRENTSPEEDMONITOR_H
|
||||||
|
#define TORRENTSPEEDMONITOR_H
|
||||||
|
|
||||||
|
#include <QThread>
|
||||||
|
#include <QWaitCondition>
|
||||||
|
#include <QList>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QMutex>
|
||||||
|
#include "qtorrenthandle.h"
|
||||||
|
|
||||||
|
class QBtSession;
|
||||||
|
|
||||||
|
class SpeedSample {
|
||||||
|
public:
|
||||||
|
SpeedSample(){}
|
||||||
|
void addSample(int s);
|
||||||
|
float average() const;
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const int max_samples = 30;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<int> m_speedSamples;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TorrentSpeedMonitor : public QThread
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit TorrentSpeedMonitor(QBtSession* session);
|
||||||
|
~TorrentSpeedMonitor();
|
||||||
|
qlonglong getETA(const QString &hash) const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void run();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
private:
|
||||||
|
void getSamples();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void removeSamples(const QString& hash);
|
||||||
|
void removeSamples(const QTorrentHandle& h);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const int sampling_interval = 1000; // 1s
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_abort;
|
||||||
|
QWaitCondition m_abortCond;
|
||||||
|
QHash<QString, SpeedSample> m_samples;
|
||||||
|
mutable QMutex mutex;
|
||||||
|
QBtSession *m_session;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TORRENTSPEEDMONITOR_H
|
Loading…
x
Reference in New Issue
Block a user