Browse Source

Merge pull request #10792 from Chocobo1/rename2

Remove empty leftover folders after rename properly
adaptive-webui-19844
Mike Tzou 5 years ago committed by GitHub
parent
commit
46d445d042
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      src/base/bittorrent/session.cpp
  2. 73
      src/base/bittorrent/torrenthandle.cpp
  3. 10
      src/base/bittorrent/torrenthandle.h
  4. 11
      src/base/utils/string.cpp
  5. 4
      src/base/utils/string.h
  6. 9
      src/gui/torrentcontenttreeview.cpp

10
src/base/bittorrent/session.cpp

@ -1657,7 +1657,7 @@ bool Session::deleteTorrent(const QString &hash, const bool deleteLocalFiles)
Utils::Fs::forceRemove(unwantedFile); Utils::Fs::forceRemove(unwantedFile);
const QString parentFolder = Utils::Fs::branchPath(unwantedFile); const QString parentFolder = Utils::Fs::branchPath(unwantedFile);
qDebug("Attempt to remove parent folder (if empty): %s", qUtf8Printable(parentFolder)); qDebug("Attempt to remove parent folder (if empty): %s", qUtf8Printable(parentFolder));
QDir().rmpath(parentFolder); QDir().rmdir(parentFolder);
} }
} }
@ -2131,9 +2131,13 @@ void Session::generateResumeData(const bool final)
{ {
for (TorrentHandle *const torrent : asConst(m_torrents)) { for (TorrentHandle *const torrent : asConst(m_torrents)) {
if (!torrent->isValid()) continue; if (!torrent->isValid()) continue;
if (torrent->isChecking() || torrent->isPaused()) continue;
if (!final && !torrent->needSaveResumeData()) continue; if (!final && !torrent->needSaveResumeData()) continue;
if (torrent->hasMissingFiles() || torrent->hasError()) continue; if (torrent->isChecking()
|| torrent->isPaused()
|| torrent->hasError()
|| torrent->hasMissingFiles())
continue;
saveTorrentResumeData(torrent); saveTorrentResumeData(torrent);
} }

73
src/base/bittorrent/torrenthandle.cpp

@ -59,6 +59,7 @@
#include "base/profile.h" #include "base/profile.h"
#include "base/tristatebool.h" #include "base/tristatebool.h"
#include "base/utils/fs.h" #include "base/utils/fs.h"
#include "base/utils/string.h"
#include "downloadpriority.h" #include "downloadpriority.h"
#include "peerinfo.h" #include "peerinfo.h"
#include "session.h" #include "session.h"
@ -1481,6 +1482,7 @@ void TorrentHandle::moveStorage(const QString &newPath, bool overwrite)
void TorrentHandle::renameFile(const int index, const QString &name) void TorrentHandle::renameFile(const int index, const QString &name)
{ {
m_oldPath[LTFileIndex {index}].push_back(filePath(index));
++m_renameCount; ++m_renameCount;
m_nativeHandle.rename_file(index, Utils::Fs::toNativePath(name).toStdString()); m_nativeHandle.rename_file(index, Utils::Fs::toNativePath(name).toStdString());
} }
@ -1723,10 +1725,14 @@ void TorrentHandle::handleSaveResumeDataFailedAlert(const lt::save_resume_data_f
{ {
// if torrent has no metadata we should save dummy fastresume data // if torrent has no metadata we should save dummy fastresume data
// containing Magnet URI and qBittorrent own resume data only // containing Magnet URI and qBittorrent own resume data only
if (p->error.value() == lt::errors::no_metadata) if (p->error.value() == lt::errors::no_metadata) {
handleSaveResumeDataAlert(nullptr); handleSaveResumeDataAlert(nullptr);
else }
else {
LogMsg(tr("Save resume data failed. Torrent: \"%1\", error: \"%2\"")
.arg(name(), QString::fromStdString(p->error.message())), Log::CRITICAL);
m_session->handleTorrentResumeDataFailed(this); m_session->handleTorrentResumeDataFailed(this);
}
} }
void TorrentHandle::handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p) void TorrentHandle::handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p)
@ -1744,45 +1750,66 @@ void TorrentHandle::handleFastResumeRejectedAlert(const lt::fastresume_rejected_
void TorrentHandle::handleFileRenamedAlert(const lt::file_renamed_alert *p) void TorrentHandle::handleFileRenamedAlert(const lt::file_renamed_alert *p)
{ {
const QString newName = Utils::Fs::fromNativePath(p->new_name());
// TODO: Check this!
if (filesCount() > 1) {
// Check if folders were renamed
QStringList oldPathParts = m_torrentInfo.origFilePath(p->index).split('/');
oldPathParts.removeLast();
QString oldPath = oldPathParts.join('/');
QStringList newPathParts = newName.split('/');
newPathParts.removeLast();
const QString newPath = newPathParts.join('/');
if (!newPathParts.isEmpty() && (oldPath != newPath)) {
qDebug("oldPath(%s) != newPath(%s)", qUtf8Printable(oldPath), qUtf8Printable(newPath));
oldPath = QString("%1/%2").arg(savePath(true), oldPath);
qDebug("Detected folder renaming, attempt to delete old folder: %s", qUtf8Printable(oldPath));
QDir().rmpath(oldPath);
}
}
// We don't really need to call updateStatus() in this place. // We don't really need to call updateStatus() in this place.
// All we need to do is make sure we have a valid instance of the TorrentInfo object. // All we need to do is make sure we have a valid instance of the TorrentInfo object.
m_torrentInfo = TorrentInfo {m_nativeHandle.torrent_file()}; m_torrentInfo = TorrentInfo {m_nativeHandle.torrent_file()};
// remove empty leftover folders
// for example renaming "a/b/c" to "d/b/c", then folders "a/b" and "a" will
// be removed if they are empty
const QString oldFilePath = m_oldPath[LTFileIndex {p->index}].takeFirst();
const QString newFilePath = Utils::Fs::fromNativePath(p->new_name());
if (m_oldPath[LTFileIndex {p->index}].isEmpty())
m_oldPath.remove(LTFileIndex {p->index});
QVector<QStringRef> oldPathParts = oldFilePath.splitRef('/', QString::SkipEmptyParts);
oldPathParts.removeLast(); // drop file name part
QVector<QStringRef> newPathParts = newFilePath.splitRef('/', QString::SkipEmptyParts);
newPathParts.removeLast(); // drop file name part
#if defined(Q_OS_WIN)
const Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive;
#else
const Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive;
#endif
int pathIdx = 0;
while ((pathIdx < oldPathParts.size()) && (pathIdx < newPathParts.size())) {
if (oldPathParts[pathIdx].compare(newPathParts[pathIdx], caseSensitivity) != 0)
break;
++pathIdx;
}
for (int i = (oldPathParts.size() - 1); i >= pathIdx; --i) {
QDir().rmdir(savePath() + Utils::String::join(oldPathParts, QLatin1String("/")));
oldPathParts.removeLast();
}
--m_renameCount; --m_renameCount;
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty()) while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
m_moveFinishedTriggers.takeFirst()(); m_moveFinishedTriggers.takeFirst()();
if (isPaused() && (m_renameCount == 0))
saveResumeData(); // otherwise the new path will not be saved
} }
void TorrentHandle::handleFileRenameFailedAlert(const lt::file_rename_failed_alert *p) void TorrentHandle::handleFileRenameFailedAlert(const lt::file_rename_failed_alert *p)
{ {
Q_UNUSED(p);
LogMsg(tr("File rename failed. Torrent: \"%1\", file: \"%2\", reason: \"%3\"") LogMsg(tr("File rename failed. Torrent: \"%1\", file: \"%2\", reason: \"%3\"")
.arg(name(), filePath(p->index) .arg(name(), filePath(p->index)
, QString::fromStdString(p->error.message())), Log::WARNING); , QString::fromStdString(p->error.message())), Log::WARNING);
m_oldPath[LTFileIndex {p->index}].removeFirst();
if (m_oldPath[LTFileIndex {p->index}].isEmpty())
m_oldPath.remove(LTFileIndex {p->index});
--m_renameCount; --m_renameCount;
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty()) while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
m_moveFinishedTriggers.takeFirst()(); m_moveFinishedTriggers.takeFirst()();
if (isPaused() && (m_renameCount == 0))
saveResumeData(); // otherwise the new path will not be saved
} }
void TorrentHandle::handleFileCompletedAlert(const lt::file_completed_alert *p) void TorrentHandle::handleFileCompletedAlert(const lt::file_completed_alert *p)

