Merge pull request #10792 from Chocobo1/rename2

Remove empty leftover folders after rename properly
This commit is contained in:
Mike Tzou 2019-06-16 12:22:22 +08:00 committed by GitHub
commit 46d445d042
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 82 additions and 35 deletions

View file

@ -1657,7 +1657,7 @@ bool Session::deleteTorrent(const QString &hash, const bool deleteLocalFiles)
Utils::Fs::forceRemove(unwantedFile);
const QString parentFolder = Utils::Fs::branchPath(unwantedFile);
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)) {
if (!torrent->isValid()) continue;
if (torrent->isChecking() || torrent->isPaused()) continue;
if (!final && !torrent->needSaveResumeData()) continue;
if (torrent->hasMissingFiles() || torrent->hasError()) continue;
if (torrent->isChecking()
|| torrent->isPaused()
|| torrent->hasError()
|| torrent->hasMissingFiles())
continue;
saveTorrentResumeData(torrent);
}

View file

@ -59,6 +59,7 @@
#include "base/profile.h"
#include "base/tristatebool.h"
#include "base/utils/fs.h"
#include "base/utils/string.h"
#include "downloadpriority.h"
#include "peerinfo.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)
{
m_oldPath[LTFileIndex {index}].push_back(filePath(index));
++m_renameCount;
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
// 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);
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);
}
}
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)
{
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.
// All we need to do is make sure we have a valid instance of the TorrentInfo object.
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;
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
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)
{
Q_UNUSED(p);
LogMsg(tr("File rename failed. Torrent: \"%1\", file: \"%2\", reason: \"%3\"")
.arg(name(), filePath(p->index)
, 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;
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
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)

View file

@ -355,6 +355,12 @@ namespace BitTorrent
private:
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(const lt::torrent_status &nativeStatus);
void updateState();
@ -417,6 +423,10 @@ namespace BitTorrent
QQueue<EventTrigger> m_moveFinishedTriggers;
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;
// Persistent data

View file

@ -198,3 +198,14 @@ TriStateBool Utils::String::parseTriStateBool(const QString &string)
return TriStateBool::False;
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;
}

View file

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

View file

@ -204,15 +204,6 @@ void TorrentContentTreeView::renameSelectedFile(BitTorrent::TorrentHandle *torre
if (needForceRecheck)
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);
}
}