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 @@ @@ -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 @@ @@ -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() @@ -114,6 +114,7 @@ QBtSession::QBtSession()
, m_upnp(0), m_natpmp(0)
#endif
, m_dynDNSUpdater(0)
, m_alertDispatcher(0)
{
BigRatioTimer = new QTimer(this);
BigRatioTimer->setInterval(10000);
@ -147,9 +148,8 @@ QBtSession::QBtSession() @@ -147,9 +148,8 @@ QBtSession::QBtSession()
PeXEnabled = false;
}
s->add_extension(&create_smart_ban_plugin);
timerAlerts = new QTimer(this);
connect(timerAlerts, SIGNAL(timeout()), SLOT(readAlerts()));
timerAlerts->start(1000);
m_alertDispatcher = new QAlertDispatcher(s, this);
connect(m_alertDispatcher, SIGNAL(alertsReceived()), SLOT(readAlerts()));
appendLabelToSavePath = pref.appendTorrentLabel();
appendqBExtension = pref.useIncompleteFilesExtension();
connect(m_scanFolders, SIGNAL(torrentsAdded(QStringList&)), SLOT(addTorrentsFromScanFolder(QStringList&)));
@ -179,7 +179,6 @@ QBtSession::~QBtSession() { @@ -179,7 +179,6 @@ QBtSession::~QBtSession() {
// Delete our objects
if (m_tracker)
delete m_tracker;
delete timerAlerts;
if (BigRatioTimer)
delete BigRatioTimer;
if (filterParser)
@ -190,6 +189,7 @@ QBtSession::~QBtSession() { @@ -190,6 +189,7 @@ QBtSession::~QBtSession() {
// HTTP Server
if (httpServer)
delete httpServer;
delete m_alertDispatcher;
qDebug("Deleting the session");
delete s;
qDebug("BTSession destructor OUT");
@ -1608,7 +1608,6 @@ void QBtSession::saveFastResumeData() { @@ -1608,7 +1608,6 @@ void QBtSession::saveFastResumeData() {
qDebug("Saving fast resume data...");
// Stop listening for alerts
resumeDataTimer.stop();
timerAlerts->stop();
int num_resume_data = 0;
// Pause session
s->pause();
@ -1633,52 +1632,52 @@ void QBtSession::saveFastResumeData() { @@ -1633,52 +1632,52 @@ void QBtSession::saveFastResumeData() {
} catch(libtorrent::invalid_handle&) {}
}
while (num_resume_data > 0) {
alert const* a = s->wait_for_alert(seconds(30));
if (a == 0) {
std::cerr << " aborting with " << num_resume_data << " outstanding "
"torrents to save resume data for" << std::endl;
break;
}
// Saving fastresume data can fail
save_resume_data_failed_alert const* rda = dynamic_cast<save_resume_data_failed_alert const*>(a);
if (rda) {
std::deque<alert*> alerts;
m_alertDispatcher->getPendingAlerts(alerts);
for (std::deque<alert*>::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<save_resume_data_failed_alert const*>(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;
}
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;
s->pop_alert();
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
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) {
s->pop_alert();
continue;
s->remove_torrent(rd->handle);
} catch(libtorrent::invalid_handle&) {}
delete a;
}
// 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) { @@ -2117,454 +2116,460 @@ void QBtSession::sendNotificationEmail(const QTorrentHandle &h) {
// Read alerts sent by the Bittorrent session
void QBtSession::readAlerts() {
// look at session alerts and display some infos
std::auto_ptr<alert> a = s->pop_alert();
while (a.get()) {
try {
if (torrent_finished_alert* p = dynamic_cast<torrent_finished_alert*>(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<h.num_files(); ++i) {
const QString torrent_relpath = h.filepath_at(i);
qDebug() << "File path:" << torrent_relpath;
if (torrent_relpath.endsWith(".torrent", Qt::CaseInsensitive)) {
qDebug("Found possible recursive torrent download.");
const QString torrent_fullpath = h.save_path()+"/"+torrent_relpath;
qDebug("Full subtorrent path is %s", qPrintable(torrent_fullpath));
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"));
typedef std::deque<alert*> 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<torrent_finished_alert*>(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<h.num_files(); ++i) {
const QString torrent_relpath = h.filepath_at(i);
qDebug() << "File path:" << torrent_relpath;
if (torrent_relpath.endsWith(".torrent", Qt::CaseInsensitive)) {
qDebug("Found possible recursive torrent download.");
const QString torrent_fullpath = h.save_path()+"/"+torrent_relpath;
qDebug("Full subtorrent path is %s", qPrintable(torrent_fullpath));
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()) {
// 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());
}
}
// 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();
}
// 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...");
}
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();
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;
}
#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());
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<char> 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<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 (save_resume_data_alert* p = dynamic_cast<save_resume_data_alert*>(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<char> 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 (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...");
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 if (file_renamed_alert* p = dynamic_cast<file_renamed_alert*>(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 {
// 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);
}
}
// Single-file torrent
// Renaming a file corresponds to changing the save path
emit savePathChanged(h);
}
}
else if (storage_moved_alert* p = dynamic_cast<storage_moved_alert*>(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);
}
else if (torrent_deleted_alert* p = dynamic_cast<torrent_deleted_alert*>(a)) {
qDebug("A torrent was deleted from the hard disk, attempting to remove the root folder too...");
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);
}
emit savePathChanged(h);
//h.force_recheck();
}
}
else if (metadata_received_alert* p = dynamic_cast<metadata_received_alert*>(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 (storage_moved_alert* p = dynamic_cast<storage_moved_alert*>(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 (file_error_alert* p = dynamic_cast<file_error_alert*>(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 (metadata_received_alert* p = dynamic_cast<metadata_received_alert*>(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);
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);
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 (file_error_alert* p = dynamic_cast<file_error_alert*>(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 (torrent_paused_alert* p = dynamic_cast<torrent_paused_alert*>(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 (file_completed_alert* p = dynamic_cast<file_completed_alert*>(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 (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);
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<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);
}
if (!HiddenData::hasData(h.hash())) {
if (!h.has_error())
h.save_resume_data();
emit pausedTorrent(h);
}
}
else if (tracker_reply_alert* p = dynamic_cast<tracker_reply_alert*>(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<QString, TrackerInfos> trackers_data = trackersInfos.value(h.hash(), QHash<QString, TrackerInfos>());
}
else if (tracker_error_alert* p = dynamic_cast<tracker_error_alert*>(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);
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>());
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
data.last_message = misc::toQStringU(p->msg);
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 {
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");
//emit UPnPError(QString(p->msg().c_str()));
}
else if (tracker_reply_alert* p = dynamic_cast<tracker_reply_alert*>(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<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());
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 (tracker_warning_alert* p = dynamic_cast<tracker_warning_alert*>(a)) {
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>());
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;
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 (portmap_error_alert* p = dynamic_cast<portmap_error_alert*>(a)) {
addConsoleMessage(tr("UPnP/NAT-PMP: Port mapping failure, message: %1").arg(misc::toQStringU(p->message())), "red");
//emit UPnPError(QString(p->msg().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;
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 (peer_ban_alert* p = dynamic_cast<peer_ban_alert*>(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<fastresume_rejected_alert*>(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 (fastresume_rejected_alert* p = dynamic_cast<fastresume_rejected_alert*>(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<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"));
//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;
QString proto = "TCP";
}
else if (url_seed_alert* p = dynamic_cast<url_seed_alert*>(a)) {
addConsoleMessage(tr("Url seed lookup failed for url: %1, message: %2").arg(misc::toQString(p->url)).arg(misc::toQStringU(p->message())), QString::fromUtf8("red"));
//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)) {
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";
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<torrent_handle> torrents = s->get_torrents();
std::vector<torrent_handle>::iterator it = torrents.begin();
std::vector<torrent_handle>::iterator itend = torrents.end();
for ( ; it != itend; ++it) {
it->force_reannounce();
}
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<torrent_handle> torrents = s->get_torrents();
std::vector<torrent_handle>::iterator it = torrents.begin();
std::vector<torrent_handle>::iterator itend = torrents.end();
for ( ; it != itend; ++it) {
it->force_reannounce();
}
else if (listen_failed_alert *p = dynamic_cast<listen_failed_alert*>(a.get())) {
boost::system::error_code ec;
QString proto = "TCP";
}
else if (listen_failed_alert *p = dynamic_cast<listen_failed_alert*>(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";
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<torrent_checked_alert*>(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()) {
// 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);
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<torrent_checked_alert*>(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<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();
}
a = s->pop_alert();
else if (external_ip_alert *p = dynamic_cast<external_ip_alert*>(a)) {
boost::system::error_code ec;
addConsoleMessage(tr("External IP: %1", "e.g. External IP: 192.168.0.1").arg(p->external_address.to_string(ec).c_str()), "blue");
}
} catch (const std::exception& e) {
qWarning() << "Caught exception in readAlerts(): " << e.what();
}
}

4
src/qtlibtorrent/qbtsession.h

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

6
src/qtlibtorrent/qtlibtorrent.pri

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

Loading…
Cancel
Save