mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-11-28 13:28:50 +03:00
Merge pull request #10792 from Chocobo1/rename2
Remove empty leftover folders after rename properly
This commit is contained in:
commit
46d445d042
6 changed files with 82 additions and 35 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue