mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-02-02 01:44:26 +00:00
- Improved a lot ETA calculation algorithm
This commit is contained in:
parent
a91d2028d8
commit
f7fa66c65d
1
TODO
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
|
||||||
|
|
||||||
|
@ -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(ETAs.contains(hash)) {
|
|
||||||
ETAs.remove(hash);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if(avg_speed) {
|
||||||
|
return (qlonglong) floor((double) (h.actual_size() - h.total_wanted_done()) / avg_speed);
|
||||||
|
} else {
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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…
x
Reference in New Issue
Block a user