1
mirror of https://github.com/qbittorrent/qBittorrent synced 2025-10-09 18:32:15 +02:00

Compare commits

...

34 Commits

Author SHA1 Message Date
sledgehammer999
4cb386af35 Bump to 4.4.1 2022-02-15 17:14:35 +02:00
sledgehammer999
14ab1b015c Update Changelog 2022-02-15 17:12:38 +02:00
sledgehammer999
0a4971c994 Partially revert e93c360db6
QShareDataPointer causes a crash upon start on 32bit Qt5 Windows.
This is a temporary fix in order to release v4.4.1.
2022-02-15 17:08:39 +02:00
sledgehammer999
a75ae21434 Sync translations from Transifex and run lupdate 2022-02-15 17:04:49 +02:00
Vladimir Golovnev (Glassez)
01eed5dae9 Try to recover missing categories 2022-02-15 16:36:33 +02:00
Chocobo1
e73397c750 Remove hack for outdated IE 6 browser
The `mask()` isn't valid in CSS.
2022-02-14 13:37:05 +08:00
sledgehammer999
869d079507 Migrate proxy settings
Q_ENUM_NS(ProxyType) was introduced in 4.4.0.
Before that wrapping QMetaEnum used the int value itself for loading/storing.

PR #16030.
Closes #15994.
2022-02-11 16:09:16 +03:00
Prince Gupta
71174edf72 Optimize completed files handling
PR #16329.

Co-authored-by: Vladimir Golovnev (Glassez) <glassez@yandex.ru>
2022-02-11 16:09:16 +03:00
thalieht
b3d46ecb78 Add Select All/None buttons in new torrent dialog 2022-02-01 08:07:03 +03:00
thalieht
80035a2520 Fix "Free space on disk" in new torrent dialog
Always initialize it.
2022-02-01 08:07:03 +03:00
Chocobo1
6790335239 Fix crash when shutting down and clicked on system tray icon
Disconnect all signals of system tray icon when shutting down.

Closes #16324.
PR #16328.
2022-02-01 12:53:33 +08:00
Vladimir Golovnev
48ff494dca Open correct directory when clicked on Browse button
PR #16252.
2022-01-28 08:27:07 +03:00
Vladimir Golovnev
c5b361ce74 Change torrent moving state when it is cancelled
PR #16267.
2022-01-28 08:27:07 +03:00
thalieht
397b7b9407 Add tooltip to Automatic Torrent Management context menu action
PR #16241
2022-01-27 07:42:23 +03:00
thalieht
6e0c1e2147 Add confirmation for enabling Auto TMM from context menu
PR #16241
2022-01-27 07:42:23 +03:00
Vladimir Golovnev
e93c360db6 Store hybrid torrents using legacy filenames
* Make Digest32 implicitly shared class
* Store hybrid torrents using legacy filenames

PR #16237.
2022-01-25 08:22:35 +03:00
thalieht
270e2023cd Fix wrong closing brace position
Regression from 0086bf8958.
PR #16172.
2022-01-22 08:17:07 +03:00
Vladimir Golovnev
5ac858213b Don't start separate event loop for QFileDialog
It conflicts with QMenu on Qt6 that causes the crash.

PR #16158.
2022-01-22 08:17:07 +03:00
Vladimir Golovnev
f0ee6aba29 Correctly handle received metadata
It did not work correctly, since it assumed that 'lt::torrent_plugin' is created at an earlier stage and is able to track all changes in the torrent state, but in reality it turned out that it was created after the torrent moved to the `downloading_metadata` state, so we had to additionally handle it in the constructor.

PR #16121.
2022-01-17 09:41:21 +03:00
Vladimir Golovnev
fa418087c4 Handle missing torrent alerts
PR #16085.
2022-01-17 09:41:21 +03:00
thalieht
8493e1ad64 Restore all settings to the torrent list's context menu
Set location
Category
Sequential download
Download first/Last pieces first
Automatic Torrent Management

PR #16016.
2022-01-16 12:06:46 +08:00
thalieht
fe90fcef5b Update the torrent's download path field when changing category
In torrent options dialog while in Automatic Management Mode.
PR #16026.
2022-01-16 12:06:46 +08:00
Vladimir Golovnev
210fd80167 Correctly concatenate paths
PR #16086.
2022-01-14 15:17:17 +03:00
Vladimir Golovnev (Glassez)
0a1e864f74 Correctly handle XML parsing errors 2022-01-14 10:20:42 +03:00
Chocobo1
7adccab687 Add Qt6 version to INSTALL file 2022-01-14 14:43:55 +08:00
Chocobo1
67e536d869 Update default value of "Type of service for peers"
Upstream change:
3d701c7380
PR #16036.
2022-01-14 14:43:55 +08:00
Vladimir Golovnev (Glassez)
86e8d848f6 Move torrent immediately when "save path" is changed 2022-01-12 08:39:23 +03:00
Vladimir Golovnev (Glassez)
88114b4588 Don't try to move storage into its current location 2022-01-12 08:39:23 +03:00
Vladimir Golovnev (Glassez)
e468f004f4 Correctly track the root folder name change 2022-01-12 08:39:23 +03:00
Vladimir Golovnev (Glassez)
4cfccc54ea Correctly handle Auto TMM in Torrent Files Watcher 2022-01-12 08:39:23 +03:00
Vladimir Golovnev (Glassez)
5ffa7e4752 Keep "torrent info" alive while generate .torrent file 2022-01-12 08:39:23 +03:00
Chocobo1
d7fd576293 WebAPI: fix wrong key used for categories
Regression from 1c0f8b4289.
Closes #15969.
2022-01-11 11:54:15 +08:00
Chocobo1
83b34053a1 Move new line character out of translation string
PR #15948.
2022-01-08 23:43:32 +08:00
sledgehammer999
b9164adb7a Bump to 4.4.0 2022-01-06 20:41:17 +02:00
141 changed files with 74604 additions and 64397 deletions

