mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-10-22 18:56:08 +03:00
Merge pull request #18014 from sledgehammer999/maybe_backport
Backports to v4_5_x
This commit is contained in:
commit
bff9189e52
39 changed files with 288 additions and 239 deletions
|
@ -3,7 +3,7 @@ host = https://www.transifex.com
|
||||||
|
|
||||||
[qbittorrent.qbittorrent_master]
|
[qbittorrent.qbittorrent_master]
|
||||||
file_filter = src/lang/qbittorrent_<lang>.ts
|
file_filter = src/lang/qbittorrent_<lang>.ts
|
||||||
lang_map = pt: pt_PT
|
lang_map = pt: pt_PT, zh: zh_CN
|
||||||
source_file = src/lang/qbittorrent_en.ts
|
source_file = src/lang/qbittorrent_en.ts
|
||||||
source_lang = en
|
source_lang = en
|
||||||
type = QT
|
type = QT
|
||||||
|
@ -19,7 +19,7 @@ mode = developer
|
||||||
|
|
||||||
[qbittorrent.qbittorrent_webui]
|
[qbittorrent.qbittorrent_webui]
|
||||||
file_filter = src/webui/www/translations/webui_<lang>.ts
|
file_filter = src/webui/www/translations/webui_<lang>.ts
|
||||||
lang_map = pt: pt_PT
|
lang_map = pt: pt_PT, zh: zh_CN
|
||||||
source_file = src/webui/www/translations/webui_en.ts
|
source_file = src/webui/www/translations/webui_en.ts
|
||||||
source_lang = en
|
source_lang = en
|
||||||
type = QT
|
type = QT
|
||||||
|
|
|
@ -882,7 +882,7 @@ void Application::createStartupProgressDialog()
|
||||||
m_startupProgressDialog = new QProgressDialog(tr("Loading torrents..."), tr("Exit"), 0, 100);
|
m_startupProgressDialog = new QProgressDialog(tr("Loading torrents..."), tr("Exit"), 0, 100);
|
||||||
m_startupProgressDialog->setAttribute(Qt::WA_DeleteOnClose);
|
m_startupProgressDialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
m_startupProgressDialog->setWindowFlag(Qt::WindowMinimizeButtonHint);
|
m_startupProgressDialog->setWindowFlag(Qt::WindowMinimizeButtonHint);
|
||||||
m_startupProgressDialog->setMinimumDuration(0); // Show dialog immediatelly by default
|
m_startupProgressDialog->setMinimumDuration(0); // Show dialog immediately by default
|
||||||
m_startupProgressDialog->setAutoReset(false);
|
m_startupProgressDialog->setAutoReset(false);
|
||||||
m_startupProgressDialog->setAutoClose(false);
|
m_startupProgressDialog->setAutoClose(false);
|
||||||
|
|
||||||
|
|
|
@ -210,7 +210,6 @@ BitTorrent::LoadResumeDataResult BitTorrent::BencodeResumeDataStorage::loadTorre
|
||||||
return nonstd::make_unexpected(tr("Cannot parse resume data: invalid format"));
|
return nonstd::make_unexpected(tr("Cannot parse resume data: invalid format"));
|
||||||
|
|
||||||
LoadTorrentParams torrentParams;
|
LoadTorrentParams torrentParams;
|
||||||
torrentParams.restored = true;
|
|
||||||
torrentParams.category = fromLTString(resumeDataRoot.dict_find_string_value("qBt-category"));
|
torrentParams.category = fromLTString(resumeDataRoot.dict_find_string_value("qBt-category"));
|
||||||
torrentParams.name = fromLTString(resumeDataRoot.dict_find_string_value("qBt-name"));
|
torrentParams.name = fromLTString(resumeDataRoot.dict_find_string_value("qBt-name"));
|
||||||
torrentParams.hasSeedStatus = resumeDataRoot.dict_find_int_value("qBt-seedStatus");
|
torrentParams.hasSeedStatus = resumeDataRoot.dict_find_int_value("qBt-seedStatus");
|
||||||
|
|
|
@ -196,7 +196,6 @@ namespace BitTorrent
|
||||||
LoadTorrentParams parseQueryResultRow(const QSqlQuery &query)
|
LoadTorrentParams parseQueryResultRow(const QSqlQuery &query)
|
||||||
{
|
{
|
||||||
LoadTorrentParams resumeData;
|
LoadTorrentParams resumeData;
|
||||||
resumeData.restored = true;
|
|
||||||
resumeData.name = query.value(DB_COLUMN_NAME.name).toString();
|
resumeData.name = query.value(DB_COLUMN_NAME.name).toString();
|
||||||
resumeData.category = query.value(DB_COLUMN_CATEGORY.name).toString();
|
resumeData.category = query.value(DB_COLUMN_CATEGORY.name).toString();
|
||||||
const QString tagsData = query.value(DB_COLUMN_TAGS.name).toString();
|
const QString tagsData = query.value(DB_COLUMN_TAGS.name).toString();
|
||||||
|
|
|
@ -58,7 +58,5 @@ namespace BitTorrent
|
||||||
|
|
||||||
qreal ratioLimit = Torrent::USE_GLOBAL_RATIO;
|
qreal ratioLimit = Torrent::USE_GLOBAL_RATIO;
|
||||||
int seedingTimeLimit = Torrent::USE_GLOBAL_SEEDING_TIME;
|
int seedingTimeLimit = Torrent::USE_GLOBAL_SEEDING_TIME;
|
||||||
|
|
||||||
bool restored = false; // is existing torrent job?
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,6 +181,31 @@ QString PeerInfo::client() const
|
||||||
return QString::fromStdString(m_nativeInfo.client);
|
return QString::fromStdString(m_nativeInfo.client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString PeerInfo::peerIdClient() const
|
||||||
|
{
|
||||||
|
// when peer ID is not known yet it contains only zero bytes,
|
||||||
|
// do not create string in such case, return empty string instead
|
||||||
|
if (m_nativeInfo.pid.is_all_zeros())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
QString result;
|
||||||
|
|
||||||
|
// interesting part of a typical peer ID is first 8 chars
|
||||||
|
for (int i = 0; i < 8; ++i)
|
||||||
|
{
|
||||||
|
const std::uint8_t c = m_nativeInfo.pid[i];
|
||||||
|
|
||||||
|
// ensure that the peer ID slice consists only of printable ASCII characters,
|
||||||
|
// this should filter out most of the improper IDs
|
||||||
|
if ((c < 32) || (c > 126))
|
||||||
|
return tr("Unknown");
|
||||||
|
|
||||||
|
result += QChar::fromLatin1(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
qreal PeerInfo::progress() const
|
qreal PeerInfo::progress() const
|
||||||
{
|
{
|
||||||
return m_nativeInfo.progress;
|
return m_nativeInfo.progress;
|
||||||
|
|
|
@ -78,6 +78,7 @@ namespace BitTorrent
|
||||||
|
|
||||||
PeerAddress address() const;
|
PeerAddress address() const;
|
||||||
QString client() const;
|
QString client() const;
|
||||||
|
QString peerIdClient() const;
|
||||||
qreal progress() const;
|
qreal progress() const;
|
||||||
int payloadUpSpeed() const;
|
int payloadUpSpeed() const;
|
||||||
int payloadDownSpeed() const;
|
int payloadDownSpeed() const;
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
|
|
||||||
#include <libtorrent/session.hpp>
|
#include <libtorrent/session.hpp>
|
||||||
|
|
||||||
|
#include "base/algorithm.h"
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
|
|
||||||
PortForwarderImpl::PortForwarderImpl(lt::session *provider, QObject *parent)
|
PortForwarderImpl::PortForwarderImpl(lt::session *provider, QObject *parent)
|
||||||
|
@ -63,45 +64,64 @@ void PortForwarderImpl::setEnabled(const bool enabled)
|
||||||
m_storeActive = enabled;
|
m_storeActive = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PortForwarderImpl::addPort(const quint16 port)
|
void PortForwarderImpl::setPorts(const QString &profile, QSet<quint16> ports)
|
||||||
{
|
{
|
||||||
if (m_mappedPorts.contains(port))
|
PortMapping &portMapping = m_portProfiles[profile];
|
||||||
return;
|
Algorithm::removeIf(portMapping, [this, &ports](const quint16 port, const std::vector<lt::port_mapping_t> &handles)
|
||||||
|
|
||||||
if (isEnabled())
|
|
||||||
m_mappedPorts.insert(port, m_provider->add_port_mapping(lt::session::tcp, port, port));
|
|
||||||
else
|
|
||||||
m_mappedPorts.insert(port, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
void PortForwarderImpl::deletePort(const quint16 port)
|
|
||||||
{
|
|
||||||
const auto iter = m_mappedPorts.find(port);
|
|
||||||
if (iter == m_mappedPorts.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (isEnabled())
|
|
||||||
{
|
{
|
||||||
for (const lt::port_mapping_t &portMapping : *iter)
|
// keep existing forwardings
|
||||||
m_provider->delete_port_mapping(portMapping);
|
const bool isAlreadyMapped = ports.remove(port);
|
||||||
|
if (isAlreadyMapped)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// remove outdated forwardings
|
||||||
|
for (const lt::port_mapping_t &handle : handles)
|
||||||
|
m_provider->delete_port_mapping(handle);
|
||||||
|
m_forwardedPorts.remove(port);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// add new forwardings
|
||||||
|
for (const quint16 port : ports)
|
||||||
|
{
|
||||||
|
// port already forwarded/taken by other profile, don't do anything
|
||||||
|
if (m_forwardedPorts.contains(port))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (isEnabled())
|
||||||
|
portMapping.insert(port, m_provider->add_port_mapping(lt::session::tcp, port, port));
|
||||||
|
else
|
||||||
|
portMapping.insert(port, {});
|
||||||
|
m_forwardedPorts.insert(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_mappedPorts.erase(iter);
|
if (portMapping.isEmpty())
|
||||||
|
m_portProfiles.remove(profile);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PortForwarderImpl::removePorts(const QString &profile)
|
||||||
|
{
|
||||||
|
setPorts(profile, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void PortForwarderImpl::start()
|
void PortForwarderImpl::start()
|
||||||
{
|
{
|
||||||
lt::settings_pack settingsPack = m_provider->get_settings();
|
lt::settings_pack settingsPack;
|
||||||
settingsPack.set_bool(lt::settings_pack::enable_upnp, true);
|
settingsPack.set_bool(lt::settings_pack::enable_upnp, true);
|
||||||
settingsPack.set_bool(lt::settings_pack::enable_natpmp, true);
|
settingsPack.set_bool(lt::settings_pack::enable_natpmp, true);
|
||||||
m_provider->apply_settings(settingsPack);
|
m_provider->apply_settings(std::move(settingsPack));
|
||||||
|
|
||||||
for (auto iter = m_mappedPorts.begin(); iter != m_mappedPorts.end(); ++iter)
|
for (auto profileIter = m_portProfiles.begin(); profileIter != m_portProfiles.end(); ++profileIter)
|
||||||
{
|
{
|
||||||
Q_ASSERT(iter.value().empty());
|
PortMapping &portMapping = profileIter.value();
|
||||||
|
for (auto iter = portMapping.begin(); iter != portMapping.end(); ++iter)
|
||||||
|
{
|
||||||
|
Q_ASSERT(iter.value().empty());
|
||||||
|
|
||||||
const quint16 port = iter.key();
|
const quint16 port = iter.key();
|
||||||
iter.value() = m_provider->add_port_mapping(lt::session::tcp, port, port);
|
iter.value() = m_provider->add_port_mapping(lt::session::tcp, port, port);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LogMsg(tr("UPnP/NAT-PMP support: ON"), Log::INFO);
|
LogMsg(tr("UPnP/NAT-PMP support: ON"), Log::INFO);
|
||||||
|
@ -109,14 +129,18 @@ void PortForwarderImpl::start()
|
||||||
|
|
||||||
void PortForwarderImpl::stop()
|
void PortForwarderImpl::stop()
|
||||||
{
|
{
|
||||||
lt::settings_pack settingsPack = m_provider->get_settings();
|
lt::settings_pack settingsPack;
|
||||||
settingsPack.set_bool(lt::settings_pack::enable_upnp, false);
|
settingsPack.set_bool(lt::settings_pack::enable_upnp, false);
|
||||||
settingsPack.set_bool(lt::settings_pack::enable_natpmp, false);
|
settingsPack.set_bool(lt::settings_pack::enable_natpmp, false);
|
||||||
m_provider->apply_settings(settingsPack);
|
m_provider->apply_settings(std::move(settingsPack));
|
||||||
|
|
||||||
// don't clear m_mappedPorts so a later `start()` call can restore the port forwarding
|
// don't clear m_portProfiles so a later `start()` call can restore the port forwardings
|
||||||
for (auto iter = m_mappedPorts.begin(); iter != m_mappedPorts.end(); ++iter)
|
for (auto profileIter = m_portProfiles.begin(); profileIter != m_portProfiles.end(); ++profileIter)
|
||||||
iter.value().clear();
|
{
|
||||||
|
PortMapping &portMapping = profileIter.value();
|
||||||
|
for (auto iter = portMapping.begin(); iter != portMapping.end(); ++iter)
|
||||||
|
iter.value().clear();
|
||||||
|
}
|
||||||
|
|
||||||
LogMsg(tr("UPnP/NAT-PMP support: OFF"), Log::INFO);
|
LogMsg(tr("UPnP/NAT-PMP support: OFF"), Log::INFO);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include <libtorrent/portmap.hpp>
|
#include <libtorrent/portmap.hpp>
|
||||||
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
|
#include <QSet>
|
||||||
|
|
||||||
#include "base/net/portforwarder.h"
|
#include "base/net/portforwarder.h"
|
||||||
#include "base/settingvalue.h"
|
#include "base/settingvalue.h"
|
||||||
|
@ -50,8 +51,8 @@ public:
|
||||||
bool isEnabled() const override;
|
bool isEnabled() const override;
|
||||||
void setEnabled(bool enabled) override;
|
void setEnabled(bool enabled) override;
|
||||||
|
|
||||||
void addPort(quint16 port) override;
|
void setPorts(const QString &profile, QSet<quint16> ports) override;
|
||||||
void deletePort(quint16 port) override;
|
void removePorts(const QString &profile) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void start();
|
void start();
|
||||||
|
@ -59,5 +60,8 @@ private:
|
||||||
|
|
||||||
CachedSettingValue<bool> m_storeActive;
|
CachedSettingValue<bool> m_storeActive;
|
||||||
lt::session *const m_provider = nullptr;
|
lt::session *const m_provider = nullptr;
|
||||||
QHash<quint16, std::vector<lt::port_mapping_t>> m_mappedPorts;
|
|
||||||
|
using PortMapping = QHash<quint16, std::vector<lt::port_mapping_t>>; // <port, handles>
|
||||||
|
QHash<QString, PortMapping> m_portProfiles;
|
||||||
|
QSet<quint16> m_forwardedPorts;
|
||||||
};
|
};
|
||||||
|
|
|
@ -80,6 +80,7 @@
|
||||||
#include "base/logger.h"
|
#include "base/logger.h"
|
||||||
#include "base/net/downloadmanager.h"
|
#include "base/net/downloadmanager.h"
|
||||||
#include "base/net/proxyconfigurationmanager.h"
|
#include "base/net/proxyconfigurationmanager.h"
|
||||||
|
#include "base/preferences.h"
|
||||||
#include "base/profile.h"
|
#include "base/profile.h"
|
||||||
#include "base/torrentfileguard.h"
|
#include "base/torrentfileguard.h"
|
||||||
#include "base/torrentfilter.h"
|
#include "base/torrentfilter.h"
|
||||||
|
@ -548,8 +549,6 @@ SessionImpl::SessionImpl(QObject *parent)
|
||||||
if (isExcludedFileNamesEnabled())
|
if (isExcludedFileNamesEnabled())
|
||||||
populateExcludedFileNamesRegExpList();
|
populateExcludedFileNamesRegExpList();
|
||||||
|
|
||||||
enableTracker(isTrackerEnabled());
|
|
||||||
|
|
||||||
connect(Net::ProxyConfigurationManager::instance()
|
connect(Net::ProxyConfigurationManager::instance()
|
||||||
, &Net::ProxyConfigurationManager::proxyConfigurationChanged
|
, &Net::ProxyConfigurationManager::proxyConfigurationChanged
|
||||||
, this, &SessionImpl::configureDeferred);
|
, this, &SessionImpl::configureDeferred);
|
||||||
|
@ -569,11 +568,14 @@ SessionImpl::SessionImpl(QObject *parent)
|
||||||
|
|
||||||
m_ioThread->start();
|
m_ioThread->start();
|
||||||
|
|
||||||
|
initMetrics();
|
||||||
|
loadStatistics();
|
||||||
|
|
||||||
// initialize PortForwarder instance
|
// initialize PortForwarder instance
|
||||||
new PortForwarderImpl(m_nativeSession);
|
new PortForwarderImpl(m_nativeSession);
|
||||||
|
|
||||||
initMetrics();
|
// start embedded tracker
|
||||||
loadStatistics();
|
enableTracker(isTrackerEnabled());
|
||||||
|
|
||||||
prepareStartup();
|
prepareStartup();
|
||||||
}
|
}
|
||||||
|
@ -1055,25 +1057,25 @@ void SessionImpl::adjustLimits()
|
||||||
{
|
{
|
||||||
if (isQueueingSystemEnabled())
|
if (isQueueingSystemEnabled())
|
||||||
{
|
{
|
||||||
lt::settings_pack settingsPack = m_nativeSession->get_settings();
|
lt::settings_pack settingsPack;
|
||||||
adjustLimits(settingsPack);
|
// Internally increase the queue limits to ensure that the magnet is started
|
||||||
m_nativeSession->apply_settings(settingsPack);
|
settingsPack.set_int(lt::settings_pack::active_downloads, adjustLimit(maxActiveDownloads()));
|
||||||
|
settingsPack.set_int(lt::settings_pack::active_limit, adjustLimit(maxActiveTorrents()));
|
||||||
|
m_nativeSession->apply_settings(std::move(settingsPack));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::applyBandwidthLimits()
|
void SessionImpl::applyBandwidthLimits()
|
||||||
{
|
{
|
||||||
lt::settings_pack settingsPack = m_nativeSession->get_settings();
|
lt::settings_pack settingsPack;
|
||||||
applyBandwidthLimits(settingsPack);
|
settingsPack.set_int(lt::settings_pack::download_rate_limit, downloadSpeedLimit());
|
||||||
m_nativeSession->apply_settings(settingsPack);
|
settingsPack.set_int(lt::settings_pack::upload_rate_limit, uploadSpeedLimit());
|
||||||
|
m_nativeSession->apply_settings(std::move(settingsPack));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::configure()
|
void SessionImpl::configure()
|
||||||
{
|
{
|
||||||
lt::settings_pack settingsPack = m_nativeSession->get_settings();
|
m_nativeSession->apply_settings(loadLTSettings());
|
||||||
loadLTSettings(settingsPack);
|
|
||||||
m_nativeSession->apply_settings(settingsPack);
|
|
||||||
|
|
||||||
configureComponents();
|
configureComponents();
|
||||||
|
|
||||||
m_deferredConfigureScheduled = false;
|
m_deferredConfigureScheduled = false;
|
||||||
|
@ -1424,10 +1426,11 @@ void SessionImpl::endStartup(ResumeSessionContext *context)
|
||||||
|
|
||||||
void SessionImpl::initializeNativeSession()
|
void SessionImpl::initializeNativeSession()
|
||||||
{
|
{
|
||||||
const std::string peerId = lt::generate_fingerprint(PEER_ID, QBT_VERSION_MAJOR, QBT_VERSION_MINOR, QBT_VERSION_BUGFIX, QBT_VERSION_BUILD);
|
lt::settings_pack pack = loadLTSettings();
|
||||||
|
|
||||||
lt::settings_pack pack;
|
const std::string peerId = lt::generate_fingerprint(PEER_ID, QBT_VERSION_MAJOR, QBT_VERSION_MINOR, QBT_VERSION_BUGFIX, QBT_VERSION_BUILD);
|
||||||
pack.set_str(lt::settings_pack::peer_fingerprint, peerId);
|
pack.set_str(lt::settings_pack::peer_fingerprint, peerId);
|
||||||
|
|
||||||
pack.set_bool(lt::settings_pack::listen_system_port_fallback, false);
|
pack.set_bool(lt::settings_pack::listen_system_port_fallback, false);
|
||||||
pack.set_str(lt::settings_pack::user_agent, USER_AGENT.toStdString());
|
pack.set_str(lt::settings_pack::user_agent, USER_AGENT.toStdString());
|
||||||
pack.set_bool(lt::settings_pack::use_dht_as_fallback, false);
|
pack.set_bool(lt::settings_pack::use_dht_as_fallback, false);
|
||||||
|
@ -1444,8 +1447,7 @@ void SessionImpl::initializeNativeSession()
|
||||||
pack.set_bool(lt::settings_pack::enable_set_file_valid_data, true);
|
pack.set_bool(lt::settings_pack::enable_set_file_valid_data, true);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
loadLTSettings(pack);
|
lt::session_params sessionParams {std::move(pack), {}};
|
||||||
lt::session_params sessionParams {pack, {}};
|
|
||||||
#ifdef QBT_USES_LIBTORRENT2
|
#ifdef QBT_USES_LIBTORRENT2
|
||||||
switch (diskIOType())
|
switch (diskIOType())
|
||||||
{
|
{
|
||||||
|
@ -1503,28 +1505,14 @@ void SessionImpl::processBannedIPs(lt::ip_filter &filter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::adjustLimits(lt::settings_pack &settingsPack) const
|
int SessionImpl::adjustLimit(const int limit) const
|
||||||
{
|
{
|
||||||
// Internally increase the queue limits to ensure that the magnet is started
|
if (limit <= -1)
|
||||||
const auto adjustLimit = [this](const int limit) -> int
|
return limit;
|
||||||
{
|
// check for overflow: (limit + m_extraLimit) < std::numeric_limits<int>::max()
|
||||||
if (limit <= -1)
|
return (m_extraLimit < (std::numeric_limits<int>::max() - limit))
|
||||||
return limit;
|
? (limit + m_extraLimit)
|
||||||
// check for overflow: (limit + m_extraLimit) < std::numeric_limits<int>::max()
|
: std::numeric_limits<int>::max();
|
||||||
return (m_extraLimit < (std::numeric_limits<int>::max() - limit))
|
|
||||||
? (limit + m_extraLimit)
|
|
||||||
: std::numeric_limits<int>::max();
|
|
||||||
};
|
|
||||||
|
|
||||||
settingsPack.set_int(lt::settings_pack::active_downloads, adjustLimit(maxActiveDownloads()));
|
|
||||||
settingsPack.set_int(lt::settings_pack::active_limit, adjustLimit(maxActiveTorrents()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SessionImpl::applyBandwidthLimits(lt::settings_pack &settingsPack) const
|
|
||||||
{
|
|
||||||
const bool altSpeedLimitEnabled = isAltGlobalSpeedLimitEnabled();
|
|
||||||
settingsPack.set_int(lt::settings_pack::download_rate_limit, altSpeedLimitEnabled ? altGlobalDownloadSpeedLimit() : globalDownloadSpeedLimit());
|
|
||||||
settingsPack.set_int(lt::settings_pack::upload_rate_limit, altSpeedLimitEnabled ? altGlobalUploadSpeedLimit() : globalUploadSpeedLimit());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::initMetrics()
|
void SessionImpl::initMetrics()
|
||||||
|
@ -1569,8 +1557,10 @@ void SessionImpl::initMetrics()
|
||||||
m_metricIndices.disk.diskJobTime = findMetricIndex("disk.disk_job_time");
|
m_metricIndices.disk.diskJobTime = findMetricIndex("disk.disk_job_time");
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::loadLTSettings(lt::settings_pack &settingsPack)
|
lt::settings_pack SessionImpl::loadLTSettings() const
|
||||||
{
|
{
|
||||||
|
lt::settings_pack settingsPack;
|
||||||
|
|
||||||
const lt::alert_category_t alertMask = lt::alert::error_notification
|
const lt::alert_category_t alertMask = lt::alert::error_notification
|
||||||
| lt::alert::file_progress_notification
|
| lt::alert::file_progress_notification
|
||||||
| lt::alert::ip_block_notification
|
| lt::alert::ip_block_notification
|
||||||
|
@ -1588,8 +1578,10 @@ void SessionImpl::loadLTSettings(lt::settings_pack &settingsPack)
|
||||||
// It will not take affect until the listen_interfaces settings is updated
|
// It will not take affect until the listen_interfaces settings is updated
|
||||||
settingsPack.set_int(lt::settings_pack::listen_queue_size, socketBacklogSize());
|
settingsPack.set_int(lt::settings_pack::listen_queue_size, socketBacklogSize());
|
||||||
|
|
||||||
configureNetworkInterfaces(settingsPack);
|
applyNetworkInterfacesSettings(settingsPack);
|
||||||
applyBandwidthLimits(settingsPack);
|
|
||||||
|
settingsPack.set_int(lt::settings_pack::download_rate_limit, downloadSpeedLimit());
|
||||||
|
settingsPack.set_int(lt::settings_pack::upload_rate_limit, uploadSpeedLimit());
|
||||||
|
|
||||||
// The most secure, rc4 only so that all streams are encrypted
|
// The most secure, rc4 only so that all streams are encrypted
|
||||||
settingsPack.set_int(lt::settings_pack::allowed_enc_level, lt::settings_pack::pe_rc4);
|
settingsPack.set_int(lt::settings_pack::allowed_enc_level, lt::settings_pack::pe_rc4);
|
||||||
|
@ -1724,7 +1716,9 @@ void SessionImpl::loadLTSettings(lt::settings_pack &settingsPack)
|
||||||
// Queueing System
|
// Queueing System
|
||||||
if (isQueueingSystemEnabled())
|
if (isQueueingSystemEnabled())
|
||||||
{
|
{
|
||||||
adjustLimits(settingsPack);
|
// Internally increase the queue limits to ensure that the magnet is started
|
||||||
|
settingsPack.set_int(lt::settings_pack::active_downloads, adjustLimit(maxActiveDownloads()));
|
||||||
|
settingsPack.set_int(lt::settings_pack::active_limit, adjustLimit(maxActiveTorrents()));
|
||||||
|
|
||||||
settingsPack.set_int(lt::settings_pack::active_seeds, maxActiveUploads());
|
settingsPack.set_int(lt::settings_pack::active_seeds, maxActiveUploads());
|
||||||
settingsPack.set_bool(lt::settings_pack::dont_count_slow_torrents, ignoreSlowTorrentsForQueueing());
|
settingsPack.set_bool(lt::settings_pack::dont_count_slow_torrents, ignoreSlowTorrentsForQueueing());
|
||||||
|
@ -1840,9 +1834,11 @@ void SessionImpl::loadLTSettings(lt::settings_pack &settingsPack)
|
||||||
settingsPack.set_int(lt::settings_pack::seed_choking_algorithm, lt::settings_pack::anti_leech);
|
settingsPack.set_int(lt::settings_pack::seed_choking_algorithm, lt::settings_pack::anti_leech);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return settingsPack;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionImpl::configureNetworkInterfaces(lt::settings_pack &settingsPack)
|
void SessionImpl::applyNetworkInterfacesSettings(lt::settings_pack &settingsPack) const
|
||||||
{
|
{
|
||||||
if (m_listenInterfaceConfigured)
|
if (m_listenInterfaceConfigured)
|
||||||
return;
|
return;
|
||||||
|
@ -1985,16 +1981,27 @@ void SessionImpl::configurePeerClasses()
|
||||||
|
|
||||||
void SessionImpl::enableTracker(const bool enable)
|
void SessionImpl::enableTracker(const bool enable)
|
||||||
{
|
{
|
||||||
|
const QString profile = u"embeddedTracker"_qs;
|
||||||
|
auto *portForwarder = Net::PortForwarder::instance();
|
||||||
|
|
||||||
if (enable)
|
if (enable)
|
||||||
{
|
{
|
||||||
if (!m_tracker)
|
if (!m_tracker)
|
||||||
m_tracker = new Tracker(this);
|
m_tracker = new Tracker(this);
|
||||||
|
|
||||||
m_tracker->start();
|
m_tracker->start();
|
||||||
|
|
||||||
|
const auto *pref = Preferences::instance();
|
||||||
|
if (pref->isTrackerPortForwardingEnabled())
|
||||||
|
portForwarder->setPorts(profile, {static_cast<quint16>(pref->getTrackerPort())});
|
||||||
|
else
|
||||||
|
portForwarder->removePorts(profile);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
delete m_tracker;
|
delete m_tracker;
|
||||||
|
|
||||||
|
portForwarder->removePorts(profile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2758,9 +2765,6 @@ bool SessionImpl::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &so
|
||||||
|
|
||||||
p.flags |= lt::torrent_flags::duplicate_is_error;
|
p.flags |= lt::torrent_flags::duplicate_is_error;
|
||||||
|
|
||||||
// Prevent torrent from saving initial resume data twice
|
|
||||||
p.flags &= ~lt::torrent_flags::need_save_resume;
|
|
||||||
|
|
||||||
p.added_time = std::time(nullptr);
|
p.added_time = std::time(nullptr);
|
||||||
|
|
||||||
// Limits
|
// Limits
|
||||||
|
@ -2903,10 +2907,8 @@ void SessionImpl::saveResumeData()
|
||||||
saveTorrentsQueue();
|
saveTorrentsQueue();
|
||||||
|
|
||||||
for (const TorrentImpl *torrent : asConst(m_torrents))
|
for (const TorrentImpl *torrent : asConst(m_torrents))
|
||||||
{
|
|
||||||
torrent->nativeHandle().save_resume_data(lt::torrent_handle::only_if_modified);
|
torrent->nativeHandle().save_resume_data(lt::torrent_handle::only_if_modified);
|
||||||
++m_numResumeData;
|
m_numResumeData += m_torrents.size();
|
||||||
}
|
|
||||||
|
|
||||||
QElapsedTimer timer;
|
QElapsedTimer timer;
|
||||||
timer.start();
|
timer.start();
|
||||||
|
@ -5212,10 +5214,8 @@ TorrentImpl *SessionImpl::createTorrent(const lt::torrent_handle &nativeHandle,
|
||||||
if (const InfoHash infoHash = torrent->infoHash(); infoHash.isHybrid())
|
if (const InfoHash infoHash = torrent->infoHash(); infoHash.isHybrid())
|
||||||
m_hybridTorrentsByAltID.insert(TorrentID::fromSHA1Hash(infoHash.v1()), torrent);
|
m_hybridTorrentsByAltID.insert(TorrentID::fromSHA1Hash(infoHash.v1()), torrent);
|
||||||
|
|
||||||
if (!params.restored)
|
if (isRestored())
|
||||||
{
|
{
|
||||||
m_resumeDataStorage->store(torrent->id(), params);
|
|
||||||
|
|
||||||
// The following is useless for newly added magnet
|
// The following is useless for newly added magnet
|
||||||
if (torrent->hasMetadata())
|
if (torrent->hasMetadata())
|
||||||
{
|
{
|
||||||
|
@ -5230,7 +5230,7 @@ TorrentImpl *SessionImpl::createTorrent(const lt::torrent_handle &nativeHandle,
|
||||||
m_seedingLimitTimer->start();
|
m_seedingLimitTimer->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.restored)
|
if (!isRestored())
|
||||||
{
|
{
|
||||||
LogMsg(tr("Restored torrent. Torrent: \"%1\"").arg(torrent->name()));
|
LogMsg(tr("Restored torrent. Torrent: \"%1\"").arg(torrent->name()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -474,11 +474,10 @@ namespace BitTorrent
|
||||||
Q_INVOKABLE void configure();
|
Q_INVOKABLE void configure();
|
||||||
void configureComponents();
|
void configureComponents();
|
||||||
void initializeNativeSession();
|
void initializeNativeSession();
|
||||||
void loadLTSettings(lt::settings_pack &settingsPack);
|
lt::settings_pack loadLTSettings() const;
|
||||||
void configureNetworkInterfaces(lt::settings_pack &settingsPack);
|
void applyNetworkInterfacesSettings(lt::settings_pack &settingsPack) const;
|
||||||
void configurePeerClasses();
|
void configurePeerClasses();
|
||||||
void adjustLimits(lt::settings_pack &settingsPack) const;
|
int adjustLimit(int limit) const;
|
||||||
void applyBandwidthLimits(lt::settings_pack &settingsPack) const;
|
|
||||||
void initMetrics();
|
void initMetrics();
|
||||||
void adjustLimits();
|
void adjustLimits();
|
||||||
void applyBandwidthLimits();
|
void applyBandwidthLimits();
|
||||||
|
@ -553,7 +552,7 @@ namespace BitTorrent
|
||||||
|
|
||||||
bool m_deferredConfigureScheduled = false;
|
bool m_deferredConfigureScheduled = false;
|
||||||
bool m_IPFilteringConfigured = false;
|
bool m_IPFilteringConfigured = false;
|
||||||
bool m_listenInterfaceConfigured = false;
|
mutable bool m_listenInterfaceConfigured = false;
|
||||||
|
|
||||||
CachedSettingValue<bool> m_isDHTEnabled;
|
CachedSettingValue<bool> m_isDHTEnabled;
|
||||||
CachedSettingValue<bool> m_isLSDEnabled;
|
CachedSettingValue<bool> m_isLSDEnabled;
|
||||||
|
|
|
@ -203,12 +203,12 @@ Tracker::Tracker(QObject *parent)
|
||||||
|
|
||||||
bool Tracker::start()
|
bool Tracker::start()
|
||||||
{
|
{
|
||||||
const QHostAddress ip = QHostAddress::Any;
|
|
||||||
const int port = Preferences::instance()->getTrackerPort();
|
const int port = Preferences::instance()->getTrackerPort();
|
||||||
|
|
||||||
if (m_server->isListening())
|
if (m_server->isListening())
|
||||||
{
|
{
|
||||||
if (m_server->serverPort() == port)
|
if (const int oldPort = m_server->serverPort()
|
||||||
|
; oldPort == port)
|
||||||
{
|
{
|
||||||
// Already listening on the right port, just return
|
// Already listening on the right port, just return
|
||||||
return true;
|
return true;
|
||||||
|
@ -218,9 +218,9 @@ bool Tracker::start()
|
||||||
m_server->close();
|
m_server->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Listen on the predefined port
|
// Listen on port
|
||||||
|
const QHostAddress ip = QHostAddress::Any;
|
||||||
const bool listenSuccess = m_server->listen(ip, port);
|
const bool listenSuccess = m_server->listen(ip, port);
|
||||||
|
|
||||||
if (listenSuccess)
|
if (listenSuccess)
|
||||||
{
|
{
|
||||||
LogMsg(tr("Embedded Tracker: Now listening on IP: %1, port: %2")
|
LogMsg(tr("Embedded Tracker: Now listening on IP: %1, port: %2")
|
||||||
|
|
|
@ -124,7 +124,7 @@ namespace
|
||||||
// Spoof HTTP Referer to allow adding torrent link from Torcache/KickAssTorrents
|
// Spoof HTTP Referer to allow adding torrent link from Torcache/KickAssTorrents
|
||||||
request.setRawHeader("Referer", request.url().toEncoded().data());
|
request.setRawHeader("Referer", request.url().toEncoded().data());
|
||||||
#ifdef QT_NO_COMPRESS
|
#ifdef QT_NO_COMPRESS
|
||||||
// The macro "QT_NO_COMPRESS" defined in QT will disable the zlib releated features
|
// The macro "QT_NO_COMPRESS" defined in QT will disable the zlib related features
|
||||||
// and reply data auto-decompression in QT will also be disabled. But we can support
|
// and reply data auto-decompression in QT will also be disabled. But we can support
|
||||||
// gzip encoding and manually decompress the reply data.
|
// gzip encoding and manually decompress the reply data.
|
||||||
request.setRawHeader("Accept-Encoding", "gzip");
|
request.setRawHeader("Accept-Encoding", "gzip");
|
||||||
|
|
|
@ -29,6 +29,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QSet>
|
||||||
|
|
||||||
|
class QString;
|
||||||
|
|
||||||
namespace Net
|
namespace Net
|
||||||
{
|
{
|
||||||
|
@ -45,8 +48,8 @@ namespace Net
|
||||||
virtual bool isEnabled() const = 0;
|
virtual bool isEnabled() const = 0;
|
||||||
virtual void setEnabled(bool enabled) = 0;
|
virtual void setEnabled(bool enabled) = 0;
|
||||||
|
|
||||||
virtual void addPort(quint16 port) = 0;
|
virtual void setPorts(const QString &profile, QSet<quint16> ports) = 0;
|
||||||
virtual void deletePort(quint16 port) = 0;
|
virtual void removePorts(const QString &profile) = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static PortForwarder *m_instance;
|
static PortForwarder *m_instance;
|
||||||
|
|
|
@ -1164,6 +1164,16 @@ void Preferences::setTrackerPort(const int port)
|
||||||
setValue(u"Preferences/Advanced/trackerPort"_qs, port);
|
setValue(u"Preferences/Advanced/trackerPort"_qs, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Preferences::isTrackerPortForwardingEnabled() const
|
||||||
|
{
|
||||||
|
return value(u"Preferences/Advanced/trackerPortForwarding"_qs, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Preferences::setTrackerPortForwardingEnabled(const bool enabled)
|
||||||
|
{
|
||||||
|
setValue(u"Preferences/Advanced/trackerPortForwarding"_qs, enabled);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
|
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
|
||||||
bool Preferences::isUpdateCheckEnabled() const
|
bool Preferences::isUpdateCheckEnabled() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -299,6 +299,8 @@ public:
|
||||||
#endif
|
#endif
|
||||||
int getTrackerPort() const;
|
int getTrackerPort() const;
|
||||||
void setTrackerPort(int port);
|
void setTrackerPort(int port);
|
||||||
|
bool isTrackerPortForwardingEnabled() const;
|
||||||
|
void setTrackerPortForwardingEnabled(bool enabled);
|
||||||
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
|
#if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
|
||||||
bool isUpdateCheckEnabled() const;
|
bool isUpdateCheckEnabled() const;
|
||||||
void setUpdateCheckEnabled(bool enabled);
|
void setUpdateCheckEnabled(bool enabled);
|
||||||
|
|
|
@ -97,6 +97,7 @@ namespace
|
||||||
// embedded tracker
|
// embedded tracker
|
||||||
TRACKER_STATUS,
|
TRACKER_STATUS,
|
||||||
TRACKER_PORT,
|
TRACKER_PORT,
|
||||||
|
TRACKER_PORT_FORWARDING,
|
||||||
// libtorrent section
|
// libtorrent section
|
||||||
LIBTORRENT_HEADER,
|
LIBTORRENT_HEADER,
|
||||||
ASYNC_IO_THREADS,
|
ASYNC_IO_THREADS,
|
||||||
|
@ -292,7 +293,9 @@ void AdvancedSettings::saveAdvancedSettings() const
|
||||||
|
|
||||||
// Tracker
|
// Tracker
|
||||||
pref->setTrackerPort(m_spinBoxTrackerPort.value());
|
pref->setTrackerPort(m_spinBoxTrackerPort.value());
|
||||||
|
pref->setTrackerPortForwardingEnabled(m_checkBoxTrackerPortForwarding.isChecked());
|
||||||
session->setTrackerEnabled(m_checkBoxTrackerStatus.isChecked());
|
session->setTrackerEnabled(m_checkBoxTrackerStatus.isChecked());
|
||||||
|
|
||||||
// Choking algorithm
|
// Choking algorithm
|
||||||
session->setChokingAlgorithm(m_comboBoxChokingAlgorithm.currentData().value<BitTorrent::ChokingAlgorithm>());
|
session->setChokingAlgorithm(m_comboBoxChokingAlgorithm.currentData().value<BitTorrent::ChokingAlgorithm>());
|
||||||
// Seed choking algorithm
|
// Seed choking algorithm
|
||||||
|
@ -732,6 +735,9 @@ void AdvancedSettings::loadAdvancedSettings()
|
||||||
m_spinBoxTrackerPort.setMaximum(65535);
|
m_spinBoxTrackerPort.setMaximum(65535);
|
||||||
m_spinBoxTrackerPort.setValue(pref->getTrackerPort());
|
m_spinBoxTrackerPort.setValue(pref->getTrackerPort());
|
||||||
addRow(TRACKER_PORT, tr("Embedded tracker port"), &m_spinBoxTrackerPort);
|
addRow(TRACKER_PORT, tr("Embedded tracker port"), &m_spinBoxTrackerPort);
|
||||||
|
// Tracker port forwarding
|
||||||
|
m_checkBoxTrackerPortForwarding.setChecked(pref->isTrackerPortForwardingEnabled());
|
||||||
|
addRow(TRACKER_PORT_FORWARDING, tr("Enable port forwarding for embedded tracker"), &m_checkBoxTrackerPortForwarding);
|
||||||
// Choking algorithm
|
// Choking algorithm
|
||||||
m_comboBoxChokingAlgorithm.addItem(tr("Fixed slots"), QVariant::fromValue(BitTorrent::ChokingAlgorithm::FixedSlots));
|
m_comboBoxChokingAlgorithm.addItem(tr("Fixed slots"), QVariant::fromValue(BitTorrent::ChokingAlgorithm::FixedSlots));
|
||||||
m_comboBoxChokingAlgorithm.addItem(tr("Upload rate based"), QVariant::fromValue(BitTorrent::ChokingAlgorithm::RateBased));
|
m_comboBoxChokingAlgorithm.addItem(tr("Upload rate based"), QVariant::fromValue(BitTorrent::ChokingAlgorithm::RateBased));
|
||||||
|
|
|
@ -68,7 +68,7 @@ private:
|
||||||
m_spinBoxSavePathHistoryLength, m_spinBoxPeerTurnover, m_spinBoxPeerTurnoverCutoff, m_spinBoxPeerTurnoverInterval, m_spinBoxRequestQueueSize;
|
m_spinBoxSavePathHistoryLength, m_spinBoxPeerTurnover, m_spinBoxPeerTurnoverCutoff, m_spinBoxPeerTurnoverInterval, m_spinBoxRequestQueueSize;
|
||||||
QCheckBox m_checkBoxOsCache, m_checkBoxRecheckCompleted, m_checkBoxResolveCountries, m_checkBoxResolveHosts,
|
QCheckBox m_checkBoxOsCache, m_checkBoxRecheckCompleted, m_checkBoxResolveCountries, m_checkBoxResolveHosts,
|
||||||
m_checkBoxProgramNotifications, m_checkBoxTorrentAddedNotifications, m_checkBoxReannounceWhenAddressChanged, m_checkBoxTrackerFavicon, m_checkBoxTrackerStatus,
|
m_checkBoxProgramNotifications, m_checkBoxTorrentAddedNotifications, m_checkBoxReannounceWhenAddressChanged, m_checkBoxTrackerFavicon, m_checkBoxTrackerStatus,
|
||||||
m_checkBoxConfirmTorrentRecheck, m_checkBoxConfirmRemoveAllTags, m_checkBoxAnnounceAllTrackers, m_checkBoxAnnounceAllTiers,
|
m_checkBoxTrackerPortForwarding, m_checkBoxConfirmTorrentRecheck, m_checkBoxConfirmRemoveAllTags, m_checkBoxAnnounceAllTrackers, m_checkBoxAnnounceAllTiers,
|
||||||
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_checkBoxSuggestMode, m_checkBoxSpeedWidgetEnabled, m_checkBoxIDNSupport;
|
||||||
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,
|
||||||
|
|
|
@ -89,7 +89,7 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
|
||||||
, m_properties(parent)
|
, m_properties(parent)
|
||||||
{
|
{
|
||||||
// Load settings
|
// Load settings
|
||||||
loadSettings();
|
const bool columnLoaded = loadSettings();
|
||||||
// Visual settings
|
// Visual settings
|
||||||
setUniformRowHeights(true);
|
setUniformRowHeights(true);
|
||||||
setRootIsDecorated(false);
|
setRootIsDecorated(false);
|
||||||
|
@ -109,6 +109,7 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
|
||||||
m_listModel->setHeaderData(PeerListColumns::FLAGS, Qt::Horizontal, tr("Flags"));
|
m_listModel->setHeaderData(PeerListColumns::FLAGS, Qt::Horizontal, tr("Flags"));
|
||||||
m_listModel->setHeaderData(PeerListColumns::CONNECTION, Qt::Horizontal, tr("Connection"));
|
m_listModel->setHeaderData(PeerListColumns::CONNECTION, Qt::Horizontal, tr("Connection"));
|
||||||
m_listModel->setHeaderData(PeerListColumns::CLIENT, Qt::Horizontal, tr("Client", "i.e.: Client application"));
|
m_listModel->setHeaderData(PeerListColumns::CLIENT, Qt::Horizontal, tr("Client", "i.e.: Client application"));
|
||||||
|
m_listModel->setHeaderData(PeerListColumns::PEERID_CLIENT, Qt::Horizontal, tr("Peer ID Client", "i.e.: Client resolved from Peer ID"));
|
||||||
m_listModel->setHeaderData(PeerListColumns::PROGRESS, Qt::Horizontal, tr("Progress", "i.e: % downloaded"));
|
m_listModel->setHeaderData(PeerListColumns::PROGRESS, Qt::Horizontal, tr("Progress", "i.e: % downloaded"));
|
||||||
m_listModel->setHeaderData(PeerListColumns::DOWN_SPEED, Qt::Horizontal, tr("Down Speed", "i.e: Download speed"));
|
m_listModel->setHeaderData(PeerListColumns::DOWN_SPEED, Qt::Horizontal, tr("Down Speed", "i.e: Download speed"));
|
||||||
m_listModel->setHeaderData(PeerListColumns::UP_SPEED, Qt::Horizontal, tr("Up Speed", "i.e: Upload speed"));
|
m_listModel->setHeaderData(PeerListColumns::UP_SPEED, Qt::Horizontal, tr("Up Speed", "i.e: Upload speed"));
|
||||||
|
@ -130,8 +131,16 @@ PeerListWidget::PeerListWidget(PropertiesWidget *parent)
|
||||||
m_proxyModel->setSourceModel(m_listModel);
|
m_proxyModel->setSourceModel(m_listModel);
|
||||||
m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
|
m_proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||||
setModel(m_proxyModel);
|
setModel(m_proxyModel);
|
||||||
|
|
||||||
hideColumn(PeerListColumns::IP_HIDDEN);
|
hideColumn(PeerListColumns::IP_HIDDEN);
|
||||||
hideColumn(PeerListColumns::COL_COUNT);
|
hideColumn(PeerListColumns::COL_COUNT);
|
||||||
|
|
||||||
|
// Default hidden columns
|
||||||
|
if (!columnLoaded)
|
||||||
|
{
|
||||||
|
hideColumn(PeerListColumns::PEERID_CLIENT);
|
||||||
|
}
|
||||||
|
|
||||||
m_resolveCountries = Preferences::instance()->resolvePeerCountries();
|
m_resolveCountries = Preferences::instance()->resolvePeerCountries();
|
||||||
if (!m_resolveCountries)
|
if (!m_resolveCountries)
|
||||||
hideColumn(PeerListColumns::COUNTRY);
|
hideColumn(PeerListColumns::COUNTRY);
|
||||||
|
@ -371,9 +380,9 @@ void PeerListWidget::clear()
|
||||||
m_listModel->removeRows(0, nbrows);
|
m_listModel->removeRows(0, nbrows);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerListWidget::loadSettings()
|
bool PeerListWidget::loadSettings()
|
||||||
{
|
{
|
||||||
header()->restoreState(Preferences::instance()->getPeerListState());
|
return header()->restoreState(Preferences::instance()->getPeerListState());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerListWidget::saveSettings() const
|
void PeerListWidget::saveSettings() const
|
||||||
|
@ -461,6 +470,8 @@ void PeerListWidget::updatePeer(const BitTorrent::Torrent *torrent, const BitTor
|
||||||
setModelData(row, PeerListColumns::FLAGS, peer.flags(), peer.flags(), {}, peer.flagsDescription());
|
setModelData(row, PeerListColumns::FLAGS, peer.flags(), peer.flags(), {}, peer.flagsDescription());
|
||||||
const QString client = peer.client().toHtmlEscaped();
|
const QString client = peer.client().toHtmlEscaped();
|
||||||
setModelData(row, PeerListColumns::CLIENT, client, client, {}, client);
|
setModelData(row, PeerListColumns::CLIENT, client, client, {}, client);
|
||||||
|
const QString peerIdClient = peer.peerIdClient().toHtmlEscaped();
|
||||||
|
setModelData(row, PeerListColumns::PEERID_CLIENT, peerIdClient, peerIdClient);
|
||||||
setModelData(row, PeerListColumns::PROGRESS, (Utils::String::fromDouble(peer.progress() * 100, 1) + u'%'), peer.progress(), intDataTextAlignment);
|
setModelData(row, PeerListColumns::PROGRESS, (Utils::String::fromDouble(peer.progress() * 100, 1) + u'%'), peer.progress(), intDataTextAlignment);
|
||||||
const QString downSpeed = (hideValues && (peer.payloadDownSpeed() <= 0)) ? QString {} : Utils::Misc::friendlyUnit(peer.payloadDownSpeed(), true);
|
const QString downSpeed = (hideValues && (peer.payloadDownSpeed() <= 0)) ? QString {} : Utils::Misc::friendlyUnit(peer.payloadDownSpeed(), true);
|
||||||
setModelData(row, PeerListColumns::DOWN_SPEED, downSpeed, peer.payloadDownSpeed(), intDataTextAlignment);
|
setModelData(row, PeerListColumns::DOWN_SPEED, downSpeed, peer.payloadDownSpeed(), intDataTextAlignment);
|
||||||
|
|
|
@ -66,6 +66,7 @@ public:
|
||||||
CONNECTION,
|
CONNECTION,
|
||||||
FLAGS,
|
FLAGS,
|
||||||
CLIENT,
|
CLIENT,
|
||||||
|
PEERID_CLIENT,
|
||||||
PROGRESS,
|
PROGRESS,
|
||||||
DOWN_SPEED,
|
DOWN_SPEED,
|
||||||
UP_SPEED,
|
UP_SPEED,
|
||||||
|
@ -87,7 +88,7 @@ public:
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void loadSettings();
|
bool loadSettings();
|
||||||
void saveSettings() const;
|
void saveSettings() const;
|
||||||
void displayColumnHeaderMenu();
|
void displayColumnHeaderMenu();
|
||||||
void showPeerListMenu();
|
void showPeerListMenu();
|
||||||
|
|
|
@ -48,7 +48,7 @@ StatusBar::StatusBar(QWidget *parent)
|
||||||
{
|
{
|
||||||
#ifndef Q_OS_MACOS
|
#ifndef Q_OS_MACOS
|
||||||
// Redefining global stylesheet breaks certain elements on mac like tabs.
|
// Redefining global stylesheet breaks certain elements on mac like tabs.
|
||||||
// Qt checks whether the stylesheet class inherts("QMacStyle") and this becomes false.
|
// Qt checks whether the stylesheet class inherits("QMacStyle") and this becomes false.
|
||||||
setStyleSheet(u"QStatusBar::item { border-width: 0; }"_qs);
|
setStyleSheet(u"QStatusBar::item { border-width: 0; }"_qs);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -221,11 +221,7 @@ void TorrentContentModel::updateFilesProgress(const QVector<qreal> &fp)
|
||||||
m_rootItem->recalculateProgress();
|
m_rootItem->recalculateProgress();
|
||||||
m_rootItem->recalculateAvailability();
|
m_rootItem->recalculateAvailability();
|
||||||
|
|
||||||
const QVector<ColumnInterval> columns =
|
emit layoutChanged();
|
||||||
{
|
|
||||||
{TorrentContentModelItem::COL_PROGRESS, TorrentContentModelItem::COL_PROGRESS}
|
|
||||||
};
|
|
||||||
notifySubtreeUpdated(index(0, 0), columns);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentContentModel::updateFilesPriorities(const QVector<BitTorrent::DownloadPriority> &fprio)
|
void TorrentContentModel::updateFilesPriorities(const QVector<BitTorrent::DownloadPriority> &fprio)
|
||||||
|
@ -239,12 +235,7 @@ void TorrentContentModel::updateFilesPriorities(const QVector<BitTorrent::Downlo
|
||||||
for (int i = 0; i < fprio.size(); ++i)
|
for (int i = 0; i < fprio.size(); ++i)
|
||||||
m_filesIndex[i]->setPriority(static_cast<BitTorrent::DownloadPriority>(fprio[i]));
|
m_filesIndex[i]->setPriority(static_cast<BitTorrent::DownloadPriority>(fprio[i]));
|
||||||
|
|
||||||
const QVector<ColumnInterval> columns =
|
emit layoutChanged();
|
||||||
{
|
|
||||||
{TorrentContentModelItem::COL_NAME, TorrentContentModelItem::COL_NAME},
|
|
||||||
{TorrentContentModelItem::COL_PRIO, TorrentContentModelItem::COL_PRIO}
|
|
||||||
};
|
|
||||||
notifySubtreeUpdated(index(0, 0), columns);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentContentModel::updateFilesAvailability(const QVector<qreal> &fa)
|
void TorrentContentModel::updateFilesAvailability(const QVector<qreal> &fa)
|
||||||
|
@ -259,11 +250,7 @@ void TorrentContentModel::updateFilesAvailability(const QVector<qreal> &fa)
|
||||||
// Update folders progress in the tree
|
// Update folders progress in the tree
|
||||||
m_rootItem->recalculateProgress();
|
m_rootItem->recalculateProgress();
|
||||||
|
|
||||||
const QVector<ColumnInterval> columns =
|
emit layoutChanged();
|
||||||
{
|
|
||||||
{TorrentContentModelItem::COL_AVAILABILITY, TorrentContentModelItem::COL_AVAILABILITY}
|
|
||||||
};
|
|
||||||
notifySubtreeUpdated(index(0, 0), columns);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<BitTorrent::DownloadPriority> TorrentContentModel::getFilePriorities() const
|
QVector<BitTorrent::DownloadPriority> TorrentContentModel::getFilePriorities() const
|
||||||
|
@ -308,17 +295,13 @@ bool TorrentContentModel::setData(const QModelIndex &index, const QVariant &valu
|
||||||
|
|
||||||
if (currentPrio != newPrio)
|
if (currentPrio != newPrio)
|
||||||
{
|
{
|
||||||
|
emit layoutAboutToBeChanged();
|
||||||
item->setPriority(newPrio);
|
item->setPriority(newPrio);
|
||||||
// Update folders progress in the tree
|
// Update folders progress in the tree
|
||||||
m_rootItem->recalculateProgress();
|
m_rootItem->recalculateProgress();
|
||||||
m_rootItem->recalculateAvailability();
|
m_rootItem->recalculateAvailability();
|
||||||
|
|
||||||
const QVector<ColumnInterval> columns =
|
emit layoutChanged();
|
||||||
{
|
|
||||||
{TorrentContentModelItem::COL_NAME, TorrentContentModelItem::COL_NAME},
|
|
||||||
{TorrentContentModelItem::COL_PRIO, TorrentContentModelItem::COL_PRIO}
|
|
||||||
};
|
|
||||||
notifySubtreeUpdated(index, columns);
|
|
||||||
emit filteredFilesChanged();
|
emit filteredFilesChanged();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -350,14 +333,9 @@ bool TorrentContentModel::setData(const QModelIndex &index, const QVariant &valu
|
||||||
const auto newPrio = static_cast<BitTorrent::DownloadPriority>(value.toInt());
|
const auto newPrio = static_cast<BitTorrent::DownloadPriority>(value.toInt());
|
||||||
if (currentPrio != newPrio)
|
if (currentPrio != newPrio)
|
||||||
{
|
{
|
||||||
|
emit layoutAboutToBeChanged();
|
||||||
item->setPriority(newPrio);
|
item->setPriority(newPrio);
|
||||||
|
emit layoutChanged();
|
||||||
const QVector<ColumnInterval> columns =
|
|
||||||
{
|
|
||||||
{TorrentContentModelItem::COL_NAME, TorrentContentModelItem::COL_NAME},
|
|
||||||
{TorrentContentModelItem::COL_PRIO, TorrentContentModelItem::COL_PRIO}
|
|
||||||
};
|
|
||||||
notifySubtreeUpdated(index, columns);
|
|
||||||
|
|
||||||
if ((newPrio == BitTorrent::DownloadPriority::Ignored)
|
if ((newPrio == BitTorrent::DownloadPriority::Ignored)
|
||||||
|| (currentPrio == BitTorrent::DownloadPriority::Ignored))
|
|| (currentPrio == BitTorrent::DownloadPriority::Ignored))
|
||||||
|
@ -541,7 +519,8 @@ void TorrentContentModel::setupModelData(const BitTorrent::AbstractFileStorage &
|
||||||
if (filesCount <= 0)
|
if (filesCount <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
emit layoutAboutToBeChanged();
|
beginResetModel();
|
||||||
|
|
||||||
// Initialize files_index array
|
// Initialize files_index array
|
||||||
qDebug("Torrent contains %d files", filesCount);
|
qDebug("Torrent contains %d files", filesCount);
|
||||||
m_filesIndex.reserve(filesCount);
|
m_filesIndex.reserve(filesCount);
|
||||||
|
@ -588,56 +567,6 @@ void TorrentContentModel::setupModelData(const BitTorrent::AbstractFileStorage &
|
||||||
lastParent->appendChild(fileItem);
|
lastParent->appendChild(fileItem);
|
||||||
m_filesIndex.push_back(fileItem);
|
m_filesIndex.push_back(fileItem);
|
||||||
}
|
}
|
||||||
emit layoutChanged();
|
|
||||||
}
|
endResetModel();
|
||||||
|
|
||||||
void TorrentContentModel::notifySubtreeUpdated(const QModelIndex &index, const QVector<ColumnInterval> &columns)
|
|
||||||
{
|
|
||||||
// For best performance, `columns` entries should be arranged from left to right
|
|
||||||
|
|
||||||
Q_ASSERT(index.isValid());
|
|
||||||
|
|
||||||
// emit itself
|
|
||||||
for (const ColumnInterval &column : columns)
|
|
||||||
emit dataChanged(index.siblingAtColumn(column.first()), index.siblingAtColumn(column.last()));
|
|
||||||
|
|
||||||
// propagate up the model
|
|
||||||
QModelIndex parentIndex = parent(index);
|
|
||||||
while (parentIndex.isValid())
|
|
||||||
{
|
|
||||||
for (const ColumnInterval &column : columns)
|
|
||||||
emit dataChanged(parentIndex.siblingAtColumn(column.first()), parentIndex.siblingAtColumn(column.last()));
|
|
||||||
parentIndex = parent(parentIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// propagate down the model
|
|
||||||
QVector<QModelIndex> parentIndexes;
|
|
||||||
|
|
||||||
if (hasChildren(index))
|
|
||||||
parentIndexes.push_back(index);
|
|
||||||
|
|
||||||
while (!parentIndexes.isEmpty())
|
|
||||||
{
|
|
||||||
const QModelIndex parent = parentIndexes.takeLast();
|
|
||||||
|
|
||||||
const int childCount = rowCount(parent);
|
|
||||||
const QModelIndex child = this->index(0, 0, parent);
|
|
||||||
|
|
||||||
// emit this generation
|
|
||||||
for (const ColumnInterval &column : columns)
|
|
||||||
{
|
|
||||||
const QModelIndex childTopLeft = child.siblingAtColumn(column.first());
|
|
||||||
const QModelIndex childBottomRight = child.sibling((childCount - 1), column.last());
|
|
||||||
emit dataChanged(childTopLeft, childBottomRight);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check generations further down
|
|
||||||
parentIndexes.reserve(childCount);
|
|
||||||
for (int i = 0; i < childCount; ++i)
|
|
||||||
{
|
|
||||||
const QModelIndex sibling = child.siblingAtRow(i);
|
|
||||||
if (hasChildren(sibling))
|
|
||||||
parentIndexes.push_back(sibling);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,10 +81,6 @@ signals:
|
||||||
void filteredFilesChanged();
|
void filteredFilesChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using ColumnInterval = IndexInterval<int>;
|
|
||||||
|
|
||||||
void notifySubtreeUpdated(const QModelIndex &index, const QVector<ColumnInterval> &columns);
|
|
||||||
|
|
||||||
TorrentContentModelFolder *m_rootItem = nullptr;
|
TorrentContentModelFolder *m_rootItem = nullptr;
|
||||||
QVector<TorrentContentModelFile *> m_filesIndex;
|
QVector<TorrentContentModelFile *> m_filesIndex;
|
||||||
QFileIconProvider *m_fileIconProvider = nullptr;
|
QFileIconProvider *m_fileIconProvider = nullptr;
|
||||||
|
|
|
@ -79,25 +79,19 @@ void TorrentContentTreeView::keyPressEvent(QKeyEvent *event)
|
||||||
|
|
||||||
event->accept();
|
event->accept();
|
||||||
|
|
||||||
QModelIndex current = currentNameCell();
|
const QVariant value = currentNameCell().data(Qt::CheckStateRole);
|
||||||
|
|
||||||
QVariant value = current.data(Qt::CheckStateRole);
|
|
||||||
if (!value.isValid())
|
if (!value.isValid())
|
||||||
{
|
{
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked
|
const Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked)
|
||||||
? Qt::Unchecked : Qt::Checked);
|
? Qt::Unchecked : Qt::Checked;
|
||||||
|
|
||||||
const QModelIndexList selection = selectionModel()->selectedRows(TorrentContentModelItem::COL_NAME);
|
const QModelIndexList selection = selectionModel()->selectedRows(TorrentContentModelItem::COL_NAME);
|
||||||
|
|
||||||
for (const QModelIndex &index : selection)
|
for (const QModelIndex &index : selection)
|
||||||
{
|
|
||||||
Q_ASSERT(index.column() == TorrentContentModelItem::COL_NAME);
|
|
||||||
model()->setData(index, state, Qt::CheckStateRole);
|
model()->setData(index, state, Qt::CheckStateRole);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentContentTreeView::renameSelectedFile(BitTorrent::AbstractFileStorage &fileStorage)
|
void TorrentContentTreeView::renameSelectedFile(BitTorrent::AbstractFileStorage &fileStorage)
|
||||||
|
@ -142,16 +136,16 @@ void TorrentContentTreeView::renameSelectedFile(BitTorrent::AbstractFileStorage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex TorrentContentTreeView::currentNameCell()
|
QModelIndex TorrentContentTreeView::currentNameCell() const
|
||||||
{
|
{
|
||||||
QModelIndex current = currentIndex();
|
const QModelIndex current = currentIndex();
|
||||||
if (!current.isValid())
|
if (!current.isValid())
|
||||||
{
|
{
|
||||||
Q_ASSERT(false);
|
Q_ASSERT(false);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return model()->index(current.row(), TorrentContentModelItem::COL_NAME, current.parent());
|
return current.siblingAtColumn(TorrentContentModelItem::COL_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorrentContentTreeView::wheelEvent(QWheelEvent *event)
|
void TorrentContentTreeView::wheelEvent(QWheelEvent *event)
|
||||||
|
|
|
@ -49,6 +49,6 @@ public:
|
||||||
void renameSelectedFile(BitTorrent::AbstractFileStorage &fileStorage);
|
void renameSelectedFile(BitTorrent::AbstractFileStorage &fileStorage);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QModelIndex currentNameCell();
|
QModelIndex currentNameCell() const;
|
||||||
void wheelEvent(QWheelEvent *event) override;
|
void wheelEvent(QWheelEvent *event) override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -632,9 +632,12 @@ bool TransferListModel::setData(const QModelIndex &index, const QVariant &value,
|
||||||
|
|
||||||
void TransferListModel::addTorrents(const QVector<BitTorrent::Torrent *> &torrents)
|
void TransferListModel::addTorrents(const QVector<BitTorrent::Torrent *> &torrents)
|
||||||
{
|
{
|
||||||
int row = m_torrentList.size();
|
qsizetype row = m_torrentList.size();
|
||||||
beginInsertRows({}, row, (row + torrents.size()));
|
const qsizetype total = row + torrents.size();
|
||||||
|
|
||||||
|
beginInsertRows({}, row, total);
|
||||||
|
|
||||||
|
m_torrentList.reserve(total);
|
||||||
for (BitTorrent::Torrent *torrent : torrents)
|
for (BitTorrent::Torrent *torrent : torrents)
|
||||||
{
|
{
|
||||||
Q_ASSERT(!m_torrentMap.contains(torrent));
|
Q_ASSERT(!m_torrentMap.contains(torrent));
|
||||||
|
|
|
@ -363,12 +363,30 @@ void TransferListWidget::setSelectedTorrentsLocation()
|
||||||
|
|
||||||
void TransferListWidget::pauseAllTorrents()
|
void TransferListWidget::pauseAllTorrents()
|
||||||
{
|
{
|
||||||
|
// Show confirmation if user would really like to Pause All
|
||||||
|
const QMessageBox::StandardButton ret =
|
||||||
|
QMessageBox::question(this, tr("Confirm pause")
|
||||||
|
, tr("Would you like to pause all torrents?")
|
||||||
|
, (QMessageBox::Yes | QMessageBox::No));
|
||||||
|
|
||||||
|
if (ret != QMessageBox::Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
for (BitTorrent::Torrent *const torrent : asConst(BitTorrent::Session::instance()->torrents()))
|
for (BitTorrent::Torrent *const torrent : asConst(BitTorrent::Session::instance()->torrents()))
|
||||||
torrent->pause();
|
torrent->pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransferListWidget::resumeAllTorrents()
|
void TransferListWidget::resumeAllTorrents()
|
||||||
{
|
{
|
||||||
|
// Show confirmation if user would really like to Resume All
|
||||||
|
const QMessageBox::StandardButton ret =
|
||||||
|
QMessageBox::question(this, tr("Confirm resume")
|
||||||
|
, tr("Would you like to resume all torrents?")
|
||||||
|
, (QMessageBox::Yes | QMessageBox::No));
|
||||||
|
|
||||||
|
if (ret != QMessageBox::Yes)
|
||||||
|
return;
|
||||||
|
|
||||||
for (BitTorrent::Torrent *const torrent : asConst(BitTorrent::Session::instance()->torrents()))
|
for (BitTorrent::Torrent *const torrent : asConst(BitTorrent::Session::instance()->torrents()))
|
||||||
torrent->resume();
|
torrent->resume();
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
<file>qbittorrent_uk.qm</file>
|
<file>qbittorrent_uk.qm</file>
|
||||||
<file>qbittorrent_uz@Latn.qm</file>
|
<file>qbittorrent_uz@Latn.qm</file>
|
||||||
<file>qbittorrent_vi.qm</file>
|
<file>qbittorrent_vi.qm</file>
|
||||||
<file>qbittorrent_zh.qm</file>
|
<file>qbittorrent_zh_CN.qm</file>
|
||||||
<file>qbittorrent_zh_HK.qm</file>
|
<file>qbittorrent_zh_HK.qm</file>
|
||||||
<file>qbittorrent_zh_TW.qm</file>
|
<file>qbittorrent_zh_TW.qm</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
|
|
|
@ -372,6 +372,7 @@ void AppController::preferencesAction()
|
||||||
// Embedded tracker
|
// Embedded tracker
|
||||||
data[u"enable_embedded_tracker"_qs] = session->isTrackerEnabled();
|
data[u"enable_embedded_tracker"_qs] = session->isTrackerEnabled();
|
||||||
data[u"embedded_tracker_port"_qs] = pref->getTrackerPort();
|
data[u"embedded_tracker_port"_qs] = pref->getTrackerPort();
|
||||||
|
data[u"embedded_tracker_port_forwarding"_qs] = pref->isTrackerPortForwardingEnabled();
|
||||||
// Choking algorithm
|
// Choking algorithm
|
||||||
data[u"upload_slots_behavior"_qs] = static_cast<int>(session->chokingAlgorithm());
|
data[u"upload_slots_behavior"_qs] = static_cast<int>(session->chokingAlgorithm());
|
||||||
// Seed choking algorithm
|
// Seed choking algorithm
|
||||||
|
@ -899,6 +900,8 @@ void AppController::setPreferencesAction()
|
||||||
// Embedded tracker
|
// Embedded tracker
|
||||||
if (hasKey(u"embedded_tracker_port"_qs))
|
if (hasKey(u"embedded_tracker_port"_qs))
|
||||||
pref->setTrackerPort(it.value().toInt());
|
pref->setTrackerPort(it.value().toInt());
|
||||||
|
if (hasKey(u"embedded_tracker_port_forwarding"_qs))
|
||||||
|
pref->setTrackerPortForwardingEnabled(it.value().toBool());
|
||||||
if (hasKey(u"enable_embedded_tracker"_qs))
|
if (hasKey(u"enable_embedded_tracker"_qs))
|
||||||
session->setTrackerEnabled(it.value().toBool());
|
session->setTrackerEnabled(it.value().toBool());
|
||||||
// Choking algorithm
|
// Choking algorithm
|
||||||
|
|
|
@ -66,6 +66,7 @@ namespace
|
||||||
|
|
||||||
// Peer keys
|
// Peer keys
|
||||||
const QString KEY_PEER_CLIENT = u"client"_qs;
|
const QString KEY_PEER_CLIENT = u"client"_qs;
|
||||||
|
const QString KEY_PEER_ID_CLIENT = u"peer_id_client"_qs;
|
||||||
const QString KEY_PEER_CONNECTION_TYPE = u"connection"_qs;
|
const QString KEY_PEER_CONNECTION_TYPE = u"connection"_qs;
|
||||||
const QString KEY_PEER_COUNTRY = u"country"_qs;
|
const QString KEY_PEER_COUNTRY = u"country"_qs;
|
||||||
const QString KEY_PEER_COUNTRY_CODE = u"country_code"_qs;
|
const QString KEY_PEER_COUNTRY_CODE = u"country_code"_qs;
|
||||||
|
@ -561,6 +562,7 @@ void SyncController::torrentPeersAction()
|
||||||
{KEY_PEER_IP, pi.address().ip.toString()},
|
{KEY_PEER_IP, pi.address().ip.toString()},
|
||||||
{KEY_PEER_PORT, pi.address().port},
|
{KEY_PEER_PORT, pi.address().port},
|
||||||
{KEY_PEER_CLIENT, pi.client()},
|
{KEY_PEER_CLIENT, pi.client()},
|
||||||
|
{KEY_PEER_ID_CLIENT, pi.peerIdClient()},
|
||||||
{KEY_PEER_PROGRESS, pi.progress()},
|
{KEY_PEER_PROGRESS, pi.progress()},
|
||||||
{KEY_PEER_DOWN_SPEED, pi.payloadDownSpeed()},
|
{KEY_PEER_DOWN_SPEED, pi.payloadDownSpeed()},
|
||||||
{KEY_PEER_UP_SPEED, pi.payloadUpSpeed()},
|
{KEY_PEER_UP_SPEED, pi.payloadUpSpeed()},
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
#include "base/utils/version.h"
|
#include "base/utils/version.h"
|
||||||
#include "api/isessionmanager.h"
|
#include "api/isessionmanager.h"
|
||||||
|
|
||||||
inline const Utils::Version<3, 2> API_VERSION {2, 8, 16};
|
inline const Utils::Version<3, 2> API_VERSION {2, 8, 18};
|
||||||
|
|
||||||
class APIController;
|
class APIController;
|
||||||
class AuthController;
|
class AuthController;
|
||||||
|
|
|
@ -50,25 +50,21 @@ void WebUI::configure()
|
||||||
{
|
{
|
||||||
m_isErrored = false; // clear previous error state
|
m_isErrored = false; // clear previous error state
|
||||||
|
|
||||||
Preferences *const pref = Preferences::instance();
|
const QString portForwardingProfile = u"webui"_qs;
|
||||||
|
const Preferences *pref = Preferences::instance();
|
||||||
const quint16 oldPort = m_port;
|
const quint16 port = pref->getWebUiPort();
|
||||||
m_port = pref->getWebUiPort();
|
|
||||||
|
|
||||||
if (pref->isWebUiEnabled())
|
if (pref->isWebUiEnabled())
|
||||||
{
|
{
|
||||||
// UPnP/NAT-PMP
|
// Port forwarding
|
||||||
|
auto *portForwarder = Net::PortForwarder::instance();
|
||||||
if (pref->useUPnPForWebUIPort())
|
if (pref->useUPnPForWebUIPort())
|
||||||
{
|
{
|
||||||
if (m_port != oldPort)
|
portForwarder->setPorts(portForwardingProfile, {port});
|
||||||
{
|
|
||||||
Net::PortForwarder::instance()->deletePort(oldPort);
|
|
||||||
Net::PortForwarder::instance()->addPort(m_port);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Net::PortForwarder::instance()->deletePort(oldPort);
|
portForwarder->removePorts(portForwardingProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
// http server
|
// http server
|
||||||
|
@ -81,7 +77,7 @@ void WebUI::configure()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ((m_httpServer->serverAddress().toString() != serverAddressString)
|
if ((m_httpServer->serverAddress().toString() != serverAddressString)
|
||||||
|| (m_httpServer->serverPort() != m_port))
|
|| (m_httpServer->serverPort() != port))
|
||||||
m_httpServer->close();
|
m_httpServer->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,15 +108,15 @@ void WebUI::configure()
|
||||||
{
|
{
|
||||||
const auto address = ((serverAddressString == u"*") || serverAddressString.isEmpty())
|
const auto address = ((serverAddressString == u"*") || serverAddressString.isEmpty())
|
||||||
? QHostAddress::Any : QHostAddress(serverAddressString);
|
? QHostAddress::Any : QHostAddress(serverAddressString);
|
||||||
bool success = m_httpServer->listen(address, m_port);
|
bool success = m_httpServer->listen(address, port);
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
LogMsg(tr("Web UI: Now listening on IP: %1, port: %2").arg(serverAddressString).arg(m_port));
|
LogMsg(tr("Web UI: Now listening on IP: %1, port: %2").arg(serverAddressString).arg(port));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
const QString errorMsg = tr("Web UI: Unable to bind to IP: %1, port: %2. Reason: %3")
|
const QString errorMsg = tr("Web UI: Unable to bind to IP: %1, port: %2. Reason: %3")
|
||||||
.arg(serverAddressString).arg(m_port).arg(m_httpServer->errorString());
|
.arg(serverAddressString).arg(port).arg(m_httpServer->errorString());
|
||||||
LogMsg(errorMsg, Log::CRITICAL);
|
LogMsg(errorMsg, Log::CRITICAL);
|
||||||
qCritical() << errorMsg;
|
qCritical() << errorMsg;
|
||||||
|
|
||||||
|
@ -144,7 +140,7 @@ void WebUI::configure()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Net::PortForwarder::instance()->deletePort(oldPort);
|
Net::PortForwarder::instance()->removePorts(portForwardingProfile);
|
||||||
|
|
||||||
delete m_httpServer;
|
delete m_httpServer;
|
||||||
delete m_webapp;
|
delete m_webapp;
|
||||||
|
|
|
@ -66,5 +66,4 @@ private:
|
||||||
QPointer<Http::Server> m_httpServer;
|
QPointer<Http::Server> m_httpServer;
|
||||||
QPointer<Net::DNSUpdater> m_dnsUpdater;
|
QPointer<Net::DNSUpdater> m_dnsUpdater;
|
||||||
QPointer<WebApplication> m_webapp;
|
QPointer<WebApplication> m_webapp;
|
||||||
quint16 m_port = 0;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1498,6 +1498,7 @@ window.qBittorrent.DynamicTable = (function() {
|
||||||
this.newColumn('connection', '', 'QBT_TR(Connection)QBT_TR[CONTEXT=PeerListWidget]', 50, true);
|
this.newColumn('connection', '', 'QBT_TR(Connection)QBT_TR[CONTEXT=PeerListWidget]', 50, true);
|
||||||
this.newColumn('flags', '', 'QBT_TR(Flags)QBT_TR[CONTEXT=PeerListWidget]', 50, true);
|
this.newColumn('flags', '', 'QBT_TR(Flags)QBT_TR[CONTEXT=PeerListWidget]', 50, true);
|
||||||
this.newColumn('client', '', 'QBT_TR(Client)QBT_TR[CONTEXT=PeerListWidget]', 140, true);
|
this.newColumn('client', '', 'QBT_TR(Client)QBT_TR[CONTEXT=PeerListWidget]', 140, true);
|
||||||
|
this.newColumn('peer_id_client', '', 'QBT_TR(Peer ID Client)QBT_TR[CONTEXT=PeerListWidget]', 60, false);
|
||||||
this.newColumn('progress', '', 'QBT_TR(Progress)QBT_TR[CONTEXT=PeerListWidget]', 50, true);
|
this.newColumn('progress', '', 'QBT_TR(Progress)QBT_TR[CONTEXT=PeerListWidget]', 50, true);
|
||||||
this.newColumn('dl_speed', '', 'QBT_TR(Down Speed)QBT_TR[CONTEXT=PeerListWidget]', 50, true);
|
this.newColumn('dl_speed', '', 'QBT_TR(Down Speed)QBT_TR[CONTEXT=PeerListWidget]', 50, true);
|
||||||
this.newColumn('up_speed', '', 'QBT_TR(Up Speed)QBT_TR[CONTEXT=PeerListWidget]', 50, true);
|
this.newColumn('up_speed', '', 'QBT_TR(Up Speed)QBT_TR[CONTEXT=PeerListWidget]', 50, true);
|
||||||
|
|
|
@ -979,18 +979,34 @@ const initializeWindows = function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
['pause', 'resume'].each(function(item) {
|
addClickEvent('pauseAll', (e) => {
|
||||||
addClickEvent(item + 'All', function(e) {
|
new Event(e).stop();
|
||||||
new Event(e).stop();
|
|
||||||
|
if (confirm('QBT_TR(Would you like to pause all torrents?)QBT_TR[CONTEXT=MainWindow]')) {
|
||||||
new Request({
|
new Request({
|
||||||
url: 'api/v2/torrents/' + item,
|
url: 'api/v2/torrents/pause',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data: {
|
data: {
|
||||||
hashes: "all"
|
hashes: "all"
|
||||||
}
|
}
|
||||||
}).send();
|
}).send();
|
||||||
updateMainData();
|
updateMainData();
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addClickEvent('resumeAll', (e) => {
|
||||||
|
new Event(e).stop();
|
||||||
|
|
||||||
|
if (confirm('QBT_TR(Would you like to resume all torrents?)QBT_TR[CONTEXT=MainWindow]')) {
|
||||||
|
new Request({
|
||||||
|
url: 'api/v2/torrents/resume',
|
||||||
|
method: 'post',
|
||||||
|
data: {
|
||||||
|
hashes: "all"
|
||||||
|
}
|
||||||
|
}).send();
|
||||||
|
updateMainData();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
['pause', 'resume', 'recheck'].each(function(item) {
|
['pause', 'resume', 'recheck'].each(function(item) {
|
||||||
|
|
|
@ -1016,6 +1016,14 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
|
||||||
<input type="text" id="embeddedTrackerPort" style="width: 15em;" />
|
<input type="text" id="embeddedTrackerPort" style="width: 15em;" />
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<label for="embeddedTrackerPortForwarding">QBT_TR(Enable port forwarding for embedded tracker:)QBT_TR[CONTEXT=OptionsDialog]</label>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="checkbox" id="embeddedTrackerPortForwarding" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset class="settings">
|
<fieldset class="settings">
|
||||||
|
@ -2099,6 +2107,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
|
||||||
$('blockPeersOnPrivilegedPorts').setProperty('checked', pref.block_peers_on_privileged_ports);
|
$('blockPeersOnPrivilegedPorts').setProperty('checked', pref.block_peers_on_privileged_ports);
|
||||||
$('enableEmbeddedTracker').setProperty('checked', pref.enable_embedded_tracker);
|
$('enableEmbeddedTracker').setProperty('checked', pref.enable_embedded_tracker);
|
||||||
$('embeddedTrackerPort').setProperty('value', pref.embedded_tracker_port);
|
$('embeddedTrackerPort').setProperty('value', pref.embedded_tracker_port);
|
||||||
|
$('embeddedTrackerPortForwarding').setProperty('checked', pref.embedded_tracker_port_forwarding);
|
||||||
$('uploadSlotsBehavior').setProperty('value', pref.upload_slots_behavior);
|
$('uploadSlotsBehavior').setProperty('value', pref.upload_slots_behavior);
|
||||||
$('uploadChokingAlgorithm').setProperty('value', pref.upload_choking_algorithm);
|
$('uploadChokingAlgorithm').setProperty('value', pref.upload_choking_algorithm);
|
||||||
$('announceAllTrackers').setProperty('checked', pref.announce_to_all_trackers);
|
$('announceAllTrackers').setProperty('checked', pref.announce_to_all_trackers);
|
||||||
|
@ -2526,6 +2535,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD
|
||||||
settings.set('block_peers_on_privileged_ports', $('blockPeersOnPrivilegedPorts').getProperty('checked'));
|
settings.set('block_peers_on_privileged_ports', $('blockPeersOnPrivilegedPorts').getProperty('checked'));
|
||||||
settings.set('enable_embedded_tracker', $('enableEmbeddedTracker').getProperty('checked'));
|
settings.set('enable_embedded_tracker', $('enableEmbeddedTracker').getProperty('checked'));
|
||||||
settings.set('embedded_tracker_port', $('embeddedTrackerPort').getProperty('value'));
|
settings.set('embedded_tracker_port', $('embeddedTrackerPort').getProperty('value'));
|
||||||
|
settings.set('embedded_tracker_port_forwarding', $('embeddedTrackerPortForwarding').getProperty('checked'));
|
||||||
settings.set('upload_slots_behavior', $('uploadSlotsBehavior').getProperty('value'));
|
settings.set('upload_slots_behavior', $('uploadSlotsBehavior').getProperty('value'));
|
||||||
settings.set('upload_choking_algorithm', $('uploadChokingAlgorithm').getProperty('value'));
|
settings.set('upload_choking_algorithm', $('uploadChokingAlgorithm').getProperty('value'));
|
||||||
settings.set('announce_to_all_trackers', $('announceAllTrackers').getProperty('checked'));
|
settings.set('announce_to_all_trackers', $('announceAllTrackers').getProperty('checked'));
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
<file>webui_uk.qm</file>
|
<file>webui_uk.qm</file>
|
||||||
<file>webui_uz@Latn.qm</file>
|
<file>webui_uz@Latn.qm</file>
|
||||||
<file>webui_vi.qm</file>
|
<file>webui_vi.qm</file>
|
||||||
<file>webui_zh.qm</file>
|
<file>webui_zh_CN.qm</file>
|
||||||
<file>webui_zh_HK.qm</file>
|
<file>webui_zh_HK.qm</file>
|
||||||
<file>webui_zh_TW.qm</file>
|
<file>webui_zh_TW.qm</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
|
|
Loading…
Reference in a new issue