diff --git a/src/properties/propertieswidget.cpp b/src/properties/propertieswidget.cpp index 1e88ff6e3..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.hash()); + 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/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 acb6863ce..29d736c82 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" @@ -114,6 +115,7 @@ QBtSession::QBtSession() , m_upnp(0), m_natpmp(0) #endif , m_dynDNSUpdater(0) + , m_alertDispatcher(0) { BigRatioTimer = new QTimer(this); BigRatioTimer->setInterval(10000); @@ -132,7 +134,7 @@ QBtSession::QBtSession() addConsoleMessage("Peer ID: "+misc::toQString(fingerprint(peer_id.toLocal8Bit().constData(), version.at(0), version.at(1), version.at(2), version.at(3)).to_string())); // 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 @@ -147,9 +149,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&))); @@ -157,7 +158,7 @@ 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); connect(downloader, SIGNAL(downloadFinished(QString, QString)), SLOT(processDownloadedFile(QString, QString))); @@ -179,7 +180,6 @@ QBtSession::~QBtSession() { // Delete our objects if (m_tracker) delete m_tracker; - delete timerAlerts; if (BigRatioTimer) delete BigRatioTimer; if (filterParser) @@ -190,6 +190,8 @@ QBtSession::~QBtSession() { // HTTP Server if (httpServer) delete httpServer; + delete m_alertDispatcher; + delete m_torrentStatistics; qDebug("Deleting the session"); delete s; qDebug("BTSession destructor OUT"); @@ -220,7 +222,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.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,15 +1558,11 @@ bool QBtSession::enableDHT(bool b) { return true; } -qreal QBtSession::getRealRatio(const QString &hash) const { - QTorrentHandle h = getTorrentHandle(hash); - 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 @@ -1577,7 +1575,7 @@ qreal QBtSession::getRealRatio(const QString &hash) const { if (all_time_download == 0) { if (all_time_upload == 0) - return 0; + return 0.0; return MAX_RATIO+1; } @@ -1612,7 +1610,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(); @@ -1637,52 +1634,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; + } } } @@ -2107,11 +2104,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); @@ -2120,454 +2118,466 @@ 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"); + } + 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(); } } @@ -2804,17 +2814,21 @@ void QBtSession::drop() } } -qlonglong QBtSession::getETA(const QString &hash) const +qlonglong QBtSession::getETA(const QString &hash, const libtorrent::torrent_status &status) const { - return m_speedMonitor->getETA(hash); + return m_speedMonitor->getETA(hash, status); } quint64 QBtSession::getAlltimeDL() const { - return m_speedMonitor->getAlltimeDL(); + return m_torrentStatistics->getAlltimeDL(); } quint64 QBtSession::getAlltimeUL() const { - return m_speedMonitor->getAlltimeUL(); + return m_torrentStatistics->getAlltimeUL(); +} + +void QBtSession::postTorrentUpdate() { + s->post_torrent_updates(); } void QBtSession::handleIPFilterParsed(int ruleCount) diff --git a/src/qtlibtorrent/qbtsession.h b/src/qtlibtorrent/qbtsession.h index c0bf51e0b..46012b9b8 100755 --- a/src/qtlibtorrent/qbtsession.h +++ b/src/qtlibtorrent/qbtsession.h @@ -47,10 +47,12 @@ #include #include #include +#include #include "qtracker.h" #include "qtorrenthandle.h" #include "trackerinfos.h" +#include "alertdispatcher.h" #define MAX_SAMPLES 20 @@ -60,6 +62,7 @@ class HttpServer; class BandwidthScheduler; class ScanFoldersModel; class TorrentSpeedMonitor; +class TorrentStatistics; class DNSUpdater; const int MAX_LOG_MESSAGES = 1000; @@ -92,7 +95,7 @@ public: qreal getPayloadUploadRate() const; libtorrent::session_status getSessionStatus() const; int getListenPort() const; - qreal getRealRatio(const QString& hash) const; + qreal getRealRatio(const libtorrent::torrent_status &status) const; QHash getTrackersInfo(const QString &hash) const; bool hasActiveTorrents() const; bool hasDownloadingTorrents() const; @@ -110,6 +113,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); @@ -121,7 +125,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); @@ -190,6 +194,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&); @@ -230,11 +235,12 @@ signals: void recursiveTorrentDownloadPossible(const QTorrentHandle &h); 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 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 +293,8 @@ private: #endif // DynDNS DNSUpdater *m_dynDNSUpdater; + QAlertDispatcher* m_alertDispatcher; + TorrentStatistics* m_torrentStatistics; }; #endif diff --git a/src/qtlibtorrent/qtlibtorrent.pri b/src/qtlibtorrent/qtlibtorrent.pri index 5ded68dcd..4610360a5 100644 --- a/src/qtlibtorrent/qtlibtorrent.pri +++ b/src/qtlibtorrent/qtlibtorrent.pri @@ -5,11 +5,15 @@ HEADERS += $$PWD/qbtsession.h \ $$PWD/bandwidthscheduler.h \ $$PWD/trackerinfos.h \ $$PWD/torrentspeedmonitor.h \ - $$PWD/filterparserthread.h + $$PWD/filterparserthread.h \ + $$PWD/alertdispatcher.h \ + $$PWD/torrentstatistics.h SOURCES += $$PWD/qbtsession.cpp \ $$PWD/qtorrenthandle.cpp \ - $$PWD/torrentspeedmonitor.cpp + $$PWD/torrentspeedmonitor.cpp \ + $$PWD/alertdispatcher.cpp \ + $$PWD/torrentstatistics.cpp !contains(DEFINES, DISABLE_GUI) { HEADERS += $$PWD/torrentmodel.h \ 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 58df2e413..cbb303908 100644 --- a/src/qtlibtorrent/torrentmodel.cpp +++ b/src/qtlibtorrent/torrentmodel.cpp @@ -37,8 +37,60 @@ 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_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())) @@ -49,75 +101,80 @@ TorrentModelItem::TorrentModelItem(const QTorrentHandle &h) m_name = h.name(); } +void TorrentModelItem::refreshStatus(libtorrent::torrent_status const& status) +{ + m_lastStatus = status; +} + TorrentModelItem::State TorrentModelItem::state() const { try { // Pause or Queued - if (m_torrent.is_paused()) { - m_icon = QIcon(":/Icons/skin/paused.png"); + 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) { - m_icon = QIcon(":/Icons/skin/queued.png"); + 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 = 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"); + if (m_lastStatus.download_payload_rate > 0) { + 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; } } case torrent_status::finished: case torrent_status::seeding: - if (m_torrent.upload_payload_rate() > 0) { - m_icon = QIcon(":/Icons/skin/uploading.png"); + if (m_lastStatus.upload_payload_rate > 0) { + 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; + return m_torrent.is_seed(m_lastStatus) ? 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; } @@ -173,28 +230,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_torrent.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.hash()); + return QBtSession::instance()->getRealRatio(m_lastStatus); case TR_LABEL: return m_label; case TR_ADD_DATE: @@ -202,19 +259,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: @@ -250,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() { @@ -382,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); } } @@ -394,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); } @@ -414,6 +474,7 @@ void TorrentModel::setRefreshInterval(int refreshInterval) void TorrentModel::forceModelRefresh() { emit dataChanged(index(0, 0), index(rowCount()-1, columnCount()-1)); + QBtSession::instance()->postTorrentUpdate(); } TorrentStatusReport TorrentModel::getTorrentStatusReport() const @@ -488,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 94e9dcae9..511077491 100644 --- a/src/qtlibtorrent/torrentmodel.h +++ b/src/qtlibtorrent/torrentmodel.h @@ -53,6 +53,7 @@ public: public: TorrentModelItem(const QTorrentHandle& h); + 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); @@ -66,6 +67,7 @@ private: private: QTorrentHandle m_torrent; + libtorrent::torrent_status m_lastStatus; QDateTime m_addedTime; QDateTime m_seedTime; QString m_label; @@ -110,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); diff --git a/src/qtlibtorrent/torrentspeedmonitor.cpp b/src/qtlibtorrent/torrentspeedmonitor.cpp index 678a63c7b..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,34 +60,16 @@ private: QList > m_speedSamples; }; -TorrentSpeedMonitor::TorrentSpeedMonitor(QBtSession* session) : - QThread(session), m_abort(false), m_session(session), - sessionUL(0), sessionDL(0), lastWrite(0), dirty(false) +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))); - loadStats(); + connect(m_session, SIGNAL(statsReceived(libtorrent::stats_alert)), SLOT(statsReceived(libtorrent::stats_alert))); } -TorrentSpeedMonitor::~TorrentSpeedMonitor() { - m_abort = true; - m_abortCond.wakeOne(); - wait(); - if (dirty) - lastWrite = 0; - saveStats(); -} - -void TorrentSpeedMonitor::run() -{ - do { - m_mutex.lock(); - getSamples(); - saveStats(); - m_abortCond.wait(&m_mutex, 1000); - m_mutex.unlock(); - } while(!m_abort); -} +TorrentSpeedMonitor::~TorrentSpeedMonitor() +{} void SpeedSample::addSample(int speedDL, int speedUL) { @@ -130,16 +111,14 @@ 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,99 +127,21 @@ 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 { - QMutexLocker l(&m_mutex); - return alltimeDL + sessionDL; -} - -quint64 TorrentSpeedMonitor::getAlltimeUL() const { - QMutexLocker l(&m_mutex); - return alltimeUL + sessionUL; -} - -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&) {} - } - 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"); - } + 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 0d71d64f6..0a6dbbcc1 100644 --- a/src/qtlibtorrent/torrentspeedmonitor.h +++ b/src/qtlibtorrent/torrentspeedmonitor.h @@ -32,51 +32,31 @@ #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; - quint64 getAlltimeDL() const; - quint64 getAlltimeUL() const; - -protected: - void run(); - -private: - void getSamples(); - void saveStats() const; - void loadStats(); + qlonglong getETA(const QString &hash, const libtorrent::torrent_status &status) const; 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; - // 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 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); diff --git a/src/webui/btjson.cpp b/src/webui/btjson.cpp index 373632cd4..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.hash()); + 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.hash()); + 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();