mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-11-22 09:16:05 +03:00
Allow to move content files to Trash instead of deleting them
PR #20252.
This commit is contained in:
parent
c5fa05299b
commit
4e27e88f6a
24 changed files with 383 additions and 116 deletions
|
@ -38,6 +38,8 @@ add_library(qbt_base STATIC
|
||||||
bittorrent/torrent.h
|
bittorrent/torrent.h
|
||||||
bittorrent/torrentcontenthandler.h
|
bittorrent/torrentcontenthandler.h
|
||||||
bittorrent/torrentcontentlayout.h
|
bittorrent/torrentcontentlayout.h
|
||||||
|
bittorrent/torrentcontentremoveoption.h
|
||||||
|
bittorrent/torrentcontentremover.h
|
||||||
bittorrent/torrentcreationmanager.h
|
bittorrent/torrentcreationmanager.h
|
||||||
bittorrent/torrentcreationtask.h
|
bittorrent/torrentcreationtask.h
|
||||||
bittorrent/torrentcreator.h
|
bittorrent/torrentcreator.h
|
||||||
|
@ -145,6 +147,7 @@ add_library(qbt_base STATIC
|
||||||
bittorrent/sslparameters.cpp
|
bittorrent/sslparameters.cpp
|
||||||
bittorrent/torrent.cpp
|
bittorrent/torrent.cpp
|
||||||
bittorrent/torrentcontenthandler.cpp
|
bittorrent/torrentcontenthandler.cpp
|
||||||
|
bittorrent/torrentcontentremover.cpp
|
||||||
bittorrent/torrentcreationmanager.cpp
|
bittorrent/torrentcreationmanager.cpp
|
||||||
bittorrent/torrentcreationtask.cpp
|
bittorrent/torrentcreationtask.cpp
|
||||||
bittorrent/torrentcreator.cpp
|
bittorrent/torrentcreator.cpp
|
||||||
|
|
|
@ -37,17 +37,12 @@
|
||||||
#include "addtorrentparams.h"
|
#include "addtorrentparams.h"
|
||||||
#include "categoryoptions.h"
|
#include "categoryoptions.h"
|
||||||
#include "sharelimitaction.h"
|
#include "sharelimitaction.h"
|
||||||
|
#include "torrentcontentremoveoption.h"
|
||||||
#include "trackerentry.h"
|
#include "trackerentry.h"
|
||||||
#include "trackerentrystatus.h"
|
#include "trackerentrystatus.h"
|
||||||
|
|
||||||
class QString;
|
class QString;
|
||||||
|
|
||||||
enum DeleteOption
|
|
||||||
{
|
|
||||||
DeleteTorrent,
|
|
||||||
DeleteTorrentAndFiles
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace BitTorrent
|
namespace BitTorrent
|
||||||
{
|
{
|
||||||
class InfoHash;
|
class InfoHash;
|
||||||
|
@ -58,6 +53,12 @@ namespace BitTorrent
|
||||||
struct CacheStatus;
|
struct CacheStatus;
|
||||||
struct SessionStatus;
|
struct SessionStatus;
|
||||||
|
|
||||||
|
enum class TorrentRemoveOption
|
||||||
|
{
|
||||||
|
KeepContent,
|
||||||
|
RemoveContent
|
||||||
|
};
|
||||||
|
|
||||||
// Using `Q_ENUM_NS()` without a wrapper namespace in our case is not advised
|
// 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.
|
// 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
|
// https://www.kdab.com/new-qt-5-8-meta-object-support-namespaces/#comment-143779
|
||||||
|
@ -434,6 +435,8 @@ namespace BitTorrent
|
||||||
virtual void setMergeTrackersEnabled(bool enabled) = 0;
|
virtual void setMergeTrackersEnabled(bool enabled) = 0;
|
||||||
virtual bool isStartPaused() const = 0;
|
virtual bool isStartPaused() const = 0;
|
||||||
virtual void setStartPaused(bool value) = 0;
|
virtual void setStartPaused(bool value) = 0;
|
||||||
|
virtual TorrentContentRemoveOption torrentContentRemoveOption() const = 0;
|
||||||
|
virtual void setTorrentContentRemoveOption(TorrentContentRemoveOption option) = 0;
|
||||||
|
|
||||||
virtual bool isRestored() const = 0;
|
virtual bool isRestored() const = 0;
|
||||||
|
|
||||||
|
@ -453,7 +456,7 @@ namespace BitTorrent
|
||||||
|
|
||||||
virtual bool isKnownTorrent(const InfoHash &infoHash) const = 0;
|
virtual bool isKnownTorrent(const InfoHash &infoHash) const = 0;
|
||||||
virtual bool addTorrent(const TorrentDescriptor &torrentDescr, const AddTorrentParams ¶ms = {}) = 0;
|
virtual bool addTorrent(const TorrentDescriptor &torrentDescr, const AddTorrentParams ¶ms = {}) = 0;
|
||||||
virtual bool deleteTorrent(const TorrentID &id, DeleteOption deleteOption = DeleteOption::DeleteTorrent) = 0;
|
virtual bool removeTorrent(const TorrentID &id, TorrentRemoveOption deleteOption = TorrentRemoveOption::KeepContent) = 0;
|
||||||
virtual bool downloadMetadata(const TorrentDescriptor &torrentDescr) = 0;
|
virtual bool downloadMetadata(const TorrentDescriptor &torrentDescr) = 0;
|
||||||
virtual bool cancelDownloadMetadata(const TorrentID &id) = 0;
|
virtual bool cancelDownloadMetadata(const TorrentID &id) = 0;
|
||||||
|
|
||||||
|
|
|
@ -101,6 +101,7 @@
|
||||||
#include "nativesessionextension.h"
|
#include "nativesessionextension.h"
|
||||||
#include "portforwarderimpl.h"
|
#include "portforwarderimpl.h"
|
||||||
#include "resumedatastorage.h"
|
#include "resumedatastorage.h"
|
||||||
|
#include "torrentcontentremover.h"
|
||||||
#include "torrentdescriptor.h"
|
#include "torrentdescriptor.h"
|
||||||
#include "torrentimpl.h"
|
#include "torrentimpl.h"
|
||||||
#include "tracker.h"
|
#include "tracker.h"
|
||||||
|
@ -525,6 +526,7 @@ SessionImpl::SessionImpl(QObject *parent)
|
||||||
, m_I2POutboundQuantity {BITTORRENT_SESSION_KEY(u"I2P/OutboundQuantity"_s), 3}
|
, m_I2POutboundQuantity {BITTORRENT_SESSION_KEY(u"I2P/OutboundQuantity"_s), 3}
|
||||||
, m_I2PInboundLength {BITTORRENT_SESSION_KEY(u"I2P/InboundLength"_s), 3}
|
, m_I2PInboundLength {BITTORRENT_SESSION_KEY(u"I2P/InboundLength"_s), 3}
|
||||||
, m_I2POutboundLength {BITTORRENT_SESSION_KEY(u"I2P/OutboundLength"_s), 3}
|
, m_I2POutboundLength {BITTORRENT_SESSION_KEY(u"I2P/OutboundLength"_s), 3}
|
||||||
|
, m_torrentContentRemoveOption {BITTORRENT_SESSION_KEY(u"TorrentContentRemoveOption"_s), TorrentContentRemoveOption::MoveToTrash}
|
||||||
, m_startPaused {BITTORRENT_SESSION_KEY(u"StartPaused"_s)}
|
, m_startPaused {BITTORRENT_SESSION_KEY(u"StartPaused"_s)}
|
||||||
, m_seedingLimitTimer {new QTimer(this)}
|
, m_seedingLimitTimer {new QTimer(this)}
|
||||||
, m_resumeDataTimer {new QTimer(this)}
|
, m_resumeDataTimer {new QTimer(this)}
|
||||||
|
@ -593,6 +595,11 @@ SessionImpl::SessionImpl(QObject *parent)
|
||||||
connect(m_ioThread.get(), &QThread::finished, m_fileSearcher, &QObject::deleteLater);
|
connect(m_ioThread.get(), &QThread::finished, m_fileSearcher, &QObject::deleteLater);
|
||||||
connect(m_fileSearcher, &FileSearcher::searchFinished, this, &SessionImpl::fileSearchFinished);
|
connect(m_fileSearcher, &FileSearcher::searchFinished, this, &SessionImpl::fileSearchFinished);
|
||||||
|
|
||||||
|
m_torrentContentRemover = new TorrentContentRemover;
|
||||||
|
m_torrentContentRemover->moveToThread(m_ioThread.get());
|
||||||
|
connect(m_ioThread.get(), &QThread::finished, m_torrentContentRemover, &QObject::deleteLater);
|
||||||
|
connect(m_torrentContentRemover, &TorrentContentRemover::jobFinished, this, &SessionImpl::torrentContentRemovingFinished);
|
||||||
|
|
||||||
m_ioThread->start();
|
m_ioThread->start();
|
||||||
|
|
||||||
initMetrics();
|
initMetrics();
|
||||||
|
@ -2287,12 +2294,12 @@ void SessionImpl::processTorrentShareLimits(TorrentImpl *torrent)
|
||||||
if (shareLimitAction == ShareLimitAction::Remove)
|
if (shareLimitAction == ShareLimitAction::Remove)
|
||||||
{
|
{
|
||||||
LogMsg(u"%1 %2 %3"_s.arg(description, tr("Removing torrent."), torrentName));
|
LogMsg(u"%1 %2 %3"_s.arg(description, tr("Removing torrent."), torrentName));
|
||||||
deleteTorrent(torrent->id());
|
removeTorrent(torrent->id(), TorrentRemoveOption::KeepContent);
|
||||||
}
|
}
|
||||||
else if (shareLimitAction == ShareLimitAction::RemoveWithContent)
|
else if (shareLimitAction == ShareLimitAction::RemoveWithContent)
|
||||||
{
|
{
|
||||||
LogMsg(u"%1 %2 %3"_s.arg(description, tr("Removing torrent and deleting its content."), torrentName));
|
LogMsg(u"%1 %2 %3"_s.arg(description, tr("Removing torrent and deleting its content."), torrentName));
|
||||||
deleteTorrent(torrent->id(), DeleteTorrentAndFiles);
|
removeTorrent(torrent->id(), TorrentRemoveOption::RemoveContent);
|
||||||
}
|
}
|
||||||
else if ((shareLimitAction == ShareLimitAction::Stop) && !torrent->isStopped())
|
else if ((shareLimitAction == ShareLimitAction::Stop) && !torrent->isStopped())
|
||||||
{
|
{
|
||||||
|
@ -2332,6 +2339,19 @@ void SessionImpl::fileSearchFinished(const TorrentID &id, const Path &savePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SessionImpl::torrentContentRemovingFinished(const QString &torrentName, const QString &errorMessage)
|
||||||
|
{
|
||||||
|
if (errorMessage.isEmpty())
|
||||||
|
{
|
||||||
|
LogMsg(tr("Torrent content removed. Torrent: \"%1\"").arg(torrentName));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LogMsg(tr("Failed to remove torrent content. Torrent: \"%1\". Error: \"%2\"")
|
||||||
|
.arg(torrentName, errorMessage), Log::WARNING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Torrent *SessionImpl::getTorrent(const TorrentID &id) const
|
Torrent *SessionImpl::getTorrent(const TorrentID &id) const
|
||||||
{
|
{
|
||||||
return m_torrents.value(id);
|
return m_torrents.value(id);
|
||||||
|
@ -2378,22 +2398,25 @@ void SessionImpl::banIP(const QString &ip)
|
||||||
|
|
||||||
// Delete a torrent from the session, given its hash
|
// Delete a torrent from the session, given its hash
|
||||||
// and from the disk, if the corresponding deleteOption is chosen
|
// and from the disk, if the corresponding deleteOption is chosen
|
||||||
bool SessionImpl::deleteTorrent(const TorrentID &id, const DeleteOption deleteOption)
|
bool SessionImpl::removeTorrent(const TorrentID &id, const TorrentRemoveOption deleteOption)
|
||||||
{
|
{
|
||||||
TorrentImpl *const torrent = m_torrents.take(id);
|
TorrentImpl *const torrent = m_torrents.take(id);
|
||||||
if (!torrent)
|
if (!torrent)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
qDebug("Deleting torrent with ID: %s", qUtf8Printable(torrent->id().toString()));
|
const TorrentID torrentID = torrent->id();
|
||||||
|
const QString torrentName = torrent->name();
|
||||||
|
|
||||||
|
qDebug("Deleting torrent with ID: %s", qUtf8Printable(torrentID.toString()));
|
||||||
emit torrentAboutToBeRemoved(torrent);
|
emit torrentAboutToBeRemoved(torrent);
|
||||||
|
|
||||||
if (const InfoHash infoHash = torrent->infoHash(); infoHash.isHybrid())
|
if (const InfoHash infoHash = torrent->infoHash(); infoHash.isHybrid())
|
||||||
m_hybridTorrentsByAltID.remove(TorrentID::fromSHA1Hash(infoHash.v1()));
|
m_hybridTorrentsByAltID.remove(TorrentID::fromSHA1Hash(infoHash.v1()));
|
||||||
|
|
||||||
// Remove it from session
|
// Remove it from session
|
||||||
if (deleteOption == DeleteTorrent)
|
if (deleteOption == TorrentRemoveOption::KeepContent)
|
||||||
{
|
{
|
||||||
m_removingTorrents[torrent->id()] = {torrent->name(), {}, deleteOption};
|
m_removingTorrents[torrentID] = {torrentName, torrent->actualStorageLocation(), {}, deleteOption};
|
||||||
|
|
||||||
const lt::torrent_handle nativeHandle {torrent->nativeHandle()};
|
const lt::torrent_handle nativeHandle {torrent->nativeHandle()};
|
||||||
const auto iter = std::find_if(m_moveStorageQueue.begin(), m_moveStorageQueue.end()
|
const auto iter = std::find_if(m_moveStorageQueue.begin(), m_moveStorageQueue.end()
|
||||||
|
@ -2415,7 +2438,7 @@ bool SessionImpl::deleteTorrent(const TorrentID &id, const DeleteOption deleteOp
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_removingTorrents[torrent->id()] = {torrent->name(), torrent->rootPath(), deleteOption};
|
m_removingTorrents[torrentID] = {torrentName, torrent->actualStorageLocation(), torrent->actualFilePaths(), deleteOption};
|
||||||
|
|
||||||
if (m_moveStorageQueue.size() > 1)
|
if (m_moveStorageQueue.size() > 1)
|
||||||
{
|
{
|
||||||
|
@ -2430,12 +2453,13 @@ bool SessionImpl::deleteTorrent(const TorrentID &id, const DeleteOption deleteOp
|
||||||
m_moveStorageQueue.erase(iter);
|
m_moveStorageQueue.erase(iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_nativeSession->remove_torrent(torrent->nativeHandle(), lt::session::delete_files);
|
m_nativeSession->remove_torrent(torrent->nativeHandle(), lt::session::delete_partfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove it from torrent resume directory
|
// Remove it from torrent resume directory
|
||||||
m_resumeDataStorage->remove(torrent->id());
|
m_resumeDataStorage->remove(torrentID);
|
||||||
|
|
||||||
|
LogMsg(tr("Torrent removed. Torrent: \"%1\"").arg(torrentName));
|
||||||
delete torrent;
|
delete torrent;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2463,7 +2487,7 @@ bool SessionImpl::cancelDownloadMetadata(const TorrentID &id)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
m_nativeSession->remove_torrent(nativeHandle, lt::session::delete_files);
|
m_nativeSession->remove_torrent(nativeHandle);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3974,6 +3998,16 @@ void SessionImpl::setStartPaused(const bool value)
|
||||||
m_startPaused = value;
|
m_startPaused = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TorrentContentRemoveOption SessionImpl::torrentContentRemoveOption() const
|
||||||
|
{
|
||||||
|
return m_torrentContentRemoveOption;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionImpl::setTorrentContentRemoveOption(const TorrentContentRemoveOption option)
|
||||||
|
{
|
||||||
|
m_torrentContentRemoveOption = option;
|
||||||
|
}
|
||||||
|
|
||||||
QStringList SessionImpl::bannedIPs() const
|
QStringList SessionImpl::bannedIPs() const
|
||||||
{
|
{
|
||||||
return m_bannedIPs;
|
return m_bannedIPs;
|
||||||
|
@ -5147,7 +5181,7 @@ void SessionImpl::handleMoveTorrentStorageJobFinished(const Path &newPath)
|
||||||
// Last job is completed for torrent that being removing, so actually remove it
|
// Last job is completed for torrent that being removing, so actually remove it
|
||||||
const lt::torrent_handle nativeHandle {finishedJob.torrentHandle};
|
const lt::torrent_handle nativeHandle {finishedJob.torrentHandle};
|
||||||
const RemovingTorrentData &removingTorrentData = m_removingTorrents[nativeHandle.info_hash()];
|
const RemovingTorrentData &removingTorrentData = m_removingTorrents[nativeHandle.info_hash()];
|
||||||
if (removingTorrentData.deleteOption == DeleteTorrent)
|
if (removingTorrentData.removeOption == TorrentRemoveOption::KeepContent)
|
||||||
m_nativeSession->remove_torrent(nativeHandle, lt::session::delete_partfile);
|
m_nativeSession->remove_torrent(nativeHandle, lt::session::delete_partfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5666,74 +5700,32 @@ TorrentImpl *SessionImpl::createTorrent(const lt::torrent_handle &nativeHandle,
|
||||||
return torrent;
|
return torrent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::handleTorrentRemovedAlert(const lt::torrent_removed_alert *alert)
|
void SessionImpl::handleTorrentRemovedAlert(const lt::torrent_removed_alert */*alert*/)
|
||||||
{
|
{
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
// We cannot consider `torrent_removed_alert` as a starting point for removing content,
|
||||||
const auto id = TorrentID::fromInfoHash(alert->info_hashes);
|
// because it has an inconsistent posting time between different versions of libtorrent,
|
||||||
#else
|
// so files may still be in use in some cases.
|
||||||
const auto id = TorrentID::fromInfoHash(alert->info_hash);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const auto removingTorrentDataIter = m_removingTorrents.find(id);
|
|
||||||
if (removingTorrentDataIter != m_removingTorrents.end())
|
|
||||||
{
|
|
||||||
if (removingTorrentDataIter->deleteOption == DeleteTorrent)
|
|
||||||
{
|
|
||||||
LogMsg(tr("Removed torrent. Torrent: \"%1\"").arg(removingTorrentDataIter->name));
|
|
||||||
m_removingTorrents.erase(removingTorrentDataIter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::handleTorrentDeletedAlert(const lt::torrent_deleted_alert *alert)
|
void SessionImpl::handleTorrentDeletedAlert(const lt::torrent_deleted_alert *alert)
|
||||||
{
|
{
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
const auto id = TorrentID::fromInfoHash(alert->info_hashes);
|
const auto torrentID = TorrentID::fromInfoHash(alert->info_hashes);
|
||||||
#else
|
#else
|
||||||
const auto id = TorrentID::fromInfoHash(alert->info_hash);
|
const auto torrentID = TorrentID::fromInfoHash(alert->info_hash);
|
||||||
#endif
|
#endif
|
||||||
|
handleRemovedTorrent(torrentID);
|
||||||
const auto removingTorrentDataIter = m_removingTorrents.find(id);
|
|
||||||
if (removingTorrentDataIter == m_removingTorrents.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// torrent_deleted_alert can also be posted due to deletion of partfile. Ignore it in such a case.
|
|
||||||
if (removingTorrentDataIter->deleteOption == DeleteTorrent)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Utils::Fs::smartRemoveEmptyFolderTree(removingTorrentDataIter->pathToRemove);
|
|
||||||
LogMsg(tr("Removed torrent and deleted its content. Torrent: \"%1\"").arg(removingTorrentDataIter->name));
|
|
||||||
m_removingTorrents.erase(removingTorrentDataIter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::handleTorrentDeleteFailedAlert(const lt::torrent_delete_failed_alert *alert)
|
void SessionImpl::handleTorrentDeleteFailedAlert(const lt::torrent_delete_failed_alert *alert)
|
||||||
{
|
{
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
const auto id = TorrentID::fromInfoHash(alert->info_hashes);
|
const auto torrentID = TorrentID::fromInfoHash(alert->info_hashes);
|
||||||
#else
|
#else
|
||||||
const auto id = TorrentID::fromInfoHash(alert->info_hash);
|
const auto torrentID = TorrentID::fromInfoHash(alert->info_hash);
|
||||||
#endif
|
#endif
|
||||||
|
const auto errorMessage = alert->error ? QString::fromLocal8Bit(alert->error.message().c_str()) : QString();
|
||||||
const auto removingTorrentDataIter = m_removingTorrents.find(id);
|
handleRemovedTorrent(torrentID, errorMessage);
|
||||||
if (removingTorrentDataIter == m_removingTorrents.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (alert->error)
|
|
||||||
{
|
|
||||||
// libtorrent won't delete the directory if it contains files not listed in the torrent,
|
|
||||||
// so we remove the directory ourselves
|
|
||||||
Utils::Fs::smartRemoveEmptyFolderTree(removingTorrentDataIter->pathToRemove);
|
|
||||||
|
|
||||||
LogMsg(tr("Removed torrent but failed to delete its content and/or partfile. Torrent: \"%1\". Error: \"%2\"")
|
|
||||||
.arg(removingTorrentDataIter->name, QString::fromLocal8Bit(alert->error.message().c_str()))
|
|
||||||
, Log::WARNING);
|
|
||||||
}
|
|
||||||
else // torrent without metadata, hence no files on disk
|
|
||||||
{
|
|
||||||
LogMsg(tr("Removed torrent. Torrent: \"%1\"").arg(removingTorrentDataIter->name));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_removingTorrents.erase(removingTorrentDataIter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::handleTorrentNeedCertAlert(const lt::torrent_need_cert_alert *alert)
|
void SessionImpl::handleTorrentNeedCertAlert(const lt::torrent_need_cert_alert *alert)
|
||||||
|
@ -6169,7 +6161,7 @@ void SessionImpl::handleTorrentConflictAlert(const lt::torrent_conflict_alert *a
|
||||||
if (torrent2)
|
if (torrent2)
|
||||||
{
|
{
|
||||||
if (torrent1)
|
if (torrent1)
|
||||||
deleteTorrent(torrentIDv1);
|
removeTorrent(torrentIDv1);
|
||||||
else
|
else
|
||||||
cancelDownloadMetadata(torrentIDv1);
|
cancelDownloadMetadata(torrentIDv1);
|
||||||
|
|
||||||
|
@ -6278,3 +6270,29 @@ void SessionImpl::updateTrackerEntryStatuses(lt::torrent_handle torrentHandle, Q
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SessionImpl::handleRemovedTorrent(const TorrentID &torrentID, const QString &partfileRemoveError)
|
||||||
|
{
|
||||||
|
const auto removingTorrentDataIter = m_removingTorrents.find(torrentID);
|
||||||
|
if (removingTorrentDataIter == m_removingTorrents.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!partfileRemoveError.isEmpty())
|
||||||
|
{
|
||||||
|
LogMsg(tr("Failed to remove partfile. Torrent: \"%1\". Reason: \"%2\".")
|
||||||
|
.arg(removingTorrentDataIter->name, partfileRemoveError)
|
||||||
|
, Log::WARNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((removingTorrentDataIter->removeOption == TorrentRemoveOption::RemoveContent)
|
||||||
|
&& !removingTorrentDataIter->contentStoragePath.isEmpty())
|
||||||
|
{
|
||||||
|
QMetaObject::invokeMethod(m_torrentContentRemover, [this, jobData = *removingTorrentDataIter]
|
||||||
|
{
|
||||||
|
m_torrentContentRemover->performJob(jobData.name, jobData.contentStoragePath
|
||||||
|
, jobData.fileNames, m_torrentContentRemoveOption);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
m_removingTorrents.erase(removingTorrentDataIter);
|
||||||
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ namespace BitTorrent
|
||||||
class InfoHash;
|
class InfoHash;
|
||||||
class ResumeDataStorage;
|
class ResumeDataStorage;
|
||||||
class Torrent;
|
class Torrent;
|
||||||
|
class TorrentContentRemover;
|
||||||
class TorrentDescriptor;
|
class TorrentDescriptor;
|
||||||
class TorrentImpl;
|
class TorrentImpl;
|
||||||
class Tracker;
|
class Tracker;
|
||||||
|
@ -411,6 +412,8 @@ namespace BitTorrent
|
||||||
void setMergeTrackersEnabled(bool enabled) override;
|
void setMergeTrackersEnabled(bool enabled) override;
|
||||||
bool isStartPaused() const override;
|
bool isStartPaused() const override;
|
||||||
void setStartPaused(bool value) override;
|
void setStartPaused(bool value) override;
|
||||||
|
TorrentContentRemoveOption torrentContentRemoveOption() const override;
|
||||||
|
void setTorrentContentRemoveOption(TorrentContentRemoveOption option) override;
|
||||||
|
|
||||||
bool isRestored() const override;
|
bool isRestored() const override;
|
||||||
|
|
||||||
|
@ -430,7 +433,7 @@ namespace BitTorrent
|
||||||
|
|
||||||
bool isKnownTorrent(const InfoHash &infoHash) const override;
|
bool isKnownTorrent(const InfoHash &infoHash) const override;
|
||||||
bool addTorrent(const TorrentDescriptor &torrentDescr, const AddTorrentParams ¶ms = {}) override;
|
bool addTorrent(const TorrentDescriptor &torrentDescr, const AddTorrentParams ¶ms = {}) override;
|
||||||
bool deleteTorrent(const TorrentID &id, DeleteOption deleteOption = DeleteTorrent) override;
|
bool removeTorrent(const TorrentID &id, TorrentRemoveOption deleteOption = TorrentRemoveOption::KeepContent) override;
|
||||||
bool downloadMetadata(const TorrentDescriptor &torrentDescr) override;
|
bool downloadMetadata(const TorrentDescriptor &torrentDescr) override;
|
||||||
bool cancelDownloadMetadata(const TorrentID &id) override;
|
bool cancelDownloadMetadata(const TorrentID &id) override;
|
||||||
|
|
||||||
|
@ -491,6 +494,7 @@ namespace BitTorrent
|
||||||
void handleIPFilterParsed(int ruleCount);
|
void handleIPFilterParsed(int ruleCount);
|
||||||
void handleIPFilterError();
|
void handleIPFilterError();
|
||||||
void fileSearchFinished(const TorrentID &id, const Path &savePath, const PathList &fileNames);
|
void fileSearchFinished(const TorrentID &id, const Path &savePath, const PathList &fileNames);
|
||||||
|
void torrentContentRemovingFinished(const QString &torrentName, const QString &errorMessage);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct ResumeSessionContext;
|
struct ResumeSessionContext;
|
||||||
|
@ -506,8 +510,9 @@ namespace BitTorrent
|
||||||
struct RemovingTorrentData
|
struct RemovingTorrentData
|
||||||
{
|
{
|
||||||
QString name;
|
QString name;
|
||||||
Path pathToRemove;
|
Path contentStoragePath;
|
||||||
DeleteOption deleteOption {};
|
PathList fileNames;
|
||||||
|
TorrentRemoveOption removeOption {};
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit SessionImpl(QObject *parent = nullptr);
|
explicit SessionImpl(QObject *parent = nullptr);
|
||||||
|
@ -599,6 +604,8 @@ namespace BitTorrent
|
||||||
|
|
||||||
void updateTrackerEntryStatuses(lt::torrent_handle torrentHandle, QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>> updatedTrackers);
|
void updateTrackerEntryStatuses(lt::torrent_handle torrentHandle, QHash<std::string, QHash<lt::tcp::endpoint, QMap<int, int>>> updatedTrackers);
|
||||||
|
|
||||||
|
void handleRemovedTorrent(const TorrentID &torrentID, const QString &partfileRemoveError = {});
|
||||||
|
|
||||||
CachedSettingValue<QString> m_DHTBootstrapNodes;
|
CachedSettingValue<QString> m_DHTBootstrapNodes;
|
||||||
CachedSettingValue<bool> m_isDHTEnabled;
|
CachedSettingValue<bool> m_isDHTEnabled;
|
||||||
CachedSettingValue<bool> m_isLSDEnabled;
|
CachedSettingValue<bool> m_isLSDEnabled;
|
||||||
|
@ -723,6 +730,7 @@ namespace BitTorrent
|
||||||
CachedSettingValue<int> m_I2POutboundQuantity;
|
CachedSettingValue<int> m_I2POutboundQuantity;
|
||||||
CachedSettingValue<int> m_I2PInboundLength;
|
CachedSettingValue<int> m_I2PInboundLength;
|
||||||
CachedSettingValue<int> m_I2POutboundLength;
|
CachedSettingValue<int> m_I2POutboundLength;
|
||||||
|
CachedSettingValue<TorrentContentRemoveOption> m_torrentContentRemoveOption;
|
||||||
SettingValue<bool> m_startPaused;
|
SettingValue<bool> m_startPaused;
|
||||||
|
|
||||||
lt::session *m_nativeSession = nullptr;
|
lt::session *m_nativeSession = nullptr;
|
||||||
|
@ -765,6 +773,7 @@ namespace BitTorrent
|
||||||
QThreadPool *m_asyncWorker = nullptr;
|
QThreadPool *m_asyncWorker = nullptr;
|
||||||
ResumeDataStorage *m_resumeDataStorage = nullptr;
|
ResumeDataStorage *m_resumeDataStorage = nullptr;
|
||||||
FileSearcher *m_fileSearcher = nullptr;
|
FileSearcher *m_fileSearcher = nullptr;
|
||||||
|
TorrentContentRemover *m_torrentContentRemover = nullptr;
|
||||||
|
|
||||||
QHash<TorrentID, lt::torrent_handle> m_downloadedMetadata;
|
QHash<TorrentID, lt::torrent_handle> m_downloadedMetadata;
|
||||||
|
|
||||||
|
|
|
@ -228,6 +228,7 @@ namespace BitTorrent
|
||||||
virtual void setShareLimitAction(ShareLimitAction action) = 0;
|
virtual void setShareLimitAction(ShareLimitAction action) = 0;
|
||||||
|
|
||||||
virtual PathList filePaths() const = 0;
|
virtual PathList filePaths() const = 0;
|
||||||
|
virtual PathList actualFilePaths() const = 0;
|
||||||
|
|
||||||
virtual TorrentInfo info() const = 0;
|
virtual TorrentInfo info() const = 0;
|
||||||
virtual bool isFinished() const = 0;
|
virtual bool isFinished() const = 0;
|
||||||
|
|
50
src/base/bittorrent/torrentcontentremoveoption.h
Normal file
50
src/base/bittorrent/torrentcontentremoveoption.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2024 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 <QMetaEnum>
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
// 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
|
||||||
|
inline namespace TorrentContentRemoveOptionNS
|
||||||
|
{
|
||||||
|
Q_NAMESPACE
|
||||||
|
|
||||||
|
enum class TorrentContentRemoveOption
|
||||||
|
{
|
||||||
|
Delete,
|
||||||
|
MoveToTrash
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_ENUM_NS(TorrentContentRemoveOption)
|
||||||
|
}
|
||||||
|
}
|
61
src/base/bittorrent/torrentcontentremover.cpp
Normal file
61
src/base/bittorrent/torrentcontentremover.cpp
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2024 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 "torrentcontentremover.h"
|
||||||
|
|
||||||
|
#include "base/utils/fs.h"
|
||||||
|
|
||||||
|
void BitTorrent::TorrentContentRemover::performJob(const QString &torrentName, const Path &basePath
|
||||||
|
, const PathList &fileNames, const TorrentContentRemoveOption option)
|
||||||
|
{
|
||||||
|
QString errorMessage;
|
||||||
|
|
||||||
|
if (!fileNames.isEmpty())
|
||||||
|
{
|
||||||
|
const auto removeFileFn = [&option](const Path &filePath)
|
||||||
|
{
|
||||||
|
return ((option == TorrentContentRemoveOption::MoveToTrash)
|
||||||
|
? Utils::Fs::moveFileToTrash : Utils::Fs::removeFile)(filePath);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const Path &fileName : fileNames)
|
||||||
|
{
|
||||||
|
if (const auto result = removeFileFn(basePath / fileName)
|
||||||
|
; !result && errorMessage.isEmpty())
|
||||||
|
{
|
||||||
|
errorMessage = result.error();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Path rootPath = Path::findRootFolder(fileNames);
|
||||||
|
if (!rootPath.isEmpty())
|
||||||
|
Utils::Fs::smartRemoveEmptyFolderTree(basePath / rootPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit jobFinished(torrentName, errorMessage);
|
||||||
|
}
|
53
src/base/bittorrent/torrentcontentremover.h
Normal file
53
src/base/bittorrent/torrentcontentremover.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2024 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 <QObject>
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
|
#include "torrentcontentremoveoption.h"
|
||||||
|
|
||||||
|
namespace BitTorrent
|
||||||
|
{
|
||||||
|
class TorrentContentRemover final : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY_MOVE(TorrentContentRemover)
|
||||||
|
|
||||||
|
public:
|
||||||
|
using QObject::QObject;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void performJob(const QString &torrentName, const Path &basePath
|
||||||
|
, const PathList &fileNames, TorrentContentRemoveOption option);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void jobFinished(const QString &torrentName, const QString &errorMessage);
|
||||||
|
};
|
||||||
|
}
|
|
@ -986,6 +986,21 @@ PathList TorrentImpl::filePaths() const
|
||||||
return m_filePaths;
|
return m_filePaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PathList TorrentImpl::actualFilePaths() const
|
||||||
|
{
|
||||||
|
if (!hasMetadata())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
PathList paths;
|
||||||
|
paths.reserve(filesCount());
|
||||||
|
|
||||||
|
const lt::file_storage files = nativeTorrentInfo()->files();
|
||||||
|
for (const lt::file_index_t &nativeIndex : asConst(m_torrentInfo.nativeIndexes()))
|
||||||
|
paths.emplaceBack(files.file_path(nativeIndex));
|
||||||
|
|
||||||
|
return paths;
|
||||||
|
}
|
||||||
|
|
||||||
QVector<DownloadPriority> TorrentImpl::filePriorities() const
|
QVector<DownloadPriority> TorrentImpl::filePriorities() const
|
||||||
{
|
{
|
||||||
return m_filePriorities;
|
return m_filePriorities;
|
||||||
|
|
|
@ -153,6 +153,7 @@ namespace BitTorrent
|
||||||
Path actualFilePath(int index) const override;
|
Path actualFilePath(int index) const override;
|
||||||
qlonglong fileSize(int index) const override;
|
qlonglong fileSize(int index) const override;
|
||||||
PathList filePaths() const override;
|
PathList filePaths() const override;
|
||||||
|
PathList actualFilePaths() const override;
|
||||||
QVector<DownloadPriority> filePriorities() const override;
|
QVector<DownloadPriority> filePriorities() const override;
|
||||||
|
|
||||||
TorrentInfo info() const override;
|
TorrentInfo info() const override;
|
||||||
|
|
|
@ -134,17 +134,17 @@ void Preferences::setCustomUIThemePath(const Path &path)
|
||||||
setValue(u"Preferences/General/CustomUIThemePath"_s, path);
|
setValue(u"Preferences/General/CustomUIThemePath"_s, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Preferences::deleteTorrentFilesAsDefault() const
|
bool Preferences::removeTorrentContent() const
|
||||||
{
|
{
|
||||||
return value(u"Preferences/General/DeleteTorrentsFilesAsDefault"_s, false);
|
return value(u"Preferences/General/DeleteTorrentsFilesAsDefault"_s, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Preferences::setDeleteTorrentFilesAsDefault(const bool del)
|
void Preferences::setRemoveTorrentContent(const bool remove)
|
||||||
{
|
{
|
||||||
if (del == deleteTorrentFilesAsDefault())
|
if (remove == removeTorrentContent())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
setValue(u"Preferences/General/DeleteTorrentsFilesAsDefault"_s, del);
|
setValue(u"Preferences/General/DeleteTorrentsFilesAsDefault"_s, remove);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Preferences::confirmOnExit() const
|
bool Preferences::confirmOnExit() const
|
||||||
|
|
|
@ -105,8 +105,8 @@ public:
|
||||||
void setUseCustomUITheme(bool use);
|
void setUseCustomUITheme(bool use);
|
||||||
Path customUIThemePath() const;
|
Path customUIThemePath() const;
|
||||||
void setCustomUIThemePath(const Path &path);
|
void setCustomUIThemePath(const Path &path);
|
||||||
bool deleteTorrentFilesAsDefault() const;
|
bool removeTorrentContent() const;
|
||||||
void setDeleteTorrentFilesAsDefault(bool del);
|
void setRemoveTorrentContent(bool remove);
|
||||||
bool confirmOnExit() const;
|
bool confirmOnExit() const;
|
||||||
void setConfirmOnExit(bool confirm);
|
void setConfirmOnExit(bool confirm);
|
||||||
bool speedInTitleBar() const;
|
bool speedInTitleBar() const;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2022-2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2012 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2012 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -29,8 +29,6 @@
|
||||||
|
|
||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
|
|
||||||
#include <cerrno>
|
|
||||||
#include <cstring>
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
|
@ -52,6 +50,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
@ -311,20 +310,42 @@ bool Utils::Fs::renameFile(const Path &from, const Path &to)
|
||||||
*
|
*
|
||||||
* This function will try to fix the file permissions before removing it.
|
* This function will try to fix the file permissions before removing it.
|
||||||
*/
|
*/
|
||||||
bool Utils::Fs::removeFile(const Path &path)
|
nonstd::expected<void, QString> Utils::Fs::removeFile(const Path &path)
|
||||||
{
|
{
|
||||||
if (QFile::remove(path.data()))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
QFile file {path.data()};
|
QFile file {path.data()};
|
||||||
|
if (file.remove())
|
||||||
|
return {};
|
||||||
|
|
||||||
if (!file.exists())
|
if (!file.exists())
|
||||||
return true;
|
return {};
|
||||||
|
|
||||||
// Make sure we have read/write permissions
|
// Make sure we have read/write permissions
|
||||||
file.setPermissions(file.permissions() | QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser);
|
file.setPermissions(file.permissions() | QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser);
|
||||||
return file.remove();
|
if (file.remove())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return nonstd::make_unexpected(file.errorString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nonstd::expected<void, QString> Utils::Fs::moveFileToTrash(const Path &path)
|
||||||
|
{
|
||||||
|
QFile file {path.data()};
|
||||||
|
if (file.moveToTrash())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (!file.exists())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// Make sure we have read/write permissions
|
||||||
|
file.setPermissions(file.permissions() | QFile::ReadOwner | QFile::WriteOwner | QFile::ReadUser | QFile::WriteUser);
|
||||||
|
if (file.moveToTrash())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const QString errorMessage = file.errorString();
|
||||||
|
return nonstd::make_unexpected(!errorMessage.isEmpty() ? errorMessage : QCoreApplication::translate("fs", "Unknown error"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool Utils::Fs::isReadable(const Path &path)
|
bool Utils::Fs::isReadable(const Path &path)
|
||||||
{
|
{
|
||||||
return QFileInfo(path.data()).isReadable();
|
return QFileInfo(path.data()).isReadable();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
* Copyright (C) 2022 Vladimir Golovnev <glassez@yandex.ru>
|
* Copyright (C) 2022-2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2012 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2012 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#include "base/3rdparty/expected.hpp"
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/pathfwd.h"
|
#include "base/pathfwd.h"
|
||||||
|
|
||||||
|
@ -60,7 +61,8 @@ namespace Utils::Fs
|
||||||
|
|
||||||
bool copyFile(const Path &from, const Path &to);
|
bool copyFile(const Path &from, const Path &to);
|
||||||
bool renameFile(const Path &from, const Path &to);
|
bool renameFile(const Path &from, const Path &to);
|
||||||
bool removeFile(const Path &path);
|
nonstd::expected<void, QString> removeFile(const Path &path);
|
||||||
|
nonstd::expected<void, QString> moveFileToTrash(const Path &path);
|
||||||
bool mkdir(const Path &dirPath);
|
bool mkdir(const Path &dirPath);
|
||||||
bool mkpath(const Path &dirPath);
|
bool mkpath(const Path &dirPath);
|
||||||
bool rmdir(const Path &dirPath);
|
bool rmdir(const Path &dirPath);
|
||||||
|
|
|
@ -63,6 +63,7 @@ namespace
|
||||||
// qBittorrent section
|
// qBittorrent section
|
||||||
QBITTORRENT_HEADER,
|
QBITTORRENT_HEADER,
|
||||||
RESUME_DATA_STORAGE,
|
RESUME_DATA_STORAGE,
|
||||||
|
TORRENT_CONTENT_REMOVE_OPTION,
|
||||||
#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_MACOS)
|
#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_MACOS)
|
||||||
MEMORY_WORKING_SET_LIMIT,
|
MEMORY_WORKING_SET_LIMIT,
|
||||||
#endif
|
#endif
|
||||||
|
@ -364,6 +365,8 @@ void AdvancedSettings::saveAdvancedSettings() const
|
||||||
session->setI2PInboundLength(m_spinBoxI2PInboundLength.value());
|
session->setI2PInboundLength(m_spinBoxI2PInboundLength.value());
|
||||||
session->setI2POutboundLength(m_spinBoxI2POutboundLength.value());
|
session->setI2POutboundLength(m_spinBoxI2POutboundLength.value());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
session->setTorrentContentRemoveOption(m_comboBoxTorrentContentRemoveOption.currentData().value<BitTorrent::TorrentContentRemoveOption>());
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef QBT_USES_LIBTORRENT2
|
#ifndef QBT_USES_LIBTORRENT2
|
||||||
|
@ -472,6 +475,11 @@ void AdvancedSettings::loadAdvancedSettings()
|
||||||
m_comboBoxResumeDataStorage.setCurrentIndex(m_comboBoxResumeDataStorage.findData(QVariant::fromValue(session->resumeDataStorageType())));
|
m_comboBoxResumeDataStorage.setCurrentIndex(m_comboBoxResumeDataStorage.findData(QVariant::fromValue(session->resumeDataStorageType())));
|
||||||
addRow(RESUME_DATA_STORAGE, tr("Resume data storage type (requires restart)"), &m_comboBoxResumeDataStorage);
|
addRow(RESUME_DATA_STORAGE, tr("Resume data storage type (requires restart)"), &m_comboBoxResumeDataStorage);
|
||||||
|
|
||||||
|
m_comboBoxTorrentContentRemoveOption.addItem(tr("Delete files permanently"), QVariant::fromValue(BitTorrent::TorrentContentRemoveOption::Delete));
|
||||||
|
m_comboBoxTorrentContentRemoveOption.addItem(tr("Move files to trash (if possible)"), QVariant::fromValue(BitTorrent::TorrentContentRemoveOption::MoveToTrash));
|
||||||
|
m_comboBoxTorrentContentRemoveOption.setCurrentIndex(m_comboBoxTorrentContentRemoveOption.findData(QVariant::fromValue(session->torrentContentRemoveOption())));
|
||||||
|
addRow(TORRENT_CONTENT_REMOVE_OPTION, tr("Torrent content removing mode"), &m_comboBoxTorrentContentRemoveOption);
|
||||||
|
|
||||||
#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_MACOS)
|
#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_MACOS)
|
||||||
// Physical memory (RAM) usage limit
|
// Physical memory (RAM) usage limit
|
||||||
m_spinBoxMemoryWorkingSetLimit.setMinimum(1);
|
m_spinBoxMemoryWorkingSetLimit.setMinimum(1);
|
||||||
|
|
|
@ -81,7 +81,7 @@ private:
|
||||||
m_checkBoxMultiConnectionsPerIp, m_checkBoxValidateHTTPSTrackerCertificate, m_checkBoxSSRFMitigation, m_checkBoxBlockPeersOnPrivilegedPorts, m_checkBoxPieceExtentAffinity,
|
m_checkBoxMultiConnectionsPerIp, m_checkBoxValidateHTTPSTrackerCertificate, m_checkBoxSSRFMitigation, m_checkBoxBlockPeersOnPrivilegedPorts, m_checkBoxPieceExtentAffinity,
|
||||||
m_checkBoxSuggestMode, m_checkBoxSpeedWidgetEnabled, m_checkBoxIDNSupport, m_checkBoxConfirmRemoveTrackerFromAllTorrents, m_checkBoxStartSessionPaused;
|
m_checkBoxSuggestMode, m_checkBoxSpeedWidgetEnabled, m_checkBoxIDNSupport, m_checkBoxConfirmRemoveTrackerFromAllTorrents, m_checkBoxStartSessionPaused;
|
||||||
QComboBox m_comboBoxInterface, m_comboBoxInterfaceAddress, m_comboBoxDiskIOReadMode, m_comboBoxDiskIOWriteMode, m_comboBoxUtpMixedMode, m_comboBoxChokingAlgorithm,
|
QComboBox m_comboBoxInterface, m_comboBoxInterfaceAddress, m_comboBoxDiskIOReadMode, m_comboBoxDiskIOWriteMode, m_comboBoxUtpMixedMode, m_comboBoxChokingAlgorithm,
|
||||||
m_comboBoxSeedChokingAlgorithm, m_comboBoxResumeDataStorage;
|
m_comboBoxSeedChokingAlgorithm, m_comboBoxResumeDataStorage, m_comboBoxTorrentContentRemoveOption;
|
||||||
QLineEdit m_lineEditAppInstanceName, m_pythonExecutablePath, m_lineEditAnnounceIP, m_lineEditDHTBootstrapNodes;
|
QLineEdit m_lineEditAppInstanceName, m_pythonExecutablePath, m_lineEditAnnounceIP, m_lineEditDHTBootstrapNodes;
|
||||||
|
|
||||||
#ifndef QBT_USES_LIBTORRENT2
|
#ifndef QBT_USES_LIBTORRENT2
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -30,6 +31,7 @@
|
||||||
|
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
|
|
||||||
|
#include "base/bittorrent/session.h"
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/preferences.h"
|
#include "base/preferences.h"
|
||||||
#include "uithememanager.h"
|
#include "uithememanager.h"
|
||||||
|
@ -53,8 +55,8 @@ DeletionConfirmationDialog::DeletionConfirmationDialog(QWidget *parent, const in
|
||||||
m_ui->rememberBtn->setIcon(UIThemeManager::instance()->getIcon(u"object-locked"_s));
|
m_ui->rememberBtn->setIcon(UIThemeManager::instance()->getIcon(u"object-locked"_s));
|
||||||
m_ui->rememberBtn->setIconSize(Utils::Gui::mediumIconSize());
|
m_ui->rememberBtn->setIconSize(Utils::Gui::mediumIconSize());
|
||||||
|
|
||||||
m_ui->checkPermDelete->setChecked(defaultDeleteFiles || Preferences::instance()->deleteTorrentFilesAsDefault());
|
m_ui->checkRemoveContent->setChecked(defaultDeleteFiles || Preferences::instance()->removeTorrentContent());
|
||||||
connect(m_ui->checkPermDelete, &QCheckBox::clicked, this, &DeletionConfirmationDialog::updateRememberButtonState);
|
connect(m_ui->checkRemoveContent, &QCheckBox::clicked, this, &DeletionConfirmationDialog::updateRememberButtonState);
|
||||||
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Remove"));
|
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Remove"));
|
||||||
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setFocus();
|
m_ui->buttonBox->button(QDialogButtonBox::Cancel)->setFocus();
|
||||||
|
|
||||||
|
@ -67,18 +69,18 @@ DeletionConfirmationDialog::~DeletionConfirmationDialog()
|
||||||
delete m_ui;
|
delete m_ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeletionConfirmationDialog::isDeleteFileSelected() const
|
bool DeletionConfirmationDialog::isRemoveContentSelected() const
|
||||||
{
|
{
|
||||||
return m_ui->checkPermDelete->isChecked();
|
return m_ui->checkRemoveContent->isChecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeletionConfirmationDialog::updateRememberButtonState()
|
void DeletionConfirmationDialog::updateRememberButtonState()
|
||||||
{
|
{
|
||||||
m_ui->rememberBtn->setEnabled(m_ui->checkPermDelete->isChecked() != Preferences::instance()->deleteTorrentFilesAsDefault());
|
m_ui->rememberBtn->setEnabled(m_ui->checkRemoveContent->isChecked() != Preferences::instance()->removeTorrentContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeletionConfirmationDialog::on_rememberBtn_clicked()
|
void DeletionConfirmationDialog::on_rememberBtn_clicked()
|
||||||
{
|
{
|
||||||
Preferences::instance()->setDeleteTorrentFilesAsDefault(m_ui->checkPermDelete->isChecked());
|
Preferences::instance()->setRemoveTorrentContent(m_ui->checkRemoveContent->isChecked());
|
||||||
m_ui->rememberBtn->setEnabled(false);
|
m_ui->rememberBtn->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Bittorrent Client using Qt and libtorrent.
|
* Bittorrent Client using Qt and libtorrent.
|
||||||
|
* Copyright (C) 2024 Vladimir Golovnev <glassez@yandex.ru>
|
||||||
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -37,16 +38,16 @@ namespace Ui
|
||||||
class DeletionConfirmationDialog;
|
class DeletionConfirmationDialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DeletionConfirmationDialog : public QDialog
|
class DeletionConfirmationDialog final : public QDialog
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_DISABLE_COPY_MOVE(DeletionConfirmationDialog)
|
Q_DISABLE_COPY_MOVE(DeletionConfirmationDialog)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DeletionConfirmationDialog(QWidget *parent, int size, const QString &name, bool defaultDeleteFiles);
|
DeletionConfirmationDialog(QWidget *parent, int size, const QString &name, bool defaultDeleteFiles);
|
||||||
~DeletionConfirmationDialog();
|
~DeletionConfirmationDialog() override;
|
||||||
|
|
||||||
bool isDeleteFileSelected() const;
|
bool isRemoveContentSelected() const;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void updateRememberButtonState();
|
void updateRememberButtonState();
|
||||||
|
|
|
@ -75,7 +75,7 @@
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="checkPermDelete">
|
<widget class="QCheckBox" name="checkRemoveContent">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||||
<horstretch>0</horstretch>
|
<horstretch>0</horstretch>
|
||||||
|
@ -88,7 +88,7 @@
|
||||||
</font>
|
</font>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Also permanently delete the files</string>
|
<string>Also remove the content files</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
|
|
@ -116,9 +116,10 @@ namespace
|
||||||
void removeTorrents(const QVector<BitTorrent::Torrent *> &torrents, const bool isDeleteFileSelected)
|
void removeTorrents(const QVector<BitTorrent::Torrent *> &torrents, const bool isDeleteFileSelected)
|
||||||
{
|
{
|
||||||
auto *session = BitTorrent::Session::instance();
|
auto *session = BitTorrent::Session::instance();
|
||||||
const DeleteOption deleteOption = isDeleteFileSelected ? DeleteTorrentAndFiles : DeleteTorrent;
|
const BitTorrent::TorrentRemoveOption removeOption = isDeleteFileSelected
|
||||||
|
? BitTorrent::TorrentRemoveOption::RemoveContent : BitTorrent::TorrentRemoveOption::KeepContent;
|
||||||
for (const BitTorrent::Torrent *torrent : torrents)
|
for (const BitTorrent::Torrent *torrent : torrents)
|
||||||
session->deleteTorrent(torrent->id(), deleteOption);
|
session->removeTorrent(torrent->id(), removeOption);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,7 +443,7 @@ void TransferListWidget::deleteSelectedTorrents(const bool deleteLocalFiles)
|
||||||
{
|
{
|
||||||
// Some torrents might be removed when waiting for user input, so refetch the torrent list
|
// Some torrents might be removed when waiting for user input, so refetch the torrent list
|
||||||
// NOTE: this will only work when dialog is modal
|
// NOTE: this will only work when dialog is modal
|
||||||
removeTorrents(getSelectedTorrents(), dialog->isDeleteFileSelected());
|
removeTorrents(getSelectedTorrents(), dialog->isRemoveContentSelected());
|
||||||
});
|
});
|
||||||
dialog->open();
|
dialog->open();
|
||||||
}
|
}
|
||||||
|
@ -465,7 +466,7 @@ void TransferListWidget::deleteVisibleTorrents()
|
||||||
{
|
{
|
||||||
// Some torrents might be removed when waiting for user input, so refetch the torrent list
|
// Some torrents might be removed when waiting for user input, so refetch the torrent list
|
||||||
// NOTE: this will only work when dialog is modal
|
// NOTE: this will only work when dialog is modal
|
||||||
removeTorrents(getVisibleTorrents(), dialog->isDeleteFileSelected());
|
removeTorrents(getVisibleTorrents(), dialog->isRemoveContentSelected());
|
||||||
});
|
});
|
||||||
dialog->open();
|
dialog->open();
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,7 +136,7 @@ void AppController::preferencesAction()
|
||||||
data[u"file_log_age"_s] = app()->fileLoggerAge();
|
data[u"file_log_age"_s] = app()->fileLoggerAge();
|
||||||
data[u"file_log_age_type"_s] = app()->fileLoggerAgeType();
|
data[u"file_log_age_type"_s] = app()->fileLoggerAgeType();
|
||||||
// Delete torrent contents files on torrent removal
|
// Delete torrent contents files on torrent removal
|
||||||
data[u"delete_torrent_content_files"_s] = pref->deleteTorrentFilesAsDefault();
|
data[u"delete_torrent_content_files"_s] = pref->removeTorrentContent();
|
||||||
|
|
||||||
// Downloads
|
// Downloads
|
||||||
// When adding a torrent
|
// When adding a torrent
|
||||||
|
@ -350,6 +350,8 @@ void AppController::preferencesAction()
|
||||||
// qBitorrent preferences
|
// qBitorrent preferences
|
||||||
// Resume data storage type
|
// Resume data storage type
|
||||||
data[u"resume_data_storage_type"_s] = Utils::String::fromEnum(session->resumeDataStorageType());
|
data[u"resume_data_storage_type"_s] = Utils::String::fromEnum(session->resumeDataStorageType());
|
||||||
|
// Torrent content removing mode
|
||||||
|
data[u"torrent_content_remove_option"_s] = Utils::String::fromEnum(session->torrentContentRemoveOption());
|
||||||
// Physical memory (RAM) usage limit
|
// Physical memory (RAM) usage limit
|
||||||
data[u"memory_working_set_limit"_s] = app()->memoryWorkingSetLimit();
|
data[u"memory_working_set_limit"_s] = app()->memoryWorkingSetLimit();
|
||||||
// Current network interface
|
// Current network interface
|
||||||
|
@ -519,7 +521,7 @@ void AppController::setPreferencesAction()
|
||||||
app()->setFileLoggerAgeType(it.value().toInt());
|
app()->setFileLoggerAgeType(it.value().toInt());
|
||||||
// Delete torrent content files on torrent removal
|
// Delete torrent content files on torrent removal
|
||||||
if (hasKey(u"delete_torrent_content_files"_s))
|
if (hasKey(u"delete_torrent_content_files"_s))
|
||||||
pref->setDeleteTorrentFilesAsDefault(it.value().toBool());
|
pref->setRemoveTorrentContent(it.value().toBool());
|
||||||
|
|
||||||
// Downloads
|
// Downloads
|
||||||
// When adding a torrent
|
// When adding a torrent
|
||||||
|
@ -931,6 +933,9 @@ void AppController::setPreferencesAction()
|
||||||
// Resume data storage type
|
// Resume data storage type
|
||||||
if (hasKey(u"resume_data_storage_type"_s))
|
if (hasKey(u"resume_data_storage_type"_s))
|
||||||
session->setResumeDataStorageType(Utils::String::toEnum(it.value().toString(), BitTorrent::ResumeDataStorageType::Legacy));
|
session->setResumeDataStorageType(Utils::String::toEnum(it.value().toString(), BitTorrent::ResumeDataStorageType::Legacy));
|
||||||
|
// Torrent content removing mode
|
||||||
|
if (hasKey(u"torrent_content_remove_option"_s))
|
||||||
|
session->setTorrentContentRemoveOption(Utils::String::toEnum(it.value().toString(), BitTorrent::TorrentContentRemoveOption::MoveToTrash));
|
||||||
// Physical memory (RAM) usage limit
|
// Physical memory (RAM) usage limit
|
||||||
if (hasKey(u"memory_working_set_limit"_s))
|
if (hasKey(u"memory_working_set_limit"_s))
|
||||||
app()->setMemoryWorkingSetLimit(it.value().toInt());
|
app()->setMemoryWorkingSetLimit(it.value().toInt());
|
||||||
|
|
|
@ -1096,11 +1096,11 @@ void TorrentsController::deleteAction()
|
||||||
requireParams({u"hashes"_s, u"deleteFiles"_s});
|
requireParams({u"hashes"_s, u"deleteFiles"_s});
|
||||||
|
|
||||||
const QStringList hashes {params()[u"hashes"_s].split(u'|')};
|
const QStringList hashes {params()[u"hashes"_s].split(u'|')};
|
||||||
const DeleteOption deleteOption = parseBool(params()[u"deleteFiles"_s]).value_or(false)
|
const BitTorrent::TorrentRemoveOption deleteOption = parseBool(params()[u"deleteFiles"_s]).value_or(false)
|
||||||
? DeleteTorrentAndFiles : DeleteTorrent;
|
? BitTorrent::TorrentRemoveOption::RemoveContent : BitTorrent::TorrentRemoveOption::KeepContent;
|
||||||
applyToTorrents(hashes, [deleteOption](const BitTorrent::Torrent *torrent)
|
applyToTorrents(hashes, [deleteOption](const BitTorrent::Torrent *torrent)
|
||||||
{
|
{
|
||||||
BitTorrent::Session::instance()->deleteTorrent(torrent->id(), deleteOption);
|
BitTorrent::Session::instance()->removeTorrent(torrent->id(), deleteOption);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@
|
||||||
<p> QBT_TR(Are you sure you want to remove the selected torrents from the transfer list?)QBT_TR[CONTEXT=HttpServer]</p>
|
<p> QBT_TR(Are you sure you want to remove the selected torrents from the transfer list?)QBT_TR[CONTEXT=HttpServer]</p>
|
||||||
<button id="rememberBtn" type="button" title="Remember choice" style="vertical-align: middle; padding: 4px 6px;">
|
<button id="rememberBtn" type="button" title="Remember choice" style="vertical-align: middle; padding: 4px 6px;">
|
||||||
</button>
|
</button>
|
||||||
<input type="checkbox" id="deleteFromDiskCB" /> <label for="deleteFromDiskCB"><i>QBT_TR(Also permanently delete the files)QBT_TR[CONTEXT=confirmDeletionDlg]</i></label><br /><br />
|
<input type="checkbox" id="deleteFromDiskCB" /> <label for="deleteFromDiskCB"><i>QBT_TR(Also remove the content files)QBT_TR[CONTEXT=confirmDeletionDlg]</i></label><br /><br />
|
||||||
<div style="text-align: right;">
|
<div style="text-align: right;">
|
||||||
<input type="button" id="cancelBtn" value="QBT_TR(Cancel)QBT_TR[CONTEXT=MainWindow]" /> <input type="button" id="confirmBtn" value="QBT_TR(Remove)QBT_TR[CONTEXT=MainWindow]" />
|
<input type="button" id="cancelBtn" value="QBT_TR(Cancel)QBT_TR[CONTEXT=MainWindow]" /> <input type="button" id="confirmBtn" value="QBT_TR(Remove)QBT_TR[CONTEXT=MainWindow]" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -997,6 +997,17 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="rowMemoryWorkingSetLimit">
|
<tr id="rowMemoryWorkingSetLimit">
|
||||||
|
<td>
|
||||||
|
<label for="torrentContentRemoveOption">QBT_TR(Torrent content removing mode:)QBT_TR[CONTEXT=OptionsDialog]</label>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<select id="torrentContentRemoveOption" style="width: 15em;">
|
||||||
|
<option value="Delete">QBT_TR(Delete files permanently)QBT_TR[CONTEXT=OptionsDialog]</option>
|
||||||
|
<option value="MoveToTrash">QBT_TR(Move files to trash (if possible))QBT_TR[CONTEXT=OptionsDialog]</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<label for="memoryWorkingSetLimit">QBT_TR(Physical memory (RAM) usage limit:)QBT_TR[CONTEXT=OptionsDialog] <a href="https://wikipedia.org/wiki/Working_set" target="_blank">(?)</a></label>
|
<label for="memoryWorkingSetLimit">QBT_TR(Physical memory (RAM) usage limit:)QBT_TR[CONTEXT=OptionsDialog] <a href="https://wikipedia.org/wiki/Working_set" target="_blank">(?)</a></label>
|
||||||
</td>
|
</td>
|
||||||
|
@ -2318,6 +2329,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
|
||||||
// Advanced settings
|
// Advanced settings
|
||||||
// qBittorrent section
|
// qBittorrent section
|
||||||
$("resumeDataStorageType").setProperty("value", pref.resume_data_storage_type);
|
$("resumeDataStorageType").setProperty("value", pref.resume_data_storage_type);
|
||||||
|
$("torrentContentRemoveOption").setProperty("value", pref.torrent_content_remove_option);
|
||||||
$("memoryWorkingSetLimit").setProperty("value", pref.memory_working_set_limit);
|
$("memoryWorkingSetLimit").setProperty("value", pref.memory_working_set_limit);
|
||||||
updateNetworkInterfaces(pref.current_network_interface, pref.current_interface_name);
|
updateNetworkInterfaces(pref.current_network_interface, pref.current_interface_name);
|
||||||
updateInterfaceAddresses(pref.current_network_interface, pref.current_interface_address);
|
updateInterfaceAddresses(pref.current_network_interface, pref.current_interface_address);
|
||||||
|
@ -2764,6 +2776,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
|
||||||
// Update advanced settings
|
// Update advanced settings
|
||||||
// qBittorrent section
|
// qBittorrent section
|
||||||
settings["resume_data_storage_type"] = $("resumeDataStorageType").getProperty("value");
|
settings["resume_data_storage_type"] = $("resumeDataStorageType").getProperty("value");
|
||||||
|
settings["torrent_content_remove_option"] = $("torrentContentRemoveOption").getProperty("value");
|
||||||
settings["memory_working_set_limit"] = Number($("memoryWorkingSetLimit").getProperty("value"));
|
settings["memory_working_set_limit"] = Number($("memoryWorkingSetLimit").getProperty("value"));
|
||||||
settings["current_network_interface"] = $("networkInterface").getProperty("value");
|
settings["current_network_interface"] = $("networkInterface").getProperty("value");
|
||||||
settings["current_interface_address"] = $("optionalIPAddressToBind").getProperty("value");
|
settings["current_interface_address"] = $("optionalIPAddressToBind").getProperty("value");
|
||||||
|
|
Loading…
Reference in a new issue