Browse Source

use set_alert_dispatch instead of timer to get an alerts from libtorrent

libtorrent allows setting a custom dispatch handler that is invoked in
libtorrent thread when new alerts are incoming. QAlertDispatcher is a
class that allows to translate these alerts to UI thread.

The concept is very simple:

1. On initialization QAlertDispatcher constructor calls set_alert_dispatch() passing
 QAlertDispatcher::dispatch as argument.

2. On deinitialization destructor calls set_alert_dispatch() passing a empty
 function. (line 25) libtorrent handles thos and switches back to queuing
 alerts in queue.

3. QAlertDispatcher::dispatch() adds alert to queue and notifies UI thread that new
 alerts are incoming. Enqueuing is done in function enqueueToMainThread().
 The invariant of class is the following:

    if alert queue is not empty, in message loop of UI thread contains a queued
    invocation of deliverSignal().

4. When message loop is pumped UI thread execute deliverSignal() function.
 It emit appropriate signal and if queue is still not empty (for example
 if slot doesn't grab alerts) rewind enqueuing to main thread.

This is a idea. But here is some details.

1. When QAlertDispatcher is destoyed, libtorrent still can call
QAlertDispatcher::dispatch a few times after destruction. This is
handled by passing a "tag". A tag is a object that references QAlertDispatch.
Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag
and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called
with invalid tag it simply discard an alert.

    Therefore we could drop a few alerts during unsubscription. So we unsubscribe
    only at exit when missing some alerts is not a problem.

2. Another problem is in QBtSession::saveFastResumeData(). It pumps alert
queue synchronously. My first attempt was to destroy QAlertDispatcher
and then pump libtorrent queue. But as I was afraid of losing alerts I
supported synchronous querying of alerts in QAlertDispatcher.
(QAlertDispatcher::getPendingAlerts)

Conflicts:
	src/qtlibtorrent/qbtsession.cpp
adaptive-webui-19844
Ivan Sorokin 11 years ago
parent
commit
eb46326d23
  1. 93
      src/qtlibtorrent/alertdispatcher.cpp
  2. 43
      src/qtlibtorrent/alertdispatcher.h
  3. 889
      src/qtlibtorrent/qbtsession.cpp
  4. 4
      src/qtlibtorrent/qbtsession.h
  5. 6
      src/qtlibtorrent/qtlibtorrent.pri

93
src/qtlibtorrent/alertdispatcher.cpp

@ -0,0 +1,93 @@
#include "alertdispatcher.h"
#include <boost/bind.hpp>
#include <boost/make_shared.hpp>
#include <QMutexLocker>
QAlertDispatcher::QAlertDispatcher(libtorrent::session *session, QObject* parent)
: QObject(parent)
, session(session)
, current_tag(new QAtomicPointer<QAlertDispatcher>(this))
, event_posted(false) {
session->set_alert_dispatch(boost::bind(&QAlertDispatcher::dispatch, current_tag, _1));
}
QAlertDispatcher::~QAlertDispatcher() {
// When QAlertDispatcher is destoyed, libtorrent still can call
// QAlertDispatcher::dispatch a few times after destruction. This is
// handled by passing a "tag". A tag is a object that references QAlertDispatch.
// Tag could be invalidated. So on destruction QAlertDispatcher invalidates a tag
// and then unsubscribes from alerts. When QAlertDispatcher::dispatch is called
// with invalid tag it simply discard an alert.
{
QMutexLocker lock(&(alerts_mutex));
*current_tag = 0;
current_tag.clear();
}
typedef boost::function<void (std::auto_ptr<libtorrent::alert>)> dispatch_function_t;
session->set_alert_dispatch(dispatch_function_t());
}
void QAlertDispatcher::getPendingAlertsNoWait(std::deque<libtorrent::alert*>& out) {
Q_ASSERT(out.empty());
QMutexLocker lock(&(alerts_mutex));
std::swap(alerts, out);
event_posted = false;
}
void QAlertDispatcher::getPendingAlerts(std::deque<libtorrent::alert*>& out) {
assert(out.empty());
QMutexLocker lock(&(alerts_mutex));
while (alerts.empty())
alerts_condvar.wait(&(alerts_mutex));
std::swap(alerts, out);
event_posted = false;
}
void QAlertDispatcher::dispatch(QSharedPointer<QAtomicPointer<QAlertDispatcher> > tag,
std::auto_ptr<libtorrent::alert> alert_ptr) {
QAlertDispatcher* that = *tag;
if (!that)
return;
QMutexLocker lock(&(that->alerts_mutex));
that = *tag;
if (!that)
return;
bool was_empty = that->alerts.empty();
that->alerts.push_back(alert_ptr.get());
alert_ptr.release();
if (was_empty)
that->alerts_condvar.wakeAll();
that->enqueueToMainThread();
Q_ASSERT(that->current_tag == tag);
}
void QAlertDispatcher::enqueueToMainThread() {
if (!event_posted) {
event_posted = true;
QMetaObject::invokeMethod(this, "deliverSignal", Qt::QueuedConnection);
}
}
void QAlertDispatcher::deliverSignal() {
emit alertsReceived();
QMutexLocker lock(&(alerts_mutex));
event_posted = false;
if (!alerts.empty())
enqueueToMainThread();
}

43
src/qtlibtorrent/alertdispatcher.h

@ -0,0 +1,43 @@
#ifndef ALERTDISPATCHER_H
#define ALERTDISPATCHER_H
#include <QObject>
#include <QMutex>
#include <QWaitCondition>
#include <QAtomicPointer>
#include <QSharedPointer>
#include <libtorrent/session.hpp>
class QAlertDispatcher : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(QAlertDispatcher)
public:
QAlertDispatcher(libtorrent::session *session, QObject* parent);
~QAlertDispatcher();
void getPendingAlertsNoWait(std::deque<libtorrent::alert*>&);
void getPendingAlerts(std::deque<libtorrent::alert*>&);
signals:
void alertsReceived();
private:
static void dispatch(QSharedPointer<QAtomicPointer<QAlertDispatcher> >,
std::auto_ptr<libtorrent::alert>);
void enqueueToMainThread();
private slots:
void deliverSignal();
private:
libtorrent::session *session;
QMutex alerts_mutex;
QWaitCondition alerts_condvar;
std::deque<libtorrent::alert*> alerts;
QSharedPointer<QAtomicPointer<QAlertDispatcher> > current_tag;
bool event_posted;
};
#endif // ALERTDISPATCHER_H

