Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport changes to v5.0.x branch #21496

Merged
merged 15 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/ci_windows.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ jobs:
- name: Install Qt
uses: jurplel/install-qt-action@v4
with:
version: "6.7.0"
version: "6.7.3"
archives: qtbase qtsvg qttools
cache: true

Expand Down
11 changes: 1 addition & 10 deletions src/app/main.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2014-2023 Vladimir Golovnev <[email protected]>
* Copyright (C) 2014-2024 Vladimir Golovnev <[email protected]>
* Copyright (C) 2006 Christophe Dumez <[email protected]>
*
* This program is free software; you can redistribute it and/or
Expand Down Expand Up @@ -58,10 +58,6 @@
#include <QSplashScreen>
#include <QTimer>

#ifdef Q_OS_WIN
#include <QOperatingSystemVersion>
#endif

#ifdef QBT_STATIC_QT
#include <QtPlugin>
Q_IMPORT_PLUGIN(QICOPlugin)
Expand Down Expand Up @@ -189,11 +185,6 @@ int main(int argc, char *argv[])
// We must save it here because QApplication constructor may change it
const bool isOneArg = (argc == 2);

#if !defined(DISABLE_GUI) && defined(Q_OS_WIN)
if (QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows10)
QApplication::setStyle(u"Fusion"_s);
#endif

// `app` must be declared out of try block to allow display message box in case of exception
std::unique_ptr<Application> app;
try
Expand Down
57 changes: 29 additions & 28 deletions src/base/addtorrentmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,36 @@ void AddTorrentManager::handleAddTorrentFailed(const QString &source, const QStr
emit addTorrentFailed(source, reason);
}

void AddTorrentManager::handleDuplicateTorrent(const QString &source, BitTorrent::Torrent *torrent, const QString &message)
void AddTorrentManager::handleDuplicateTorrent(const QString &source
, const BitTorrent::TorrentDescriptor &torrentDescr, BitTorrent::Torrent *existingTorrent)
{
const bool hasMetadata = torrentDescr.info().has_value();
if (hasMetadata)
{
// Trying to set metadata to existing torrent in case if it has none
existingTorrent->setMetadata(*torrentDescr.info());
}

const bool isPrivate = existingTorrent->isPrivate() || (hasMetadata && torrentDescr.info()->isPrivate());
QString message;
if (!btSession()->isMergeTrackersEnabled())
{
message = tr("Merging of trackers is disabled");
}
else if (isPrivate)
{
message = tr("Trackers cannot be merged because it is a private torrent");
}
else
{
// merge trackers and web seeds
existingTorrent->addTrackers(torrentDescr.trackers());
existingTorrent->addUrlSeeds(torrentDescr.urlSeeds());
message = tr("Trackers are merged from new source");
}

LogMsg(tr("Detected an attempt to add a duplicate torrent. Source: %1. Existing torrent: %2. Result: %3")
.arg(source, torrent->name(), message));
.arg(source, existingTorrent->name(), message));
emit addTorrentFailed(source, message);
}

