diff --git a/src/bittorrent.cpp b/src/bittorrent.cpp index 0cc770e15..fe0da1aee 100644 --- a/src/bittorrent.cpp +++ b/src/bittorrent.cpp @@ -116,6 +116,8 @@ bittorrent::~bittorrent() { if(FSWatcher) { delete FSWatcher; } + if(timerETA) + delete timerETA; // Delete BT session qDebug("Deleting session"); delete s; @@ -459,21 +461,64 @@ void bittorrent::configureSession() { qDebug("Session configured"); } -// Calculate the ETA using GASA -// GASA: global Average Speed Algorithm -qlonglong bittorrent::getETA(QString hash) const { - QTorrentHandle h = getTorrentHandle(hash); - if(!h.is_valid()) return -1; - switch(h.state()) { - case torrent_status::downloading: { - if(h.active_time() == 0) - return -1; - double avg_speed = (double)h.all_time_download() / h.active_time(); - return (qlonglong) floor((double) (h.actual_size() - h.total_wanted_done()) / avg_speed); +void bittorrent::takeETASamples() { + bool change = false;; + foreach(const QString &hash, ETA_samples.keys()) { + QTorrentHandle h = getTorrentHandle(hash); + if(h.is_valid() && !h.is_paused() && !h.is_seed()) { + QList samples = ETA_samples.value(h.hash(), QList()); + 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); } - default: - return -1; } + 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 bittorrent::getETA(QString hash) { + 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 + 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(); + } + if(!timerETA) { + timerETA = new QTimer(this); + connect(timerETA, SIGNAL(timeout()), this, SLOT(takeETASamples())); + timerETA->start(); + } else { + QList samples = ETA_samples.value(h.hash(), QList()); + int nb_samples = samples.size(); + if(nb_samples > 3) { + long sum_samples = 0; + foreach(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); } std::vector bittorrent::getTorrents() const { diff --git a/src/bittorrent.h b/src/bittorrent.h index f6fdbc29d..509d0c1d6 100644 --- a/src/bittorrent.h +++ b/src/bittorrent.h @@ -42,6 +42,8 @@ using namespace libtorrent; +#define MAX_SAMPLES 20 + class downloadThread; class QTimer; class FileSystemWatcher; @@ -77,6 +79,8 @@ class bittorrent : public QObject { QHash savepath_fromurl; bool resolve_countries; bool geoipDBLoaded; + QPointer timerETA; + QHash > ETA_samples; protected: QString getSavePath(QString hash); @@ -106,7 +110,7 @@ class bittorrent : public QObject { int loadTorrentPriority(QString hash); QStringList getConsoleMessages() const; QStringList getPeerBanMessages() const; - qlonglong getETA(QString hash) const; + qlonglong getETA(QString hash); bool useTemporaryFolder() const; QString getDefaultSavePath() const; @@ -177,6 +181,7 @@ class bittorrent : public QObject { void readAlerts(); void loadTrackerFile(QString hash); void deleteBigRatios(); + void takeETASamples(); signals: void addedTorrent(QTorrentHandle& h);