889
src/qtlibtorrent/qbtsession.cpp

@ -114,6 +114,7 @@ QBtSession::QBtSession()
, m_upnp(0), m_natpmp(0) , m_upnp(0), m_natpmp(0)
#endif #endif
, m_dynDNSUpdater(0) , m_dynDNSUpdater(0)
, m_alertDispatcher(0)
{ {
BigRatioTimer = new QTimer(this); BigRatioTimer = new QTimer(this);
BigRatioTimer->setInterval(10000); BigRatioTimer->setInterval(10000);
@ -147,9 +148,8 @@ QBtSession::QBtSession()
PeXEnabled = false; PeXEnabled = false;
} }
s->add_extension(&create_smart_ban_plugin); s->add_extension(&create_smart_ban_plugin);
timerAlerts = new QTimer(this); m_alertDispatcher = new QAlertDispatcher(s, this);
connect(timerAlerts, SIGNAL(timeout()), SLOT(readAlerts())); connect(m_alertDispatcher, SIGNAL(alertsReceived()), SLOT(readAlerts()));
timerAlerts->start(1000);
appendLabelToSavePath = pref.appendTorrentLabel(); appendLabelToSavePath = pref.appendTorrentLabel();
appendqBExtension = pref.useIncompleteFilesExtension(); appendqBExtension = pref.useIncompleteFilesExtension();
connect(m_scanFolders, SIGNAL(torrentsAdded(QStringList&)), SLOT(addTorrentsFromScanFolder(QStringList&))); connect(m_scanFolders, SIGNAL(torrentsAdded(QStringList&)), SLOT(addTorrentsFromScanFolder(QStringList&)));
@ -179,7 +179,6 @@ QBtSession::~QBtSession() {
// Delete our objects // Delete our objects
if (m_tracker) if (m_tracker)
delete m_tracker; delete m_tracker;
delete timerAlerts;
if (BigRatioTimer) if (BigRatioTimer)
delete BigRatioTimer; delete BigRatioTimer;
if (filterParser) if (filterParser)
@ -190,6 +189,7 @@ QBtSession::~QBtSession() {
// HTTP Server // HTTP Server
if (httpServer) if (httpServer)
delete httpServer; delete httpServer;
delete m_alertDispatcher;
qDebug("Deleting the session"); qDebug("Deleting the session");
delete s; delete s;
qDebug("BTSession destructor OUT"); qDebug("BTSession destructor OUT");
@ -1608,7 +1608,6 @@ void QBtSession::saveFastResumeData() {
qDebug("Saving fast resume data..."); qDebug("Saving fast resume data...");
// Stop listening for alerts // Stop listening for alerts
resumeDataTimer.stop(); resumeDataTimer.stop();
timerAlerts->stop();
int num_resume_data = 0; int num_resume_data = 0;
// Pause session // Pause session
s->pause(); s->pause();
@ -1633,52 +1632,52 @@ void QBtSession::saveFastResumeData() {
} catch(libtorrent::invalid_handle&) {} } catch(libtorrent::invalid_handle&) {}
} }
while (num_resume_data > 0) { while (num_resume_data > 0) {
alert const* a = s->wait_for_alert(seconds(30)); std::deque<alert*> alerts;
if (a == 0) { m_alertDispatcher->getPendingAlerts(alerts);
std::cerr << " aborting with " << num_resume_data << " outstanding "
"torrents to save resume data for" << std::endl; for (std::deque<alert*>::const_iterator i = alerts.begin(), end = alerts.end(); i != end; ++i)
break; {
} alert const* a = *i;
// Saving fastresume data can fail // Saving fastresume data can fail
save_resume_data_failed_alert const* rda = dynamic_cast<save_resume_data_failed_alert const*>(a); save_resume_data_failed_alert const* rda = dynamic_cast<save_resume_data_failed_alert const*>(a);
if (rda) { if (rda) {
--num_resume_data;
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<save_resume_data_alert const*>(a);
if (!rd) {
continue;
}
// Saving fast resume data was successful
--num_resume_data; --num_resume_data;
s->pop_alert(); if (!rd->resume_data) continue;
QDir torrentBackup(fsutils::BTBackupLocation());
const QTorrentHandle h(rd->handle);
if (!h.is_valid()) continue;
try { try {
// Remove old fastresume file if it exists
backupPersistentData(h.hash(), rd->resume_data);
vector<char> 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 // Remove torrent from session
if (rda->handle.is_valid()) s->remove_torrent(rd->handle);
s->remove_torrent(rda->handle); } catch(libtorrent::invalid_handle&) {}
}catch(libtorrent::libtorrent_exception) {}
continue; delete a;
}
save_resume_data_alert const* rd = dynamic_cast<save_resume_data_alert const*>(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<char> 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);
s->pop_alert();
} catch(libtorrent::invalid_handle&) {}
} }
} }
@ -2117,454 +2116,460 @@ void QBtSession::sendNotificationEmail(const QTorrentHandle &h) {
// Read alerts sent by the Bittorrent session // Read alerts sent by the Bittorrent session
void QBtSession::readAlerts() { void QBtSession::readAlerts() {
// look at session alerts and display some infos
std::auto_ptr<alert> a = s->pop_alert(); typedef std::deque<alert*> alerts_t;
while (a.get()) { alerts_t alerts;
try { m_alertDispatcher->getPendingAlertsNoWait(alerts);
if (torrent_finished_alert* p = dynamic_cast<torrent_finished_alert*>(a.get())) {
QTorrentHandle h(p->handle); for (alerts_t::const_iterator i = alerts.begin(), end = alerts.end(); i != end; ++i) {
if (h.is_valid()) { handleAlert(*i);
const QString hash = h.hash(); delete *i;
qDebug("Got a torrent finished alert for %s", qPrintable(h.name())); }
// Remove .!qB extension if necessary }
if (appendqBExtension)
appendqBextensionToTorrent(h, false); void QBtSession::handleAlert(libtorrent::alert* a) {
try {
const bool was_already_seeded = TorrentPersistentData::isSeed(hash); if (torrent_finished_alert* p = dynamic_cast<torrent_finished_alert*>(a)) {
qDebug("Was already seeded: %d", was_already_seeded); QTorrentHandle h(p->handle);
if (!was_already_seeded) { if (h.is_valid()) {
h.save_resume_data(); const QString hash = h.hash();
qDebug("Checking if the torrent contains torrent files to download"); qDebug("Got a torrent finished alert for %s", qPrintable(h.name()));
// Check if there are torrent files inside // Remove .!qB extension if necessary
for (int i=0; i<h.num_files(); ++i) { if (appendqBExtension)
const QString torrent_relpath = h.filepath_at(i); appendqBextensionToTorrent(h, false);
qDebug() << "File path:" << torrent_relpath;
if (torrent_relpath.endsWith(".torrent", Qt::CaseInsensitive)) { const bool was_already_seeded = TorrentPersistentData::isSeed(hash);
qDebug("Found possible recursive torrent download."); qDebug("Was already seeded: %d", was_already_seeded);
const QString torrent_fullpath = h.save_path()+"/"+torrent_relpath; if (!was_already_seeded) {
qDebug("Full subtorrent path is %s", qPrintable(torrent_fullpath)); h.save_resume_data();
try { qDebug("Checking if the torrent contains torrent files to download");
boost::intrusive_ptr<torrent_info> t = new torrent_info(fsutils::toNativePath(torrent_fullpath).toUtf8().constData()); // Check if there are torrent files inside
if (t->is_valid()) { for (int i=0; i<h.num_files(); ++i) {
qDebug("emitting recursiveTorrentDownloadPossible()"); const QString torrent_relpath = h.filepath_at(i);
emit recursiveTorrentDownloadPossible(h); qDebug() << "File path:" << torrent_relpath;
break; if (torrent_relpath.endsWith(".torrent", Qt::CaseInsensitive)) {
} qDebug("Found possible recursive torrent download.");
} catch(std::exception&) { const QString torrent_fullpath = h.save_path()+"/"+torrent_relpath;
qDebug("Caught error loading torrent"); qDebug("Full subtorrent path is %s", qPrintable(torrent_fullpath));
addConsoleMessage(tr("Unable to decode %1 torrent file.").arg(fsutils::toNativePath(torrent_fullpath)), QString::fromUtf8("red")); try {
boost::intrusive_ptr<torrent_info> 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()) { // Move to download directory if necessary
// Check if directory is different if (!defaultTempPath.isEmpty()) {
const QDir current_dir(h.save_path()); // Check if directory is different
const QDir save_dir(getSavePath(hash)); const QDir current_dir(h.save_path());
if (current_dir != save_dir) { const QDir save_dir(getSavePath(hash));
qDebug("Moving torrent from the temp folder"); if (current_dir != save_dir) {
h.move_storage(save_dir.absolutePath()); qDebug("Moving torrent from the temp folder");
} h.move_storage(save_dir.absolutePath());
} }
// Remember finished state }
qDebug("Saving seed status"); // Remember finished state
TorrentPersistentData::saveSeedStatus(h); qDebug("Saving seed status");
// Recheck if the user asked to TorrentPersistentData::saveSeedStatus(h);
Preferences pref; // Recheck if the user asked to
if (pref.recheckTorrentsOnCompletion()) { Preferences pref;
h.force_recheck(); 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...");
} }
qDebug("Emitting finishedTorrent() signal"); if (!ShutdownConfirmDlg::askForConfirmation(confirm_msg))
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; 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;
} }
#endif // DISABLE_GUI qDebug("Exiting the application");
qApp->exit();
return;
} }
#endif // DISABLE_GUI
} }
} }
else if (save_resume_data_alert* p = dynamic_cast<save_resume_data_alert*>(a.get())) { }
const QDir torrentBackup(fsutils::BTBackupLocation()); else if (save_resume_data_alert* p = dynamic_cast<save_resume_data_alert*>(a)) {
const QTorrentHandle h(p->handle); const QDir torrentBackup(fsutils::BTBackupLocation());
if (h.is_valid() && p->resume_data) { const QTorrentHandle h(p->handle);
const QString filepath = torrentBackup.absoluteFilePath(h.hash()+".fastresume"); if (h.is_valid() && p->resume_data) {
QFile resume_file(filepath); const QString filepath = torrentBackup.absoluteFilePath(h.hash()+".fastresume");
if (resume_file.exists()) QFile resume_file(filepath);
fsutils::forceRemove(filepath); if (resume_file.exists())
qDebug("Saving fastresume data in %s", qPrintable(filepath)); fsutils::forceRemove(filepath);
backupPersistentData(h.hash(), p->resume_data); qDebug("Saving fastresume data in %s", qPrintable(filepath));
vector<char> out; backupPersistentData(h.hash(), p->resume_data);
bencode(back_inserter(out), *p->resume_data); vector<char> out;
if (!out.empty() && resume_file.open(QIODevice::WriteOnly)) { bencode(back_inserter(out), *p->resume_data);
resume_file.write(&out[0], out.size()); if (!out.empty() && resume_file.open(QIODevice::WriteOnly)) {
resume_file.close(); resume_file.write(&out[0], out.size());
} resume_file.close();
}
}
else if (file_renamed_alert* p = dynamic_cast<file_renamed_alert*>(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<torrent_deleted_alert*>(a.get())) { }
qDebug("A torrent was deleted from the hard disk, attempting to remove the root folder too..."); else if (file_renamed_alert* p = dynamic_cast<file_renamed_alert*>(a)) {
QString hash = misc::toQString(p->info_hash); QTorrentHandle h(p->handle);
if (!hash.isEmpty()) { if (h.is_valid()) {
if (savePathsToRemove.contains(hash)) { if (h.num_files() > 1) {
const QString dirpath = savePathsToRemove.take(hash); // Check if folders were renamed
qDebug() << "Removing save path: " << dirpath << "..."; QStringList old_path_parts = h.orig_filepath_at(p->index).split("/");
bool ok = fsutils::smartRemoveEmptyFolderTree(dirpath); old_path_parts.removeLast();
Q_UNUSED(ok); QString old_path = old_path_parts.join("/");
qDebug() << "Folder was removed: " << ok; 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 { } else {
// Fallback // Single-file torrent
qDebug() << "hash is empty, use fallback to remove save path"; // Renaming a file corresponds to changing the save path
foreach (const QString& key, savePathsToRemove.keys()) { emit savePathChanged(h);
// Attempt to delete
if (QDir().rmdir(savePathsToRemove[key])) {
savePathsToRemove.remove(key);
}
}
} }
} }
else if (storage_moved_alert* p = dynamic_cast<storage_moved_alert*>(a.get())) { }
QTorrentHandle h(p->handle); else if (torrent_deleted_alert* p = dynamic_cast<torrent_deleted_alert*>(a)) {
if (h.is_valid()) { qDebug("A torrent was deleted from the hard disk, attempting to remove the root folder too...");
// Attempt to remove old folder if empty QString hash = misc::toQString(p->info_hash);
const QString old_save_path = fsutils::fromNativePath(TorrentPersistentData::getPreviousPath(h.hash())); if (!hash.isEmpty()) {
const QString new_save_path = fsutils::fromNativePath(misc::toQStringU(p->path.c_str())); if (savePathsToRemove.contains(hash)) {
qDebug("Torrent moved from %s to %s", qPrintable(old_save_path), qPrintable(new_save_path)); const QString dirpath = savePathsToRemove.take(hash);
QDir old_save_dir(old_save_path); qDebug() << "Removing save path: " << dirpath << "...";
if (old_save_dir != QDir(defaultSavePath) && old_save_dir != QDir(defaultTempPath)) { bool ok = fsutils::smartRemoveEmptyFolderTree(dirpath);
qDebug("Attempting to remove %s", qPrintable(old_save_path)); Q_UNUSED(ok);
QDir().rmpath(old_save_path); qDebug() << "Folder was removed: " << ok;
} }
if (defaultTempPath.isEmpty() || !new_save_path.startsWith(defaultTempPath)) { } else {
qDebug("Storage has been moved, updating save path to %s", qPrintable(new_save_path)); // Fallback
TorrentPersistentData::saveSavePath(h.hash(), new_save_path); 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);
} }
emit savePathChanged(h);
//h.force_recheck();
} }
} }
else if (metadata_received_alert* p = dynamic_cast<metadata_received_alert*>(a.get())) { }
QTorrentHandle h(p->handle); else if (storage_moved_alert* p = dynamic_cast<storage_moved_alert*>(a)) {
Preferences pref; QTorrentHandle h(p->handle);
if (h.is_valid()) { if (h.is_valid()) {
QString hash(h.hash()); // Attempt to remove old folder if empty
if (HiddenData::hasData(hash)) { const QString old_save_path = fsutils::fromNativePath(TorrentPersistentData::getPreviousPath(h.hash()));
HiddenData::gotMetadata(hash); const QString new_save_path = fsutils::fromNativePath(misc::toQStringU(p->path.c_str()));
if (pref.isQueueingSystemEnabled()) { qDebug("Torrent moved from %s to %s", qPrintable(old_save_path), qPrintable(new_save_path));
//Internally decrease the queue limits to ensure that that other queued items aren't started QDir old_save_dir(old_save_path);
libtorrent::session_settings sessionSettings(s->settings()); if (old_save_dir != QDir(defaultSavePath) && old_save_dir != QDir(defaultTempPath)) {
int max_downloading = pref.getMaxActiveDownloads(); qDebug("Attempting to remove %s", qPrintable(old_save_path));
int max_active = pref.getMaxActiveTorrents(); QDir().rmpath(old_save_path);
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);
}
} }
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 (file_error_alert* p = dynamic_cast<file_error_alert*>(a.get())) { }
QTorrentHandle h(p->handle); else if (metadata_received_alert* p = dynamic_cast<metadata_received_alert*>(a)) {
if (h.is_valid()) { QTorrentHandle h(p->handle);
h.pause(); Preferences pref;
std::cerr << "File Error: " << p->message().c_str() << std::endl; if (h.is_valid()) {
addConsoleMessage(tr("An I/O error occurred, '%1' paused.").arg(h.name())); QString hash(h.hash());
addConsoleMessage(tr("Reason: %1").arg(misc::toQStringU(p->message()))); if (HiddenData::hasData(hash)) {
if (h.is_valid()) { HiddenData::gotMetadata(hash);
emit fullDiskError(h, misc::toQStringU(p->message())); if (pref.isQueueingSystemEnabled()) {
//h.pause(); //Internally decrease the queue limits to ensure that that other queued items aren't started
emit pausedTorrent(h); 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_completed_alert* p = dynamic_cast<file_completed_alert*>(a.get())) { }
QTorrentHandle h(p->handle); else if (file_error_alert* p = dynamic_cast<file_error_alert*>(a)) {
qDebug("A file completed download in torrent %s", qPrintable(h.name())); QTorrentHandle h(p->handle);
if (appendqBExtension) { if (h.is_valid()) {
qDebug("appendqBTExtension is true"); h.pause();
QString name = h.filepath_at(p->index); std::cerr << "File Error: " << p->message().c_str() << std::endl;
if (name.endsWith(".!qB")) { addConsoleMessage(tr("An I/O error occurred, '%1' paused.").arg(h.name()));
const QString old_name = name; addConsoleMessage(tr("Reason: %1").arg(misc::toQStringU(p->message())));
name.chop(4); if (h.is_valid()) {
qDebug("Renaming %s to %s", qPrintable(old_name), qPrintable(name)); emit fullDiskError(h, misc::toQStringU(p->message()));
h.rename_file(p->index, name); //h.pause();
} emit pausedTorrent(h);
} }
} }
else if (torrent_paused_alert* p = dynamic_cast<torrent_paused_alert*>(a.get())) { }
if (p->handle.is_valid()) { else if (file_completed_alert* p = dynamic_cast<file_completed_alert*>(a)) {
QTorrentHandle h(p->handle); QTorrentHandle h(p->handle);
if (!HiddenData::hasData(h.hash())) { qDebug("A file completed download in torrent %s", qPrintable(h.name()));
if (!h.has_error()) if (appendqBExtension) {
h.save_resume_data(); qDebug("appendqBTExtension is true");
emit pausedTorrent(h); 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 (tracker_error_alert* p = dynamic_cast<tracker_error_alert*>(a.get())) { }
// Level: fatal else if (torrent_paused_alert* p = dynamic_cast<torrent_paused_alert*>(a)) {
if (p->handle.is_valid()) {
QTorrentHandle h(p->handle); QTorrentHandle h(p->handle);
if (h.is_valid()) { if (!HiddenData::hasData(h.hash())) {
// Authentication if (!h.has_error())
if (p->status_code != 401) { h.save_resume_data();
qDebug("Received a tracker error for %s: %s", p->url.c_str(), p->msg.c_str()); emit pausedTorrent(h);
const QString tracker_url = misc::toQString(p->url);
QHash<QString, TrackerInfos> trackers_data = trackersInfos.value(h.hash(), QHash<QString, TrackerInfos>());
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<tracker_reply_alert*>(a.get())) { }
const QTorrentHandle h(p->handle); else if (tracker_error_alert* p = dynamic_cast<tracker_error_alert*>(a)) {
if (h.is_valid()) { // Level: fatal
qDebug("Received a tracker reply from %s (Num_peers=%d)", p->url.c_str(), p->num_peers); QTorrentHandle h(p->handle);
// Connection was successful now. Remove possible old errors if (h.is_valid()) {
QHash<QString, TrackerInfos> trackers_data = trackersInfos.value(h.hash(), QHash<QString, TrackerInfos>()); // 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); 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<tracker_warning_alert*>(a.get())) {
const QTorrentHandle h(p->handle);
if (h.is_valid()) {
// Connection was successful now but there is a warning message
QHash<QString, TrackerInfos> trackers_data = trackersInfos.value(h.hash(), QHash<QString, TrackerInfos>()); QHash<QString, TrackerInfos> trackers_data = trackersInfos.value(h.hash(), QHash<QString, TrackerInfos>());
const QString tracker_url = misc::toQString(p->url);
TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url)); TrackerInfos data = trackers_data.value(tracker_url, TrackerInfos(tracker_url));
data.last_message = misc::toQStringU(p->msg); // Store warning message data.last_message = misc::toQStringU(p->msg);
trackers_data.insert(tracker_url, data); trackers_data.insert(tracker_url, data);
trackersInfos[h.hash()] = trackers_data; trackersInfos[h.hash()] = trackers_data;
qDebug("Received a tracker warning from %s: %s", p->url.c_str(), p->msg.c_str()); } else {
emit trackerAuthenticationRequired(h);
} }
} }
else if (portmap_error_alert* p = dynamic_cast<portmap_error_alert*>(a.get())) { }
addConsoleMessage(tr("UPnP/NAT-PMP: Port mapping failure, message: %1").arg(misc::toQStringU(p->message())), "red"); else if (tracker_reply_alert* p = dynamic_cast<tracker_reply_alert*>(a)) {
//emit UPnPError(QString(p->msg().c_str())); 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<QString, TrackerInfos> trackers_data = trackersInfos.value(h.hash(), QHash<QString, TrackerInfos>());
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 (portmap_alert* p = dynamic_cast<portmap_alert*>(a.get())) { }
qDebug("UPnP Success, msg: %s", p->message().c_str()); else if (tracker_warning_alert* p = dynamic_cast<tracker_warning_alert*>(a)) {
addConsoleMessage(tr("UPnP/NAT-PMP: Port mapping successful, message: %1").arg(misc::toQStringU(p->message())), "blue"); const QTorrentHandle h(p->handle);
//emit UPnPSuccess(QString(p->msg().c_str())); if (h.is_valid()) {
// Connection was successful now but there is a warning message
QHash<QString, TrackerInfos> trackers_data = trackersInfos.value(h.hash(), QHash<QString, TrackerInfos>());
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 (peer_blocked_alert* p = dynamic_cast<peer_blocked_alert*>(a.get())) { }
boost::system::error_code ec; else if (portmap_error_alert* p = dynamic_cast<portmap_error_alert*>(a)) {
string ip = p->ip.to_string(ec); addConsoleMessage(tr("UPnP/NAT-PMP: Port mapping failure, message: %1").arg(misc::toQStringU(p->message())), "red");
if (!ec) { //emit UPnPError(QString(p->msg().c_str()));
addPeerBanMessage(QString::fromLatin1(ip.c_str()), true); }
//emit peerBlocked(QString::fromLatin1(ip.c_str())); else if (portmap_alert* p = dynamic_cast<portmap_alert*>(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<peer_blocked_alert*>(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<peer_ban_alert*>(a.get())) { }
boost::system::error_code ec; else if (peer_ban_alert* p = dynamic_cast<peer_ban_alert*>(a)) {
string ip = p->ip.address().to_string(ec); boost::system::error_code ec;
if (!ec) { string ip = p->ip.address().to_string(ec);
addPeerBanMessage(QString::fromLatin1(ip.c_str()), false); if (!ec) {
//emit peerBlocked(QString::fromLatin1(ip.c_str())); addPeerBanMessage(QString::fromLatin1(ip.c_str()), false);
} //emit peerBlocked(QString::fromLatin1(ip.c_str()));
} }
else if (fastresume_rejected_alert* p = dynamic_cast<fastresume_rejected_alert*>(a.get())) { }
QTorrentHandle h(p->handle); else if (fastresume_rejected_alert* p = dynamic_cast<fastresume_rejected_alert*>(a)) {
if (h.is_valid()) { QTorrentHandle h(p->handle);
qDebug("/!\\ Fast resume failed for %s, reason: %s", qPrintable(h.name()), p->message().c_str()); if (h.is_valid()) {
if (p->error.value() == 134 && TorrentPersistentData::isSeed(h.hash()) && h.has_missing_files()) { qDebug("/!\\ Fast resume failed for %s, reason: %s", qPrintable(h.name()), p->message().c_str());
const QString hash = h.hash(); if (p->error.value() == 134 && TorrentPersistentData::isSeed(h.hash()) && h.has_missing_files()) {
// Mismatching file size (files were probably moved const QString hash = h.hash();
addConsoleMessage(tr("File sizes mismatch for torrent %1, pausing it.").arg(h.name())); // Mismatching file size (files were probably moved
TorrentPersistentData::setErrorState(hash, true); addConsoleMessage(tr("File sizes mismatch for torrent %1, pausing it.").arg(h.name()));
pauseTorrent(hash); TorrentPersistentData::setErrorState(hash, true);
} else { pauseTorrent(hash);
addConsoleMessage(tr("Fast resume data was rejected for torrent %1, checking again...").arg(h.name()), QString::fromUtf8("red")); } else {
addConsoleMessage(tr("Reason: %1").arg(misc::toQStringU(p->message()))); 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<url_seed_alert*>(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")); else if (url_seed_alert* p = dynamic_cast<url_seed_alert*>(a)) {
//emit urlSeedProblem(QString::fromUtf8(p->url.c_str()), QString::fromUtf8(p->msg().c_str())); 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<listen_succeeded_alert*>(a.get())) { }
boost::system::error_code ec; else if (listen_succeeded_alert *p = dynamic_cast<listen_succeeded_alert*>(a)) {
QString proto = "TCP"; boost::system::error_code ec;
QString proto = "TCP";
#if LIBTORRENT_VERSION_NUM >= 10000 #if LIBTORRENT_VERSION_NUM >= 10000
if (p->sock_type == listen_succeeded_alert::udp) if (p->sock_type == listen_succeeded_alert::udp)
proto = "UDP"; proto = "UDP";
else if (p->sock_type == listen_succeeded_alert::tcp) else if (p->sock_type == listen_succeeded_alert::tcp)
proto = "TCP"; proto = "TCP";
else if (p->sock_type == listen_succeeded_alert::tcp_ssl) else if (p->sock_type == listen_succeeded_alert::tcp_ssl)
proto = "TCP_SSL"; proto = "TCP_SSL";
#endif #endif
qDebug() << "Successfully listening on " << proto << p->endpoint.address().to_string(ec).c_str() << "/" << p->endpoint.port(); 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"); 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 // Force reannounce on all torrents because some trackers blacklist some ports
std::vector<torrent_handle> torrents = s->get_torrents(); std::vector<torrent_handle> torrents = s->get_torrents();
std::vector<torrent_handle>::iterator it = torrents.begin(); std::vector<torrent_handle>::iterator it = torrents.begin();
std::vector<torrent_handle>::iterator itend = torrents.end(); std::vector<torrent_handle>::iterator itend = torrents.end();
for ( ; it != itend; ++it) { for ( ; it != itend; ++it) {
it->force_reannounce(); it->force_reannounce();
}
} }
else if (listen_failed_alert *p = dynamic_cast<listen_failed_alert*>(a.get())) { }
boost::system::error_code ec; else if (listen_failed_alert *p = dynamic_cast<listen_failed_alert*>(a)) {
QString proto = "TCP"; boost::system::error_code ec;
QString proto = "TCP";
#if LIBTORRENT_VERSION_NUM >= 10000 #if LIBTORRENT_VERSION_NUM >= 10000
if (p->sock_type == listen_failed_alert::udp) if (p->sock_type == listen_failed_alert::udp)
proto = "UDP"; proto = "UDP";
else if (p->sock_type == listen_failed_alert::tcp) else if (p->sock_type == listen_failed_alert::tcp)
proto = "TCP"; proto = "TCP";
else if (p->sock_type == listen_failed_alert::tcp_ssl) else if (p->sock_type == listen_failed_alert::tcp_ssl)
proto = "TCP_SSL"; proto = "TCP_SSL";
else if (p->sock_type == listen_failed_alert::i2p) else if (p->sock_type == listen_failed_alert::i2p)
proto = "I2P"; proto = "I2P";
else if (p->sock_type == listen_failed_alert::socks5) else if (p->sock_type == listen_failed_alert::socks5)
proto = "SOCKS5"; proto = "SOCKS5";
#endif #endif
qDebug() << "Failed listening on " << proto << p->endpoint.address().to_string(ec).c_str() << "/" << p->endpoint.port(); qDebug() << "Failed listening on " << proto << p->endpoint.address().to_string(ec).c_str() << "/" << p->endpoint.port();
addConsoleMessage(tr("qBittorrent failed listening on interface %1 port: %2/%3. Reason: %4", "e.g: qBittorrent failed listening on interface 192.168.0.1 port: TCP/6881. Reason: already in use").arg(p->endpoint.address().to_string(ec).c_str()).arg(proto).arg(QString::number(p->endpoint.port())).arg(misc::toQStringU(p->error.message())), "red"); addConsoleMessage(tr("qBittorrent failed listening on interface %1 port: %2/%3. Reason: %4", "e.g: qBittorrent failed listening on interface 192.168.0.1 port: TCP/6881. Reason: already in use").arg(p->endpoint.address().to_string(ec).c_str()).arg(proto).arg(QString::number(p->endpoint.port())).arg(misc::toQStringU(p->error.message())), "red");
} }
else if (torrent_checked_alert* p = dynamic_cast<torrent_checked_alert*>(a.get())) { else if (torrent_checked_alert* p = dynamic_cast<torrent_checked_alert*>(a)) {
QTorrentHandle h(p->handle); QTorrentHandle h(p->handle);
if (h.is_valid()) { if (h.is_valid()) {
const QString hash = h.hash(); const QString hash = h.hash();
qDebug("%s have just finished checking", qPrintable(hash)); qDebug("%s have just finished checking", qPrintable(hash));
// Save seed status // Save seed status
TorrentPersistentData::saveSeedStatus(h); TorrentPersistentData::saveSeedStatus(h);
// Move to temp directory if necessary // Move to temp directory if necessary
if (!h.is_seed() && !defaultTempPath.isEmpty()) { if (!h.is_seed() && !defaultTempPath.isEmpty()) {
// Check if directory is different // Check if directory is different
const QDir current_dir(h.save_path()); const QDir current_dir(h.save_path());
const QDir save_dir(getSavePath(h.hash())); const QDir save_dir(getSavePath(h.hash()));
if (current_dir == save_dir) { if (current_dir == save_dir) {
qDebug("Moving the torrent to the temp directory..."); qDebug("Moving the torrent to the temp directory...");
QString torrent_tmp_path = defaultTempPath; QString torrent_tmp_path = defaultTempPath;
h.move_storage(torrent_tmp_path); h.move_storage(torrent_tmp_path);
}
}
emit torrentFinishedChecking(h);
if (torrentsToPausedAfterChecking.contains(hash)) {
torrentsToPausedAfterChecking.removeOne(hash);
h.pause();
emit pausedTorrent(h);
} }
} }
emit torrentFinishedChecking(h);
if (torrentsToPausedAfterChecking.contains(hash)) {
torrentsToPausedAfterChecking.removeOne(hash);
h.pause();
emit pausedTorrent(h);
}
} }
else if (external_ip_alert *p = dynamic_cast<external_ip_alert*>(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 (external_ip_alert *p = dynamic_cast<external_ip_alert*>(a)) {
a = s->pop_alert(); 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();
} }
} }

4
src/qtlibtorrent/qbtsession.h

@ -51,6 +51,7 @@
#include "qtracker.h" #include "qtracker.h"
#include "qtorrenthandle.h" #include "qtorrenthandle.h"
#include "trackerinfos.h" #include "trackerinfos.h"
#include "alertdispatcher.h"
#define MAX_SAMPLES 20 #define MAX_SAMPLES 20
@ -190,6 +191,7 @@ private:
void updateRatioTimer(); void updateRatioTimer();
void recoverPersistentData(const QString &hash, const std::vector<char> &buf); void recoverPersistentData(const QString &hash, const std::vector<char> &buf);
void backupPersistentData(const QString &hash, boost::shared_ptr<libtorrent::entry> data); void backupPersistentData(const QString &hash, boost::shared_ptr<libtorrent::entry> data);
void handleAlert(libtorrent::alert* a);
private slots: private slots:
void addTorrentsFromScanFolder(QStringList&); void addTorrentsFromScanFolder(QStringList&);
@ -234,7 +236,6 @@ signals:
private: private:
// Bittorrent // Bittorrent
libtorrent::session *s; libtorrent::session *s;
QPointer<QTimer> timerAlerts;
QPointer<BandwidthScheduler> bd_scheduler; QPointer<BandwidthScheduler> bd_scheduler;
QMap<QUrl, QPair<QString, QString> > savepathLabel_fromurl; // Use QMap for compatibility with Qt < 4.7: qHash(QUrl) QMap<QUrl, QPair<QString, QString> > savepathLabel_fromurl; // Use QMap for compatibility with Qt < 4.7: qHash(QUrl)
QHash<QString, QHash<QString, TrackerInfos> > trackersInfos; QHash<QString, QHash<QString, TrackerInfos> > trackersInfos;
@ -287,6 +288,7 @@ private:
#endif #endif
// DynDNS // DynDNS
DNSUpdater *m_dynDNSUpdater; DNSUpdater *m_dynDNSUpdater;
QAlertDispatcher* m_alertDispatcher;
}; };
#endif #endif

6
src/qtlibtorrent/qtlibtorrent.pri

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

Loading…
Cancel
Save