Skip to content

Commit

Permalink
Merge pull request #12035 from glassez/move-storage
Browse files Browse the repository at this point in the history
Move torrent storages one by one
  • Loading branch information
glassez authored Mar 13, 2020
2 parents f80b7af + e4ff206 commit 5127156
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 166 deletions.
116 changes: 113 additions & 3 deletions src/base/bittorrent/session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1815,8 +1815,19 @@ bool Session::deleteTorrent(const InfoHash &hash, const DeleteOption deleteOptio
for (const QString &file : files)
Utils::Fs::forceRemove(resumeDataDir.absoluteFilePath(file));

if (m_moveStorageQueue.size() > 1) {
// Delete "move storage job" for the deleted torrent
// (note: we shouldn't delete active job)
const auto iter = std::find_if(m_moveStorageQueue.begin() + 1, m_moveStorageQueue.end()
, [torrent](const MoveStorageJob &job)
{
return job.torrent == torrent;
});
if (iter != m_moveStorageQueue.end())
m_moveStorageQueue.erase(iter);
}

delete torrent;
qDebug("Torrent deleted.");
return true;
}

Expand Down Expand Up @@ -3938,6 +3949,78 @@ void Session::handleTorrentTrackerError(TorrentHandle *const torrent, const QStr
emit trackerError(torrent, trackerUrl);
}

bool Session::addMoveTorrentStorageJob(TorrentHandle *torrent, const QString &newPath, const MoveStorageMode mode)
{
Q_ASSERT(torrent);

if (m_moveStorageQueue.size() > 1) {
const auto iter = std::find_if(m_moveStorageQueue.begin() + 1, m_moveStorageQueue.end()
, [torrent](const MoveStorageJob &job)
{
return job.torrent == torrent;
});

if (iter != m_moveStorageQueue.end()) {
// remove existing inactive job
m_moveStorageQueue.erase(iter);
}
}

QString currentLocation = QString::fromStdString(
torrent->nativeHandle().status(lt::torrent_handle::query_save_path).save_path);
if (!m_moveStorageQueue.isEmpty() && (m_moveStorageQueue.first().torrent == torrent)) {
// if there is active job for this torrent consider its target path as current location
// of this torrent to prevent creating meaningless job that will do nothing
currentLocation = m_moveStorageQueue.first().path;
}

if (QDir {currentLocation} == QDir {newPath})
return false;

const MoveStorageJob moveStorageJob {torrent, newPath, mode};
qDebug("Move storage from \"%s\" to \"%s\" is enqueued.", qUtf8Printable(currentLocation), qUtf8Printable(newPath));

if (m_moveStorageQueue.size() == 1)
moveTorrentStorage(moveStorageJob);

return true;
}

void Session::moveTorrentStorage(const MoveStorageJob &job) const
{
lt::torrent_handle handle = job.torrent->nativeHandle();

qDebug("Moving torrent storage to \"%s\"...", qUtf8Printable(job.path));
#if (LIBTORRENT_VERSION_NUM < 10200)
handle.move_storage(job.path.toUtf8().constData()
, ((job.mode == MoveStorageMode::Overwrite)
? lt::always_replace_files : lt::dont_replace));
#else
handle.move_storage(job.path.toUtf8().constData()
, ((job.mode == MoveStorageMode::Overwrite)
? lt::move_flags_t::always_replace_files : lt::move_flags_t::dont_replace));
#endif
}

void Session::handleMoveTorrentStorageJobFinished(const QString &errorMessage)
{
Q_ASSERT(!m_moveStorageQueue.isEmpty());

const MoveStorageJob finishedJob = m_moveStorageQueue.takeFirst();
if (!m_moveStorageQueue.isEmpty())
moveTorrentStorage(m_moveStorageQueue.first());

const auto iter = std::find_if(m_moveStorageQueue.cbegin(), m_moveStorageQueue.cend()
, [&finishedJob](const MoveStorageJob &job)
{
return job.torrent == finishedJob.torrent;
});
if (iter == m_moveStorageQueue.cend()) {
// There is no more job for this torrent
finishedJob.torrent->handleStorageMoved(finishedJob.path, errorMessage);
}
}

