Browse Source

Add file name filter/blacklist

Blacklist filtered file names from being downloaded from torrent(s).
Files matching any of the filters in this list will have their priority automatically set to "Do not download".
See Options > Downloads >Do not download.

Closes #3369.
PR #17106.
adaptive-webui-19844
mxtsdev 2 years ago committed by GitHub
parent
commit
5e6174c087
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 66
      src/base/bittorrent/session.cpp
  2. 6
      src/base/bittorrent/session.h
  3. 18
      src/base/bittorrent/torrentimpl.cpp
  4. 15
      src/gui/addnewtorrentdialog.cpp
  5. 3
      src/gui/optionsdialog.cpp
  6. 38
      src/gui/optionsdialog.ui
  7. 5
      src/webui/api/appcontroller.cpp
  8. 2
      src/webui/webapplication.h
  9. 8
      src/webui/www/private/views/preferences.html

66
src/base/bittorrent/session.cpp

@ -414,6 +414,7 @@ Session::Session(QObject *parent) @@ -414,6 +414,7 @@ Session::Session(QObject *parent)
, m_peerTurnoverCutoff(BITTORRENT_SESSION_KEY(u"PeerTurnoverCutOff"_qs), 90)
, m_peerTurnoverInterval(BITTORRENT_SESSION_KEY(u"PeerTurnoverInterval"_qs), 300)
, m_requestQueueSize(BITTORRENT_SESSION_KEY(u"RequestQueueSize"_qs), 500)
, m_excludedFileNames(BITTORRENT_SESSION_KEY(u"ExcludedFileNames"_qs))
, m_bannedIPs(u"State/BannedIPs"_qs
, QStringList()
, [](const QStringList &value)
@ -466,6 +467,7 @@ Session::Session(QObject *parent) @@ -466,6 +467,7 @@ Session::Session(QObject *parent)
enqueueRefresh();
updateSeedingLimitTimer();
populateAdditionalTrackers();
populateExcludedFileNamesRegExpList();
enableTracker(isTrackerEnabled());
@ -2244,19 +2246,30 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source @@ -2244,19 +2246,30 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
}
const auto nativeIndexes = torrentInfo.nativeIndexes();
if (!filePaths.isEmpty())
{
for (int index = 0; index < filePaths.size(); ++index)
p.renamed_files[nativeIndexes[index]] = filePaths.at(index).toString().toStdString();
}
for (int index = 0; index < filePaths.size(); ++index)
p.renamed_files[nativeIndexes[index]] = filePaths.at(index).toString().toStdString();
Q_ASSERT(p.file_priorities.empty());
Q_ASSERT(addTorrentParams.filePriorities.isEmpty() || (addTorrentParams.filePriorities.size() == nativeIndexes.size()));
const int internalFilesCount = torrentInfo.nativeInfo()->files().num_files(); // including .pad files
// Use qBittorrent default priority rather than libtorrent's (4)
p.file_priorities = std::vector(internalFilesCount, LT::toNative(DownloadPriority::Normal));
Q_ASSERT(addTorrentParams.filePriorities.isEmpty() || (addTorrentParams.filePriorities.size() == nativeIndexes.size()));
for (int i = 0; i < addTorrentParams.filePriorities.size(); ++i)
p.file_priorities[LT::toUnderlyingType(nativeIndexes[i])] = LT::toNative(addTorrentParams.filePriorities[i]);
if (addTorrentParams.filePriorities.size() == 0)
{
// Check file name blacklist when priorities are not explicitly set
for (int i = 0; i < filePaths.size(); ++i)
{
if (isFilenameExcluded(filePaths.at(i).filename()))
p.file_priorities[LT::toUnderlyingType(nativeIndexes[i])] = lt::dont_download;
}
}
else
{
for (int i = 0; i < addTorrentParams.filePriorities.size(); ++i)
p.file_priorities[LT::toUnderlyingType(nativeIndexes[i])] = LT::toNative(addTorrentParams.filePriorities[i]);
}
p.ti = torrentInfo.nativeInfo();
}
@ -3078,6 +3091,43 @@ void Session::setIPFilterFile(const Path &path) @@ -3078,6 +3091,43 @@ void Session::setIPFilterFile(const Path &path)
}
}
QStringList Session::excludedFileNames() const
{
return m_excludedFileNames;
}
void Session::setExcludedFileNames(const QStringList &excludedFileNames)
{
if (excludedFileNames != m_excludedFileNames)
{
m_excludedFileNames = excludedFileNames;
populateExcludedFileNamesRegExpList();
}
}
void Session::populateExcludedFileNamesRegExpList()
{
const QStringList excludedNames = excludedFileNames();
m_excludedFileNamesRegExpList.clear();
m_excludedFileNamesRegExpList.reserve(excludedNames.size());
for (const QString &str : excludedNames)
{
const QString pattern = QRegularExpression::anchoredPattern(QRegularExpression::wildcardToRegularExpression(str));
const QRegularExpression re {pattern, QRegularExpression::CaseInsensitiveOption};
m_excludedFileNamesRegExpList.append(re);
}
}
bool Session::isFilenameExcluded(const QString &fileName) const
{
return std::any_of(m_excludedFileNamesRegExpList.begin(), m_excludedFileNamesRegExpList.end(), [&fileName](const QRegularExpression &re)
{
return re.match(fileName).hasMatch();
});
}
void Session::setBannedIPs(const QStringList &newList)
{
if (newList == m_bannedIPs)

6
src/base/bittorrent/session.h

@ -455,6 +455,9 @@ namespace BitTorrent @@ -455,6 +455,9 @@ namespace BitTorrent
void setBlockPeersOnPrivilegedPorts(bool enabled);
bool isTrackerFilteringEnabled() const;
void setTrackerFilteringEnabled(bool enabled);
QStringList excludedFileNames() const;
void setExcludedFileNames(const QStringList &newList);
bool isFilenameExcluded(const QString &fileName) const;
QStringList bannedIPs() const;
void setBannedIPs(const QStringList &newList);
ResumeDataStorageType resumeDataStorageType() const;
@ -624,6 +627,7 @@ namespace BitTorrent @@ -624,6 +627,7 @@ namespace BitTorrent
void applyOSMemoryPriority() const;
#endif
void processTrackerStatuses();
void populateExcludedFileNamesRegExpList();
bool loadTorrent(LoadTorrentParams params);
LoadTorrentParams initLoadTorrentParams(const AddTorrentParams &addTorrentParams);
@ -778,6 +782,7 @@ namespace BitTorrent @@ -778,6 +782,7 @@ namespace BitTorrent
CachedSettingValue<int> m_peerTurnoverCutoff;
CachedSettingValue<int> m_peerTurnoverInterval;
CachedSettingValue<int> m_requestQueueSize;
CachedSettingValue<QStringList> m_excludedFileNames;
CachedSettingValue<QStringList> m_bannedIPs;
CachedSettingValue<ResumeDataStorageType> m_resumeDataStorageType;
#if defined(Q_OS_WIN)
@ -792,6 +797,7 @@ namespace BitTorrent @@ -792,6 +797,7 @@ namespace BitTorrent
int m_numResumeData = 0;
int m_extraLimit = 0;
QVector<TrackerEntry> m_additionalTrackerList;
QVector<QRegularExpression> m_excludedFileNamesRegExpList;
bool m_refreshEnqueued = false;
QTimer *m_seedingLimitTimer = nullptr;

18
src/base/bittorrent/torrentimpl.cpp

@ -1519,9 +1519,8 @@ void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathLi @@ -1519,9 +1519,8 @@ void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathLi
m_torrentInfo = TorrentInfo(*metadata);
m_filePriorities.reserve(filesCount());
const auto nativeIndexes = m_torrentInfo.nativeIndexes();
const std::vector<lt::download_priority_t> filePriorities =
resized(p.file_priorities, metadata->files().num_files()
, LT::toNative(p.file_priorities.empty() ? DownloadPriority::Normal : DownloadPriority::Ignored));
p.file_priorities = resized(p.file_priorities, metadata->files().num_files()
, LT::toNative(p.file_priorities.empty() ? DownloadPriority::Normal : DownloadPriority::Ignored));
m_completedFiles.fill(static_cast<bool>(p.flags & lt::torrent_flags::seed_mode), filesCount());
@ -1529,17 +1528,20 @@ void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathLi @@ -1529,17 +1528,20 @@ void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathLi
{
const auto nativeIndex = nativeIndexes.at(i);
const Path filePath = fileNames.at(i);
p.renamed_files[nativeIndex] = filePath.toString().toStdString();
const Path actualFilePath = fileNames.at(i);
p.renamed_files[nativeIndex] = actualFilePath.toString().toStdString();
m_filePaths.append(filePath.removedExtension(QB_EXT));
const Path filePath = actualFilePath.removedExtension(QB_EXT);
m_filePaths.append(filePath);
const auto priority = LT::fromNative(filePriorities[LT::toUnderlyingType(nativeIndex)]);
lt::download_priority_t &nativePriority = p.file_priorities[LT::toUnderlyingType(nativeIndex)];
if ((nativePriority != lt::dont_download) && m_session->isFilenameExcluded(filePath.filename()))
nativePriority = lt::dont_download;
const auto priority = LT::fromNative(nativePriority);
m_filePriorities.append(priority);
}
p.save_path = savePath.toString().toStdString();
p.ti = metadata;
p.file_priorities = filePriorities;
reload();

15
src/gui/addnewtorrentdialog.cpp

@ -975,6 +975,21 @@ void AddNewTorrentDialog::setupTreeview() @@ -975,6 +975,21 @@ void AddNewTorrentDialog::setupTreeview()
currentIndex = m_contentModel->index(0, 0, currentIndex);
m_ui->contentTreeView->setExpanded(currentIndex, true);
}
// Check file name blacklist for torrents that are manually added
QVector<BitTorrent::DownloadPriority> priorities = m_contentModel->model()->getFilePriorities();
Q_ASSERT(priorities.size() == m_torrentInfo.filesCount());
for (int i = 0; i < priorities.size(); ++i)
{
if (priorities[i] == BitTorrent::DownloadPriority::Ignored)
continue;
if (BitTorrent::Session::instance()->isFilenameExcluded(m_torrentInfo.filePath(i).filename()))
priorities[i] = BitTorrent::DownloadPriority::Ignored;
}
m_contentModel->model()->updateFilesPriorities(priorities);
}
updateDiskSpaceLabel();

