Browse Source

- Improved a lot ETA calculation algorithm

adaptive-webui-19844
Christophe Dumez 17 years ago
parent
commit
f7fa66c65d
  1. 1
      TODO
  2. 127
      src/bittorrent.cpp
  3. 10
      src/bittorrent.h
  4. 2
      src/misc.h

1
TODO

@ -32,5 +32,6 @@
- Allow user to organize the downloads into categories/folders? - Allow user to organize the downloads into categories/folders?
// in v1.1.0 // in v1.1.0
- Stop calculating ETAs when ETA column is hidden
-> See https://blueprints.launchpad.net/qbittorrent -> See https://blueprints.launchpad.net/qbittorrent

127
src/bittorrent.cpp

@ -38,8 +38,6 @@
#include <libtorrent/torrent_info.hpp> #include <libtorrent/torrent_info.hpp>
#include <boost/filesystem/exception.hpp> #include <boost/filesystem/exception.hpp>
#define ETAS_MAX_VALUES 3
#define ETA_REFRESH_INTERVAL 10000
#define MAX_TRACKER_ERRORS 2 #define MAX_TRACKER_ERRORS 2
// Main constructor // Main constructor
@ -55,9 +53,6 @@ bittorrent::bittorrent() : timerScan(0), DHTEnabled(false), preAllocateAll(false
timerAlerts = new QTimer(); timerAlerts = new QTimer();
connect(timerAlerts, SIGNAL(timeout()), this, SLOT(readAlerts())); connect(timerAlerts, SIGNAL(timeout()), this, SLOT(readAlerts()));
timerAlerts->start(3000); timerAlerts->start(3000);
ETARefresher = new QTimer();
connect(ETARefresher, SIGNAL(timeout()), this, SLOT(updateETAs()));
ETARefresher->start(ETA_REFRESH_INTERVAL);
fastResumeSaver = new QTimer(); fastResumeSaver = new QTimer();
connect(fastResumeSaver, SIGNAL(timeout()), this, SLOT(saveFastResumeAndRatioData())); connect(fastResumeSaver, SIGNAL(timeout()), this, SLOT(saveFastResumeAndRatioData()));
fastResumeSaver->start(60000); fastResumeSaver->start(60000);
@ -67,6 +62,7 @@ bittorrent::bittorrent() : timerScan(0), DHTEnabled(false), preAllocateAll(false
connect(downloader, SIGNAL(downloadFailure(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString))); connect(downloader, SIGNAL(downloadFailure(QString, QString)), this, SLOT(handleDownloadFailure(QString, QString)));
// File deleter (thread) // File deleter (thread)
deleter = new deleteThread(this); deleter = new deleteThread(this);
BigRatioTimer = 0;
qDebug("* BTSession constructed"); qDebug("* BTSession constructed");
} }
@ -87,7 +83,8 @@ bittorrent::~bittorrent() {
delete deleter; delete deleter;
delete fastResumeSaver; delete fastResumeSaver;
delete timerAlerts; delete timerAlerts;
delete ETARefresher; if(BigRatioTimer != 0)
delete BigRatioTimer;
delete downloader; delete downloader;
// Delete BT session // Delete BT session
qDebug("Deleting session"); qDebug("Deleting session");
@ -154,57 +151,34 @@ void bittorrent::startTorrentsInPause(bool b) {
addInPause = b; addInPause = b;
} }
void bittorrent::updateETAs() { // Calculate the ETA using GASA
QString hash; // GASA: global Average Speed Algorithm
foreach(hash, unfinishedTorrents) { qlonglong bittorrent::getETA(QString hash) const {
QTorrentHandle h = getTorrentHandle(hash); QTorrentHandle h = getTorrentHandle(hash);
if(h.is_valid()) { if(!h.is_valid()) return -1;
if(h.is_paused()) continue; switch(h.state()) {
QString hash = h.hash(); case torrent_status::downloading:
QList<qlonglong> listEtas = ETAstats.value(hash, QList<qlonglong>()); case torrent_status::connecting_to_tracker: {
// XXX: We can still get an overflow if remaining file size is approximately if(!TorrentsStartTime.contains(hash)) return -1;
// 8.38*10^5 TiB (let's assume this can't happen) int timeElapsed = TorrentsStartTime.value(hash).secsTo(QDateTime::currentDateTime());
if(h.download_payload_rate() > 0.1) { double avg_speed;
if(listEtas.size() == ETAS_MAX_VALUES) { if(timeElapsed) {
listEtas.removeFirst(); size_type data_origin = TorrentsStartData.value(hash, 0);
} avg_speed = (double)(h.total_payload_download()-data_origin) / (double)timeElapsed;
listEtas << (qlonglong)((h.actual_size()-h.total_wanted_done())/(double)h.download_payload_rate());
ETAstats[hash] = listEtas;
qlonglong moy = 0;
qlonglong val;
unsigned int nbETAs = listEtas.size();
Q_ASSERT(nbETAs);
foreach(val, listEtas) {
moy += (qlonglong)((double)val/(double)nbETAs);
}
if(moy < 0) {
if(ETAstats.contains(hash)) {
ETAstats.remove(hash);
}
if(ETAs.contains(hash)) {
ETAs.remove(hash);
}
} else {
ETAs[hash] = moy;
}
} else { } else {
// Speed is too low, we don't want an overflow. return -1;
if(ETAstats.contains(hash)) { }
ETAstats.remove(hash); if(avg_speed) {
} return (qlonglong) floor((double) (h.actual_size() - h.total_wanted_done()) / avg_speed);
if(ETAs.contains(hash)) { } else {
ETAs.remove(hash); return -1;
}
} }
} }
default:
return -1;
} }
// Delete big ratios
if(max_ratio != -1)
deleteBigRatios();
}
long bittorrent::getETA(QString hash) const{
return ETAs.value(hash, -1);
} }
// Return the torrent handle, given its hash // Return the torrent handle, given its hash
@ -251,9 +225,9 @@ void bittorrent::deleteTorrent(QString hash, bool permanent) {
foreach(file, files) { foreach(file, files) {
torrentBackup.remove(file); torrentBackup.remove(file);
} }
// Remove it from ETAs hash tables // Remove it from TorrentsStartTime hash table
ETAstats.remove(hash); TorrentsStartTime.remove(hash);
ETAs.remove(hash); TorrentsStartData.remove(hash);
// Remove tracker errors // Remove tracker errors
trackersErrors.remove(hash); trackersErrors.remove(hash);
// Remove it from ratio table // Remove it from ratio table
@ -301,6 +275,9 @@ void bittorrent::setUnfinishedTorrent(QString hash) {
} }
if(!unfinishedTorrents.contains(hash)) { if(!unfinishedTorrents.contains(hash)) {
unfinishedTorrents << hash; unfinishedTorrents << hash;
QTorrentHandle h = getTorrentHandle(hash);
TorrentsStartData[hash] = h.total_payload_download();
TorrentsStartTime[hash] = QDateTime::currentDateTime();
} }
} }
@ -318,9 +295,9 @@ void bittorrent::setFinishedTorrent(QString hash) {
if(index != -1) { if(index != -1) {
unfinishedTorrents.removeAt(index); unfinishedTorrents.removeAt(index);
} }
// Remove it from ETAs hash tables // Remove it from TorrentsStartTime hash table
ETAstats.remove(hash); TorrentsStartTime.remove(hash);
ETAs.remove(hash); TorrentsStartData.remove(hash);
// Save fast resume data // Save fast resume data
saveFastResumeAndRatioData(hash); saveFastResumeAndRatioData(hash);
} }
@ -348,9 +325,9 @@ bool bittorrent::pauseTorrent(QString hash) {
paused_file.open(QIODevice::WriteOnly | QIODevice::Text); paused_file.open(QIODevice::WriteOnly | QIODevice::Text);
paused_file.close(); paused_file.close();
} }
// Remove it from ETAs hash tables // Remove it from TorrentsStartTime hash table
ETAstats.remove(hash); TorrentsStartTime.remove(hash);
ETAs.remove(hash); TorrentsStartData.remove(hash);
return change; return change;
} }
@ -359,6 +336,9 @@ bool bittorrent::resumeTorrent(QString hash) {
bool success = false; bool success = false;
QTorrentHandle h = getTorrentHandle(hash); QTorrentHandle h = getTorrentHandle(hash);
if(h.is_valid() && h.is_paused()) { if(h.is_valid() && h.is_paused()) {
// Save Addition DateTime
TorrentsStartData[hash] = h.total_payload_download();
TorrentsStartTime[hash] = QDateTime::currentDateTime();
h.resume(); h.resume();
success = true; success = true;
} }
@ -1029,9 +1009,22 @@ void bittorrent::setGlobalRatio(float ratio) {
// be automatically deleted // be automatically deleted
void bittorrent::setDeleteRatio(float ratio) { void bittorrent::setDeleteRatio(float ratio) {
if(ratio != -1 && ratio < 1.) ratio = 1.; if(ratio != -1 && ratio < 1.) ratio = 1.;
max_ratio = ratio; if(max_ratio == -1 && ratio != -1) {
qDebug("* Set deleteRatio to %.1f", max_ratio); Q_ASSERT(!BigRatioTimer);
deleteBigRatios(); BigRatioTimer = new QTimer(this);
connect(BigRatioTimer, SIGNAL(timeout()), this, SLOT(deleteBigRatios()));
BigRatioTimer->start(5000);
} else {
if(max_ratio != -1 && ratio == -1) {
delete BigRatioTimer;
BigRatioTimer = 0;
}
}
if(max_ratio != ratio) {
max_ratio = ratio;
qDebug("* Set deleteRatio to %.1f", max_ratio);
deleteBigRatios();
}
} }
bool bittorrent::loadTrackerFile(QString hash) { bool bittorrent::loadTrackerFile(QString hash) {
@ -1186,6 +1179,10 @@ void bittorrent::readAlerts() {
// Pause torrent // Pause torrent
pauseTorrent(hash); pauseTorrent(hash);
qDebug("%s was paused after checking", hash.toUtf8().data()); qDebug("%s was paused after checking", hash.toUtf8().data());
} else {
// Save Addition DateTime
TorrentsStartTime[hash] = QDateTime::currentDateTime();
TorrentsStartData[hash] = h.total_payload_download();
} }
emit torrentFinishedChecking(hash); emit torrentFinishedChecking(hash);
} }

10
src/bittorrent.h

@ -25,6 +25,7 @@
#include <QList> #include <QList>
#include <QPair> #include <QPair>
#include <QStringList> #include <QStringList>
#include <QDateTime>
#include <libtorrent/session.hpp> #include <libtorrent/session.hpp>
#include <libtorrent/ip_filter.hpp> #include <libtorrent/ip_filter.hpp>
@ -45,14 +46,14 @@ class bittorrent : public QObject{
QTimer *timerScan; QTimer *timerScan;
QTimer *timerAlerts; QTimer *timerAlerts;
QTimer *fastResumeSaver; QTimer *fastResumeSaver;
QTimer *BigRatioTimer;
bool DHTEnabled; bool DHTEnabled;
downloadThread *downloader; downloadThread *downloader;
QString defaultSavePath; QString defaultSavePath;
QStringList torrentsToPauseAfterChecking; QStringList torrentsToPauseAfterChecking;
QHash<QString, QList<qlonglong> > ETAstats; QHash<QString, QDateTime> TorrentsStartTime;
QHash<QString, qlonglong> ETAs; QHash<QString, size_type> TorrentsStartData;
QHash<QString, QPair<size_type,size_type> > ratioData; QHash<QString, QPair<size_type,size_type> > ratioData;
QTimer *ETARefresher;
QHash<QString, QList<QPair<QString, QString> > > trackersErrors; QHash<QString, QList<QPair<QString, QString> > > trackersErrors;
deleteThread *deleter; deleteThread *deleter;
QStringList finishedTorrents; QStringList finishedTorrents;
@ -82,7 +83,7 @@ class bittorrent : public QObject{
session_status getSessionStatus() const; session_status getSessionStatus() const;
int getListenPort() const; int getListenPort() const;
QStringList getTorrentsToPauseAfterChecking() const; QStringList getTorrentsToPauseAfterChecking() const;
long getETA(QString hash) const; qlonglong getETA(QString hash) const;
float getRealRatio(QString hash) const; float getRealRatio(QString hash) const;
session* getSession() const; session* getSession() const;
QList<QPair<QString, QString> > getTrackersErrors(QString hash) const; QList<QPair<QString, QString> > getTrackersErrors(QString hash) const;
@ -108,7 +109,6 @@ class bittorrent : public QObject{
void enableIPFilter(ip_filter filter); void enableIPFilter(ip_filter filter);
void disableIPFilter(); void disableIPFilter();
void resumeUnfinishedTorrents(); void resumeUnfinishedTorrents();
void updateETAs();
void saveTorrentSpeedLimits(QString hash); void saveTorrentSpeedLimits(QString hash);
void loadTorrentSpeedLimits(QString hash); void loadTorrentSpeedLimits(QString hash);
void saveDownloadUploadForTorrent(QString hash); void saveDownloadUploadForTorrent(QString hash);

2
src/misc.h

@ -298,7 +298,7 @@ class misc : public QObject{
// Take a number of seconds and return an user-friendly // Take a number of seconds and return an user-friendly
// time duration like "1d 2h 10m". // time duration like "1d 2h 10m".
static QString userFriendlyDuration(const long int seconds) { static QString userFriendlyDuration(qlonglong seconds) {
if(seconds < 0) { if(seconds < 0) {
return tr("Unknown"); return tr("Unknown");
} }

Loading…
Cancel
Save