void Session::handleTorrentTrackerWarning(TorrentHandle *const torrent, const QString &trackerUrl)
{
emit trackerWarning(torrent, trackerUrl);
Expand Down Expand Up @@ -4254,8 +4337,6 @@ void Session::handleAlert(const lt::alert *a)
case lt::torrent_finished_alert::alert_type:
case lt::save_resume_data_alert::alert_type:
case lt::save_resume_data_failed_alert::alert_type:
case lt::storage_moved_alert::alert_type:
case lt::storage_moved_failed_alert::alert_type:
case lt::torrent_paused_alert::alert_type:
case lt::torrent_resumed_alert::alert_type:
case lt::tracker_error_alert::alert_type:
Expand Down Expand Up @@ -4319,6 +4400,12 @@ void Session::handleAlert(const lt::alert *a)
handleAlertsDroppedAlert(static_cast<const lt::alerts_dropped_alert *>(a));
break;
#endif
case lt::storage_moved_alert::alert_type:
handleStorageMovedAlert(static_cast<const lt::storage_moved_alert*>(a));
break;
case lt::storage_moved_failed_alert::alert_type:
handleStorageMovedFailedAlert(static_cast<const lt::storage_moved_failed_alert*>(a));
break;
}
}
catch (const std::exception &exc) {
Expand Down Expand Up @@ -4813,6 +4900,29 @@ void Session::handleAlertsDroppedAlert(const lt::alerts_dropped_alert *p) const
}
#endif

void Session::handleStorageMovedAlert(const lt::storage_moved_alert *p)
{
if (m_moveStorageQueue.isEmpty()) return;

const TorrentHandle *torrent = m_torrents.value(p->handle.info_hash());
const MoveStorageJob &currentJob = m_moveStorageQueue.first();
if (currentJob.torrent != torrent) return;

const QString newPath {p->storage_path()};
handleMoveTorrentStorageJobFinished(newPath != currentJob.path ? tr("New path doesn't match a target path.") : QString {});
}

void Session::handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert *p)
{
if (m_moveStorageQueue.isEmpty()) return;

const TorrentHandle *torrent = m_torrents.value(p->handle.info_hash());
const MoveStorageJob &currentJob = m_moveStorageQueue.first();
if (currentJob.torrent != torrent) return;

handleMoveTorrentStorageJobFinished(QString::fromStdString(p->message()));
}

void Session::handleStateUpdateAlert(const lt::state_update_alert *p)
{
QVector<BitTorrent::TorrentHandle *> updatedTorrents;
Expand Down
18 changes: 18 additions & 0 deletions src/base/bittorrent/session.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ namespace BitTorrent
class TrackerEntry;
struct CreateTorrentParams;

enum class MoveStorageMode;

// Using `Q_ENUM_NS()` without a wrapper namespace in our case is not advised
// since `Q_NAMESPACE` cannot be used when the same namespace resides at different files.
// https://www.kdab.com/new-qt-5-8-meta-object-support-namespaces/#comment-143779
Expand Down Expand Up @@ -461,6 +463,8 @@ namespace BitTorrent
void handleTorrentTrackerWarning(TorrentHandle *const torrent, const QString &trackerUrl);
void handleTorrentTrackerError(TorrentHandle *const torrent, const QString &trackerUrl);

bool addMoveTorrentStorageJob(TorrentHandle *torrent, const QString &newPath, MoveStorageMode mode);

signals:
void addTorrentFailed(const QString &error);
void allTorrentsFinished();
Expand Down Expand Up @@ -514,6 +518,13 @@ namespace BitTorrent
void networkConfigurationChange(const QNetworkConfiguration &);

private:
struct MoveStorageJob
{
TorrentHandle *torrent;
QString path;
MoveStorageMode mode;
};

struct RemovingTorrentData
{
QString name;
Expand Down Expand Up @@ -583,6 +594,8 @@ namespace BitTorrent
#if (LIBTORRENT_VERSION_NUM >= 10200)
void handleAlertsDroppedAlert(const lt::alerts_dropped_alert *p) const;
#endif
void handleStorageMovedAlert(const lt::storage_moved_alert *p);
void handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert *p);

void createTorrentHandle(const lt::torrent_handle &nativeHandle);

Expand All @@ -592,6 +605,9 @@ namespace BitTorrent

std::vector<lt::alert *> getPendingAlerts(lt::time_duration time = lt::time_duration::zero()) const;

void moveTorrentStorage(const MoveStorageJob &job) const;
void handleMoveTorrentStorageJobFinished(const QString &errorMessage = {});

// BitTorrent
lt::session *m_nativeSession = nullptr;

Expand Down Expand Up @@ -732,6 +748,8 @@ namespace BitTorrent

QNetworkConfigurationManager *m_networkManager = nullptr;

QList<MoveStorageJob> m_moveStorageQueue;

static Session *m_instance;
};
}
Expand Down
Loading

0 comments on commit 5127156

Please # to comment.