3
src/gui/optionsdialog.cpp

@ -375,6 +375,7 @@ OptionsDialog::OptionsDialog(QWidget *parent) @@ -375,6 +375,7 @@ OptionsDialog::OptionsDialog(QWidget *parent)
connect(m_ui->checkUseDownloadPath, &QAbstractButton::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->checkUseDownloadPath, &QAbstractButton::toggled, m_ui->textDownloadPath, &QWidget::setEnabled);
connect(m_ui->addWatchedFolderButton, &QAbstractButton::clicked, this, &ThisType::enableApplyButton);
connect(m_ui->textExcludedFileNames, &QPlainTextEdit::textChanged, this, &ThisType::enableApplyButton);
connect(m_ui->removeWatchedFolderButton, &QAbstractButton::clicked, this, &ThisType::enableApplyButton);
connect(m_ui->groupMailNotification, &QGroupBox::toggled, this, &ThisType::enableApplyButton);
connect(m_ui->senderEmailTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton);
@ -757,6 +758,7 @@ void OptionsDialog::saveOptions() @@ -757,6 +758,7 @@ void OptionsDialog::saveOptions()
session->setTorrentContentLayout(static_cast<BitTorrent::TorrentContentLayout>(m_ui->contentLayoutComboBox->currentIndex()));
auto watchedFoldersModel = static_cast<WatchedFoldersModel *>(m_ui->scanFoldersView->model());
watchedFoldersModel->apply();
session->setExcludedFileNames(m_ui->textExcludedFileNames->toPlainText().split(u'\n', Qt::SkipEmptyParts));
session->setTorrentExportDirectory(getTorrentExportDir());
session->setFinishedTorrentExportDirectory(getFinishedTorrentExportDir());
pref->setMailNotificationEnabled(m_ui->groupMailNotification->isChecked());
@ -1016,6 +1018,7 @@ void OptionsDialog::loadOptions() @@ -1016,6 +1018,7 @@ void OptionsDialog::loadOptions()
m_ui->checkAppendqB->setChecked(session->isAppendExtensionEnabled());
m_ui->checkPreallocateAll->setChecked(session->isPreallocationEnabled());
m_ui->checkRecursiveDownload->setChecked(!pref->recursiveDownloadDisabled());
m_ui->textExcludedFileNames->setPlainText(session->excludedFileNames().join(u'\n'));
if (session->torrentExportDirectory().isEmpty())
{

38
src/gui/optionsdialog.ui

@ -1261,6 +1261,44 @@ Manual: Various torrent properties (e.g. save path) must be assigned manually</s @@ -1261,6 +1261,44 @@ Manual: Various torrent properties (e.g. save path) must be assigned manually</s
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupExcludedFileNames">
<property name="title">
<string>Excluded file names</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_311">
<item>
<widget class="QLabel" name="label_1">
<property name="text">
<string>Filters:</string>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="textExcludedFileNames">
<property name="toolTip">
<string>Blacklist filtered file names from being downloaded from torrent(s).
Files matching any of the filters in this list will have their priority automatically set to &quot;Do not download&quot;.
Use newlines to separate multiple entries. Can use wildcards as outlined below.
*: matches zero or more of any characters.
?: matches any single character.
[...]: sets of characters can be represented in square brackets.
Examples
*.exe: filter '.exe' file extension.
readme.txt: filter exact file name.
?.txt: filter 'a.txt', 'b.txt' but not 'aa.txt'.
readme[0-9].txt: filter 'readme1.txt', 'readme2.txt' but not 'readme10.txt'.</string>
</property>
<property name="lineWrapMode">
<enum>QPlainTextEdit::NoWrap</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupMailNotification">
<property name="title">

5
src/webui/api/appcontroller.cpp

@ -141,6 +141,8 @@ void AppController::preferencesAction() @@ -141,6 +141,8 @@ void AppController::preferencesAction()
data[u"scan_dirs"_qs] = nativeDirs;
// === END DEPRECATED CODE === //
data[u"excluded_file_names"_qs] = session->excludedFileNames().join(u'\n');
// Email notification upon download completion
data[u"mail_notification_enabled"_qs] = pref->isMailNotificationEnabled();
data[u"mail_notification_sender"_qs] = pref->getMailNotificationSender();
@ -476,6 +478,9 @@ void AppController::setPreferencesAction() @@ -476,6 +478,9 @@ void AppController::setPreferencesAction()
}
// === END DEPRECATED CODE === //
if (hasKey(u"excluded_file_names"_qs))
session->setExcludedFileNames(it.value().toString().split(u'\n'));
// Email notification upon download completion
if (hasKey(u"mail_notification_enabled"_qs))
pref->setMailNotificationEnabled(it.value().toBool());