10
src/base/bittorrent/torrenthandle.h

@ -355,6 +355,12 @@ namespace BitTorrent
private: private:
typedef std::function<void ()> EventTrigger; typedef std::function<void ()> EventTrigger;
#if (LIBTORRENT_VERSION_NUM < 10200)
using LTFileIndex = int;
#else
using LTFileIndex = lt::file_index_t;
#endif
void updateStatus(); void updateStatus();
void updateStatus(const lt::torrent_status &nativeStatus); void updateStatus(const lt::torrent_status &nativeStatus);
void updateState(); void updateState();
@ -417,6 +423,10 @@ namespace BitTorrent
QQueue<EventTrigger> m_moveFinishedTriggers; QQueue<EventTrigger> m_moveFinishedTriggers;
int m_renameCount; int m_renameCount;
// Until libtorrent provide an "old_name" field in `file_renamed_alert`
// we will rely on this workaround to remove empty leftover folders
QHash<LTFileIndex, QVector<QString>> m_oldPath;
bool m_useAutoTMM; bool m_useAutoTMM;
// Persistent data // Persistent data

11
src/base/utils/string.cpp

@ -198,3 +198,14 @@ TriStateBool Utils::String::parseTriStateBool(const QString &string)
return TriStateBool::False; return TriStateBool::False;
return TriStateBool::Undefined; return TriStateBool::Undefined;
} }
QString Utils::String::join(const QVector<QStringRef> &strings, const QString &separator)
{
if (strings.empty())
return {};
QString ret = strings[0].toString();
for (int i = 1; i < strings.count(); ++i)
ret += (separator + strings[i]);
return ret;
}