Expand All @@ -184,32 +210,7 @@ bool AddTorrentManager::processTorrent(const QString &source, const BitTorrent::
if (BitTorrent::Torrent *torrent = btSession()->findTorrent(infoHash))
{
// a duplicate torrent is being added

const bool hasMetadata = torrentDescr.info().has_value();
if (hasMetadata)
{
// Trying to set metadata to existing torrent in case if it has none
torrent->setMetadata(*torrentDescr.info());
}

if (!btSession()->isMergeTrackersEnabled())
{
handleDuplicateTorrent(source, torrent, tr("Merging of trackers is disabled"));
return false;
}

const bool isPrivate = torrent->isPrivate() || (hasMetadata && torrentDescr.info()->isPrivate());
if (isPrivate)
{
handleDuplicateTorrent(source, torrent, tr("Trackers cannot be merged because it is a private torrent"));
return false;
}

// merge trackers and web seeds
torrent->addTrackers(torrentDescr.trackers());
torrent->addUrlSeeds(torrentDescr.urlSeeds());

handleDuplicateTorrent(source, torrent, tr("Trackers are merged from new source"));
handleDuplicateTorrent(source, torrentDescr, torrent);
return false;
}

Expand Down
2 changes: 1 addition & 1 deletion src/base/addtorrentmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class AddTorrentManager : public ApplicationComponent<QObject>
bool addTorrentToSession(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr
, const BitTorrent::AddTorrentParams &addTorrentParams);
void handleAddTorrentFailed(const QString &source, const QString &reason);
void handleDuplicateTorrent(const QString &source, BitTorrent::Torrent *torrent, const QString &message);
void handleDuplicateTorrent(const QString &source, const BitTorrent::TorrentDescriptor &torrentDescr, BitTorrent::Torrent *existingTorrent);
void setTorrentFileGuard(const QString &source, std::shared_ptr<TorrentFileGuard> torrentFileGuard);
void releaseTorrentFileGuard(const QString &source);

Expand Down
3 changes: 2 additions & 1 deletion src/base/bittorrent/session.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ namespace BitTorrent
{
Default = 0,
MMap = 1,
Posix = 2
Posix = 2,
SimplePreadPwrite = 3
};
Q_ENUM_NS(DiskIOType)

Expand Down
87 changes: 52 additions & 35 deletions src/base/bittorrent/sessionimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ SessionImpl::SessionImpl(QObject *parent)
, m_I2POutboundQuantity {BITTORRENT_SESSION_KEY(u"I2P/OutboundQuantity"_s), 3}
, m_I2PInboundLength {BITTORRENT_SESSION_KEY(u"I2P/InboundLength"_s), 3}
, m_I2POutboundLength {BITTORRENT_SESSION_KEY(u"I2P/OutboundLength"_s), 3}
, m_torrentContentRemoveOption {BITTORRENT_SESSION_KEY(u"TorrentContentRemoveOption"_s), TorrentContentRemoveOption::MoveToTrash}
, m_torrentContentRemoveOption {BITTORRENT_SESSION_KEY(u"TorrentContentRemoveOption"_s), TorrentContentRemoveOption::Delete}
, m_startPaused {BITTORRENT_SESSION_KEY(u"StartPaused"_s)}
, m_seedingLimitTimer {new QTimer(this)}
, m_resumeDataTimer {new QTimer(this)}
Expand Down Expand Up @@ -1638,6 +1638,13 @@ void SessionImpl::initializeNativeSession()
#ifdef QBT_USES_LIBTORRENT2
// preserve the same behavior as in earlier libtorrent versions
pack.set_bool(lt::settings_pack::enable_set_file_valid_data, true);

// This is a special case. We use MMap disk IO but tweak it to always fallback to pread/pwrite.
if (diskIOType() == DiskIOType::SimplePreadPwrite)
{
pack.set_int(lt::settings_pack::mmap_file_size_cutoff, std::numeric_limits<int>::max());
pack.set_int(lt::settings_pack::disk_write_mode, lt::settings_pack::mmap_write_mode_t::always_pwrite);
}
#endif

lt::session_params sessionParams {std::move(pack), {}};
Expand All @@ -1648,6 +1655,7 @@ void SessionImpl::initializeNativeSession()
sessionParams.disk_io_constructor = customPosixDiskIOConstructor;
break;
case DiskIOType::MMap:
case DiskIOType::SimplePreadPwrite:
sessionParams.disk_io_constructor = customMMapDiskIOConstructor;
break;
default:
Expand Down Expand Up @@ -2807,6 +2815,19 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr
loadTorrentParams.name = contentName;
}

const auto nativeIndexes = torrentInfo.nativeIndexes();

Q_ASSERT(p.file_priorities.empty());
Q_ASSERT(addTorrentParams.filePriorities.isEmpty() || (addTorrentParams.filePriorities.size() == nativeIndexes.size()));
QList<DownloadPriority> filePriorities = addTorrentParams.filePriorities;

// Filename filter should be applied before `findIncompleteFiles()` is called.
if (filePriorities.isEmpty() && isExcludedFileNamesEnabled())
{
// Check file name blacklist when priorities are not explicitly set
applyFilenameFilter(filePaths, filePriorities);
}

if (!loadTorrentParams.hasFinishedStatus)
{
const Path actualDownloadPath = useAutoTMM
Expand All @@ -2815,24 +2836,12 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr
isFindingIncompleteFiles = true;
}

const auto nativeIndexes = torrentInfo.nativeIndexes();
if (!isFindingIncompleteFiles)
{
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()));

QList<DownloadPriority> filePriorities = addTorrentParams.filePriorities;

if (filePriorities.isEmpty() && isExcludedFileNamesEnabled())
{
// Check file name blacklist when priorities are not explicitly set
applyFilenameFilter(filePaths, filePriorities);
}

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));
Expand Down Expand Up @@ -5206,6 +5215,9 @@ void SessionImpl::handleMoveTorrentStorageJobFinished(const Path &newPath)
if (torrent)
{
torrent->handleMoveStorageJobFinished(newPath, finishedJob.context, torrentHasOutstandingJob);
// The torrent may become "finished" at the end of the move if it was moved
// from the "incomplete" location after downloading finished.
processPendingFinishedTorrents();
}
else if (!torrentHasOutstandingJob)
{
Expand All @@ -5217,6 +5229,32 @@ void SessionImpl::handleMoveTorrentStorageJobFinished(const Path &newPath)
}
}

void SessionImpl::processPendingFinishedTorrents()
{
if (m_pendingFinishedTorrents.isEmpty())
return;

for (TorrentImpl *torrent : asConst(m_pendingFinishedTorrents))
{
LogMsg(tr("Torrent download finished. Torrent: \"%1\"").arg(torrent->name()));
emit torrentFinished(torrent);

if (const Path exportPath = finishedTorrentExportDirectory(); !exportPath.isEmpty())
exportTorrentFile(torrent, exportPath);

processTorrentShareLimits(torrent);
}

m_pendingFinishedTorrents.clear();

const bool hasUnfinishedTorrents = std::any_of(m_torrents.cbegin(), m_torrents.cend(), [](const TorrentImpl *torrent)
{
return !(torrent->isFinished() || torrent->isStopped() || torrent->isErrored());
});
if (!hasUnfinishedTorrents)
emit allTorrentsFinished();
}