View File

@@ -1,3 +1,25 @@
Tue Feb 15 2022 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.4.1
- FEATURE: Restore all torrent settings to the torrent's main context menu (thalieht)
- FEATURE: Add confirmation for enabling Auto TMM from context menu (thalieht)
- FEATURE: Add tooltip to Automatic Torrent Management context menu action (thalieht)
- FEATURE: Add Select All/None buttons in new torrent dialog (thalieht)
- BUGFIX: Keep "torrent info" alive while generate .torrent file (glassez)
- BUGFIX: Correctly handle Auto TMM in Torrent Files Watcher (glassez)
- BUGFIX: Correctly track the root folder name change (glassez)
- BUGFIX: Various fixes to the moving torrent code (glassez)
- BUGFIX: Update the torrent's download path field when changing category (thalieht)
- BUGFIX: Correctly handle received metadata (glassez)
- BUGFIX: Store hybrid torrents using legacy filenames (glassez)
- BUGFIX: Open correct directory when clicked on Browse button (glassez)
- BUGFIX: Fix crash when shutting down and clicing on system tray icon (Chocobo1)
- BUGFIX: Fix "Free space on disk" in new torrent dialog (thalieht)
- BUGFIX: Optimize completed files handling (Prince Gupta)
- BUGFIX: Migrate proxy settings (sledgehammer999)
- BUGFIX: Try to recover missing categories (glassez)
- WEBUI: WebAPI: fix wrong key used for categories (Chocobo1)
- WEBUI: Remove hack for outdated IE 6 browser (Chocobo1)
- RSS: Correctly handle XML parsing errors (glassez)
Thu Jan 06 2022 - sledgehammer999 <sledgehammer999@qbittorrent.org> - v4.4.0
- FEATURE: Support for v2 torrents along with libtorrent 2.0.x support (glassez, Chocobo1)
- FEATURE: Support for Qt6 (glassez)

View File

@@ -11,7 +11,7 @@ qBittorrent - A BitTorrent client in C++ / Qt
- OpenSSL >= 1.1.1
- Qt 5.15.2 - 5.x
- Qt 5.15.2 - 5.x || 6.2.0 - 6.x
- zlib >= 1.2.11