2
src/webui/webapplication.h

@ -48,7 +48,7 @@ @@ -48,7 +48,7 @@
#include "base/utils/version.h"
#include "api/isessionmanager.h"
inline const Utils::Version<int, 3, 2> API_VERSION {2, 8, 11};
inline const Utils::Version<int, 3, 2> API_VERSION {2, 8, 12};
class APIController;
class AuthController;

8
src/webui/www/private/views/preferences.html

@ -130,6 +130,12 @@ @@ -130,6 +130,12 @@
</table>
</fieldset>
<fieldset class="settings">
<legend>QBT_TR(Excluded file names)QBT_TR[CONTEXT=OptionsDialog]</legend>
<label for="excludedFileNamesTextarea">QBT_TR(Filters:)QBT_TR[CONTEXT=OptionsDialog]</label><br>
<textarea id="excludedFileNamesTextarea" rows="6" cols="70"></textarea>
</fieldset>
<fieldset class="settings">
<legend>
<input type="checkbox" id="mail_notification_checkbox" onclick="qBittorrent.Preferences.updateMailNotification();" />
@ -1733,6 +1739,7 @@ @@ -1733,6 +1739,7 @@
addWatchFolder(folder, sel, other);
}
addWatchFolder();
$('excludedFileNamesTextarea').setProperty('value', pref.excluded_file_names);
// Email notification upon download completion
$('mail_notification_checkbox').setProperty('checked', pref.mail_notification_enabled);
@ -2053,6 +2060,7 @@ @@ -2053,6 +2060,7 @@
// Automatically add torrents from
settings.set('scan_dirs', getWatchedFolders());
settings.set('excluded_file_names', $('excludedFileNamesTextarea').getProperty('value'));
// Email notification upon download completion
settings.set('mail_notification_enabled', $('mail_notification_checkbox').getProperty('checked'));

Loading…
Cancel
Save