mirror of
https://github.com/d47081/qBittorrent.git
synced 2025-01-11 07:18:08 +00:00
Merge pull request #13995 from glassez/rename-files
Improve content file/folder names handling
This commit is contained in:
commit
348109a1f9
@ -2,6 +2,7 @@ add_library(qbt_base STATIC
|
|||||||
# headers
|
# headers
|
||||||
algorithm.h
|
algorithm.h
|
||||||
asyncfilestorage.h
|
asyncfilestorage.h
|
||||||
|
bittorrent/abstractfilestorage.h
|
||||||
bittorrent/addtorrentparams.h
|
bittorrent/addtorrentparams.h
|
||||||
bittorrent/bandwidthscheduler.h
|
bittorrent/bandwidthscheduler.h
|
||||||
bittorrent/cachestatus.h
|
bittorrent/cachestatus.h
|
||||||
@ -89,6 +90,7 @@ add_library(qbt_base STATIC
|
|||||||
|
|
||||||
# sources
|
# sources
|
||||||
asyncfilestorage.cpp
|
asyncfilestorage.cpp
|
||||||
|
bittorrent/abstractfilestorage.cpp
|
||||||
bittorrent/bandwidthscheduler.cpp
|
bittorrent/bandwidthscheduler.cpp
|
||||||
bittorrent/customstorage.cpp
|
bittorrent/customstorage.cpp
|
||||||
bittorrent/downloadpriority.cpp
|
bittorrent/downloadpriority.cpp
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
HEADERS += \
|
HEADERS += \
|
||||||
$$PWD/algorithm.h \
|
$$PWD/algorithm.h \
|
||||||
$$PWD/asyncfilestorage.h \
|
$$PWD/asyncfilestorage.h \
|
||||||
|
$$PWD/bittorrent/abstractfilestorage.h \
|
||||||
$$PWD/bittorrent/addtorrentparams.h \
|
$$PWD/bittorrent/addtorrentparams.h \
|
||||||
$$PWD/bittorrent/bandwidthscheduler.h \
|
$$PWD/bittorrent/bandwidthscheduler.h \
|
||||||
$$PWD/bittorrent/cachestatus.h \
|
$$PWD/bittorrent/cachestatus.h \
|
||||||
@ -89,6 +90,7 @@ HEADERS += \
|
|||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
$$PWD/asyncfilestorage.cpp \
|
$$PWD/asyncfilestorage.cpp \
|
||||||
|
$$PWD/bittorrent/abstractfilestorage.cpp \
|
||||||
$$PWD/bittorrent/bandwidthscheduler.cpp \
|
$$PWD/bittorrent/bandwidthscheduler.cpp \
|
||||||
$$PWD/bittorrent/customstorage.cpp \
|
$$PWD/bittorrent/customstorage.cpp \
|
||||||
$$PWD/bittorrent/downloadpriority.cpp \
|
$$PWD/bittorrent/downloadpriority.cpp \
|
||||||
|
140
src/base/bittorrent/abstractfilestorage.cpp
Normal file
140
src/base/bittorrent/abstractfilestorage.cpp
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2020 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* In addition, as a special exception, the copyright holders give permission to
|
||||||
|
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. You must obey the GNU General Public
|
||||||
|
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||||
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
|
* exception statement from your version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "abstractfilestorage.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
|
#include "base/bittorrent/common.h"
|
||||||
|
#include "base/exceptions.h"
|
||||||
|
#include "base/utils/fs.h"
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
const Qt::CaseSensitivity CASE_SENSITIVITY {Qt::CaseInsensitive};
|
||||||
|
#else
|
||||||
|
const Qt::CaseSensitivity CASE_SENSITIVITY {Qt::CaseSensitive};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
bool areSameFileNames(QString first, QString second)
|
||||||
|
{
|
||||||
|
if (first.endsWith(QB_EXT, Qt::CaseInsensitive))
|
||||||
|
first.chop(QB_EXT.size());
|
||||||
|
if (second.endsWith(QB_EXT, Qt::CaseInsensitive))
|
||||||
|
second.chop(QB_EXT.size());
|
||||||
|
return QString::compare(first, second, CASE_SENSITIVITY) == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BitTorrent::AbstractFileStorage::renameFile(const QString &oldPath, const QString &newPath)
|
||||||
|
{
|
||||||
|
if (!Utils::Fs::isValidFileSystemName(oldPath, true))
|
||||||
|
throw RuntimeError {tr("The old path is invalid: '%1'.").arg(oldPath)};
|
||||||
|
if (!Utils::Fs::isValidFileSystemName(newPath, true))
|
||||||
|
throw RuntimeError {tr("The new path is invalid: '%1'.").arg(newPath)};
|
||||||
|
|
||||||
|
const QString oldFilePath = Utils::Fs::toUniformPath(oldPath);
|
||||||
|
if (oldFilePath.endsWith(QLatin1Char {'/'}))
|
||||||
|
throw RuntimeError {tr("Invalid file path: '%1'.").arg(oldFilePath)};
|
||||||
|
|
||||||
|
const QString newFilePath = Utils::Fs::toUniformPath(newPath);
|
||||||
|
if (newFilePath.endsWith(QLatin1Char {'/'}))
|
||||||
|
throw RuntimeError {tr("Invalid file path: '%1'.").arg(newFilePath)};
|
||||||
|
if (QDir().isAbsolutePath(newFilePath))
|
||||||
|
throw RuntimeError {tr("Absolute path isn't allowed: '%1'.").arg(newFilePath)};
|
||||||
|
|
||||||
|
int renamingFileIndex = -1;
|
||||||
|
for (int i = 0; i < filesCount(); ++i)
|
||||||
|
{
|
||||||
|
const QString path = filePath(i);
|
||||||
|
|
||||||
|
if ((renamingFileIndex < 0) && areSameFileNames(path, oldFilePath))
|
||||||
|
renamingFileIndex = i;
|
||||||
|
|
||||||
|
if (areSameFileNames(path, newFilePath))
|
||||||
|
throw RuntimeError {tr("The file already exists: '%1'.").arg(newFilePath)};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renamingFileIndex < 0)
|
||||||
|
throw RuntimeError {tr("No such file: '%1'.").arg(oldFilePath)};
|
||||||
|
|
||||||
|
const auto extAdjusted = [](const QString &path, const bool needExt) -> QString
|
||||||
|
{
|
||||||
|
if (path.endsWith(QB_EXT, Qt::CaseInsensitive) == needExt)
|
||||||
|
return path;
|
||||||
|
|
||||||
|
return (needExt ? (path + QB_EXT) : (path.left(path.size() - QB_EXT.size())));
|
||||||
|
};
|
||||||
|
|
||||||
|
renameFile(renamingFileIndex, extAdjusted(newFilePath, filePath(renamingFileIndex).endsWith(QB_EXT, Qt::CaseInsensitive)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void BitTorrent::AbstractFileStorage::renameFolder(const QString &oldPath, const QString &newPath)
|
||||||
|
{
|
||||||
|
if (!Utils::Fs::isValidFileSystemName(oldPath, true))
|
||||||
|
throw RuntimeError {tr("The old path is invalid: '%1'.").arg(oldPath)};
|
||||||
|
if (!Utils::Fs::isValidFileSystemName(newPath, true))
|
||||||
|
throw RuntimeError {tr("The new path is invalid: '%1'.").arg(newPath)};
|
||||||
|
|
||||||
|
const auto cleanFolderPath = [](const QString &path) -> QString
|
||||||
|
{
|
||||||
|
const QString uniformPath = Utils::Fs::toUniformPath(path);
|
||||||
|
return (uniformPath.endsWith(QLatin1Char {'/'}) ? uniformPath : uniformPath + QLatin1Char {'/'});
|
||||||
|
};
|
||||||
|
|
||||||
|
const QString oldFolderPath = cleanFolderPath(oldPath);
|
||||||
|
const QString newFolderPath = cleanFolderPath(newPath);
|
||||||
|
if (QDir().isAbsolutePath(newFolderPath))
|
||||||
|
throw RuntimeError {tr("Absolute path isn't allowed: '%1'.").arg(newFolderPath)};
|
||||||
|
|
||||||
|
QVector<int> renamingFileIndexes;
|
||||||
|
renamingFileIndexes.reserve(filesCount());
|
||||||
|
|
||||||
|
for (int i = 0; i < filesCount(); ++i)
|
||||||
|
{
|
||||||
|
const QString path = filePath(i);
|
||||||
|
|
||||||
|
if (path.startsWith(oldFolderPath, CASE_SENSITIVITY))
|
||||||
|
renamingFileIndexes.append(i);
|
||||||
|
|
||||||
|
if (path.startsWith(newFolderPath, CASE_SENSITIVITY))
|
||||||
|
throw RuntimeError {tr("The folder already exists: '%1'.").arg(newFolderPath)};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renamingFileIndexes.isEmpty())
|
||||||
|
throw RuntimeError {tr("No such folder: '%1'.").arg(oldFolderPath)};
|
||||||
|
|
||||||
|
for (const int index : renamingFileIndexes)
|
||||||
|
{
|
||||||
|
const QString newFilePath = newFolderPath + filePath(index).mid(oldFolderPath.size());
|
||||||
|
renameFile(index, newFilePath);
|
||||||
|
}
|
||||||
|
}
|
53
src/base/bittorrent/abstractfilestorage.h
Normal file
53
src/base/bittorrent/abstractfilestorage.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2020 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version 2
|
||||||
|
* of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
* In addition, as a special exception, the copyright holders give permission to
|
||||||
|
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
||||||
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
||||||
|
* and distribute the linked executables. You must obey the GNU General Public
|
||||||
|
* License in all respects for all of the code used other than "OpenSSL". If you
|
||||||
|
* modify file(s), you may extend this exception to your version of the file(s),
|
||||||
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
||||||
|
* exception statement from your version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QtGlobal>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
|
||||||
|
class QString;
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
class AbstractFileStorage
|
||||||
|
{
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(AbstractFileStorage)
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual int filesCount() const = 0;
|
||||||
|
virtual QString filePath(int index) const = 0;
|
||||||
|
virtual QString fileName(int index) const = 0;
|
||||||
|
virtual qlonglong fileSize(int index) const = 0;
|
||||||
|
|
||||||
|
virtual void renameFile(int index, const QString &name) = 0;
|
||||||
|
|
||||||
|
void renameFile(const QString &oldPath, const QString &newPath);
|
||||||
|
void renameFolder(const QString &oldPath, const QString &newPath);
|
||||||
|
};
|
||||||
|
}
|
@ -33,6 +33,8 @@
|
|||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QtContainerFwd>
|
#include <QtContainerFwd>
|
||||||
|
|
||||||
|
#include "abstractfilestorage.h"
|
||||||
|
|
||||||
class QBitArray;
|
class QBitArray;
|
||||||
class QDateTime;
|
class QDateTime;
|
||||||
class QUrl;
|
class QUrl;
|
||||||
@ -89,7 +91,7 @@ namespace BitTorrent
|
|||||||
|
|
||||||
uint qHash(TorrentState key, uint seed);
|
uint qHash(TorrentState key, uint seed);
|
||||||
|
|
||||||
class TorrentHandle
|
class TorrentHandle : public AbstractFileStorage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const qreal USE_GLOBAL_RATIO;
|
static const qreal USE_GLOBAL_RATIO;
|
||||||
@ -177,7 +179,6 @@ namespace BitTorrent
|
|||||||
virtual bool removeTag(const QString &tag) = 0;
|
virtual bool removeTag(const QString &tag) = 0;
|
||||||
virtual void removeAllTags() = 0;
|
virtual void removeAllTags() = 0;
|
||||||
|
|
||||||
virtual int filesCount() const = 0;
|
|
||||||
virtual int piecesCount() const = 0;
|
virtual int piecesCount() const = 0;
|
||||||
virtual int piecesHave() const = 0;
|
virtual int piecesHave() const = 0;
|
||||||
virtual qreal progress() const = 0;
|
virtual qreal progress() const = 0;
|
||||||
@ -185,9 +186,6 @@ namespace BitTorrent
|
|||||||
virtual qreal ratioLimit() const = 0;
|
virtual qreal ratioLimit() const = 0;
|
||||||
virtual int seedingTimeLimit() const = 0;
|
virtual int seedingTimeLimit() const = 0;
|
||||||
|
|
||||||
virtual QString filePath(int index) const = 0;
|
|
||||||
virtual QString fileName(int index) const = 0;
|
|
||||||
virtual qlonglong fileSize(int index) const = 0;
|
|
||||||
virtual QStringList absoluteFilePaths() const = 0;
|
virtual QStringList absoluteFilePaths() const = 0;
|
||||||
virtual QVector<DownloadPriority> filePriorities() const = 0;
|
virtual QVector<DownloadPriority> filePriorities() const = 0;
|
||||||
|
|
||||||
@ -273,7 +271,6 @@ namespace BitTorrent
|
|||||||
virtual void forceReannounce(int index = -1) = 0;
|
virtual void forceReannounce(int index = -1) = 0;
|
||||||
virtual void forceDHTAnnounce() = 0;
|
virtual void forceDHTAnnounce() = 0;
|
||||||
virtual void forceRecheck() = 0;
|
virtual void forceRecheck() = 0;
|
||||||
virtual void renameFile(int index, const QString &name) = 0;
|
|
||||||
virtual void prioritizeFiles(const QVector<DownloadPriority> &priorities) = 0;
|
virtual void prioritizeFiles(const QVector<DownloadPriority> &priorities) = 0;
|
||||||
virtual void setRatioLimit(qreal limit) = 0;
|
virtual void setRatioLimit(qreal limit) = 0;
|
||||||
virtual void setSeedingTimeLimit(int limit) = 0;
|
virtual void setSeedingTimeLimit(int limit) = 0;
|
||||||
|
@ -1405,11 +1405,12 @@ void TorrentHandleImpl::moveStorage(const QString &newPath, const MoveStorageMod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandleImpl::renameFile(const int index, const QString &name)
|
void TorrentHandleImpl::renameFile(const int index, const QString &path)
|
||||||
{
|
{
|
||||||
m_oldPath[lt::file_index_t {index}].push_back(filePath(index));
|
const QString oldPath = filePath(index);
|
||||||
|
m_oldPath[lt::file_index_t {index}].push_back(oldPath);
|
||||||
++m_renameCount;
|
++m_renameCount;
|
||||||
m_nativeHandle.rename_file(lt::file_index_t {index}, Utils::Fs::toNativePath(name).toStdString());
|
m_nativeHandle.rename_file(lt::file_index_t {index}, Utils::Fs::toNativePath(path).toStdString());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentHandleImpl::handleStateUpdate(const lt::torrent_status &nativeStatus)
|
void TorrentHandleImpl::handleStateUpdate(const lt::torrent_status &nativeStatus)
|
||||||
@ -1826,7 +1827,7 @@ void TorrentHandleImpl::manageIncompleteFiles()
|
|||||||
QString name = filePath(i);
|
QString name = filePath(i);
|
||||||
if (isAppendExtensionEnabled && (fileSize(i) > 0) && (fp[i] < 1))
|
if (isAppendExtensionEnabled && (fileSize(i) > 0) && (fp[i] < 1))
|
||||||
{
|
{
|
||||||
if (!name.endsWith(QB_EXT))
|
if (!name.endsWith(QB_EXT, Qt::CaseInsensitive))
|
||||||
{
|
{
|
||||||
const QString newName = name + QB_EXT;
|
const QString newName = name + QB_EXT;
|
||||||
qDebug() << "Renaming" << name << "to" << newName;
|
qDebug() << "Renaming" << name << "to" << newName;
|
||||||
@ -1835,7 +1836,7 @@ void TorrentHandleImpl::manageIncompleteFiles()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (name.endsWith(QB_EXT))
|
if (name.endsWith(QB_EXT, Qt::CaseInsensitive))
|
||||||
{
|
{
|
||||||
const QString oldName = name;
|
const QString oldName = name;
|
||||||
name.chop(QB_EXT.size());
|
name.chop(QB_EXT.size());
|
||||||
|
@ -220,7 +220,7 @@ namespace BitTorrent
|
|||||||
void forceReannounce(int index = -1) override;
|
void forceReannounce(int index = -1) override;
|
||||||
void forceDHTAnnounce() override;
|
void forceDHTAnnounce() override;
|
||||||
void forceRecheck() override;
|
void forceRecheck() override;
|
||||||
void renameFile(int index, const QString &name) override;
|
void renameFile(int index, const QString &path) override;
|
||||||
void prioritizeFiles(const QVector<DownloadPriority> &priorities) override;
|
void prioritizeFiles(const QVector<DownloadPriority> &priorities) override;
|
||||||
void setRatioLimit(qreal limit) override;
|
void setRatioLimit(qreal limit) override;
|
||||||
void setSeedingTimeLimit(int limit) override;
|
void setSeedingTimeLimit(int limit) override;
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include <QtContainerFwd>
|
#include <QtContainerFwd>
|
||||||
|
|
||||||
#include "base/indexrange.h"
|
#include "base/indexrange.h"
|
||||||
|
#include "abstractfilestorage.h"
|
||||||
#include "torrentcontentlayout.h"
|
#include "torrentcontentlayout.h"
|
||||||
|
|
||||||
class QByteArray;
|
class QByteArray;
|
||||||
@ -46,7 +47,7 @@ namespace BitTorrent
|
|||||||
class InfoHash;
|
class InfoHash;
|
||||||
class TrackerEntry;
|
class TrackerEntry;
|
||||||
|
|
||||||
class TorrentInfo
|
class TorrentInfo final : public AbstractFileStorage
|
||||||
{
|
{
|
||||||
Q_DECLARE_TR_FUNCTIONS(TorrentInfo)
|
Q_DECLARE_TR_FUNCTIONS(TorrentInfo)
|
||||||
|
|
||||||
@ -68,15 +69,15 @@ namespace BitTorrent
|
|||||||
QString comment() const;
|
QString comment() const;
|
||||||
bool isPrivate() const;
|
bool isPrivate() const;
|
||||||
qlonglong totalSize() const;
|
qlonglong totalSize() const;
|
||||||
int filesCount() const;
|
int filesCount() const override;
|
||||||
int pieceLength() const;
|
int pieceLength() const;
|
||||||
int pieceLength(int index) const;
|
int pieceLength(int index) const;
|
||||||
int piecesCount() const;
|
int piecesCount() const;
|
||||||
QString filePath(int index) const;
|
QString filePath(int index) const override;
|
||||||
QStringList filePaths() const;
|
QStringList filePaths() const;
|
||||||
QString fileName(int index) const;
|
QString fileName(int index) const override;
|
||||||
QString origFilePath(int index) const;
|
QString origFilePath(int index) const;
|
||||||
qlonglong fileSize(int index) const;
|
qlonglong fileSize(int index) const override;
|
||||||
qlonglong fileOffset(int index) const;
|
qlonglong fileOffset(int index) const;
|
||||||
QVector<TrackerEntry> trackers() const;
|
QVector<TrackerEntry> trackers() const;
|
||||||
QVector<QUrl> urlSeeds() const;
|
QVector<QUrl> urlSeeds() const;
|
||||||
@ -91,7 +92,7 @@ namespace BitTorrent
|
|||||||
PieceRange filePieces(const QString &file) const;
|
PieceRange filePieces(const QString &file) const;
|
||||||
PieceRange filePieces(int fileIndex) const;
|
PieceRange filePieces(int fileIndex) const;
|
||||||
|
|
||||||
void renameFile(int index, const QString &newPath);
|
void renameFile(int index, const QString &newPath) override;
|
||||||
|
|
||||||
QString rootFolder() const;
|
QString rootFolder() const;
|
||||||
bool hasRootFolder() const;
|
bool hasRootFolder() const;
|
||||||
|
@ -95,7 +95,7 @@ QString Utils::Fs::folderName(const QString &filePath)
|
|||||||
const QString path = toUniformPath(filePath);
|
const QString path = toUniformPath(filePath);
|
||||||
const int slashIndex = path.lastIndexOf('/');
|
const int slashIndex = path.lastIndexOf('/');
|
||||||
if (slashIndex == -1)
|
if (slashIndex == -1)
|
||||||
return path;
|
return {};
|
||||||
return path.left(slashIndex);
|
return path.left(slashIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ PropertiesWidget::PropertiesWidget(QWidget *parent)
|
|||||||
connect(m_ui->listWebSeeds, &QListWidget::doubleClicked, this, &PropertiesWidget::editWebSeed);
|
connect(m_ui->listWebSeeds, &QListWidget::doubleClicked, this, &PropertiesWidget::editWebSeed);
|
||||||
|
|
||||||
const auto *renameFileHotkey = new QShortcut(Qt::Key_F2, m_ui->filesList, nullptr, nullptr, Qt::WidgetShortcut);
|
const auto *renameFileHotkey = new QShortcut(Qt::Key_F2, m_ui->filesList, nullptr, nullptr, Qt::WidgetShortcut);
|
||||||
connect(renameFileHotkey, &QShortcut::activated, this, [this]() { m_ui->filesList->renameSelectedFile(m_torrent); });
|
connect(renameFileHotkey, &QShortcut::activated, this, [this]() { m_ui->filesList->renameSelectedFile(*m_torrent); });
|
||||||
const auto *openFileHotkeyReturn = new QShortcut(Qt::Key_Return, m_ui->filesList, nullptr, nullptr, Qt::WidgetShortcut);
|
const auto *openFileHotkeyReturn = new QShortcut(Qt::Key_Return, m_ui->filesList, nullptr, nullptr, Qt::WidgetShortcut);
|
||||||
connect(openFileHotkeyReturn, &QShortcut::activated, this, &PropertiesWidget::openSelectedFile);
|
connect(openFileHotkeyReturn, &QShortcut::activated, this, &PropertiesWidget::openSelectedFile);
|
||||||
const auto *openFileHotkeyEnter = new QShortcut(Qt::Key_Enter, m_ui->filesList, nullptr, nullptr, Qt::WidgetShortcut);
|
const auto *openFileHotkeyEnter = new QShortcut(Qt::Key_Enter, m_ui->filesList, nullptr, nullptr, Qt::WidgetShortcut);
|
||||||
@ -593,7 +593,7 @@ void PropertiesWidget::displayFilesListMenu(const QPoint &)
|
|||||||
connect(actOpenContainingFolder, &QAction::triggered, this, [this, index]() { openParentFolder(index); });
|
connect(actOpenContainingFolder, &QAction::triggered, this, [this, index]() { openParentFolder(index); });
|
||||||
|
|
||||||
const QAction *actRename = menu->addAction(UIThemeManager::instance()->getIcon("edit-rename"), tr("Rename..."));
|
const QAction *actRename = menu->addAction(UIThemeManager::instance()->getIcon("edit-rename"), tr("Rename..."));
|
||||||
connect(actRename, &QAction::triggered, this, [this]() { m_ui->filesList->renameSelectedFile(m_torrent); });
|
connect(actRename, &QAction::triggered, this, [this]() { m_ui->filesList->renameSelectedFile(*m_torrent); });
|
||||||
|
|
||||||
menu->addSeparator();
|
menu->addSeparator();
|
||||||
}
|
}
|
||||||
|
@ -37,10 +37,12 @@
|
|||||||
#include <QTableView>
|
#include <QTableView>
|
||||||
#include <QThread>
|
#include <QThread>
|
||||||
|
|
||||||
|
#include "base/bittorrent/abstractfilestorage.h"
|
||||||
#include "base/bittorrent/common.h"
|
#include "base/bittorrent/common.h"
|
||||||
#include "base/bittorrent/session.h"
|
#include "base/bittorrent/session.h"
|
||||||
#include "base/bittorrent/torrenthandle.h"
|
#include "base/bittorrent/torrenthandle.h"
|
||||||
#include "base/bittorrent/torrentinfo.h"
|
#include "base/bittorrent/torrentinfo.h"
|
||||||
|
#include "base/exceptions.h"
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "autoexpandabledialog.h"
|
#include "autoexpandabledialog.h"
|
||||||
@ -48,6 +50,17 @@
|
|||||||
#include "torrentcontentfiltermodel.h"
|
#include "torrentcontentfiltermodel.h"
|
||||||
#include "torrentcontentmodelitem.h"
|
#include "torrentcontentmodelitem.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
QString getFullPath(const QModelIndex &idx)
|
||||||
|
{
|
||||||
|
QStringList paths;
|
||||||
|
for (QModelIndex i = idx; i.isValid(); i = i.parent())
|
||||||
|
paths.prepend(i.data().toString());
|
||||||
|
return paths.join(QLatin1Char {'/'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TorrentContentTreeView::TorrentContentTreeView(QWidget *parent)
|
TorrentContentTreeView::TorrentContentTreeView(QWidget *parent)
|
||||||
: QTreeView(parent)
|
: QTreeView(parent)
|
||||||
{
|
{
|
||||||
@ -91,138 +104,7 @@ void TorrentContentTreeView::keyPressEvent(QKeyEvent *event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentContentTreeView::renameSelectedFile(BitTorrent::TorrentHandle *torrent)
|
void TorrentContentTreeView::renameSelectedFile(BitTorrent::AbstractFileStorage &fileStorage)
|
||||||
{
|
|
||||||
if (!torrent) return;
|
|
||||||
|
|
||||||
const QModelIndexList selectedIndexes = selectionModel()->selectedRows(0);
|
|
||||||
if (selectedIndexes.size() != 1) return;
|
|
||||||
|
|
||||||
const QPersistentModelIndex modelIndex = selectedIndexes.first();
|
|
||||||
if (!modelIndex.isValid()) return;
|
|
||||||
|
|
||||||
auto model = dynamic_cast<TorrentContentFilterModel *>(TorrentContentTreeView::model());
|
|
||||||
if (!model) return;
|
|
||||||
|
|
||||||
const bool isFile = (model->itemType(modelIndex) == TorrentContentModelItem::FileType);
|
|
||||||
|
|
||||||
// Ask for new name
|
|
||||||
bool ok = false;
|
|
||||||
QString newName = AutoExpandableDialog::getText(this, tr("Renaming"), tr("New name:"), QLineEdit::Normal
|
|
||||||
, modelIndex.data().toString(), &ok, isFile).trimmed();
|
|
||||||
if (!ok || !modelIndex.isValid()) return;
|
|
||||||
|
|
||||||
if (!Utils::Fs::isValidFileSystemName(newName))
|
|
||||||
{
|
|
||||||
RaisedMessageBox::warning(this, tr("Rename error"),
|
|
||||||
tr("The name is empty or contains forbidden characters, please choose a different one."),
|
|
||||||
QMessageBox::Ok);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFile)
|
|
||||||
{
|
|
||||||
const int fileIndex = model->getFileIndex(modelIndex);
|
|
||||||
|
|
||||||
if (newName.endsWith(QB_EXT))
|
|
||||||
newName.chop(QB_EXT.size());
|
|
||||||
const QString oldFileName = torrent->fileName(fileIndex);
|
|
||||||
const QString oldFilePath = torrent->filePath(fileIndex);
|
|
||||||
|
|
||||||
const bool useFilenameExt = BitTorrent::Session::instance()->isAppendExtensionEnabled()
|
|
||||||
&& (torrent->filesProgress()[fileIndex] != 1);
|
|
||||||
const QString newFileName = newName + (useFilenameExt ? QB_EXT : QString());
|
|
||||||
const QString newFilePath = oldFilePath.leftRef(oldFilePath.size() - oldFileName.size()) + newFileName;
|
|
||||||
|
|
||||||
if (oldFileName == newFileName)
|
|
||||||
{
|
|
||||||
qDebug("Name did not change: %s", qUtf8Printable(oldFileName));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if that name is already used
|
|
||||||
for (int i = 0; i < torrent->filesCount(); ++i)
|
|
||||||
{
|
|
||||||
if (i == fileIndex) continue;
|
|
||||||
if (Utils::Fs::sameFileNames(torrent->filePath(i), newFilePath))
|
|
||||||
{
|
|
||||||
RaisedMessageBox::warning(this, tr("Rename error"),
|
|
||||||
tr("This name is already in use in this folder. Please use a different name."),
|
|
||||||
QMessageBox::Ok);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug("Renaming %s to %s", qUtf8Printable(oldFilePath), qUtf8Printable(newFilePath));
|
|
||||||
torrent->renameFile(fileIndex, newFilePath);
|
|
||||||
|
|
||||||
model->setData(modelIndex, newName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// renaming a folder
|
|
||||||
|
|
||||||
const QString oldName = modelIndex.data().toString();
|
|
||||||
if (newName == oldName)
|
|
||||||
return; // Name did not change
|
|
||||||
|
|
||||||
QString parentPath;
|
|
||||||
for (QModelIndex idx = model->parent(modelIndex); idx.isValid(); idx = model->parent(idx))
|
|
||||||
parentPath.prepend(idx.data().toString() + '/');
|
|
||||||
|
|
||||||
const QString oldPath {parentPath + oldName + '/'};
|
|
||||||
const QString newPath {parentPath + newName + '/'};
|
|
||||||
|
|
||||||
// Check for overwriting
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
const Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive;
|
|
||||||
#else
|
|
||||||
const Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (int i = 0; i < torrent->filesCount(); ++i)
|
|
||||||
{
|
|
||||||
const QString currentPath = torrent->filePath(i);
|
|
||||||
|
|
||||||
if (currentPath.startsWith(oldPath))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (currentPath.startsWith(newPath, caseSensitivity))
|
|
||||||
{
|
|
||||||
RaisedMessageBox::warning(this, tr("The folder could not be renamed"),
|
|
||||||
tr("This name is already in use. Please use a different name."),
|
|
||||||
QMessageBox::Ok);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace path in all files
|
|
||||||
bool needForceRecheck = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < torrent->filesCount(); ++i)
|
|
||||||
{
|
|
||||||
const QString currentPath = torrent->filePath(i);
|
|
||||||
|
|
||||||
if (currentPath.startsWith(oldPath))
|
|
||||||
{
|
|
||||||
const QString path {newPath + currentPath.mid(oldPath.length())};
|
|
||||||
|
|
||||||
if (!needForceRecheck && QFile::exists(path))
|
|
||||||
needForceRecheck = true;
|
|
||||||
|
|
||||||
torrent->renameFile(i, path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force recheck
|
|
||||||
if (needForceRecheck)
|
|
||||||
torrent->forceRecheck();
|
|
||||||
|
|
||||||
model->setData(modelIndex, newName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TorrentContentTreeView::renameSelectedFile(BitTorrent::TorrentInfo &torrent)
|
|
||||||
{
|
{
|
||||||
const QModelIndexList selectedIndexes = selectionModel()->selectedRows(0);
|
const QModelIndexList selectedIndexes = selectionModel()->selectedRows(0);
|
||||||
if (selectedIndexes.size() != 1) return;
|
if (selectedIndexes.size() != 1) return;
|
||||||
@ -241,99 +123,26 @@ void TorrentContentTreeView::renameSelectedFile(BitTorrent::TorrentInfo &torrent
|
|||||||
, modelIndex.data().toString(), &ok, isFile).trimmed();
|
, modelIndex.data().toString(), &ok, isFile).trimmed();
|
||||||
if (!ok || !modelIndex.isValid()) return;
|
if (!ok || !modelIndex.isValid()) return;
|
||||||
|
|
||||||
if (!Utils::Fs::isValidFileSystemName(newName))
|
const QString oldName = modelIndex.data().toString();
|
||||||
|
if (newName == oldName)
|
||||||
|
return; // Name did not change
|
||||||
|
|
||||||
|
const QString parentPath = getFullPath(modelIndex.parent());
|
||||||
|
const QString oldPath {parentPath.isEmpty() ? oldName : parentPath + QLatin1Char {'/'} + oldName};
|
||||||
|
const QString newPath {parentPath.isEmpty() ? newName : parentPath + QLatin1Char {'/'} + newName};
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
RaisedMessageBox::warning(this, tr("Rename error"),
|
if (isFile)
|
||||||
tr("The name is empty or contains forbidden characters, please choose a different one."),
|
fileStorage.renameFile(oldPath, newPath);
|
||||||
QMessageBox::Ok);
|
else
|
||||||
return;
|
fileStorage.renameFolder(oldPath, newPath);
|
||||||
}
|
|
||||||
|
|
||||||
if (isFile)
|
|
||||||
{
|
|
||||||
const int fileIndex = model->getFileIndex(modelIndex);
|
|
||||||
|
|
||||||
if (newName.endsWith(QB_EXT))
|
|
||||||
newName.chop(QB_EXT.size());
|
|
||||||
const QString oldFileName = torrent.fileName(fileIndex);
|
|
||||||
const QString oldFilePath = torrent.filePath(fileIndex);
|
|
||||||
const QString newFilePath = oldFilePath.leftRef(oldFilePath.size() - oldFileName.size()) + newName;
|
|
||||||
|
|
||||||
if (oldFileName == newName)
|
|
||||||
{
|
|
||||||
qDebug("Name did not change: %s", qUtf8Printable(oldFileName));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if that name is already used
|
|
||||||
for (int i = 0; i < torrent.filesCount(); ++i)
|
|
||||||
{
|
|
||||||
if (i == fileIndex) continue;
|
|
||||||
if (Utils::Fs::sameFileNames(torrent.filePath(i), newFilePath))
|
|
||||||
{
|
|
||||||
RaisedMessageBox::warning(this, tr("Rename error"),
|
|
||||||
tr("This name is already in use in this folder. Please use a different name."),
|
|
||||||
QMessageBox::Ok);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug("Renaming %s to %s", qUtf8Printable(oldFilePath), qUtf8Printable(newFilePath));
|
|
||||||
torrent.renameFile(fileIndex, newFilePath);
|
|
||||||
|
|
||||||
model->setData(modelIndex, newName);
|
model->setData(modelIndex, newName);
|
||||||
}
|
}
|
||||||
else
|
catch (const RuntimeError &error)
|
||||||
{
|
{
|
||||||
// renaming a folder
|
RaisedMessageBox::warning(this, tr("Rename error"), error.message(), QMessageBox::Ok);
|
||||||
|
|
||||||
const QString oldName = modelIndex.data().toString();
|
|
||||||
if (newName == oldName)
|
|
||||||
return; // Name did not change
|
|
||||||
|
|
||||||
QString parentPath;
|
|
||||||
for (QModelIndex idx = model->parent(modelIndex); idx.isValid(); idx = model->parent(idx))
|
|
||||||
parentPath.prepend(idx.data().toString() + '/');
|
|
||||||
|
|
||||||
const QString oldPath {parentPath + oldName + '/'};
|
|
||||||
const QString newPath {parentPath + newName + '/'};
|
|
||||||
|
|
||||||
// Check for overwriting
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
const Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive;
|
|
||||||
#else
|
|
||||||
const Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (int i = 0; i < torrent.filesCount(); ++i)
|
|
||||||
{
|
|
||||||
const QString currentPath = torrent.filePath(i);
|
|
||||||
|
|
||||||
if (currentPath.startsWith(oldPath))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (currentPath.startsWith(newPath, caseSensitivity))
|
|
||||||
{
|
|
||||||
RaisedMessageBox::warning(this, tr("The folder could not be renamed"),
|
|
||||||
tr("This name is already in use. Please use a different name."),
|
|
||||||
QMessageBox::Ok);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace path in all files
|
|
||||||
for (int i = 0; i < torrent.filesCount(); ++i)
|
|
||||||
{
|
|
||||||
const QString currentPath = torrent.filePath(i);
|
|
||||||
|
|
||||||
if (currentPath.startsWith(oldPath))
|
|
||||||
{
|
|
||||||
const QString path {newPath + currentPath.mid(oldPath.length())};
|
|
||||||
torrent.renameFile(i, path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
model->setData(modelIndex, newName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
|
class AbstractFileStorage;
|
||||||
class TorrentHandle;
|
class TorrentHandle;
|
||||||
class TorrentInfo;
|
class TorrentInfo;
|
||||||
}
|
}
|
||||||
@ -39,13 +40,13 @@ namespace BitTorrent
|
|||||||
class TorrentContentTreeView final : public QTreeView
|
class TorrentContentTreeView final : public QTreeView
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(TorrentContentTreeView)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit TorrentContentTreeView(QWidget *parent = nullptr);
|
explicit TorrentContentTreeView(QWidget *parent = nullptr);
|
||||||
void keyPressEvent(QKeyEvent *event) override;
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
|
|
||||||
void renameSelectedFile(BitTorrent::TorrentHandle *torrent);
|
void renameSelectedFile(BitTorrent::AbstractFileStorage &fileStorage);
|
||||||
void renameSelectedFile(BitTorrent::TorrentInfo &torrent);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QModelIndex currentNameCell();
|
QModelIndex currentNameCell();
|
||||||
|
@ -1251,44 +1251,44 @@ void TorrentsController::tagsAction()
|
|||||||
|
|
||||||
void TorrentsController::renameFileAction()
|
void TorrentsController::renameFileAction()
|
||||||
{
|
{
|
||||||
requireParams({"hash", "id", "name"});
|
requireParams({"hash", "oldPath", "newPath"});
|
||||||
|
|
||||||
const QString hash = params()["hash"];
|
const QString hash = params()["hash"];
|
||||||
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
|
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
|
||||||
if (!torrent)
|
if (!torrent)
|
||||||
throw APIError(APIErrorType::NotFound);
|
throw APIError(APIErrorType::NotFound);
|
||||||
|
|
||||||
QString newName = params()["name"].trimmed();
|
const QString oldPath = params()["oldPath"];
|
||||||
if (newName.isEmpty())
|
const QString newPath = params()["newPath"];
|
||||||
throw APIError(APIErrorType::BadParams, tr("Name cannot be empty"));
|
|
||||||
if (!Utils::Fs::isValidFileSystemName(newName))
|
|
||||||
throw APIError(APIErrorType::Conflict, tr("Name is not valid"));
|
|
||||||
if (newName.endsWith(QB_EXT))
|
|
||||||
newName.chop(QB_EXT.size());
|
|
||||||
|
|
||||||
bool ok = false;
|
try
|
||||||
const int fileIndex = params()["id"].toInt(&ok);
|
|
||||||
if (!ok || (fileIndex < 0) || (fileIndex >= torrent->filesCount()))
|
|
||||||
throw APIError(APIErrorType::Conflict, tr("ID is not valid"));
|
|
||||||
|
|
||||||
const QString oldFileName = torrent->fileName(fileIndex);
|
|
||||||
const QString oldFilePath = torrent->filePath(fileIndex);
|
|
||||||
|
|
||||||
const bool useFilenameExt = BitTorrent::Session::instance()->isAppendExtensionEnabled()
|
|
||||||
&& (torrent->filesProgress()[fileIndex] != 1);
|
|
||||||
const QString newFileName = (newName + (useFilenameExt ? QB_EXT : QString()));
|
|
||||||
const QString newFilePath = (oldFilePath.leftRef(oldFilePath.size() - oldFileName.size()) + newFileName);
|
|
||||||
|
|
||||||
if (oldFileName == newFileName)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// check if new name is already used
|
|
||||||
for (int i = 0; i < torrent->filesCount(); ++i)
|
|
||||||
{
|
{
|
||||||
if (i == fileIndex) continue;
|
torrent->renameFile(oldPath, newPath);
|
||||||
if (Utils::Fs::sameFileNames(torrent->filePath(i), newFilePath))
|
}
|
||||||
throw APIError(APIErrorType::Conflict, tr("Name is already in use"));
|
catch (const RuntimeError &error)
|
||||||
|
{
|
||||||
|
throw APIError(APIErrorType::Conflict, error.message());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TorrentsController::renameFolderAction()
|
||||||
|
{
|
||||||
|
requireParams({"hash", "oldPath", "newPath"});
|
||||||
|
|
||||||
|
const QString hash = params()["hash"];
|
||||||
|
BitTorrent::TorrentHandle *const torrent = BitTorrent::Session::instance()->findTorrent(hash);
|
||||||
|
if (!torrent)
|
||||||
|
throw APIError(APIErrorType::NotFound);
|
||||||
|
|
||||||
|
const QString oldPath = params()["oldPath"];
|
||||||
|
const QString newPath = params()["newPath"];
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
torrent->renameFolder(oldPath, newPath);
|
||||||
|
}
|
||||||
|
catch (const RuntimeError &error)
|
||||||
|
{
|
||||||
|
throw APIError(APIErrorType::Conflict, error.message());
|
||||||
}
|
}
|
||||||
|
|
||||||
torrent->renameFile(fileIndex, newFilePath);
|
|
||||||
}
|
}
|
||||||
|
@ -84,4 +84,5 @@ private slots:
|
|||||||
void toggleSequentialDownloadAction();
|
void toggleSequentialDownloadAction();
|
||||||
void toggleFirstLastPiecePrioAction();
|
void toggleFirstLastPiecePrioAction();
|
||||||
void renameFileAction();
|
void renameFileAction();
|
||||||
|
void renameFolderAction();
|
||||||
};
|
};
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
<script src="scripts/lib/mootools-1.2-core-yc.js"></script>
|
<script src="scripts/lib/mootools-1.2-core-yc.js"></script>
|
||||||
<script src="scripts/lib/mootools-1.2-more.js"></script>
|
<script src="scripts/lib/mootools-1.2-more.js"></script>
|
||||||
<script src="scripts/misc.js?locale=${LANG}&v=${CACHEID}"></script>
|
<script src="scripts/misc.js?locale=${LANG}&v=${CACHEID}"></script>
|
||||||
|
<script src="scripts/filesystem.js?v=${CACHEID}"></script>
|
||||||
<script>
|
<script>
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
@ -31,14 +32,15 @@
|
|||||||
|
|
||||||
window.addEvent('domready', function() {
|
window.addEvent('domready', function() {
|
||||||
const hash = new URI().getData('hash');
|
const hash = new URI().getData('hash');
|
||||||
const name = new URI().getData('name');
|
const path = new URI().getData('path');
|
||||||
const id = new URI().getData('id');
|
const isFolder = ((new URI().getData('isFolder')) === 'true');
|
||||||
if (!hash || !name || !id) return;
|
|
||||||
|
|
||||||
const decodedName = decodeURIComponent(name);
|
const oldPath = decodeURIComponent(path);
|
||||||
$('rename').value = decodedName;
|
const oldName = window.qBittorrent.Filesystem.fileName(oldPath);
|
||||||
|
$('rename').value = oldName;
|
||||||
$('rename').focus();
|
$('rename').focus();
|
||||||
$('rename').setSelectionRange(0, decodedName.lastIndexOf('.'));
|
if (!isFolder)
|
||||||
|
$('rename').setSelectionRange(0, oldName.lastIndexOf('.'));
|
||||||
|
|
||||||
$('renameButton').addEvent('click', function(e) {
|
$('renameButton').addEvent('click', function(e) {
|
||||||
new Event(e).stop();
|
new Event(e).stop();
|
||||||
@ -49,20 +51,24 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newName === name) {
|
if (newName === oldName) {
|
||||||
alert('QBT_TR(Name is unchanged)QBT_TR[CONTEXT=HttpServer]');
|
alert('QBT_TR(Name is unchanged)QBT_TR[CONTEXT=HttpServer]');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$('renameButton').disabled = true;
|
$('renameButton').disabled = true;
|
||||||
|
|
||||||
|
const parentPath = window.qBittorrent.Filesystem.folderName(oldPath)
|
||||||
|
const newPath = parentPath
|
||||||
|
? parentPath + window.qBittorrent.Filesystem.PathSeparator + newName
|
||||||
|
: newName;
|
||||||
new Request({
|
new Request({
|
||||||
url: 'api/v2/torrents/renameFile',
|
url: isFolder ? 'api/v2/torrents/renameFolder' : 'api/v2/torrents/renameFile',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: {
|
data: {
|
||||||
hash: hash,
|
hash: hash,
|
||||||
id: id,
|
oldPath: oldPath,
|
||||||
name: newName
|
newPath: newPath
|
||||||
},
|
},
|
||||||
onSuccess: function() {
|
onSuccess: function() {
|
||||||
window.parent.closeWindows();
|
window.parent.closeWindows();
|
||||||
|
@ -117,6 +117,7 @@ window.qBittorrent.FileTree = (function() {
|
|||||||
|
|
||||||
const FileNode = new Class({
|
const FileNode = new Class({
|
||||||
name: "",
|
name: "",
|
||||||
|
path: "",
|
||||||
rowId: null,
|
rowId: null,
|
||||||
size: 0,
|
size: 0,
|
||||||
checked: TriState.Unchecked,
|
checked: TriState.Unchecked,
|
||||||
|
@ -70,7 +70,7 @@ window.qBittorrent.Filesystem = (function() {
|
|||||||
const folderName = function(filepath) {
|
const folderName = function(filepath) {
|
||||||
const slashIndex = filepath.lastIndexOf(PathSeparator);
|
const slashIndex = filepath.lastIndexOf(PathSeparator);
|
||||||
if (slashIndex === -1)
|
if (slashIndex === -1)
|
||||||
return filepath;
|
return '';
|
||||||
return filepath.substring(0, slashIndex);
|
return filepath.substring(0, slashIndex);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -423,9 +423,9 @@ window.qBittorrent.PropFiles = (function() {
|
|||||||
|
|
||||||
rows.forEach(function(row) {
|
rows.forEach(function(row) {
|
||||||
let parent = rootNode;
|
let parent = rootNode;
|
||||||
const pathFolders = row.fileName.split(window.qBittorrent.Filesystem.PathSeparator);
|
let folderPath = window.qBittorrent.Filesystem.folderName(row.fileName);
|
||||||
pathFolders.pop();
|
while (folderPath) {
|
||||||
pathFolders.forEach(function(folderName) {
|
const folderName = window.qBittorrent.Filesystem.fileName(folderPath);
|
||||||
if (folderName === '.unwanted')
|
if (folderName === '.unwanted')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -439,8 +439,10 @@ window.qBittorrent.PropFiles = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parentNode === null) {
|
if (parentNode === null) {
|
||||||
parentNode = new window.qBittorrent.FileTree.FolderNode();
|
parentNode = new window.qBittorrent.FileTree.FolderNode();
|
||||||
|
parentNode.path = folderPath;
|
||||||
parentNode.name = folderName;
|
parentNode.name = folderName;
|
||||||
parentNode.rowId = rowId;
|
parentNode.rowId = rowId;
|
||||||
parentNode.root = parent;
|
parentNode.root = parent;
|
||||||
@ -450,12 +452,14 @@ window.qBittorrent.PropFiles = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parent = parentNode;
|
parent = parentNode;
|
||||||
});
|
folderPath = window.qBittorrent.Filesystem.folderName(folderPath);
|
||||||
|
}
|
||||||
|
|
||||||
const isChecked = row.checked ? TriState.Checked : TriState.Unchecked;
|
const isChecked = row.checked ? TriState.Checked : TriState.Unchecked;
|
||||||
const remaining = (row.priority === FilePriority.Ignored) ? 0 : row.remaining;
|
const remaining = (row.priority === FilePriority.Ignored) ? 0 : row.remaining;
|
||||||
const childNode = new window.qBittorrent.FileTree.FileNode();
|
const childNode = new window.qBittorrent.FileTree.FileNode();
|
||||||
childNode.name = row.name;
|
childNode.name = row.name;
|
||||||
|
childNode.path = row.fileName;
|
||||||
childNode.rowId = rowId;
|
childNode.rowId = rowId;
|
||||||
childNode.size = row.size;
|
childNode.size = row.size;
|
||||||
childNode.checked = isChecked;
|
childNode.checked = isChecked;
|
||||||
@ -527,17 +531,16 @@ window.qBittorrent.PropFiles = (function() {
|
|||||||
if (rowId === undefined) return;
|
if (rowId === undefined) return;
|
||||||
const row = torrentFilesTable.rows[rowId];
|
const row = torrentFilesTable.rows[rowId];
|
||||||
if (!row) return;
|
if (!row) return;
|
||||||
const node = torrentFilesTable.getNode(rowId);
|
|
||||||
if (node.isFolder) return;
|
|
||||||
|
|
||||||
const name = row.full_data.name;
|
const node = torrentFilesTable.getNode(rowId);
|
||||||
const fileId = row.full_data.fileId;
|
const path = node.path;
|
||||||
|
|
||||||
new MochaUI.Window({
|
new MochaUI.Window({
|
||||||
id: 'renamePage',
|
id: 'renamePage',
|
||||||
title: "QBT_TR(Renaming)QBT_TR[CONTEXT=TorrentContentTreeView]",
|
title: "QBT_TR(Renaming)QBT_TR[CONTEXT=TorrentContentTreeView]",
|
||||||
loadMethod: 'iframe',
|
loadMethod: 'iframe',
|
||||||
contentURL: 'rename_file.html?hash=' + hash + '&id=' + fileId + '&name=' + encodeURIComponent(name),
|
contentURL: 'rename_file.html?hash=' + hash + '&isFolder=' + node.isFolder
|
||||||
|
+ '&path=' + encodeURIComponent(path),
|
||||||
scrollbars: false,
|
scrollbars: false,
|
||||||
resizable: false,
|
resizable: false,
|
||||||
maximizable: false,
|
maximizable: false,
|
||||||
@ -570,13 +573,6 @@ window.qBittorrent.PropFiles = (function() {
|
|||||||
this.hideItem('FilePrio');
|
this.hideItem('FilePrio');
|
||||||
else
|
else
|
||||||
this.showItem('FilePrio');
|
this.showItem('FilePrio');
|
||||||
|
|
||||||
const rowId = torrentFilesTable.selectedRowsIds()[0];
|
|
||||||
const node = torrentFilesTable.getNode(rowId);
|
|
||||||
if (node.isFolder)
|
|
||||||
this.hideItem('Rename');
|
|
||||||
else
|
|
||||||
this.showItem('Rename');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user