20
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.71 for qbittorrent v4.4.0alpha.
# Generated by GNU Autoconf 2.71 for qbittorrent v4.4.1.
#
# Report bugs to <bugs.qbittorrent.org>.
#
@@ -611,8 +611,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='qbittorrent'
PACKAGE_TARNAME='qbittorrent'
PACKAGE_VERSION='v4.4.0alpha'
PACKAGE_STRING='qbittorrent v4.4.0alpha'
PACKAGE_VERSION='v4.4.1'
PACKAGE_STRING='qbittorrent v4.4.1'
PACKAGE_BUGREPORT='bugs.qbittorrent.org'
PACKAGE_URL='https://www.qbittorrent.org/'
@@ -1329,7 +1329,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures qbittorrent v4.4.0alpha to adapt to many kinds of systems.
\`configure' configures qbittorrent v4.4.1 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1400,7 +1400,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of qbittorrent v4.4.0alpha:";;
short | recursive ) echo "Configuration of qbittorrent v4.4.1:";;
esac
cat <<\_ACEOF
@@ -1533,7 +1533,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
qbittorrent configure v4.4.0alpha
qbittorrent configure v4.4.1
generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc.
@@ -1648,7 +1648,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by qbittorrent $as_me v4.4.0alpha, which was
It was created by qbittorrent $as_me v4.4.1, which was
generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw
@@ -4779,7 +4779,7 @@ fi
# Define the identity of the package.
PACKAGE='qbittorrent'
VERSION='v4.4.0alpha'
VERSION='v4.4.1'
printf "%s\n" "#define PACKAGE \"$PACKAGE\"" >>confdefs.h
@@ -7254,7 +7254,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by qbittorrent $as_me v4.4.0alpha, which was
This file was extended by qbittorrent $as_me v4.4.1, which was
generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -7314,7 +7314,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\
qbittorrent config.status v4.4.0alpha
qbittorrent config.status v4.4.1
configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"

View File

@@ -1,5 +1,4 @@
AC_INIT([qbittorrent], [v4.4.0alpha], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
AC_INIT([qbittorrent], [v4.4.1], [bugs.qbittorrent.org], [], [https://www.qbittorrent.org/])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
: ${CFLAGS=""}

2
dist/mac/Info.plist vendored
View File

@@ -55,7 +55,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>4.4.0</string>
<string>4.4.1</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>

View File

@@ -74,6 +74,6 @@
<url type="translate">https://github.com/qbittorrent/qBittorrent/wiki/How-to-translate-qBittorrent</url>
<content_rating type="oars-1.1"/>
<releases>
<release version="4.4.0" date="2020-10-18"/>
<release version="4.4.1" date="2022-02-15"/>
</releases>
</component>

View File

@@ -115,6 +115,8 @@ Name[lt]=qBittorrent
Comment[mk]=Превземајте и споделувајте фајлови преку BitTorrent
GenericName[mk]=BitTorrent клиент
Name[mk]=qBittorrent
Comment[my]=တောရန့်ဖြင့်ဖိုင်များဒေါင်းလုဒ်ဆွဲရန်နှင့်မျှဝေရန်
GenericName[my]=တောရန့်စီမံခန့်ခွဲသည့်အရာ
Name[my]=qBittorrent
Comment[nb]=Last ned og del filer over BitTorrent
GenericName[nb]=BitTorrent-klient

View File

@@ -28,7 +28,7 @@ XPStyle on
!define CSIDL_LOCALAPPDATA '0x1C' ;Local Application Data path
; Program specific
!define PROG_VERSION "4.4.0"
!define PROG_VERSION "4.4.1"
!define MUI_FINISHPAGE_RUN
!define MUI_FINISHPAGE_RUN_FUNCTION PageFinishRun

View File

@@ -650,8 +650,8 @@ int Application::exec(const QStringList &params)
const auto scheme = QString::fromLatin1(pref->isWebUiHttpsEnabled() ? "https" : "http");
const auto url = QString::fromLatin1("%1://localhost:%2\n").arg(scheme, QString::number(pref->getWebUiPort()));
const QString mesg = QString::fromLatin1("\n******** %1 ********\n").arg(tr("Information"))
+ tr("To control qBittorrent, access the WebUI at: %1\n").arg(url);
printf("%s", qUtf8Printable(mesg));
+ tr("To control qBittorrent, access the WebUI at: %1").arg(url);
printf("%s\n", qUtf8Printable(mesg));
if (pref->getWebUIPassword() == "ARQ77eY1NUZaQsuDHbIMCA==:0WMRkYTUWVT9wVvdDtHAjU9b3b7uB8NR1Gur2hmQCvCDpm39Q+PsJRJPaCU51dEiz+dTzh8qbPsL8WkFljQYFQ==")
{

View File

@@ -32,6 +32,7 @@
#include "base/bittorrent/torrentcontentlayout.h"
#include "base/logger.h"
#include "base/net/proxyconfigurationmanager.h"
#include "base/preferences.h"
#include "base/profile.h"
#include "base/settingsstorage.h"
@@ -42,7 +43,7 @@
namespace
{
const int MIGRATION_VERSION = 2;
const int MIGRATION_VERSION = 3;
const char MIGRATION_VERSION_KEY[] = "Meta/MigrationVersion";
void exportWebUIHttpsFiles()
@@ -326,6 +327,46 @@ namespace
}
}
}
void migrateProxySettingsEnum()
{
auto *settingsStorage = SettingsStorage::instance();
const auto key = QString::fromLatin1("Network/Proxy/Type");
const auto value = settingsStorage->loadValue<QString>(key);
bool ok = false;
const auto number = value.toInt(&ok);
if (ok)
{
switch (number)
{
case 0:
settingsStorage->storeValue(key, Net::ProxyType::None);
break;
case 1:
settingsStorage->storeValue(key, Net::ProxyType::HTTP);
break;
case 2:
settingsStorage->storeValue(key, Net::ProxyType::SOCKS5);
break;
case 3:
settingsStorage->storeValue(key, Net::ProxyType::HTTP_PW);
break;
case 4:
settingsStorage->storeValue(key, Net::ProxyType::SOCKS5_PW);
break;
case 5:
settingsStorage->storeValue(key, Net::ProxyType::SOCKS4);
break;
default:
LogMsg(QObject::tr("Invalid value found in configuration file, reverting it to default. Key: \"%1\". Invalid value: \"%2\".")
.arg(key, QString::number(number)), Log::WARNING);
settingsStorage->removeValue(key);
break;
}
}
}
}
bool upgrade(const bool /*ask*/)
@@ -343,9 +384,13 @@ bool upgrade(const bool /*ask*/)
upgradeDNSServiceSettings();
upgradeTrayIconStyleSettings();
}
if (version < 2)
migrateSettingKeys();
if (version < 3)
migrateProxySettingsEnum();
version = MIGRATION_VERSION;
}

View File

@@ -36,6 +36,13 @@ BitTorrent::InfoHash::InfoHash(const WrappedType &nativeHash)
{
}
#ifdef QBT_USES_LIBTORRENT2
BitTorrent::InfoHash::InfoHash(const SHA1Hash &v1, const SHA256Hash &v2)
: InfoHash {WrappedType(v1, v2)}
{
}
#endif
bool BitTorrent::InfoHash::isValid() const
{
return m_valid;

View File

@@ -65,6 +65,9 @@ namespace BitTorrent
InfoHash() = default;
InfoHash(const WrappedType &nativeHash);
#ifdef QBT_USES_LIBTORRENT2
InfoHash(const SHA1Hash &v1, const SHA256Hash &v2);
#endif
bool isValid() const;
SHA1Hash v1() const;
@@ -85,3 +88,6 @@ namespace BitTorrent
}
Q_DECLARE_METATYPE(BitTorrent::TorrentID)
// We can declare it as Q_MOVABLE_TYPE to improve performance
// since base type uses QSharedDataPointer as the only member
Q_DECLARE_TYPEINFO(BitTorrent::TorrentID, Q_MOVABLE_TYPE);

View File

@@ -41,6 +41,7 @@ namespace
NativeTorrentExtension::NativeTorrentExtension(const lt::torrent_handle &torrentHandle)
: m_torrentHandle {torrentHandle}
{
on_state(m_torrentHandle.status({}).state);
}
bool NativeTorrentExtension::on_pause()
@@ -56,7 +57,10 @@ bool NativeTorrentExtension::on_pause()
void NativeTorrentExtension::on_state(const lt::torrent_status::state_t state)
{
if (m_state == lt::torrent_status::downloading_metadata)
m_torrentHandle.set_flags(lt::torrent_flags::stop_when_ready);
{
m_torrentHandle.unset_flags(lt::torrent_flags::auto_managed);
m_torrentHandle.pause();
}
m_state = state;
}

File diff suppressed because it is too large Load Diff

View File

@@ -273,13 +273,16 @@ TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession
m_torrentInfo = TorrentInfo(*m_ltAddTorrentParams.ti);
Q_ASSERT(m_filePaths.isEmpty());
Q_ASSERT(m_indexMap.isEmpty());
const int filesCount = m_torrentInfo.filesCount();
m_filePaths.reserve(filesCount);
m_indexMap.reserve(filesCount);
const std::shared_ptr<const lt::torrent_info> currentInfo = m_nativeHandle.torrent_file();
const lt::file_storage &fileStorage = currentInfo->files();
for (int i = 0; i < filesCount; ++i)
{
const lt::file_index_t nativeIndex = m_torrentInfo.nativeIndexes().at(i);
m_indexMap[nativeIndex] = i;
const QString filePath = Utils::Fs::toUniformPath(QString::fromStdString(fileStorage.file_path(nativeIndex)));
m_filePaths.append(filePath);
}
@@ -412,7 +415,7 @@ void TorrentImpl::setSavePath(const QString &path)
m_session->handleTorrentNeedSaveResumeData(this);
const bool isFinished = isSeed() || m_hasSeedStatus;
if (isFinished)
if (isFinished || downloadPath().isEmpty())
moveStorage(savePath(), MoveStorageMode::KeepExistingFiles);
}
@@ -444,7 +447,7 @@ QString TorrentImpl::rootPath() const
if (!hasMetadata())
return {};
const QString relativeRootPath = m_torrentInfo.rootFolder();
const QString relativeRootPath = Utils::Fs::findRootFolder(filePaths());
if (relativeRootPath.isEmpty())
return {};
@@ -1500,14 +1503,22 @@ void TorrentImpl::fileSearchFinished(const QString &savePath, const QStringList
void TorrentImpl::endReceivedMetadataHandling(const QString &savePath, const QStringList &fileNames)
{
Q_ASSERT(m_filePaths.isEmpty());
Q_ASSERT(m_indexMap.isEmpty());
lt::add_torrent_params &p = m_ltAddTorrentParams;
const std::shared_ptr<lt::torrent_info> metadata = std::const_pointer_cast<lt::torrent_info>(m_nativeHandle.torrent_file());
m_torrentInfo = TorrentInfo(*metadata);
m_filePaths = fileNames;
m_indexMap.reserve(filesCount());
const auto nativeIndexes = m_torrentInfo.nativeIndexes();
for (int i = 0; i < fileNames.size(); ++i)
p.renamed_files[nativeIndexes[i]] = fileNames[i].toStdString();
{
const auto nativeIndex = nativeIndexes.at(i);
m_indexMap[nativeIndex] = i;
p.renamed_files[nativeIndex] = fileNames[i].toStdString();
}
p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
p.ti = metadata;
@@ -1862,7 +1873,7 @@ void TorrentImpl::handleFastResumeRejectedAlert(const lt::fastresume_rejected_al
void TorrentImpl::handleFileRenamedAlert(const lt::file_renamed_alert *p)
{
const int fileIndex = m_torrentInfo.nativeIndexes().indexOf(p->index);
const int fileIndex = m_indexMap.value(p->index, -1);
Q_ASSERT(fileIndex >= 0);
// Remove empty leftover folders
@@ -1892,9 +1903,10 @@ void TorrentImpl::handleFileRenamedAlert(const lt::file_renamed_alert *p)
++pathIdx;
}
QDir storageDir {actualStorageLocation()};
for (int i = (oldPathParts.size() - 1); i >= pathIdx; --i)
{
QDir().rmdir(savePath() + Utils::String::join(oldPathParts, QString::fromLatin1("/")));
storageDir.rmdir(Utils::String::join(oldPathParts, QString::fromLatin1("/")));
oldPathParts.removeLast();
}
@@ -1907,7 +1919,7 @@ void TorrentImpl::handleFileRenamedAlert(const lt::file_renamed_alert *p)
void TorrentImpl::handleFileRenameFailedAlert(const lt::file_rename_failed_alert *p)
{
const int fileIndex = m_torrentInfo.nativeIndexes().indexOf(p->index);
const int fileIndex = m_indexMap.value(p->index, -1);
Q_ASSERT(fileIndex >= 0);
LogMsg(tr("File rename failed. Torrent: \"%1\", file: \"%2\", reason: \"%3\"")
@@ -1922,12 +1934,11 @@ void TorrentImpl::handleFileRenameFailedAlert(const lt::file_rename_failed_alert
void TorrentImpl::handleFileCompletedAlert(const lt::file_completed_alert *p)
{
const int fileIndex = m_torrentInfo.nativeIndexes().indexOf(p->index);
Q_ASSERT(fileIndex >= 0);
qDebug("A file completed download in torrent \"%s\"", qUtf8Printable(name()));
if (m_session->isAppendExtensionEnabled())
{
const int fileIndex = m_indexMap.value(p->index, -1);
Q_ASSERT(fileIndex >= 0);
QString name = filePath(fileIndex);
if (name.endsWith(QB_EXT))
{
@@ -2081,7 +2092,8 @@ void TorrentImpl::adjustStorageLocation()
const bool isFinished = isSeed() || m_hasSeedStatus;
const QDir targetDir {((isFinished || downloadPath.isEmpty()) ? savePath() : downloadPath)};
moveStorage(targetDir.absolutePath(), MoveStorageMode::Overwrite);
if ((targetDir != QDir(actualStorageLocation())) || isMoveInProgress())
moveStorage(targetDir.absolutePath(), MoveStorageMode::Overwrite);
}
lt::torrent_handle TorrentImpl::nativeHandle() const

View File

@@ -286,6 +286,7 @@ namespace BitTorrent
TorrentState m_state = TorrentState::Unknown;
TorrentInfo m_torrentInfo;
QStringList m_filePaths;
QHash<lt::file_index_t, int> m_indexMap;
SpeedMonitor m_speedMonitor;
InfoHash m_infoHash;

View File

@@ -140,7 +140,7 @@ nonstd::expected<void, QString> TorrentInfo::saveToFile(const QString &path) con
try
{
const auto torrentCreator = lt::create_torrent(*nativeInfo());
const auto torrentCreator = lt::create_torrent(*m_nativeInfo);
const lt::entry torrentEntry = torrentCreator.generate();
const nonstd::expected<void, QString> result = Utils::IO::saveToFile(path, torrentEntry);
if (!result)
@@ -409,19 +409,6 @@ int TorrentInfo::fileIndex(const QString &fileName) const
return -1;
}
QString TorrentInfo::rootFolder() const
{
if (!isValid())
return {};
return Utils::Fs::findRootFolder(filePaths());
}
bool TorrentInfo::hasRootFolder() const
{
return !rootFolder().isEmpty();
}
TorrentContentLayout TorrentInfo::contentLayout() const
{
if (!isValid())

View File

@@ -92,9 +92,6 @@ namespace BitTorrent
PieceRange filePieces(const QString &file) const;
PieceRange filePieces(int fileIndex) const;
QString rootFolder() const;
bool hasRootFolder() const;
std::shared_ptr<lt::torrent_info> nativeInfo() const;
QVector<lt::file_index_t> nativeIndexes() const;

View File

@@ -591,16 +591,16 @@ void Parser::parse_impl(const QByteArray &feedData)
xml.skipCurrentElement();
}
if (!foundChannel)
{
m_result.error = tr("Invalid RSS feed.");
}
else if (xml.hasError())
if (xml.hasError())
{
m_result.error = tr("%1 (line: %2, column: %3, offset: %4).")
.arg(xml.errorString()).arg(xml.lineNumber())
.arg(xml.columnNumber()).arg(xml.characterOffset());
}
else if (!foundChannel)
{
m_result.error = tr("Invalid RSS feed.");
}
emit finished(m_result);
m_result.articles.clear(); // clear articles only

View File

@@ -511,7 +511,8 @@ void TorrentFilesWatcher::Worker::processFolder(const QString &path, const QStri
if (path != watchedFolderPath)
{
const QString subdirPath = watchedDir.relativeFilePath(path);
if (addTorrentParams.useAutoTMM)
const bool useAutoTMM = addTorrentParams.useAutoTMM.value_or(!BitTorrent::Session::instance()->isAutoTMMDisabledByDefault());
if (useAutoTMM)
{
addTorrentParams.category = addTorrentParams.category.isEmpty()
? subdirPath : (addTorrentParams.category + QLatin1Char('/') + subdirPath);
@@ -590,7 +591,8 @@ void TorrentFilesWatcher::Worker::processFailedTorrents()
if (exactDirPath != dir.path())
{
const QString subdirPath = dir.relativeFilePath(exactDirPath);
if (addTorrentParams.useAutoTMM)
const bool useAutoTMM = addTorrentParams.useAutoTMM.value_or(!BitTorrent::Session::instance()->isAutoTMMDisabledByDefault());
if (useAutoTMM)
{
addTorrentParams.category = addTorrentParams.category.isEmpty()
? subdirPath : (addTorrentParams.category + QLatin1Char('/') + subdirPath);

View File

@@ -30,9 +30,9 @@
#define QBT_VERSION_MAJOR 4
#define QBT_VERSION_MINOR 4
#define QBT_VERSION_BUGFIX 0
#define QBT_VERSION_BUGFIX 1
#define QBT_VERSION_BUILD 0
#define QBT_VERSION_STATUS "rc1" // Should be empty for stable releases!
#define QBT_VERSION_STATUS "" // Should be empty for stable releases!
#define QBT__STRINGIFY(x) #x
#define QBT_STRINGIFY(x) QBT__STRINGIFY(x)

View File

@@ -200,7 +200,7 @@ AddNewTorrentDialog::AddNewTorrentDialog(const BitTorrent::AddTorrentParams &inP
m_ui->startTorrentCheckBox->setChecked(!m_torrentParams.addPaused.value_or(session->isAddTorrentPaused()));
m_ui->comboTTM->blockSignals(true); // the TreeView size isn't correct if the slot does it job at this point
m_ui->comboTTM->blockSignals(true); // the TreeView size isn't correct if the slot does its job at this point
m_ui->comboTTM->setCurrentIndex(session->isAutoTMMDisabledByDefault() ? 0 : 1);
m_ui->comboTTM->blockSignals(false);
@@ -874,6 +874,8 @@ void AddNewTorrentDialog::setupTreeview()
connect(m_ui->contentTreeView, &QAbstractItemView::clicked, m_ui->contentTreeView
, qOverload<const QModelIndex &>(&QAbstractItemView::edit));
connect(m_ui->contentTreeView, &QWidget::customContextMenuRequested, this, &AddNewTorrentDialog::displayContentTreeMenu);
connect(m_ui->buttonSelectAll, &QPushButton::clicked, m_contentModel, &TorrentContentFilterModel::selectAll);
connect(m_ui->buttonSelectNone, &QPushButton::clicked, m_contentModel, &TorrentContentFilterModel::selectNone);
const auto contentLayout = ((m_ui->contentLayoutComboBox->currentIndex() == 0)
? BitTorrent::detectContentLayout(m_torrentInfo.filePaths())
@@ -964,9 +966,9 @@ void AddNewTorrentDialog::TMMChanged(int index)
m_ui->groupBoxDownloadPath->blockSignals(true);
m_ui->groupBoxDownloadPath->setChecked(!downloadPath.isEmpty());
updateDiskSpaceLabel();
}
updateDiskSpaceLabel();
}
void AddNewTorrentDialog::doNotDeleteTorrentClicked(bool checked)

View File

@@ -392,22 +392,59 @@
</item>
</layout>
</widget>
<widget class="TorrentContentTreeView" name="contentTreeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QHBoxLayout" name="contentFilterLayout">
<item>
<widget class="QPushButton" name="buttonSelectAll">
<property name="text">
<string>Select All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonSelectNone">
<property name="text">
<string>Select None</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_6">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>168</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="TorrentContentTreeView" name="contentTreeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>1</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="contextMenuPolicy">
<enum>Qt::CustomContextMenu</enum>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="sortingEnabled">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>

View File

@@ -111,8 +111,12 @@ FileSystemPathEdit::FileSystemPathEditPrivate::FileSystemPathEditPrivate(
void FileSystemPathEdit::FileSystemPathEditPrivate::browseActionTriggered()
{
Q_Q(FileSystemPathEdit);
const QFileInfo fileInfo {q->selectedPath()};
const QString directory = (m_mode == FileSystemPathEdit::Mode::DirectoryOpen) || (m_mode == FileSystemPathEdit::Mode::DirectorySave)
? fileInfo.absoluteFilePath()
: fileInfo.absolutePath();
QString filter = q->fileNameFilter();
QString directory = q->currentDirectory().isEmpty() ? QDir::homePath() : q->currentDirectory();
QString selectedPath;
switch (m_mode)
@@ -308,11 +312,6 @@ void FileSystemPathEdit::setDialogCaption(const QString &caption)
d->m_dialogCaption = caption;
}
QString FileSystemPathEdit::currentDirectory() const
{
return QFileInfo(selectedPath()).absoluteDir().absolutePath();
}
QWidget *FileSystemPathEdit::editWidgetImpl() const
{
Q_D(const FileSystemPathEdit);

View File

@@ -64,7 +64,6 @@ public:
Mode mode() const;
void setMode(Mode mode);
QString currentDirectory() const;
QString selectedPath() const;
void setSelectedPath(const QString &val);

View File

@@ -1235,6 +1235,7 @@ void MainWindow::closeEvent(QCloseEvent *e)
#ifndef Q_OS_MACOS
if (m_systrayIcon)
{
m_systrayIcon->disconnect();
m_systrayIcon->setToolTip(tr("qBittorrent is shutting down..."));
m_trayIconMenu->setEnabled(false);
}

View File

@@ -519,6 +519,9 @@ void TorrentOptionsDialog::handleCategoryChanged(const int index)
{
const QString savePath = BitTorrent::Session::instance()->categorySavePath(m_ui->comboCategory->currentText());
m_ui->savePath->setSelectedPath(Utils::Fs::toNativePath(savePath));
const QString downloadPath = BitTorrent::Session::instance()->categoryDownloadPath(m_ui->comboCategory->currentText());
m_ui->downloadPath->setSelectedPath(Utils::Fs::toNativePath(downloadPath));
m_ui->checkUseDownloadPath->setChecked(!downloadPath.isEmpty());
}
}

View File

@@ -331,6 +331,40 @@ QVector<BitTorrent::Torrent *> TransferListWidget::getVisibleTorrents() const
return torrents;
}
void TransferListWidget::setSelectedTorrentsLocation()
{
const QVector<BitTorrent::Torrent *> torrents = getSelectedTorrents();
if (torrents.isEmpty())
return;
const QString oldLocation = torrents[0]->savePath();
auto fileDialog = new QFileDialog(this, tr("Choose save path"), oldLocation);
fileDialog->setAttribute(Qt::WA_DeleteOnClose);
fileDialog->setFileMode(QFileDialog::Directory);
fileDialog->setModal(true);
fileDialog->setOptions(QFileDialog::DontConfirmOverwrite | QFileDialog::ShowDirsOnly | QFileDialog::HideNameFilterDetails);
connect(fileDialog, &QDialog::accepted, this, [this, fileDialog]()
{
const QVector<BitTorrent::Torrent *> torrents = getSelectedTorrents();
if (torrents.isEmpty())
return;
const QString newLocation = fileDialog->selectedFiles().constFirst();
if (newLocation.isEmpty() || !QDir(newLocation).exists())
return;
// Actually move storage
for (BitTorrent::Torrent *const torrent : torrents)
{
torrent->setAutoTMMEnabled(false);
torrent->setSavePath(newLocation);
}
});
fileDialog->show();
}
void TransferListWidget::pauseAllTorrents()
{
for (BitTorrent::Torrent *const torrent : asConst(BitTorrent::Session::instance()->torrents()))
@@ -647,8 +681,28 @@ void TransferListWidget::setSelectedTorrentsSuperSeeding(const bool enabled) con
}
}
void TransferListWidget::setSelectedAutoTMMEnabled(const bool enabled) const
void TransferListWidget::setSelectedTorrentsSequentialDownload(const bool enabled) const
{
for (BitTorrent::Torrent *const torrent : asConst(getSelectedTorrents()))
torrent->setSequentialDownload(enabled);
}
void TransferListWidget::setSelectedFirstLastPiecePrio(const bool enabled) const
{
for (BitTorrent::Torrent *const torrent : asConst(getSelectedTorrents()))
torrent->setFirstLastPiecePriority(enabled);
}
void TransferListWidget::setSelectedAutoTMMEnabled(const bool enabled)
{
if (enabled)
{
const QMessageBox::StandardButton btn = QMessageBox::question(this, tr("Enable automatic torrent management")
, tr("Are you sure you want to enable Automatic Torrent Management for the selected torrent(s)? They may be relocated.")
, (QMessageBox::Yes | QMessageBox::No), QMessageBox::Yes);
if (btn != QMessageBox::Yes) return;
}
for (BitTorrent::Torrent *const torrent : asConst(getSelectedTorrents()))
torrent->setAutoTMMEnabled(enabled);
}
@@ -796,6 +850,7 @@ void TransferListWidget::displayListMenu(const QPoint &)
auto *listMenu = new QMenu(this);
listMenu->setAttribute(Qt::WA_DeleteOnClose);
listMenu->setToolTipsVisible(true);
// Create actions
@@ -821,6 +876,8 @@ void TransferListWidget::displayListMenu(const QPoint &)
connect(actionTopQueuePos, &QAction::triggered, this, &TransferListWidget::topQueuePosSelectedTorrents);
auto *actionBottomQueuePos = new QAction(UIThemeManager::instance()->getIcon("go-bottom"), tr("Move to bottom", "i.e. Move to bottom of the queue"), listMenu);
connect(actionBottomQueuePos, &QAction::triggered, this, &TransferListWidget::bottomQueuePosSelectedTorrents);
auto *actionSetTorrentPath = new QAction(UIThemeManager::instance()->getIcon("inode-directory"), tr("Set location..."), listMenu);
connect(actionSetTorrentPath, &QAction::triggered, this, &TransferListWidget::setSelectedTorrentsLocation);
auto *actionForceRecheck = new QAction(UIThemeManager::instance()->getIcon("document-edit-verify"), tr("Force recheck"), listMenu);
connect(actionForceRecheck, &QAction::triggered, this, &TransferListWidget::recheckSelectedTorrents);
auto *actionForceReannounce = new QAction(UIThemeManager::instance()->getIcon("document-edit-verify"), tr("Force reannounce"), listMenu);
@@ -839,6 +896,13 @@ void TransferListWidget::displayListMenu(const QPoint &)
connect(actionSuperSeedingMode, &QAction::triggered, this, &TransferListWidget::setSelectedTorrentsSuperSeeding);
auto *actionRename = new QAction(UIThemeManager::instance()->getIcon("edit-rename"), tr("Rename..."), listMenu);
connect(actionRename, &QAction::triggered, this, &TransferListWidget::renameSelectedTorrent);
auto *actionSequentialDownload = new TriStateAction(tr("Download in sequential order"), listMenu);
connect(actionSequentialDownload, &QAction::triggered, this, &TransferListWidget::setSelectedTorrentsSequentialDownload);
auto *actionFirstLastPiecePrio = new TriStateAction(tr("Download first and last pieces first"), listMenu);
connect(actionFirstLastPiecePrio, &QAction::triggered, this, &TransferListWidget::setSelectedFirstLastPiecePrio);
auto *actionAutoTMM = new TriStateAction(tr("Automatic Torrent Management"), listMenu);
actionAutoTMM->setToolTip(tr("Automatic mode means that various torrent properties (e.g. save path) will be decided by the associated category"));
connect(actionAutoTMM, &QAction::triggered, this, &TransferListWidget::setSelectedAutoTMMEnabled);
auto *actionEditTracker = new QAction(UIThemeManager::instance()->getIcon("edit-rename"), tr("Edit trackers..."), listMenu);
connect(actionEditTracker, &QAction::triggered, this, &TransferListWidget::editTorrentTrackers);
// End of actions
@@ -851,6 +915,8 @@ void TransferListWidget::displayListMenu(const QPoint &)
bool sequentialDownloadMode = false, prioritizeFirstLast = false;
bool oneHasMetadata = false, oneNotSeed = false;
bool allSameCategory = true;
bool allSameAutoTMM = true;
bool firstAutoTMM = false;
QString firstCategory;
bool first = true;
TagSet tagsInAny;
@@ -874,6 +940,7 @@ void TransferListWidget::displayListMenu(const QPoint &)
if (first)
{
firstAutoTMM = torrent->isAutoTMMEnabled();
tagsInAll = torrentTags;
}
else
@@ -881,6 +948,9 @@ void TransferListWidget::displayListMenu(const QPoint &)
tagsInAll.intersect(torrentTags);
}
if (firstAutoTMM != torrent->isAutoTMMEnabled())
allSameAutoTMM = false;
if (torrent->hasMetadata())
oneHasMetadata = true;
if (!torrent->isSeed())
@@ -940,7 +1010,7 @@ void TransferListWidget::displayListMenu(const QPoint &)
if (oneHasMetadata && oneNotSeed && !allSameSequentialDownloadMode
&& !allSamePrioFirstlast && !allSameSuperSeeding && !allSameCategory
&& needsStart && needsForce && needsPause && needsPreview
&& needsStart && needsForce && needsPause && needsPreview && !allSameAutoTMM
&& hasInfohashV1 && hasInfohashV2)
{
break;
@@ -956,10 +1026,36 @@ void TransferListWidget::displayListMenu(const QPoint &)
listMenu->addSeparator();
listMenu->addAction(actionDelete);
listMenu->addSeparator();
listMenu->addAction(actionSetTorrentPath);
if (selectedIndexes.size() == 1)
listMenu->addAction(actionRename);
listMenu->addAction(actionEditTracker);
// Category Menu
QStringList categories = BitTorrent::Session::instance()->categories();
std::sort(categories.begin(), categories.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
QMenu *categoryMenu = listMenu->addMenu(UIThemeManager::instance()->getIcon("view-categories"), tr("Category"));
categoryMenu->addAction(UIThemeManager::instance()->getIcon("list-add"), tr("New...", "New category...")
, this, &TransferListWidget::askNewCategoryForSelection);
categoryMenu->addAction(UIThemeManager::instance()->getIcon("edit-clear"), tr("Reset", "Reset category")
, this, [this]() { setSelectionCategory(""); });
categoryMenu->addSeparator();
for (const QString &category : asConst(categories))
{
const QString escapedCategory = QString(category).replace('&', "&&"); // avoid '&' becomes accelerator key
QAction *categoryAction = categoryMenu->addAction(UIThemeManager::instance()->getIcon("inode-directory"), escapedCategory
, this, [this, category]() { setSelectionCategory(category); });
if (allSameCategory && (category == firstCategory))
{
categoryAction->setCheckable(true);
categoryAction->setChecked(true);
}
}
// Tag Menu
QStringList tags(BitTorrent::Session::instance()->tags().values());
std::sort(tags.begin(), tags.end(), Utils::Compare::NaturalLessThan<Qt::CaseInsensitive>());
@@ -998,6 +1094,11 @@ void TransferListWidget::displayListMenu(const QPoint &)
tagsMenu->addAction(action);
}
actionAutoTMM->setCheckState(allSameAutoTMM
? (firstAutoTMM ? Qt::Checked : Qt::Unchecked)
: Qt::PartiallyChecked);
listMenu->addAction(actionAutoTMM);
listMenu->addSeparator();
listMenu->addAction(actionTorrentOptions);
if (!oneNotSeed && oneHasMetadata)
@@ -1015,7 +1116,19 @@ void TransferListWidget::displayListMenu(const QPoint &)
addedPreviewAction = true;
}
if (oneNotSeed)
{
actionSequentialDownload->setCheckState(allSameSequentialDownloadMode
? (sequentialDownloadMode ? Qt::Checked : Qt::Unchecked)
: Qt::PartiallyChecked);
listMenu->addAction(actionSequentialDownload);
actionFirstLastPiecePrio->setCheckState(allSamePrioFirstlast
? (prioritizeFirstLast ? Qt::Checked : Qt::Unchecked)
: Qt::PartiallyChecked);
listMenu->addAction(actionFirstLastPiecePrio);
addedPreviewAction = true;
}
if (addedPreviewAction)
listMenu->addSeparator();

View File

@@ -65,6 +65,7 @@ public slots:
void addSelectionTag(const QString &tag);
void removeSelectionTag(const QString &tag);
void clearSelectionTags();
void setSelectedTorrentsLocation();
void pauseAllTorrents();
void resumeAllTorrents();
void startSelectedTorrents();
@@ -90,7 +91,7 @@ public slots:
void setTorrentOptions();
void previewSelectedTorrents();
void hideQueuePosColumn(bool hide);
void displayDLHoSMenu(const QPoint&);
void displayDLHoSMenu(const QPoint &);
void applyNameFilter(const QString &name);
void applyStatusFilter(int f);
void applyCategoryFilter(const QString &category);
@@ -108,7 +109,9 @@ private slots:
void displayListMenu(const QPoint &);
void currentChanged(const QModelIndex &current, const QModelIndex&) override;
void setSelectedTorrentsSuperSeeding(bool enabled) const;
void setSelectedAutoTMMEnabled(bool enabled) const;
void setSelectedTorrentsSequentialDownload(bool enabled) const;
void setSelectedFirstLastPiecePrio(bool enabled) const;
void setSelectedAutoTMMEnabled(bool enabled);
void askNewCategoryForSelection();
void saveSettings();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More