Browse Source

Merge pull request #1703 from sorokin/alerts

Use torrent_status to be more efficient.
adaptive-webui-19844
sledgehammer999 11 years ago
parent
commit
c8035dff1e
  1. 37
      src/properties/propertieswidget.cpp
  2. 93
      src/qtlibtorrent/alertdispatcher.cpp
  3. 43
      src/qtlibtorrent/alertdispatcher.h
  4. 134
      src/qtlibtorrent/qbtsession.cpp
  5. 14
      src/qtlibtorrent/qbtsession.h
  6. 8
      src/qtlibtorrent/qtlibtorrent.pri
  7. 181
      src/qtlibtorrent/qtorrenthandle.cpp
  8. 33
      src/qtlibtorrent/qtorrenthandle.h
  9. 151
      src/qtlibtorrent/torrentmodel.cpp
  10. 3
      src/qtlibtorrent/torrentmodel.h
  11. 129
      src/qtlibtorrent/torrentspeedmonitor.cpp
  12. 30
      src/qtlibtorrent/torrentspeedmonitor.h
  13. 95
      src/qtlibtorrent/torrentstatistics.cpp
  14. 41
      src/qtlibtorrent/torrentstatistics.h
  15. 5
      src/transferlistdelegate.h
  16. 64
      src/webui/btjson.cpp

37
src/properties/propertieswidget.cpp

