From bbc4080a5d5d363d7d2ff085cddc92fb33b0c176 Mon Sep 17 00:00:00 2001 From: Ivan Sorokin Date: Sun, 11 May 2014 14:36:25 +0400 Subject: [PATCH 1/8] fewer calls to hash() --- src/properties/propertieswidget.cpp | 2 +- src/qtlibtorrent/qbtsession.cpp | 5 ++--- src/qtlibtorrent/qbtsession.h | 2 +- src/qtlibtorrent/torrentmodel.cpp | 4 ++-- src/webui/btjson.cpp | 4 ++-- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/properties/propertieswidget.cpp b/src/properties/propertieswidget.cpp index 1e88ff6e3..95fd5fd1f 100644 --- a/src/properties/propertieswidget.cpp +++ b/src/properties/propertieswidget.cpp @@ -335,7 +335,7 @@ void PropertiesWidget::loadDynamicData() { // Update next announce time reannounce_lbl->setText(h.next_announce()); // Update ratio info - const qreal ratio = QBtSession::instance()->getRealRatio(h.hash()); + const qreal ratio = QBtSession::instance()->getRealRatio(h); shareRatio->setText(ratio > QBtSession::MAX_RATIO ? QString::fromUtf8("∞") : misc::accurateDoubleToString(ratio, 2)); if (!h.is_seed() && h.has_metadata()) { showPiecesDownloaded(true); diff --git a/src/qtlibtorrent/qbtsession.cpp b/src/qtlibtorrent/qbtsession.cpp index acb6863ce..eeaa1ab31 100755 --- a/src/qtlibtorrent/qbtsession.cpp +++ b/src/qtlibtorrent/qbtsession.cpp @@ -220,7 +220,7 @@ void QBtSession::processBigRatios() { if (!h.is_valid()) continue; if (h.is_seed()) { const QString hash = h.hash(); - const qreal ratio = getRealRatio(hash); + const qreal ratio = getRealRatio(h); qreal ratio_limit = TorrentPersistentData::getRatioLimit(hash); if (ratio_limit == TorrentPersistentData::USE_GLOBAL_RATIO) ratio_limit = global_ratio_limit; @@ -1556,8 +1556,7 @@ bool QBtSession::enableDHT(bool b) { return true; } -qreal QBtSession::getRealRatio(const QString &hash) const { - QTorrentHandle h = getTorrentHandle(hash); +qreal QBtSession::getRealRatio(QTorrentHandle h) const { if (!h.is_valid()) { return 0.; } diff --git a/src/qtlibtorrent/qbtsession.h b/src/qtlibtorrent/qbtsession.h index c0bf51e0b..f69d8d0d0 100755 --- a/src/qtlibtorrent/qbtsession.h +++ b/src/qtlibtorrent/qbtsession.h @@ -92,7 +92,7 @@ public: qreal getPayloadUploadRate() const; libtorrent::session_status getSessionStatus() const; int getListenPort() const; - qreal getRealRatio(const QString& hash) const; + qreal getRealRatio(QTorrentHandle h) const; QHash getTrackersInfo(const QString &hash) const; bool hasActiveTorrents() const; bool hasDownloadingTorrents() const; diff --git a/src/qtlibtorrent/torrentmodel.cpp b/src/qtlibtorrent/torrentmodel.cpp index 58df2e413..2efcdc4f8 100644 --- a/src/qtlibtorrent/torrentmodel.cpp +++ b/src/qtlibtorrent/torrentmodel.cpp @@ -191,10 +191,10 @@ QVariant TorrentModelItem::data(int column, int role) const case TR_ETA: { // XXX: Is this correct? if (m_torrent.is_paused() || m_torrent.is_queued()) return MAX_ETA; - return QBtSession::instance()->getETA(m_torrent.hash()); + return QBtSession::instance()->getETA(m_hash); } case TR_RATIO: - return QBtSession::instance()->getRealRatio(m_torrent.hash()); + return QBtSession::instance()->getRealRatio(m_torrent); case TR_LABEL: return m_label; case TR_ADD_DATE: diff --git a/src/webui/btjson.cpp b/src/webui/btjson.cpp index 373632cd4..b8f87b989 100644 --- a/src/webui/btjson.cpp +++ b/src/webui/btjson.cpp @@ -143,7 +143,7 @@ static JsonDict toJson(const QTorrentHandle& h) if (h.num_incomplete() > 0) leechs += " ("+QString::number(h.num_incomplete())+")"; ret.add(KEY_TORRENT_LEECHS, leechs); - const qreal ratio = QBtSession::instance()->getRealRatio(h.hash()); + const qreal ratio = QBtSession::instance()->getRealRatio(h); ret.add(KEY_TORRENT_RATIO, (ratio > 100.) ? QString::fromUtf8("∞") : misc::accurateDoubleToString(ratio, 1)); QString eta; QString state; @@ -305,7 +305,7 @@ QString btjson::getPropertiesForTorrent(const QString& hash) elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(h.seeding_time()))+")"; 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())) + ")")); - const qreal ratio = QBtSession::instance()->getRealRatio(h.hash()); + const qreal ratio = QBtSession::instance()->getRealRatio(h); data.add(KEY_PROP_RATIO, ratio > 100. ? QString::fromUtf8("∞") : misc::accurateDoubleToString(ratio, 1)); } catch(const std::exception& e) { qWarning() << Q_FUNC_INFO << "Invalid torrent: " << e.what(); From 1c98c11dd071dec818d437a5463a77d4370b8c90 Mon Sep 17 00:00:00 2001 From: Ivan Sorokin Date: Sun, 11 May 2014 15:29:06 +0400 Subject: [PATCH 2/8] speed up icon loading --- src/qtlibtorrent/torrentmodel.cpp | 77 +++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 13 deletions(-) diff --git a/src/qtlibtorrent/torrentmodel.cpp b/src/qtlibtorrent/torrentmodel.cpp index 2efcdc4f8..577e289c3 100644 --- a/src/qtlibtorrent/torrentmodel.cpp +++ b/src/qtlibtorrent/torrentmodel.cpp @@ -37,6 +37,57 @@ 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) : m_torrent(h) , m_addedTime(TorrentPersistentData::getAddedDate(h.hash())) @@ -54,7 +105,7 @@ TorrentModelItem::State TorrentModelItem::state() const try { // Pause or Queued if (m_torrent.is_paused()) { - m_icon = QIcon(":/Icons/skin/paused.png"); + m_icon = get_paused_icon(); m_fgColor = QColor("red"); return m_torrent.is_seed() ? STATE_PAUSED_UP : STATE_PAUSED_DL; } @@ -62,7 +113,7 @@ TorrentModelItem::State TorrentModelItem::state() const if (m_torrent.state() != torrent_status::queued_for_checking && m_torrent.state() != torrent_status::checking_resume_data && m_torrent.state() != torrent_status::checking_files) { - m_icon = QIcon(":/Icons/skin/queued.png"); + m_icon = get_queued_icon(); m_fgColor = QColor("grey"); return m_torrent.is_seed() ? STATE_QUEUED_UP : STATE_QUEUED_DL; } @@ -70,20 +121,20 @@ TorrentModelItem::State TorrentModelItem::state() const // Other states switch(m_torrent.state()) { case torrent_status::allocating: - m_icon = QIcon(":/Icons/skin/stalledDL.png"); + m_icon = get_stalled_downloading_icon(); m_fgColor = QColor("grey"); return STATE_ALLOCATING; case torrent_status::downloading_metadata: - m_icon = QIcon(":/Icons/skin/downloading.png"); + m_icon = get_downloading_icon(); m_fgColor = QColor("green"); return STATE_DOWNLOADING_META; case torrent_status::downloading: { if (m_torrent.download_payload_rate() > 0) { - m_icon = QIcon(":/Icons/skin/downloading.png"); + m_icon = get_downloading_icon(); m_fgColor = QColor("green"); return STATE_DOWNLOADING; } else { - m_icon = QIcon(":/Icons/skin/stalledDL.png"); + m_icon = get_stalled_downloading_icon(); m_fgColor = QColor("grey"); return STATE_STALLED_DL; } @@ -91,33 +142,33 @@ TorrentModelItem::State TorrentModelItem::state() const case torrent_status::finished: case torrent_status::seeding: if (m_torrent.upload_payload_rate() > 0) { - m_icon = QIcon(":/Icons/skin/uploading.png"); + m_icon = get_uploading_icon(); m_fgColor = QColor("orange"); return STATE_SEEDING; } else { - m_icon = QIcon(":/Icons/skin/stalledUP.png"); + m_icon = get_stalled_uploading_icon(); m_fgColor = QColor("grey"); return STATE_STALLED_UP; } case torrent_status::queued_for_checking: - m_icon = QIcon(":/Icons/skin/checking.png"); + m_icon = get_checking_icon(); m_fgColor = QColor("grey"); return STATE_QUEUED_CHECK; case torrent_status::checking_resume_data: - m_icon = QIcon(":/Icons/skin/checking.png"); + m_icon = get_checking_icon(); m_fgColor = QColor("grey"); return STATE_QUEUED_FASTCHECK; case torrent_status::checking_files: - m_icon = QIcon(":/Icons/skin/checking.png"); + m_icon = get_checking_icon(); m_fgColor = QColor("grey"); return m_torrent.is_seed() ? STATE_CHECKING_UP : STATE_CHECKING_DL; default: - m_icon = QIcon(":/Icons/skin/error.png"); + m_icon = get_error_icon(); m_fgColor = QColor("red"); return STATE_INVALID; } } catch(invalid_handle&) { - m_icon = QIcon(":/Icons/skin/error.png"); + m_icon = get_error_icon(); m_fgColor = QColor("red"); return STATE_INVALID; } From 5af778bc938f12521d0051da593f267c95d50d40 Mon Sep 17 00:00:00 2001 From: Ivan Sorokin Date: Thu, 15 May 2014 02:34:45 +0400 Subject: [PATCH 3/8] don't make the same request to TorrentModelItem twice --- src/transferlistdelegate.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/transferlistdelegate.h b/src/transferlistdelegate.h index 17e52b296..ecda9f644 100644 --- a/src/transferlistdelegate.h +++ b/src/transferlistdelegate.h @@ -78,9 +78,10 @@ public: case TorrentModelItem::TR_SEEDS: case TorrentModelItem::TR_PEERS: { 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 - display += " ("+QString::number(index.data(Qt::UserRole).toLongLong())+")"; + display += " ("+QString::number(total)+")"; } QItemDelegate::drawBackground(painter, opt, index); QItemDelegate::drawDisplay(painter, opt, opt.rect, display); From 329b7541970a04e57807dd6f062ce25391ef3f37 Mon Sep 17 00:00:00 2001 From: Ivan Sorokin Date: Wed, 14 May 2014 02:09:45 +0400 Subject: [PATCH 4/8] cache torrent_status --- src/properties/propertieswidget.cpp | 37 ++--- src/qtlibtorrent/qbtsession.cpp | 24 ++- src/qtlibtorrent/qbtsession.h | 4 +- src/qtlibtorrent/qtorrenthandle.cpp | 181 +++++++---------------- src/qtlibtorrent/qtorrenthandle.h | 33 +---- src/qtlibtorrent/torrentmodel.cpp | 65 ++++---- src/qtlibtorrent/torrentmodel.h | 2 + src/qtlibtorrent/torrentspeedmonitor.cpp | 16 +- src/qtlibtorrent/torrentspeedmonitor.h | 2 +- src/webui/btjson.cpp | 64 ++++---- 10 files changed, 176 insertions(+), 252 deletions(-) diff --git a/src/properties/propertieswidget.cpp b/src/properties/propertieswidget.cpp index 95fd5fd1f..3552d52c1 100644 --- a/src/properties/propertieswidget.cpp +++ b/src/properties/propertieswidget.cpp @@ -316,28 +316,31 @@ void PropertiesWidget::loadDynamicData() { // Refresh only if the torrent handle is valid and if visible if (!h.is_valid() || main_window->getCurrentTabWidget() != transferList || state != VISIBLE) return; try { + libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters + | torrent_handle::query_distributed_copies + | torrent_handle::query_pieces); // Transfer infos if (stackedProperties->currentIndex() == PropTabBar::MAIN_TAB) { - wasted->setText(misc::friendlyUnit(h.total_failed_bytes()+h.total_redundant_bytes())); - upTotal->setText(misc::friendlyUnit(h.all_time_upload()) + " ("+misc::friendlyUnit(h.total_payload_upload())+" "+tr("this session")+")"); - dlTotal->setText(misc::friendlyUnit(h.all_time_download()) + " ("+misc::friendlyUnit(h.total_payload_download())+" "+tr("this session")+")"); + wasted->setText(misc::friendlyUnit(status.total_failed_bytes+status.total_redundant_bytes)); + upTotal->setText(misc::friendlyUnit(status.all_time_upload) + " ("+misc::friendlyUnit(status.total_payload_upload)+" "+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_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()); - if (h.is_seed()) { - elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(h.seeding_time()))+")"; + QString elapsed_txt = misc::userFriendlyDuration(status.active_time); + if (h.is_seed(status)) { + elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(status.seeding_time))+")"; } lbl_elapsed->setText(elapsed_txt); - if (h.connections_limit() > 0) - lbl_connections->setText(QString::number(h.num_connections())+" ("+tr("%1 max", "e.g. 10 max").arg(QString::number(h.connections_limit()))+")"); + if (status.connections_limit > 0) + lbl_connections->setText(QString::number(status.num_connections)+" ("+tr("%1 max", "e.g. 10 max").arg(QString::number(status.connections_limit))+")"); else - lbl_connections->setText(QString::number(h.num_connections())); + lbl_connections->setText(QString::number(status.num_connections)); // Update next announce time - reannounce_lbl->setText(h.next_announce()); + reannounce_lbl->setText(misc::userFriendlyDuration(status.next_announce.total_seconds())); // Update ratio info - const qreal ratio = QBtSession::instance()->getRealRatio(h); + const qreal ratio = QBtSession::instance()->getRealRatio(status); 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); // Downloaded pieces #if LIBTORRENT_VERSION_NUM < 10000 @@ -346,19 +349,19 @@ void PropertiesWidget::loadDynamicData() { bitfield bf(h.torrent_file()->num_pieces(), 0); #endif h.downloading_pieces(bf); - downloaded_pieces->setProgress(h.pieces(), bf); + downloaded_pieces->setProgress(status.pieces, bf); // 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); std::vector avail; h.piece_availability(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 { showPiecesAvailability(false); } // Progress - qreal progress = h.progress()*100.; + qreal progress = h.progress(status)*100.; progress_lbl->setText(misc::accurateDoubleToString(progress, 1)+"%"); } else { showPiecesAvailability(false); @@ -378,7 +381,7 @@ void PropertiesWidget::loadDynamicData() { } if (stackedProperties->currentIndex() == PropTabBar::FILES_TAB) { // Files progress - if (h.is_valid() && h.has_metadata()) { + if (h.is_valid() && status.has_metadata) { qDebug("Updating priorities in files tab"); filesList->setUpdatesEnabled(false); std::vector fp; diff --git a/src/qtlibtorrent/qbtsession.cpp b/src/qtlibtorrent/qbtsession.cpp index eeaa1ab31..0d00f228b 100755 --- a/src/qtlibtorrent/qbtsession.cpp +++ b/src/qtlibtorrent/qbtsession.cpp @@ -220,7 +220,7 @@ void QBtSession::processBigRatios() { if (!h.is_valid()) continue; if (h.is_seed()) { const QString hash = h.hash(); - const qreal ratio = getRealRatio(h); + const qreal ratio = getRealRatio(h.status(torrent_handle::query_accurate_download_counters)); qreal ratio_limit = TorrentPersistentData::getRatioLimit(hash); if (ratio_limit == TorrentPersistentData::USE_GLOBAL_RATIO) ratio_limit = global_ratio_limit; @@ -1556,14 +1556,11 @@ bool QBtSession::enableDHT(bool b) { return true; } -qreal QBtSession::getRealRatio(QTorrentHandle h) const { - if (!h.is_valid()) { - return 0.; - } +qreal QBtSession::getRealRatio(const libtorrent::torrent_status &status) const { + libtorrent::size_type all_time_upload = status.all_time_upload; + libtorrent::size_type all_time_download = status.all_time_download; + 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) { // We have more data on disk than we downloaded // either because the user imported the file @@ -1576,7 +1573,7 @@ qreal QBtSession::getRealRatio(QTorrentHandle h) const { if (all_time_download == 0) { if (all_time_upload == 0) - return 0; + return 0.0; return MAX_RATIO+1; } @@ -2106,11 +2103,12 @@ void QBtSession::autoRunExternalProgram(const QTorrentHandle &h) { } void QBtSession::sendNotificationEmail(const QTorrentHandle &h) { + libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters); // Prepare mail content 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("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"; // Send the notification email Smtp *sender = new Smtp(this); @@ -2803,9 +2801,9 @@ 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 { diff --git a/src/qtlibtorrent/qbtsession.h b/src/qtlibtorrent/qbtsession.h index f69d8d0d0..3a35b30e0 100755 --- a/src/qtlibtorrent/qbtsession.h +++ b/src/qtlibtorrent/qbtsession.h @@ -92,7 +92,7 @@ public: qreal getPayloadUploadRate() const; libtorrent::session_status getSessionStatus() const; int getListenPort() const; - qreal getRealRatio(QTorrentHandle h) const; + qreal getRealRatio(const libtorrent::torrent_status &status) const; QHash getTrackersInfo(const QString &hash) const; bool hasActiveTorrents() const; bool hasDownloadingTorrents() const; @@ -121,7 +121,7 @@ public slots: void startUpTorrents(); void recheckTorrent(const QString &hash); 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 */ void pauseAllTorrents(); void pauseTorrent(const QString &hash); diff --git a/src/qtlibtorrent/qtorrenthandle.cpp b/src/qtlibtorrent/qtorrenthandle.cpp index e6987a1f3..a44eae05e 100644 --- a/src/qtlibtorrent/qtorrenthandle.cpp +++ b/src/qtlibtorrent/qtorrenthandle.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include "fs_utils.h" #include "misc.h" @@ -87,7 +88,7 @@ QString QTorrentHandle::name() const { #if LIBTORRENT_VERSION_NUM < 10000 name = misc::toQStringU(torrent_handle::name()); #else - name = misc::toQStringU(torrent_handle::status(torrent_handle::query_name).name); + name = misc::toQStringU(status(query_name).name); #endif } return name; @@ -102,41 +103,16 @@ QString QTorrentHandle::creation_date() const { 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 { - return misc::toQString(torrent_handle::status(0x0).current_tracker); + return misc::toQString(status(0x0).current_tracker); } bool QTorrentHandle::is_paused() const { - torrent_status st = torrent_handle::status(0x0); - return st.paused && !st.auto_managed; + return is_paused(status(0x0)); } bool QTorrentHandle::is_queued() const { - torrent_status st = torrent_handle::status(0x0); - return st.paused && st.auto_managed; + return is_queued(status(0x0)); } size_type QTorrentHandle::total_size() const { @@ -189,38 +165,6 @@ bool QTorrentHandle::first_last_piece_first() const { && (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 { #if LIBTORRENT_VERSION_NUM < 10000 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 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 { @@ -317,7 +261,7 @@ QString QTorrentHandle::orig_filepath_at(unsigned int index) const { } torrent_status::state_t QTorrentHandle::state() const { - return torrent_handle::status(0x0).state; + return status(0x0).state; } QString QTorrentHandle::creator() const { @@ -336,37 +280,8 @@ QString QTorrentHandle::comment() const { #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 { - torrent_status st = torrent_handle::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 is_checking(status(0x0)); } // Return a list of absolute paths corresponding @@ -408,44 +323,17 @@ int QTorrentHandle::queue_position() const { return torrent_handle::queue_position()+1; } -int QTorrentHandle::num_uploads() const { - return torrent_handle::status(0x0).num_uploads; -} - bool QTorrentHandle::is_seed() const { // Affected by bug http://code.rasterbar.com/libtorrent/ticket/402 //return torrent_handle::is_seed(); // May suffer from approximation problems //return (progress() == 1.); // This looks safe - torrent_status::state_t st = state(); - 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; + return is_seed(status(0x0)); } bool QTorrentHandle::is_sequential_download() const { - torrent_status status = torrent_handle::status(0x0); - 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; + return status(0x0).sequential_download; } bool QTorrentHandle::priv() const { @@ -482,12 +370,11 @@ QString QTorrentHandle::root_path() const } bool QTorrentHandle::has_error() const { - torrent_status st = torrent_handle::status(0x0); - return st.paused && !st.error.empty(); + return has_error(status(0x0)); } 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 { @@ -503,11 +390,7 @@ void QTorrentHandle::downloading_pieces(bitfield &bf) const { } bool QTorrentHandle::has_metadata() const { - return torrent_handle::status(0x0).has_metadata; -} - -float QTorrentHandle::distributed_copies() const { - return torrent_handle::status(query_distributed_copies).distributed_copies; + return status(0x0).has_metadata; } void QTorrentHandle::file_progress(std::vector& fp) const { @@ -732,3 +615,41 @@ void QTorrentHandle::rename_file(int index, const QString& name) const { bool QTorrentHandle::operator ==(const QTorrentHandle& new_h) const { 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; +} diff --git a/src/qtlibtorrent/qtorrenthandle.h b/src/qtlibtorrent/qtorrenthandle.h index a32453f48..9c79e93e2 100644 --- a/src/qtlibtorrent/qtorrenthandle.h +++ b/src/qtlibtorrent/qtorrenthandle.h @@ -59,24 +59,12 @@ public: // QString hash() const; QString name() const; - float progress() const; - libtorrent::bitfield pieces() const; QString current_tracker() const; bool is_paused() const; bool has_filtered_pieces() const; libtorrent::size_type total_size() const; libtorrent::size_type piece_length() 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_parsed() const; QStringList url_seeds() const; @@ -91,26 +79,13 @@ public: libtorrent::torrent_status::state_t state() const; QString creator() 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_uneeded() const; bool has_missing_files() const; - int num_uploads() const; bool is_seed() const; bool is_checking() const; - bool is_auto_managed() const; bool is_sequential_download() const; - qlonglong active_time() const; - qlonglong seeding_time() const; QString creation_date() const; - QString next_announce() const; - qlonglong next_announce_s() const; bool priv() const; bool first_last_piece_first() const; QString root_path() const; @@ -119,7 +94,6 @@ public: QString error() const; void downloading_pieces(libtorrent::bitfield& bf) const; bool has_metadata() const; - float distributed_copies() const; void file_progress(std::vector& fp) const; // @@ -142,6 +116,13 @@ public: // 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: void prioritize_first_last_piece(int file_index, bool b) const; diff --git a/src/qtlibtorrent/torrentmodel.cpp b/src/qtlibtorrent/torrentmodel.cpp index 577e289c3..8140f4833 100644 --- a/src/qtlibtorrent/torrentmodel.cpp +++ b/src/qtlibtorrent/torrentmodel.cpp @@ -90,6 +90,7 @@ namespace TorrentModelItem::TorrentModelItem(const QTorrentHandle &h) : m_torrent(h) + , m_lastStatus(h.status(torrent_handle::query_accurate_download_counters)) , m_addedTime(TorrentPersistentData::getAddedDate(h.hash())) , m_seedTime(TorrentPersistentData::getSeedDate(h.hash())) , m_label(TorrentPersistentData::getLabel(h.hash())) @@ -100,26 +101,33 @@ TorrentModelItem::TorrentModelItem(const QTorrentHandle &h) m_name = h.name(); } +void TorrentModelItem::refreshStatus() +{ + try { + m_lastStatus = m_torrent.status(); + } catch(invalid_handle&) {} +} + TorrentModelItem::State TorrentModelItem::state() const { try { // Pause or Queued - if (m_torrent.is_paused()) { + if (m_torrent.is_paused(m_lastStatus)) { m_icon = get_paused_icon(); 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.state() != torrent_status::queued_for_checking - && m_torrent.state() != torrent_status::checking_resume_data - && m_torrent.state() != torrent_status::checking_files) { + if (m_torrent.is_queued(m_lastStatus)) { + if (m_lastStatus.state != torrent_status::queued_for_checking + && m_lastStatus.state != torrent_status::checking_resume_data + && m_lastStatus.state != torrent_status::checking_files) { m_icon = get_queued_icon(); 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 - switch(m_torrent.state()) { + switch(m_lastStatus.state) { case torrent_status::allocating: m_icon = get_stalled_downloading_icon(); m_fgColor = QColor("grey"); @@ -129,7 +137,7 @@ TorrentModelItem::State TorrentModelItem::state() const m_fgColor = QColor("green"); return STATE_DOWNLOADING_META; case torrent_status::downloading: { - if (m_torrent.download_payload_rate() > 0) { + if (m_lastStatus.download_payload_rate > 0) { m_icon = get_downloading_icon(); m_fgColor = QColor("green"); return STATE_DOWNLOADING; @@ -141,7 +149,7 @@ TorrentModelItem::State TorrentModelItem::state() const } case torrent_status::finished: case torrent_status::seeding: - if (m_torrent.upload_payload_rate() > 0) { + if (m_lastStatus.upload_payload_rate > 0) { m_icon = get_uploading_icon(); m_fgColor = QColor("orange"); return STATE_SEEDING; @@ -161,7 +169,7 @@ TorrentModelItem::State TorrentModelItem::state() const case torrent_status::checking_files: m_icon = get_checking_icon(); 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: m_icon = get_error_icon(); m_fgColor = QColor("red"); @@ -224,28 +232,28 @@ QVariant TorrentModelItem::data(int column, int role) const return pos; } case TR_SIZE: - return m_torrent.has_metadata() ? static_cast(m_torrent.actual_size()) : -1; + return m_lastStatus.has_metadata ? static_cast(m_lastStatus.total_wanted) : -1; case TR_PROGRESS: - return m_torrent.progress(); + return m_torrent.progress(m_lastStatus); case TR_STATUS: return state(); 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: { - 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: - return m_torrent.download_payload_rate(); + return m_lastStatus.download_payload_rate; case TR_UPSPEED: - return m_torrent.upload_payload_rate(); + return m_lastStatus.upload_payload_rate; case TR_ETA: { // XXX: Is this correct? - if (m_torrent.is_paused() || m_torrent.is_queued()) return MAX_ETA; - return QBtSession::instance()->getETA(m_hash); + if (m_torrent.is_paused(m_lastStatus) || m_torrent.is_queued(m_lastStatus)) return MAX_ETA; + return QBtSession::instance()->getETA(m_hash, m_lastStatus); } case TR_RATIO: - return QBtSession::instance()->getRealRatio(m_torrent); + return QBtSession::instance()->getRealRatio(m_lastStatus); case TR_LABEL: return m_label; case TR_ADD_DATE: @@ -253,19 +261,19 @@ QVariant TorrentModelItem::data(int column, int role) const case TR_SEED_DATE: return m_seedTime; case TR_TRACKER: - return m_torrent.current_tracker(); + return misc::toQString(m_lastStatus.current_tracker); case TR_DLLIMIT: return m_torrent.download_limit(); case TR_UPLIMIT: return m_torrent.upload_limit(); case TR_AMOUNT_DOWNLOADED: - return static_cast(m_torrent.all_time_download()); + return static_cast(m_lastStatus.all_time_download); case TR_AMOUNT_UPLOADED: - return static_cast(m_torrent.all_time_upload()); + return static_cast(m_lastStatus.all_time_upload); case TR_AMOUNT_LEFT: - return static_cast(m_torrent.total_wanted() - m_torrent.total_wanted_done()); + return static_cast(m_lastStatus.total_wanted - m_lastStatus.total_wanted_done); 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: return fsutils::toNativePath(m_torrent.save_path_parsed()); default: @@ -464,6 +472,13 @@ void TorrentModel::setRefreshInterval(int refreshInterval) void TorrentModel::forceModelRefresh() { + QList::const_iterator it = m_torrents.constBegin(); + QList::const_iterator itend = m_torrents.constEnd(); + for ( ; it != itend; ++it) { + TorrentModelItem* item = *it; + item->refreshStatus(); + } + emit dataChanged(index(0, 0), index(rowCount()-1, columnCount()-1)); } diff --git a/src/qtlibtorrent/torrentmodel.h b/src/qtlibtorrent/torrentmodel.h index 94e9dcae9..ce1648801 100644 --- a/src/qtlibtorrent/torrentmodel.h +++ b/src/qtlibtorrent/torrentmodel.h @@ -53,6 +53,7 @@ public: public: TorrentModelItem(const QTorrentHandle& h); + void refreshStatus(); inline int columnCount() const { return NB_COLUMNS; } QVariant data(int column, int role = Qt::DisplayRole) const; bool setData(int column, const QVariant &value, int role = Qt::DisplayRole); @@ -66,6 +67,7 @@ private: private: QTorrentHandle m_torrent; + libtorrent::torrent_status m_lastStatus; QDateTime m_addedTime; QDateTime m_seedTime; QString m_label; diff --git a/src/qtlibtorrent/torrentspeedmonitor.cpp b/src/qtlibtorrent/torrentspeedmonitor.cpp index 678a63c7b..623e47b7e 100644 --- a/src/qtlibtorrent/torrentspeedmonitor.cpp +++ b/src/qtlibtorrent/torrentspeedmonitor.cpp @@ -130,16 +130,16 @@ void TorrentSpeedMonitor::removeSamples(const QTorrentHandle& h) { } 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); - QTorrentHandle h = m_session->getTorrentHandle(hash); - if (h.is_paused() || !m_samples.contains(hash)) + + if (QTorrentHandle::is_paused(status) || !m_samples.contains(hash)) return MAX_ETA; const Sample speed_average = m_samples[hash].average(); - if (h.is_seed()) { + if (QTorrentHandle::is_seed(status)) { if (!speed_average.upload) return MAX_ETA; @@ -148,17 +148,17 @@ qlonglong TorrentSpeedMonitor::getETA(const QString &hash) const if (max_ratio < 0) return MAX_ETA; - libtorrent::size_type realDL = h.all_time_download(); + libtorrent::size_type realDL = status.all_time_download; 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) 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 { diff --git a/src/qtlibtorrent/torrentspeedmonitor.h b/src/qtlibtorrent/torrentspeedmonitor.h index 0d71d64f6..3707e7740 100644 --- a/src/qtlibtorrent/torrentspeedmonitor.h +++ b/src/qtlibtorrent/torrentspeedmonitor.h @@ -48,7 +48,7 @@ class TorrentSpeedMonitor : public QThread public: explicit TorrentSpeedMonitor(QBtSession* session); ~TorrentSpeedMonitor(); - qlonglong getETA(const QString &hash) const; + qlonglong getETA(const QString &hash, const libtorrent::torrent_status &status) const; quint64 getAlltimeDL() const; quint64 getAlltimeUL() const; diff --git a/src/webui/btjson.cpp b/src/webui/btjson.cpp index b8f87b989..42cbd0d34 100644 --- a/src/webui/btjson.cpp +++ b/src/webui/btjson.cpp @@ -124,53 +124,55 @@ static const char KEY_TRANSFER_UPSPEED[] = "up_info"; static JsonDict toJson(const QTorrentHandle& h) { + libtorrent::torrent_status status = h.status(torrent_handle::query_accurate_download_counters); + JsonDict ret; ret.add(KEY_TORRENT_HASH, h.hash()); 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_PROGRESS, (double)h.progress()); - ret.add(KEY_TORRENT_DLSPEED, misc::friendlyUnit(h.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_SIZE, misc::friendlyUnit(status.total_wanted)); // FIXME: Should pass as Number, not formatted String (for sorting). + ret.add(KEY_TORRENT_PROGRESS, (double)h.progress(status)); + 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(status.upload_payload_rate, true)); // FIXME: Should be passed as a Number if (QBtSession::instance()->isQueueingEnabled() && h.queue_position() >= 0) ret.add(KEY_TORRENT_PRIORITY, QString::number(h.queue_position())); else ret.add(KEY_TORRENT_PRIORITY, "*"); - QString seeds = QString::number(h.num_seeds()); - if (h.num_complete() > 0) - seeds += " ("+QString::number(h.num_complete())+")"; + QString seeds = QString::number(status.num_seeds); + if (status.num_complete > 0) + seeds += " ("+QString::number(status.num_complete)+")"; ret.add(KEY_TORRENT_SEEDS, seeds); - QString leechs = QString::number(h.num_peers() - h.num_seeds()); - if (h.num_incomplete() > 0) - leechs += " ("+QString::number(h.num_incomplete())+")"; + QString leechs = QString::number(status.num_peers - status.num_seeds); + if (status.num_incomplete > 0) + leechs += " ("+QString::number(status.num_incomplete)+")"; ret.add(KEY_TORRENT_LEECHS, leechs); - const qreal ratio = QBtSession::instance()->getRealRatio(h); + const qreal ratio = QBtSession::instance()->getRealRatio(status); ret.add(KEY_TORRENT_RATIO, (ratio > 100.) ? QString::fromUtf8("∞") : misc::accurateDoubleToString(ratio, 1)); QString eta; QString state; - if (h.is_paused()) { - if (h.has_error()) + if (h.is_paused(status)) { + if (h.has_error(status)) state = "error"; else - state = h.is_seed() ? "pausedUP" : "pausedDL"; + state = h.is_seed(status) ? "pausedUP" : "pausedDL"; } else { - if (QBtSession::instance()->isQueueingEnabled() && h.is_queued()) - state = h.is_seed() ? "queuedUP" : "queuedDL"; + if (QBtSession::instance()->isQueueingEnabled() && h.is_queued(status)) + state = h.is_seed(status) ? "queuedUP" : "queuedDL"; else { - switch (h.state()) { + switch (status.state) { case torrent_status::finished: case torrent_status::seeding: - state = h.upload_payload_rate() > 0 ? "uploading" : "stalledUP"; + state = status.upload_payload_rate > 0 ? "uploading" : "stalledUP"; break; case torrent_status::allocating: case torrent_status::checking_files: case torrent_status::queued_for_checking: case torrent_status::checking_resume_data: - state = h.is_seed() ? "checkingUP" : "checkingDL"; + state = h.is_seed(status) ? "checkingUP" : "checkingDL"; break; case torrent_status::downloading: case torrent_status::downloading_metadata: - state = h.download_payload_rate() > 0 ? "downloading" : "stalledDL"; - eta = misc::userFriendlyDuration(QBtSession::instance()->getETA(h.hash())); + state = status.download_payload_rate > 0 ? "downloading" : "stalledDL"; + eta = misc::userFriendlyDuration(QBtSession::instance()->getETA(h.hash(), status)); break; default: qWarning("Unrecognized torrent status, should not happen!!! status was %d", h.state()); @@ -284,7 +286,9 @@ QString btjson::getPropertiesForTorrent(const QString& hash) try { 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(); // 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_PIECE_SIZE, misc::friendlyUnit(h.piece_length())); 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_UPLOADED, QString(misc::friendlyUnit(h.all_time_upload()) + " (" + misc::friendlyUnit(h.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_WASTED, misc::friendlyUnit(status.total_failed_bytes + status.total_redundant_bytes)); + 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(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_DL_LIMIT, h.download_limit() <= 0 ? QString::fromUtf8("∞") : misc::friendlyUnit(h.download_limit(), true)); - QString elapsed_txt = misc::userFriendlyDuration(h.active_time()); - if (h.is_seed()) - elapsed_txt += " ("+tr("Seeded for %1", "e.g. Seeded for 3m10s").arg(misc::userFriendlyDuration(h.seeding_time()))+")"; + QString elapsed_txt = misc::userFriendlyDuration(status.active_time); + if (h.is_seed(status)) + 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_CONNECT_COUNT, QString(QString::number(h.num_connections()) + " (" + tr("%1 max", "e.g. 10 max").arg(QString::number(h.connections_limit())) + ")")); - const qreal ratio = QBtSession::instance()->getRealRatio(h); + 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(status); data.add(KEY_PROP_RATIO, ratio > 100. ? QString::fromUtf8("∞") : misc::accurateDoubleToString(ratio, 1)); } catch(const std::exception& e) { qWarning() << Q_FUNC_INFO << "Invalid torrent: " << e.what(); From eb46326d23a97275bb1cb6c5385ad60e4671e077 Mon Sep 17 00:00:00 2001 From: Ivan Sorokin Date: Sat, 17 May 2014 13:04:33 +0400 Subject: [PATCH 5/8] use set_alert_dispatch instead of timer to get an alerts from libtorrent libtorrent allows setting a custom dispatch handler that is invoked in libtorrent thread when new alerts are incoming. QAlertDispatcher is a class that allows to translate these alerts to UI thread. The concept is very simple: 1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing QAlertDispatcher::dispatch as argument. 2. On deinitialization destructor calls set_alert_dispatch() passing a empty function. (line 25) libtorrent handles thos and switches back to queuing alerts in queue. 3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new alerts are incoming. Enqueuing is done in function enqueueToMainThread(). The invariant of class is the following: if alert queue is not empty, in message loop of UI thread contains a queued invocation of deliverSignal(). 4. When message loop is pumped UI thread execute deliverSignal() function. It emit appropriate signal and if queue is still not empty (for example if slot doesn't grab alerts) rewind enqueuing to main thread. This is a idea. But here is some details. 1. 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. Therefore we could drop a few alerts during unsubscription. So we unsubscribe only at exit when missing some alerts is not a problem. 2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert queue synchronously. My first attempt was to destroy QAlertDispatcher and then pump libtorrent queue. But as I was afraid of losing alerts I supported synchronous querying of alerts in QAlertDispatcher. (QAlertDispatcher::getPendingAlerts) Conflicts: src/qtlibtorrent/qbtsession.cpp --- src/qtlibtorrent/alertdispatcher.cpp | 93 +++ src/qtlibtorrent/alertdispatcher.h | 43 ++ src/qtlibtorrent/qbtsession.cpp | 973 ++++++++++++++------------- src/qtlibtorrent/qbtsession.h | 4 +- src/qtlibtorrent/qtlibtorrent.pri | 6 +- 5 files changed, 632 insertions(+), 487 deletions(-) create mode 100644 src/qtlibtorrent/alertdispatcher.cpp create mode 100644 src/qtlibtorrent/alertdispatcher.h diff --git a/src/qtlibtorrent/alertdispatcher.cpp b/src/qtlibtorrent/alertdispatcher.cpp new file mode 100644 index 000000000..345d4e163 --- /dev/null +++ b/src/qtlibtorrent/alertdispatcher.cpp @@ -0,0 +1,93 @@ +#include "alertdispatcher.h" + +#include +#include +#include + +QAlertDispatcher::QAlertDispatcher(libtorrent::session *session, QObject* parent) + : QObject(parent) + , session(session) + , current_tag(new QAtomicPointer(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)> dispatch_function_t; + session->set_alert_dispatch(dispatch_function_t()); +} + +void QAlertDispatcher::getPendingAlertsNoWait(std::deque& out) { + Q_ASSERT(out.empty()); + + QMutexLocker lock(&(alerts_mutex)); + std::swap(alerts, out); + event_posted = false; +} + +void QAlertDispatcher::getPendingAlerts(std::deque& 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 > tag, + std::auto_ptr 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(); +} diff --git a/src/qtlibtorrent/alertdispatcher.h b/src/qtlibtorrent/alertdispatcher.h new file mode 100644 index 000000000..55e937621 --- /dev/null +++ b/src/qtlibtorrent/alertdispatcher.h @@ -0,0 +1,43 @@ +#ifndef ALERTDISPATCHER_H +#define ALERTDISPATCHER_H + +#include +#include +#include +#include +#include +#include + +class QAlertDispatcher : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(QAlertDispatcher) + +public: + QAlertDispatcher(libtorrent::session *session, QObject* parent); + ~QAlertDispatcher(); + + void getPendingAlertsNoWait(std::deque&); + void getPendingAlerts(std::deque&); + +signals: + void alertsReceived(); + +private: + static void dispatch(QSharedPointer >, + std::auto_ptr); + void enqueueToMainThread(); + +private slots: + void deliverSignal(); + +private: + libtorrent::session *session; + QMutex alerts_mutex; + QWaitCondition alerts_condvar; + std::deque alerts; + QSharedPointer > current_tag; + bool event_posted; +}; + +#endif // ALERTDISPATCHER_H diff --git a/src/qtlibtorrent/qbtsession.cpp b/src/qtlibtorrent/qbtsession.cpp index 0d00f228b..57f83eefb 100755 --- a/src/qtlibtorrent/qbtsession.cpp +++ b/src/qtlibtorrent/qbtsession.cpp @@ -114,6 +114,7 @@ QBtSession::QBtSession() , m_upnp(0), m_natpmp(0) #endif , m_dynDNSUpdater(0) + , m_alertDispatcher(0) { BigRatioTimer = new QTimer(this); BigRatioTimer->setInterval(10000); @@ -147,9 +148,8 @@ QBtSession::QBtSession() PeXEnabled = false; } s->add_extension(&create_smart_ban_plugin); - timerAlerts = new QTimer(this); - connect(timerAlerts, SIGNAL(timeout()), SLOT(readAlerts())); - timerAlerts->start(1000); + m_alertDispatcher = new QAlertDispatcher(s, this); + connect(m_alertDispatcher, SIGNAL(alertsReceived()), SLOT(readAlerts())); appendLabelToSavePath = pref.appendTorrentLabel(); appendqBExtension = pref.useIncompleteFilesExtension(); connect(m_scanFolders, SIGNAL(torrentsAdded(QStringList&)), SLOT(addTorrentsFromScanFolder(QStringList&))); @@ -179,7 +179,6 @@ QBtSession::~QBtSession() { // Delete our objects if (m_tracker) delete m_tracker; - delete timerAlerts; if (BigRatioTimer) delete BigRatioTimer; if (filterParser) @@ -190,6 +189,7 @@ QBtSession::~QBtSession() { // HTTP Server if (httpServer) delete httpServer; + delete m_alertDispatcher; qDebug("Deleting the session"); delete s; qDebug("BTSession destructor OUT"); @@ -1608,7 +1608,6 @@ void QBtSession::saveFastResumeData() { qDebug("Saving fast resume data..."); // Stop listening for alerts resumeDataTimer.stop(); - timerAlerts->stop(); int num_resume_data = 0; // Pause session s->pause(); @@ -1633,52 +1632,52 @@ void QBtSession::saveFastResumeData() { } catch(libtorrent::invalid_handle&) {} } while (num_resume_data > 0) { - alert const* a = s->wait_for_alert(seconds(30)); - if (a == 0) { - std::cerr << " aborting with " << num_resume_data << " outstanding " - "torrents to save resume data for" << std::endl; - break; - } - // Saving fastresume data can fail - save_resume_data_failed_alert const* rda = dynamic_cast(a); - if (rda) { - --num_resume_data; - s->pop_alert(); - try { - // Remove torrent from session - if (rda->handle.is_valid()) - s->remove_torrent(rda->handle); - }catch(libtorrent::libtorrent_exception) {} - continue; - } - save_resume_data_alert const* rd = dynamic_cast(a); - if (!rd) { - s->pop_alert(); - continue; - } - // Saving fast resume data was successful - --num_resume_data; - if (!rd->resume_data) continue; - QDir torrentBackup(fsutils::BTBackupLocation()); - const QTorrentHandle h(rd->handle); - if (!h.is_valid()) continue; - try { - // Remove old fastresume file if it exists - backupPersistentData(h.hash(), rd->resume_data); - vector out; - bencode(back_inserter(out), *rd->resume_data); - const QString filepath = torrentBackup.absoluteFilePath(h.hash()+".fastresume"); - QFile resume_file(filepath); - if (resume_file.exists()) - fsutils::forceRemove(filepath); - if (!out.empty() && resume_file.open(QIODevice::WriteOnly)) { - resume_file.write(&out[0], out.size()); - resume_file.close(); + std::deque alerts; + m_alertDispatcher->getPendingAlerts(alerts); + + for (std::deque::const_iterator i = alerts.begin(), end = alerts.end(); i != end; ++i) + { + alert const* a = *i; + // Saving fastresume data can fail + save_resume_data_failed_alert const* rda = dynamic_cast(a); + if (rda) { + --num_resume_data; + try { + // Remove torrent from session + if (rda->handle.is_valid()) + s->remove_torrent(rda->handle); + }catch(libtorrent::libtorrent_exception) {} + continue; } - // Remove torrent from session - s->remove_torrent(rd->handle); - s->pop_alert(); - } catch(libtorrent::invalid_handle&) {} + save_resume_data_alert const* rd = dynamic_cast(a); + if (!rd) { + continue; + } + // Saving fast resume data was successful + --num_resume_data; + if (!rd->resume_data) continue; + QDir torrentBackup(fsutils::BTBackupLocation()); + const QTorrentHandle h(rd->handle); + if (!h.is_valid()) continue; + try { + // Remove old fastresume file if it exists + backupPersistentData(h.hash(), rd->resume_data); + vector out; + bencode(back_inserter(out), *rd->resume_data); + const QString filepath = torrentBackup.absoluteFilePath(h.hash()+".fastresume"); + QFile resume_file(filepath); + if (resume_file.exists()) + fsutils::forceRemove(filepath); + if (!out.empty() && resume_file.open(QIODevice::WriteOnly)) { + resume_file.write(&out[0], out.size()); + resume_file.close(); + } + // Remove torrent from session + s->remove_torrent(rd->handle); + } catch(libtorrent::invalid_handle&) {} + + delete a; + } } } @@ -2117,454 +2116,460 @@ void QBtSession::sendNotificationEmail(const QTorrentHandle &h) { // Read alerts sent by the Bittorrent session void QBtSession::readAlerts() { - // look at session alerts and display some infos - std::auto_ptr a = s->pop_alert(); - while (a.get()) { - try { - if (torrent_finished_alert* p = dynamic_cast(a.get())) { - QTorrentHandle h(p->handle); - if (h.is_valid()) { - const QString hash = h.hash(); - qDebug("Got a torrent finished alert for %s", qPrintable(h.name())); - // Remove .!qB extension if necessary - if (appendqBExtension) - appendqBextensionToTorrent(h, false); - const bool was_already_seeded = TorrentPersistentData::isSeed(hash); - qDebug("Was already seeded: %d", was_already_seeded); - if (!was_already_seeded) { - h.save_resume_data(); - qDebug("Checking if the torrent contains torrent files to download"); - // Check if there are torrent files inside - for (int i=0; i t = new torrent_info(fsutils::toNativePath(torrent_fullpath).toUtf8().constData()); - if (t->is_valid()) { - qDebug("emitting recursiveTorrentDownloadPossible()"); - emit recursiveTorrentDownloadPossible(h); - break; - } - } catch(std::exception&) { - qDebug("Caught error loading torrent"); - addConsoleMessage(tr("Unable to decode %1 torrent file.").arg(fsutils::toNativePath(torrent_fullpath)), QString::fromUtf8("red")); + typedef std::deque alerts_t; + 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 { + if (torrent_finished_alert* p = dynamic_cast(a)) { + QTorrentHandle h(p->handle); + if (h.is_valid()) { + const QString hash = h.hash(); + qDebug("Got a torrent finished alert for %s", qPrintable(h.name())); + // Remove .!qB extension if necessary + if (appendqBExtension) + appendqBextensionToTorrent(h, false); + + const bool was_already_seeded = TorrentPersistentData::isSeed(hash); + qDebug("Was already seeded: %d", was_already_seeded); + if (!was_already_seeded) { + h.save_resume_data(); + qDebug("Checking if the torrent contains torrent files to download"); + // Check if there are torrent files inside + for (int i=0; i t = new torrent_info(fsutils::toNativePath(torrent_fullpath).toUtf8().constData()); + if (t->is_valid()) { + qDebug("emitting recursiveTorrentDownloadPossible()"); + emit recursiveTorrentDownloadPossible(h); + break; } + } catch(std::exception&) { + qDebug("Caught error loading torrent"); + addConsoleMessage(tr("Unable to decode %1 torrent file.").arg(fsutils::toNativePath(torrent_fullpath)), QString::fromUtf8("red")); } } - // Move to download directory if necessary - if (!defaultTempPath.isEmpty()) { - // Check if directory is different - const QDir current_dir(h.save_path()); - const QDir save_dir(getSavePath(hash)); - if (current_dir != save_dir) { - qDebug("Moving torrent from the temp folder"); - h.move_storage(save_dir.absolutePath()); - } - } - // Remember finished state - qDebug("Saving seed status"); - TorrentPersistentData::saveSeedStatus(h); - // Recheck if the user asked to - Preferences pref; - if (pref.recheckTorrentsOnCompletion()) { - h.force_recheck(); - } - qDebug("Emitting finishedTorrent() signal"); - emit finishedTorrent(h); - qDebug("Received finished alert for %s", qPrintable(h.name())); - #ifndef DISABLE_GUI - bool will_shutdown = (pref.shutdownWhenDownloadsComplete() || - pref.shutdownqBTWhenDownloadsComplete() || - pref.suspendWhenDownloadsComplete()) - && !hasDownloadingTorrents(); - #else - bool will_shutdown = false; - #endif - // AutoRun program - if (pref.isAutoRunEnabled()) - autoRunExternalProgram(h); - // Move .torrent file to another folder - if (pref.isFinishedTorrentExportEnabled()) - exportTorrentFile(h, FinishedTorrentExportFolder); - // Mail notification - if (pref.isMailNotificationEnabled()) - sendNotificationEmail(h); - #ifndef DISABLE_GUI - // Auto-Shutdown - if (will_shutdown) { - bool suspend = pref.suspendWhenDownloadsComplete(); - bool shutdown = pref.shutdownWhenDownloadsComplete(); - // Confirm shutdown - QString confirm_msg; - if (suspend) { - confirm_msg = tr("The computer will now go to sleep mode unless you cancel within the next 15 seconds..."); - } else if (shutdown) { - confirm_msg = tr("The computer will now be switched off unless you cancel within the next 15 seconds..."); - } else { - confirm_msg = tr("qBittorrent will now exit unless you cancel within the next 15 seconds..."); - } - if (!ShutdownConfirmDlg::askForConfirmation(confirm_msg)) - return; - // Actually shut down - if (suspend || shutdown) { - qDebug("Preparing for auto-shutdown because all downloads are complete!"); - // Disabling it for next time - pref.setShutdownWhenDownloadsComplete(false); - pref.setSuspendWhenDownloadsComplete(false); - // Make sure preferences are synced before exiting - if (suspend) - m_shutdownAct = SUSPEND_COMPUTER; - else - m_shutdownAct = SHUTDOWN_COMPUTER; - } - qDebug("Exiting the application"); - qApp->exit(); - return; - } - #endif // DISABLE_GUI } - } - } - else if (save_resume_data_alert* p = dynamic_cast(a.get())) { - const QDir torrentBackup(fsutils::BTBackupLocation()); - const QTorrentHandle h(p->handle); - if (h.is_valid() && p->resume_data) { - const QString filepath = torrentBackup.absoluteFilePath(h.hash()+".fastresume"); - QFile resume_file(filepath); - if (resume_file.exists()) - fsutils::forceRemove(filepath); - qDebug("Saving fastresume data in %s", qPrintable(filepath)); - backupPersistentData(h.hash(), p->resume_data); - vector out; - bencode(back_inserter(out), *p->resume_data); - if (!out.empty() && resume_file.open(QIODevice::WriteOnly)) { - resume_file.write(&out[0], out.size()); - resume_file.close(); - } - } - } - else if (file_renamed_alert* p = dynamic_cast(a.get())) { - QTorrentHandle h(p->handle); - if (h.is_valid()) { - if (h.num_files() > 1) { - // Check if folders were renamed - QStringList old_path_parts = h.orig_filepath_at(p->index).split("/"); - old_path_parts.removeLast(); - QString old_path = old_path_parts.join("/"); - QStringList new_path_parts = fsutils::fromNativePath(misc::toQStringU(p->name)).split("/"); - new_path_parts.removeLast(); - if (!new_path_parts.isEmpty() && old_path != new_path_parts.join("/")) { - qDebug("Old_path(%s) != new_path(%s)", qPrintable(old_path), qPrintable(new_path_parts.join("/"))); - old_path = h.save_path()+"/"+old_path; - qDebug("Detected folder renaming, attempt to delete old folder: %s", qPrintable(old_path)); - QDir().rmpath(old_path); - } - } else { - // Single-file torrent - // Renaming a file corresponds to changing the save path - emit savePathChanged(h); - } - } - } - else if (torrent_deleted_alert* p = dynamic_cast(a.get())) { - qDebug("A torrent was deleted from the hard disk, attempting to remove the root folder too..."); - QString hash = misc::toQString(p->info_hash); - if (!hash.isEmpty()) { - if (savePathsToRemove.contains(hash)) { - const QString dirpath = savePathsToRemove.take(hash); - qDebug() << "Removing save path: " << dirpath << "..."; - bool ok = fsutils::smartRemoveEmptyFolderTree(dirpath); - Q_UNUSED(ok); - qDebug() << "Folder was removed: " << ok; - } - } else { - // Fallback - qDebug() << "hash is empty, use fallback to remove save path"; - foreach (const QString& key, savePathsToRemove.keys()) { - // Attempt to delete - if (QDir().rmdir(savePathsToRemove[key])) { - savePathsToRemove.remove(key); - } - } - } - } - else if (storage_moved_alert* p = dynamic_cast(a.get())) { - QTorrentHandle h(p->handle); - if (h.is_valid()) { - // Attempt to remove old folder if empty - const QString old_save_path = fsutils::fromNativePath(TorrentPersistentData::getPreviousPath(h.hash())); - const QString new_save_path = fsutils::fromNativePath(misc::toQStringU(p->path.c_str())); - qDebug("Torrent moved from %s to %s", qPrintable(old_save_path), qPrintable(new_save_path)); - QDir old_save_dir(old_save_path); - if (old_save_dir != QDir(defaultSavePath) && old_save_dir != QDir(defaultTempPath)) { - qDebug("Attempting to remove %s", qPrintable(old_save_path)); - QDir().rmpath(old_save_path); - } - if (defaultTempPath.isEmpty() || !new_save_path.startsWith(defaultTempPath)) { - qDebug("Storage has been moved, updating save path to %s", qPrintable(new_save_path)); - TorrentPersistentData::saveSavePath(h.hash(), new_save_path); - } - emit savePathChanged(h); - //h.force_recheck(); - } - } - else if (metadata_received_alert* p = dynamic_cast(a.get())) { - QTorrentHandle h(p->handle); - Preferences pref; - if (h.is_valid()) { - QString hash(h.hash()); - if (HiddenData::hasData(hash)) { - HiddenData::gotMetadata(hash); - if (pref.isQueueingSystemEnabled()) { - //Internally decrease the queue limits to ensure that that other queued items aren't started - libtorrent::session_settings sessionSettings(s->settings()); - int max_downloading = pref.getMaxActiveDownloads(); - int max_active = pref.getMaxActiveTorrents(); - if (max_downloading > -1) - sessionSettings.active_downloads = max_downloading + HiddenData::getDownloadingSize(); - else - sessionSettings.active_downloads = max_downloading; - if (max_active > -1) - sessionSettings.active_limit = max_active + HiddenData::getDownloadingSize(); - else - sessionSettings.active_limit = max_active; - s->set_settings(sessionSettings); - } - h.pause(); - } - qDebug("Received metadata for %s", qPrintable(h.hash())); - // Save metadata - const QDir torrentBackup(fsutils::BTBackupLocation()); - if (!QFile::exists(torrentBackup.absoluteFilePath(h.hash()+QString(".torrent")))) - h.save_torrent_file(torrentBackup.absoluteFilePath(h.hash()+QString(".torrent"))); - // Copy the torrent file to the export folder - if (m_torrentExportEnabled) - exportTorrentFile(h); - // Append .!qB to incomplete files - if (appendqBExtension) - appendqBextensionToTorrent(h, true); - - if (!HiddenData::hasData(hash)) - emit metadataReceived(h); - else - emit metadataReceivedHidden(h); - - if (h.is_paused() && !HiddenData::hasData(hash)) { - // XXX: Unfortunately libtorrent-rasterbar does not send a torrent_paused_alert - // and the torrent can be paused when metadata is received - emit pausedTorrent(h); - } - - } - } - else if (file_error_alert* p = dynamic_cast(a.get())) { - QTorrentHandle h(p->handle); - if (h.is_valid()) { - h.pause(); - std::cerr << "File Error: " << p->message().c_str() << std::endl; - addConsoleMessage(tr("An I/O error occurred, '%1' paused.").arg(h.name())); - addConsoleMessage(tr("Reason: %1").arg(misc::toQStringU(p->message()))); - if (h.is_valid()) { - emit fullDiskError(h, misc::toQStringU(p->message())); - //h.pause(); - emit pausedTorrent(h); - } - } - } - else if (file_completed_alert* p = dynamic_cast(a.get())) { - QTorrentHandle h(p->handle); - qDebug("A file completed download in torrent %s", qPrintable(h.name())); - if (appendqBExtension) { - qDebug("appendqBTExtension is true"); - QString name = h.filepath_at(p->index); - if (name.endsWith(".!qB")) { - const QString old_name = name; - name.chop(4); - qDebug("Renaming %s to %s", qPrintable(old_name), qPrintable(name)); - h.rename_file(p->index, name); - } - } - } - else if (torrent_paused_alert* p = dynamic_cast(a.get())) { - if (p->handle.is_valid()) { - QTorrentHandle h(p->handle); - if (!HiddenData::hasData(h.hash())) { - if (!h.has_error()) - h.save_resume_data(); - emit pausedTorrent(h); - } - } - } - else if (tracker_error_alert* p = dynamic_cast(a.get())) { - // Level: fatal - QTorrentHandle h(p->handle); - if (h.is_valid()) { - // Authentication - if (p->status_code != 401) { - qDebug("Received a tracker error for %s: %s", p->url.c_str(), p->msg.c_str()); - const QString tracker_url = misc::toQString(p->url); - QHash trackers_data = trackersInfos.value(h.hash(), QHash()); - TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url)); - data.last_message = misc::toQStringU(p->msg); - trackers_data.insert(tracker_url, data); - trackersInfos[h.hash()] = trackers_data; - } else { - emit trackerAuthenticationRequired(h); - } - } - } - else if (tracker_reply_alert* p = dynamic_cast(a.get())) { - const QTorrentHandle h(p->handle); - if (h.is_valid()) { - qDebug("Received a tracker reply from %s (Num_peers=%d)", p->url.c_str(), p->num_peers); - // Connection was successful now. Remove possible old errors - QHash trackers_data = trackersInfos.value(h.hash(), QHash()); - const QString tracker_url = misc::toQString(p->url); - TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url)); - data.last_message = ""; // Reset error/warning message - data.num_peers = p->num_peers; - trackers_data.insert(tracker_url, data); - trackersInfos[h.hash()] = trackers_data; - } - } - else if (tracker_warning_alert* p = dynamic_cast(a.get())) { - const QTorrentHandle h(p->handle); - if (h.is_valid()) { - // Connection was successful now but there is a warning message - QHash trackers_data = trackersInfos.value(h.hash(), QHash()); - const QString tracker_url = misc::toQString(p->url); - TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url)); - data.last_message = misc::toQStringU(p->msg); // Store warning message - trackers_data.insert(tracker_url, data); - trackersInfos[h.hash()] = trackers_data; - qDebug("Received a tracker warning from %s: %s", p->url.c_str(), p->msg.c_str()); - } - } - else if (portmap_error_alert* p = dynamic_cast(a.get())) { - addConsoleMessage(tr("UPnP/NAT-PMP: Port mapping failure, message: %1").arg(misc::toQStringU(p->message())), "red"); - //emit UPnPError(QString(p->msg().c_str())); - } - else if (portmap_alert* p = dynamic_cast(a.get())) { - 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"); - //emit UPnPSuccess(QString(p->msg().c_str())); - } - else if (peer_blocked_alert* p = dynamic_cast(a.get())) { - boost::system::error_code ec; - string ip = p->ip.to_string(ec); - if (!ec) { - addPeerBanMessage(QString::fromLatin1(ip.c_str()), true); - //emit peerBlocked(QString::fromLatin1(ip.c_str())); - } - } - else if (peer_ban_alert* p = dynamic_cast(a.get())) { - boost::system::error_code ec; - string ip = p->ip.address().to_string(ec); - if (!ec) { - addPeerBanMessage(QString::fromLatin1(ip.c_str()), false); - //emit peerBlocked(QString::fromLatin1(ip.c_str())); - } - } - else if (fastresume_rejected_alert* p = dynamic_cast(a.get())) { - QTorrentHandle h(p->handle); - if (h.is_valid()) { - qDebug("/!\\ Fast resume failed for %s, reason: %s", qPrintable(h.name()), p->message().c_str()); - if (p->error.value() == 134 && TorrentPersistentData::isSeed(h.hash()) && h.has_missing_files()) { - const QString hash = h.hash(); - // Mismatching file size (files were probably moved - addConsoleMessage(tr("File sizes mismatch for torrent %1, pausing it.").arg(h.name())); - TorrentPersistentData::setErrorState(hash, true); - pauseTorrent(hash); - } else { - addConsoleMessage(tr("Fast resume data was rejected for torrent %1, checking again...").arg(h.name()), QString::fromUtf8("red")); - addConsoleMessage(tr("Reason: %1").arg(misc::toQStringU(p->message()))); - } - } - } - else if (url_seed_alert* p = dynamic_cast(a.get())) { - 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())); - } - else if (listen_succeeded_alert *p = dynamic_cast(a.get())) { - boost::system::error_code ec; - QString proto = "TCP"; -#if LIBTORRENT_VERSION_NUM >= 10000 - if (p->sock_type == listen_succeeded_alert::udp) - proto = "UDP"; - else if (p->sock_type == listen_succeeded_alert::tcp) - proto = "TCP"; - else if (p->sock_type == listen_succeeded_alert::tcp_ssl) - proto = "TCP_SSL"; -#endif - qDebug() << "Successfully listening on " << proto << p->endpoint.address().to_string(ec).c_str() << "/" << p->endpoint.port(); - addConsoleMessage(tr("qBittorrent is successfully listening on interface %1 port: %2/%3", "e.g: qBittorrent is successfully listening on interface 192.168.0.1 port: TCP/6881").arg(p->endpoint.address().to_string(ec).c_str()).arg(proto).arg(QString::number(p->endpoint.port())), "blue"); - // Force reannounce on all torrents because some trackers blacklist some ports - std::vector torrents = s->get_torrents(); - - std::vector::iterator it = torrents.begin(); - std::vector::iterator itend = torrents.end(); - for ( ; it != itend; ++it) { - it->force_reannounce(); - } - } - else if (listen_failed_alert *p = dynamic_cast(a.get())) { - boost::system::error_code ec; - QString proto = "TCP"; -#if LIBTORRENT_VERSION_NUM >= 10000 - if (p->sock_type == listen_failed_alert::udp) - proto = "UDP"; - else if (p->sock_type == listen_failed_alert::tcp) - proto = "TCP"; - else if (p->sock_type == listen_failed_alert::tcp_ssl) - proto = "TCP_SSL"; - else if (p->sock_type == listen_failed_alert::i2p) - proto = "I2P"; - else if (p->sock_type == listen_failed_alert::socks5) - proto = "SOCKS5"; -#endif - 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"); - } - else if (torrent_checked_alert* p = dynamic_cast(a.get())) { - QTorrentHandle h(p->handle); - if (h.is_valid()) { - const QString hash = h.hash(); - qDebug("%s have just finished checking", qPrintable(hash)); - // Save seed status - TorrentPersistentData::saveSeedStatus(h); - // Move to temp directory if necessary - if (!h.is_seed() && !defaultTempPath.isEmpty()) { + // Move to download directory if necessary + if (!defaultTempPath.isEmpty()) { // Check if directory is different const QDir current_dir(h.save_path()); - const QDir save_dir(getSavePath(h.hash())); - if (current_dir == save_dir) { - qDebug("Moving the torrent to the temp directory..."); - QString torrent_tmp_path = defaultTempPath; - h.move_storage(torrent_tmp_path); + const QDir save_dir(getSavePath(hash)); + if (current_dir != save_dir) { + qDebug("Moving torrent from the temp folder"); + h.move_storage(save_dir.absolutePath()); } } - emit torrentFinishedChecking(h); - if (torrentsToPausedAfterChecking.contains(hash)) { - torrentsToPausedAfterChecking.removeOne(hash); - h.pause(); - emit pausedTorrent(h); + // Remember finished state + qDebug("Saving seed status"); + TorrentPersistentData::saveSeedStatus(h); + // Recheck if the user asked to + Preferences pref; + if (pref.recheckTorrentsOnCompletion()) { + h.force_recheck(); + } + qDebug("Emitting finishedTorrent() signal"); + emit finishedTorrent(h); + qDebug("Received finished alert for %s", qPrintable(h.name())); +#ifndef DISABLE_GUI + bool will_shutdown = (pref.shutdownWhenDownloadsComplete() || + pref.shutdownqBTWhenDownloadsComplete() || + pref.suspendWhenDownloadsComplete()) + && !hasDownloadingTorrents(); +#else + bool will_shutdown = false; +#endif + // AutoRun program + if (pref.isAutoRunEnabled()) + autoRunExternalProgram(h); + // Move .torrent file to another folder + if (pref.isFinishedTorrentExportEnabled()) + exportTorrentFile(h, FinishedTorrentExportFolder); + // Mail notification + if (pref.isMailNotificationEnabled()) + sendNotificationEmail(h); +#ifndef DISABLE_GUI + // Auto-Shutdown + if (will_shutdown) { + bool suspend = pref.suspendWhenDownloadsComplete(); + bool shutdown = pref.shutdownWhenDownloadsComplete(); + // Confirm shutdown + QString confirm_msg; + if (suspend) { + confirm_msg = tr("The computer will now go to sleep mode unless you cancel within the next 15 seconds..."); + } else if (shutdown) { + confirm_msg = tr("The computer will now be switched off unless you cancel within the next 15 seconds..."); + } else { + confirm_msg = tr("qBittorrent will now exit unless you cancel within the next 15 seconds..."); + } + if (!ShutdownConfirmDlg::askForConfirmation(confirm_msg)) + return; + // Actually shut down + if (suspend || shutdown) { + qDebug("Preparing for auto-shutdown because all downloads are complete!"); + // Disabling it for next time + pref.setShutdownWhenDownloadsComplete(false); + pref.setSuspendWhenDownloadsComplete(false); + // Make sure preferences are synced before exiting + if (suspend) + m_shutdownAct = SUSPEND_COMPUTER; + else + m_shutdownAct = SHUTDOWN_COMPUTER; + } + qDebug("Exiting the application"); + qApp->exit(); + return; + } +#endif // DISABLE_GUI + } + } + } + else if (save_resume_data_alert* p = dynamic_cast(a)) { + const QDir torrentBackup(fsutils::BTBackupLocation()); + const QTorrentHandle h(p->handle); + if (h.is_valid() && p->resume_data) { + const QString filepath = torrentBackup.absoluteFilePath(h.hash()+".fastresume"); + QFile resume_file(filepath); + if (resume_file.exists()) + fsutils::forceRemove(filepath); + qDebug("Saving fastresume data in %s", qPrintable(filepath)); + backupPersistentData(h.hash(), p->resume_data); + vector out; + bencode(back_inserter(out), *p->resume_data); + if (!out.empty() && resume_file.open(QIODevice::WriteOnly)) { + resume_file.write(&out[0], out.size()); + resume_file.close(); + } + } + } + else if (file_renamed_alert* p = dynamic_cast(a)) { + QTorrentHandle h(p->handle); + if (h.is_valid()) { + if (h.num_files() > 1) { + // Check if folders were renamed + QStringList old_path_parts = h.orig_filepath_at(p->index).split("/"); + old_path_parts.removeLast(); + QString old_path = old_path_parts.join("/"); + QStringList new_path_parts = fsutils::fromNativePath(misc::toQStringU(p->name)).split("/"); + new_path_parts.removeLast(); + if (!new_path_parts.isEmpty() && old_path != new_path_parts.join("/")) { + qDebug("Old_path(%s) != new_path(%s)", qPrintable(old_path), qPrintable(new_path_parts.join("/"))); + old_path = h.save_path()+"/"+old_path; + qDebug("Detected folder renaming, attempt to delete old folder: %s", qPrintable(old_path)); + QDir().rmpath(old_path); + } + } else { + // Single-file torrent + // Renaming a file corresponds to changing the save path + emit savePathChanged(h); + } + } + } + else if (torrent_deleted_alert* p = dynamic_cast(a)) { + qDebug("A torrent was deleted from the hard disk, attempting to remove the root folder too..."); + QString hash = misc::toQString(p->info_hash); + if (!hash.isEmpty()) { + if (savePathsToRemove.contains(hash)) { + const QString dirpath = savePathsToRemove.take(hash); + qDebug() << "Removing save path: " << dirpath << "..."; + bool ok = fsutils::smartRemoveEmptyFolderTree(dirpath); + Q_UNUSED(ok); + qDebug() << "Folder was removed: " << ok; + } + } else { + // Fallback + qDebug() << "hash is empty, use fallback to remove save path"; + foreach (const QString& key, savePathsToRemove.keys()) { + // Attempt to delete + if (QDir().rmdir(savePathsToRemove[key])) { + savePathsToRemove.remove(key); } } } - else if (external_ip_alert *p = dynamic_cast(a.get())) { - 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"); - } - } catch (const std::exception& e) { - qWarning() << "Caught exception in readAlerts(): " << e.what(); } + else if (storage_moved_alert* p = dynamic_cast(a)) { + QTorrentHandle h(p->handle); + if (h.is_valid()) { + // Attempt to remove old folder if empty + const QString old_save_path = fsutils::fromNativePath(TorrentPersistentData::getPreviousPath(h.hash())); + const QString new_save_path = fsutils::fromNativePath(misc::toQStringU(p->path.c_str())); + qDebug("Torrent moved from %s to %s", qPrintable(old_save_path), qPrintable(new_save_path)); + QDir old_save_dir(old_save_path); + if (old_save_dir != QDir(defaultSavePath) && old_save_dir != QDir(defaultTempPath)) { + qDebug("Attempting to remove %s", qPrintable(old_save_path)); + QDir().rmpath(old_save_path); + } + if (defaultTempPath.isEmpty() || !new_save_path.startsWith(defaultTempPath)) { + qDebug("Storage has been moved, updating save path to %s", qPrintable(new_save_path)); + TorrentPersistentData::saveSavePath(h.hash(), new_save_path); + } + emit savePathChanged(h); + //h.force_recheck(); + } + } + else if (metadata_received_alert* p = dynamic_cast(a)) { + QTorrentHandle h(p->handle); + Preferences pref; + if (h.is_valid()) { + QString hash(h.hash()); + if (HiddenData::hasData(hash)) { + HiddenData::gotMetadata(hash); + if (pref.isQueueingSystemEnabled()) { + //Internally decrease the queue limits to ensure that that other queued items aren't started + libtorrent::session_settings sessionSettings(s->settings()); + int max_downloading = pref.getMaxActiveDownloads(); + int max_active = pref.getMaxActiveTorrents(); + if (max_downloading > -1) + sessionSettings.active_downloads = max_downloading + HiddenData::getDownloadingSize(); + else + sessionSettings.active_downloads = max_downloading; + if (max_active > -1) + sessionSettings.active_limit = max_active + HiddenData::getDownloadingSize(); + else + sessionSettings.active_limit = max_active; + s->set_settings(sessionSettings); + } + h.pause(); + } + qDebug("Received metadata for %s", qPrintable(h.hash())); + // Save metadata + const QDir torrentBackup(fsutils::BTBackupLocation()); + if (!QFile::exists(torrentBackup.absoluteFilePath(h.hash()+QString(".torrent")))) + h.save_torrent_file(torrentBackup.absoluteFilePath(h.hash()+QString(".torrent"))); + // Copy the torrent file to the export folder + if (m_torrentExportEnabled) + exportTorrentFile(h); + // Append .!qB to incomplete files + if (appendqBExtension) + appendqBextensionToTorrent(h, true); - a = s->pop_alert(); + if (!HiddenData::hasData(hash)) + emit metadataReceived(h); + else + emit metadataReceivedHidden(h); + + if (h.is_paused() && !HiddenData::hasData(hash)) { + // XXX: Unfortunately libtorrent-rasterbar does not send a torrent_paused_alert + // and the torrent can be paused when metadata is received + emit pausedTorrent(h); + } + + } + } + else if (file_error_alert* p = dynamic_cast(a)) { + QTorrentHandle h(p->handle); + if (h.is_valid()) { + h.pause(); + std::cerr << "File Error: " << p->message().c_str() << std::endl; + addConsoleMessage(tr("An I/O error occurred, '%1' paused.").arg(h.name())); + addConsoleMessage(tr("Reason: %1").arg(misc::toQStringU(p->message()))); + if (h.is_valid()) { + emit fullDiskError(h, misc::toQStringU(p->message())); + //h.pause(); + emit pausedTorrent(h); + } + } + } + else if (file_completed_alert* p = dynamic_cast(a)) { + QTorrentHandle h(p->handle); + qDebug("A file completed download in torrent %s", qPrintable(h.name())); + if (appendqBExtension) { + qDebug("appendqBTExtension is true"); + QString name = h.filepath_at(p->index); + if (name.endsWith(".!qB")) { + const QString old_name = name; + name.chop(4); + qDebug("Renaming %s to %s", qPrintable(old_name), qPrintable(name)); + h.rename_file(p->index, name); + } + } + } + else if (torrent_paused_alert* p = dynamic_cast(a)) { + if (p->handle.is_valid()) { + QTorrentHandle h(p->handle); + if (!HiddenData::hasData(h.hash())) { + if (!h.has_error()) + h.save_resume_data(); + emit pausedTorrent(h); + } + } + } + else if (tracker_error_alert* p = dynamic_cast(a)) { + // Level: fatal + QTorrentHandle h(p->handle); + if (h.is_valid()) { + // Authentication + if (p->status_code != 401) { + qDebug("Received a tracker error for %s: %s", p->url.c_str(), p->msg.c_str()); + const QString tracker_url = misc::toQString(p->url); + QHash trackers_data = trackersInfos.value(h.hash(), QHash()); + TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url)); + data.last_message = misc::toQStringU(p->msg); + trackers_data.insert(tracker_url, data); + trackersInfos[h.hash()] = trackers_data; + } else { + emit trackerAuthenticationRequired(h); + } + } + } + else if (tracker_reply_alert* p = dynamic_cast(a)) { + const QTorrentHandle h(p->handle); + if (h.is_valid()) { + qDebug("Received a tracker reply from %s (Num_peers=%d)", p->url.c_str(), p->num_peers); + // Connection was successful now. Remove possible old errors + QHash trackers_data = trackersInfos.value(h.hash(), QHash()); + const QString tracker_url = misc::toQString(p->url); + TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url)); + data.last_message = ""; // Reset error/warning message + data.num_peers = p->num_peers; + trackers_data.insert(tracker_url, data); + trackersInfos[h.hash()] = trackers_data; + } + } + else if (tracker_warning_alert* p = dynamic_cast(a)) { + const QTorrentHandle h(p->handle); + if (h.is_valid()) { + // Connection was successful now but there is a warning message + QHash trackers_data = trackersInfos.value(h.hash(), QHash()); + const QString tracker_url = misc::toQString(p->url); + TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url)); + data.last_message = misc::toQStringU(p->msg); // Store warning message + trackers_data.insert(tracker_url, data); + trackersInfos[h.hash()] = trackers_data; + qDebug("Received a tracker warning from %s: %s", p->url.c_str(), p->msg.c_str()); + } + } + else if (portmap_error_alert* p = dynamic_cast(a)) { + addConsoleMessage(tr("UPnP/NAT-PMP: Port mapping failure, message: %1").arg(misc::toQStringU(p->message())), "red"); + //emit UPnPError(QString(p->msg().c_str())); + } + else if (portmap_alert* p = dynamic_cast(a)) { + 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"); + //emit UPnPSuccess(QString(p->msg().c_str())); + } + else if (peer_blocked_alert* p = dynamic_cast(a)) { + boost::system::error_code ec; + string ip = p->ip.to_string(ec); + if (!ec) { + addPeerBanMessage(QString::fromLatin1(ip.c_str()), true); + //emit peerBlocked(QString::fromLatin1(ip.c_str())); + } + } + else if (peer_ban_alert* p = dynamic_cast(a)) { + boost::system::error_code ec; + string ip = p->ip.address().to_string(ec); + if (!ec) { + addPeerBanMessage(QString::fromLatin1(ip.c_str()), false); + //emit peerBlocked(QString::fromLatin1(ip.c_str())); + } + } + else if (fastresume_rejected_alert* p = dynamic_cast(a)) { + QTorrentHandle h(p->handle); + if (h.is_valid()) { + qDebug("/!\\ Fast resume failed for %s, reason: %s", qPrintable(h.name()), p->message().c_str()); + if (p->error.value() == 134 && TorrentPersistentData::isSeed(h.hash()) && h.has_missing_files()) { + const QString hash = h.hash(); + // Mismatching file size (files were probably moved + addConsoleMessage(tr("File sizes mismatch for torrent %1, pausing it.").arg(h.name())); + TorrentPersistentData::setErrorState(hash, true); + pauseTorrent(hash); + } else { + addConsoleMessage(tr("Fast resume data was rejected for torrent %1, checking again...").arg(h.name()), QString::fromUtf8("red")); + addConsoleMessage(tr("Reason: %1").arg(misc::toQStringU(p->message()))); + } + } + } + else if (url_seed_alert* p = dynamic_cast(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")); + //emit urlSeedProblem(QString::fromUtf8(p->url.c_str()), QString::fromUtf8(p->msg().c_str())); + } + else if (listen_succeeded_alert *p = dynamic_cast(a)) { + boost::system::error_code ec; + QString proto = "TCP"; +#if LIBTORRENT_VERSION_NUM >= 10000 + if (p->sock_type == listen_succeeded_alert::udp) + proto = "UDP"; + else if (p->sock_type == listen_succeeded_alert::tcp) + proto = "TCP"; + else if (p->sock_type == listen_succeeded_alert::tcp_ssl) + proto = "TCP_SSL"; +#endif + qDebug() << "Successfully listening on " << proto << p->endpoint.address().to_string(ec).c_str() << "/" << p->endpoint.port(); + addConsoleMessage(tr("qBittorrent is successfully listening on interface %1 port: %2/%3", "e.g: qBittorrent is successfully listening on interface 192.168.0.1 port: TCP/6881").arg(p->endpoint.address().to_string(ec).c_str()).arg(proto).arg(QString::number(p->endpoint.port())), "blue"); + // Force reannounce on all torrents because some trackers blacklist some ports + std::vector torrents = s->get_torrents(); + + std::vector::iterator it = torrents.begin(); + std::vector::iterator itend = torrents.end(); + for ( ; it != itend; ++it) { + it->force_reannounce(); + } + } + else if (listen_failed_alert *p = dynamic_cast(a)) { + boost::system::error_code ec; + QString proto = "TCP"; +#if LIBTORRENT_VERSION_NUM >= 10000 + if (p->sock_type == listen_failed_alert::udp) + proto = "UDP"; + else if (p->sock_type == listen_failed_alert::tcp) + proto = "TCP"; + else if (p->sock_type == listen_failed_alert::tcp_ssl) + proto = "TCP_SSL"; + else if (p->sock_type == listen_failed_alert::i2p) + proto = "I2P"; + else if (p->sock_type == listen_failed_alert::socks5) + proto = "SOCKS5"; +#endif + 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"); + } + else if (torrent_checked_alert* p = dynamic_cast(a)) { + QTorrentHandle h(p->handle); + if (h.is_valid()) { + const QString hash = h.hash(); + qDebug("%s have just finished checking", qPrintable(hash)); + // Save seed status + TorrentPersistentData::saveSeedStatus(h); + // Move to temp directory if necessary + if (!h.is_seed() && !defaultTempPath.isEmpty()) { + // Check if directory is different + const QDir current_dir(h.save_path()); + const QDir save_dir(getSavePath(h.hash())); + if (current_dir == save_dir) { + qDebug("Moving the torrent to the temp directory..."); + QString torrent_tmp_path = defaultTempPath; + h.move_storage(torrent_tmp_path); + } + } + emit torrentFinishedChecking(h); + if (torrentsToPausedAfterChecking.contains(hash)) { + torrentsToPausedAfterChecking.removeOne(hash); + h.pause(); + emit pausedTorrent(h); + } + } + } + else if (external_ip_alert *p = dynamic_cast(a)) { + 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"); + } + } catch (const std::exception& e) { + qWarning() << "Caught exception in readAlerts(): " << e.what(); } } diff --git a/src/qtlibtorrent/qbtsession.h b/src/qtlibtorrent/qbtsession.h index 3a35b30e0..a794ad1da 100755 --- a/src/qtlibtorrent/qbtsession.h +++ b/src/qtlibtorrent/qbtsession.h @@ -51,6 +51,7 @@ #include "qtracker.h" #include "qtorrenthandle.h" #include "trackerinfos.h" +#include "alertdispatcher.h" #define MAX_SAMPLES 20 @@ -190,6 +191,7 @@ private: void updateRatioTimer(); void recoverPersistentData(const QString &hash, const std::vector &buf); void backupPersistentData(const QString &hash, boost::shared_ptr data); + void handleAlert(libtorrent::alert* a); private slots: void addTorrentsFromScanFolder(QStringList&); @@ -234,7 +236,6 @@ signals: private: // Bittorrent libtorrent::session *s; - QPointer timerAlerts; QPointer bd_scheduler; QMap > savepathLabel_fromurl; // Use QMap for compatibility with Qt < 4.7: qHash(QUrl) QHash > trackersInfos; @@ -287,6 +288,7 @@ private: #endif // DynDNS DNSUpdater *m_dynDNSUpdater; + QAlertDispatcher* m_alertDispatcher; }; #endif diff --git a/src/qtlibtorrent/qtlibtorrent.pri b/src/qtlibtorrent/qtlibtorrent.pri index 5ded68dcd..4af0adf18 100644 --- a/src/qtlibtorrent/qtlibtorrent.pri +++ b/src/qtlibtorrent/qtlibtorrent.pri @@ -5,11 +5,13 @@ HEADERS += $$PWD/qbtsession.h \ $$PWD/bandwidthscheduler.h \ $$PWD/trackerinfos.h \ $$PWD/torrentspeedmonitor.h \ - $$PWD/filterparserthread.h + $$PWD/filterparserthread.h \ + $$PWD/alertdispatcher.h SOURCES += $$PWD/qbtsession.cpp \ $$PWD/qtorrenthandle.cpp \ - $$PWD/torrentspeedmonitor.cpp + $$PWD/torrentspeedmonitor.cpp \ + $$PWD/alertdispatcher.cpp !contains(DEFINES, DISABLE_GUI) { HEADERS += $$PWD/torrentmodel.h \ From b50d7331c7d395e2bd3cb4798cd53f4bfbc9a8ab Mon Sep 17 00:00:00 2001 From: Ivan Sorokin Date: Sun, 18 May 2014 12:07:49 +0400 Subject: [PATCH 6/8] use post_status_update() Conflicts: src/qtlibtorrent/qbtsession.cpp --- src/qtlibtorrent/qbtsession.cpp | 7 +++++++ src/qtlibtorrent/qbtsession.h | 2 ++ src/qtlibtorrent/torrentmodel.cpp | 31 ++++++++++++++++++++----------- src/qtlibtorrent/torrentmodel.h | 3 ++- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/src/qtlibtorrent/qbtsession.cpp b/src/qtlibtorrent/qbtsession.cpp index 57f83eefb..57b32a39b 100755 --- a/src/qtlibtorrent/qbtsession.cpp +++ b/src/qtlibtorrent/qbtsession.cpp @@ -2568,6 +2568,9 @@ void QBtSession::handleAlert(libtorrent::alert* a) { 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"); } + else if (state_update_alert *p = dynamic_cast(a)) { + emit stateUpdate(p->status); + } } catch (const std::exception& e) { qWarning() << "Caught exception in readAlerts(): " << e.what(); } @@ -2819,6 +2822,10 @@ quint64 QBtSession::getAlltimeUL() const { return m_speedMonitor->getAlltimeUL(); } +void QBtSession::postTorrentUpdate() { + s->post_torrent_updates(); +} + void QBtSession::handleIPFilterParsed(int ruleCount) { addConsoleMessage(tr("Successfully parsed the provided IP filter: %1 rules were applied.", "%1 is a number").arg(ruleCount)); diff --git a/src/qtlibtorrent/qbtsession.h b/src/qtlibtorrent/qbtsession.h index a794ad1da..df1888667 100755 --- a/src/qtlibtorrent/qbtsession.h +++ b/src/qtlibtorrent/qbtsession.h @@ -111,6 +111,7 @@ public: inline bool isQueueingEnabled() const { return queueingEnabled; } quint64 getAlltimeDL() const; quint64 getAlltimeUL() const; + void postTorrentUpdate(); public slots: QTorrentHandle addTorrent(QString path, bool fromScanDir = false, QString from_url = QString(), bool resumed = false); @@ -232,6 +233,7 @@ signals: void recursiveTorrentDownloadPossible(const QTorrentHandle &h); void ipFilterParsed(bool error, int ruleCount); void metadataReceivedHidden(const QTorrentHandle &h); + void stateUpdate(const std::vector &statuses); private: // Bittorrent diff --git a/src/qtlibtorrent/torrentmodel.cpp b/src/qtlibtorrent/torrentmodel.cpp index 8140f4833..cbb303908 100644 --- a/src/qtlibtorrent/torrentmodel.cpp +++ b/src/qtlibtorrent/torrentmodel.cpp @@ -101,11 +101,9 @@ TorrentModelItem::TorrentModelItem(const QTorrentHandle &h) m_name = h.name(); } -void TorrentModelItem::refreshStatus() +void TorrentModelItem::refreshStatus(libtorrent::torrent_status const& status) { - try { - m_lastStatus = m_torrent.status(); - } catch(invalid_handle&) {} + m_lastStatus = status; } TorrentModelItem::State TorrentModelItem::state() const @@ -309,6 +307,7 @@ void TorrentModel::populate() { connect(QBtSession::instance(), SIGNAL(resumedTorrent(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(stateUpdate(std::vector)), SLOT(stateUpdated(std::vector))); } TorrentModel::~TorrentModel() { @@ -441,6 +440,7 @@ void TorrentModel::handleTorrentUpdate(const QTorrentHandle &h) { const int row = torrentRow(h.hash()); if (row >= 0) { + m_torrents[row]->refreshStatus(h.status(torrent_handle::query_accurate_download_counters)); notifyTorrentChanged(row); } } @@ -453,6 +453,7 @@ void TorrentModel::handleFinishedTorrent(const QTorrentHandle& h) // Update completion date 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); } @@ -472,14 +473,8 @@ void TorrentModel::setRefreshInterval(int refreshInterval) void TorrentModel::forceModelRefresh() { - QList::const_iterator it = m_torrents.constBegin(); - QList::const_iterator itend = m_torrents.constEnd(); - for ( ; it != itend; ++it) { - TorrentModelItem* item = *it; - item->refreshStatus(); - } - emit dataChanged(index(0, 0), index(rowCount()-1, columnCount()-1)); + QBtSession::instance()->postTorrentUpdate(); } TorrentStatusReport TorrentModel::getTorrentStatusReport() const @@ -554,6 +549,20 @@ void TorrentModel::handleTorrentAboutToBeRemoved(const QTorrentHandle &h) } } +void TorrentModel::stateUpdated(const std::vector &statuses) +{ + typedef std::vector 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() { QList::const_iterator it = m_torrents.constBegin(); diff --git a/src/qtlibtorrent/torrentmodel.h b/src/qtlibtorrent/torrentmodel.h index ce1648801..511077491 100644 --- a/src/qtlibtorrent/torrentmodel.h +++ b/src/qtlibtorrent/torrentmodel.h @@ -53,7 +53,7 @@ public: public: TorrentModelItem(const QTorrentHandle& h); - void refreshStatus(); + void refreshStatus(libtorrent::torrent_status const& status); inline int columnCount() const { return NB_COLUMNS; } QVariant data(int column, int role = Qt::DisplayRole) const; bool setData(int column, const QVariant &value, int role = Qt::DisplayRole); @@ -112,6 +112,7 @@ private slots: void forceModelRefresh(); void handleTorrentLabelChange(QString previous, QString current); void handleTorrentAboutToBeRemoved(const QTorrentHandle & h); + void stateUpdated(const std::vector &statuses); private: void beginInsertTorrent(int row); From 6f38616193817f9ed1db20263e35cb008c9859a3 Mon Sep 17 00:00:00 2001 From: Ivan Sorokin Date: Sat, 24 May 2014 16:24:33 +0400 Subject: [PATCH 7/8] extract torrent statistics from torrent speed monitor to separate file --- src/qtlibtorrent/qbtsession.cpp | 7 +- src/qtlibtorrent/qbtsession.h | 2 + src/qtlibtorrent/qtlibtorrent.pri | 6 +- src/qtlibtorrent/torrentspeedmonitor.cpp | 74 +----------------- src/qtlibtorrent/torrentspeedmonitor.h | 11 --- src/qtlibtorrent/torrentstatistics.cpp | 95 ++++++++++++++++++++++++ src/qtlibtorrent/torrentstatistics.h | 41 ++++++++++ 7 files changed, 148 insertions(+), 88 deletions(-) create mode 100644 src/qtlibtorrent/torrentstatistics.cpp create mode 100644 src/qtlibtorrent/torrentstatistics.h diff --git a/src/qtlibtorrent/qbtsession.cpp b/src/qtlibtorrent/qbtsession.cpp index 57b32a39b..3d5ebef22 100755 --- a/src/qtlibtorrent/qbtsession.cpp +++ b/src/qtlibtorrent/qbtsession.cpp @@ -39,6 +39,7 @@ #include "smtp.h" #include "filesystemwatcher.h" #include "torrentspeedmonitor.h" +#include "torrentstatistics.h" #include "qbtsession.h" #include "misc.h" #include "fs_utils.h" @@ -158,6 +159,7 @@ QBtSession::QBtSession() // Torrent speed monitor m_speedMonitor = new TorrentSpeedMonitor(this); m_speedMonitor->start(); + m_torrentStatistics = new TorrentStatistics(this, this); // To download from urls downloader = new DownloadThread(this); connect(downloader, SIGNAL(downloadFinished(QString, QString)), SLOT(processDownloadedFile(QString, QString))); @@ -190,6 +192,7 @@ QBtSession::~QBtSession() { if (httpServer) delete httpServer; delete m_alertDispatcher; + delete m_torrentStatistics; qDebug("Deleting the session"); delete s; qDebug("BTSession destructor OUT"); @@ -2815,11 +2818,11 @@ qlonglong QBtSession::getETA(const QString &hash, const libtorrent::torrent_stat } quint64 QBtSession::getAlltimeDL() const { - return m_speedMonitor->getAlltimeDL(); + return m_torrentStatistics->getAlltimeDL(); } quint64 QBtSession::getAlltimeUL() const { - return m_speedMonitor->getAlltimeUL(); + return m_torrentStatistics->getAlltimeUL(); } void QBtSession::postTorrentUpdate() { diff --git a/src/qtlibtorrent/qbtsession.h b/src/qtlibtorrent/qbtsession.h index df1888667..fff6849b2 100755 --- a/src/qtlibtorrent/qbtsession.h +++ b/src/qtlibtorrent/qbtsession.h @@ -61,6 +61,7 @@ class HttpServer; class BandwidthScheduler; class ScanFoldersModel; class TorrentSpeedMonitor; +class TorrentStatistics; class DNSUpdater; const int MAX_LOG_MESSAGES = 1000; @@ -291,6 +292,7 @@ private: // DynDNS DNSUpdater *m_dynDNSUpdater; QAlertDispatcher* m_alertDispatcher; + TorrentStatistics* m_torrentStatistics; }; #endif diff --git a/src/qtlibtorrent/qtlibtorrent.pri b/src/qtlibtorrent/qtlibtorrent.pri index 4af0adf18..4610360a5 100644 --- a/src/qtlibtorrent/qtlibtorrent.pri +++ b/src/qtlibtorrent/qtlibtorrent.pri @@ -6,12 +6,14 @@ HEADERS += $$PWD/qbtsession.h \ $$PWD/trackerinfos.h \ $$PWD/torrentspeedmonitor.h \ $$PWD/filterparserthread.h \ - $$PWD/alertdispatcher.h + $$PWD/alertdispatcher.h \ + $$PWD/torrentstatistics.h SOURCES += $$PWD/qbtsession.cpp \ $$PWD/qtorrenthandle.cpp \ $$PWD/torrentspeedmonitor.cpp \ - $$PWD/alertdispatcher.cpp + $$PWD/alertdispatcher.cpp \ + $$PWD/torrentstatistics.cpp !contains(DEFINES, DISABLE_GUI) { HEADERS += $$PWD/torrentmodel.h \ diff --git a/src/qtlibtorrent/torrentspeedmonitor.cpp b/src/qtlibtorrent/torrentspeedmonitor.cpp index 623e47b7e..a4400445d 100644 --- a/src/qtlibtorrent/torrentspeedmonitor.cpp +++ b/src/qtlibtorrent/torrentspeedmonitor.cpp @@ -62,21 +62,16 @@ private: }; TorrentSpeedMonitor::TorrentSpeedMonitor(QBtSession* session) : - QThread(session), m_abort(false), m_session(session), - sessionUL(0), sessionDL(0), lastWrite(0), dirty(false) + QThread(session), m_abort(false), m_session(session) { connect(m_session, SIGNAL(deletedTorrent(QString)), SLOT(removeSamples(QString))); connect(m_session, SIGNAL(pausedTorrent(QTorrentHandle)), SLOT(removeSamples(QTorrentHandle))); - loadStats(); } TorrentSpeedMonitor::~TorrentSpeedMonitor() { m_abort = true; m_abortCond.wakeOne(); wait(); - if (dirty) - lastWrite = 0; - saveStats(); } void TorrentSpeedMonitor::run() @@ -84,7 +79,6 @@ void TorrentSpeedMonitor::run() do { m_mutex.lock(); getSamples(); - saveStats(); m_abortCond.wait(&m_mutex, 1000); m_mutex.unlock(); } while(!m_abort); @@ -161,16 +155,6 @@ qlonglong TorrentSpeedMonitor::getETA(const QString &hash, const libtorrent::tor 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 { - QMutexLocker l(&m_mutex); - return alltimeUL + sessionUL; -} - void TorrentSpeedMonitor::getSamples() { const std::vector torrents = m_session->getSession()->get_torrents(); @@ -187,60 +171,4 @@ void TorrentSpeedMonitor::getSamples() } } 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"); - } } diff --git a/src/qtlibtorrent/torrentspeedmonitor.h b/src/qtlibtorrent/torrentspeedmonitor.h index 3707e7740..ab5ab3ac1 100644 --- a/src/qtlibtorrent/torrentspeedmonitor.h +++ b/src/qtlibtorrent/torrentspeedmonitor.h @@ -49,16 +49,12 @@ public: explicit TorrentSpeedMonitor(QBtSession* session); ~TorrentSpeedMonitor(); 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: void removeSamples(const QString& hash); @@ -70,13 +66,6 @@ private: QHash m_samples; mutable QMutex m_mutex; 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 diff --git a/src/qtlibtorrent/torrentstatistics.cpp b/src/qtlibtorrent/torrentstatistics.cpp new file mode 100644 index 000000000..b0229887d --- /dev/null +++ b/src/qtlibtorrent/torrentstatistics.cpp @@ -0,0 +1,95 @@ +#include "torrentstatistics.h" + +#include + +#include + +#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"); + } +} diff --git a/src/qtlibtorrent/torrentstatistics.h b/src/qtlibtorrent/torrentstatistics.h new file mode 100644 index 000000000..0c38fa857 --- /dev/null +++ b/src/qtlibtorrent/torrentstatistics.h @@ -0,0 +1,41 @@ +#ifndef TORRENTSTATISTICS_H +#define TORRENTSTATISTICS_H + +#include +#include + +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 From c2a23f2265a85fbc36c73bdcca76ed89941a3513 Mon Sep 17 00:00:00 2001 From: Ivan Sorokin Date: Sun, 25 May 2014 00:30:30 +0400 Subject: [PATCH 8/8] use stats_alert in TorrentSpeedMonitor Conflicts: src/qtlibtorrent/qbtsession.cpp --- src/qtlibtorrent/qbtsession.cpp | 6 ++-- src/qtlibtorrent/qbtsession.h | 2 ++ src/qtlibtorrent/torrentspeedmonitor.cpp | 43 +++++------------------- src/qtlibtorrent/torrentspeedmonitor.h | 17 +++------- 4 files changed, 18 insertions(+), 50 deletions(-) diff --git a/src/qtlibtorrent/qbtsession.cpp b/src/qtlibtorrent/qbtsession.cpp index 3d5ebef22..29d736c82 100755 --- a/src/qtlibtorrent/qbtsession.cpp +++ b/src/qtlibtorrent/qbtsession.cpp @@ -134,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())); // 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 loadSessionState(); // Enabling plugins @@ -158,7 +158,6 @@ QBtSession::QBtSession() configureSession(); // Torrent speed monitor m_speedMonitor = new TorrentSpeedMonitor(this); - m_speedMonitor->start(); m_torrentStatistics = new TorrentStatistics(this, this); // To download from urls downloader = new DownloadThread(this); @@ -2574,6 +2573,9 @@ void QBtSession::handleAlert(libtorrent::alert* a) { else if (state_update_alert *p = dynamic_cast(a)) { emit stateUpdate(p->status); } + else if (stats_alert *p = dynamic_cast(a)) { + emit statsReceived(*p); + } } catch (const std::exception& e) { qWarning() << "Caught exception in readAlerts(): " << e.what(); } diff --git a/src/qtlibtorrent/qbtsession.h b/src/qtlibtorrent/qbtsession.h index fff6849b2..46012b9b8 100755 --- a/src/qtlibtorrent/qbtsession.h +++ b/src/qtlibtorrent/qbtsession.h @@ -47,6 +47,7 @@ #include #include #include +#include #include "qtracker.h" #include "qtorrenthandle.h" @@ -235,6 +236,7 @@ signals: void ipFilterParsed(bool error, int ruleCount); void metadataReceivedHidden(const QTorrentHandle &h); void stateUpdate(const std::vector &statuses); + void statsReceived(const libtorrent::stats_alert&); private: // Bittorrent diff --git a/src/qtlibtorrent/torrentspeedmonitor.cpp b/src/qtlibtorrent/torrentspeedmonitor.cpp index a4400445d..0d70b5502 100644 --- a/src/qtlibtorrent/torrentspeedmonitor.cpp +++ b/src/qtlibtorrent/torrentspeedmonitor.cpp @@ -28,7 +28,6 @@ * Contact : chris@qbittorrent.org */ -#include #include #include #include @@ -61,28 +60,16 @@ private: QList > m_speedSamples; }; -TorrentSpeedMonitor::TorrentSpeedMonitor(QBtSession* session) : - QThread(session), m_abort(false), m_session(session) +TorrentSpeedMonitor::TorrentSpeedMonitor(QBtSession* session) + : m_session(session) { connect(m_session, SIGNAL(deletedTorrent(QString)), SLOT(removeSamples(QString))); connect(m_session, SIGNAL(pausedTorrent(QTorrentHandle)), SLOT(removeSamples(QTorrentHandle))); + connect(m_session, SIGNAL(statsReceived(libtorrent::stats_alert)), SLOT(statsReceived(libtorrent::stats_alert))); } -TorrentSpeedMonitor::~TorrentSpeedMonitor() { - m_abort = true; - m_abortCond.wakeOne(); - wait(); -} - -void TorrentSpeedMonitor::run() -{ - do { - m_mutex.lock(); - getSamples(); - m_abortCond.wait(&m_mutex, 1000); - m_mutex.unlock(); - } while(!m_abort); -} +TorrentSpeedMonitor::~TorrentSpeedMonitor() +{} void SpeedSample::addSample(int speedDL, int speedUL) { @@ -126,8 +113,6 @@ void TorrentSpeedMonitor::removeSamples(const QTorrentHandle& h) { 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)) return MAX_ETA; @@ -155,20 +140,8 @@ qlonglong TorrentSpeedMonitor::getETA(const QString &hash, const libtorrent::tor return (status.total_wanted - status.total_wanted_done) / speed_average.download; } -void TorrentSpeedMonitor::getSamples() +void TorrentSpeedMonitor::statsReceived(const stats_alert &stats) { - const std::vector torrents = m_session->getSession()->get_torrents(); - - std::vector::const_iterator it = torrents.begin(); - std::vector::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&) {} - } + 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); } diff --git a/src/qtlibtorrent/torrentspeedmonitor.h b/src/qtlibtorrent/torrentspeedmonitor.h index ab5ab3ac1..0a6dbbcc1 100644 --- a/src/qtlibtorrent/torrentspeedmonitor.h +++ b/src/qtlibtorrent/torrentspeedmonitor.h @@ -32,39 +32,30 @@ #define TORRENTSPEEDMONITOR_H #include -#include -#include #include -#include #include "qtorrenthandle.h" +#include class QBtSession; class SpeedSample; -class TorrentSpeedMonitor : public QThread +class TorrentSpeedMonitor : public QObject { Q_OBJECT + Q_DISABLE_COPY(TorrentSpeedMonitor) public: explicit TorrentSpeedMonitor(QBtSession* session); ~TorrentSpeedMonitor(); qlonglong getETA(const QString &hash, const libtorrent::torrent_status &status) const; -protected: - void run(); - -private: - void getSamples(); - private slots: + void statsReceived(const libtorrent::stats_alert& stats); void removeSamples(const QString& hash); void removeSamples(const QTorrentHandle& h); private: - bool m_abort; - QWaitCondition m_abortCond; QHash m_samples; - mutable QMutex m_mutex; QBtSession *m_session; };