void SessionImpl::storeCategories() const
{
QJsonObject jsonObj;
Expand Down Expand Up @@ -6108,28 +6146,7 @@ void SessionImpl::handleStateUpdateAlert(const lt::state_update_alert *alert)
if (!updatedTorrents.isEmpty())
emit torrentsUpdated(updatedTorrents);

if (!m_pendingFinishedTorrents.isEmpty())
{
for (TorrentImpl *torrent : m_pendingFinishedTorrents)
{
LogMsg(tr("Torrent download finished. Torrent: \"%1\"").arg(torrent->name()));
emit torrentFinished(torrent);

if (const Path exportPath = finishedTorrentExportDirectory(); !exportPath.isEmpty())
exportTorrentFile(torrent, exportPath);

processTorrentShareLimits(torrent);
}

m_pendingFinishedTorrents.clear();

const bool hasUnfinishedTorrents = std::any_of(m_torrents.cbegin(), m_torrents.cend(), [](const TorrentImpl *torrent)
{
return !(torrent->isFinished() || torrent->isStopped() || torrent->isErrored());
});
if (!hasUnfinishedTorrents)
emit allTorrentsFinished();
}
processPendingFinishedTorrents();

if (m_needSaveTorrentsQueue)
saveTorrentsQueue();
Expand Down
1 change: 1 addition & 0 deletions src/base/bittorrent/sessionimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ namespace BitTorrent

void moveTorrentStorage(const MoveStorageJob &job) const;
void handleMoveTorrentStorageJobFinished(const Path &newPath);
void processPendingFinishedTorrents();

void loadCategories();
void storeCategories() const;
Expand Down
2 changes: 1 addition & 1 deletion src/base/bittorrent/torrentimpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1819,7 +1819,7 @@ void TorrentImpl::endReceivedMetadataHandling(const Path &savePath, const PathLi
m_filePriorities.append(LT::fromNative(p.file_priorities[LT::toUnderlyingType(nativeIndex)]));
}

m_session->applyFilenameFilter(fileNames, m_filePriorities);
m_session->applyFilenameFilter(m_filePaths, m_filePriorities);
for (int i = 0; i < m_filePriorities.size(); ++i)
p.file_priorities[LT::toUnderlyingType(nativeIndexes[i])] = LT::toNative(m_filePriorities[i]);

Expand Down
13 changes: 13 additions & 0 deletions src/base/preferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,19 @@ void Preferences::setWinStartup(const bool b)
settings.remove(profileID);
}
}

QString Preferences::getStyle() const
{
return value<QString>(u"Appearance/Style"_s);
}

void Preferences::setStyle(const QString &styleName)
{
if (styleName == getStyle())
return;

setValue(u"Appearance/Style"_s, styleName);
}
#endif // Q_OS_WIN

// Downloads
Expand Down
2 changes: 2 additions & 0 deletions src/base/preferences.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ class Preferences final : public QObject
#ifdef Q_OS_WIN
bool WinStartup() const;
void setWinStartup(bool b);
QString getStyle() const;
void setStyle(const QString &styleName);
#endif

// Downloads
Expand Down
5 changes: 5 additions & 0 deletions src/base/utils/os.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,11 @@ Path Utils::OS::windowsSystemPath()
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
bool Utils::OS::applyMarkOfTheWeb(const Path &file, const QString &url)
{
// Trying to apply this to a non-existent file is unacceptable,
// as it may unexpectedly create such a file.
if (!file.exists())
return false;

Q_ASSERT(url.isEmpty() || url.startsWith(u"http:") || url.startsWith(u"https:"));

#ifdef Q_OS_MACOS
Expand Down
1 change: 1 addition & 0 deletions src/gui/advancedsettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,7 @@ void AdvancedSettings::loadAdvancedSettings()
m_comboBoxDiskIOType.addItem(tr("Default"), QVariant::fromValue(BitTorrent::DiskIOType::Default));
m_comboBoxDiskIOType.addItem(tr("Memory mapped files"), QVariant::fromValue(BitTorrent::DiskIOType::MMap));
m_comboBoxDiskIOType.addItem(tr("POSIX-compliant"), QVariant::fromValue(BitTorrent::DiskIOType::Posix));
m_comboBoxDiskIOType.addItem(tr("Simple pread/pwrite"), QVariant::fromValue(BitTorrent::DiskIOType::SimplePreadPwrite));
m_comboBoxDiskIOType.setCurrentIndex(m_comboBoxDiskIOType.findData(QVariant::fromValue(session->diskIOType())));
addRow(DISK_IO_TYPE, tr("Disk IO type (requires restart)") + u' ' + makeLink(u"https://www.libtorrent.org/single-page-ref.html#default-disk-io-constructor", u"(?)")
, &m_comboBoxDiskIOType);
Expand Down
Loading
Loading