@ -316,28 +316,31 @@ void PropertiesWidget::loadDynamicData() {
// Refresh only if the torrent handle is valid and if visible // Refresh only if the torrent handle is valid and if visible
if (!h.is_valid() || main_window->getCurrentTabWidget() != transferList || state != VISIBLE) return; if (!h.is_valid() || main_window->getCurrentTabWidget() != transferList || state != VISIBLE) return;
try { try {
libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters
| torrent_handle::query_distributed_copies
| torrent_handle::query_pieces);
// Transfer infos // Transfer infos
if (stackedProperties->currentIndex() == PropTabBar::MAIN_TAB) { if (stackedProperties->currentIndex() == PropTabBar::MAIN_TAB) {
wasted->setText(misc::friendlyUnit(h.total_failed_bytes()+h.total_redundant_bytes())); wasted->setText(misc::friendlyUnit(status.total_failed_bytes+status.total_redundant_bytes));
upTotal->setText(misc::friendlyUnit(h.all_time_upload()) + " ("+misc::friendlyUnit(h.total_payload_upload())+" "+tr("this session")+")"); upTotal->setText(misc::friendlyUnit(status.all_time_upload) + " ("+misc::friendlyUnit(status.total_payload_upload)+" "+tr("this session")+")");
dlTotal->setText(misc::friendlyUnit(h.all_time_download()) + " ("+misc::friendlyUnit(h.total_payload_download())+" "+tr("this session")+")"); dlTotal->setText(misc::friendlyUnit(status.all_time_download) + " ("+misc::friendlyUnit(status.total_payload_download)+" "+tr("this session")+")");
lbl_uplimit->setText(h.upload_limit() <= 0 ? QString::fromUtf8("") : misc::friendlyUnit(h.upload_limit())+tr("/s", "/second (i.e. per second)")); lbl_uplimit->setText(h.upload_limit() <= 0 ? QString::fromUtf8("") : misc::friendlyUnit(h.upload_limit())+tr("/s", "/second (i.e. per second)"));
lbl_dllimit->setText(h.download_limit() <= 0 ? QString::fromUtf8("") : misc::friendlyUnit(h.download_limit())+tr("/s", "/second (i.e. per second)")); lbl_dllimit->setText(h.download_limit() <= 0 ? QString::fromUtf8("") : misc::friendlyUnit(h.download_limit())+tr("/s", "/second (i.e. per second)"));
QString elapsed_txt = misc::userFriendlyDuration(h.active_time()); QString elapsed_txt = misc::userFriendlyDuration(status.active_time);
if (h.is_seed()) { if (h.is_seed(status)) {
elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(h.seeding_time()))+")"; elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(status.seeding_time))+")";
} }
lbl_elapsed->setText(elapsed_txt); lbl_elapsed->setText(elapsed_txt);
if (h.connections_limit() > 0) if (status.connections_limit > 0)
lbl_connections->setText(QString::number(h.num_connections())+" ("+tr("%1 max", "e.g. 10 max").arg(QString::number(h.connections_limit()))+")"); lbl_connections->setText(QString::number(status.num_connections)+" ("+tr("%1 max", "e.g. 10 max").arg(QString::number(status.connections_limit))+")");
else else
lbl_connections->setText(QString::number(h.num_connections())); lbl_connections->setText(QString::number(status.num_connections));
// Update next announce time // Update next announce time
reannounce_lbl->setText(h.next_announce()); reannounce_lbl->setText(misc::userFriendlyDuration(status.next_announce.total_seconds()));
// Update ratio info // Update ratio info
const qreal ratio = QBtSession::instance()->getRealRatio(h.hash()); const qreal ratio = QBtSession::instance()->getRealRatio(status);
shareRatio->setText(ratio > QBtSession::MAX_RATIO ? QString::fromUtf8("") : misc::accurateDoubleToString(ratio, 2)); shareRatio->setText(ratio > QBtSession::MAX_RATIO ? QString::fromUtf8("") : misc::accurateDoubleToString(ratio, 2));
if (!h.is_seed() && h.has_metadata()) { if (!h.is_seed(status) && status.has_metadata) {
showPiecesDownloaded(true); showPiecesDownloaded(true);
// Downloaded pieces // Downloaded pieces
#if LIBTORRENT_VERSION_NUM < 10000 #if LIBTORRENT_VERSION_NUM < 10000
@ -346,19 +349,19 @@ void PropertiesWidget::loadDynamicData() {
bitfield bf(h.torrent_file()->num_pieces(), 0); bitfield bf(h.torrent_file()->num_pieces(), 0);
#endif #endif
h.downloading_pieces(bf); h.downloading_pieces(bf);
downloaded_pieces->setProgress(h.pieces(), bf); downloaded_pieces->setProgress(status.pieces, bf);
// Pieces availability // Pieces availability
if (!h.is_paused() && !h.is_queued() && !h.is_checking()) { if (!h.is_paused(status) && !h.is_queued(status) && !h.is_checking(status)) {
showPiecesAvailability(true); showPiecesAvailability(true);
std::vector<int> avail; std::vector<int> avail;
h.piece_availability(avail); h.piece_availability(avail);
pieces_availability->setAvailability(avail); pieces_availability->setAvailability(avail);
avail_average_lbl->setText(misc::accurateDoubleToString(h.distributed_copies(), 3)); avail_average_lbl->setText(misc::accurateDoubleToString(status.distributed_copies, 3));
} else { } else {
showPiecesAvailability(false); showPiecesAvailability(false);
} }
// Progress // Progress
qreal progress = h.progress()*100.; qreal progress = h.progress(status)*100.;
progress_lbl->setText(misc::accurateDoubleToString(progress, 1)+"%"); progress_lbl->setText(misc::accurateDoubleToString(progress, 1)+"%");
} else { } else {
showPiecesAvailability(false); showPiecesAvailability(false);
@ -378,7 +381,7 @@ void PropertiesWidget::loadDynamicData() {
} }
if (stackedProperties->currentIndex() == PropTabBar::FILES_TAB) { if (stackedProperties->currentIndex() == PropTabBar::FILES_TAB) {
// Files progress // Files progress
if (h.is_valid() && h.has_metadata()) { if (h.is_valid() && status.has_metadata) {
qDebug("Updating priorities in files tab"); qDebug("Updating priorities in files tab");
filesList->setUpdatesEnabled(false); filesList->setUpdatesEnabled(false);
std::vector<size_type> fp; std::vector<size_type> fp;

93
src/qtlibtorrent/alertdispatcher.cpp

@ -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();
}

43
src/qtlibtorrent/alertdispatcher.h

@ -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

134
src/qtlibtorrent/qbtsession.cpp

@ -39,6 +39,7 @@
#include "smtp.h" #include "smtp.h"
#include "filesystemwatcher.h" #include "filesystemwatcher.h"
#include "torrentspeedmonitor.h" #include "torrentspeedmonitor.h"
#include "torrentstatistics.h"
#include "qbtsession.h" #include "qbtsession.h"
#include "misc.h" #include "misc.h"
#include "fs_utils.h" #include "fs_utils.h"
@ -114,6 +115,7 @@ QBtSession::QBtSession()
, m_upnp(0), m_natpmp(0) , m_upnp(0), m_natpmp(0)
#endif #endif
, m_dynDNSUpdater(0) , m_dynDNSUpdater(0)
, m_alertDispatcher(0)
{ {
BigRatioTimer = new QTimer(this); BigRatioTimer = new QTimer(this);
BigRatioTimer->setInterval(10000); BigRatioTimer->setInterval(10000);
@ -132,7 +134,7 @@ QBtSession::QBtSession()
addConsoleMessage("Peer ID: "+misc::toQString(fingerprint(peer_id.toLocal8Bit().constData(), version.at(0), version.at(1), version.at(2), version.at(3)).to_string())); addConsoleMessage("Peer ID: "+misc::toQString(fingerprint(peer_id.toLocal8Bit().constData(), version.at(0), version.at(1), version.at(2), version.at(3)).to_string()));
// Set severity level of libtorrent session // Set severity level of libtorrent session
s->set_alert_mask(alert::error_notification | alert::peer_notification | alert::port_mapping_notification | alert::storage_notification | alert::tracker_notification | alert::status_notification | alert::ip_block_notification | alert::progress_notification); s->set_alert_mask(alert::error_notification | alert::peer_notification | alert::port_mapping_notification | alert::storage_notification | alert::tracker_notification | alert::status_notification | alert::ip_block_notification | alert::progress_notification | alert::stats_notification);
// Load previous state // Load previous state
loadSessionState(); loadSessionState();
// Enabling plugins // Enabling plugins
@ -147,9 +149,8 @@ QBtSession::QBtSession()
PeXEnabled = false; PeXEnabled = false;
} }
s->add_extension(&create_smart_ban_plugin); s->add_extension(&create_smart_ban_plugin);
timerAlerts = new QTimer(this); m_alertDispatcher = new QAlertDispatcher(s, this);
connect(timerAlerts, SIGNAL(timeout()), SLOT(readAlerts())); connect(m_alertDispatcher, SIGNAL(alertsReceived()), SLOT(readAlerts()));
timerAlerts->start(1000);
appendLabelToSavePath = pref.appendTorrentLabel(); appendLabelToSavePath = pref.appendTorrentLabel();
appendqBExtension = pref.useIncompleteFilesExtension(); appendqBExtension = pref.useIncompleteFilesExtension();
connect(m_scanFolders, SIGNAL(torrentsAdded(QStringList&)), SLOT(addTorrentsFromScanFolder(QStringList&))); connect(m_scanFolders, SIGNAL(torrentsAdded(QStringList&)), SLOT(addTorrentsFromScanFolder(QStringList&)));
@ -157,7 +158,7 @@ QBtSession::QBtSession()
configureSession(); configureSession();
// Torrent speed monitor // Torrent speed monitor
m_speedMonitor = new TorrentSpeedMonitor(this); m_speedMonitor = new TorrentSpeedMonitor(this);
m_speedMonitor->start(); m_torrentStatistics = new TorrentStatistics(this, this);
// To download from urls // To download from urls
downloader = new DownloadThread(this); downloader = new DownloadThread(this);
connect(downloader, SIGNAL(downloadFinished(QString, QString)), SLOT(processDownloadedFile(QString, QString))); connect(downloader, SIGNAL(downloadFinished(QString, QString)), SLOT(processDownloadedFile(QString, QString)));
@ -179,7 +180,6 @@ QBtSession::~QBtSession() {
// Delete our objects // Delete our objects
if (m_tracker) if (m_tracker)
delete m_tracker; delete m_tracker;
delete timerAlerts;
if (BigRatioTimer) if (BigRatioTimer)
delete BigRatioTimer; delete BigRatioTimer;
if (filterParser) if (filterParser)
@ -190,6 +190,8 @@ QBtSession::~QBtSession() {
// HTTP Server // HTTP Server
if (httpServer) if (httpServer)
delete httpServer; delete httpServer;
delete m_alertDispatcher;
delete m_torrentStatistics;
qDebug("Deleting the session"); qDebug("Deleting the session");
delete s; delete s;
qDebug("BTSession destructor OUT"); qDebug("BTSession destructor OUT");
@ -220,7 +222,7 @@ void QBtSession::processBigRatios() {
if (!h.is_valid()) continue; if (!h.is_valid()) continue;
if (h.is_seed()) { if (h.is_seed()) {
const QString hash = h.hash(); const QString hash = h.hash();
const qreal ratio = getRealRatio(hash); const qreal ratio = getRealRatio(h.status(torrent_handle::query_accurate_download_counters));
qreal ratio_limit = TorrentPersistentData::getRatioLimit(hash); qreal ratio_limit = TorrentPersistentData::getRatioLimit(hash);
if (ratio_limit == TorrentPersistentData::USE_GLOBAL_RATIO) if (ratio_limit == TorrentPersistentData::USE_GLOBAL_RATIO)
ratio_limit = global_ratio_limit; ratio_limit = global_ratio_limit;
@ -1556,15 +1558,11 @@ bool QBtSession::enableDHT(bool b) {
return true; return true;
} }
qreal QBtSession::getRealRatio(const QString &hash) const { qreal QBtSession::getRealRatio(const libtorrent::torrent_status &status) const {
QTorrentHandle h = getTorrentHandle(hash); libtorrent::size_type all_time_upload = status.all_time_upload;
if (!h.is_valid()) { libtorrent::size_type all_time_download = status.all_time_download;
return 0.; libtorrent::size_type total_done = status.total_done;
}
libtorrent::size_type all_time_upload = h.all_time_upload();
libtorrent::size_type all_time_download = h.all_time_download();
libtorrent::size_type total_done = h.total_done();
if (all_time_download < total_done) { if (all_time_download < total_done) {
// We have more data on disk than we downloaded // We have more data on disk than we downloaded
// either because the user imported the file // either because the user imported the file
@ -1577,7 +1575,7 @@ qreal QBtSession::getRealRatio(const QString &hash) const {
if (all_time_download == 0) { if (all_time_download == 0) {
if (all_time_upload == 0) if (all_time_upload == 0)
return 0; return 0.0;
return MAX_RATIO+1; return MAX_RATIO+1;
} }
@ -1612,7 +1610,6 @@ void QBtSession::saveFastResumeData() {
qDebug("Saving fast resume data..."); qDebug("Saving fast resume data...");
// Stop listening for alerts // Stop listening for alerts
resumeDataTimer.stop(); resumeDataTimer.stop();
timerAlerts->stop();
int num_resume_data = 0; int num_resume_data = 0;
// Pause session // Pause session
s->pause(); s->pause();
@ -1637,17 +1634,16 @@ void QBtSession::saveFastResumeData() {
} catch(libtorrent::invalid_handle&) {} } catch(libtorrent::invalid_handle&) {}
} }
while (num_resume_data > 0) { while (num_resume_data > 0) {
alert const* a = s->wait_for_alert(seconds(30)); std::deque<alert*> alerts;
if (a == 0) { m_alertDispatcher->getPendingAlerts(alerts);
std::cerr << " aborting with " << num_resume_data << " outstanding "
"torrents to save resume data for" << std::endl; for (std::deque<alert*>::const_iterator i = alerts.begin(), end = alerts.end(); i != end; ++i)
break; {
} alert const* a = *i;
// Saving fastresume data can fail // Saving fastresume data can fail
save_resume_data_failed_alert const* rda = dynamic_cast<save_resume_data_failed_alert const*>(a); save_resume_data_failed_alert const* rda = dynamic_cast<save_resume_data_failed_alert const*>(a);
if (rda) { if (rda) {
--num_resume_data; --num_resume_data;
s->pop_alert();
try { try {
// Remove torrent from session // Remove torrent from session
if (rda->handle.is_valid()) if (rda->handle.is_valid())
@ -1657,7 +1653,6 @@ void QBtSession::saveFastResumeData() {
} }
save_resume_data_alert const* rd = dynamic_cast<save_resume_data_alert const*>(a); save_resume_data_alert const* rd = dynamic_cast<save_resume_data_alert const*>(a);
if (!rd) { if (!rd) {
s->pop_alert();
continue; continue;
} }
// Saving fast resume data was successful // Saving fast resume data was successful
@ -1681,8 +1676,10 @@ void QBtSession::saveFastResumeData() {
} }
// Remove torrent from session // Remove torrent from session
s->remove_torrent(rd->handle); s->remove_torrent(rd->handle);
s->pop_alert();
} catch(libtorrent::invalid_handle&) {} } catch(libtorrent::invalid_handle&) {}
delete a;
}
} }
} }
@ -2107,11 +2104,12 @@ void QBtSession::autoRunExternalProgram(const QTorrentHandle &h) {
} }
void QBtSession::sendNotificationEmail(const QTorrentHandle &h) { void QBtSession::sendNotificationEmail(const QTorrentHandle &h) {
libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters);
// Prepare mail content // Prepare mail content
QString content = tr("Torrent name: %1").arg(h.name()) + "\n"; QString content = tr("Torrent name: %1").arg(h.name()) + "\n";
content += tr("Torrent size: %1").arg(misc::friendlyUnit(h.actual_size())) + "\n"; content += tr("Torrent size: %1").arg(misc::friendlyUnit(status.total_wanted)) + "\n";
content += tr("Save path: %1").arg(TorrentPersistentData::getSavePath(h.hash())) + "\n\n"; content += tr("Save path: %1").arg(TorrentPersistentData::getSavePath(h.hash())) + "\n\n";
content += tr("The torrent was downloaded in %1.", "The torrent was downloaded in 1 hour and 20 seconds").arg(misc::userFriendlyDuration(h.active_time())) + "\n\n\n"; content += tr("The torrent was downloaded in %1.", "The torrent was downloaded in 1 hour and 20 seconds").arg(misc::userFriendlyDuration(status.active_time)) + "\n\n\n";
content += tr("Thank you for using qBittorrent.") + "\n"; content += tr("Thank you for using qBittorrent.") + "\n";
// Send the notification email // Send the notification email
Smtp *sender = new Smtp(this); Smtp *sender = new Smtp(this);
@ -2120,11 +2118,20 @@ void QBtSession::sendNotificationEmail(const QTorrentHandle &h) {
// Read alerts sent by the Bittorrent session // Read alerts sent by the Bittorrent session
void QBtSession::readAlerts() { void QBtSession::readAlerts() {
// look at session alerts and display some infos
std::auto_ptr<alert> a = s->pop_alert(); typedef std::deque<alert*> alerts_t;
while (a.get()) { alerts_t alerts;
m_alertDispatcher->getPendingAlertsNoWait(alerts);
for (alerts_t::const_iterator i = alerts.begin(), end = alerts.end(); i != end; ++i) {
handleAlert(*i);
delete *i;
}
}
void QBtSession::handleAlert(libtorrent::alert* a) {
try { try {
if (torrent_finished_alert* p = dynamic_cast<torrent_finished_alert*>(a.get())) { if (torrent_finished_alert* p = dynamic_cast<torrent_finished_alert*>(a)) {
QTorrentHandle h(p->handle); QTorrentHandle h(p->handle);
if (h.is_valid()) { if (h.is_valid()) {
const QString hash = h.hash(); const QString hash = h.hash();
@ -2233,7 +2240,7 @@ void QBtSession::readAlerts() {
} }
} }
} }
else if (save_resume_data_alert* p = dynamic_cast<save_resume_data_alert*>(a.get())) { else if (save_resume_data_alert* p = dynamic_cast<save_resume_data_alert*>(a)) {
const QDir torrentBackup(fsutils::BTBackupLocation()); const QDir torrentBackup(fsutils::BTBackupLocation());
const QTorrentHandle h(p->handle); const QTorrentHandle h(p->handle);
if (h.is_valid() && p->resume_data) { if (h.is_valid() && p->resume_data) {
@ -2251,7 +2258,7 @@ void QBtSession::readAlerts() {
} }
} }
} }
else if (file_renamed_alert* p = dynamic_cast<file_renamed_alert*>(a.get())) { else if (file_renamed_alert* p = dynamic_cast<file_renamed_alert*>(a)) {
QTorrentHandle h(p->handle); QTorrentHandle h(p->handle);
if (h.is_valid()) { if (h.is_valid()) {
if (h.num_files() > 1) { if (h.num_files() > 1) {
@ -2274,7 +2281,7 @@ void QBtSession::readAlerts() {
} }
} }
} }
else if (torrent_deleted_alert* p = dynamic_cast<torrent_deleted_alert*>(a.get())) { else if (torrent_deleted_alert* p = dynamic_cast<torrent_deleted_alert*>(a)) {
qDebug("A torrent was deleted from the hard disk, attempting to remove the root folder too..."); qDebug("A torrent was deleted from the hard disk, attempting to remove the root folder too...");
QString hash = misc::toQString(p->info_hash); QString hash = misc::toQString(p->info_hash);
if (!hash.isEmpty()) { if (!hash.isEmpty()) {
@ -2296,7 +2303,7 @@ void QBtSession::readAlerts() {
} }
} }
} }
else if (storage_moved_alert* p = dynamic_cast<storage_moved_alert*>(a.get())) { else if (storage_moved_alert* p = dynamic_cast<storage_moved_alert*>(a)) {
QTorrentHandle h(p->handle); QTorrentHandle h(p->handle);
if (h.is_valid()) { if (h.is_valid()) {
// Attempt to remove old folder if empty // Attempt to remove old folder if empty
@ -2316,7 +2323,7 @@ void QBtSession::readAlerts() {
//h.force_recheck(); //h.force_recheck();
} }
} }
else if (metadata_received_alert* p = dynamic_cast<metadata_received_alert*>(a.get())) { else if (metadata_received_alert* p = dynamic_cast<metadata_received_alert*>(a)) {
QTorrentHandle h(p->handle); QTorrentHandle h(p->handle);
Preferences pref; Preferences pref;
if (h.is_valid()) { if (h.is_valid()) {
@ -2365,7 +2372,7 @@ void QBtSession::readAlerts() {
} }
} }
else if (file_error_alert* p = dynamic_cast<file_error_alert*>(a.get())) { else if (file_error_alert* p = dynamic_cast<file_error_alert*>(a)) {
QTorrentHandle h(p->handle); QTorrentHandle h(p->handle);
if (h.is_valid()) { if (h.is_valid()) {
h.pause(); h.pause();
@ -2379,7 +2386,7 @@ void QBtSession::readAlerts() {
} }
} }
} }
else if (file_completed_alert* p = dynamic_cast<file_completed_alert*>(a.get())) { else if (file_completed_alert* p = dynamic_cast<file_completed_alert*>(a)) {
QTorrentHandle h(p->handle); QTorrentHandle h(p->handle);
qDebug("A file completed download in torrent %s", qPrintable(h.name())); qDebug("A file completed download in torrent %s", qPrintable(h.name()));
if (appendqBExtension) { if (appendqBExtension) {
@ -2393,7 +2400,7 @@ void QBtSession::readAlerts() {
} }
} }
} }
else if (torrent_paused_alert* p = dynamic_cast<torrent_paused_alert*>(a.get())) { else if (torrent_paused_alert* p = dynamic_cast<torrent_paused_alert*>(a)) {
if (p->handle.is_valid()) { if (p->handle.is_valid()) {
QTorrentHandle h(p->handle); QTorrentHandle h(p->handle);
if (!HiddenData::hasData(h.hash())) { if (!HiddenData::hasData(h.hash())) {
@ -2403,7 +2410,7 @@ void QBtSession::readAlerts() {
} }
} }
} }
else if (tracker_error_alert* p = dynamic_cast<tracker_error_alert*>(a.get())) { else if (tracker_error_alert* p = dynamic_cast<tracker_error_alert*>(a)) {
// Level: fatal // Level: fatal
QTorrentHandle h(p->handle); QTorrentHandle h(p->handle);
if (h.is_valid()) { if (h.is_valid()) {
@ -2421,7 +2428,7 @@ void QBtSession::readAlerts() {
} }
} }
} }
else if (tracker_reply_alert* p = dynamic_cast<tracker_reply_alert*>(a.get())) { else if (tracker_reply_alert* p = dynamic_cast<tracker_reply_alert*>(a)) {
const QTorrentHandle h(p->handle); const QTorrentHandle h(p->handle);
if (h.is_valid()) { if (h.is_valid()) {
qDebug("Received a tracker reply from %s (Num_peers=%d)", p->url.c_str(), p->num_peers); qDebug("Received a tracker reply from %s (Num_peers=%d)", p->url.c_str(), p->num_peers);
@ -2435,7 +2442,7 @@ void QBtSession::readAlerts() {
trackersInfos[h.hash()] = trackers_data; trackersInfos[h.hash()] = trackers_data;
} }
} }
else if (tracker_warning_alert* p = dynamic_cast<tracker_warning_alert*>(a.get())) { else if (tracker_warning_alert* p = dynamic_cast<tracker_warning_alert*>(a)) {
const QTorrentHandle h(p->handle); const QTorrentHandle h(p->handle);
if (h.is_valid()) { if (h.is_valid()) {
// Connection was successful now but there is a warning message // Connection was successful now but there is a warning message
@ -2448,16 +2455,16 @@ void QBtSession::readAlerts() {
qDebug("Received a tracker warning from %s: %s", p->url.c_str(), p->msg.c_str()); qDebug("Received a tracker warning from %s: %s", p->url.c_str(), p->msg.c_str());
} }
} }
else if (portmap_error_alert* p = dynamic_cast<portmap_error_alert*>(a.get())) { else if (portmap_error_alert* p = dynamic_cast<portmap_error_alert*>(a)) {
addConsoleMessage(tr("UPnP/NAT-PMP: Port mapping failure, message: %1").arg(misc::toQStringU(p->message())), "red"); addConsoleMessage(tr("UPnP/NAT-PMP: Port mapping failure, message: %1").arg(misc::toQStringU(p->message())), "red");
//emit UPnPError(QString(p->msg().c_str())); //emit UPnPError(QString(p->msg().c_str()));
} }
else if (portmap_alert* p = dynamic_cast<portmap_alert*>(a.get())) { else if (portmap_alert* p = dynamic_cast<portmap_alert*>(a)) {
qDebug("UPnP Success, msg: %s", p->message().c_str()); qDebug("UPnP Success, msg: %s", p->message().c_str());
addConsoleMessage(tr("UPnP/NAT-PMP: Port mapping successful, message: %1").arg(misc::toQStringU(p->message())), "blue"); addConsoleMessage(tr("UPnP/NAT-PMP: Port mapping successful, message: %1").arg(misc::toQStringU(p->message())), "blue");
//emit UPnPSuccess(QString(p->msg().c_str())); //emit UPnPSuccess(QString(p->msg().c_str()));
} }
else if (peer_blocked_alert* p = dynamic_cast<peer_blocked_alert*>(a.get())) { else if (peer_blocked_alert* p = dynamic_cast<peer_blocked_alert*>(a)) {
boost::system::error_code ec; boost::system::error_code ec;
string ip = p->ip.to_string(ec); string ip = p->ip.to_string(ec);
if (!ec) { if (!ec) {
@ -2465,7 +2472,7 @@ void QBtSession::readAlerts() {
//emit peerBlocked(QString::fromLatin1(ip.c_str())); //emit peerBlocked(QString::fromLatin1(ip.c_str()));
} }
} }
else if (peer_ban_alert* p = dynamic_cast<peer_ban_alert*>(a.get())) { else if (peer_ban_alert* p = dynamic_cast<peer_ban_alert*>(a)) {
boost::system::error_code ec; boost::system::error_code ec;
string ip = p->ip.address().to_string(ec); string ip = p->ip.address().to_string(ec);
if (!ec) { if (!ec) {
@ -2473,7 +2480,7 @@ void QBtSession::readAlerts() {
//emit peerBlocked(QString::fromLatin1(ip.c_str())); //emit peerBlocked(QString::fromLatin1(ip.c_str()));
} }
} }
else if (fastresume_rejected_alert* p = dynamic_cast<fastresume_rejected_alert*>(a.get())) { else if (fastresume_rejected_alert* p = dynamic_cast<fastresume_rejected_alert*>(a)) {
QTorrentHandle h(p->handle); QTorrentHandle h(p->handle);
if (h.is_valid()) { if (h.is_valid()) {
qDebug("/!\\ Fast resume failed for %s, reason: %s", qPrintable(h.name()), p->message().c_str()); qDebug("/!\\ Fast resume failed for %s, reason: %s", qPrintable(h.name()), p->message().c_str());
@ -2489,11 +2496,11 @@ void QBtSession::readAlerts() {
} }
} }
} }
else if (url_seed_alert* p = dynamic_cast<url_seed_alert*>(a.get())) { else if (url_seed_alert* p = dynamic_cast<url_seed_alert*>(a)) {
addConsoleMessage(tr("Url seed lookup failed for url: %1, message: %2").arg(misc::toQString(p->url)).arg(misc::toQStringU(p->message())), QString::fromUtf8("red")); addConsoleMessage(tr("Url seed lookup failed for url: %1, message: %2").arg(misc::toQString(p->url)).arg(misc::toQStringU(p->message())), QString::fromUtf8("red"));
//emit urlSeedProblem(QString::fromUtf8(p->url.c_str()), QString::fromUtf8(p->msg().c_str())); //emit urlSeedProblem(QString::fromUtf8(p->url.c_str()), QString::fromUtf8(p->msg().c_str()));
} }
else if (listen_succeeded_alert *p = dynamic_cast<listen_succeeded_alert*>(a.get())) { else if (listen_succeeded_alert *p = dynamic_cast<listen_succeeded_alert*>(a)) {
boost::system::error_code ec; boost::system::error_code ec;
QString proto = "TCP"; QString proto = "TCP";
#if LIBTORRENT_VERSION_NUM >= 10000 #if LIBTORRENT_VERSION_NUM >= 10000
@ -2515,7 +2522,7 @@ void QBtSession::readAlerts() {
it->force_reannounce(); it->force_reannounce();
} }
} }
else if (listen_failed_alert *p = dynamic_cast<listen_failed_alert*>(a.get())) { else if (listen_failed_alert *p = dynamic_cast<listen_failed_alert*>(a)) {
boost::system::error_code ec; boost::system::error_code ec;
QString proto = "TCP"; QString proto = "TCP";
#if LIBTORRENT_VERSION_NUM >= 10000 #if LIBTORRENT_VERSION_NUM >= 10000
@ -2533,7 +2540,7 @@ void QBtSession::readAlerts() {
qDebug() << "Failed listening on " << proto << p->endpoint.address().to_string(ec).c_str() << "/" << p->endpoint.port(); qDebug() << "Failed listening on " << proto << p->endpoint.address().to_string(ec).c_str() << "/" << p->endpoint.port();
addConsoleMessage(tr("qBittorrent failed listening on interface %1 port: %2/%3. Reason: %4", "e.g: qBittorrent failed listening on interface 192.168.0.1 port: TCP/6881. Reason: already in use").arg(p->endpoint.address().to_string(ec).c_str()).arg(proto).arg(QString::number(p->endpoint.port())).arg(misc::toQStringU(p->error.message())), "red"); addConsoleMessage(tr("qBittorrent failed listening on interface %1 port: %2/%3. Reason: %4", "e.g: qBittorrent failed listening on interface 192.168.0.1 port: TCP/6881. Reason: already in use").arg(p->endpoint.address().to_string(ec).c_str()).arg(proto).arg(QString::number(p->endpoint.port())).arg(misc::toQStringU(p->error.message())), "red");
} }
else if (torrent_checked_alert* p = dynamic_cast<torrent_checked_alert*>(a.get())) { else if (torrent_checked_alert* p = dynamic_cast<torrent_checked_alert*>(a)) {
QTorrentHandle h(p->handle); QTorrentHandle h(p->handle);
if (h.is_valid()) { if (h.is_valid()) {
const QString hash = h.hash(); const QString hash = h.hash();
@ -2559,16 +2566,19 @@ void QBtSession::readAlerts() {
} }
} }
} }
else if (external_ip_alert *p = dynamic_cast<external_ip_alert*>(a.get())) { else if (external_ip_alert *p = dynamic_cast<external_ip_alert*>(a)) {
boost::system::error_code ec; boost::system::error_code ec;
addConsoleMessage(tr("External IP: %1", "e.g. External IP: 192.168.0.1").arg(p->external_address.to_string(ec).c_str()), "blue"); addConsoleMessage(tr("External IP: %1", "e.g. External IP: 192.168.0.1").arg(p->external_address.to_string(ec).c_str()), "blue");
} }
else if (state_update_alert *p = dynamic_cast<state_update_alert *>(a)) {
emit stateUpdate(p->status);
}
else if (stats_alert *p = dynamic_cast<stats_alert *>(a)) {
emit statsReceived(*p);
}
} catch (const std::exception& e) { } catch (const std::exception& e) {
qWarning() << "Caught exception in readAlerts(): " << e.what(); qWarning() << "Caught exception in readAlerts(): " << e.what();
} }
a = s->pop_alert();
}
} }
void QBtSession::recheckTorrent(const QString &hash) { void QBtSession::recheckTorrent(const QString &hash) {
@ -2804,17 +2814,21 @@ void QBtSession::drop()
} }
} }
qlonglong QBtSession::getETA(const QString &hash) const qlonglong QBtSession::getETA(const QString &hash, const libtorrent::torrent_status &status) const
{ {
return m_speedMonitor->getETA(hash); return m_speedMonitor->getETA(hash, status);
} }
quint64 QBtSession::getAlltimeDL() const { quint64 QBtSession::getAlltimeDL() const {
return m_speedMonitor->getAlltimeDL(); return m_torrentStatistics->getAlltimeDL();
} }
quint64 QBtSession::getAlltimeUL() const { quint64 QBtSession::getAlltimeUL() const {
return m_speedMonitor->getAlltimeUL(); return m_torrentStatistics->getAlltimeUL();
}
void QBtSession::postTorrentUpdate() {
s->post_torrent_updates();
} }
void QBtSession::handleIPFilterParsed(int ruleCount) void QBtSession::handleIPFilterParsed(int ruleCount)

14
src/qtlibtorrent/qbtsession.h

@ -47,10 +47,12 @@
#include <libtorrent/version.hpp> #include <libtorrent/version.hpp>
#include <libtorrent/session.hpp> #include <libtorrent/session.hpp>
#include <libtorrent/ip_filter.hpp> #include <libtorrent/ip_filter.hpp>
#include <libtorrent/alert_types.hpp>
#include "qtracker.h" #include "qtracker.h"
#include "qtorrenthandle.h" #include "qtorrenthandle.h"
#include "trackerinfos.h" #include "trackerinfos.h"
#include "alertdispatcher.h"
#define MAX_SAMPLES 20 #define MAX_SAMPLES 20
@ -60,6 +62,7 @@ class HttpServer;
class BandwidthScheduler; class BandwidthScheduler;
class ScanFoldersModel; class ScanFoldersModel;
class TorrentSpeedMonitor; class TorrentSpeedMonitor;
class TorrentStatistics;
class DNSUpdater; class DNSUpdater;
const int MAX_LOG_MESSAGES = 1000; const int MAX_LOG_MESSAGES = 1000;
@ -92,7 +95,7 @@ public:
qreal getPayloadUploadRate() const; qreal getPayloadUploadRate() const;
libtorrent::session_status getSessionStatus() const; libtorrent::session_status getSessionStatus() const;
int getListenPort() const; int getListenPort() const;
qreal getRealRatio(const QString& hash) const; qreal getRealRatio(const libtorrent::torrent_status &status) const;
QHash<QString, TrackerInfos> getTrackersInfo(const QString &hash) const; QHash<QString, TrackerInfos> getTrackersInfo(const QString &hash) const;
bool hasActiveTorrents() const; bool hasActiveTorrents() const;
bool hasDownloadingTorrents() const; bool hasDownloadingTorrents() const;
@ -110,6 +113,7 @@ public:
inline bool isQueueingEnabled() const { return queueingEnabled; } inline bool isQueueingEnabled() const { return queueingEnabled; }
quint64 getAlltimeDL() const; quint64 getAlltimeDL() const;
quint64 getAlltimeUL() const; quint64 getAlltimeUL() const;
void postTorrentUpdate();
public slots: public slots:
QTorrentHandle addTorrent(QString path, bool fromScanDir = false, QString from_url = QString(), bool resumed = false); QTorrentHandle addTorrent(QString path, bool fromScanDir = false, QString from_url = QString(), bool resumed = false);
@ -121,7 +125,7 @@ public slots:
void startUpTorrents(); void startUpTorrents();
void recheckTorrent(const QString &hash); void recheckTorrent(const QString &hash);
void useAlternativeSpeedsLimit(bool alternative); void useAlternativeSpeedsLimit(bool alternative);
qlonglong getETA(const QString& hash) const; qlonglong getETA(const QString& hash, const libtorrent::torrent_status &status) const;
/* Needed by Web UI */ /* Needed by Web UI */
void pauseAllTorrents(); void pauseAllTorrents();
void pauseTorrent(const QString &hash); void pauseTorrent(const QString &hash);
@ -190,6 +194,7 @@ private:
void updateRatioTimer(); void updateRatioTimer();
void recoverPersistentData(const QString &hash, const std::vector<char> &buf); void recoverPersistentData(const QString &hash, const std::vector<char> &buf);
void backupPersistentData(const QString &hash, boost::shared_ptr<libtorrent::entry> data); void backupPersistentData(const QString &hash, boost::shared_ptr<libtorrent::entry> data);
void handleAlert(libtorrent::alert* a);
private slots: private slots:
void addTorrentsFromScanFolder(QStringList&); void addTorrentsFromScanFolder(QStringList&);
@ -230,11 +235,12 @@ signals:
void recursiveTorrentDownloadPossible(const QTorrentHandle &h); void recursiveTorrentDownloadPossible(const QTorrentHandle &h);
void ipFilterParsed(bool error, int ruleCount); void ipFilterParsed(bool error, int ruleCount);
void metadataReceivedHidden(const QTorrentHandle &h); void metadataReceivedHidden(const QTorrentHandle &h);
void stateUpdate(const std::vector<libtorrent::torrent_status> &statuses);
void statsReceived(const libtorrent::stats_alert&);
private: private:
// Bittorrent // Bittorrent
libtorrent::session *s; libtorrent::session *s;
QPointer<QTimer> timerAlerts;
QPointer<BandwidthScheduler> bd_scheduler; QPointer<BandwidthScheduler> bd_scheduler;
QMap<QUrl, QPair<QString, QString> > savepathLabel_fromurl; // Use QMap for compatibility with Qt < 4.7: qHash(QUrl) QMap<QUrl, QPair<QString, QString> > savepathLabel_fromurl; // Use QMap for compatibility with Qt < 4.7: qHash(QUrl)
QHash<QString, QHash<QString, TrackerInfos> > trackersInfos; QHash<QString, QHash<QString, TrackerInfos> > trackersInfos;
@ -287,6 +293,8 @@ private:
#endif #endif
// DynDNS // DynDNS
DNSUpdater *m_dynDNSUpdater; DNSUpdater *m_dynDNSUpdater;
QAlertDispatcher* m_alertDispatcher;
TorrentStatistics* m_torrentStatistics;
}; };
#endif #endif

8
src/qtlibtorrent/qtlibtorrent.pri

@ -5,11 +5,15 @@ HEADERS += $$PWD/qbtsession.h \
$$PWD/bandwidthscheduler.h \ $$PWD/bandwidthscheduler.h \
$$PWD/trackerinfos.h \ $$PWD/trackerinfos.h \
$$PWD/torrentspeedmonitor.h \ $$PWD/torrentspeedmonitor.h \
$$PWD/filterparserthread.h $$PWD/filterparserthread.h \
$$PWD/alertdispatcher.h \
$$PWD/torrentstatistics.h
SOURCES += $$PWD/qbtsession.cpp \ SOURCES += $$PWD/qbtsession.cpp \
$$PWD/qtorrenthandle.cpp \ $$PWD/qtorrenthandle.cpp \
$$PWD/torrentspeedmonitor.cpp $$PWD/torrentspeedmonitor.cpp \
$$PWD/alertdispatcher.cpp \
$$PWD/torrentstatistics.cpp
!contains(DEFINES, DISABLE_GUI) { !contains(DEFINES, DISABLE_GUI) {
HEADERS += $$PWD/torrentmodel.h \ HEADERS += $$PWD/torrentmodel.h \

181
src/qtlibtorrent/qtorrenthandle.cpp

@ -33,6 +33,7 @@
#include <QFile> #include <QFile>
#include <QDir> #include <QDir>
#include <QByteArray> #include <QByteArray>
#include <QDateTime>
#include <math.h> #include <math.h>
#include "fs_utils.h" #include "fs_utils.h"
#include "misc.h" #include "misc.h"
@ -87,7 +88,7 @@ QString QTorrentHandle::name() const {
#if LIBTORRENT_VERSION_NUM < 10000 #if LIBTORRENT_VERSION_NUM < 10000
name = misc::toQStringU(torrent_handle::name()); name = misc::toQStringU(torrent_handle::name());
#else #else
name = misc::toQStringU(torrent_handle::status(torrent_handle::query_name).name); name = misc::toQStringU(status(query_name).name);
#endif #endif
} }
return name; return name;
@ -102,41 +103,16 @@ QString QTorrentHandle::creation_date() const {
return t ? misc::toQString(*t) : ""; return t ? misc::toQString(*t) : "";
} }
QString QTorrentHandle::next_announce() const {
return misc::userFriendlyDuration(torrent_handle::status(0x0).next_announce.total_seconds());
}
qlonglong QTorrentHandle::next_announce_s() const {
return torrent_handle::status(0x0).next_announce.total_seconds();
}
float QTorrentHandle::progress() const {
torrent_status st = torrent_handle::status(query_accurate_download_counters);
if (!st.total_wanted)
return 0.;
if (st.total_wanted_done == st.total_wanted)
return 1.;
float progress = (float) st.total_wanted_done / (float) st.total_wanted;
Q_ASSERT(progress >= 0. && progress <= 1.);
return progress;
}
bitfield QTorrentHandle::pieces() const {
return torrent_handle::status(torrent_handle::query_pieces).pieces;
}
QString QTorrentHandle::current_tracker() const { QString QTorrentHandle::current_tracker() const {
return misc::toQString(torrent_handle::status(0x0).current_tracker); return misc::toQString(status(0x0).current_tracker);
} }
bool QTorrentHandle::is_paused() const { bool QTorrentHandle::is_paused() const {
torrent_status st = torrent_handle::status(0x0); return is_paused(status(0x0));
return st.paused && !st.auto_managed;
} }
bool QTorrentHandle::is_queued() const { bool QTorrentHandle::is_queued() const {
torrent_status st = torrent_handle::status(0x0); return is_queued(status(0x0));
return st.paused && st.auto_managed;
} }
size_type QTorrentHandle::total_size() const { size_type QTorrentHandle::total_size() const {
@ -189,38 +165,6 @@ bool QTorrentHandle::first_last_piece_first() const {
&& (torrent_handle::piece_priority(extremities.second) == 7); && (torrent_handle::piece_priority(extremities.second) == 7);
} }
size_type QTorrentHandle::total_wanted_done() const {
return torrent_handle::status(query_accurate_download_counters).total_wanted_done;
}
size_type QTorrentHandle::total_wanted() const {
return torrent_handle::status(0x0).total_wanted;
}
qreal QTorrentHandle::download_payload_rate() const {
return torrent_handle::status(0x0).download_payload_rate;
}
qreal QTorrentHandle::upload_payload_rate() const {
return torrent_handle::status(0x0).upload_payload_rate;
}
int QTorrentHandle::num_peers() const {
return torrent_handle::status(0x0).num_peers;
}
int QTorrentHandle::num_seeds() const {
return torrent_handle::status(0x0).num_seeds;
}
int QTorrentHandle::num_complete() const {
return torrent_handle::status(0x0).num_complete;
}
int QTorrentHandle::num_incomplete() const {
return torrent_handle::status(0x0).num_incomplete;
}
QString QTorrentHandle::save_path() const { QString QTorrentHandle::save_path() const {
#if LIBTORRENT_VERSION_NUM < 10000 #if LIBTORRENT_VERSION_NUM < 10000
return fsutils::fromNativePath(misc::toQStringU(torrent_handle::save_path())); return fsutils::fromNativePath(misc::toQStringU(torrent_handle::save_path()));
@ -260,7 +204,7 @@ QStringList QTorrentHandle::url_seeds() const {
// get the size of the torrent without the filtered files // get the size of the torrent without the filtered files
size_type QTorrentHandle::actual_size() const { size_type QTorrentHandle::actual_size() const {
return torrent_handle::status(query_accurate_download_counters).total_wanted; return status(query_accurate_download_counters).total_wanted;
} }
bool QTorrentHandle::has_filtered_pieces() const { bool QTorrentHandle::has_filtered_pieces() const {
@ -317,7 +261,7 @@ QString QTorrentHandle::orig_filepath_at(unsigned int index) const {
} }
torrent_status::state_t QTorrentHandle::state() const { torrent_status::state_t QTorrentHandle::state() const {
return torrent_handle::status(0x0).state; return status(0x0).state;
} }
QString QTorrentHandle::creator() const { QString QTorrentHandle::creator() const {
@ -336,37 +280,8 @@ QString QTorrentHandle::comment() const {
#endif #endif
} }
size_type QTorrentHandle::total_failed_bytes() const {
return torrent_handle::status(0x0).total_failed_bytes;
}
size_type QTorrentHandle::total_redundant_bytes() const {
return torrent_handle::status(0x0).total_redundant_bytes;
}
bool QTorrentHandle::is_checking() const { bool QTorrentHandle::is_checking() const {
torrent_status st = torrent_handle::status(0x0); return is_checking(status(0x0));
return st.state == torrent_status::checking_files || st.state == torrent_status::checking_resume_data;
}
size_type QTorrentHandle::total_done() const {
return torrent_handle::status(query_accurate_download_counters).total_done;
}
size_type QTorrentHandle::all_time_download() const {
return torrent_handle::status(0x0).all_time_download;
}
size_type QTorrentHandle::all_time_upload() const {
return torrent_handle::status(0x0).all_time_upload;
}
size_type QTorrentHandle::total_payload_download() const {
return torrent_handle::status(0x0).total_payload_download;
}
size_type QTorrentHandle::total_payload_upload() const {
return torrent_handle::status(0x0).total_payload_upload;
} }
// Return a list of absolute paths corresponding // Return a list of absolute paths corresponding
@ -408,44 +323,17 @@ int QTorrentHandle::queue_position() const {
return torrent_handle::queue_position()+1; return torrent_handle::queue_position()+1;
} }
int QTorrentHandle::num_uploads() const {
return torrent_handle::status(0x0).num_uploads;
}
bool QTorrentHandle::is_seed() const { bool QTorrentHandle::is_seed() const {
// Affected by bug http://code.rasterbar.com/libtorrent/ticket/402 // Affected by bug http://code.rasterbar.com/libtorrent/ticket/402
//return torrent_handle::is_seed(); //return torrent_handle::is_seed();
// May suffer from approximation problems // May suffer from approximation problems
//return (progress() == 1.); //return (progress() == 1.);
// This looks safe // This looks safe
torrent_status::state_t st = state(); return is_seed(status(0x0));
return (st == torrent_status::finished || st == torrent_status::seeding);
}
bool QTorrentHandle::is_auto_managed() const {
torrent_status status = torrent_handle::status(0x0);
return status.auto_managed;
} }
bool QTorrentHandle::is_sequential_download() const { bool QTorrentHandle::is_sequential_download() const {
torrent_status status = torrent_handle::status(0x0); return status(0x0).sequential_download;
return status.sequential_download;
}
qlonglong QTorrentHandle::active_time() const {
return torrent_handle::status(0x0).active_time;
}
qlonglong QTorrentHandle::seeding_time() const {
return torrent_handle::status(0x0).seeding_time;
}
int QTorrentHandle::num_connections() const {
return torrent_handle::status(0x0).num_connections;
}
int QTorrentHandle::connections_limit() const {
return torrent_handle::status(0x0).connections_limit;
} }
bool QTorrentHandle::priv() const { bool QTorrentHandle::priv() const {
@ -482,12 +370,11 @@ QString QTorrentHandle::root_path() const
} }
bool QTorrentHandle::has_error() const { bool QTorrentHandle::has_error() const {
torrent_status st = torrent_handle::status(0x0); return has_error(status(0x0));
return st.paused && !st.error.empty();
} }
QString QTorrentHandle::error() const { QString QTorrentHandle::error() const {
return misc::toQString(torrent_handle::status(0x0).error); return misc::toQString(status(0x0).error);
} }
void QTorrentHandle::downloading_pieces(bitfield &bf) const { void QTorrentHandle::downloading_pieces(bitfield &bf) const {
@ -503,11 +390,7 @@ void QTorrentHandle::downloading_pieces(bitfield &bf) const {
} }
bool QTorrentHandle::has_metadata() const { bool QTorrentHandle::has_metadata() const {
return torrent_handle::status(0x0).has_metadata; return status(0x0).has_metadata;
}
float QTorrentHandle::distributed_copies() const {
return torrent_handle::status(query_distributed_copies).distributed_copies;
} }
void QTorrentHandle::file_progress(std::vector<size_type>& fp) const { void QTorrentHandle::file_progress(std::vector<size_type>& fp) const {
@ -732,3 +615,41 @@ void QTorrentHandle::rename_file(int index, const QString& name) const {
bool QTorrentHandle::operator ==(const QTorrentHandle& new_h) const { bool QTorrentHandle::operator ==(const QTorrentHandle& new_h) const {
return info_hash() == new_h.info_hash(); return info_hash() == new_h.info_hash();
} }
bool QTorrentHandle::is_paused(const libtorrent::torrent_status &status)
{
return status.paused && !status.auto_managed;
}
bool QTorrentHandle::is_queued(const libtorrent::torrent_status &status)
{
return status.paused && status.auto_managed;
}
bool QTorrentHandle::is_seed(const libtorrent::torrent_status &status)
{
return status.state == torrent_status::finished
|| status.state == torrent_status::seeding;
}
bool QTorrentHandle::is_checking(const libtorrent::torrent_status &status)
{
return status.state == torrent_status::checking_files
|| status.state == torrent_status::checking_resume_data;
}
bool QTorrentHandle::has_error(const libtorrent::torrent_status &status)
{
return status.paused && !status.error.empty();
}
float QTorrentHandle::progress(const libtorrent::torrent_status &status)
{
if (!status.total_wanted)
return 0.;
if (status.total_wanted_done == status.total_wanted)
return 1.;
float progress = (float) status.total_wanted_done / (float) status.total_wanted;
Q_ASSERT(progress >= 0.f && progress <= 1.f);
return progress;
}

33
src/qtlibtorrent/qtorrenthandle.h

@ -59,24 +59,12 @@ public:
// //
QString hash() const; QString hash() const;
QString name() const; QString name() const;
float progress() const;
libtorrent::bitfield pieces() const;
QString current_tracker() const; QString current_tracker() const;
bool is_paused() const; bool is_paused() const;
bool has_filtered_pieces() const; bool has_filtered_pieces() const;
libtorrent::size_type total_size() const; libtorrent::size_type total_size() const;
libtorrent::size_type piece_length() const; libtorrent::size_type piece_length() const;
int num_pieces() const; int num_pieces() const;
libtorrent::size_type total_wanted_done() const;
libtorrent::size_type total_wanted() const;
qreal download_payload_rate() const;
qreal upload_payload_rate() const;
int num_connections() const;
int connections_limit() const;
int num_peers() const;
int num_seeds() const;
int num_complete() const;
int num_incomplete() const;
QString save_path() const; QString save_path() const;
QString save_path_parsed() const; QString save_path_parsed() const;
QStringList url_seeds() const; QStringList url_seeds() const;
@ -91,26 +79,13 @@ public:
libtorrent::torrent_status::state_t state() const; libtorrent::torrent_status::state_t state() const;
QString creator() const; QString creator() const;
QString comment() const; QString comment() const;
libtorrent::size_type total_failed_bytes() const;
libtorrent::size_type total_redundant_bytes() const;
libtorrent::size_type total_payload_download() const;
libtorrent::size_type total_payload_upload() const;
libtorrent::size_type all_time_upload() const;
libtorrent::size_type all_time_download() const;
libtorrent::size_type total_done() const;
QStringList absolute_files_path() const; QStringList absolute_files_path() const;
QStringList absolute_files_path_uneeded() const; QStringList absolute_files_path_uneeded() const;
bool has_missing_files() const; bool has_missing_files() const;
int num_uploads() const;
bool is_seed() const; bool is_seed() const;
bool is_checking() const; bool is_checking() const;
bool is_auto_managed() const;
bool is_sequential_download() const; bool is_sequential_download() const;
qlonglong active_time() const;
qlonglong seeding_time() const;
QString creation_date() const; QString creation_date() const;
QString next_announce() const;
qlonglong next_announce_s() const;
bool priv() const; bool priv() const;
bool first_last_piece_first() const; bool first_last_piece_first() const;
QString root_path() const; QString root_path() const;
@ -119,7 +94,6 @@ public:
QString error() const; QString error() const;
void downloading_pieces(libtorrent::bitfield& bf) const; void downloading_pieces(libtorrent::bitfield& bf) const;
bool has_metadata() const; bool has_metadata() const;
float distributed_copies() const;
void file_progress(std::vector<libtorrent::size_type>& fp) const; void file_progress(std::vector<libtorrent::size_type>& fp) const;
// //
@ -142,6 +116,13 @@ public:
// //
bool operator ==(const QTorrentHandle& new_h) const; bool operator ==(const QTorrentHandle& new_h) const;
static bool is_paused(const libtorrent::torrent_status &status);
static bool is_queued(const libtorrent::torrent_status &status);
static bool is_seed(const libtorrent::torrent_status &status);
static bool is_checking(const libtorrent::torrent_status &status);
static bool has_error(const libtorrent::torrent_status &status);
static float progress(const libtorrent::torrent_status &status);
private: private:
void prioritize_first_last_piece(int file_index, bool b) const; void prioritize_first_last_piece(int file_index, bool b) const;

151
src/qtlibtorrent/torrentmodel.cpp

@ -37,8 +37,60 @@
using namespace libtorrent; using namespace libtorrent;
namespace
{
QIcon get_paused_icon()
{
static QIcon cached = QIcon(":/Icons/skin/paused.png");
return cached;
}
QIcon get_queued_icon()
{
static QIcon cached = QIcon(":/Icons/skin/queued.png");
return cached;
}
QIcon get_downloading_icon()
{
static QIcon cached = QIcon(":/Icons/skin/downloading.png");
return cached;
}
QIcon get_stalled_downloading_icon()
{
static QIcon cached = QIcon(":/Icons/skin/stalledDL.png");
return cached;
}
QIcon get_uploading_icon()
{
static QIcon cached = QIcon(":/Icons/skin/uploading.png");
return cached;
}
QIcon get_stalled_uploading_icon()
{
static QIcon cached = QIcon(":/Icons/skin/stalledUP.png");
return cached;
}
QIcon get_checking_icon()
{
static QIcon cached = QIcon(":/Icons/skin/checking.png");
return cached;
}
QIcon get_error_icon()
{
static QIcon cached = QIcon(":/Icons/skin/error.png");
return cached;
}
}
TorrentModelItem::TorrentModelItem(const QTorrentHandle &h) TorrentModelItem::TorrentModelItem(const QTorrentHandle &h)
: m_torrent(h) : m_torrent(h)
, m_lastStatus(h.status(torrent_handle::query_accurate_download_counters))
, m_addedTime(TorrentPersistentData::getAddedDate(h.hash())) , m_addedTime(TorrentPersistentData::getAddedDate(h.hash()))
, m_seedTime(TorrentPersistentData::getSeedDate(h.hash())) , m_seedTime(TorrentPersistentData::getSeedDate(h.hash()))
, m_label(TorrentPersistentData::getLabel(h.hash())) , m_label(TorrentPersistentData::getLabel(h.hash()))
@ -49,75 +101,80 @@ TorrentModelItem::TorrentModelItem(const QTorrentHandle &h)
m_name = h.name(); m_name = h.name();
} }
void TorrentModelItem::refreshStatus(libtorrent::torrent_status const& status)
{
m_lastStatus = status;
}
TorrentModelItem::State TorrentModelItem::state() const TorrentModelItem::State TorrentModelItem::state() const
{ {
try { try {
// Pause or Queued // Pause or Queued
if (m_torrent.is_paused()) { if (m_torrent.is_paused(m_lastStatus)) {
m_icon = QIcon(":/Icons/skin/paused.png"); m_icon = get_paused_icon();
m_fgColor = QColor("red"); m_fgColor = QColor("red");
return m_torrent.is_seed() ? STATE_PAUSED_UP : STATE_PAUSED_DL; return m_torrent.is_seed(m_lastStatus) ? STATE_PAUSED_UP : STATE_PAUSED_DL;
} }
if (m_torrent.is_queued()) { if (m_torrent.is_queued(m_lastStatus)) {
if (m_torrent.state() != torrent_status::queued_for_checking if (m_lastStatus.state != torrent_status::queued_for_checking
&& m_torrent.state() != torrent_status::checking_resume_data && m_lastStatus.state != torrent_status::checking_resume_data
&& m_torrent.state() != torrent_status::checking_files) { && m_lastStatus.state != torrent_status::checking_files) {
m_icon = QIcon(":/Icons/skin/queued.png"); m_icon = get_queued_icon();
m_fgColor = QColor("grey"); m_fgColor = QColor("grey");
return m_torrent.is_seed() ? STATE_QUEUED_UP : STATE_QUEUED_DL; return m_torrent.is_seed(m_lastStatus) ? STATE_QUEUED_UP : STATE_QUEUED_DL;
} }
} }
// Other states // Other states
switch(m_torrent.state()) { switch(m_lastStatus.state) {
case torrent_status::allocating: case torrent_status::allocating:
m_icon = QIcon(":/Icons/skin/stalledDL.png"); m_icon = get_stalled_downloading_icon();
m_fgColor = QColor("grey"); m_fgColor = QColor("grey");
return STATE_ALLOCATING; return STATE_ALLOCATING;
case torrent_status::downloading_metadata: case torrent_status::downloading_metadata:
m_icon = QIcon(":/Icons/skin/downloading.png"); m_icon = get_downloading_icon();
m_fgColor = QColor("green"); m_fgColor = QColor("green");
return STATE_DOWNLOADING_META; return STATE_DOWNLOADING_META;
case torrent_status::downloading: { case torrent_status::downloading: {
if (m_torrent.download_payload_rate() > 0) { if (m_lastStatus.download_payload_rate > 0) {
m_icon = QIcon(":/Icons/skin/downloading.png"); m_icon = get_downloading_icon();
m_fgColor = QColor("green"); m_fgColor = QColor("green");
return STATE_DOWNLOADING; return STATE_DOWNLOADING;
} else { } else {
m_icon = QIcon(":/Icons/skin/stalledDL.png"); m_icon = get_stalled_downloading_icon();
m_fgColor = QColor("grey"); m_fgColor = QColor("grey");
return STATE_STALLED_DL; return STATE_STALLED_DL;
} }
} }
case torrent_status::finished: case torrent_status::finished:
case torrent_status::seeding: case torrent_status::seeding:
if (m_torrent.upload_payload_rate() > 0) { if (m_lastStatus.upload_payload_rate > 0) {
m_icon = QIcon(":/Icons/skin/uploading.png"); m_icon = get_uploading_icon();
m_fgColor = QColor("orange"); m_fgColor = QColor("orange");
return STATE_SEEDING; return STATE_SEEDING;
} else { } else {
m_icon = QIcon(":/Icons/skin/stalledUP.png"); m_icon = get_stalled_uploading_icon();
m_fgColor = QColor("grey"); m_fgColor = QColor("grey");
return STATE_STALLED_UP; return STATE_STALLED_UP;
} }
case torrent_status::queued_for_checking: case torrent_status::queued_for_checking:
m_icon = QIcon(":/Icons/skin/checking.png"); m_icon = get_checking_icon();
m_fgColor = QColor("grey"); m_fgColor = QColor("grey");
return STATE_QUEUED_CHECK; return STATE_QUEUED_CHECK;
case torrent_status::checking_resume_data: case torrent_status::checking_resume_data:
m_icon = QIcon(":/Icons/skin/checking.png"); m_icon = get_checking_icon();
m_fgColor = QColor("grey"); m_fgColor = QColor("grey");
return STATE_QUEUED_FASTCHECK; return STATE_QUEUED_FASTCHECK;
case torrent_status::checking_files: case torrent_status::checking_files:
m_icon = QIcon(":/Icons/skin/checking.png"); m_icon = get_checking_icon();
m_fgColor = QColor("grey"); m_fgColor = QColor("grey");
return m_torrent.is_seed() ? STATE_CHECKING_UP : STATE_CHECKING_DL; return m_torrent.is_seed(m_lastStatus) ? STATE_CHECKING_UP : STATE_CHECKING_DL;
default: default:
m_icon = QIcon(":/Icons/skin/error.png"); m_icon = get_error_icon();
m_fgColor = QColor("red"); m_fgColor = QColor("red");
return STATE_INVALID; return STATE_INVALID;
} }
} catch(invalid_handle&) { } catch(invalid_handle&) {
m_icon = QIcon(":/Icons/skin/error.png"); m_icon = get_error_icon();
m_fgColor = QColor("red"); m_fgColor = QColor("red");
return STATE_INVALID; return STATE_INVALID;
} }
@ -173,28 +230,28 @@ QVariant TorrentModelItem::data(int column, int role) const
return pos; return pos;
} }
case TR_SIZE: case TR_SIZE:
return m_torrent.has_metadata() ? static_cast<qlonglong>(m_torrent.actual_size()) : -1; return m_lastStatus.has_metadata ? static_cast<qlonglong>(m_lastStatus.total_wanted) : -1;
case TR_PROGRESS: case TR_PROGRESS:
return m_torrent.progress(); return m_torrent.progress(m_lastStatus);
case TR_STATUS: case TR_STATUS:
return state(); return state();
case TR_SEEDS: { case TR_SEEDS: {
return (role == Qt::DisplayRole) ? m_torrent.num_seeds() : m_torrent.num_complete(); return (role == Qt::DisplayRole) ? m_lastStatus.num_seeds : m_lastStatus.num_complete;
} }
case TR_PEERS: { case TR_PEERS: {
return (role == Qt::DisplayRole) ? (m_torrent.num_peers()-m_torrent.num_seeds()) : m_torrent.num_incomplete(); return (role == Qt::DisplayRole) ? (m_lastStatus.num_peers-m_lastStatus.num_seeds) : m_lastStatus.num_incomplete;
} }
case TR_DLSPEED: case TR_DLSPEED:
return m_torrent.download_payload_rate(); return m_lastStatus.download_payload_rate;
case TR_UPSPEED: case TR_UPSPEED:
return m_torrent.upload_payload_rate(); return m_lastStatus.upload_payload_rate;
case TR_ETA: { case TR_ETA: {
// XXX: Is this correct? // XXX: Is this correct?
if (m_torrent.is_paused() || m_torrent.is_queued()) return MAX_ETA; if (m_torrent.is_paused(m_lastStatus) || m_torrent.is_queued(m_lastStatus)) return MAX_ETA;
return QBtSession::instance()->getETA(m_torrent.hash()); return QBtSession::instance()->getETA(m_hash, m_lastStatus);
} }
case TR_RATIO: case TR_RATIO:
return QBtSession::instance()->getRealRatio(m_torrent.hash()); return QBtSession::instance()->getRealRatio(m_lastStatus);
case TR_LABEL: case TR_LABEL:
return m_label; return m_label;
case TR_ADD_DATE: case TR_ADD_DATE:
@ -202,19 +259,19 @@ QVariant TorrentModelItem::data(int column, int role) const
case TR_SEED_DATE: case TR_SEED_DATE:
return m_seedTime; return m_seedTime;
case TR_TRACKER: case TR_TRACKER:
return m_torrent.current_tracker(); return misc::toQString(m_lastStatus.current_tracker);
case TR_DLLIMIT: case TR_DLLIMIT:
return m_torrent.download_limit(); return m_torrent.download_limit();
case TR_UPLIMIT: case TR_UPLIMIT:
return m_torrent.upload_limit(); return m_torrent.upload_limit();
case TR_AMOUNT_DOWNLOADED: case TR_AMOUNT_DOWNLOADED:
return static_cast<qlonglong>(m_torrent.all_time_download()); return static_cast<qlonglong>(m_lastStatus.all_time_download);
case TR_AMOUNT_UPLOADED: case TR_AMOUNT_UPLOADED:
return static_cast<qlonglong>(m_torrent.all_time_upload()); return static_cast<qlonglong>(m_lastStatus.all_time_upload);
case TR_AMOUNT_LEFT: case TR_AMOUNT_LEFT:
return static_cast<qlonglong>(m_torrent.total_wanted() - m_torrent.total_wanted_done()); return static_cast<qlonglong>(m_lastStatus.total_wanted - m_lastStatus.total_wanted_done);
case TR_TIME_ELAPSED: case TR_TIME_ELAPSED:
return (role == Qt::DisplayRole) ? m_torrent.active_time() : m_torrent.seeding_time(); return (role == Qt::DisplayRole) ? m_lastStatus.active_time : m_lastStatus.seeding_time;
case TR_SAVE_PATH: case TR_SAVE_PATH:
return fsutils::toNativePath(m_torrent.save_path_parsed()); return fsutils::toNativePath(m_torrent.save_path_parsed());
default: default:
@ -250,6 +307,7 @@ void TorrentModel::populate() {
connect(QBtSession::instance(), SIGNAL(resumedTorrent(QTorrentHandle)), SLOT(handleTorrentUpdate(QTorrentHandle))); connect(QBtSession::instance(), SIGNAL(resumedTorrent(QTorrentHandle)), SLOT(handleTorrentUpdate(QTorrentHandle)));
connect(QBtSession::instance(), SIGNAL(pausedTorrent(QTorrentHandle)), SLOT(handleTorrentUpdate(QTorrentHandle))); connect(QBtSession::instance(), SIGNAL(pausedTorrent(QTorrentHandle)), SLOT(handleTorrentUpdate(QTorrentHandle)));
connect(QBtSession::instance(), SIGNAL(torrentFinishedChecking(QTorrentHandle)), SLOT(handleTorrentUpdate(QTorrentHandle))); connect(QBtSession::instance(), SIGNAL(torrentFinishedChecking(QTorrentHandle)), SLOT(handleTorrentUpdate(QTorrentHandle)));
connect(QBtSession::instance(), SIGNAL(stateUpdate(std::vector<libtorrent::torrent_status>)), SLOT(stateUpdated(std::vector<libtorrent::torrent_status>)));
} }
TorrentModel::~TorrentModel() { TorrentModel::~TorrentModel() {
@ -382,6 +440,7 @@ void TorrentModel::handleTorrentUpdate(const QTorrentHandle &h)
{ {
const int row = torrentRow(h.hash()); const int row = torrentRow(h.hash());
if (row >= 0) { if (row >= 0) {
m_torrents[row]->refreshStatus(h.status(torrent_handle::query_accurate_download_counters));
notifyTorrentChanged(row); notifyTorrentChanged(row);
} }
} }
@ -394,6 +453,7 @@ void TorrentModel::handleFinishedTorrent(const QTorrentHandle& h)
// Update completion date // Update completion date
m_torrents[row]->setData(TorrentModelItem::TR_SEED_DATE, QDateTime::currentDateTime(), Qt::DisplayRole); m_torrents[row]->setData(TorrentModelItem::TR_SEED_DATE, QDateTime::currentDateTime(), Qt::DisplayRole);
m_torrents[row]->refreshStatus(h.status(torrent_handle::query_accurate_download_counters));
notifyTorrentChanged(row); notifyTorrentChanged(row);
} }
@ -414,6 +474,7 @@ void TorrentModel::setRefreshInterval(int refreshInterval)
void TorrentModel::forceModelRefresh() void TorrentModel::forceModelRefresh()
{ {
emit dataChanged(index(0, 0), index(rowCount()-1, columnCount()-1)); emit dataChanged(index(0, 0), index(rowCount()-1, columnCount()-1));
QBtSession::instance()->postTorrentUpdate();
} }
TorrentStatusReport TorrentModel::getTorrentStatusReport() const TorrentStatusReport TorrentModel::getTorrentStatusReport() const
@ -488,6 +549,20 @@ void TorrentModel::handleTorrentAboutToBeRemoved(const QTorrentHandle &h)
} }
} }
void TorrentModel::stateUpdated(const std::vector<libtorrent::torrent_status> &statuses)
{
typedef std::vector<libtorrent::torrent_status> statuses_t;
for (statuses_t::const_iterator i = statuses.begin(), end = statuses.end(); i != end; ++i)
{
libtorrent::torrent_status const& status = *i;
const int row = torrentRow(misc::toQString(status.handle.info_hash()));
if (row >= 0)
m_torrents[row]->refreshStatus(status);
}
}
bool TorrentModel::inhibitSystem() bool TorrentModel::inhibitSystem()
{ {
QList<TorrentModelItem*>::const_iterator it = m_torrents.constBegin(); QList<TorrentModelItem*>::const_iterator it = m_torrents.constBegin();

3
src/qtlibtorrent/torrentmodel.h

@ -53,6 +53,7 @@ public:
public: public:
TorrentModelItem(const QTorrentHandle& h); TorrentModelItem(const QTorrentHandle& h);
void refreshStatus(libtorrent::torrent_status const& status);
inline int columnCount() const { return NB_COLUMNS; } inline int columnCount() const { return NB_COLUMNS; }
QVariant data(int column, int role = Qt::DisplayRole) const; QVariant data(int column, int role = Qt::DisplayRole) const;
bool setData(int column, const QVariant &value, int role = Qt::DisplayRole); bool setData(int column, const QVariant &value, int role = Qt::DisplayRole);
@ -66,6 +67,7 @@ private:
private: private:
QTorrentHandle m_torrent; QTorrentHandle m_torrent;
libtorrent::torrent_status m_lastStatus;
QDateTime m_addedTime; QDateTime m_addedTime;
QDateTime m_seedTime; QDateTime m_seedTime;
QString m_label; QString m_label;
@ -110,6 +112,7 @@ private slots:
void forceModelRefresh(); void forceModelRefresh();
void handleTorrentLabelChange(QString previous, QString current); void handleTorrentLabelChange(QString previous, QString current);
void handleTorrentAboutToBeRemoved(const QTorrentHandle & h); void handleTorrentAboutToBeRemoved(const QTorrentHandle & h);
void stateUpdated(const std::vector<libtorrent::torrent_status> &statuses);
private: private:
void beginInsertTorrent(int row); void beginInsertTorrent(int row);

129
src/qtlibtorrent/torrentspeedmonitor.cpp

@ -28,7 +28,6 @@
* Contact : chris@qbittorrent.org * Contact : chris@qbittorrent.org
*/ */
#include <QMutexLocker>
#include <QList> #include <QList>
#include <QDateTime> #include <QDateTime>
#include <vector> #include <vector>
@ -61,34 +60,16 @@ private:
QList<Sample<int> > m_speedSamples; QList<Sample<int> > m_speedSamples;
}; };
TorrentSpeedMonitor::TorrentSpeedMonitor(QBtSession* session) : TorrentSpeedMonitor::TorrentSpeedMonitor(QBtSession* session)
QThread(session), m_abort(false), m_session(session), : m_session(session)
sessionUL(0), sessionDL(0), lastWrite(0), dirty(false)
{ {
connect(m_session, SIGNAL(deletedTorrent(QString)), SLOT(removeSamples(QString))); connect(m_session, SIGNAL(deletedTorrent(QString)), SLOT(removeSamples(QString)));
connect(m_session, SIGNAL(pausedTorrent(QTorrentHandle)), SLOT(removeSamples(QTorrentHandle))); connect(m_session, SIGNAL(pausedTorrent(QTorrentHandle)), SLOT(removeSamples(QTorrentHandle)));
loadStats(); connect(m_session, SIGNAL(statsReceived(libtorrent::stats_alert)), SLOT(statsReceived(libtorrent::stats_alert)));
} }
TorrentSpeedMonitor::~TorrentSpeedMonitor() { TorrentSpeedMonitor::~TorrentSpeedMonitor()
m_abort = true; {}
m_abortCond.wakeOne();
wait();
if (dirty)
lastWrite = 0;
saveStats();
}
void TorrentSpeedMonitor::run()
{
do {
m_mutex.lock();
getSamples();
saveStats();
m_abortCond.wait(&m_mutex, 1000);
m_mutex.unlock();
} while(!m_abort);
}
void SpeedSample::addSample(int speedDL, int speedUL) void SpeedSample::addSample(int speedDL, int speedUL)
{ {
@ -130,16 +111,14 @@ void TorrentSpeedMonitor::removeSamples(const QTorrentHandle& h) {
} catch(invalid_handle&) {} } catch(invalid_handle&) {}
} }
qlonglong TorrentSpeedMonitor::getETA(const QString &hash) const qlonglong TorrentSpeedMonitor::getETA(const QString &hash, const libtorrent::torrent_status &status) const
{ {
QMutexLocker locker(&m_mutex); if (QTorrentHandle::is_paused(status) || !m_samples.contains(hash))
QTorrentHandle h = m_session->getTorrentHandle(hash);
if (h.is_paused() || !m_samples.contains(hash))
return MAX_ETA; return MAX_ETA;
const Sample<qreal> speed_average = m_samples[hash].average(); const Sample<qreal> speed_average = m_samples[hash].average();
if (h.is_seed()) { if (QTorrentHandle::is_seed(status)) {
if (!speed_average.upload) if (!speed_average.upload)
return MAX_ETA; return MAX_ETA;
@ -148,99 +127,21 @@ qlonglong TorrentSpeedMonitor::getETA(const QString &hash) const
if (max_ratio < 0) if (max_ratio < 0)
return MAX_ETA; return MAX_ETA;
libtorrent::size_type realDL = h.all_time_download(); libtorrent::size_type realDL = status.all_time_download;
if (realDL <= 0) if (realDL <= 0)
realDL = h.total_wanted(); realDL = status.total_wanted;
return (realDL * max_ratio - h.all_time_upload()) / speed_average.upload; return (realDL * max_ratio - status.all_time_upload) / speed_average.upload;
} }
if (!speed_average.download) if (!speed_average.download)
return MAX_ETA; return MAX_ETA;
return (h.total_wanted() - h.total_wanted_done()) / speed_average.download; return (status.total_wanted - status.total_wanted_done) / speed_average.download;
}
quint64 TorrentSpeedMonitor::getAlltimeDL() const {
QMutexLocker l(&m_mutex);
return alltimeDL + sessionDL;
} }
quint64 TorrentSpeedMonitor::getAlltimeUL() const { void TorrentSpeedMonitor::statsReceived(const stats_alert &stats)
QMutexLocker l(&m_mutex);
return alltimeUL + sessionUL;
}
void TorrentSpeedMonitor::getSamples()
{ {
const std::vector<torrent_handle> torrents = m_session->getSession()->get_torrents(); m_samples[misc::toQString(stats.handle.info_hash())].addSample(stats.transferred[stats_alert::download_payload] * 1000 / stats.interval,
stats.transferred[stats_alert::upload_payload] * 1000 / stats.interval);
std::vector<torrent_handle>::const_iterator it = torrents.begin();
std::vector<torrent_handle>::const_iterator itend = torrents.end();
for ( ; it != itend; ++it) {
try {
torrent_status st = it->status(0x0);
if (!st.paused) {
int up = st.upload_payload_rate;
int down = st.download_payload_rate;
m_samples[misc::toQString(it->info_hash())].addSample(down, up);
}
} catch(invalid_handle&) {}
}
libtorrent::session_status ss = m_session->getSessionStatus();
if (ss.total_download > sessionDL) {
sessionDL = ss.total_download;
dirty = true;
}
if (ss.total_upload > sessionUL) {
sessionUL = ss.total_upload;
dirty = true;
}
}
void TorrentSpeedMonitor::saveStats() const {
if (!(dirty && (QDateTime::currentMSecsSinceEpoch() - lastWrite >= 15*60*1000) ))
return;
QIniSettings s("qBittorrent", "qBittorrent-data");
QVariantHash v;
v.insert("AlltimeDL", alltimeDL + sessionDL);
v.insert("AlltimeUL", alltimeUL + sessionUL);
s.setValue("Stats/AllStats", v);
dirty = false;
lastWrite = QDateTime::currentMSecsSinceEpoch();
}
void TorrentSpeedMonitor::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();
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();
alltimeDL = v["AlltimeDL"].toULongLong();
alltimeUL = v["AlltimeUL"].toULongLong();
if (dirty) {
saveStats();
s_old.remove("Stats/AllStats");
}
} }

30
src/qtlibtorrent/torrentspeedmonitor.h

@ -32,51 +32,31 @@
#define TORRENTSPEEDMONITOR_H #define TORRENTSPEEDMONITOR_H
#include <QString> #include <QString>
#include <QThread>
#include <QWaitCondition>
#include <QHash> #include <QHash>
#include <QMutex>
#include "qtorrenthandle.h" #include "qtorrenthandle.h"
#include <libtorrent/alert_types.hpp>
class QBtSession; class QBtSession;
class SpeedSample; class SpeedSample;
class TorrentSpeedMonitor : public QThread class TorrentSpeedMonitor : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(TorrentSpeedMonitor)
public: public:
explicit TorrentSpeedMonitor(QBtSession* session); explicit TorrentSpeedMonitor(QBtSession* session);
~TorrentSpeedMonitor(); ~TorrentSpeedMonitor();
qlonglong getETA(const QString &hash) const; qlonglong getETA(const QString &hash, const libtorrent::torrent_status &status) const;
quint64 getAlltimeDL() const;
quint64 getAlltimeUL() const;
protected:
void run();
private:
void getSamples();
void saveStats() const;
void loadStats();
private slots: private slots:
void statsReceived(const libtorrent::stats_alert& stats);
void removeSamples(const QString& hash); void removeSamples(const QString& hash);
void removeSamples(const QTorrentHandle& h); void removeSamples(const QTorrentHandle& h);
private: private:
bool m_abort;
QWaitCondition m_abortCond;
QHash<QString, SpeedSample> m_samples; QHash<QString, SpeedSample> m_samples;
mutable QMutex m_mutex;
QBtSession *m_session; QBtSession *m_session;
// Will overflow at 15.9 EiB
quint64 alltimeUL;
quint64 alltimeDL;
qint64 sessionUL;
qint64 sessionDL;
mutable qint64 lastWrite;
mutable bool dirty;
}; };
#endif // TORRENTSPEEDMONITOR_H #endif // TORRENTSPEEDMONITOR_H

95
src/qtlibtorrent/torrentstatistics.cpp

@ -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");
}
}

41
src/qtlibtorrent/torrentstatistics.h

@ -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

5
src/transferlistdelegate.h

@ -78,9 +78,10 @@ public:
case TorrentModelItem::TR_SEEDS: case TorrentModelItem::TR_SEEDS:
case TorrentModelItem::TR_PEERS: { case TorrentModelItem::TR_PEERS: {
QString display = QString::number(index.data().toLongLong()); QString display = QString::number(index.data().toLongLong());
if (index.data(Qt::UserRole).toLongLong() > 0) { qlonglong total = index.data(Qt::UserRole).toLongLong();
if (total > 0) {
// Scrape was successful, we have total values // Scrape was successful, we have total values
display += " ("+QString::number(index.data(Qt::UserRole).toLongLong())+")"; display += " ("+QString::number(total)+")";
} }
QItemDelegate::drawBackground(painter, opt, index); QItemDelegate::drawBackground(painter, opt, index);
QItemDelegate::drawDisplay(painter, opt, opt.rect, display); QItemDelegate::drawDisplay(painter, opt, opt.rect, display);

64
src/webui/btjson.cpp

@ -124,53 +124,55 @@ static const char KEY_TRANSFER_UPSPEED[] = "up_info";
static JsonDict toJson(const QTorrentHandle& h) static JsonDict toJson(const QTorrentHandle& h)
{ {
libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters);
JsonDict ret; JsonDict ret;
ret.add(KEY_TORRENT_HASH, h.hash()); ret.add(KEY_TORRENT_HASH, h.hash());
ret.add(KEY_TORRENT_NAME, h.name()); ret.add(KEY_TORRENT_NAME, h.name());
ret.add(KEY_TORRENT_SIZE, misc::friendlyUnit(h.actual_size())); // FIXME: Should pass as Number, not formatted String (for sorting). ret.add(KEY_TORRENT_SIZE, misc::friendlyUnit(status.total_wanted)); // FIXME: Should pass as Number, not formatted String (for sorting).
ret.add(KEY_TORRENT_PROGRESS, (double)h.progress()); ret.add(KEY_TORRENT_PROGRESS, (double)h.progress(status));
ret.add(KEY_TORRENT_DLSPEED, misc::friendlyUnit(h.download_payload_rate(), true)); // FIXME: Should be passed as a Number ret.add(KEY_TORRENT_DLSPEED, misc::friendlyUnit(status.download_payload_rate, true)); // FIXME: Should be passed as a Number
ret.add(KEY_TORRENT_UPSPEED, misc::friendlyUnit(h.upload_payload_rate(), true)); // FIXME: Should be passed as a Number ret.add(KEY_TORRENT_UPSPEED, misc::friendlyUnit(status.upload_payload_rate, true)); // FIXME: Should be passed as a Number
if (QBtSession::instance()->isQueueingEnabled() && h.queue_position() >= 0) if (QBtSession::instance()->isQueueingEnabled() && h.queue_position() >= 0)
ret.add(KEY_TORRENT_PRIORITY, QString::number(h.queue_position())); ret.add(KEY_TORRENT_PRIORITY, QString::number(h.queue_position()));
else else
ret.add(KEY_TORRENT_PRIORITY, "*"); ret.add(KEY_TORRENT_PRIORITY, "*");
QString seeds = QString::number(h.num_seeds()); QString seeds = QString::number(status.num_seeds);
if (h.num_complete() > 0) if (status.num_complete > 0)
seeds += " ("+QString::number(h.num_complete())+")"; seeds += " ("+QString::number(status.num_complete)+")";
ret.add(KEY_TORRENT_SEEDS, seeds); ret.add(KEY_TORRENT_SEEDS, seeds);
QString leechs = QString::number(h.num_peers() - h.num_seeds()); QString leechs = QString::number(status.num_peers - status.num_seeds);
if (h.num_incomplete() > 0) if (status.num_incomplete > 0)
leechs += " ("+QString::number(h.num_incomplete())+")"; leechs += " ("+QString::number(status.num_incomplete)+")";
ret.add(KEY_TORRENT_LEECHS, leechs); ret.add(KEY_TORRENT_LEECHS, leechs);
const qreal ratio = QBtSession::instance()->getRealRatio(h.hash()); const qreal ratio = QBtSession::instance()->getRealRatio(status);
ret.add(KEY_TORRENT_RATIO, (ratio > 100.) ? QString::fromUtf8("") : misc::accurateDoubleToString(ratio, 1)); ret.add(KEY_TORRENT_RATIO, (ratio > 100.) ? QString::fromUtf8("") : misc::accurateDoubleToString(ratio, 1));
QString eta; QString eta;
QString state; QString state;
if (h.is_paused()) { if (h.is_paused(status)) {
if (h.has_error()) if (h.has_error(status))
state = "error"; state = "error";
else else
state = h.is_seed() ? "pausedUP" : "pausedDL"; state = h.is_seed(status) ? "pausedUP" : "pausedDL";
} else { } else {
if (QBtSession::instance()->isQueueingEnabled() && h.is_queued()) if (QBtSession::instance()->isQueueingEnabled() && h.is_queued(status))
state = h.is_seed() ? "queuedUP" : "queuedDL"; state = h.is_seed(status) ? "queuedUP" : "queuedDL";
else { else {
switch (h.state()) { switch (status.state) {
case torrent_status::finished: case torrent_status::finished:
case torrent_status::seeding: case torrent_status::seeding:
state = h.upload_payload_rate() > 0 ? "uploading" : "stalledUP"; state = status.upload_payload_rate > 0 ? "uploading" : "stalledUP";
break; break;
case torrent_status::allocating: case torrent_status::allocating:
case torrent_status::checking_files: case torrent_status::checking_files:
case torrent_status::queued_for_checking: case torrent_status::queued_for_checking:
case torrent_status::checking_resume_data: case torrent_status::checking_resume_data:
state = h.is_seed() ? "checkingUP" : "checkingDL"; state = h.is_seed(status) ? "checkingUP" : "checkingDL";
break; break;
case torrent_status::downloading: case torrent_status::downloading:
case torrent_status::downloading_metadata: case torrent_status::downloading_metadata:
state = h.download_payload_rate() > 0 ? "downloading" : "stalledDL"; state = status.download_payload_rate > 0 ? "downloading" : "stalledDL";
eta = misc::userFriendlyDuration(QBtSession::instance()->getETA(h.hash())); eta = misc::userFriendlyDuration(QBtSession::instance()->getETA(h.hash(), status));
break; break;
default: default:
qWarning("Unrecognized torrent status, should not happen!!! status was %d", h.state()); qWarning("Unrecognized torrent status, should not happen!!! status was %d", h.state());
@ -284,7 +286,9 @@ QString btjson::getPropertiesForTorrent(const QString& hash)
try { try {
QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash); QTorrentHandle h = QBtSession::instance()->getTorrentHandle(hash);
if (!h.has_metadata()) libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters);
if (!status.has_metadata)
return QString(); return QString();
// Save path // Save path
@ -295,17 +299,17 @@ QString btjson::getPropertiesForTorrent(const QString& hash)
data.add(KEY_PROP_CREATION_DATE, h.creation_date()); data.add(KEY_PROP_CREATION_DATE, h.creation_date());
data.add(KEY_PROP_PIECE_SIZE, misc::friendlyUnit(h.piece_length())); data.add(KEY_PROP_PIECE_SIZE, misc::friendlyUnit(h.piece_length()));
data.add(KEY_PROP_COMMENT, h.comment()); data.add(KEY_PROP_COMMENT, h.comment());
data.add(KEY_PROP_WASTED, misc::friendlyUnit(h.total_failed_bytes() + h.total_redundant_bytes())); data.add(KEY_PROP_WASTED, misc::friendlyUnit(status.total_failed_bytes + status.total_redundant_bytes));
data.add(KEY_PROP_UPLOADED, QString(misc::friendlyUnit(h.all_time_upload()) + " (" + misc::friendlyUnit(h.total_payload_upload()) + " " + tr("this session") + ")")); data.add(KEY_PROP_UPLOADED, QString(misc::friendlyUnit(status.all_time_upload) + " (" + misc::friendlyUnit(status.total_payload_upload) + " " + tr("this session") + ")"));
data.add(KEY_PROP_DOWNLOADED, QString(misc::friendlyUnit(h.all_time_download()) + " (" + misc::friendlyUnit(h.total_payload_download()) + " " + tr("this session") + ")")); data.add(KEY_PROP_DOWNLOADED, QString(misc::friendlyUnit(status.all_time_download) + " (" + misc::friendlyUnit(status.total_payload_download) + " " + tr("this session") + ")"));
data.add(KEY_PROP_UP_LIMIT, h.upload_limit() <= 0 ? QString::fromUtf8("") : misc::friendlyUnit(h.upload_limit(), true)); data.add(KEY_PROP_UP_LIMIT, h.upload_limit() <= 0 ? QString::fromUtf8("") : misc::friendlyUnit(h.upload_limit(), true));
data.add(KEY_PROP_DL_LIMIT, h.download_limit() <= 0 ? QString::fromUtf8("") : misc::friendlyUnit(h.download_limit(), true)); data.add(KEY_PROP_DL_LIMIT, h.download_limit() <= 0 ? QString::fromUtf8("") : misc::friendlyUnit(h.download_limit(), true));
QString elapsed_txt = misc::userFriendlyDuration(h.active_time()); QString elapsed_txt = misc::userFriendlyDuration(status.active_time);
if (h.is_seed()) if (h.is_seed(status))
elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(h.seeding_time()))+")"; elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(status.seeding_time))+")";
data.add(KEY_PROP_TIME_ELAPSED, elapsed_txt); data.add(KEY_PROP_TIME_ELAPSED, elapsed_txt);
data.add(KEY_PROP_CONNECT_COUNT, QString(QString::number(h.num_connections()) + " (" + tr("%1 max", "e.g. 10 max").arg(QString::number(h.connections_limit())) + ")")); data.add(KEY_PROP_CONNECT_COUNT, QString(QString::number(status.num_connections) + " (" + tr("%1 max", "e.g. 10 max").arg(QString::number(status.connections_limit)) + ")"));
const qreal ratio = QBtSession::instance()->getRealRatio(h.hash()); const qreal ratio = QBtSession::instance()->getRealRatio(status);
data.add(KEY_PROP_RATIO, ratio > 100. ? QString::fromUtf8("") : misc::accurateDoubleToString(ratio, 1)); data.add(KEY_PROP_RATIO, ratio > 100. ? QString::fromUtf8("") : misc::accurateDoubleToString(ratio, 1));
} catch(const std::exception& e) { } catch(const std::exception& e) {
qWarning() << Q_FUNC_INFO << "Invalid torrent: " << e.what(); qWarning() << Q_FUNC_INFO << "Invalid torrent: " << e.what();

Loading…
Cancel
Save