4
src/base/utils/string.h

@ -31,8 +31,10 @@
#define UTILS_STRING_H #define UTILS_STRING_H
#include <QLatin1String> #include <QLatin1String>
#include <QVector>
class QString; class QString;
class QStringRef;
class TriStateBool; class TriStateBool;
@ -66,6 +68,8 @@ namespace Utils
bool parseBool(const QString &string, bool defaultValue); bool parseBool(const QString &string, bool defaultValue);
TriStateBool parseTriStateBool(const QString &string); TriStateBool parseTriStateBool(const QString &string);
QString join(const QVector<QStringRef> &strings, const QString &separator);
} }
} }

9
src/gui/torrentcontenttreeview.cpp

@ -204,15 +204,6 @@ void TorrentContentTreeView::renameSelectedFile(BitTorrent::TorrentHandle *torre
if (needForceRecheck) if (needForceRecheck)
torrent->forceRecheck(); torrent->forceRecheck();
// Remove old folder
const QString oldFullPath = torrent->savePath(true) + oldPath;
int timeout = 10;
while (!QDir().rmpath(oldFullPath) && (timeout > 0)) {
// FIXME: We should not sleep here (freezes the UI for 1 second)
QThread::msleep(100);
--timeout;
}
model->setData(modelIndex, newName); model->setData(modelIndex, newName);
} }
} }

Loading…
Cancel
Save