diff --git a/.github/workflows/ci_file_health.yaml b/.github/workflows/ci_file_health.yaml index 48bc52f59..1eab10eb4 100644 --- a/.github/workflows/ci_file_health.yaml +++ b/.github/workflows/ci_file_health.yaml @@ -52,13 +52,13 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | pip install zizmor - IGNORE_RULEID='(.ruleId != "template-injection") - and (.ruleId != "unpinned-uses")' - IGNORE_ID='(.id != "template-injection") - and (.id != "unpinned-uses")' + IGNORE_RULEID='(.ruleId != "zizmor/template-injection") + and (.ruleId != "zizmor/unpinned-uses")' + IGNORE_ID='(.id != "zizmor/template-injection") + and (.id != "zizmor/unpinned-uses")' zizmor \ --format sarif \ - --pedantic \ + --persona auditor \ ./ \ | jq "(.runs[].results |= map(select($IGNORE_RULEID))) | (.runs[].tool.driver.rules |= map(select($IGNORE_ID)))" \ diff --git a/.github/workflows/ci_ubuntu.yaml b/.github/workflows/ci_ubuntu.yaml index 704ab0630..807eef19f 100644 --- a/.github/workflows/ci_ubuntu.yaml +++ b/.github/workflows/ci_ubuntu.yaml @@ -21,7 +21,7 @@ jobs: matrix: libt_version: ["2.0.11", "1.2.20"] qbt_gui: ["GUI=ON", "GUI=OFF"] - qt_version: ["6.5.2"] + qt_version: ["6.6.3"] env: boost_path: "${{ github.workspace }}/../boost" @@ -162,6 +162,7 @@ jobs: - name: Package AppImage run: | + rm -f "${{ runner.workspace }}/Qt/${{ matrix.qt_version }}/gcc_64/plugins/sqldrivers/libqsqlmimer.so" ./linuxdeploy-x86_64.AppImage --appdir qbittorrent --plugin qt rm qbittorrent/apprun-hooks/* cp .github/workflows/helper/appimage/export_vars.sh qbittorrent/apprun-hooks/export_vars.sh diff --git a/.github/workflows/coverity-scan.yaml b/.github/workflows/coverity-scan.yaml index ce970c773..22c5a7082 100644 --- a/.github/workflows/coverity-scan.yaml +++ b/.github/workflows/coverity-scan.yaml @@ -16,7 +16,7 @@ jobs: matrix: libt_version: ["2.0.11"] qbt_gui: ["GUI=ON"] - qt_version: ["6.5.2"] + qt_version: ["6.6.3"] env: boost_path: "${{ github.workspace }}/../boost" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4ab414b05..26852b0d1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -69,7 +69,7 @@ repos: - ts - repo: https://github.com/codespell-project/codespell.git - rev: v2.4.0 + rev: v2.4.1 hooks: - id: codespell name: Check spelling (codespell) @@ -88,7 +88,7 @@ repos: - ts - repo: https://github.com/crate-ci/typos.git - rev: v1.29.4 + rev: v1.32.0 hooks: - id: typos name: Check spelling (typos) diff --git a/CMakeLists.txt b/CMakeLists.txt index 86ede6b3c..97ecb3684 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ project(qBittorrent # version requirements - older versions may work, but you are on your own set(minBoostVersion 1.76) -set(minQt6Version 6.5.0) +set(minQt6Version 6.6.0) set(minOpenSSLVersion 3.0.2) set(minLibtorrent1Version 1.2.19) set(minLibtorrentVersion 2.0.10) diff --git a/INSTALL b/INSTALL index ec3ff78ea..b17e323fb 100644 --- a/INSTALL +++ b/INSTALL @@ -11,7 +11,7 @@ qBittorrent - A BitTorrent client in C++ / Qt - OpenSSL >= 3.0.2 - - Qt 6.5.0 - 6.x + - Qt 6.6.0 - 6.x - zlib >= 1.2.11 diff --git a/cmake/Modules/CommonConfig.cmake b/cmake/Modules/CommonConfig.cmake index b57dc4bcf..85535313a 100644 --- a/cmake/Modules/CommonConfig.cmake +++ b/cmake/Modules/CommonConfig.cmake @@ -20,7 +20,7 @@ target_compile_features(qbt_common_cfg INTERFACE ) target_compile_definitions(qbt_common_cfg INTERFACE - QT_DISABLE_DEPRECATED_UP_TO=0x060500 + QT_DISABLE_DEPRECATED_UP_TO=0x060600 QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_BYTEARRAY diff --git a/src/app/application.cpp b/src/app/application.cpp index 90e785e1a..8fe47033e 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -410,7 +410,7 @@ void Application::setMemoryWorkingSetLimit(const int size) return; m_storeMemoryWorkingSetLimit = size; -#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_MACOS) +#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_LINUX) && !defined(Q_OS_MACOS) applyMemoryWorkingSetLimit(); #endif } @@ -674,9 +674,7 @@ void Application::runExternalProgram(const QString &programTemplate, const BitTo QProcess proc; proc.setProgram(command); proc.setArguments(args); -#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) proc.setUnixProcessParameters(QProcess::UnixProcessFlag::CloseFileDescriptors); -#endif if (proc.startDetached()) { @@ -846,7 +844,7 @@ int Application::exec() printf("%s\n", qUtf8Printable(loadingStr)); #endif -#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_MACOS) +#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_LINUX) && !defined(Q_OS_MACOS) applyMemoryWorkingSetLimit(); #endif @@ -1204,7 +1202,7 @@ void Application::shutdownCleanup([[maybe_unused]] QSessionManager &manager) } #endif -#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_MACOS) +#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_LINUX) && !defined(Q_OS_MACOS) void Application::applyMemoryWorkingSetLimit() const { const size_t MiB = 1024 * 1024; diff --git a/src/app/cmdoptions.cpp b/src/app/cmdoptions.cpp index b65e13fd7..5ed6e4865 100644 --- a/src/app/cmdoptions.cpp +++ b/src/app/cmdoptions.cpp @@ -491,6 +491,12 @@ QString makeUsage(const QString &prgName) { const QString indentation {USAGE_INDENTATION, u' '}; +#if defined(Q_OS_WIN) + const QString noSplashCommand = u"set QBT_NO_SPLASH=1 && " + prgName; +#else + const QString noSplashCommand = u"QBT_NO_SPLASH=1 " + prgName; +#endif + const QString text = QCoreApplication::translate("CMD Options", "Usage:") + u'\n' + indentation + prgName + u' ' + QCoreApplication::translate("CMD Options", "[options] [( | )...]") + u'\n' @@ -542,7 +548,7 @@ QString makeUsage(const QString &prgName) "'parameter-name', environment variable name is 'QBT_PARAMETER_NAME' (in upper " "case, '-' replaced with '_'). To pass flag values, set the variable to '1' or " "'TRUE'. For example, to disable the splash screen: "), 0) + u'\n' - + u"QBT_NO_SPLASH=1 " + prgName + u'\n' + + noSplashCommand + u'\n' + wrapText(QCoreApplication::translate("CMD Options", "Command line parameters take precedence over environment variables"), 0) + u'\n'; return text; diff --git a/src/base/bittorrent/filesearcher.cpp b/src/base/bittorrent/filesearcher.cpp index 2fcd31f8f..e7241b889 100644 --- a/src/base/bittorrent/filesearcher.cpp +++ b/src/base/bittorrent/filesearcher.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2020 Vladimir Golovnev + * Copyright (C) 2020-2025 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -27,13 +27,14 @@ */ #include "filesearcher.h" -#include "base/bittorrent/common.h" -#include "base/bittorrent/infohash.h" -void FileSearcher::search(const BitTorrent::TorrentID &id, const PathList &originalFileNames - , const Path &savePath, const Path &downloadPath, const bool forceAppendExt) +#include + +#include "base/bittorrent/common.h" + +namespace { - const auto findInDir = [](const Path &dirPath, PathList &fileNames, const bool forceAppendExt) -> bool + bool findInDir(const Path &dirPath, PathList &fileNames, const bool forceAppendExt) { bool found = false; for (Path &fileName : fileNames) @@ -58,8 +59,12 @@ void FileSearcher::search(const BitTorrent::TorrentID &id, const PathList &origi } return found; - }; + } +} +void FileSearcher::search(const PathList &originalFileNames, const Path &savePath + , const Path &downloadPath, const bool forceAppendExt, QPromise &promise) +{ Path usedPath = savePath; PathList adjustedFileNames = originalFileNames; const bool found = findInDir(usedPath, adjustedFileNames, (forceAppendExt && downloadPath.isEmpty())); @@ -69,5 +74,5 @@ void FileSearcher::search(const BitTorrent::TorrentID &id, const PathList &origi findInDir(usedPath, adjustedFileNames, forceAppendExt); } - emit searchFinished(id, usedPath, adjustedFileNames); + promise.addResult(FileSearchResult {.savePath = usedPath, .fileNames = adjustedFileNames}); } diff --git a/src/base/bittorrent/filesearcher.h b/src/base/bittorrent/filesearcher.h index 04495bd76..f225b95dc 100644 --- a/src/base/bittorrent/filesearcher.h +++ b/src/base/bittorrent/filesearcher.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2020 Vladimir Golovnev + * Copyright (C) 2020-2025 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,10 +32,13 @@ #include "base/path.h" -namespace BitTorrent +template class QPromise; + +struct FileSearchResult { - class TorrentID; -} + Path savePath; + PathList fileNames; +}; class FileSearcher final : public QObject { @@ -43,12 +46,8 @@ class FileSearcher final : public QObject Q_DISABLE_COPY_MOVE(FileSearcher) public: - FileSearcher() = default; + using QObject::QObject; -public slots: - void search(const BitTorrent::TorrentID &id, const PathList &originalFileNames - , const Path &savePath, const Path &downloadPath, bool forceAppendExt); - -signals: - void searchFinished(const BitTorrent::TorrentID &id, const Path &savePath, const PathList &fileNames); + void search(const PathList &originalFileNames, const Path &savePath + , const Path &downloadPath, bool forceAppendExt, QPromise &promise); }; diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 95c7ac4be..cae03ba83 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -493,7 +493,6 @@ namespace BitTorrent void categoryOptionsChanged(const QString &categoryName); void fullDiskError(Torrent *torrent, const QString &msg); void IPFilterParsed(bool error, int ruleCount); - void loadTorrentFailed(const QString &error); void metadataDownloaded(const TorrentInfo &info); void restored(); void paused(); diff --git a/src/base/bittorrent/sessionimpl.cpp b/src/base/bittorrent/sessionimpl.cpp index 64d274f19..1cbd1b3c6 100644 --- a/src/base/bittorrent/sessionimpl.cpp +++ b/src/base/bittorrent/sessionimpl.cpp @@ -30,6 +30,7 @@ #include "sessionimpl.h" #include +#include #include #include #include @@ -61,6 +62,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +71,7 @@ #include #include #include +#include #include #include #include @@ -313,6 +316,44 @@ namespace break; } } + +#ifdef QBT_USES_LIBTORRENT2 + template + concept HasInfoHashMember = requires (T t) { { t.info_hashes } -> std::convertible_to; }; + + template + concept HasInfoHashMemberFn = requires (T t) { { t.info_hashes() } -> std::convertible_to; }; + + template + InfoHash getInfoHash(const T &t) { return t.info_hashes; } + + template + InfoHash getInfoHash(const T &t) { return t.info_hashes(); } + + InfoHash getInfoHash(const lt::add_torrent_params &addTorrentParams) + { + const bool hasMetadata = (addTorrentParams.ti && addTorrentParams.ti->is_valid()); + return hasMetadata ? getInfoHash(*addTorrentParams.ti) : InfoHash(addTorrentParams.info_hashes); + } + #else + template + concept HasInfoHashMember = requires (T t) { { t.info_hash } -> std::convertible_to; }; + + template + concept HasInfoHashMemberFn = requires (T t) { { t.info_hash() } -> std::convertible_to; }; + + template + InfoHash getInfoHash(const T &t) { return t.info_hash; } + + template + InfoHash getInfoHash(const T &t) { return t.info_hash(); } + + InfoHash getInfoHash(const lt::add_torrent_params &addTorrentParams) + { + const bool hasMetadata = (addTorrentParams.ti && addTorrentParams.ti->is_valid()); + return hasMetadata ? getInfoHash(*addTorrentParams.ti) : InfoHash(addTorrentParams.info_hash); + } + #endif } struct BitTorrent::SessionImpl::ResumeSessionContext final : public QObject @@ -470,11 +511,11 @@ SessionImpl::SessionImpl(QObject *parent) , m_additionalTrackers(BITTORRENT_SESSION_KEY(u"AdditionalTrackers"_s)) , m_isAddTrackersFromURLEnabled(BITTORRENT_SESSION_KEY(u"AddTrackersFromURLEnabled"_s), false) , m_additionalTrackersURL(BITTORRENT_SESSION_KEY(u"AdditionalTrackersURL"_s)) - , m_globalMaxRatio(BITTORRENT_SESSION_KEY(u"GlobalMaxRatio"_s), -1, [](qreal r) { return r < 0 ? -1. : r;}) - , m_globalMaxSeedingMinutes(BITTORRENT_SESSION_KEY(u"GlobalMaxSeedingMinutes"_s), Torrent::NO_SEEDING_TIME_LIMIT - , clampValue(Torrent::NO_SEEDING_TIME_LIMIT, Torrent::MAX_SEEDING_TIME)) - , m_globalMaxInactiveSeedingMinutes(BITTORRENT_SESSION_KEY(u"GlobalMaxInactiveSeedingMinutes"_s), Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT - , clampValue(Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT, Torrent::MAX_INACTIVE_SEEDING_TIME)) + , m_globalMaxRatio(BITTORRENT_SESSION_KEY(u"GlobalMaxRatio"_s), -1, [](qreal r) { return r < 0 ? -1. : r; }) + , m_globalMaxSeedingMinutes(BITTORRENT_SESSION_KEY(u"GlobalMaxSeedingMinutes"_s) + , Torrent::NO_SEEDING_TIME_LIMIT, lowerLimited(Torrent::NO_SEEDING_TIME_LIMIT)) + , m_globalMaxInactiveSeedingMinutes(BITTORRENT_SESSION_KEY(u"GlobalMaxInactiveSeedingMinutes"_s) + , Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT, lowerLimited(Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT)) , m_isAddTorrentToQueueTop(BITTORRENT_SESSION_KEY(u"AddTorrentToTopOfQueue"_s), false) , m_isAddTorrentStopped(BITTORRENT_SESSION_KEY(u"AddTorrentStopped"_s), false) , m_torrentStopCondition(BITTORRENT_SESSION_KEY(u"TorrentStopCondition"_s), Torrent::StopCondition::None) @@ -622,7 +663,6 @@ SessionImpl::SessionImpl(QObject *parent) m_fileSearcher = new FileSearcher; m_fileSearcher->moveToThread(m_ioThread.get()); connect(m_ioThread.get(), &QThread::finished, m_fileSearcher, &QObject::deleteLater); - connect(m_fileSearcher, &FileSearcher::searchFinished, this, &SessionImpl::fileSearchFinished); m_torrentContentRemover = new TorrentContentRemover; m_torrentContentRemover->moveToThread(m_ioThread.get()); @@ -1241,7 +1281,7 @@ qreal SessionImpl::globalMaxRatio() const void SessionImpl::setGlobalMaxRatio(qreal ratio) { if (ratio < 0) - ratio = -1.; + ratio = Torrent::NO_RATIO_LIMIT; if (ratio != globalMaxRatio()) { @@ -1257,7 +1297,7 @@ int SessionImpl::globalMaxSeedingMinutes() const void SessionImpl::setGlobalMaxSeedingMinutes(int minutes) { - minutes = std::clamp(minutes, Torrent::NO_SEEDING_TIME_LIMIT, Torrent::MAX_SEEDING_TIME); + minutes = std::max(minutes, Torrent::NO_SEEDING_TIME_LIMIT); if (minutes != globalMaxSeedingMinutes()) { @@ -1273,7 +1313,7 @@ int SessionImpl::globalMaxInactiveSeedingMinutes() const void SessionImpl::setGlobalMaxInactiveSeedingMinutes(int minutes) { - minutes = std::clamp(minutes, Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT, Torrent::MAX_INACTIVE_SEEDING_TIME); + minutes = std::max(minutes, Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT); if (minutes != globalMaxInactiveSeedingMinutes()) { @@ -1442,10 +1482,8 @@ void SessionImpl::processNextResumeData(ResumeSessionContext *context) LoadTorrentParams resumeData = *loadResumeDataResult; bool needStore = false; + const InfoHash infoHash = getInfoHash(resumeData.ltAddTorrentParams); #ifdef QBT_USES_LIBTORRENT2 - const InfoHash infoHash {(resumeData.ltAddTorrentParams.ti - ? resumeData.ltAddTorrentParams.ti->info_hashes() - : resumeData.ltAddTorrentParams.info_hashes)}; const bool isHybrid = infoHash.isHybrid(); const auto torrentIDv2 = TorrentID::fromInfoHash(infoHash); const auto torrentIDv1 = TorrentID::fromSHA1Hash(infoHash.v1()); @@ -1493,9 +1531,6 @@ void SessionImpl::processNextResumeData(ResumeSessionContext *context) return; } #else - const lt::sha1_hash infoHash = (resumeData.ltAddTorrentParams.ti - ? resumeData.ltAddTorrentParams.ti->info_hash() - : resumeData.ltAddTorrentParams.info_hash); if (torrentID != TorrentID::fromInfoHash(infoHash)) { LogMsg(tr("Failed to resume torrent: inconsistent torrent ID is detected. Torrent: \"%1\"") @@ -1585,16 +1620,23 @@ void SessionImpl::processNextResumeData(ResumeSessionContext *context) #endif qDebug() << "Starting up torrent" << torrentID.toString() << "..."; - m_loadingTorrents.insert(torrentID, resumeData); -#ifdef QBT_USES_LIBTORRENT2 - if (infoHash.isHybrid()) - { - // this allows to know the being added hybrid torrent by its v1 info hash - // without having yet another mapping table - m_hybridTorrentsByAltID.insert(torrentIDv1, nullptr); - } -#endif m_nativeSession->async_add_torrent(resumeData.ltAddTorrentParams); + m_addTorrentAlertHandlers.enqueue([this, resumeData = std::move(resumeData)](const lt::add_torrent_alert *alert) + { + if (alert->error) + { + const QString msg = QString::fromStdString(alert->message()); + LogMsg(tr("Failed to load torrent. Reason: \"%1\"").arg(msg), Log::WARNING); + } + else + { + Torrent *torrent = createTorrent(alert->handle, resumeData); + m_loadedTorrents.append(torrent); + + LogMsg(tr("Restored torrent. Torrent: \"%1\"").arg(torrent->name())); + } + }); + ++context->processingResumeDataCount; } @@ -1642,11 +1684,7 @@ void SessionImpl::endStartup(ResumeSessionContext *context) auto wakeupCheckTimer = new QTimer(this); connect(wakeupCheckTimer, &QTimer::timeout, this, [this] { -#if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) const bool hasSystemSlept = m_wakeupCheckTimestamp.durationElapsed() > 100s; -#else - const bool hasSystemSlept = m_wakeupCheckTimestamp.elapsed() > std::chrono::milliseconds(100s).count(); -#endif if (hasSystemSlept) { LogMsg(tr("System wake-up event detected. Re-announcing to all the trackers...")); @@ -2334,19 +2372,19 @@ void SessionImpl::processTorrentShareLimits(TorrentImpl *torrent) QString description; if (const qreal ratio = torrent->realRatio(); - (ratioLimit >= 0) && (ratio <= Torrent::MAX_RATIO) && (ratio >= ratioLimit)) + (ratioLimit >= 0) && (ratio >= ratioLimit)) { reached = true; description = tr("Torrent reached the share ratio limit."); } else if (const qlonglong seedingTimeInMinutes = torrent->finishedTime() / 60; - (seedingTimeLimit >= 0) && (seedingTimeInMinutes <= Torrent::MAX_SEEDING_TIME) && (seedingTimeInMinutes >= seedingTimeLimit)) + (seedingTimeLimit >= 0) && (seedingTimeInMinutes >= seedingTimeLimit)) { reached = true; description = tr("Torrent reached the seeding time limit."); } else if (const qlonglong inactiveSeedingTimeInMinutes = torrent->timeSinceActivity() / 60; - (inactiveSeedingTimeLimit >= 0) && (inactiveSeedingTimeInMinutes <= Torrent::MAX_INACTIVE_SEEDING_TIME) && (inactiveSeedingTimeInMinutes >= inactiveSeedingTimeLimit)) + (inactiveSeedingTimeLimit >= 0) && (inactiveSeedingTimeInMinutes >= inactiveSeedingTimeLimit)) { reached = true; description = tr("Torrent reached the inactive seeding time limit."); @@ -2380,31 +2418,6 @@ void SessionImpl::processTorrentShareLimits(TorrentImpl *torrent) } } -void SessionImpl::fileSearchFinished(const TorrentID &id, const Path &savePath, const PathList &fileNames) -{ - TorrentImpl *torrent = m_torrents.value(id); - if (torrent) - { - torrent->fileSearchFinished(savePath, fileNames); - return; - } - - const auto loadingTorrentsIter = m_loadingTorrents.find(id); - if (loadingTorrentsIter != m_loadingTorrents.end()) - { - LoadTorrentParams ¶ms = loadingTorrentsIter.value(); - lt::add_torrent_params &p = params.ltAddTorrentParams; - - p.save_path = savePath.toString().toStdString(); - const TorrentInfo torrentInfo {*p.ti}; - const auto nativeIndexes = torrentInfo.nativeIndexes(); - for (int i = 0; i < fileNames.size(); ++i) - p.renamed_files[nativeIndexes[i]] = fileNames[i].toString().toStdString(); - - m_nativeSession->async_add_torrent(p); - } -} - void SessionImpl::torrentContentRemovingFinished(const QString &torrentName, const QString &errorMessage) { if (errorMessage.isEmpty()) @@ -2426,7 +2439,7 @@ Torrent *SessionImpl::getTorrent(const TorrentID &id) const Torrent *SessionImpl::findTorrent(const InfoHash &infoHash) const { const auto id = TorrentID::fromInfoHash(infoHash); - if (Torrent *torrent = m_torrents.value(id); torrent) + if (Torrent *torrent = m_torrents.value(id)) return torrent; if (!infoHash.isHybrid()) @@ -2543,7 +2556,7 @@ bool SessionImpl::cancelDownloadMetadata(const TorrentID &id) return true; #ifdef QBT_USES_LIBTORRENT2 - const InfoHash infoHash {nativeHandle.info_hashes()}; + const InfoHash infoHash = getInfoHash(nativeHandle); if (infoHash.isHybrid()) { // if magnet link was hybrid initially then it is indexed also by v1 info hash @@ -2772,14 +2785,6 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr // alternative ID can be useful to find existing torrent in case if hybrid torrent was added by v1 info hash const auto altID = (infoHash.isHybrid() ? TorrentID::fromSHA1Hash(infoHash.v1()) : TorrentID()); - // We should not add the torrent if it is already - // processed or is pending to add to session - if (m_loadingTorrents.contains(id) || (infoHash.isHybrid() && m_loadingTorrents.contains(altID))) - { - emit addTorrentFailed(infoHash, {AddTorrentError::DuplicateTorrent, tr("Duplicate torrent")}); - return false; - } - if (Torrent *torrent = findTorrent(infoHash)) { // a duplicate torrent is being added @@ -2832,11 +2837,12 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr lt::add_torrent_params &p = loadTorrentParams.ltAddTorrentParams; p = source.ltAddTorrentParams(); - bool isFindingIncompleteFiles = false; - const bool useAutoTMM = loadTorrentParams.useAutoTMM; const Path actualSavePath = useAutoTMM ? categorySavePath(loadTorrentParams.category) : loadTorrentParams.savePath; + bool needFindIncompleteFiles = false; + PathList filePaths; + if (hasMetadata) { // Torrent that is being added with metadata is considered to be added as stopped @@ -2851,7 +2857,7 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr Q_ASSERT(addTorrentParams.filePaths.isEmpty() || (addTorrentParams.filePaths.size() == torrentInfo.filesCount())); - PathList filePaths = addTorrentParams.filePaths; + filePaths = addTorrentParams.filePaths; if (filePaths.isEmpty()) { filePaths = torrentInfo.filePaths(); @@ -2896,18 +2902,7 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr } if (!loadTorrentParams.hasFinishedStatus) - { - const Path actualDownloadPath = useAutoTMM - ? categoryDownloadPath(loadTorrentParams.category) : loadTorrentParams.downloadPath; - findIncompleteFiles(torrentInfo, actualSavePath, actualDownloadPath, filePaths); - isFindingIncompleteFiles = true; - } - - if (!isFindingIncompleteFiles) - { - for (int index = 0; index < filePaths.size(); ++index) - p.renamed_files[nativeIndexes[index]] = filePaths.at(index).toString().toStdString(); - } + needFindIncompleteFiles = true; const int internalFilesCount = torrentInfo.nativeInfo()->files().num_files(); // including .pad files // Use qBittorrent default priority rather than libtorrent's (4) @@ -2927,8 +2922,6 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr loadTorrentParams.name = QString::fromStdString(p.name); } - p.save_path = actualSavePath.toString().toStdString(); - if (isAddTrackersEnabled() && !(hasMetadata && p.ti->priv())) { const auto maxTierIter = std::max_element(p.tracker_tiers.cbegin(), p.tracker_tiers.cend()); @@ -2999,26 +2992,80 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr p.storage = customStorageConstructor; #endif - m_loadingTorrents.insert(id, loadTorrentParams); - if (infoHash.isHybrid()) - m_hybridTorrentsByAltID.insert(altID, nullptr); - if (!isFindingIncompleteFiles) + const auto resolveFileNames = [&, this] + { + if (!needFindIncompleteFiles) + return QtFuture::makeReadyValueFuture(FileSearchResult {.savePath = actualSavePath, .fileNames = filePaths}); + + const Path actualDownloadPath = loadTorrentParams.useAutoTMM + ? categoryDownloadPath(loadTorrentParams.category) : loadTorrentParams.downloadPath; + return findIncompleteFiles(actualSavePath, actualDownloadPath, filePaths); + }; + + resolveFileNames().then(this, [this, id, loadTorrentParams = std::move(loadTorrentParams)](const FileSearchResult &result) mutable + { + lt::add_torrent_params &p = loadTorrentParams.ltAddTorrentParams; + + p.save_path = result.savePath.toString().toStdString(); + if (p.ti) + { + const TorrentInfo torrentInfo {*p.ti}; + const auto nativeIndexes = torrentInfo.nativeIndexes(); + for (int i = 0; i < result.fileNames.size(); ++i) + p.renamed_files[nativeIndexes[i]] = result.fileNames[i].toString().toStdString(); + } + m_nativeSession->async_add_torrent(p); + m_addTorrentAlertHandlers.enqueue([this, loadTorrentParams = std::move(loadTorrentParams)](const lt::add_torrent_alert *alert) + { + if (alert->error) + { + const QString msg = QString::fromStdString(alert->message()); + LogMsg(tr("Failed to add torrent. Reason: \"%1\"").arg(msg), Log::WARNING); + + const InfoHash infoHash = getInfoHash(alert->params); + const AddTorrentError::Kind errorKind = (alert->error == lt::errors::duplicate_torrent) + ? AddTorrentError::DuplicateTorrent : AddTorrentError::Other; + emit addTorrentFailed(infoHash, {errorKind, msg}); + } + else + { + if (loadTorrentParams.addToQueueTop) + alert->handle.queue_position_top(); + + TorrentImpl *torrent = createTorrent(alert->handle, loadTorrentParams); + m_loadedTorrents.append(torrent); + + torrent->requestResumeData(lt::torrent_handle::save_info_dict); + + LogMsg(tr("Added new torrent. Torrent: \"%1\"").arg(torrent->name())); + emit torrentAdded(torrent); + + // The following is useless for newly added magnet + if (torrent->hasMetadata()) + { + if (!torrentExportDirectory().isEmpty()) + exportTorrentFile(torrent, torrentExportDirectory()); + } + } + }); + }); return true; } -void SessionImpl::findIncompleteFiles(const TorrentInfo &torrentInfo, const Path &savePath - , const Path &downloadPath, const PathList &filePaths) const +QFuture SessionImpl::findIncompleteFiles(const Path &savePath, const Path &downloadPath, const PathList &filePaths) const { - Q_ASSERT(filePaths.isEmpty() || (filePaths.size() == torrentInfo.filesCount())); - - const auto searchId = TorrentID::fromInfoHash(torrentInfo.infoHash()); - const PathList originalFileNames = (filePaths.isEmpty() ? torrentInfo.filePaths() : filePaths); - QMetaObject::invokeMethod(m_fileSearcher, [=, this] + QPromise promise; + QFuture future = promise.future(); + promise.start(); + QMetaObject::invokeMethod(m_fileSearcher, [=, this, promise = std::move(promise)]() mutable { - m_fileSearcher->search(searchId, originalFileNames, savePath, downloadPath, isAppendExtensionEnabled()); + m_fileSearcher->search(filePaths, savePath, downloadPath, isAppendExtensionEnabled(), promise); + promise.finish(); }); + + return future; } void SessionImpl::enablePortMapping() @@ -3156,6 +3203,33 @@ bool SessionImpl::downloadMetadata(const TorrentDescriptor &torrentDescr) // Adding torrent to libtorrent session m_nativeSession->async_add_torrent(p); m_downloadedMetadata.insert(id, {}); + m_addTorrentAlertHandlers.enqueue([this](const lt::add_torrent_alert *alert) + { + if (alert->error) + { + const QString msg = QString::fromStdString(alert->message()); + LogMsg(tr("Failed to download torrent metadata. Reason: \"%1\"").arg(msg), Log::WARNING); + + m_downloadedMetadata.remove(getInfoHash(alert->params).toTorrentID()); + } + else + { + const InfoHash infoHash = getInfoHash(alert->handle); + const auto torrentID = TorrentID::fromInfoHash(infoHash); + + if (const auto downloadedMetadataIter = m_downloadedMetadata.find(torrentID) + ; downloadedMetadataIter != m_downloadedMetadata.end()) + { + downloadedMetadataIter.value() = alert->handle; + if (infoHash.isHybrid()) + { + // index hybrid magnet links by both v1 and v2 info hashes + const auto altID = TorrentID::fromSHA1Hash(infoHash.v1()); + m_downloadedMetadata[altID] = alert->handle; + } + } + } + }); return true; } @@ -5204,8 +5278,6 @@ bool SessionImpl::isKnownTorrent(const InfoHash &infoHash) const // in case if hybrid torrent was added by v1 info hash const auto altID = (isHybrid ? TorrentID::fromSHA1Hash(infoHash.v1()) : TorrentID()); - if (m_loadingTorrents.contains(id) || (isHybrid && m_loadingTorrents.contains(altID))) - return true; if (m_downloadedMetadata.contains(id) || (isHybrid && m_downloadedMetadata.contains(altID))) return true; return findTorrent(infoHash); @@ -5418,15 +5490,25 @@ bool SessionImpl::addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &new return true; } +lt::torrent_handle SessionImpl::reloadTorrent(const lt::torrent_handle ¤tHandle, lt::add_torrent_params params) +{ + m_nativeSession->remove_torrent(currentHandle, lt::session::delete_partfile); + + auto *const extensionData = new ExtensionData; + params.userdata = LTClientData(extensionData); +#ifndef QBT_USES_LIBTORRENT2 + params.storage = customStorageConstructor; +#endif + + // libtorrent will post an add_torrent_alert anyway, so we have to add an empty handler to ignore it. + m_addTorrentAlertHandlers.enqueue({}); + return m_nativeSession->add_torrent(std::move(params)); +} + void SessionImpl::moveTorrentStorage(const MoveStorageJob &job) const { -#ifdef QBT_USES_LIBTORRENT2 - const auto id = TorrentID::fromInfoHash(job.torrentHandle.info_hashes()); -#else - const auto id = TorrentID::fromInfoHash(job.torrentHandle.info_hash()); -#endif - const TorrentImpl *torrent = m_torrents.value(id); - const QString torrentName = (torrent ? torrent->name() : id.toString()); + const TorrentImpl *torrent = getTorrent(job.torrentHandle); + const QString torrentName = (torrent ? torrent->name() : getInfoHash(job.torrentHandle).toTorrentID().toString()); LogMsg(tr("Start moving torrent. Torrent: \"%1\". Destination: \"%2\"").arg(torrentName, job.path.toString())); job.torrentHandle.move_storage(job.path.toString().toStdString(), toNative(job.mode)); @@ -5446,7 +5528,7 @@ void SessionImpl::handleMoveTorrentStorageJobFinished(const Path &newPath) const bool torrentHasOutstandingJob = (iter != m_moveStorageQueue.cend()); - TorrentImpl *torrent = m_torrents.value(finishedJob.torrentHandle.info_hash()); + TorrentImpl *torrent = getTorrent(finishedJob.torrentHandle); if (torrent) { torrent->handleMoveStorageJobFinished(newPath, finishedJob.context, torrentHasOutstandingJob); @@ -5454,8 +5536,9 @@ void SessionImpl::handleMoveTorrentStorageJobFinished(const Path &newPath) else if (!torrentHasOutstandingJob) { // Last job is completed for torrent that being removing, so actually remove it - const lt::torrent_handle nativeHandle {finishedJob.torrentHandle}; - const RemovingTorrentData &removingTorrentData = m_removingTorrents[nativeHandle.info_hash()]; + const lt::torrent_handle nativeHandle = finishedJob.torrentHandle; + const TorrentID torrentID = getInfoHash(nativeHandle).toTorrentID(); + const RemovingTorrentData &removingTorrentData = m_removingTorrents[torrentID]; if (removingTorrentData.removeOption == TorrentRemoveOption::KeepContent) m_nativeSession->remove_torrent(nativeHandle, lt::session::delete_partfile); } @@ -5746,72 +5829,12 @@ void SessionImpl::handleAddTorrentAlert(const lt::add_torrent_alert *alert) { ++m_receivedAddTorrentAlertsCount; - if (alert->error) - { - const QString msg = QString::fromStdString(alert->message()); - LogMsg(tr("Failed to load torrent. Reason: \"%1\"").arg(msg), Log::WARNING); - emit loadTorrentFailed(msg); - - const lt::add_torrent_params ¶ms = alert->params; - const bool hasMetadata = (params.ti && params.ti->is_valid()); - -#ifdef QBT_USES_LIBTORRENT2 - const InfoHash infoHash {(hasMetadata ? params.ti->info_hashes() : params.info_hashes)}; - if (infoHash.isHybrid()) - m_hybridTorrentsByAltID.remove(TorrentID::fromSHA1Hash(infoHash.v1())); -#else - const InfoHash infoHash {(hasMetadata ? params.ti->info_hash() : params.info_hash)}; -#endif - if (const auto loadingTorrentsIter = m_loadingTorrents.constFind(TorrentID::fromInfoHash(infoHash)) - ; loadingTorrentsIter != m_loadingTorrents.cend()) - { - const AddTorrentError::Kind errorKind = (alert->error == lt::errors::duplicate_torrent) - ? AddTorrentError::DuplicateTorrent : AddTorrentError::Other; - emit addTorrentFailed(infoHash, {errorKind, msg}); - m_loadingTorrents.erase(loadingTorrentsIter); - } - else if (const auto downloadedMetadataIter = m_downloadedMetadata.constFind(TorrentID::fromInfoHash(infoHash)) - ; downloadedMetadataIter != m_downloadedMetadata.cend()) - { - m_downloadedMetadata.erase(downloadedMetadataIter); - if (infoHash.isHybrid()) - { - // index hybrid magnet links by both v1 and v2 info hashes - const auto altID = TorrentID::fromSHA1Hash(infoHash.v1()); - m_downloadedMetadata.remove(altID); - } - } - + Q_ASSERT(!m_addTorrentAlertHandlers.isEmpty()); + if (m_addTorrentAlertHandlers.isEmpty()) [[unlikely]] return; - } -#ifdef QBT_USES_LIBTORRENT2 - const InfoHash infoHash {alert->handle.info_hashes()}; -#else - const InfoHash infoHash {alert->handle.info_hash()}; -#endif - const auto torrentID = TorrentID::fromInfoHash(infoHash); - - if (const auto loadingTorrentsIter = m_loadingTorrents.constFind(torrentID) - ; loadingTorrentsIter != m_loadingTorrents.cend()) - { - const LoadTorrentParams params = loadingTorrentsIter.value(); - m_loadingTorrents.erase(loadingTorrentsIter); - - Torrent *torrent = createTorrent(alert->handle, params); - m_loadedTorrents.append(torrent); - } - else if (const auto downloadedMetadataIter = m_downloadedMetadata.find(torrentID) - ; downloadedMetadataIter != m_downloadedMetadata.end()) - { - downloadedMetadataIter.value() = alert->handle; - if (infoHash.isHybrid()) - { - // index hybrid magnet links by both v1 and v2 info hashes - const auto altID = TorrentID::fromSHA1Hash(infoHash.v1()); - m_downloadedMetadata[altID] = alert->handle; - } - } + if (const AddTorrentAlertHandler handleAlert = m_addTorrentAlertHandlers.dequeue()) + handleAlert(alert); } void SessionImpl::handleAlert(const lt::alert *alert) @@ -5933,12 +5956,11 @@ void SessionImpl::dispatchTorrentAlert(const lt::torrent_alert *alert) --m_numResumeData; } - const TorrentID torrentID {alert->handle.info_hash()}; - TorrentImpl *torrent = m_torrents.value(torrentID); + TorrentImpl *torrent = getTorrent(alert->handle); #ifdef QBT_USES_LIBTORRENT2 if (!torrent && (alert->type() == lt::metadata_received_alert::alert_type)) { - const InfoHash infoHash {alert->handle.info_hashes()}; + const InfoHash infoHash = getInfoHash(alert->handle); if (infoHash.isHybrid()) torrent = m_torrents.value(TorrentID::fromSHA1Hash(infoHash.v1())); } @@ -5960,42 +5982,17 @@ void SessionImpl::dispatchTorrentAlert(const lt::torrent_alert *alert) TorrentImpl *SessionImpl::createTorrent(const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms) { - auto *const torrent = new TorrentImpl(this, m_nativeSession, nativeHandle, params); + auto *const torrent = new TorrentImpl(this, nativeHandle, params); m_torrents.insert(torrent->id(), torrent); if (const InfoHash infoHash = torrent->infoHash(); infoHash.isHybrid()) m_hybridTorrentsByAltID.insert(TorrentID::fromSHA1Hash(infoHash.v1()), torrent); - if (isRestored()) - { - if (params.addToQueueTop) - nativeHandle.queue_position_top(); - - torrent->requestResumeData(lt::torrent_handle::save_info_dict); - - // The following is useless for newly added magnet - if (torrent->hasMetadata()) - { - if (!torrentExportDirectory().isEmpty()) - exportTorrentFile(torrent, torrentExportDirectory()); - } - } - if (((torrent->ratioLimit() >= 0) || (torrent->seedingTimeLimit() >= 0)) && !m_seedingLimitTimer->isActive()) { m_seedingLimitTimer->start(); } - if (!isRestored()) - { - LogMsg(tr("Restored torrent. Torrent: \"%1\"").arg(torrent->name())); - } - else - { - LogMsg(tr("Added new torrent. Torrent: \"%1\"").arg(torrent->name())); - emit torrentAdded(torrent); - } - // Torrent could have error just after adding to libtorrent if (torrent->hasError()) LogMsg(tr("Torrent errored. Torrent: \"%1\". Error: \"%2\"").arg(torrent->name(), torrent->error()), Log::WARNING); @@ -6003,6 +6000,11 @@ TorrentImpl *SessionImpl::createTorrent(const lt::torrent_handle &nativeHandle, return torrent; } +TorrentImpl *SessionImpl::getTorrent(const lt::torrent_handle &nativeHandle) const +{ + return m_torrents.value(getInfoHash(nativeHandle).toTorrentID()); +} + void SessionImpl::handleTorrentRemovedAlert(const lt::torrent_removed_alert */*alert*/) { // We cannot consider `torrent_removed_alert` as a starting point for removing content, @@ -6012,34 +6014,19 @@ void SessionImpl::handleTorrentRemovedAlert(const lt::torrent_removed_alert */*a void SessionImpl::handleTorrentDeletedAlert(const lt::torrent_deleted_alert *alert) { -#ifdef QBT_USES_LIBTORRENT2 - const auto torrentID = TorrentID::fromInfoHash(alert->info_hashes); -#else - const auto torrentID = TorrentID::fromInfoHash(alert->info_hash); -#endif - handleRemovedTorrent(torrentID); + handleRemovedTorrent(getInfoHash(*alert).toTorrentID()); } void SessionImpl::handleTorrentDeleteFailedAlert(const lt::torrent_delete_failed_alert *alert) { -#ifdef QBT_USES_LIBTORRENT2 - const auto torrentID = TorrentID::fromInfoHash(alert->info_hashes); -#else - const auto torrentID = TorrentID::fromInfoHash(alert->info_hash); -#endif + const TorrentID torrentID = getInfoHash(*alert).toTorrentID(); const auto errorMessage = alert->error ? Utils::String::fromLocal8Bit(alert->error.message()) : QString(); handleRemovedTorrent(torrentID, errorMessage); } void SessionImpl::handleTorrentNeedCertAlert(const lt::torrent_need_cert_alert *alert) { -#ifdef QBT_USES_LIBTORRENT2 - const InfoHash infoHash {alert->handle.info_hashes()}; -#else - const InfoHash infoHash {alert->handle.info_hash()}; -#endif - const auto torrentID = TorrentID::fromInfoHash(infoHash); - + const TorrentID torrentID = getInfoHash(alert->handle).toTorrentID(); TorrentImpl *const torrent = m_torrents.value(torrentID); if (!torrent) [[unlikely]] return; @@ -6053,7 +6040,8 @@ void SessionImpl::handleTorrentNeedCertAlert(const lt::torrent_need_cert_alert * void SessionImpl::handleMetadataReceivedAlert(const lt::metadata_received_alert *alert) { - const TorrentID torrentID {alert->handle.info_hash()}; + const InfoHash infoHash = getInfoHash(alert->handle); + const TorrentID torrentID = infoHash.toTorrentID(); bool found = false; if (const auto iter = m_downloadedMetadata.constFind(torrentID); iter != m_downloadedMetadata.cend()) @@ -6062,7 +6050,6 @@ void SessionImpl::handleMetadataReceivedAlert(const lt::metadata_received_alert m_downloadedMetadata.erase(iter); } #ifdef QBT_USES_LIBTORRENT2 - const InfoHash infoHash {alert->handle.info_hashes()}; if (infoHash.isHybrid()) { const auto altID = TorrentID::fromSHA1Hash(infoHash.v1()); @@ -6084,7 +6071,7 @@ void SessionImpl::handleMetadataReceivedAlert(const lt::metadata_received_alert void SessionImpl::handleFileErrorAlert(const lt::file_error_alert *alert) { - TorrentImpl *const torrent = m_torrents.value(alert->handle.info_hash()); + TorrentImpl *const torrent = getTorrent(alert->handle); if (!torrent) return; @@ -6155,7 +6142,7 @@ void SessionImpl::handlePeerBanAlert(const lt::peer_ban_alert *alert) void SessionImpl::handleUrlSeedAlert(const lt::url_seed_alert *alert) { - const TorrentImpl *torrent = m_torrents.value(alert->handle.info_hash()); + const TorrentImpl *torrent = getTorrent(alert->handle); if (!torrent) return; @@ -6334,14 +6321,8 @@ void SessionImpl::handleStorageMovedAlert(const lt::storage_moved_alert *alert) const Path newPath {QString::fromUtf8(alert->storage_path())}; Q_ASSERT(newPath == currentJob.path); -#ifdef QBT_USES_LIBTORRENT2 - const auto id = TorrentID::fromInfoHash(currentJob.torrentHandle.info_hashes()); -#else - const auto id = TorrentID::fromInfoHash(currentJob.torrentHandle.info_hash()); -#endif - - TorrentImpl *torrent = m_torrents.value(id); - const QString torrentName = (torrent ? torrent->name() : id.toString()); + TorrentImpl *torrent = getTorrent(currentJob.torrentHandle); + const QString torrentName = (torrent ? torrent->name() : getInfoHash(currentJob.torrentHandle).toTorrentID().toString()); LogMsg(tr("Moved torrent successfully. Torrent: \"%1\". Destination: \"%2\"").arg(torrentName, newPath.toString())); handleMoveTorrentStorageJobFinished(newPath); @@ -6354,14 +6335,8 @@ void SessionImpl::handleStorageMovedFailedAlert(const lt::storage_moved_failed_a const MoveStorageJob ¤tJob = m_moveStorageQueue.constFirst(); Q_ASSERT(currentJob.torrentHandle == alert->handle); -#ifdef QBT_USES_LIBTORRENT2 - const auto id = TorrentID::fromInfoHash(currentJob.torrentHandle.info_hashes()); -#else - const auto id = TorrentID::fromInfoHash(currentJob.torrentHandle.info_hash()); -#endif - - TorrentImpl *torrent = m_torrents.value(id); - const QString torrentName = (torrent ? torrent->name() : id.toString()); + TorrentImpl *torrent = getTorrent(currentJob.torrentHandle); + const QString torrentName = (torrent ? torrent->name() : getInfoHash(currentJob.torrentHandle).toTorrentID().toString()); const Path currentLocation = (torrent ? torrent->actualStorageLocation() : Path(alert->handle.status(lt::torrent_handle::query_save_path).save_path)); const QString errorMessage = QString::fromStdString(alert->message()); @@ -6378,12 +6353,7 @@ void SessionImpl::handleStateUpdateAlert(const lt::state_update_alert *alert) for (const lt::torrent_status &status : alert->status) { -#ifdef QBT_USES_LIBTORRENT2 - const auto id = TorrentID::fromInfoHash(status.info_hashes); -#else - const auto id = TorrentID::fromInfoHash(status.info_hash); -#endif - TorrentImpl *const torrent = m_torrents.value(id); + TorrentImpl *const torrent = getTorrent(status.handle); if (!torrent) continue; @@ -6427,7 +6397,7 @@ void SessionImpl::handleI2PAlert(const lt::i2p_alert *alert) const void SessionImpl::handleTrackerAlert(const lt::tracker_alert *alert) { - TorrentImpl *torrent = m_torrents.value(alert->handle.info_hash()); + TorrentImpl *torrent = getTorrent(alert->handle); if (!torrent) return; @@ -6453,8 +6423,9 @@ void SessionImpl::handleTrackerAlert(const lt::tracker_alert *alert) #ifdef QBT_USES_LIBTORRENT2 void SessionImpl::handleTorrentConflictAlert(const lt::torrent_conflict_alert *alert) { - const auto torrentIDv1 = TorrentID::fromSHA1Hash(alert->metadata->info_hashes().v1); - const auto torrentIDv2 = TorrentID::fromSHA256Hash(alert->metadata->info_hashes().v2); + const InfoHash infoHash = getInfoHash(*alert->metadata); + const auto torrentIDv1 = TorrentID::fromSHA1Hash(infoHash.v1()); + const auto torrentIDv2 = TorrentID::fromSHA256Hash(infoHash.v2()); TorrentImpl *torrent1 = m_torrents.value(torrentIDv1); TorrentImpl *torrent2 = m_torrents.value(torrentIDv2); if (torrent2) diff --git a/src/base/bittorrent/sessionimpl.h b/src/base/bittorrent/sessionimpl.h index 1ad2108ff..aec79c4ea 100644 --- a/src/base/bittorrent/sessionimpl.h +++ b/src/base/bittorrent/sessionimpl.h @@ -30,6 +30,7 @@ #pragma once #include +#include #include #include @@ -44,6 +45,7 @@ #include #include #include +#include #include #include @@ -61,12 +63,16 @@ class QString; class QTimer; class QUrl; +template class QFuture; + class BandwidthScheduler; class FileSearcher; class FilterParserThread; class FreeDiskSpaceChecker; class NativeSessionExtension; +struct FileSearchResult; + namespace BitTorrent { enum class MoveStorageMode; @@ -478,8 +484,9 @@ namespace BitTorrent bool addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &newPath, MoveStorageMode mode, MoveStorageContext context); - void findIncompleteFiles(const TorrentInfo &torrentInfo, const Path &savePath - , const Path &downloadPath, const PathList &filePaths = {}) const; + lt::torrent_handle reloadTorrent(const lt::torrent_handle ¤tHandle, lt::add_torrent_params params); + + QFuture findIncompleteFiles(const Path &savePath, const Path &downloadPath, const PathList &filePaths = {}) const; void enablePortMapping(); void disablePortMapping(); @@ -514,7 +521,6 @@ namespace BitTorrent void generateResumeData(); void handleIPFilterParsed(int ruleCount); void handleIPFilterError(); - void fileSearchFinished(const TorrentID &id, const Path &savePath, const PathList &fileNames); void torrentContentRemovingFinished(const QString &torrentName, const QString &errorMessage); private: @@ -604,6 +610,7 @@ namespace BitTorrent #endif TorrentImpl *createTorrent(const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms); + TorrentImpl *getTorrent(const lt::torrent_handle &nativeHandle) const; void saveResumeData(); void saveTorrentsQueue(); @@ -810,11 +817,13 @@ namespace BitTorrent FileSearcher *m_fileSearcher = nullptr; TorrentContentRemover *m_torrentContentRemover = nullptr; + using AddTorrentAlertHandler = std::function; + QQueue m_addTorrentAlertHandlers; + QHash m_downloadedMetadata; QHash m_torrents; QHash m_hybridTorrentsByAltID; - QHash m_loadingTorrents; QHash m_removingTorrents; QHash m_changedTorrentIDs; QMap m_categories; diff --git a/src/base/bittorrent/torrent.cpp b/src/base/bittorrent/torrent.cpp index c92fb9aea..b8098d77f 100644 --- a/src/base/bittorrent/torrent.cpp +++ b/src/base/bittorrent/torrent.cpp @@ -29,6 +29,8 @@ #include "torrent.h" +#include + #include #include "infohash.h" @@ -51,9 +53,7 @@ namespace BitTorrent const int Torrent::USE_GLOBAL_INACTIVE_SEEDING_TIME = -2; const int Torrent::NO_INACTIVE_SEEDING_TIME_LIMIT = -1; - const qreal Torrent::MAX_RATIO = 9999; - const int Torrent::MAX_SEEDING_TIME = 525600; - const int Torrent::MAX_INACTIVE_SEEDING_TIME = 525600; + const qreal Torrent::MAX_RATIO = std::numeric_limits::infinity(); TorrentID Torrent::id() const { diff --git a/src/base/bittorrent/torrent.h b/src/base/bittorrent/torrent.h index d2eefbabf..644af158b 100644 --- a/src/base/bittorrent/torrent.h +++ b/src/base/bittorrent/torrent.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2015-2024 Vladimir Golovnev + * Copyright (C) 2015-2025 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -45,6 +45,8 @@ class QByteArray; class QDateTime; class QUrl; +template class QFuture; + namespace BitTorrent { enum class DownloadPriority; @@ -132,8 +134,6 @@ namespace BitTorrent static const int NO_INACTIVE_SEEDING_TIME_LIMIT; static const qreal MAX_RATIO; - static const int MAX_SEEDING_TIME; - static const int MAX_INACTIVE_SEEDING_TIME; using TorrentContentHandler::TorrentContentHandler; @@ -275,10 +275,7 @@ namespace BitTorrent virtual bool isDHTDisabled() const = 0; virtual bool isPEXDisabled() const = 0; virtual bool isLSDDisabled() const = 0; - virtual QList peers() const = 0; virtual QBitArray pieces() const = 0; - virtual QBitArray downloadingPieces() const = 0; - virtual QList pieceAvailability() const = 0; virtual qreal distributedCopies() const = 0; virtual qreal maxRatio() const = 0; virtual int maxSeedingTime() const = 0; @@ -325,10 +322,10 @@ namespace BitTorrent virtual nonstd::expected exportToBuffer() const = 0; virtual nonstd::expected exportToFile(const Path &path) const = 0; - virtual void fetchPeerInfo(std::function)> resultHandler) const = 0; - virtual void fetchURLSeeds(std::function)> resultHandler) const = 0; - virtual void fetchPieceAvailability(std::function)> resultHandler) const = 0; - virtual void fetchDownloadingPieces(std::function resultHandler) const = 0; + virtual QFuture> fetchPeerInfo() const = 0; + virtual QFuture> fetchURLSeeds() const = 0; + virtual QFuture> fetchPieceAvailability() const = 0; + virtual QFuture fetchDownloadingPieces() const = 0; TorrentID id() const; bool isRunning() const; diff --git a/src/base/bittorrent/torrentcontenthandler.h b/src/base/bittorrent/torrentcontenthandler.h index 2e3702d2c..350db753e 100644 --- a/src/base/bittorrent/torrentcontenthandler.h +++ b/src/base/bittorrent/torrentcontenthandler.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2022-2023 Vladimir Golovnev + * Copyright (C) 2022-2025 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -34,6 +34,8 @@ #include "abstractfilestorage.h" #include "downloadpriority.h" +template class QFuture; + namespace BitTorrent { class TorrentContentHandler : public QObject, public AbstractFileStorage @@ -52,8 +54,7 @@ namespace BitTorrent * This is not the same as torrrent availability, it is just a fraction of pieces * that can be downloaded right now. It varies between 0 to 1. */ - virtual QList availableFileFractions() const = 0; - virtual void fetchAvailableFileFractions(std::function)> resultHandler) const = 0; + virtual QFuture> fetchAvailableFileFractions() const = 0; virtual void prioritizeFiles(const QList &priorities) = 0; virtual void flushCache() const = 0; diff --git a/src/base/bittorrent/torrentcreator.cpp b/src/base/bittorrent/torrentcreator.cpp index 727e9a9dc..23568b2df 100644 --- a/src/base/bittorrent/torrentcreator.cpp +++ b/src/base/bittorrent/torrentcreator.cpp @@ -124,18 +124,20 @@ void TorrentCreator::run() // need to sort the file names by natural sort order QStringList dirs = {m_params.sourcePath.data()}; -#ifdef Q_OS_WIN - // libtorrent couldn't handle .lnk files on Windows - // Also, Windows users do not expect torrent creator to traverse into .lnk files so skip over them - const QDir::Filters dirFilters {QDir::AllDirs | QDir::NoDotAndDotDot | QDir::NoSymLinks}; -#else - const QDir::Filters dirFilters {QDir::AllDirs | QDir::NoDotAndDotDot}; -#endif - QDirIterator dirIter {m_params.sourcePath.data(), dirFilters, QDirIterator::Subdirectories}; + QDirIterator dirIter {m_params.sourcePath.data(), (QDir::AllDirs | QDir::NoDotAndDotDot), QDirIterator::Subdirectories}; while (dirIter.hasNext()) { - const QString filePath = dirIter.next(); - dirs.append(filePath); + const QFileInfo dirInfo = dirIter.nextFileInfo(); + +#ifdef Q_OS_WIN + // .lnk to directory + // Windows users do not expect torrent creator to traverse into .lnk files so skip over them + if (dirInfo.isShortcut()) + continue; +#endif + + const QString dirPath = dirInfo.filePath(); + dirs.append(dirPath); } std::sort(dirs.begin(), dirs.end(), naturalLessThan); @@ -146,19 +148,29 @@ void TorrentCreator::run() { QStringList tmpNames; // natural sort files within each dir -#ifdef Q_OS_WIN - const QDir::Filters fileFilters {QDir::Files | QDir::NoSymLinks}; -#else - const QDir::Filters fileFilters {QDir::Files}; -#endif - QDirIterator fileIter {dir, fileFilters}; + QDirIterator fileIter {dir, QDir::Files}; while (fileIter.hasNext()) { const QFileInfo fileInfo = fileIter.nextFileInfo(); + const Path filePath {fileInfo.filePath()}; + qint64 fileSize = fileInfo.size(); - const Path relFilePath = parentPath.relativePathOf(Path(fileInfo.filePath())); +#ifdef Q_OS_WIN + // .lnk to file + // libtorrent couldn't handle .lnk files on Windows + if (fileInfo.isShortcut()) + continue; + + // file symbolic link + // QFileInfo::size() failed to return the target file size + // and we need to redirect it manually + if (fileInfo.isSymbolicLink()) + fileSize = QFileInfo(fileInfo.symLinkTarget()).size(); +#endif + + const Path relFilePath = parentPath.relativePathOf(filePath); tmpNames.append(relFilePath.toString()); - fileSizeMap[tmpNames.last()] = fileInfo.size(); + fileSizeMap[tmpNames.last()] = fileSize; } std::sort(tmpNames.begin(), tmpNames.end(), naturalLessThan); @@ -174,8 +186,8 @@ void TorrentCreator::run() #ifdef QBT_USES_LIBTORRENT2 lt::create_torrent newTorrent {fs, m_params.pieceSize, toNativeTorrentFormatFlag(m_params.torrentFormat)}; #else - lt::create_torrent newTorrent(fs, m_params.pieceSize, m_params.paddedFileSizeLimit - , (m_params.isAlignmentOptimized ? lt::create_torrent::optimize_alignment : lt::create_flags_t {})); + lt::create_torrent newTorrent {fs, m_params.pieceSize, m_params.paddedFileSizeLimit + , (m_params.isAlignmentOptimized ? lt::create_torrent::optimize_alignment : lt::create_flags_t {})}; #endif // Add url seeds diff --git a/src/base/bittorrent/torrentimpl.cpp b/src/base/bittorrent/torrentimpl.cpp index 45cc7f3bf..97921246c 100644 --- a/src/base/bittorrent/torrentimpl.cpp +++ b/src/base/bittorrent/torrentimpl.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2015-2024 Vladimir Golovnev + * Copyright (C) 2015-2025 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -51,7 +51,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -67,6 +69,7 @@ #include "common.h" #include "downloadpriority.h" #include "extensiondata.h" +#include "filesearcher.h" #include "loadtorrentparams.h" #include "ltqbitarray.h" #include "lttypecast.h" @@ -287,11 +290,9 @@ namespace // TorrentImpl -TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession - , const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms) +TorrentImpl::TorrentImpl(SessionImpl *session, const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms) : Torrent(session) , m_session(session) - , m_nativeSession(nativeSession) , m_nativeHandle(nativeHandle) #ifdef QBT_USES_LIBTORRENT2 , m_infoHash(m_nativeHandle.info_hashes()) @@ -1465,48 +1466,11 @@ bool TorrentImpl::isLSDDisabled() const return static_cast(m_nativeStatus.flags & lt::torrent_flags::disable_lsd); } -QList TorrentImpl::peers() const -{ - std::vector nativePeers; - m_nativeHandle.get_peer_info(nativePeers); - - QList peers; - peers.reserve(static_cast(nativePeers.size())); - - for (const lt::peer_info &peer : nativePeers) - peers.append(PeerInfo(peer, pieces())); - - return peers; -} - QBitArray TorrentImpl::pieces() const { return m_pieces; } -QBitArray TorrentImpl::downloadingPieces() const -{ - if (!hasMetadata()) - return {}; - - std::vector queue; - m_nativeHandle.get_download_queue(queue); - - QBitArray result {piecesCount()}; - for (const lt::partial_piece_info &info : queue) - result.setBit(LT::toUnderlyingType(info.piece_index)); - - return result; -} - -QList TorrentImpl::pieceAvailability() const -{ - std::vector avail; - m_nativeHandle.piece_availability(avail); - - return {avail.cbegin(), avail.cend()}; -} - qreal TorrentImpl::distributedCopies() const { return m_nativeStatus.distributed_copies; @@ -1549,7 +1513,8 @@ qreal TorrentImpl::realRatio() const const qreal ratio = upload / static_cast(download); Q_ASSERT(ratio >= 0); - return (ratio > MAX_RATIO) ? MAX_RATIO : ratio; + + return ratio; } int TorrentImpl::uploadPayloadRate() const @@ -1751,12 +1716,6 @@ void TorrentImpl::applyFirstLastPiecePriority(const bool enabled) m_nativeHandle.prioritize_pieces(piecePriorities); } -void TorrentImpl::fileSearchFinished(const Path &savePath, const PathList &fileNames) -{ - if (m_maintenanceJob == MaintenanceJob::HandleMetadata) - endReceivedMetadataHandling(savePath, fileNames); -} - TrackerEntryStatus TorrentImpl::updateTrackerEntryStatus(const lt::announce_entry &announceEntry, const QHash> &updateInfo) { const auto it = std::find_if(m_trackerEntryStatuses.begin(), m_trackerEntryStatuses.end() @@ -1876,16 +1835,6 @@ void TorrentImpl::reload() { try { - m_completedFiles.fill(false); - m_filesProgress.fill(0); - m_pieces.fill(false); - m_nativeStatus.pieces.clear_all(); - m_nativeStatus.num_pieces = 0; - - const auto queuePos = m_nativeHandle.queue_position(); - - m_nativeSession->remove_torrent(m_nativeHandle, lt::session::delete_partfile); - lt::add_torrent_params p = m_ltAddTorrentParams; p.flags |= lt::torrent_flags::update_subscribe | lt::torrent_flags::override_trackers @@ -1905,19 +1854,21 @@ void TorrentImpl::reload() p.flags &= ~(lt::torrent_flags::auto_managed | lt::torrent_flags::paused); } - auto *const extensionData = new ExtensionData; - p.userdata = LTClientData(extensionData); -#ifndef QBT_USES_LIBTORRENT2 - p.storage = customStorageConstructor; -#endif - m_nativeHandle = m_nativeSession->add_torrent(p); + const auto queuePos = m_nativeHandle.queue_position(); - m_nativeStatus = extensionData->status; + m_nativeHandle = m_session->reloadTorrent(m_nativeHandle, std::move(p)); + m_nativeStatus = static_cast(m_nativeHandle.userdata())->status; if (queuePos >= lt::queue_position_t {}) m_nativeHandle.queue_position_set(queuePos); m_nativeStatus.queue_position = queuePos; + m_completedFiles.fill(false); + m_filesProgress.fill(0); + m_pieces.fill(false); + m_nativeStatus.pieces.clear_all(); + m_nativeStatus.num_pieces = 0; + updateState(); } catch (const lt::system_error &err) @@ -2150,7 +2101,7 @@ void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p) // URL seed list have been changed by libtorrent for some reason, so we need to update cached one. // Unfortunately, URL seed list containing in "resume data" is generated according to different rules // than the list we usually cache, so we have to request it from the appropriate source. - fetchURLSeeds([this](const QList &urlSeeds) { m_urlSeeds = urlSeeds; }); + fetchURLSeeds().then(this, [this](const QList &urlSeeds) { m_urlSeeds = urlSeeds; }); } if ((m_maintenanceJob == MaintenanceJob::HandleMetadata) && p->params.ti) @@ -2197,7 +2148,12 @@ void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p) filePaths[i] = Path(it->second); } - m_session->findIncompleteFiles(metadata, savePath(), downloadPath(), filePaths); + m_session->findIncompleteFiles(savePath(), downloadPath(), filePaths).then(this + , [this](const FileSearchResult &result) + { + if (m_maintenanceJob == MaintenanceJob::HandleMetadata) + endReceivedMetadataHandling(result.savePath, result.fileNames); + }); } else { @@ -2712,8 +2668,6 @@ void TorrentImpl::setRatioLimit(qreal limit) { if (limit < USE_GLOBAL_RATIO) limit = NO_RATIO_LIMIT; - else if (limit > MAX_RATIO) - limit = MAX_RATIO; if (m_ratioLimit != limit) { @@ -2727,8 +2681,6 @@ void TorrentImpl::setSeedingTimeLimit(int limit) { if (limit < USE_GLOBAL_SEEDING_TIME) limit = NO_SEEDING_TIME_LIMIT; - else if (limit > MAX_SEEDING_TIME) - limit = MAX_SEEDING_TIME; if (m_seedingTimeLimit != limit) { @@ -2742,8 +2694,6 @@ void TorrentImpl::setInactiveSeedingTimeLimit(int limit) { if (limit < USE_GLOBAL_INACTIVE_SEEDING_TIME) limit = NO_INACTIVE_SEEDING_TIME_LIMIT; - else if (limit > MAX_INACTIVE_SEEDING_TIME) - limit = MAX_SEEDING_TIME; if (m_inactiveSeedingTimeLimit != limit) { @@ -2930,9 +2880,9 @@ nonstd::expected TorrentImpl::exportToFile(const Path &path) cons return {}; } -void TorrentImpl::fetchPeerInfo(std::function)> resultHandler) const +QFuture> TorrentImpl::fetchPeerInfo() const { - invokeAsync([nativeHandle = m_nativeHandle, allPieces = pieces()]() -> QList + return invokeAsync([nativeHandle = m_nativeHandle, allPieces = pieces()]() -> QList { try { @@ -2947,13 +2897,12 @@ void TorrentImpl::fetchPeerInfo(std::function)> resultHand catch (const std::exception &) {} return {}; - } - , std::move(resultHandler)); + }); } -void TorrentImpl::fetchURLSeeds(std::function)> resultHandler) const +QFuture> TorrentImpl::fetchURLSeeds() const { - invokeAsync([nativeHandle = m_nativeHandle]() -> QList + return invokeAsync([nativeHandle = m_nativeHandle]() -> QList { try { @@ -2967,13 +2916,12 @@ void TorrentImpl::fetchURLSeeds(std::function)> resultHandler) catch (const std::exception &) {} return {}; - } - , std::move(resultHandler)); + }); } -void TorrentImpl::fetchPieceAvailability(std::function)> resultHandler) const +QFuture> TorrentImpl::fetchPieceAvailability() const { - invokeAsync([nativeHandle = m_nativeHandle]() -> QList + return invokeAsync([nativeHandle = m_nativeHandle]() -> QList { try { @@ -2984,13 +2932,12 @@ void TorrentImpl::fetchPieceAvailability(std::function)> result catch (const std::exception &) {} return {}; - } - , std::move(resultHandler)); + }); } -void TorrentImpl::fetchDownloadingPieces(std::function resultHandler) const +QFuture TorrentImpl::fetchDownloadingPieces() const { - invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QBitArray + return invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QBitArray { try { @@ -3009,13 +2956,12 @@ void TorrentImpl::fetchDownloadingPieces(std::function resultH catch (const std::exception &) {} return {}; - } - , std::move(resultHandler)); + }); } -void TorrentImpl::fetchAvailableFileFractions(std::function)> resultHandler) const +QFuture> TorrentImpl::fetchAvailableFileFractions() const { - invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QList + return invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QList { if (!torrentInfo.isValid() || (torrentInfo.filesCount() <= 0)) return {}; @@ -3049,8 +2995,7 @@ void TorrentImpl::fetchAvailableFileFractions(std::function)> catch (const std::exception &) {} return {}; - } - , std::move(resultHandler)); + }); } void TorrentImpl::prioritizeFiles(const QList &priorities) @@ -3090,47 +3035,17 @@ void TorrentImpl::prioritizeFiles(const QList &priorities) manageActualFilePaths(); } -QList TorrentImpl::availableFileFractions() const +template +QFuture> TorrentImpl::invokeAsync(Func &&func) const { - Q_ASSERT(hasMetadata()); - - const int filesCount = this->filesCount(); - if (filesCount <= 0) return {}; - - const QList piecesAvailability = pieceAvailability(); - // libtorrent returns empty array for seeding only torrents - if (piecesAvailability.empty()) return QList(filesCount, -1); - - QList res; - res.reserve(filesCount); - for (int i = 0; i < filesCount; ++i) + QPromise> promise; + const auto future = promise.future(); + promise.start(); + m_session->invokeAsync([func = std::forward(func), promise = std::move(promise)]() mutable { - const TorrentInfo::PieceRange filePieces = m_torrentInfo.filePieces(i); - - int availablePieces = 0; - for (const int piece : filePieces) - availablePieces += (piecesAvailability[piece] > 0) ? 1 : 0; - - const qreal availability = filePieces.isEmpty() - ? 1 // the file has no pieces, so it is available by default - : static_cast(availablePieces) / filePieces.size(); - res.push_back(availability); - } - return res; -} - -template -void TorrentImpl::invokeAsync(Func func, Callback resultHandler) const -{ - m_session->invokeAsync([session = m_session - , func = std::move(func) - , resultHandler = std::move(resultHandler) - , thisTorrent = QPointer(this)]() mutable - { - session->invoke([result = func(), thisTorrent, resultHandler = std::move(resultHandler)] - { - if (thisTorrent) - resultHandler(result); - }); + promise.addResult(func()); + promise.finish(); }); + + return future; } diff --git a/src/base/bittorrent/torrentimpl.h b/src/base/bittorrent/torrentimpl.h index 5c54b2d00..1875ba11c 100644 --- a/src/base/bittorrent/torrentimpl.h +++ b/src/base/bittorrent/torrentimpl.h @@ -94,8 +94,7 @@ namespace BitTorrent Q_DISABLE_COPY_MOVE(TorrentImpl) public: - TorrentImpl(SessionImpl *session, lt::session *nativeSession - , const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms); + TorrentImpl(SessionImpl *session, const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms); ~TorrentImpl() override; bool isValid() const; @@ -203,10 +202,7 @@ namespace BitTorrent bool isDHTDisabled() const override; bool isPEXDisabled() const override; bool isLSDDisabled() const override; - QList peers() const override; QBitArray pieces() const override; - QBitArray downloadingPieces() const override; - QList pieceAvailability() const override; qreal distributedCopies() const override; qreal maxRatio() const override; int maxSeedingTime() const override; @@ -220,7 +216,6 @@ namespace BitTorrent int connectionsCount() const override; int connectionsLimit() const override; qlonglong nextAnnounce() const override; - QList availableFileFractions() const override; void setName(const QString &name) override; void setSequentialDownload(bool enable) override; @@ -258,11 +253,11 @@ namespace BitTorrent nonstd::expected exportToBuffer() const override; nonstd::expected exportToFile(const Path &path) const override; - void fetchPeerInfo(std::function)> resultHandler) const override; - void fetchURLSeeds(std::function)> resultHandler) const override; - void fetchPieceAvailability(std::function)> resultHandler) const override; - void fetchDownloadingPieces(std::function resultHandler) const override; - void fetchAvailableFileFractions(std::function)> resultHandler) const override; + QFuture> fetchPeerInfo() const override; + QFuture> fetchURLSeeds() const override; + QFuture> fetchPieceAvailability() const override; + QFuture fetchDownloadingPieces() const override; + QFuture> fetchAvailableFileFractions() const override; bool needSaveResumeData() const; @@ -278,7 +273,6 @@ namespace BitTorrent void requestResumeData(lt::resume_data_flags_t flags = {}); void deferredRequestResumeData(); void handleMoveStorageJobFinished(const Path &path, MoveStorageContext context, bool hasOutstandingJob); - void fileSearchFinished(const Path &savePath, const PathList &fileNames); TrackerEntryStatus updateTrackerEntryStatus(const lt::announce_entry &announceEntry, const QHash> &updateInfo); void resetTrackerEntryStatuses(); @@ -326,11 +320,10 @@ namespace BitTorrent nonstd::expected exportTorrent() const; - template - void invokeAsync(Func func, Callback resultHandler) const; + template + QFuture> invokeAsync(Func &&func) const; SessionImpl *const m_session = nullptr; - lt::session *m_nativeSession = nullptr; lt::torrent_handle m_nativeHandle; mutable lt::torrent_status m_nativeStatus; TorrentState m_state = TorrentState::Unknown; diff --git a/src/base/search/searchdownloadhandler.cpp b/src/base/search/searchdownloadhandler.cpp index 34289bf8c..42869579f 100644 --- a/src/base/search/searchdownloadhandler.cpp +++ b/src/base/search/searchdownloadhandler.cpp @@ -42,7 +42,7 @@ SearchDownloadHandler::SearchDownloadHandler(const QString &pluginName, const QS , m_downloadProcess {new QProcess(this)} { m_downloadProcess->setProcessEnvironment(m_manager->proxyEnvironment()); -#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) +#ifdef Q_OS_UNIX m_downloadProcess->setUnixProcessParameters(QProcess::UnixProcessFlag::CloseFileDescriptors); #endif connect(m_downloadProcess, qOverload(&QProcess::finished) @@ -55,7 +55,7 @@ SearchDownloadHandler::SearchDownloadHandler(const QString &pluginName, const QS url }; // Launch search - m_downloadProcess->start(Utils::ForeignApps::pythonInfo().executableName, params, QIODevice::ReadOnly); + m_downloadProcess->start(Utils::ForeignApps::pythonInfo().executablePath.data(), params, QIODevice::ReadOnly); } void SearchDownloadHandler::downloadProcessFinished(int exitcode) diff --git a/src/base/search/searchhandler.cpp b/src/base/search/searchhandler.cpp index 114ddce5f..8479a1a64 100644 --- a/src/base/search/searchhandler.cpp +++ b/src/base/search/searchhandler.cpp @@ -71,8 +71,8 @@ SearchHandler::SearchHandler(const QString &pattern, const QString &category, co { // Load environment variables (proxy) m_searchProcess->setProcessEnvironment(m_manager->proxyEnvironment()); - m_searchProcess->setProgram(Utils::ForeignApps::pythonInfo().executableName); -#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) + m_searchProcess->setProgram(Utils::ForeignApps::pythonInfo().executablePath.data()); +#ifdef Q_OS_UNIX m_searchProcess->setUnixProcessParameters(QProcess::UnixProcessFlag::CloseFileDescriptors); #endif diff --git a/src/base/search/searchpluginmanager.cpp b/src/base/search/searchpluginmanager.cpp index c79ba5d60..7815e441a 100644 --- a/src/base/search/searchpluginmanager.cpp +++ b/src/base/search/searchpluginmanager.cpp @@ -546,7 +546,7 @@ void SearchPluginManager::update() { QProcess nova; nova.setProcessEnvironment(proxyEnvironment()); -#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) +#ifdef Q_OS_UNIX nova.setUnixProcessParameters(QProcess::UnixProcessFlag::CloseFileDescriptors); #endif @@ -556,7 +556,7 @@ void SearchPluginManager::update() (engineLocation() / Path(u"/nova2.py"_s)).toString(), u"--capabilities"_s }; - nova.start(Utils::ForeignApps::pythonInfo().executableName, params, QIODevice::ReadOnly); + nova.start(Utils::ForeignApps::pythonInfo().executablePath.data(), params, QIODevice::ReadOnly); nova.waitForFinished(); const auto capabilities = QString::fromUtf8(nova.readAllStandardOutput()); diff --git a/src/base/utils/foreignapps.cpp b/src/base/utils/foreignapps.cpp index 038d5128f..91a254d91 100644 --- a/src/base/utils/foreignapps.cpp +++ b/src/base/utils/foreignapps.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2018 Mike Tzou + * Copyright (C) 2018-2025 Mike Tzou (Chocobo1) * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -30,6 +30,7 @@ #include "foreignapps.h" #if defined(Q_OS_WIN) +#include #include #endif @@ -40,27 +41,30 @@ #if defined(Q_OS_WIN) #include +#include #endif -#include "base/global.h" #include "base/logger.h" -#include "base/path.h" #include "base/preferences.h" #include "base/utils/bytearray.h" +#if defined(Q_OS_WIN) +#include "base/utils/compare.h" +#endif + using namespace Utils::ForeignApps; namespace { - bool testPythonInstallation(const QString &exeName, PythonInfo &info) + bool testPythonInstallation(const Path &exePath, PythonInfo &info) { info = {}; QProcess proc; -#if defined(Q_OS_UNIX) && (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) +#ifdef Q_OS_UNIX proc.setUnixProcessParameters(QProcess::UnixProcessFlag::CloseFileDescriptors); #endif - proc.start(exeName, {u"--version"_s}, QIODevice::ReadOnly); + proc.start(exePath.data(), {u"--version"_s}, QIODevice::ReadOnly); if (proc.waitForFinished() && (proc.exitCode() == QProcess::NormalExit)) { QByteArray procOutput = proc.readAllStandardOutput(); @@ -83,9 +87,9 @@ namespace if (!version.isValid()) return false; - info = {exeName, version}; + info = {.executablePath = exePath, .version = version}; LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Found Python executable. Name: \"%1\". Version: \"%2\"") - .arg(info.executableName, info.version.toString()), Log::INFO); + .arg(info.executablePath.toString(), info.version.toString()), Log::INFO); return true; } @@ -100,178 +104,160 @@ namespace SYSTEM_64BIT }; - QStringList getRegSubkeys(const HKEY handle) + PathList getRegSubkeys(const HKEY handle) { - QStringList keys; + PathList keys; DWORD cSubKeys = 0; DWORD cMaxSubKeyLen = 0; - LONG res = ::RegQueryInfoKeyW(handle, NULL, NULL, NULL, &cSubKeys, &cMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL); + const LSTATUS result = ::RegQueryInfoKeyW(handle, NULL, NULL, NULL, &cSubKeys, &cMaxSubKeyLen, NULL, NULL, NULL, NULL, NULL, NULL); - if (res == ERROR_SUCCESS) + if (result == ERROR_SUCCESS) { ++cMaxSubKeyLen; // For null character - LPWSTR lpName = new WCHAR[cMaxSubKeyLen]; - DWORD cName; + LPWSTR lpName = new WCHAR[cMaxSubKeyLen] {0}; + [[maybe_unused]] const auto lpNameGuard = qScopeGuard([&lpName] { delete[] lpName; }); + + keys.reserve(cSubKeys); for (DWORD i = 0; i < cSubKeys; ++i) { - cName = cMaxSubKeyLen; - res = ::RegEnumKeyExW(handle, i, lpName, &cName, NULL, NULL, NULL, NULL); + DWORD cName = cMaxSubKeyLen; + const LSTATUS res = ::RegEnumKeyExW(handle, i, lpName, &cName, NULL, NULL, NULL, NULL); if (res == ERROR_SUCCESS) - keys.push_back(QString::fromWCharArray(lpName)); + keys.append(Path(QString::fromWCharArray(lpName))); } - - delete[] lpName; } return keys; } - QString getRegValue(const HKEY handle, const QString &name = {}) + Path getRegValue(const HKEY handle, const QString &name = {}) { const std::wstring nameWStr = name.toStdWString(); DWORD type = 0; DWORD cbData = 0; - // Discover the size of the value ::RegQueryValueExW(handle, nameWStr.c_str(), NULL, &type, NULL, &cbData); - DWORD cBuffer = (cbData / sizeof(WCHAR)) + 1; - LPWSTR lpData = new WCHAR[cBuffer]; - LONG res = ::RegQueryValueExW(handle, nameWStr.c_str(), NULL, &type, reinterpret_cast(lpData), &cbData); - QString result; + const DWORD cBuffer = (cbData / sizeof(WCHAR)) + 1; + LPWSTR lpData = new WCHAR[cBuffer] {0}; + [[maybe_unused]] const auto lpDataGuard = qScopeGuard([&lpData] { delete[] lpData; }); + + const LSTATUS res = ::RegQueryValueExW(handle, nameWStr.c_str(), NULL, &type, reinterpret_cast(lpData), &cbData); if (res == ERROR_SUCCESS) - { - lpData[cBuffer - 1] = 0; - result = QString::fromWCharArray(lpData); - } - delete[] lpData; + return Path(QString::fromWCharArray(lpData)); - return result; + return {}; } - QString pythonSearchReg(const REG_SEARCH_TYPE type) + PathList pythonSearchReg(const REG_SEARCH_TYPE type) { - HKEY hkRoot; - if (type == USER) - hkRoot = HKEY_CURRENT_USER; - else - hkRoot = HKEY_LOCAL_MACHINE; + const HKEY hkRoot = (type == USER) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + const REGSAM samDesired = KEY_READ + | ((type == SYSTEM_64BIT) ? KEY_WOW64_64KEY : KEY_WOW64_32KEY); + PathList ret; - REGSAM samDesired = KEY_READ; - if (type == SYSTEM_32BIT) - samDesired |= KEY_WOW64_32KEY; - else if (type == SYSTEM_64BIT) - samDesired |= KEY_WOW64_64KEY; - - QString path; - LONG res = 0; - HKEY hkPythonCore; - res = ::RegOpenKeyExW(hkRoot, L"SOFTWARE\\Python\\PythonCore", 0, samDesired, &hkPythonCore); - - if (res == ERROR_SUCCESS) + HKEY hkPythonCore {0}; + if (::RegOpenKeyExW(hkRoot, L"SOFTWARE\\Python\\PythonCore", 0, samDesired, &hkPythonCore) == ERROR_SUCCESS) { - QStringList versions = getRegSubkeys(hkPythonCore); - versions.sort(); + [[maybe_unused]] const auto hkPythonCoreGuard = qScopeGuard([&hkPythonCore] { ::RegCloseKey(hkPythonCore); }); - bool found = false; - while (!found && !versions.empty()) + // start with the largest version + PathList versions = getRegSubkeys(hkPythonCore); + // ordinary sort won't suffice, it needs to sort ["3.9", "3.10"] correctly + const Utils::Compare::NaturalCompare comparator; + std::sort(versions.begin(), versions.end(), [&comparator](const Path &left, const Path &right) { - const std::wstring version = QString(versions.takeLast() + u"\\InstallPath").toStdWString(); + return comparator(left.data(), right.data()); + }); - HKEY hkInstallPath; - res = ::RegOpenKeyExW(hkPythonCore, version.c_str(), 0, samDesired, &hkInstallPath); + ret.reserve(versions.size() * 2); - if (res == ERROR_SUCCESS) + while (!versions.empty()) + { + const std::wstring version = (versions.takeLast() / Path(u"InstallPath"_s)).toString().toStdWString(); + + HKEY hkInstallPath {0}; + if (::RegOpenKeyExW(hkPythonCore, version.c_str(), 0, samDesired, &hkInstallPath) == ERROR_SUCCESS) { - qDebug("Detected possible Python v%ls location", version.c_str()); - path = getRegValue(hkInstallPath); - ::RegCloseKey(hkInstallPath); + [[maybe_unused]] const auto hkInstallPathGuard = qScopeGuard([&hkInstallPath] { ::RegCloseKey(hkInstallPath); }); - if (!path.isEmpty()) - { - const QDir baseDir {path}; + const Path basePath = getRegValue(hkInstallPath); + if (basePath.isEmpty()) + continue; - if (baseDir.exists(u"python3.exe"_s)) - { - found = true; - path = baseDir.filePath(u"python3.exe"_s); - } - else if (baseDir.exists(u"python.exe"_s)) - { - found = true; - path = baseDir.filePath(u"python.exe"_s); - } - } + if (const Path path = (basePath / Path(u"python3.exe"_s)); path.exists()) + ret.append(path); + if (const Path path = (basePath / Path(u"python.exe"_s)); path.exists()) + ret.append(path); } } - - if (!found) - path = QString(); - - ::RegCloseKey(hkPythonCore); } - return path; + return ret; } - QString findPythonPath() + PathList searchPythonPaths() { - QString path = pythonSearchReg(USER); - if (!path.isEmpty()) - return path; - - path = pythonSearchReg(SYSTEM_32BIT); - if (!path.isEmpty()) - return path; - - path = pythonSearchReg(SYSTEM_64BIT); - if (!path.isEmpty()) - return path; + // From registry + PathList ret = pythonSearchReg(USER) + + pythonSearchReg(SYSTEM_64BIT) + + pythonSearchReg(SYSTEM_32BIT); // Fallback: Detect python from default locations const QFileInfoList dirs = QDir(u"C:/"_s).entryInfoList({u"Python*"_s}, QDir::Dirs, (QDir::Name | QDir::Reversed)); for (const QFileInfo &info : dirs) { - const QString py3Path {info.absolutePath() + u"/python3.exe"}; - if (QFile::exists(py3Path)) - return py3Path; + const Path absPath {info.absolutePath()}; - const QString pyPath {info.absolutePath() + u"/python.exe"}; - if (QFile::exists(pyPath)) - return pyPath; + if (const Path path = (absPath / Path(u"python3.exe"_s)); path.exists()) + ret.append(path); + if (const Path path = (absPath / Path(u"python.exe"_s)); path.exists()) + ret.append(path); } - return {}; + return ret; } #endif // Q_OS_WIN } bool Utils::ForeignApps::PythonInfo::isValid() const { - return (!executableName.isEmpty() && version.isValid()); + return (executablePath.isValid() && version.isValid()); } bool Utils::ForeignApps::PythonInfo::isSupportedVersion() const { - return (version >= Version {3, 9, 0}); + return (version >= MINIMUM_SUPPORTED_VERSION); } PythonInfo Utils::ForeignApps::pythonInfo() { static PythonInfo pyInfo; - const QString preferredPythonPath = Preferences::instance()->getPythonExecutablePath().toString(); - if (pyInfo.isValid() && (preferredPythonPath == pyInfo.executableName)) + const Path preferredPythonPath = Preferences::instance()->getPythonExecutablePath(); + if (pyInfo.isValid() && (preferredPythonPath == pyInfo.executablePath)) return pyInfo; + const QString invalidVersionMessage = QCoreApplication::translate("Utils::ForeignApps" + , "Python failed to meet minimum version requirement. Path: \"%1\". Found version: \"%2\". Minimum supported version: \"%3\"."); + if (!preferredPythonPath.isEmpty()) { if (testPythonInstallation(preferredPythonPath, pyInfo)) - return pyInfo; - LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Failed to find Python executable. Path: \"%1\".") - .arg(preferredPythonPath), Log::WARNING); + { + if (pyInfo.isSupportedVersion()) + return pyInfo; + + LogMsg(invalidVersionMessage.arg(pyInfo.executablePath.toString() + , pyInfo.version.toString(), PythonInfo::MINIMUM_SUPPORTED_VERSION.toString()), Log::WARNING); + } + else + { + LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Failed to find Python executable. Path: \"%1\".") + .arg(preferredPythonPath.toString()), Log::WARNING); + } } else { @@ -279,24 +265,40 @@ PythonInfo Utils::ForeignApps::pythonInfo() if (!pyInfo.isValid()) { - if (testPythonInstallation(u"python3"_s, pyInfo)) - return pyInfo; - LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Failed to find `python3` executable in PATH environment variable. PATH: \"%1\"") - .arg(qEnvironmentVariable("PATH")), Log::INFO); + // search in `PATH` environment variable + const QString exeNames[] = {u"python3"_s, u"python"_s}; + for (const QString &exeName : exeNames) + { + if (testPythonInstallation(Path(exeName), pyInfo)) + { + if (pyInfo.isSupportedVersion()) + return pyInfo; - if (testPythonInstallation(u"python"_s, pyInfo)) - return pyInfo; - LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Failed to find `python` executable in PATH environment variable. PATH: \"%1\"") - .arg(qEnvironmentVariable("PATH")), Log::INFO); + LogMsg(invalidVersionMessage.arg(pyInfo.executablePath.toString() + , pyInfo.version.toString(), PythonInfo::MINIMUM_SUPPORTED_VERSION.toString()), Log::INFO); + } + else + { + LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Failed to find `%1` executable in PATH environment variable. PATH: \"%2\"") + .arg(exeName, qEnvironmentVariable("PATH")), Log::INFO); + } + } #if defined(Q_OS_WIN) - if (testPythonInstallation(findPythonPath(), pyInfo)) - return pyInfo; - LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Failed to find `python` executable in Windows Registry."), Log::INFO); + for (const Path &path : asConst(searchPythonPaths())) + { + if (testPythonInstallation(path, pyInfo)) + { + if (pyInfo.isSupportedVersion()) + return pyInfo; + + LogMsg(invalidVersionMessage.arg(pyInfo.executablePath.toString() + , pyInfo.version.toString(), PythonInfo::MINIMUM_SUPPORTED_VERSION.toString()), Log::INFO); + } + } #endif LogMsg(QCoreApplication::translate("Utils::ForeignApps", "Failed to find Python executable"), Log::WARNING); - } } diff --git a/src/base/utils/foreignapps.h b/src/base/utils/foreignapps.h index 45d05438b..9621fd2ba 100644 --- a/src/base/utils/foreignapps.h +++ b/src/base/utils/foreignapps.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2018 Mike Tzou + * Copyright (C) 2018-2025 Mike Tzou (Chocobo1) * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -32,6 +32,7 @@ #include #include "base/global.h" +#include "base/path.h" #include "base/utils/version.h" namespace Utils::ForeignApps @@ -45,8 +46,10 @@ namespace Utils::ForeignApps bool isValid() const; bool isSupportedVersion() const; - QString executableName; + Path executablePath; Version version; + + inline static const Version MINIMUM_SUPPORTED_VERSION {3, 9, 0}; }; PythonInfo pythonInfo(); diff --git a/src/base/utils/random.cpp b/src/base/utils/random.cpp index 57f84c753..bc0ec3392 100644 --- a/src/base/utils/random.cpp +++ b/src/base/utils/random.cpp @@ -42,7 +42,7 @@ uint32_t Utils::Random::rand(const uint32_t min, const uint32_t max) { - static RandomLayer layer; + static const RandomLayer layer; // new distribution is cheap: https://stackoverflow.com/a/19036349 std::uniform_int_distribution uniform(min, max); diff --git a/src/base/utils/randomlayer_linux.cpp b/src/base/utils/randomlayer_linux.cpp index 29b24631c..81310f3bb 100644 --- a/src/base/utils/randomlayer_linux.cpp +++ b/src/base/utils/randomlayer_linux.cpp @@ -27,6 +27,7 @@ */ #include +#include #include #include @@ -44,6 +45,27 @@ namespace RandomLayer() { + if (::getrandom(nullptr, 0, 0) < 0) + { + if (errno == ENOSYS) + { + // underlying kernel does not implement this system call + // fallback to `urandom` + m_randDev = fopen("/dev/urandom", "rb"); + if (!m_randDev) + qFatal("Failed to open /dev/urandom. Reason: \"%s\". Error code: %d.", std::strerror(errno), errno); + } + else + { + qFatal("getrandom() error. Reason: \"%s\". Error code: %d.", std::strerror(errno), errno); + } + } + } + + ~RandomLayer() + { + if (m_randDev) + fclose(m_randDev); } static constexpr result_type min() @@ -56,7 +78,15 @@ namespace return std::numeric_limits::max(); } - result_type operator()() + result_type operator()() const + { + if (!m_randDev) + return getRandomViaAPI(); + return getRandomViaFile(); + } + + private: + result_type getRandomViaAPI() const { const int RETRY_MAX = 3; @@ -68,10 +98,21 @@ namespace return buf; if (result < 0) - qFatal("getrandom() error. Reason: %s. Error code: %d.", std::strerror(errno), errno); + qFatal("getrandom() error. Reason: \"%s\". Error code: %d.", std::strerror(errno), errno); } qFatal("getrandom() failed. Reason: too many retries."); } + + result_type getRandomViaFile() const + { + result_type buf = 0; + if (fread(&buf, sizeof(buf), 1, m_randDev) == 1) + return buf; + + qFatal("Read /dev/urandom error. Reason: \"%s\". Error code: %d.", std::strerror(errno), errno); + } + + FILE *m_randDev = nullptr; }; } diff --git a/src/base/utils/randomlayer_other.cpp b/src/base/utils/randomlayer_other.cpp index fa7e61f38..4e794a1ae 100644 --- a/src/base/utils/randomlayer_other.cpp +++ b/src/base/utils/randomlayer_other.cpp @@ -46,7 +46,7 @@ namespace : m_randDev {fopen("/dev/urandom", "rb")} { if (!m_randDev) - qFatal("Failed to open /dev/urandom. Reason: %s. Error code: %d.", std::strerror(errno), errno); + qFatal("Failed to open /dev/urandom. Reason: \"%s\". Error code: %d.", std::strerror(errno), errno); } ~RandomLayer() @@ -67,10 +67,10 @@ namespace result_type operator()() const { result_type buf = 0; - if (fread(&buf, sizeof(buf), 1, m_randDev) != 1) - qFatal("Read /dev/urandom error. Reason: %s. Error code: %d.", std::strerror(errno), errno); + if (fread(&buf, sizeof(buf), 1, m_randDev) == 1) + return buf; - return buf; + qFatal("Read /dev/urandom error. Reason: \"%s\". Error code: %d.", std::strerror(errno), errno); } private: diff --git a/src/base/utils/randomlayer_win.cpp b/src/base/utils/randomlayer_win.cpp index 916ed6acf..946a26c94 100644 --- a/src/base/utils/randomlayer_win.cpp +++ b/src/base/utils/randomlayer_win.cpp @@ -60,7 +60,7 @@ namespace return std::numeric_limits::max(); } - result_type operator()() + result_type operator()() const { result_type buf = 0; const bool result = m_processPrng(reinterpret_cast(&buf), sizeof(buf)); diff --git a/src/base/utils/string.cpp b/src/base/utils/string.cpp index 1fc6568f8..2dc408c85 100644 --- a/src/base/utils/string.cpp +++ b/src/base/utils/string.cpp @@ -61,7 +61,7 @@ QString Utils::String::fromLocal8Bit(const std::string_view string) QString Utils::String::wildcardToRegexPattern(const QString &pattern) { -#if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) +#if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 1)) return QRegularExpression::wildcardToRegularExpression(pattern , (QRegularExpression::UnanchoredWildcardConversion | QRegularExpression::NonPathWildcardConversion)); #else diff --git a/src/gui/addnewtorrentdialog.cpp b/src/gui/addnewtorrentdialog.cpp index 321e1528b..8954a1c4d 100644 --- a/src/gui/addnewtorrentdialog.cpp +++ b/src/gui/addnewtorrentdialog.cpp @@ -32,12 +32,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -242,14 +244,13 @@ public: return QList(filesCount(), 0); } - QList availableFileFractions() const override + QFuture> fetchAvailableFileFractions() const override { - return QList(filesCount(), 0); - } - - void fetchAvailableFileFractions(std::function)> resultHandler) const override - { - resultHandler(availableFileFractions()); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) + return QtFuture::makeReadyValueFuture(QList(filesCount(), 0)); +#else + return QtFuture::makeReadyFuture(QList(filesCount(), 0)); +#endif } void prioritizeFiles(const QList &priorities) override diff --git a/src/gui/advancedsettings.cpp b/src/gui/advancedsettings.cpp index b5a56f936..ba620e027 100644 --- a/src/gui/advancedsettings.cpp +++ b/src/gui/advancedsettings.cpp @@ -64,7 +64,7 @@ namespace QBITTORRENT_HEADER, RESUME_DATA_STORAGE, TORRENT_CONTENT_REMOVE_OPTION, -#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_MACOS) +#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_LINUX) && !defined(Q_OS_MACOS) MEMORY_WORKING_SET_LIMIT, #endif #if defined(Q_OS_WIN) @@ -209,7 +209,7 @@ void AdvancedSettings::saveAdvancedSettings() const BitTorrent::Session *const session = BitTorrent::Session::instance(); session->setResumeDataStorageType(m_comboBoxResumeDataStorage.currentData().value()); -#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_MACOS) +#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_LINUX) && !defined(Q_OS_MACOS) // Physical memory (RAM) usage limit app()->setMemoryWorkingSetLimit(m_spinBoxMemoryWorkingSetLimit.value()); #endif @@ -494,12 +494,11 @@ void AdvancedSettings::loadAdvancedSettings() m_comboBoxTorrentContentRemoveOption.setCurrentIndex(m_comboBoxTorrentContentRemoveOption.findData(QVariant::fromValue(session->torrentContentRemoveOption()))); addRow(TORRENT_CONTENT_REMOVE_OPTION, tr("Torrent content removing mode"), &m_comboBoxTorrentContentRemoveOption); -#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_MACOS) +#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_LINUX) && !defined(Q_OS_MACOS) // Physical memory (RAM) usage limit m_spinBoxMemoryWorkingSetLimit.setMinimum(1); m_spinBoxMemoryWorkingSetLimit.setMaximum(std::numeric_limits::max()); m_spinBoxMemoryWorkingSetLimit.setSuffix(tr(" MiB")); - m_spinBoxMemoryWorkingSetLimit.setToolTip(tr("This option is less effective on Linux")); m_spinBoxMemoryWorkingSetLimit.setValue(app()->memoryWorkingSetLimit()); addRow(MEMORY_WORKING_SET_LIMIT, (tr("Physical memory (RAM) usage limit") + u' ' + makeLink(u"https://wikipedia.org/wiki/Working_set", u"(?)")) , &m_spinBoxMemoryWorkingSetLimit); diff --git a/src/gui/advancedsettings.h b/src/gui/advancedsettings.h index 2305e830a..7ffca4f33 100644 --- a/src/gui/advancedsettings.h +++ b/src/gui/advancedsettings.h @@ -93,7 +93,7 @@ private: QSpinBox m_spinBoxHashingThreads; #endif -#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_MACOS) +#if defined(QBT_USES_LIBTORRENT2) && !defined(Q_OS_LINUX) && !defined(Q_OS_MACOS) QSpinBox m_spinBoxMemoryWorkingSetLimit; #endif diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index fc78ccbc8..d4d6b668f 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -1616,14 +1616,14 @@ void MainWindow::on_actionSearchWidget_triggered() #ifdef Q_OS_WIN const QMessageBox::StandardButton buttonPressed = QMessageBox::question(this, tr("Old Python Runtime") , tr("Your Python version (%1) is outdated. Minimum requirement: %2.\nDo you want to install a newer version now?") - .arg(pyInfo.version.toString(), u"3.9.0") + .arg(pyInfo.version.toString(), Utils::ForeignApps::PythonInfo::MINIMUM_SUPPORTED_VERSION.toString()) , (QMessageBox::Yes | QMessageBox::No), QMessageBox::Yes); if (buttonPressed == QMessageBox::Yes) installPython(); #else QMessageBox::information(this, tr("Old Python Runtime") , tr("Your Python version (%1) is outdated. Please upgrade to latest version for search engines to work.\nMinimum requirement: %2.") - .arg(pyInfo.version.toString(), u"3.9.0")); + .arg(pyInfo.version.toString(), Utils::ForeignApps::PythonInfo::MINIMUM_SUPPORTED_VERSION.toString())); #endif return; } diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui index 263c2bf91..a21f7af04 100644 --- a/src/gui/optionsdialog.ui +++ b/src/gui/optionsdialog.ui @@ -1031,7 +1031,7 @@ - When duplicate torrent is being added + When adding a duplicate torrent @@ -1047,7 +1047,7 @@ - Ask for merging trackers when torrent is being added manually + Ask to merge trackers for manually added torrent true @@ -3028,9 +3028,6 @@ Disable encryption: Only connect to peers without protocol encryption false - - 9998.000000000000000 - 0.050000000000000 diff --git a/src/gui/properties/peerlistwidget.cpp b/src/gui/properties/peerlistwidget.cpp index 8546d7143..4c27d4eb4 100644 --- a/src/gui/properties/peerlistwidget.cpp +++ b/src/gui/properties/peerlistwidget.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2023 Vladimir Golovnev + * Copyright (C) 2023-2025 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -406,10 +407,13 @@ void PeerListWidget::loadPeers(const BitTorrent::Torrent *torrent) return; using TorrentPtr = QPointer; - torrent->fetchPeerInfo([this, torrent = TorrentPtr(torrent)](const QList &peers) + torrent->fetchPeerInfo().then(this, [this, torrent = TorrentPtr(torrent)](const QList &peers) { - if (torrent != m_properties->getCurrentTorrent()) + if (const BitTorrent::Torrent *currentTorrent = m_properties->getCurrentTorrent(); + !currentTorrent || (currentTorrent != torrent)) + { return; + } // Remove I2P peers since they will be completely reloaded. for (const QStandardItem *item : asConst(m_I2PPeerItems)) diff --git a/src/gui/properties/propertieswidget.cpp b/src/gui/properties/propertieswidget.cpp index ad9673272..d86f5a074 100644 --- a/src/gui/properties/propertieswidget.cpp +++ b/src/gui/properties/propertieswidget.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2022-2024 Vladimir Golovnev + * Copyright (C) 2022-2025 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -440,10 +441,10 @@ void PropertiesWidget::loadDynamicData() // Update ratio info const qreal ratio = m_torrent->realRatio(); - m_ui->labelShareRatioVal->setText(ratio > BitTorrent::Torrent::MAX_RATIO ? C_INFINITY : Utils::String::fromDouble(ratio, 2)); + m_ui->labelShareRatioVal->setText(ratio >= BitTorrent::Torrent::MAX_RATIO ? C_INFINITY : Utils::String::fromDouble(ratio, 2)); const qreal popularity = m_torrent->popularity(); - m_ui->labelPopularityVal->setText(popularity > BitTorrent::Torrent::MAX_RATIO ? C_INFINITY : Utils::String::fromDouble(popularity, 2)); + m_ui->labelPopularityVal->setText(popularity >= BitTorrent::Torrent::MAX_RATIO ? C_INFINITY : Utils::String::fromDouble(popularity, 2)); m_ui->labelSeedsVal->setText(tr("%1 (%2 total)", "%1 and %2 are numbers, e.g. 3 (10 total)") .arg(QString::number(m_torrent->seedsCount()) @@ -471,17 +472,18 @@ void PropertiesWidget::loadDynamicData() if (m_torrent->hasMetadata()) { - using TorrentPtr = QPointer; - m_ui->labelTotalPiecesVal->setText(tr("%1 x %2 (have %3)", "(torrent pieces) eg 152 x 4MB (have 25)").arg(m_torrent->piecesCount()).arg(Utils::Misc::friendlyUnit(m_torrent->pieceLength())).arg(m_torrent->piecesHave())); if (!m_torrent->isFinished() && !m_torrent->isStopped() && !m_torrent->isQueued() && !m_torrent->isChecking()) { // Pieces availability showPiecesAvailability(true); - m_torrent->fetchPieceAvailability([this, torrent = TorrentPtr(m_torrent)](const QList &pieceAvailability) + + using TorrentPtr = QPointer; + m_torrent->fetchPieceAvailability().then(this + , [this, torrent = TorrentPtr(m_torrent)](const QList &pieceAvailability) { - if (torrent == m_torrent) + if (m_torrent && (m_torrent == torrent)) m_piecesAvailability->setAvailability(pieceAvailability); }); @@ -496,9 +498,11 @@ void PropertiesWidget::loadDynamicData() qreal progress = m_torrent->progress() * 100.; m_ui->labelProgressVal->setText(Utils::String::fromDouble(progress, 1) + u'%'); - m_torrent->fetchDownloadingPieces([this, torrent = TorrentPtr(m_torrent)](const QBitArray &downloadingPieces) + using TorrentPtr = QPointer; + m_torrent->fetchDownloadingPieces().then(this + , [this, torrent = TorrentPtr(m_torrent)](const QBitArray &downloadingPieces) { - if (torrent == m_torrent) + if (m_torrent && (m_torrent == torrent)) m_downloadedPieces->setProgress(m_torrent->pieces(), downloadingPieces); }); } @@ -525,9 +529,9 @@ void PropertiesWidget::loadUrlSeeds() return; using TorrentPtr = QPointer; - m_torrent->fetchURLSeeds([this, torrent = TorrentPtr(m_torrent)](const QList &urlSeeds) + m_torrent->fetchURLSeeds().then(this, [this, torrent = TorrentPtr(m_torrent)](const QList &urlSeeds) { - if (torrent != m_torrent) + if (!m_torrent || (m_torrent != torrent)) return; m_ui->listWebSeeds->clear(); diff --git a/src/gui/rss/rsswidget.cpp b/src/gui/rss/rsswidget.cpp index 98c272014..fb764a19c 100644 --- a/src/gui/rss/rsswidget.cpp +++ b/src/gui/rss/rsswidget.cpp @@ -333,7 +333,7 @@ void RSSWidget::on_newFeedButton_clicked() // Ask for feed URL const QString clipText = qApp->clipboard()->text(); - const QString defaultURL = Net::DownloadManager::hasSupportedScheme(clipText) ? clipText : u"http://"_s; + const QString defaultURL = Net::DownloadManager::hasSupportedScheme(clipText) ? clipText : u"https://"_s; RSS::Feed *newFeed = nullptr; RSSFeedDialog dialog {this}; @@ -623,58 +623,60 @@ void RSSWidget::renderArticle(const RSS::Article *article) const { Q_ASSERT(article); + const QString articleLink = article->link(); const QString highlightedBaseColor = m_ui->textBrowser->palette().color(QPalette::Active, QPalette::Highlight).name(); const QString highlightedBaseTextColor = m_ui->textBrowser->palette().color(QPalette::Active, QPalette::HighlightedText).name(); const QString alternateBaseColor = m_ui->textBrowser->palette().color(QPalette::Active, QPalette::AlternateBase).name(); - QString html = - u"
" + - u"
%3
"_s.arg(highlightedBaseColor, highlightedBaseTextColor, article->title()); - if (article->date().isValid()) - html += u"
%2%3
"_s.arg(alternateBaseColor, tr("Date: "), QLocale::system().toString(article->date().toLocalTime())); + QString html = u"
" + + u"
%3
"_s.arg(highlightedBaseColor, highlightedBaseTextColor, article->title()); + if (const QDateTime articleDate = article->date(); articleDate.isValid()) + html += u"
%2%3
"_s.arg(alternateBaseColor, tr("Date: "), QLocale::system().toString(articleDate.toLocalTime(), QLocale::ShortFormat)); if (m_ui->feedListWidget->currentItem() == m_ui->feedListWidget->stickyUnreadItem()) html += u"
%2%3
"_s.arg(alternateBaseColor, tr("Feed: "), article->feed()->title()); - if (!article->author().isEmpty()) - html += u"
%2%3
"_s.arg(alternateBaseColor, tr("Author: "), article->author()); + if (const QString articleAuthor = article->author(); !articleAuthor.isEmpty()) + html += u"
%2%3
"_s.arg(alternateBaseColor, tr("Author: "), articleAuthor); + if (!articleLink.isEmpty()) + html += u""_s.arg(alternateBaseColor, articleLink, tr("Open link")); html += u"
" u"
"; - if (Qt::mightBeRichText(article->description())) + if (QString description = article->description(); Qt::mightBeRichText(description)) { - html += article->description(); + html += description; } else { - QString description = article->description(); QRegularExpression rx; // If description is plain text, replace BBCode tags with HTML and wrap everything in
 so it looks nice
         rx.setPatternOptions(QRegularExpression::InvertedGreedinessOption
                              | QRegularExpression::CaseInsensitiveOption);
 
         rx.setPattern(u"\\[img\\](.+)\\[/img\\]"_s);
-        description = description.replace(rx, u""_s);
+        description.replace(rx, u""_s);
 
         rx.setPattern(u"\\[url=(\")?(.+)\\1\\]"_s);
-        description = description.replace(rx, u""_s);
-        description = description.replace(u"[/url]"_s, u""_s, Qt::CaseInsensitive);
+        description.replace(rx, u""_s)
+            .replace(u"[/url]"_s, u""_s, Qt::CaseInsensitive);
 
         rx.setPattern(u"\\[(/)?([bius])\\]"_s);
-        description = description.replace(rx, u"<\\1\\2>"_s);
+        description.replace(rx, u"<\\1\\2>"_s);
 
         rx.setPattern(u"\\[color=(\")?(.+)\\1\\]"_s);
-        description = description.replace(rx, u""_s);
-        description = description.replace(u"[/color]"_s, u""_s, Qt::CaseInsensitive);
+        description.replace(rx, u""_s)
+            .replace(u"[/color]"_s, u""_s, Qt::CaseInsensitive);
 
         rx.setPattern(u"\\[size=(\")?(.+)\\d\\1\\]"_s);
-        description = description.replace(rx, u""_s);
-        description = description.replace(u"[/size]"_s, u""_s, Qt::CaseInsensitive);
+        description.replace(rx, u""_s)
+            .replace(u"[/size]"_s, u""_s, Qt::CaseInsensitive);
 
         html += u"
" + description + u"
"; } + html += u"
"; // Supplement relative URLs to absolute ones - const QUrl url {article->link()}; + const QUrl url {articleLink}; const QString baseUrl = url.toString(QUrl::RemovePath | QUrl::RemoveQuery); convertRelativeUrlToAbsolute(html, baseUrl); - html += u"
"; + m_ui->textBrowser->setHtml(html); } diff --git a/src/gui/torrentcontentmodel.cpp b/src/gui/torrentcontentmodel.cpp index e75fc1856..2df7b8176 100644 --- a/src/gui/torrentcontentmodel.cpp +++ b/src/gui/torrentcontentmodel.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2022-2024 Vladimir Golovnev + * Copyright (C) 2022-2025 Vladimir Golovnev * Copyright (C) 2006-2012 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -219,18 +220,14 @@ void TorrentContentModel::updateFilesAvailability() Q_ASSERT(m_contentHandler && m_contentHandler->hasMetadata()); using HandlerPtr = QPointer; - m_contentHandler->fetchAvailableFileFractions([this, handler = HandlerPtr(m_contentHandler)](const QList &availableFileFractions) + m_contentHandler->fetchAvailableFileFractions().then(this + , [this, handler = HandlerPtr(m_contentHandler)](const QList &availableFileFractions) { - if (handler != m_contentHandler) - return; - - Q_ASSERT(m_filesIndex.size() == availableFileFractions.size()); - // XXX: Why is this necessary? - if (m_filesIndex.size() != availableFileFractions.size()) [[unlikely]] + if (!m_contentHandler || (m_contentHandler != handler)) return; for (int i = 0; i < m_filesIndex.size(); ++i) - m_filesIndex[i]->setAvailability(availableFileFractions[i]); + m_filesIndex[i]->setAvailability(availableFileFractions.value(i, 0)); // Update folders progress in the tree m_rootItem->recalculateProgress(); }); diff --git a/src/gui/torrentsharelimitswidget.ui b/src/gui/torrentsharelimitswidget.ui index 9ada9ed67..13fbfe0a2 100644 --- a/src/gui/torrentsharelimitswidget.ui +++ b/src/gui/torrentsharelimitswidget.ui @@ -47,9 +47,6 @@ false - - 9998.000000000000000 - 0.050000000000000 diff --git a/src/gui/trackerlist/trackerlistmodel.cpp b/src/gui/trackerlist/trackerlistmodel.cpp index 78465488c..4c90e0a6a 100644 --- a/src/gui/trackerlist/trackerlistmodel.cpp +++ b/src/gui/trackerlist/trackerlistmodel.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2023-2024 Vladimir Golovnev + * Copyright (C) 2023-2025 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -42,6 +42,7 @@ #include #include +#include #include #include #include @@ -309,16 +310,16 @@ void TrackerListModel::populate() m_items->emplace_back(std::make_shared(u"** [LSD] **", privateTorrentMessage)); using TorrentPtr = QPointer; - m_torrent->fetchPeerInfo([this, torrent = TorrentPtr(m_torrent)](const QList &peers) + m_torrent->fetchPeerInfo().then(this, [this, torrent = TorrentPtr(m_torrent)](const QList &peers) { - if (torrent != m_torrent) + if (!m_torrent || (m_torrent != torrent)) return; - // XXX: libtorrent should provide this info... - // Count peers from DHT, PeX, LSD - uint seedsDHT = 0, seedsPeX = 0, seedsLSD = 0, peersDHT = 0, peersPeX = 0, peersLSD = 0; - for (const BitTorrent::PeerInfo &peer : peers) - { + // XXX: libtorrent should provide this info... + // Count peers from DHT, PeX, LSD + uint seedsDHT = 0, seedsPeX = 0, seedsLSD = 0, peersDHT = 0, peersPeX = 0, peersLSD = 0; + for (const BitTorrent::PeerInfo &peer : peers) + { if (peer.isConnecting()) continue; diff --git a/src/gui/transferlistmodel.cpp b/src/gui/transferlistmodel.cpp index 7c827c1a6..0298db047 100644 --- a/src/gui/transferlistmodel.cpp +++ b/src/gui/transferlistmodel.cpp @@ -293,7 +293,7 @@ QString TransferListModel::displayValue(const BitTorrent::Torrent *torrent, cons if (hideValues && (value <= 0)) return {}; - return ((static_cast(value) == -1) || (value > BitTorrent::Torrent::MAX_RATIO)) + return ((static_cast(value) == -1) || (value >= BitTorrent::Torrent::MAX_RATIO)) ? C_INFINITY : Utils::String::fromDouble(value, 2); }; diff --git a/src/gui/utils.cpp b/src/gui/utils.cpp index ac5d922dc..e987d75c0 100644 --- a/src/gui/utils.cpp +++ b/src/gui/utils.cpp @@ -176,9 +176,7 @@ void Utils::Gui::openFolderSelect(const Path &path) const int lineMaxLength = 64; QProcess proc; -#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0) proc.setUnixProcessParameters(QProcess::UnixProcessFlag::CloseFileDescriptors); -#endif proc.start(u"xdg-mime"_s, {u"query"_s, u"default"_s, u"inode/directory"_s}); proc.waitForFinished(); const auto output = QString::fromLocal8Bit(proc.readLine(lineMaxLength).simplified()); diff --git a/src/webui/api/serialize/serialize_torrent.cpp b/src/webui/api/serialize/serialize_torrent.cpp index bc75a7698..1543170d4 100644 --- a/src/webui/api/serialize/serialize_torrent.cpp +++ b/src/webui/api/serialize/serialize_torrent.cpp @@ -96,7 +96,7 @@ QVariantMap serialize(const BitTorrent::Torrent &torrent) const auto adjustRatio = [](const qreal ratio) -> qreal { - return (ratio > BitTorrent::Torrent::MAX_RATIO) ? -1 : ratio; + return (ratio >= BitTorrent::Torrent::MAX_RATIO) ? -1 : ratio; }; const auto getLastActivityTime = [&torrent]() -> qlonglong diff --git a/src/webui/api/synccontroller.cpp b/src/webui/api/synccontroller.cpp index 222b066aa..574a833ed 100644 --- a/src/webui/api/synccontroller.cpp +++ b/src/webui/api/synccontroller.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2018-2024 Vladimir Golovnev + * Copyright (C) 2018-2025 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -28,6 +28,7 @@ #include "synccontroller.h" +#include #include #include #include @@ -745,7 +746,7 @@ void SyncController::torrentPeersAction() QVariantMap data; QVariantHash peers; - const QList peersList = torrent->peers(); + const QList peersList = torrent->fetchPeerInfo().takeResult(); bool resolvePeerCountries = Preferences::instance()->resolvePeerCountries(); diff --git a/src/webui/api/torrentscontroller.cpp b/src/webui/api/torrentscontroller.cpp index 6fd5f6fbf..83459a892 100644 --- a/src/webui/api/torrentscontroller.cpp +++ b/src/webui/api/torrentscontroller.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2018-2023 Vladimir Golovnev + * Copyright (C) 2018-2025 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -180,9 +181,11 @@ namespace QJsonArray getStickyTrackers(const BitTorrent::Torrent *const torrent) { int seedsDHT = 0, seedsPeX = 0, seedsLSD = 0, leechesDHT = 0, leechesPeX = 0, leechesLSD = 0; - for (const BitTorrent::PeerInfo &peer : asConst(torrent->peers())) + const QList peersList = torrent->fetchPeerInfo().takeResult(); + for (const BitTorrent::PeerInfo &peer : peersList) { - if (peer.isConnecting()) continue; + if (peer.isConnecting()) + continue; if (peer.isSeed()) { @@ -520,8 +523,8 @@ void TorrentsController::propertiesAction() {KEY_PROP_SEEDS_TOTAL, torrent->totalSeedsCount()}, {KEY_PROP_PEERS, torrent->leechsCount()}, {KEY_PROP_PEERS_TOTAL, torrent->totalLeechersCount()}, - {KEY_PROP_RATIO, ((ratio > BitTorrent::Torrent::MAX_RATIO) ? -1 : ratio)}, - {KEY_PROP_POPULARITY, ((popularity > BitTorrent::Torrent::MAX_RATIO) ? -1 : popularity)}, + {KEY_PROP_RATIO, ((ratio >= BitTorrent::Torrent::MAX_RATIO) ? -1 : ratio)}, + {KEY_PROP_POPULARITY, ((popularity >= BitTorrent::Torrent::MAX_RATIO) ? -1 : popularity)}, {KEY_PROP_REANNOUNCE, torrent->nextAnnounce()}, {KEY_PROP_TOTAL_SIZE, torrent->totalSize()}, {KEY_PROP_PIECES_NUM, torrent->piecesCount()}, @@ -727,7 +730,7 @@ void TorrentsController::filesAction() { const QList priorities = torrent->filePriorities(); const QList fp = torrent->filesProgress(); - const QList fileAvailability = torrent->availableFileFractions(); + const QList fileAvailability = torrent->fetchAvailableFileFractions().takeResult(); const BitTorrent::TorrentInfo info = torrent->info(); for (const int index : asConst(fileIndexes)) { @@ -796,7 +799,7 @@ void TorrentsController::pieceStatesAction() for (int i = 0; i < states.size(); ++i) pieceStates.append(static_cast(states[i]) * 2); - const QBitArray dlstates = torrent->downloadingPieces(); + const QBitArray dlstates = torrent->fetchDownloadingPieces().takeResult(); for (int i = 0; i < states.size(); ++i) { if (dlstates[i]) diff --git a/src/webui/www/eslint.config.mjs b/src/webui/www/eslint.config.mjs index 6aafb8d64..ed4f3ab22 100644 --- a/src/webui/www/eslint.config.mjs +++ b/src/webui/www/eslint.config.mjs @@ -34,12 +34,15 @@ export default [ "no-undef": "off", "no-unused-vars": "off", "no-var": "error", + "object-shorthand": ["error", "consistent"], "operator-assignment": "error", "prefer-arrow-callback": "error", "prefer-const": "error", "prefer-template": "error", "radix": "error", + "require-await": "error", "PreferArrowFunctions/prefer-arrow-functions": "error", + "Stylistic/no-extra-semi": "error", "Stylistic/no-mixed-operators": [ "error", { diff --git a/src/webui/www/private/addpeers.html b/src/webui/www/private/addpeers.html index ad645b3d9..881bf3f48 100644 --- a/src/webui/www/private/addpeers.html +++ b/src/webui/www/private/addpeers.html @@ -12,7 +12,7 @@ diff --git a/src/webui/www/private/editfeedurl.html b/src/webui/www/private/editfeedurl.html index 83618a0b5..980fb8dcd 100644 --- a/src/webui/www/private/editfeedurl.html +++ b/src/webui/www/private/editfeedurl.html @@ -13,12 +13,12 @@ + diff --git a/src/webui/www/private/newcategory.html b/src/webui/www/private/newcategory.html index b2ec949fa..2102270d2 100644 --- a/src/webui/www/private/newcategory.html +++ b/src/webui/www/private/newcategory.html @@ -14,12 +14,12 @@ @@ -172,17 +172,17 @@
- +
- +
- +
diff --git a/src/webui/www/private/upload.html b/src/webui/www/private/upload.html index d7a065a26..82a939884 100644 --- a/src/webui/www/private/upload.html +++ b/src/webui/www/private/upload.html @@ -160,23 +160,23 @@ let submitted = false; - $("uploadForm").addEventListener("submit", () => { - $("startTorrentHidden").value = $("startTorrent").checked ? "false" : "true"; + document.getElementById("uploadForm").addEventListener("submit", (event) => { + document.getElementById("startTorrentHidden").value = document.getElementById("startTorrent").checked ? "false" : "true"; - $("dlLimitHidden").value = Number($("dlLimitText").value) * 1024; - $("upLimitHidden").value = Number($("upLimitText").value) * 1024; + document.getElementById("dlLimitHidden").value = Number(document.getElementById("dlLimitText").value) * 1024; + document.getElementById("upLimitHidden").value = Number(document.getElementById("upLimitText").value) * 1024; - $("upload_spinner").style.display = "block"; + document.getElementById("upload_spinner").style.display = "block"; submitted = true; }); - $("upload_frame").addEventListener("load", () => { + document.getElementById("upload_frame").addEventListener("load", (event) => { if (submitted) window.parent.qBittorrent.Client.closeFrameWindow(window); }); if ((Browser.platform === "ios") || ((Browser.platform === "mac") && (navigator.maxTouchPoints > 1))) - $("fileselect").accept = ".torrent"; + document.getElementById("fileselect").accept = ".torrent"; window.qBittorrent.pathAutofill.attachPathAutofill(); diff --git a/src/webui/www/private/uploadlimit.html b/src/webui/www/private/uploadlimit.html index ed672cd47..fb60da531 100644 --- a/src/webui/www/private/uploadlimit.html +++ b/src/webui/www/private/uploadlimit.html @@ -37,7 +37,7 @@ switch (event.key) { case "Enter": event.preventDefault(); - $("applyButton").click(); + document.getElementById("applyButton").click(); break; case "Escape": event.preventDefault(); @@ -48,7 +48,7 @@ const hashes = new URLSearchParams(window.location.search).get("hashes").split("|"); const setUpLimit = () => { - const limit = Number($("uplimitUpdatevalue").value) * 1024; + const limit = Number(document.getElementById("uplimitUpdatevalue").value) * 1024; if (hashes[0] === "global") { fetch("api/v2/transfer/setUploadLimit", { method: "POST", @@ -81,7 +81,7 @@ } }; - $("uplimitUpdatevalue").focus(); + document.getElementById("uplimitUpdatevalue").focus(); MochaUI.addUpLimitSlider(hashes); diff --git a/src/webui/www/private/views/about.html b/src/webui/www/private/views/about.html index cf265f585..c5a67ed83 100644 --- a/src/webui/www/private/views/about.html +++ b/src/webui/www/private/views/about.html @@ -855,12 +855,12 @@ const qbtVersion = window.parent.qBittorrent.Cache.qbtVersion.get(); const buildInfo = window.parent.qBittorrent.Cache.buildInfo.get(); - $("qbittorrentVersion").textContent = `qBittorrent ${qbtVersion} QBT_TR(WebUI)QBT_TR[CONTEXT=OptionsDialog]`; - $("qtVersion").textContent = buildInfo.qt; - $("libtorrentVersion").textContent = buildInfo.libtorrent; - $("boostVersion").textContent = buildInfo.boost; - $("opensslVersion").textContent = buildInfo.openssl; - $("zlibVersion").textContent = buildInfo.zlib; - $("qbittorrentVersion").textContent += ` (${buildInfo.bitness}-bit)`; + document.getElementById("qbittorrentVersion").textContent = `qBittorrent ${qbtVersion} QBT_TR(WebUI)QBT_TR[CONTEXT=OptionsDialog]`; + document.getElementById("qtVersion").textContent = buildInfo.qt; + document.getElementById("libtorrentVersion").textContent = buildInfo.libtorrent; + document.getElementById("boostVersion").textContent = buildInfo.boost; + document.getElementById("opensslVersion").textContent = buildInfo.openssl; + document.getElementById("zlibVersion").textContent = buildInfo.zlib; + document.getElementById("qbittorrentVersion").textContent += ` (${buildInfo.bitness}-bit)`; })(); diff --git a/src/webui/www/private/views/aboutToolbar.html b/src/webui/www/private/views/aboutToolbar.html index 3557a11e5..b097c6c3f 100644 --- a/src/webui/www/private/views/aboutToolbar.html +++ b/src/webui/www/private/views/aboutToolbar.html @@ -16,34 +16,34 @@ (() => { MochaUI.initializeTabs("aboutTabs"); - $("aboutAboutLink").addEventListener("click", () => { + document.getElementById("aboutAboutLink").addEventListener("click", (event) => { Array.prototype.forEach.call(document.querySelectorAll(".aboutTabContent"), (tab => tab.classList.add("invisible"))); - $("aboutAboutContent").classList.remove("invisible"); + document.getElementById("aboutAboutContent").classList.remove("invisible"); }); - $("aboutAuthorLink").addEventListener("click", () => { + document.getElementById("aboutAuthorLink").addEventListener("click", (event) => { Array.prototype.forEach.call(document.querySelectorAll(".aboutTabContent"), (tab => tab.classList.add("invisible"))); - $("aboutAuthorContent").classList.remove("invisible"); + document.getElementById("aboutAuthorContent").classList.remove("invisible"); }); - $("aboutSpecialThanksLink").addEventListener("click", () => { + document.getElementById("aboutSpecialThanksLink").addEventListener("click", (event) => { Array.prototype.forEach.call(document.querySelectorAll(".aboutTabContent"), (tab => tab.classList.add("invisible"))); - $("aboutSpecialThanksContent").classList.remove("invisible"); + document.getElementById("aboutSpecialThanksContent").classList.remove("invisible"); }); - $("aboutTranslatorsLink").addEventListener("click", () => { + document.getElementById("aboutTranslatorsLink").addEventListener("click", (event) => { Array.prototype.forEach.call(document.querySelectorAll(".aboutTabContent"), (tab => tab.classList.add("invisible"))); - $("aboutTranslatorsContent").classList.remove("invisible"); + document.getElementById("aboutTranslatorsContent").classList.remove("invisible"); }); - $("aboutLicenseLink").addEventListener("click", () => { + document.getElementById("aboutLicenseLink").addEventListener("click", (event) => { Array.prototype.forEach.call(document.querySelectorAll(".aboutTabContent"), (tab => tab.classList.add("invisible"))); - $("aboutLicenseContent").classList.remove("invisible"); + document.getElementById("aboutLicenseContent").classList.remove("invisible"); }); - $("aboutSoftwareUsedLink").addEventListener("click", () => { + document.getElementById("aboutSoftwareUsedLink").addEventListener("click", (event) => { Array.prototype.forEach.call(document.querySelectorAll(".aboutTabContent"), (tab => tab.classList.add("invisible"))); - $("aboutSoftwareUsedContent").classList.remove("invisible"); + document.getElementById("aboutSoftwareUsedContent").classList.remove("invisible"); }); })(); diff --git a/src/webui/www/private/views/cookies.html b/src/webui/www/private/views/cookies.html index e7e301057..f6ffe4000 100644 --- a/src/webui/www/private/views/cookies.html +++ b/src/webui/www/private/views/cookies.html @@ -104,7 +104,7 @@ }; const deleteCookie = (element) => { - element.closest("tr").destroy(); + element.closest("tr").remove(); }; const save = () => { diff --git a/src/webui/www/private/views/installsearchplugin.html b/src/webui/www/private/views/installsearchplugin.html index de77a46e1..74e1b7aaa 100644 --- a/src/webui/www/private/views/installsearchplugin.html +++ b/src/webui/www/private/views/installsearchplugin.html @@ -61,11 +61,11 @@ } }); - $("newPluginPath").select(); + document.getElementById("newPluginPath").select(); }; const newPluginOk = () => { - const path = $("newPluginPath").value.trim(); + const path = document.getElementById("newPluginPath").value.trim(); if (path) { fetch("api/v2/search/installPlugin", { method: "POST", diff --git a/src/webui/www/private/views/log.html b/src/webui/www/private/views/log.html index bbafca8cc..b7944b784 100644 --- a/src/webui/www/private/views/log.html +++ b/src/webui/www/private/views/log.html @@ -188,7 +188,7 @@ let selectedLogLevels = JSON.parse(LocalPreferences.get("qbt_selected_log_levels")) || ["1", "2", "4", "8"]; const init = () => { - for (const option of $("logLevelSelect").options) + for (const option of document.getElementById("logLevelSelect").options) option.toggleAttribute("selected", selectedLogLevels.includes(option.value)); selectBox = new vanillaSelectBox("#logLevelSelect", { @@ -282,7 +282,7 @@ }; const filterTextChanged = () => { - const value = $("filterTextInput").value.trim(); + const value = document.getElementById("filterTextInput").value.trim(); if (inputtedFilterText !== value) { inputtedFilterText = value; logFilterChanged(); @@ -306,14 +306,14 @@ currentSelectedTab = tab; if (currentSelectedTab === "main") { selectBox.enable(); - $("logMessageView").classList.remove("invisible"); - $("logPeerView").classList.add("invisible"); + document.getElementById("logMessageView").classList.remove("invisible"); + document.getElementById("logPeerView").classList.add("invisible"); resetTableTimer("peer"); } else { selectBox.disable(); - $("logMessageView").classList.add("invisible"); - $("logPeerView").classList.remove("invisible"); + document.getElementById("logMessageView").classList.add("invisible"); + document.getElementById("logPeerView").classList.remove("invisible"); resetTableTimer("main"); } @@ -331,8 +331,8 @@ if (curTab === undefined) curTab = currentSelectedTab; - $("numFilteredLogs").textContent = tableInfo[curTab].instance.filteredLength; - $("numTotalLogs").textContent = tableInfo[curTab].instance.getRowSize(); + document.getElementById("numFilteredLogs").textContent = tableInfo[curTab].instance.filteredLength; + document.getElementById("numTotalLogs").textContent = tableInfo[curTab].instance.getRowSize(); }; const syncLogData = (curTab) => { @@ -368,7 +368,7 @@ }) .then(async (response) => { if (!response.ok) { - const errorDiv = $("error_div"); + const errorDiv = document.getElementById("error_div"); if (errorDiv) errorDiv.textContent = "QBT_TR(qBittorrent client is not reachable)QBT_TR[CONTEXT=HttpServer]"; tableInfo[curTab].progress = false; @@ -376,9 +376,9 @@ return; } - $("error_div").textContent = ""; + document.getElementById("error_div").textContent = ""; - if ($("logTabColumn").classList.contains("invisible")) + if (document.getElementById("logTabColumn").classList.contains("invisible")) return; const responseJSON = await response.json(); diff --git a/src/webui/www/private/views/preferences.html b/src/webui/www/private/views/preferences.html index 07109e68e..a4cf7e5ae 100644 --- a/src/webui/www/private/views/preferences.html +++ b/src/webui/www/private/views/preferences.html @@ -141,7 +141,7 @@
- QBT_TR(When duplicate torrent is being added)QBT_TR[CONTEXT=OptionsDialog] + QBT_TR(When adding a duplicate torrent)QBT_TR[CONTEXT=OptionsDialog]
@@ -1159,7 +1159,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD -   QBT_TR(MiB)QBT_TR[CONTEXT=OptionsDialog] +   QBT_TR(MiB)QBT_TR[CONTEXT=OptionsDialog] @@ -1801,32 +1801,32 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD }; const updateFileLogEnabled = () => { - const isFileLogEnabled = $("filelog_checkbox").checked; - $("filelog_save_path_input").disabled = !isFileLogEnabled; - $("filelog_backup_checkbox").disabled = !isFileLogEnabled; - $("filelog_delete_old_checkbox").disabled = !isFileLogEnabled; + const isFileLogEnabled = document.getElementById("filelog_checkbox").checked; + document.getElementById("filelog_save_path_input").disabled = !isFileLogEnabled; + document.getElementById("filelog_backup_checkbox").disabled = !isFileLogEnabled; + document.getElementById("filelog_delete_old_checkbox").disabled = !isFileLogEnabled; updateFileLogBackupEnabled(); updateFileLogDeleteEnabled(); }; const updateFileLogBackupEnabled = () => { - const pros = $("filelog_backup_checkbox").getProperties("disabled", "checked"); - $("filelog_max_size_input").disabled = pros.disabled || !pros.checked; + const pros = document.getElementById("filelog_backup_checkbox").getProperties("disabled", "checked"); + document.getElementById("filelog_max_size_input").disabled = pros.disabled || !pros.checked; }; const updateFileLogDeleteEnabled = () => { - const pros = $("filelog_delete_old_checkbox").getProperties("disabled", "checked"); - $("filelog_age_input").disabled = pros.disabled || !pros.checked; - $("filelog_age_type_select").disabled = pros.disabled || !pros.checked; + const pros = document.getElementById("filelog_delete_old_checkbox").getProperties("disabled", "checked"); + document.getElementById("filelog_age_input").disabled = pros.disabled || !pros.checked; + document.getElementById("filelog_age_type_select").disabled = pros.disabled || !pros.checked; }; // Downloads tab let watchedFoldersTable; const updateTempDirEnabled = () => { - const isTempDirEnabled = $("temppath_checkbox").checked; - $("temppath_text").disabled = !isTempDirEnabled; + const isTempDirEnabled = document.getElementById("temppath_checkbox").checked; + document.getElementById("temppath_text").disabled = !isTempDirEnabled; }; const changeWatchFolderSelect = (item) => { @@ -1843,7 +1843,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD }; const addWatchFolder = (folder = "", sel = "default_folder", other = "") => { - const pos = $("watched_folders_tab").getChildren("tbody")[0].getChildren("tr").length; + const pos = document.getElementById("watched_folders_tab").getChildren("tbody")[0].getChildren("tr").length; const myinput = ``; const disableInput = (sel !== "other"); const mycb = `
` @@ -1857,28 +1857,28 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD + `
`; watchedFoldersTable.push([myinput, mycb]); - $(`cb_watch_${pos}`).value = sel; + document.getElementById(`cb_watch_${pos}`).value = sel; if (disableInput) { - const elt = $(`cb_watch_${pos}`); + const elt = document.getElementById(`cb_watch_${pos}`); other = elt.options[elt.selectedIndex].textContent; } - $(`cb_watch_txt_${pos}`).value = other; + document.getElementById(`cb_watch_txt_${pos}`).value = other; // hide previous img if (pos > 0) - $(`addFolderImg_${pos - 1}`).style.display = "none"; + document.getElementById(`addFolderImg_${pos - 1}`).style.display = "none"; }; const getWatchedFolders = () => { const folders = {}; - const entryCount = $("watched_folders_tab").getChildren("tbody")[0].getChildren("tr").length; + const entryCount = document.getElementById("watched_folders_tab").getChildren("tbody")[0].getChildren("tr").length; for (let i = 0; i < entryCount; ++i) { - const fpath = $(`text_watch_${i}`).value.trim(); + const fpath = document.getElementById(`text_watch_${i}`).value.trim(); if (fpath.length <= 0) continue; - const sel = $(`cb_watch_${i}`).value.trim(); + const sel = document.getElementById(`cb_watch_${i}`).value.trim(); let other; switch (sel) { @@ -1889,9 +1889,9 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD other = 1; break; case "other": - other = $(`cb_watch_txt_${i}`).value.trim(); + other = document.getElementById(`cb_watch_txt_${i}`).value.trim(); break; - }; + } folders[fpath] = other; } @@ -1900,39 +1900,39 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD }; const updateExcludedFileNamesEnabled = () => { - const isAExcludedFileNamesEnabled = $("excludedFileNamesCheckbox").checked; - $("excludedFileNamesTextarea").disabled = !isAExcludedFileNamesEnabled; + const isAExcludedFileNamesEnabled = document.getElementById("excludedFileNamesCheckbox").checked; + document.getElementById("excludedFileNamesTextarea").disabled = !isAExcludedFileNamesEnabled; }; const updateExportDirEnabled = () => { - const isExportDirEnabled = $("exportdir_checkbox").checked; - $("exportdir_text").disabled = !isExportDirEnabled; + const isExportDirEnabled = document.getElementById("exportdir_checkbox").checked; + document.getElementById("exportdir_text").disabled = !isExportDirEnabled; }; const updateExportDirFinEnabled = () => { - const isExportDirFinEnabled = $("exportdirfin_checkbox").checked; - $("exportdirfin_text").disabled = !isExportDirFinEnabled; + const isExportDirFinEnabled = document.getElementById("exportdirfin_checkbox").checked; + document.getElementById("exportdirfin_text").disabled = !isExportDirFinEnabled; }; const updateMailNotification = () => { - const isMailNotificationEnabled = $("mail_notification_checkbox").checked; - $("src_email_txt").disabled = !isMailNotificationEnabled; - $("dest_email_txt").disabled = !isMailNotificationEnabled; - $("smtp_server_txt").disabled = !isMailNotificationEnabled; - $("mail_ssl_checkbox").disabled = !isMailNotificationEnabled; - $("mail_auth_checkbox").disabled = !isMailNotificationEnabled; - $("mail_test_button").disabled = !isMailNotificationEnabled; + const isMailNotificationEnabled = document.getElementById("mail_notification_checkbox").checked; + document.getElementById("src_email_txt").disabled = !isMailNotificationEnabled; + document.getElementById("dest_email_txt").disabled = !isMailNotificationEnabled; + document.getElementById("smtp_server_txt").disabled = !isMailNotificationEnabled; + document.getElementById("mail_ssl_checkbox").disabled = !isMailNotificationEnabled; + document.getElementById("mail_auth_checkbox").disabled = !isMailNotificationEnabled; + document.getElementById("mail_test_button").disabled = !isMailNotificationEnabled; if (!isMailNotificationEnabled) { - $("mail_auth_checkbox").checked = !isMailNotificationEnabled; + document.getElementById("mail_auth_checkbox").checked = !isMailNotificationEnabled; updateMailAuthSettings(); } }; const updateMailAuthSettings = () => { - const isMailAuthEnabled = $("mail_auth_checkbox").checked; - $("mail_username_text").disabled = !isMailAuthEnabled; - $("mail_password_text").disabled = !isMailAuthEnabled; + const isMailAuthEnabled = document.getElementById("mail_auth_checkbox").checked; + document.getElementById("mail_username_text").disabled = !isMailAuthEnabled; + document.getElementById("mail_password_text").disabled = !isMailAuthEnabled; }; const sendTestEmail = () => { @@ -1950,167 +1950,167 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD }; const updateAutoRunOnTorrentAdded = () => { - const isAutoRunOnTorrentAddedEnabled = $("autorunOnTorrentAddedCheckbox").checked; - $("autorunOnTorrentAddedProgram").disabled = !isAutoRunOnTorrentAddedEnabled; + const isAutoRunOnTorrentAddedEnabled = document.getElementById("autorunOnTorrentAddedCheckbox").checked; + document.getElementById("autorunOnTorrentAddedProgram").disabled = !isAutoRunOnTorrentAddedEnabled; }; const updateAutoRun = () => { - const isAutoRunEnabled = $("autorun_checkbox").checked; - $("autorunProg_txt").disabled = !isAutoRunEnabled; + const isAutoRunEnabled = document.getElementById("autorun_checkbox").checked; + document.getElementById("autorunProg_txt").disabled = !isAutoRunEnabled; }; // Connection tab const updateMaxConnecEnabled = () => { - const isMaxConnecEnabled = $("maxConnectionsCheckbox").checked; - $("maxConnectionsValue").disabled = !isMaxConnecEnabled; + const isMaxConnecEnabled = document.getElementById("maxConnectionsCheckbox").checked; + document.getElementById("maxConnectionsValue").disabled = !isMaxConnecEnabled; }; const updateMaxConnecPerTorrentEnabled = () => { - const isMaxConnecPerTorrentEnabled = $("maxConnectionsPerTorrentCheckbox").checked; - $("maxConnectionsPerTorrentValue").disabled = !isMaxConnecPerTorrentEnabled; + const isMaxConnecPerTorrentEnabled = document.getElementById("maxConnectionsPerTorrentCheckbox").checked; + document.getElementById("maxConnectionsPerTorrentValue").disabled = !isMaxConnecPerTorrentEnabled; }; const updateMaxUploadsEnabled = () => { - const isMaxUploadsEnabled = $("maxUploadsCheckbox").checked; - $("max_uploads_value").disabled = !isMaxUploadsEnabled; + const isMaxUploadsEnabled = document.getElementById("maxUploadsCheckbox").checked; + document.getElementById("max_uploads_value").disabled = !isMaxUploadsEnabled; }; const updateMaxUploadsPerTorrentEnabled = () => { - const isMaxUploadsPerTorrentEnabled = $("maxUploadsPerTorrentCheckbox").checked; - $("maxUploadsPerTorrentValue").disabled = !isMaxUploadsPerTorrentEnabled; + const isMaxUploadsPerTorrentEnabled = document.getElementById("maxUploadsPerTorrentCheckbox").checked; + document.getElementById("maxUploadsPerTorrentValue").disabled = !isMaxUploadsPerTorrentEnabled; }; const updateI2PSettingsEnabled = () => { - const isI2PEnabled = $("i2pEnabledCheckbox").checked; - $("i2pAddress").disabled = !isI2PEnabled; - $("i2pPort").disabled = !isI2PEnabled; - $("i2pMixedMode").disabled = !isI2PEnabled; + const isI2PEnabled = document.getElementById("i2pEnabledCheckbox").checked; + document.getElementById("i2pAddress").disabled = !isI2PEnabled; + document.getElementById("i2pPort").disabled = !isI2PEnabled; + document.getElementById("i2pMixedMode").disabled = !isI2PEnabled; }; const updatePeerProxySettings = () => { - const proxyType = $("peer_proxy_type_select").value; + const proxyType = document.getElementById("peer_proxy_type_select").value; const isProxyDisabled = (proxyType === "None"); const isProxySocks4 = (proxyType === "SOCKS4"); - $("peer_proxy_host_text").disabled = isProxyDisabled; - $("peer_proxy_port_value").disabled = isProxyDisabled; + document.getElementById("peer_proxy_host_text").disabled = isProxyDisabled; + document.getElementById("peer_proxy_port_value").disabled = isProxyDisabled; - $("peer_proxy_auth_checkbox").disabled = (isProxyDisabled || isProxySocks4); - $("proxy_bittorrent_checkbox").disabled = isProxyDisabled; - $("use_peer_proxy_checkbox").disabled = (isProxyDisabled || !$("proxy_bittorrent_checkbox").checked); - $("proxyHostnameLookupCheckbox").disabled = (isProxyDisabled || isProxySocks4); - $("proxy_rss_checkbox").disabled = (isProxyDisabled || isProxySocks4); - $("proxy_misc_checkbox").disabled = (isProxyDisabled || isProxySocks4); + document.getElementById("peer_proxy_auth_checkbox").disabled = (isProxyDisabled || isProxySocks4); + document.getElementById("proxy_bittorrent_checkbox").disabled = isProxyDisabled; + document.getElementById("use_peer_proxy_checkbox").disabled = (isProxyDisabled || !document.getElementById("proxy_bittorrent_checkbox").checked); + document.getElementById("proxyHostnameLookupCheckbox").disabled = (isProxyDisabled || isProxySocks4); + document.getElementById("proxy_rss_checkbox").disabled = (isProxyDisabled || isProxySocks4); + document.getElementById("proxy_misc_checkbox").disabled = (isProxyDisabled || isProxySocks4); updatePeerProxyAuthSettings(); }; const updatePeerProxyAuthSettings = () => { - const proxyType = $("peer_proxy_type_select").value; + const proxyType = document.getElementById("peer_proxy_type_select").value; const isProxyDisabled = (proxyType === "None"); - const isPeerProxyAuthEnabled = (!$("peer_proxy_auth_checkbox").disabled && $("peer_proxy_auth_checkbox").checked); - $("peer_proxy_username_text").disabled = (isProxyDisabled || !isPeerProxyAuthEnabled); - $("peer_proxy_password_text").disabled = (isProxyDisabled || !isPeerProxyAuthEnabled); + const isPeerProxyAuthEnabled = (!document.getElementById("peer_proxy_auth_checkbox").disabled && document.getElementById("peer_proxy_auth_checkbox").checked); + document.getElementById("peer_proxy_username_text").disabled = (isProxyDisabled || !isPeerProxyAuthEnabled); + document.getElementById("peer_proxy_password_text").disabled = (isProxyDisabled || !isPeerProxyAuthEnabled); }; const updateFilterSettings = () => { - const isIPFilterEnabled = $("ipfilter_text_checkbox").checked; - $("ipfilter_text").disabled = !isIPFilterEnabled; + const isIPFilterEnabled = document.getElementById("ipfilter_text_checkbox").checked; + document.getElementById("ipfilter_text").disabled = !isIPFilterEnabled; }; // Speed tab const updateSchedulingEnabled = () => { - const isLimitSchedulingEnabled = $("limitSchedulingCheckbox").checked; - $("schedule_from_hour").disabled = !isLimitSchedulingEnabled; - $("schedule_from_min").disabled = !isLimitSchedulingEnabled; - $("schedule_to_hour").disabled = !isLimitSchedulingEnabled; - $("schedule_to_min").disabled = !isLimitSchedulingEnabled; - $("schedule_freq_select").disabled = !isLimitSchedulingEnabled; + const isLimitSchedulingEnabled = document.getElementById("limitSchedulingCheckbox").checked; + document.getElementById("schedule_from_hour").disabled = !isLimitSchedulingEnabled; + document.getElementById("schedule_from_min").disabled = !isLimitSchedulingEnabled; + document.getElementById("schedule_to_hour").disabled = !isLimitSchedulingEnabled; + document.getElementById("schedule_to_min").disabled = !isLimitSchedulingEnabled; + document.getElementById("schedule_freq_select").disabled = !isLimitSchedulingEnabled; }; // Bittorrent tab const updateQueueingSystem = () => { - const isQueueingEnabled = $("queueingCheckbox").checked; - $("maxActiveDlValue").disabled = !isQueueingEnabled; - $("maxActiveUpValue").disabled = !isQueueingEnabled; - $("maxActiveToValue").disabled = !isQueueingEnabled; - $("dontCountSlowTorrentsCheckbox").disabled = !isQueueingEnabled; + const isQueueingEnabled = document.getElementById("queueingCheckbox").checked; + document.getElementById("maxActiveDlValue").disabled = !isQueueingEnabled; + document.getElementById("maxActiveUpValue").disabled = !isQueueingEnabled; + document.getElementById("maxActiveToValue").disabled = !isQueueingEnabled; + document.getElementById("dontCountSlowTorrentsCheckbox").disabled = !isQueueingEnabled; updateSlowTorrentsSettings(); }; const updateSlowTorrentsSettings = () => { - const isDontCountSlowTorrentsEnabled = (!$("dontCountSlowTorrentsCheckbox").disabled) && $("dontCountSlowTorrentsCheckbox").checked; - $("dlRateThresholdValue").disabled = !isDontCountSlowTorrentsEnabled; - $("ulRateThresholdValue").disabled = !isDontCountSlowTorrentsEnabled; - $("torrentInactiveTimerValue").disabled = !isDontCountSlowTorrentsEnabled; + const isDontCountSlowTorrentsEnabled = (!document.getElementById("dontCountSlowTorrentsCheckbox").disabled) && document.getElementById("dontCountSlowTorrentsCheckbox").checked; + document.getElementById("dlRateThresholdValue").disabled = !isDontCountSlowTorrentsEnabled; + document.getElementById("ulRateThresholdValue").disabled = !isDontCountSlowTorrentsEnabled; + document.getElementById("torrentInactiveTimerValue").disabled = !isDontCountSlowTorrentsEnabled; }; const updateMaxRatioTimeEnabled = () => { - const isMaxRatioEnabled = $("maxRatioCheckbox").checked; - $("maxRatioValue").disabled = !isMaxRatioEnabled; + const isMaxRatioEnabled = document.getElementById("maxRatioCheckbox").checked; + document.getElementById("maxRatioValue").disabled = !isMaxRatioEnabled; - const isMaxSeedingTimeEnabled = $("maxSeedingTimeCheckbox").checked; - $("maxSeedingTimeValue").disabled = !isMaxSeedingTimeEnabled; + const isMaxSeedingTimeEnabled = document.getElementById("maxSeedingTimeCheckbox").checked; + document.getElementById("maxSeedingTimeValue").disabled = !isMaxSeedingTimeEnabled; - const isMaxInactiveSeedingTimeEnabled = $("maxInactiveSeedingTimeCheckbox").checked; - $("maxInactiveSeedingTimeValue").disabled = !isMaxInactiveSeedingTimeEnabled; + const isMaxInactiveSeedingTimeEnabled = document.getElementById("maxInactiveSeedingTimeCheckbox").checked; + document.getElementById("maxInactiveSeedingTimeValue").disabled = !isMaxInactiveSeedingTimeEnabled; - $("maxRatioSelect").disabled = !(isMaxRatioEnabled || isMaxSeedingTimeEnabled || isMaxInactiveSeedingTimeEnabled); + document.getElementById("maxRatioSelect").disabled = !(isMaxRatioEnabled || isMaxSeedingTimeEnabled || isMaxInactiveSeedingTimeEnabled); }; const updateAddTrackersEnabled = () => { - const isAddTrackersEnabled = $("add_trackers_checkbox").checked; - $("add_trackers_textarea").disabled = !isAddTrackersEnabled; + const isAddTrackersEnabled = document.getElementById("add_trackers_checkbox").checked; + document.getElementById("add_trackers_textarea").disabled = !isAddTrackersEnabled; }; const updateAddTrackersFromURLEnabled = () => { - const isAddTrackersFromURLEnabled = $("addTrackersFromURLCheckbox").checked; - $("addTrackersURL").disabled = !isAddTrackersFromURLEnabled; + const isAddTrackersFromURLEnabled = document.getElementById("addTrackersFromURLCheckbox").checked; + document.getElementById("addTrackersURL").disabled = !isAddTrackersFromURLEnabled; }; // WebUI tab const updateHttpsSettings = () => { - const isUseHttpsEnabled = $("use_https_checkbox").checked; - $("ssl_cert_text").disabled = !isUseHttpsEnabled; - $("ssl_key_text").disabled = !isUseHttpsEnabled; + const isUseHttpsEnabled = document.getElementById("use_https_checkbox").checked; + document.getElementById("ssl_cert_text").disabled = !isUseHttpsEnabled; + document.getElementById("ssl_key_text").disabled = !isUseHttpsEnabled; }; const updateBypasssAuthSettings = () => { - const isBypassAuthSubnetWhitelistEnabled = $("bypass_auth_subnet_whitelist_checkbox").checked; - $("bypass_auth_subnet_whitelist_textarea").disabled = !isBypassAuthSubnetWhitelistEnabled; + const isBypassAuthSubnetWhitelistEnabled = document.getElementById("bypass_auth_subnet_whitelist_checkbox").checked; + document.getElementById("bypass_auth_subnet_whitelist_textarea").disabled = !isBypassAuthSubnetWhitelistEnabled; }; const updateAlternativeWebUISettings = () => { - const isUseAlternativeWebUIEnabled = $("use_alt_webui_checkbox").checked; - $("webui_files_location_textarea").disabled = !isUseAlternativeWebUIEnabled; + const isUseAlternativeWebUIEnabled = document.getElementById("use_alt_webui_checkbox").checked; + document.getElementById("webui_files_location_textarea").disabled = !isUseAlternativeWebUIEnabled; }; const updateHostHeaderValidationSettings = () => { - const isHostHeaderValidationEnabled = $("host_header_validation_checkbox").checked; - $("webuiDomainTextarea").disabled = !isHostHeaderValidationEnabled; + const isHostHeaderValidationEnabled = document.getElementById("host_header_validation_checkbox").checked; + document.getElementById("webuiDomainTextarea").disabled = !isHostHeaderValidationEnabled; }; const updateWebUICustomHTTPHeadersSettings = () => { - const isEnabled = $("webUIUseCustomHTTPHeadersCheckbox").checked; - $("webUICustomHTTPHeadersTextarea").disabled = !isEnabled; + const isEnabled = document.getElementById("webUIUseCustomHTTPHeadersCheckbox").checked; + document.getElementById("webUICustomHTTPHeadersTextarea").disabled = !isEnabled; }; const updateWebUIReverseProxySettings = () => { - const isEnabled = $("webUIReverseProxySupportCheckbox").checked; - $("webUIReverseProxiesListTextarea").disabled = !isEnabled; + const isEnabled = document.getElementById("webUIReverseProxySupportCheckbox").checked; + document.getElementById("webUIReverseProxiesListTextarea").disabled = !isEnabled; }; const updateDynDnsSettings = () => { - const isDynDnsEnabled = $("use_dyndns_checkbox").checked; - $("dyndns_select").disabled = !isDynDnsEnabled; - $("dyndns_domain_text").disabled = !isDynDnsEnabled; - $("dyndns_username_text").disabled = !isDynDnsEnabled; - $("dyndns_password_text").disabled = !isDynDnsEnabled; + const isDynDnsEnabled = document.getElementById("use_dyndns_checkbox").checked; + document.getElementById("dyndns_select").disabled = !isDynDnsEnabled; + document.getElementById("dyndns_domain_text").disabled = !isDynDnsEnabled; + document.getElementById("dyndns_username_text").disabled = !isDynDnsEnabled; + document.getElementById("dyndns_password_text").disabled = !isDynDnsEnabled; }; const registerDynDns = () => { - if (Number($("dyndns_select").value) === 1) + if (Number(document.getElementById("dyndns_select").value) === 1) window.open("http://www.no-ip.com/services/managed_dns/free_dynamic_dns.html", "NO-IP Registration"); else window.open("https://www.dyndns.com/account/services/hosts/add.html", "DynDNS Registration"); @@ -2120,7 +2120,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD const min = 1024; const max = 65535; const port = Math.floor(Math.random() * (max - min + 1) + min); - $("portValue").value = port; + document.getElementById("portValue").value = port; }; const time_padding = (val) => { @@ -2132,7 +2132,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD // Advanced Tab const updateNetworkInterfaces = (default_iface, default_iface_name) => { - [...document.getElementById("networkInterface").children].forEach((el) => { el.destroy(); }); + [...document.getElementById("networkInterface").children].forEach((el) => { el.remove(); }); fetch("api/v2/app/networkInterfaceList", { method: "GET", @@ -2152,16 +2152,16 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD if (default_iface && !ifaces.some((item) => item.value === default_iface)) ifaces.push({ name: default_iface_name || default_iface, value: default_iface }); - $("networkInterface").options.add(new Option("QBT_TR(Any interface)QBT_TR[CONTEXT=OptionsDialog]", "")); + document.getElementById("networkInterface").options.add(new Option("QBT_TR(Any interface)QBT_TR[CONTEXT=OptionsDialog]", "")); ifaces.forEach((item, index) => { - $("networkInterface").options.add(new Option(item.name, item.value)); + document.getElementById("networkInterface").options.add(new Option(item.name, item.value)); }); - $("networkInterface").value = default_iface; + document.getElementById("networkInterface").value = default_iface; }); }; const updateInterfaceAddresses = (iface, default_addr) => { - [...document.getElementById("optionalIPAddressToBind").children].forEach((el) => { el.destroy(); }); + [...document.getElementById("optionalIPAddressToBind").children].forEach((el) => { el.remove(); }); const url = new URL("api/v2/app/networkInterfaceAddressList", window.location); url.search = new URLSearchParams({ @@ -2181,26 +2181,26 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD if (!addresses) return; - $("optionalIPAddressToBind").options.add(new Option("QBT_TR(All addresses)QBT_TR[CONTEXT=OptionDialog]", "")); - $("optionalIPAddressToBind").options.add(new Option("QBT_TR(All IPv4 addresses)QBT_TR[CONTEXT=OptionDialog]", "0.0.0.0")); - $("optionalIPAddressToBind").options.add(new Option("QBT_TR(All IPv6 addresses)QBT_TR[CONTEXT=OptionDialog]", "::")); + document.getElementById("optionalIPAddressToBind").options.add(new Option("QBT_TR(All addresses)QBT_TR[CONTEXT=OptionDialog]", "")); + document.getElementById("optionalIPAddressToBind").options.add(new Option("QBT_TR(All IPv4 addresses)QBT_TR[CONTEXT=OptionDialog]", "0.0.0.0")); + document.getElementById("optionalIPAddressToBind").options.add(new Option("QBT_TR(All IPv6 addresses)QBT_TR[CONTEXT=OptionDialog]", "::")); addresses.forEach((item, index) => { - $("optionalIPAddressToBind").options.add(new Option(item, item)); + document.getElementById("optionalIPAddressToBind").options.add(new Option(item, item)); }); - $("optionalIPAddressToBind").value = default_addr; + document.getElementById("optionalIPAddressToBind").value = default_addr; }); }; const updateWebuiLocaleSelect = (selected) => { const languages = []; - for (let i = 0; i < $("locale_select").options.length; i++) - languages.push($("locale_select").options[i].value); + for (let i = 0; i < document.getElementById("locale_select").options.length; i++) + languages.push(document.getElementById("locale_select").options[i].value); if (!languages.includes(selected)) { const lang = selected.split("_", 1)[0]; selected = languages.includes(lang) ? lang : "en"; } - $("locale_select").value = selected; + document.getElementById("locale_select").value = selected; }; const updateColoSchemeSelect = () => { @@ -2222,22 +2222,22 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD // Language updateWebuiLocaleSelect(pref.locale); updateColoSchemeSelect(); - $("statusBarExternalIP").checked = pref.status_bar_external_ip; - $("performanceWarning").checked = pref.performance_warning; + document.getElementById("statusBarExternalIP").checked = pref.status_bar_external_ip; + document.getElementById("performanceWarning").checked = pref.performance_warning; document.getElementById("displayFullURLTrackerColumn").checked = (LocalPreferences.get("full_url_tracker_column", "false") === "true"); document.getElementById("useVirtualList").checked = (LocalPreferences.get("use_virtual_list", "false") === "true"); document.getElementById("hideZeroFiltersCheckbox").checked = (LocalPreferences.get("hide_zero_status_filters", "false") === "true"); - $("dblclickDownloadSelect").value = LocalPreferences.get("dblclick_download", "1"); - $("dblclickCompleteSelect").value = LocalPreferences.get("dblclick_complete", "1"); + document.getElementById("dblclickDownloadSelect").value = LocalPreferences.get("dblclick_download", "1"); + document.getElementById("dblclickCompleteSelect").value = LocalPreferences.get("dblclick_complete", "1"); document.getElementById("confirmTorrentDeletion").checked = pref.confirm_torrent_deletion; document.getElementById("useAltRowColorsInput").checked = (LocalPreferences.get("use_alt_row_colors", "true") === "true"); - $("filelog_checkbox").checked = pref.file_log_enabled; - $("filelog_save_path_input").value = pref.file_log_path; - $("filelog_backup_checkbox").checked = pref.file_log_backup_enabled; - $("filelog_max_size_input").value = pref.file_log_max_size; - $("filelog_delete_old_checkbox").checked = pref.file_log_delete_old; - $("filelog_age_input").value = pref.file_log_age; - $("filelog_age_type_select").value = pref.file_log_age_type; + document.getElementById("filelog_checkbox").checked = pref.file_log_enabled; + document.getElementById("filelog_save_path_input").value = pref.file_log_path; + document.getElementById("filelog_backup_checkbox").checked = pref.file_log_backup_enabled; + document.getElementById("filelog_max_size_input").value = pref.file_log_max_size; + document.getElementById("filelog_delete_old_checkbox").checked = pref.file_log_delete_old; + document.getElementById("filelog_age_input").value = pref.file_log_age; + document.getElementById("filelog_age_type_select").value = pref.file_log_age_type; updateFileLogEnabled(); // Downloads tab @@ -2254,9 +2254,9 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD index = 2; break; } - $("contentlayout_select").getChildren("option")[index].selected = true; - $("addToTopOfQueueCheckbox").checked = pref.add_to_top_of_queue; - $("dontstartdownloads_checkbox").checked = pref.add_stopped_enabled; + document.getElementById("contentlayout_select").getChildren("option")[index].selected = true; + document.getElementById("addToTopOfQueueCheckbox").checked = pref.add_to_top_of_queue; + document.getElementById("dontstartdownloads_checkbox").checked = pref.add_stopped_enabled; switch (pref.torrent_stop_condition) { case "None": index = 0; @@ -2268,41 +2268,41 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD index = 2; break; } - $("stopConditionSelect").getChildren("option")[index].selected = true; + document.getElementById("stopConditionSelect").getChildren("option")[index].selected = true; document.getElementById("mergeTrackersInput").checked = pref.merge_trackers; - $("deletetorrentfileafter_checkbox").checked = pref.auto_delete_mode; + document.getElementById("deletetorrentfileafter_checkbox").checked = pref.auto_delete_mode; - $("preallocateall_checkbox").checked = pref.preallocate_all; - $("appendext_checkbox").checked = pref.incomplete_files_ext; - $("unwantedfolder_checkbox").checked = pref.use_unwanted_folder; + document.getElementById("preallocateall_checkbox").checked = pref.preallocate_all; + document.getElementById("appendext_checkbox").checked = pref.incomplete_files_ext; + document.getElementById("unwantedfolder_checkbox").checked = pref.use_unwanted_folder; // Saving Management - $("default_tmm_combobox").value = pref.auto_tmm_enabled; - $("torrent_changed_tmm_combobox").value = pref.torrent_changed_tmm_enabled; - $("save_path_changed_tmm_combobox").value = pref.save_path_changed_tmm_enabled; - $("category_changed_tmm_combobox").value = pref.category_changed_tmm_enabled; - $("use_subcategories_checkbox").checked = pref.use_subcategories; + document.getElementById("default_tmm_combobox").value = pref.auto_tmm_enabled; + document.getElementById("torrent_changed_tmm_combobox").value = pref.torrent_changed_tmm_enabled; + document.getElementById("save_path_changed_tmm_combobox").value = pref.save_path_changed_tmm_enabled; + document.getElementById("category_changed_tmm_combobox").value = pref.category_changed_tmm_enabled; + document.getElementById("use_subcategories_checkbox").checked = pref.use_subcategories; document.getElementById("categoryPathsManualModeCheckbox").checked = pref.use_category_paths_in_manual_mode; - $("savepath_text").value = pref.save_path; - $("temppath_checkbox").checked = pref.temp_path_enabled; - $("temppath_text").value = pref.temp_path; + document.getElementById("savepath_text").value = pref.save_path; + document.getElementById("temppath_checkbox").checked = pref.temp_path_enabled; + document.getElementById("temppath_text").value = pref.temp_path; updateTempDirEnabled(); if (pref.export_dir !== "") { - $("exportdir_checkbox").checked = true; - $("exportdir_text").value = pref.export_dir; + document.getElementById("exportdir_checkbox").checked = true; + document.getElementById("exportdir_text").value = pref.export_dir; } else { - $("exportdir_checkbox").checked = false; - $("exportdir_text").value = ""; + document.getElementById("exportdir_checkbox").checked = false; + document.getElementById("exportdir_text").value = ""; } updateExportDirEnabled(); if (pref.export_dir_fin !== "") { - $("exportdirfin_checkbox").checked = true; - $("exportdirfin_text").value = pref.export_dir_fin; + document.getElementById("exportdirfin_checkbox").checked = true; + document.getElementById("exportdirfin_text").value = pref.export_dir_fin; } else { - $("exportdirfin_checkbox").checked = false; - $("exportdirfin_text").value = ""; + document.getElementById("exportdirfin_checkbox").checked = false; + document.getElementById("exportdirfin_text").value = ""; } updateExportDirFinEnabled(); @@ -2323,159 +2323,159 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD addWatchFolder(); // Excluded file names - $("excludedFileNamesCheckbox").checked = pref.excluded_file_names_enabled; - $("excludedFileNamesTextarea").value = pref.excluded_file_names; + document.getElementById("excludedFileNamesCheckbox").checked = pref.excluded_file_names_enabled; + document.getElementById("excludedFileNamesTextarea").value = pref.excluded_file_names; // Email notification upon download completion - $("mail_notification_checkbox").checked = pref.mail_notification_enabled; - $("src_email_txt").value = pref.mail_notification_sender; - $("dest_email_txt").value = pref.mail_notification_email; - $("smtp_server_txt").value = pref.mail_notification_smtp; - $("mail_ssl_checkbox").checked = pref.mail_notification_ssl_enabled; - $("mail_auth_checkbox").checked = pref.mail_notification_auth_enabled; - $("mail_username_text").value = pref.mail_notification_username; - $("mail_password_text").value = pref.mail_notification_password; + document.getElementById("mail_notification_checkbox").checked = pref.mail_notification_enabled; + document.getElementById("src_email_txt").value = pref.mail_notification_sender; + document.getElementById("dest_email_txt").value = pref.mail_notification_email; + document.getElementById("smtp_server_txt").value = pref.mail_notification_smtp; + document.getElementById("mail_ssl_checkbox").checked = pref.mail_notification_ssl_enabled; + document.getElementById("mail_auth_checkbox").checked = pref.mail_notification_auth_enabled; + document.getElementById("mail_username_text").value = pref.mail_notification_username; + document.getElementById("mail_password_text").value = pref.mail_notification_password; updateMailNotification(); updateMailAuthSettings(); // Run an external program on torrent added - $("autorunOnTorrentAddedCheckbox").checked = pref.autorun_on_torrent_added_enabled; - $("autorunOnTorrentAddedProgram").value = pref.autorun_on_torrent_added_program; + document.getElementById("autorunOnTorrentAddedCheckbox").checked = pref.autorun_on_torrent_added_enabled; + document.getElementById("autorunOnTorrentAddedProgram").value = pref.autorun_on_torrent_added_program; updateAutoRunOnTorrentAdded(); // Run an external program on torrent finished - $("autorun_checkbox").checked = pref.autorun_enabled; - $("autorunProg_txt").value = pref.autorun_program; + document.getElementById("autorun_checkbox").checked = pref.autorun_enabled; + document.getElementById("autorunProg_txt").value = pref.autorun_program; updateAutoRun(); // Connection tab // Listening Port - $("portValue").value = Number(pref.listen_port); - $("upnpCheckbox").checked = pref.upnp; + document.getElementById("portValue").value = Number(pref.listen_port); + document.getElementById("upnpCheckbox").checked = pref.upnp; // Connections Limits const maxConnec = Number(pref.max_connec); if (maxConnec <= 0) { - $("maxConnectionsCheckbox").checked = false; - $("maxConnectionsValue").value = 500; + document.getElementById("maxConnectionsCheckbox").checked = false; + document.getElementById("maxConnectionsValue").value = 500; } else { - $("maxConnectionsCheckbox").checked = true; - $("maxConnectionsValue").value = maxConnec; + document.getElementById("maxConnectionsCheckbox").checked = true; + document.getElementById("maxConnectionsValue").value = maxConnec; } updateMaxConnecEnabled(); const maxConnecPerTorrent = Number(pref.max_connec_per_torrent); if (maxConnecPerTorrent <= 0) { - $("maxConnectionsPerTorrentCheckbox").checked = false; - $("maxConnectionsPerTorrentValue").value = 100; + document.getElementById("maxConnectionsPerTorrentCheckbox").checked = false; + document.getElementById("maxConnectionsPerTorrentValue").value = 100; } else { - $("maxConnectionsPerTorrentCheckbox").checked = true; - $("maxConnectionsPerTorrentValue").value = maxConnecPerTorrent; + document.getElementById("maxConnectionsPerTorrentCheckbox").checked = true; + document.getElementById("maxConnectionsPerTorrentValue").value = maxConnecPerTorrent; } updateMaxConnecPerTorrentEnabled(); const maxUploads = Number(pref.max_uploads); if (maxUploads <= 0) { - $("maxUploadsCheckbox").checked = false; - $("max_uploads_value").value = 8; + document.getElementById("maxUploadsCheckbox").checked = false; + document.getElementById("max_uploads_value").value = 8; } else { - $("maxUploadsCheckbox").checked = true; - $("max_uploads_value").value = maxUploads; + document.getElementById("maxUploadsCheckbox").checked = true; + document.getElementById("max_uploads_value").value = maxUploads; } updateMaxUploadsEnabled(); const maxUploadsPerTorrent = Number(pref.max_uploads_per_torrent); if (maxUploadsPerTorrent <= 0) { - $("maxUploadsPerTorrentCheckbox").checked = false; - $("maxUploadsPerTorrentValue").value = 4; + document.getElementById("maxUploadsPerTorrentCheckbox").checked = false; + document.getElementById("maxUploadsPerTorrentValue").value = 4; } else { - $("maxUploadsPerTorrentCheckbox").checked = true; - $("maxUploadsPerTorrentValue").value = maxUploadsPerTorrent; + document.getElementById("maxUploadsPerTorrentCheckbox").checked = true; + document.getElementById("maxUploadsPerTorrentValue").value = maxUploadsPerTorrent; } updateMaxUploadsPerTorrentEnabled(); // I2P - $("i2pEnabledCheckbox").checked = pref.i2p_enabled; - $("i2pAddress").value = pref.i2p_address; - $("i2pPort").value = pref.i2p_port; - $("i2pMixedMode").checked = pref.i2p_mixed_mode; + document.getElementById("i2pEnabledCheckbox").checked = pref.i2p_enabled; + document.getElementById("i2pAddress").value = pref.i2p_address; + document.getElementById("i2pPort").value = pref.i2p_port; + document.getElementById("i2pMixedMode").checked = pref.i2p_mixed_mode; updateI2PSettingsEnabled(); // Proxy Server - $("peer_proxy_type_select").value = pref.proxy_type; - $("peer_proxy_host_text").value = pref.proxy_ip; - $("peer_proxy_port_value").value = pref.proxy_port; - $("peer_proxy_auth_checkbox").checked = pref.proxy_auth_enabled; - $("peer_proxy_username_text").value = pref.proxy_username; - $("peer_proxy_password_text").value = pref.proxy_password; - $("proxyHostnameLookupCheckbox").checked = pref.proxy_hostname_lookup; - $("proxy_bittorrent_checkbox").checked = pref.proxy_bittorrent; - $("use_peer_proxy_checkbox").checked = pref.proxy_peer_connections; - $("proxy_rss_checkbox").checked = pref.proxy_rss; - $("proxy_misc_checkbox").checked = pref.proxy_misc; + document.getElementById("peer_proxy_type_select").value = pref.proxy_type; + document.getElementById("peer_proxy_host_text").value = pref.proxy_ip; + document.getElementById("peer_proxy_port_value").value = pref.proxy_port; + document.getElementById("peer_proxy_auth_checkbox").checked = pref.proxy_auth_enabled; + document.getElementById("peer_proxy_username_text").value = pref.proxy_username; + document.getElementById("peer_proxy_password_text").value = pref.proxy_password; + document.getElementById("proxyHostnameLookupCheckbox").checked = pref.proxy_hostname_lookup; + document.getElementById("proxy_bittorrent_checkbox").checked = pref.proxy_bittorrent; + document.getElementById("use_peer_proxy_checkbox").checked = pref.proxy_peer_connections; + document.getElementById("proxy_rss_checkbox").checked = pref.proxy_rss; + document.getElementById("proxy_misc_checkbox").checked = pref.proxy_misc; updatePeerProxySettings(); // IP Filtering - $("ipfilter_text_checkbox").checked = pref.ip_filter_enabled; - $("ipfilter_text").value = pref.ip_filter_path; - $("ipfilter_trackers_checkbox").checked = pref.ip_filter_trackers; - $("banned_IPs_textarea").value = pref.banned_IPs; + document.getElementById("ipfilter_text_checkbox").checked = pref.ip_filter_enabled; + document.getElementById("ipfilter_text").value = pref.ip_filter_path; + document.getElementById("ipfilter_trackers_checkbox").checked = pref.ip_filter_trackers; + document.getElementById("banned_IPs_textarea").value = pref.banned_IPs; updateFilterSettings(); // Speed tab // Global Rate Limits - $("upLimitValue").value = (Number(pref.up_limit) / 1024); - $("dlLimitValue").value = (Number(pref.dl_limit) / 1024); + document.getElementById("upLimitValue").value = (Number(pref.up_limit) / 1024); + document.getElementById("dlLimitValue").value = (Number(pref.dl_limit) / 1024); // Alternative Global Rate Limits - $("altUpLimitValue").value = (Number(pref.alt_up_limit) / 1024); - $("altDlLimitValue").value = (Number(pref.alt_dl_limit) / 1024); + document.getElementById("altUpLimitValue").value = (Number(pref.alt_up_limit) / 1024); + document.getElementById("altDlLimitValue").value = (Number(pref.alt_dl_limit) / 1024); - $("enable_protocol_combobox").value = pref.bittorrent_protocol; - $("limit_utp_rate_checkbox").checked = pref.limit_utp_rate; - $("limit_tcp_overhead_checkbox").checked = pref.limit_tcp_overhead; - $("limit_lan_peers_checkbox").checked = pref.limit_lan_peers; + document.getElementById("enable_protocol_combobox").value = pref.bittorrent_protocol; + document.getElementById("limit_utp_rate_checkbox").checked = pref.limit_utp_rate; + document.getElementById("limit_tcp_overhead_checkbox").checked = pref.limit_tcp_overhead; + document.getElementById("limit_lan_peers_checkbox").checked = pref.limit_lan_peers; // Scheduling - $("limitSchedulingCheckbox").checked = pref.scheduler_enabled; - $("schedule_from_hour").value = time_padding(pref.schedule_from_hour); - $("schedule_from_min").value = time_padding(pref.schedule_from_min); - $("schedule_to_hour").value = time_padding(pref.schedule_to_hour); - $("schedule_to_min").value = time_padding(pref.schedule_to_min); - $("schedule_freq_select").value = pref.scheduler_days; + document.getElementById("limitSchedulingCheckbox").checked = pref.scheduler_enabled; + document.getElementById("schedule_from_hour").value = time_padding(pref.schedule_from_hour); + document.getElementById("schedule_from_min").value = time_padding(pref.schedule_from_min); + document.getElementById("schedule_to_hour").value = time_padding(pref.schedule_to_hour); + document.getElementById("schedule_to_min").value = time_padding(pref.schedule_to_min); + document.getElementById("schedule_freq_select").value = pref.scheduler_days; updateSchedulingEnabled(); // Bittorrent tab // Privacy - $("dht_checkbox").checked = pref.dht; - $("pex_checkbox").checked = pref.pex; - $("lsd_checkbox").checked = pref.lsd; + document.getElementById("dht_checkbox").checked = pref.dht; + document.getElementById("pex_checkbox").checked = pref.pex; + document.getElementById("lsd_checkbox").checked = pref.lsd; const encryption = Number(pref.encryption); - $("encryption_select").getChildren("option")[encryption].selected = true; - $("anonymous_mode_checkbox").checked = pref.anonymous_mode; + document.getElementById("encryption_select").getChildren("option")[encryption].selected = true; + document.getElementById("anonymous_mode_checkbox").checked = pref.anonymous_mode; - $("maxActiveCheckingTorrents").value = pref.max_active_checking_torrents; + document.getElementById("maxActiveCheckingTorrents").value = pref.max_active_checking_torrents; // Torrent Queueing - $("queueingCheckbox").checked = pref.queueing_enabled; - $("maxActiveDlValue").value = Number(pref.max_active_downloads); - $("maxActiveUpValue").value = Number(pref.max_active_uploads); - $("maxActiveToValue").value = Number(pref.max_active_torrents); - $("dontCountSlowTorrentsCheckbox").checked = pref.dont_count_slow_torrents; - $("dlRateThresholdValue").value = Number(pref.slow_torrent_dl_rate_threshold); - $("ulRateThresholdValue").value = Number(pref.slow_torrent_ul_rate_threshold); - $("torrentInactiveTimerValue").value = Number(pref.slow_torrent_inactive_timer); + document.getElementById("queueingCheckbox").checked = pref.queueing_enabled; + document.getElementById("maxActiveDlValue").value = Number(pref.max_active_downloads); + document.getElementById("maxActiveUpValue").value = Number(pref.max_active_uploads); + document.getElementById("maxActiveToValue").value = Number(pref.max_active_torrents); + document.getElementById("dontCountSlowTorrentsCheckbox").checked = pref.dont_count_slow_torrents; + document.getElementById("dlRateThresholdValue").value = Number(pref.slow_torrent_dl_rate_threshold); + document.getElementById("ulRateThresholdValue").value = Number(pref.slow_torrent_ul_rate_threshold); + document.getElementById("torrentInactiveTimerValue").value = Number(pref.slow_torrent_inactive_timer); updateQueueingSystem(); // Share Limiting - $("maxRatioCheckbox").checked = pref.max_ratio_enabled; - $("maxRatioValue").value = (pref.max_ratio_enabled ? pref.max_ratio : 1); - $("maxSeedingTimeCheckbox").checked = pref.max_seeding_time_enabled; - $("maxSeedingTimeValue").value = (pref.max_seeding_time_enabled ? Number(pref.max_seeding_time) : 1440); - $("maxInactiveSeedingTimeCheckbox").checked = pref.max_inactive_seeding_time_enabled; - $("maxInactiveSeedingTimeValue").value = (pref.max_inactive_seeding_time_enabled ? Number(pref.max_inactive_seeding_time) : 1440); + document.getElementById("maxRatioCheckbox").checked = pref.max_ratio_enabled; + document.getElementById("maxRatioValue").value = (pref.max_ratio_enabled ? pref.max_ratio : 1); + document.getElementById("maxSeedingTimeCheckbox").checked = pref.max_seeding_time_enabled; + document.getElementById("maxSeedingTimeValue").value = (pref.max_seeding_time_enabled ? Number(pref.max_seeding_time) : 1440); + document.getElementById("maxInactiveSeedingTimeCheckbox").checked = pref.max_inactive_seeding_time_enabled; + document.getElementById("maxInactiveSeedingTimeValue").value = (pref.max_inactive_seeding_time_enabled ? Number(pref.max_inactive_seeding_time) : 1440); let maxRatioAct = 0; switch (Number(pref.max_ratio_act)) { case 0: // Stop @@ -2492,151 +2492,151 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD maxRatioAct = 2; break; } - $("maxRatioSelect").getChildren("option")[maxRatioAct].selected = true; + document.getElementById("maxRatioSelect").getChildren("option")[maxRatioAct].selected = true; updateMaxRatioTimeEnabled(); // Add trackers - $("add_trackers_checkbox").checked = pref.add_trackers_enabled; - $("add_trackers_textarea").value = pref.add_trackers; - $("addTrackersFromURLCheckbox").checked = pref.add_trackers_from_url_enabled; - $("addTrackersURLListTextarea").value = pref.add_trackers_url_list; - $("addTrackersURL").value = pref.add_trackers_url; + document.getElementById("add_trackers_checkbox").checked = pref.add_trackers_enabled; + document.getElementById("add_trackers_textarea").value = pref.add_trackers; + document.getElementById("addTrackersFromURLCheckbox").checked = pref.add_trackers_from_url_enabled; + document.getElementById("addTrackersURLListTextarea").value = pref.add_trackers_url_list; + document.getElementById("addTrackersURL").value = pref.add_trackers_url; updateAddTrackersEnabled(); updateAddTrackersFromURLEnabled(); // RSS Tab - $("enable_fetching_rss_feeds_checkbox").checked = pref.rss_processing_enabled; - $("feed_refresh_interval").value = pref.rss_refresh_interval; - $("feedFetchDelay").value = pref.rss_fetch_delay; - $("maximum_article_number").value = pref.rss_max_articles_per_feed; - $("enable_auto_downloading_rss_torrents_checkbox").checked = pref.rss_auto_downloading_enabled; - $("downlock_repack_proper_episodes").checked = pref.rss_download_repack_proper_episodes; - $("rss_filter_textarea").value = pref.rss_smart_episode_filters; + document.getElementById("enable_fetching_rss_feeds_checkbox").checked = pref.rss_processing_enabled; + document.getElementById("feed_refresh_interval").value = pref.rss_refresh_interval; + document.getElementById("feedFetchDelay").value = pref.rss_fetch_delay; + document.getElementById("maximum_article_number").value = pref.rss_max_articles_per_feed; + document.getElementById("enable_auto_downloading_rss_torrents_checkbox").checked = pref.rss_auto_downloading_enabled; + document.getElementById("downlock_repack_proper_episodes").checked = pref.rss_download_repack_proper_episodes; + document.getElementById("rss_filter_textarea").value = pref.rss_smart_episode_filters; // WebUI tab // HTTP Server - $("webuiDomainTextarea").value = pref.web_ui_domain_list; - $("webuiAddressValue").value = pref.web_ui_address; - $("webuiPortValue").value = pref.web_ui_port; - $("webuiUpnpCheckbox").checked = pref.web_ui_upnp; - $("use_https_checkbox").checked = pref.use_https; - $("ssl_cert_text").value = pref.web_ui_https_cert_path; - $("ssl_key_text").value = pref.web_ui_https_key_path; + document.getElementById("webuiDomainTextarea").value = pref.web_ui_domain_list; + document.getElementById("webuiAddressValue").value = pref.web_ui_address; + document.getElementById("webuiPortValue").value = pref.web_ui_port; + document.getElementById("webuiUpnpCheckbox").checked = pref.web_ui_upnp; + document.getElementById("use_https_checkbox").checked = pref.use_https; + document.getElementById("ssl_cert_text").value = pref.web_ui_https_cert_path; + document.getElementById("ssl_key_text").value = pref.web_ui_https_key_path; updateHttpsSettings(); // Authentication - $("webui_username_text").value = pref.web_ui_username; - $("bypass_local_auth_checkbox").checked = pref.bypass_local_auth; - $("bypass_auth_subnet_whitelist_checkbox").checked = pref.bypass_auth_subnet_whitelist_enabled; - $("bypass_auth_subnet_whitelist_textarea").value = pref.bypass_auth_subnet_whitelist; + document.getElementById("webui_username_text").value = pref.web_ui_username; + document.getElementById("bypass_local_auth_checkbox").checked = pref.bypass_local_auth; + document.getElementById("bypass_auth_subnet_whitelist_checkbox").checked = pref.bypass_auth_subnet_whitelist_enabled; + document.getElementById("bypass_auth_subnet_whitelist_textarea").value = pref.bypass_auth_subnet_whitelist; updateBypasssAuthSettings(); - $("webUIMaxAuthFailCountInput").value = Number(pref.web_ui_max_auth_fail_count); - $("webUIBanDurationInput").value = Number(pref.web_ui_ban_duration); - $("webUISessionTimeoutInput").value = Number(pref.web_ui_session_timeout); + document.getElementById("webUIMaxAuthFailCountInput").value = Number(pref.web_ui_max_auth_fail_count); + document.getElementById("webUIBanDurationInput").value = Number(pref.web_ui_ban_duration); + document.getElementById("webUISessionTimeoutInput").value = Number(pref.web_ui_session_timeout); // Use alternative WebUI - $("use_alt_webui_checkbox").checked = pref.alternative_webui_enabled; - $("webui_files_location_textarea").value = pref.alternative_webui_path; + document.getElementById("use_alt_webui_checkbox").checked = pref.alternative_webui_enabled; + document.getElementById("webui_files_location_textarea").value = pref.alternative_webui_path; updateAlternativeWebUISettings(); // Security - $("clickjacking_protection_checkbox").checked = pref.web_ui_clickjacking_protection_enabled; - $("csrf_protection_checkbox").checked = pref.web_ui_csrf_protection_enabled; - $("secureCookieCheckbox").checked = pref.web_ui_secure_cookie_enabled; - $("host_header_validation_checkbox").checked = pref.web_ui_host_header_validation_enabled; + document.getElementById("clickjacking_protection_checkbox").checked = pref.web_ui_clickjacking_protection_enabled; + document.getElementById("csrf_protection_checkbox").checked = pref.web_ui_csrf_protection_enabled; + document.getElementById("secureCookieCheckbox").checked = pref.web_ui_secure_cookie_enabled; + document.getElementById("host_header_validation_checkbox").checked = pref.web_ui_host_header_validation_enabled; updateHostHeaderValidationSettings(); // Custom HTTP headers - $("webUIUseCustomHTTPHeadersCheckbox").checked = pref.web_ui_use_custom_http_headers_enabled; - $("webUICustomHTTPHeadersTextarea").value = pref.web_ui_custom_http_headers; + document.getElementById("webUIUseCustomHTTPHeadersCheckbox").checked = pref.web_ui_use_custom_http_headers_enabled; + document.getElementById("webUICustomHTTPHeadersTextarea").value = pref.web_ui_custom_http_headers; updateWebUICustomHTTPHeadersSettings(); // Reverse Proxy - $("webUIReverseProxySupportCheckbox").checked = pref.web_ui_reverse_proxy_enabled; - $("webUIReverseProxiesListTextarea").value = pref.web_ui_reverse_proxies_list; + document.getElementById("webUIReverseProxySupportCheckbox").checked = pref.web_ui_reverse_proxy_enabled; + document.getElementById("webUIReverseProxiesListTextarea").value = pref.web_ui_reverse_proxies_list; updateWebUIReverseProxySettings(); // Update my dynamic domain name - $("use_dyndns_checkbox").checked = pref.dyndns_enabled; - $("dyndns_select").value = pref.dyndns_service; - $("dyndns_domain_text").value = pref.dyndns_domain; - $("dyndns_username_text").value = pref.dyndns_username; - $("dyndns_password_text").value = pref.dyndns_password; + document.getElementById("use_dyndns_checkbox").checked = pref.dyndns_enabled; + document.getElementById("dyndns_select").value = pref.dyndns_service; + document.getElementById("dyndns_domain_text").value = pref.dyndns_domain; + document.getElementById("dyndns_username_text").value = pref.dyndns_username; + document.getElementById("dyndns_password_text").value = pref.dyndns_password; updateDynDnsSettings(); // Advanced settings // qBittorrent section - $("resumeDataStorageType").value = pref.resume_data_storage_type; - $("torrentContentRemoveOption").value = pref.torrent_content_remove_option; - $("memoryWorkingSetLimit").value = pref.memory_working_set_limit; + document.getElementById("resumeDataStorageType").value = pref.resume_data_storage_type; + document.getElementById("torrentContentRemoveOption").value = pref.torrent_content_remove_option; + document.getElementById("memoryWorkingSetLimit").value = pref.memory_working_set_limit; updateNetworkInterfaces(pref.current_network_interface, pref.current_interface_name); updateInterfaceAddresses(pref.current_network_interface, pref.current_interface_address); - $("saveResumeDataInterval").value = pref.save_resume_data_interval; - $("saveStatisticsInterval").value = pref.save_statistics_interval; - $("torrentFileSizeLimit").value = (pref.torrent_file_size_limit / 1024 / 1024); + document.getElementById("saveResumeDataInterval").value = pref.save_resume_data_interval; + document.getElementById("saveStatisticsInterval").value = pref.save_statistics_interval; + document.getElementById("torrentFileSizeLimit").value = (pref.torrent_file_size_limit / 1024 / 1024); document.getElementById("confirmTorrentRecheck").checked = pref.confirm_torrent_recheck; - $("recheckTorrentsOnCompletion").checked = pref.recheck_completed_torrents; - $("appInstanceName").value = pref.app_instance_name; - $("refreshInterval").value = pref.refresh_interval; - $("resolvePeerCountries").checked = pref.resolve_peer_countries; - $("reannounceWhenAddressChanged").checked = pref.reannounce_when_address_changed; - $("enableEmbeddedTracker").checked = pref.enable_embedded_tracker; - $("embeddedTrackerPort").value = pref.embedded_tracker_port; - $("embeddedTrackerPortForwarding").checked = pref.embedded_tracker_port_forwarding; - $("markOfTheWeb").checked = pref.mark_of_the_web; - $("ignoreSSLErrors").checked = pref.ignore_ssl_errors; - $("pythonExecutablePath").value = pref.python_executable_path; + document.getElementById("recheckTorrentsOnCompletion").checked = pref.recheck_completed_torrents; + document.getElementById("appInstanceName").value = pref.app_instance_name; + document.getElementById("refreshInterval").value = pref.refresh_interval; + document.getElementById("resolvePeerCountries").checked = pref.resolve_peer_countries; + document.getElementById("reannounceWhenAddressChanged").checked = pref.reannounce_when_address_changed; + document.getElementById("enableEmbeddedTracker").checked = pref.enable_embedded_tracker; + document.getElementById("embeddedTrackerPort").value = pref.embedded_tracker_port; + document.getElementById("embeddedTrackerPortForwarding").checked = pref.embedded_tracker_port_forwarding; + document.getElementById("markOfTheWeb").checked = pref.mark_of_the_web; + document.getElementById("ignoreSSLErrors").checked = pref.ignore_ssl_errors; + document.getElementById("pythonExecutablePath").value = pref.python_executable_path; // libtorrent section - $("bdecodeDepthLimit").value = pref.bdecode_depth_limit; - $("bdecodeTokenLimit").value = pref.bdecode_token_limit; - $("asyncIOThreads").value = pref.async_io_threads; - $("hashingThreads").value = pref.hashing_threads; - $("filePoolSize").value = pref.file_pool_size; - $("outstandMemoryWhenCheckingTorrents").value = pref.checking_memory_use; - $("diskCache").value = pref.disk_cache; - $("diskCacheExpiryInterval").value = pref.disk_cache_ttl; - $("diskQueueSize").value = (pref.disk_queue_size / 1024); - $("diskIOType").value = pref.disk_io_type; - $("diskIOReadMode").value = pref.disk_io_read_mode; - $("diskIOWriteMode").value = pref.disk_io_write_mode; - $("coalesceReadsAndWrites").checked = pref.enable_coalesce_read_write; - $("pieceExtentAffinity").checked = pref.enable_piece_extent_affinity; - $("sendUploadPieceSuggestions").checked = pref.enable_upload_suggestions; - $("sendBufferWatermark").value = pref.send_buffer_watermark; - $("sendBufferLowWatermark").value = pref.send_buffer_low_watermark; - $("sendBufferWatermarkFactor").value = pref.send_buffer_watermark_factor; - $("connectionSpeed").value = pref.connection_speed; - $("socketSendBufferSize").value = (pref.socket_send_buffer_size / 1024); - $("socketReceiveBufferSize").value = (pref.socket_receive_buffer_size / 1024); - $("socketBacklogSize").value = pref.socket_backlog_size; - $("outgoingPortsMin").value = pref.outgoing_ports_min; - $("outgoingPortsMax").value = pref.outgoing_ports_max; - $("UPnPLeaseDuration").value = pref.upnp_lease_duration; - $("peerToS").value = pref.peer_tos; - $("utpTCPMixedModeAlgorithm").value = pref.utp_tcp_mixed_mode; - $("hostnameCacheTTL").value = pref.hostname_cache_ttl; - $("IDNSupportCheckbox").checked = pref.idn_support_enabled; - $("allowMultipleConnectionsFromTheSameIPAddress").checked = pref.enable_multi_connections_from_same_ip; - $("validateHTTPSTrackerCertificate").checked = pref.validate_https_tracker_certificate; - $("mitigateSSRF").checked = pref.ssrf_mitigation; - $("blockPeersOnPrivilegedPorts").checked = pref.block_peers_on_privileged_ports; - $("uploadSlotsBehavior").value = pref.upload_slots_behavior; - $("uploadChokingAlgorithm").value = pref.upload_choking_algorithm; - $("announceAllTrackers").checked = pref.announce_to_all_trackers; - $("announceAllTiers").checked = pref.announce_to_all_tiers; - $("announceIP").value = pref.announce_ip; - $("announcePort").value = pref.announce_port; - $("maxConcurrentHTTPAnnounces").value = pref.max_concurrent_http_announces; - $("stopTrackerTimeout").value = pref.stop_tracker_timeout; - $("peerTurnover").value = pref.peer_turnover; - $("peerTurnoverCutoff").value = pref.peer_turnover_cutoff; - $("peerTurnoverInterval").value = pref.peer_turnover_interval; - $("requestQueueSize").value = pref.request_queue_size; - $("dhtBootstrapNodes").value = pref.dht_bootstrap_nodes; - $("i2pInboundQuantity").value = pref.i2p_inbound_quantity; - $("i2pOutboundQuantity").value = pref.i2p_outbound_quantity; - $("i2pInboundLength").value = pref.i2p_inbound_length; - $("i2pOutboundLength").value = pref.i2p_outbound_length; + document.getElementById("bdecodeDepthLimit").value = pref.bdecode_depth_limit; + document.getElementById("bdecodeTokenLimit").value = pref.bdecode_token_limit; + document.getElementById("asyncIOThreads").value = pref.async_io_threads; + document.getElementById("hashingThreads").value = pref.hashing_threads; + document.getElementById("filePoolSize").value = pref.file_pool_size; + document.getElementById("outstandMemoryWhenCheckingTorrents").value = pref.checking_memory_use; + document.getElementById("diskCache").value = pref.disk_cache; + document.getElementById("diskCacheExpiryInterval").value = pref.disk_cache_ttl; + document.getElementById("diskQueueSize").value = (pref.disk_queue_size / 1024); + document.getElementById("diskIOType").value = pref.disk_io_type; + document.getElementById("diskIOReadMode").value = pref.disk_io_read_mode; + document.getElementById("diskIOWriteMode").value = pref.disk_io_write_mode; + document.getElementById("coalesceReadsAndWrites").checked = pref.enable_coalesce_read_write; + document.getElementById("pieceExtentAffinity").checked = pref.enable_piece_extent_affinity; + document.getElementById("sendUploadPieceSuggestions").checked = pref.enable_upload_suggestions; + document.getElementById("sendBufferWatermark").value = pref.send_buffer_watermark; + document.getElementById("sendBufferLowWatermark").value = pref.send_buffer_low_watermark; + document.getElementById("sendBufferWatermarkFactor").value = pref.send_buffer_watermark_factor; + document.getElementById("connectionSpeed").value = pref.connection_speed; + document.getElementById("socketSendBufferSize").value = (pref.socket_send_buffer_size / 1024); + document.getElementById("socketReceiveBufferSize").value = (pref.socket_receive_buffer_size / 1024); + document.getElementById("socketBacklogSize").value = pref.socket_backlog_size; + document.getElementById("outgoingPortsMin").value = pref.outgoing_ports_min; + document.getElementById("outgoingPortsMax").value = pref.outgoing_ports_max; + document.getElementById("UPnPLeaseDuration").value = pref.upnp_lease_duration; + document.getElementById("peerToS").value = pref.peer_tos; + document.getElementById("utpTCPMixedModeAlgorithm").value = pref.utp_tcp_mixed_mode; + document.getElementById("hostnameCacheTTL").value = pref.hostname_cache_ttl; + document.getElementById("IDNSupportCheckbox").checked = pref.idn_support_enabled; + document.getElementById("allowMultipleConnectionsFromTheSameIPAddress").checked = pref.enable_multi_connections_from_same_ip; + document.getElementById("validateHTTPSTrackerCertificate").checked = pref.validate_https_tracker_certificate; + document.getElementById("mitigateSSRF").checked = pref.ssrf_mitigation; + document.getElementById("blockPeersOnPrivilegedPorts").checked = pref.block_peers_on_privileged_ports; + document.getElementById("uploadSlotsBehavior").value = pref.upload_slots_behavior; + document.getElementById("uploadChokingAlgorithm").value = pref.upload_choking_algorithm; + document.getElementById("announceAllTrackers").checked = pref.announce_to_all_trackers; + document.getElementById("announceAllTiers").checked = pref.announce_to_all_tiers; + document.getElementById("announceIP").value = pref.announce_ip; + document.getElementById("announcePort").value = pref.announce_port; + document.getElementById("maxConcurrentHTTPAnnounces").value = pref.max_concurrent_http_announces; + document.getElementById("stopTrackerTimeout").value = pref.stop_tracker_timeout; + document.getElementById("peerTurnover").value = pref.peer_turnover; + document.getElementById("peerTurnoverCutoff").value = pref.peer_turnover_cutoff; + document.getElementById("peerTurnoverInterval").value = pref.peer_turnover_interval; + document.getElementById("requestQueueSize").value = pref.request_queue_size; + document.getElementById("dhtBootstrapNodes").value = pref.dht_bootstrap_nodes; + document.getElementById("i2pInboundQuantity").value = pref.i2p_inbound_quantity; + document.getElementById("i2pOutboundQuantity").value = pref.i2p_outbound_quantity; + document.getElementById("i2pInboundLength").value = pref.i2p_inbound_length; + document.getElementById("i2pOutboundLength").value = pref.i2p_outbound_length; } }); }; @@ -2647,7 +2647,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD // Behavior tab // Language - settings["locale"] = $("locale_select").value; + settings["locale"] = document.getElementById("locale_select").value; const colorScheme = Number(document.getElementById("colorSchemeSelect").value); if (colorScheme === 0) LocalPreferences.remove("color_scheme"); @@ -2655,52 +2655,52 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD LocalPreferences.set("color_scheme", "light"); else LocalPreferences.set("color_scheme", "dark"); - settings["status_bar_external_ip"] = $("statusBarExternalIP").checked; - settings["performance_warning"] = $("performanceWarning").checked; + settings["status_bar_external_ip"] = document.getElementById("statusBarExternalIP").checked; + settings["performance_warning"] = document.getElementById("performanceWarning").checked; LocalPreferences.set("full_url_tracker_column", document.getElementById("displayFullURLTrackerColumn").checked.toString()); LocalPreferences.set("use_virtual_list", document.getElementById("useVirtualList").checked.toString()); LocalPreferences.set("hide_zero_status_filters", document.getElementById("hideZeroFiltersCheckbox").checked.toString()); - LocalPreferences.set("dblclick_download", $("dblclickDownloadSelect").value); - LocalPreferences.set("dblclick_complete", $("dblclickCompleteSelect").value); + LocalPreferences.set("dblclick_download", document.getElementById("dblclickDownloadSelect").value); + LocalPreferences.set("dblclick_complete", document.getElementById("dblclickCompleteSelect").value); settings["confirm_torrent_deletion"] = document.getElementById("confirmTorrentDeletion").checked; LocalPreferences.set("use_alt_row_colors", document.getElementById("useAltRowColorsInput").checked.toString()); - settings["file_log_enabled"] = $("filelog_checkbox").checked; - settings["file_log_path"] = $("filelog_save_path_input").value; - settings["file_log_backup_enabled"] = $("filelog_backup_checkbox").checked; - settings["file_log_max_size"] = Number($("filelog_max_size_input").value); - settings["file_log_delete_old"] = $("filelog_delete_old_checkbox").checked; - settings["file_log_age"] = Number($("filelog_age_input").value); - settings["file_log_age_type"] = Number($("filelog_age_type_select").value); + settings["file_log_enabled"] = document.getElementById("filelog_checkbox").checked; + settings["file_log_path"] = document.getElementById("filelog_save_path_input").value; + settings["file_log_backup_enabled"] = document.getElementById("filelog_backup_checkbox").checked; + settings["file_log_max_size"] = Number(document.getElementById("filelog_max_size_input").value); + settings["file_log_delete_old"] = document.getElementById("filelog_delete_old_checkbox").checked; + settings["file_log_age"] = Number(document.getElementById("filelog_age_input").value); + settings["file_log_age_type"] = Number(document.getElementById("filelog_age_type_select").value); // Downloads tab // When adding a torrent - settings["torrent_content_layout"] = $("contentlayout_select").getSelected()[0].value; - settings["add_to_top_of_queue"] = $("addToTopOfQueueCheckbox").checked; - settings["add_stopped_enabled"] = $("dontstartdownloads_checkbox").checked; - settings["torrent_stop_condition"] = $("stopConditionSelect").getSelected()[0].value; + settings["torrent_content_layout"] = document.getElementById("contentlayout_select").getSelected()[0].value; + settings["add_to_top_of_queue"] = document.getElementById("addToTopOfQueueCheckbox").checked; + settings["add_stopped_enabled"] = document.getElementById("dontstartdownloads_checkbox").checked; + settings["torrent_stop_condition"] = document.getElementById("stopConditionSelect").getSelected()[0].value; settings["merge_trackers"] = document.getElementById("mergeTrackersInput").checked; - settings["auto_delete_mode"] = Number($("deletetorrentfileafter_checkbox").checked); + settings["auto_delete_mode"] = Number(document.getElementById("deletetorrentfileafter_checkbox").checked); - settings["preallocate_all"] = $("preallocateall_checkbox").checked; - settings["incomplete_files_ext"] = $("appendext_checkbox").checked; - settings["use_unwanted_folder"] = $("unwantedfolder_checkbox").checked; + settings["preallocate_all"] = document.getElementById("preallocateall_checkbox").checked; + settings["incomplete_files_ext"] = document.getElementById("appendext_checkbox").checked; + settings["use_unwanted_folder"] = document.getElementById("unwantedfolder_checkbox").checked; // Saving Management - settings["auto_tmm_enabled"] = ($("default_tmm_combobox").value === "true"); - settings["torrent_changed_tmm_enabled"] = ($("torrent_changed_tmm_combobox").value === "true"); - settings["save_path_changed_tmm_enabled"] = ($("save_path_changed_tmm_combobox").value === "true"); - settings["category_changed_tmm_enabled"] = ($("category_changed_tmm_combobox").value === "true"); - settings["use_subcategories"] = $("use_subcategories_checkbox").checked; + settings["auto_tmm_enabled"] = (document.getElementById("default_tmm_combobox").value === "true"); + settings["torrent_changed_tmm_enabled"] = (document.getElementById("torrent_changed_tmm_combobox").value === "true"); + settings["save_path_changed_tmm_enabled"] = (document.getElementById("save_path_changed_tmm_combobox").value === "true"); + settings["category_changed_tmm_enabled"] = (document.getElementById("category_changed_tmm_combobox").value === "true"); + settings["use_subcategories"] = document.getElementById("use_subcategories_checkbox").checked; settings["use_category_paths_in_manual_mode"] = document.getElementById("categoryPathsManualModeCheckbox").checked; - settings["save_path"] = $("savepath_text").value; - settings["temp_path_enabled"] = $("temppath_checkbox").checked; - settings["temp_path"] = $("temppath_text").value; - if ($("exportdir_checkbox").checked) - settings["export_dir"] = $("exportdir_text").value; + settings["save_path"] = document.getElementById("savepath_text").value; + settings["temp_path_enabled"] = document.getElementById("temppath_checkbox").checked; + settings["temp_path"] = document.getElementById("temppath_text").value; + if (document.getElementById("exportdir_checkbox").checked) + settings["export_dir"] = document.getElementById("exportdir_text").value; else settings["export_dir"] = ""; - if ($("exportdirfin_checkbox").checked) - settings["export_dir_fin"] = $("exportdirfin_text").value; + if (document.getElementById("exportdirfin_checkbox").checked) + settings["export_dir_fin"] = document.getElementById("exportdirfin_text").value; else settings["export_dir_fin"] = ""; @@ -2708,40 +2708,40 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD settings["scan_dirs"] = getWatchedFolders(); // Excluded file names - settings["excluded_file_names_enabled"] = $("excludedFileNamesCheckbox").checked; - settings["excluded_file_names"] = $("excludedFileNamesTextarea").value; + settings["excluded_file_names_enabled"] = document.getElementById("excludedFileNamesCheckbox").checked; + settings["excluded_file_names"] = document.getElementById("excludedFileNamesTextarea").value; // Email notification upon download completion - settings["mail_notification_enabled"] = $("mail_notification_checkbox").checked; - settings["mail_notification_sender"] = $("src_email_txt").value; - settings["mail_notification_email"] = $("dest_email_txt").value; - settings["mail_notification_smtp"] = $("smtp_server_txt").value; - settings["mail_notification_ssl_enabled"] = $("mail_ssl_checkbox").checked; - settings["mail_notification_auth_enabled"] = $("mail_auth_checkbox").checked; - settings["mail_notification_username"] = $("mail_username_text").value; - settings["mail_notification_password"] = $("mail_password_text").value; + settings["mail_notification_enabled"] = document.getElementById("mail_notification_checkbox").checked; + settings["mail_notification_sender"] = document.getElementById("src_email_txt").value; + settings["mail_notification_email"] = document.getElementById("dest_email_txt").value; + settings["mail_notification_smtp"] = document.getElementById("smtp_server_txt").value; + settings["mail_notification_ssl_enabled"] = document.getElementById("mail_ssl_checkbox").checked; + settings["mail_notification_auth_enabled"] = document.getElementById("mail_auth_checkbox").checked; + settings["mail_notification_username"] = document.getElementById("mail_username_text").value; + settings["mail_notification_password"] = document.getElementById("mail_password_text").value; // Run an external program on torrent added - settings["autorun_on_torrent_added_enabled"] = $("autorunOnTorrentAddedCheckbox").checked; - settings["autorun_on_torrent_added_program"] = $("autorunOnTorrentAddedProgram").value; + settings["autorun_on_torrent_added_enabled"] = document.getElementById("autorunOnTorrentAddedCheckbox").checked; + settings["autorun_on_torrent_added_program"] = document.getElementById("autorunOnTorrentAddedProgram").value; // Run an external program on torrent finished - settings["autorun_enabled"] = $("autorun_checkbox").checked; - settings["autorun_program"] = $("autorunProg_txt").value; + settings["autorun_enabled"] = document.getElementById("autorun_checkbox").checked; + settings["autorun_program"] = document.getElementById("autorunProg_txt").value; // Connection tab // Listening Port - const listenPort = Number($("portValue").value); + const listenPort = Number(document.getElementById("portValue").value); if (Number.isNaN(listenPort) || (listenPort < 0) || (listenPort > 65535)) { alert("QBT_TR(The port used for incoming connections must be between 0 and 65535.)QBT_TR[CONTEXT=HttpServer]"); return; } settings["listen_port"] = listenPort; - settings["upnp"] = $("upnpCheckbox").checked; + settings["upnp"] = document.getElementById("upnpCheckbox").checked; // Connections Limits let maxConnec = -1; - if ($("maxConnectionsCheckbox").checked) { - maxConnec = Number($("maxConnectionsValue").value); + if (document.getElementById("maxConnectionsCheckbox").checked) { + maxConnec = Number(document.getElementById("maxConnectionsValue").value); if (Number.isNaN(maxConnec) || (maxConnec <= 0)) { alert("QBT_TR(Maximum number of connections limit must be greater than 0 or disabled.)QBT_TR[CONTEXT=HttpServer]"); return; @@ -2750,8 +2750,8 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD settings["max_connec"] = maxConnec; let maxConnecPerTorrent = -1; - if ($("maxConnectionsPerTorrentCheckbox").checked) { - maxConnecPerTorrent = Number($("maxConnectionsPerTorrentValue").value); + if (document.getElementById("maxConnectionsPerTorrentCheckbox").checked) { + maxConnecPerTorrent = Number(document.getElementById("maxConnectionsPerTorrentValue").value); if (Number.isNaN(maxConnecPerTorrent) || (maxConnecPerTorrent <= 0)) { alert("QBT_TR(Maximum number of connections per torrent limit must be greater than 0 or disabled.)QBT_TR[CONTEXT=HttpServer]"); return; @@ -2760,8 +2760,8 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD settings["max_connec_per_torrent"] = maxConnecPerTorrent; let maxUploads = -1; - if ($("maxUploadsCheckbox").checked) { - maxUploads = Number($("max_uploads_value").value); + if (document.getElementById("maxUploadsCheckbox").checked) { + maxUploads = Number(document.getElementById("max_uploads_value").value); if (Number.isNaN(maxUploads) || (maxUploads <= 0)) { alert("QBT_TR(Global number of upload slots limit must be greater than 0 or disabled.)QBT_TR[CONTEXT=HttpServer]"); return; @@ -2770,8 +2770,8 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD settings["max_uploads"] = maxUploads; let maxUploadsPerTorrent = -1; - if ($("maxUploadsPerTorrentCheckbox").checked) { - maxUploadsPerTorrent = Number($("maxUploadsPerTorrentValue").value); + if (document.getElementById("maxUploadsPerTorrentCheckbox").checked) { + maxUploadsPerTorrent = Number(document.getElementById("maxUploadsPerTorrentValue").value); if (Number.isNaN(maxUploadsPerTorrent) || (maxUploadsPerTorrent <= 0)) { alert("QBT_TR(Maximum number of upload slots per torrent limit must be greater than 0 or disabled.)QBT_TR[CONTEXT=HttpServer]"); return; @@ -2780,40 +2780,40 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD settings["max_uploads_per_torrent"] = maxUploadsPerTorrent; // I2P - settings["i2p_enabled"] = $("i2pEnabledCheckbox").checked; - settings["i2p_address"] = $("i2pAddress").value; - settings["i2p_port"] = Number($("i2pPort").value); - settings["i2p_mixed_mode"] = $("i2pMixedMode").checked; + settings["i2p_enabled"] = document.getElementById("i2pEnabledCheckbox").checked; + settings["i2p_address"] = document.getElementById("i2pAddress").value; + settings["i2p_port"] = Number(document.getElementById("i2pPort").value); + settings["i2p_mixed_mode"] = document.getElementById("i2pMixedMode").checked; // Proxy Server - settings["proxy_type"] = $("peer_proxy_type_select").value; - settings["proxy_ip"] = $("peer_proxy_host_text").value; - settings["proxy_port"] = Number($("peer_proxy_port_value").value); - settings["proxy_auth_enabled"] = $("peer_proxy_auth_checkbox").checked; - settings["proxy_username"] = $("peer_proxy_username_text").value; - settings["proxy_password"] = $("peer_proxy_password_text").value; - settings["proxy_hostname_lookup"] = $("proxyHostnameLookupCheckbox").checked; - settings["proxy_bittorrent"] = $("proxy_bittorrent_checkbox").checked; - settings["proxy_peer_connections"] = $("use_peer_proxy_checkbox").checked; - settings["proxy_rss"] = $("proxy_rss_checkbox").checked; - settings["proxy_misc"] = $("proxy_misc_checkbox").checked; + settings["proxy_type"] = document.getElementById("peer_proxy_type_select").value; + settings["proxy_ip"] = document.getElementById("peer_proxy_host_text").value; + settings["proxy_port"] = Number(document.getElementById("peer_proxy_port_value").value); + settings["proxy_auth_enabled"] = document.getElementById("peer_proxy_auth_checkbox").checked; + settings["proxy_username"] = document.getElementById("peer_proxy_username_text").value; + settings["proxy_password"] = document.getElementById("peer_proxy_password_text").value; + settings["proxy_hostname_lookup"] = document.getElementById("proxyHostnameLookupCheckbox").checked; + settings["proxy_bittorrent"] = document.getElementById("proxy_bittorrent_checkbox").checked; + settings["proxy_peer_connections"] = document.getElementById("use_peer_proxy_checkbox").checked; + settings["proxy_rss"] = document.getElementById("proxy_rss_checkbox").checked; + settings["proxy_misc"] = document.getElementById("proxy_misc_checkbox").checked; // IP Filtering - settings["ip_filter_enabled"] = $("ipfilter_text_checkbox").checked; - settings["ip_filter_path"] = $("ipfilter_text").value; - settings["ip_filter_trackers"] = $("ipfilter_trackers_checkbox").checked; - settings["banned_IPs"] = $("banned_IPs_textarea").value; + settings["ip_filter_enabled"] = document.getElementById("ipfilter_text_checkbox").checked; + settings["ip_filter_path"] = document.getElementById("ipfilter_text").value; + settings["ip_filter_trackers"] = document.getElementById("ipfilter_trackers_checkbox").checked; + settings["banned_IPs"] = document.getElementById("banned_IPs_textarea").value; // Speed tab // Global Rate Limits - const upLimit = Number($("upLimitValue").value) * 1024; + const upLimit = Number(document.getElementById("upLimitValue").value) * 1024; if (Number.isNaN(upLimit) || (upLimit < 0)) { alert("QBT_TR(Global upload rate limit must be greater than 0 or disabled.)QBT_TR[CONTEXT=HttpServer]"); return; } settings["up_limit"] = upLimit; - const dlLimit = Number($("dlLimitValue").value) * 1024; + const dlLimit = Number(document.getElementById("dlLimitValue").value) * 1024; if (Number.isNaN(dlLimit) || (dlLimit < 0)) { alert("QBT_TR(Global download rate limit must be greater than 0 or disabled.)QBT_TR[CONTEXT=HttpServer]"); return; @@ -2821,86 +2821,86 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD settings["dl_limit"] = dlLimit; // Alternative Global Rate Limits - const altUpLimit = Number($("altUpLimitValue").value) * 1024; + const altUpLimit = Number(document.getElementById("altUpLimitValue").value) * 1024; if (Number.isNaN(altUpLimit) || (altUpLimit < 0)) { alert("QBT_TR(Alternative upload rate limit must be greater than 0 or disabled.)QBT_TR[CONTEXT=HttpServer]"); return; } settings["alt_up_limit"] = altUpLimit; - const altDlLimit = Number($("altDlLimitValue").value) * 1024; + const altDlLimit = Number(document.getElementById("altDlLimitValue").value) * 1024; if (Number.isNaN(altDlLimit) || (altDlLimit < 0)) { alert("QBT_TR(Alternative download rate limit must be greater than 0 or disabled.)QBT_TR[CONTEXT=HttpServer]"); return; } settings["alt_dl_limit"] = altDlLimit; - settings["bittorrent_protocol"] = Number($("enable_protocol_combobox").value); - settings["limit_utp_rate"] = $("limit_utp_rate_checkbox").checked; - settings["limit_tcp_overhead"] = $("limit_tcp_overhead_checkbox").checked; - settings["limit_lan_peers"] = $("limit_lan_peers_checkbox").checked; + settings["bittorrent_protocol"] = Number(document.getElementById("enable_protocol_combobox").value); + settings["limit_utp_rate"] = document.getElementById("limit_utp_rate_checkbox").checked; + settings["limit_tcp_overhead"] = document.getElementById("limit_tcp_overhead_checkbox").checked; + settings["limit_lan_peers"] = document.getElementById("limit_lan_peers_checkbox").checked; // Scheduler - const schedulingEnabled = $("limitSchedulingCheckbox").checked; + const schedulingEnabled = document.getElementById("limitSchedulingCheckbox").checked; settings["scheduler_enabled"] = schedulingEnabled; if (schedulingEnabled) { - settings["schedule_from_hour"] = Number($("schedule_from_hour").value); - settings["schedule_from_min"] = Number($("schedule_from_min").value); - settings["schedule_to_hour"] = Number($("schedule_to_hour").value); - settings["schedule_to_min"] = Number($("schedule_to_min").value); - settings["scheduler_days"] = Number($("schedule_freq_select").value); + settings["schedule_from_hour"] = Number(document.getElementById("schedule_from_hour").value); + settings["schedule_from_min"] = Number(document.getElementById("schedule_from_min").value); + settings["schedule_to_hour"] = Number(document.getElementById("schedule_to_hour").value); + settings["schedule_to_min"] = Number(document.getElementById("schedule_to_min").value); + settings["scheduler_days"] = Number(document.getElementById("schedule_freq_select").value); } // Bittorrent tab // Privacy - settings["dht"] = $("dht_checkbox").checked; - settings["pex"] = $("pex_checkbox").checked; - settings["lsd"] = $("lsd_checkbox").checked; - settings["encryption"] = Number($("encryption_select").getSelected()[0].value); - settings["anonymous_mode"] = $("anonymous_mode_checkbox").checked; + settings["dht"] = document.getElementById("dht_checkbox").checked; + settings["pex"] = document.getElementById("pex_checkbox").checked; + settings["lsd"] = document.getElementById("lsd_checkbox").checked; + settings["encryption"] = Number(document.getElementById("encryption_select").getSelected()[0].value); + settings["anonymous_mode"] = document.getElementById("anonymous_mode_checkbox").checked; - settings["max_active_checking_torrents"] = Number($("maxActiveCheckingTorrents").value); + settings["max_active_checking_torrents"] = Number(document.getElementById("maxActiveCheckingTorrents").value); // Torrent Queueing - settings["queueing_enabled"] = $("queueingCheckbox").checked; - if ($("queueingCheckbox").checked) { - const maxActiveDownloads = Number($("maxActiveDlValue").value); + settings["queueing_enabled"] = document.getElementById("queueingCheckbox").checked; + if (document.getElementById("queueingCheckbox").checked) { + const maxActiveDownloads = Number(document.getElementById("maxActiveDlValue").value); if (Number.isNaN(maxActiveDownloads) || (maxActiveDownloads < -1)) { alert("QBT_TR(Maximum active downloads must be greater than -1.)QBT_TR[CONTEXT=HttpServer]"); return; } settings["max_active_downloads"] = maxActiveDownloads; - const maxActiveUploads = Number($("maxActiveUpValue").value); + const maxActiveUploads = Number(document.getElementById("maxActiveUpValue").value); if (Number.isNaN(maxActiveUploads) || (maxActiveUploads < -1)) { alert("QBT_TR(Maximum active uploads must be greater than -1.)QBT_TR[CONTEXT=HttpServer]"); return; } settings["max_active_uploads"] = maxActiveUploads; - const maxActiveTorrents = Number($("maxActiveToValue").value); + const maxActiveTorrents = Number(document.getElementById("maxActiveToValue").value); if (Number.isNaN(maxActiveTorrents) || (maxActiveTorrents < -1)) { alert("QBT_TR(Maximum active torrents must be greater than -1.)QBT_TR[CONTEXT=HttpServer]"); return; } settings["max_active_torrents"] = maxActiveTorrents; - settings["dont_count_slow_torrents"] = $("dontCountSlowTorrentsCheckbox").checked; + settings["dont_count_slow_torrents"] = document.getElementById("dontCountSlowTorrentsCheckbox").checked; - const dlRateThreshold = Number($("dlRateThresholdValue").value); + const dlRateThreshold = Number(document.getElementById("dlRateThresholdValue").value); if (Number.isNaN(dlRateThreshold) || (dlRateThreshold < 1)) { alert("QBT_TR(Download rate threshold must be greater than 0.)QBT_TR[CONTEXT=HttpServer]"); return; } settings["slow_torrent_dl_rate_threshold"] = dlRateThreshold; - const ulRateThreshold = Number($("ulRateThresholdValue").value); + const ulRateThreshold = Number(document.getElementById("ulRateThresholdValue").value); if (Number.isNaN(ulRateThreshold) || (ulRateThreshold < 1)) { alert("QBT_TR(Upload rate threshold must be greater than 0.)QBT_TR[CONTEXT=HttpServer]"); return; } settings["slow_torrent_ul_rate_threshold"] = ulRateThreshold; - const torrentInactiveTimer = Number($("torrentInactiveTimerValue").value); + const torrentInactiveTimer = Number(document.getElementById("torrentInactiveTimerValue").value); if (Number.isNaN(torrentInactiveTimer) || (torrentInactiveTimer < 1)) { alert("QBT_TR(Torrent inactivity timer must be greater than 0.)QBT_TR[CONTEXT=HttpServer]"); return; @@ -2910,78 +2910,78 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD // Share Ratio Limiting let maxRatio = -1; - if ($("maxRatioCheckbox").checked) { - maxRatio = Number($("maxRatioValue").value); - if (isNaN(maxRatio) || (maxRatio < 0) || (maxRatio > 9998)) { - alert("QBT_TR(Share ratio limit must be between 0 and 9998.)QBT_TR[CONTEXT=HttpServer]"); + if (document.getElementById("maxRatioCheckbox").checked) { + maxRatio = Number(document.getElementById("maxRatioValue").value); + if (isNaN(maxRatio) || (maxRatio < 0)) { + alert("QBT_TR(Share ratio limit must not have a negative value.)QBT_TR[CONTEXT=HttpServer]"); return; } } - settings["max_ratio_enabled"] = $("maxRatioCheckbox").checked; + settings["max_ratio_enabled"] = document.getElementById("maxRatioCheckbox").checked; settings["max_ratio"] = maxRatio; let maxSeedingTime = -1; - if ($("maxSeedingTimeCheckbox").checked) { - maxSeedingTime = Number($("maxSeedingTimeValue").value); - if (Number.isNaN(maxSeedingTime) || (maxSeedingTime < 0) || (maxSeedingTime > 525600)) { - alert("QBT_TR(Seeding time limit must be between 0 and 525600 minutes.)QBT_TR[CONTEXT=HttpServer]"); + if (document.getElementById("maxSeedingTimeCheckbox").checked) { + maxSeedingTime = Number(document.getElementById("maxSeedingTimeValue").value); + if (Number.isNaN(maxSeedingTime) || (maxSeedingTime < 0)) { + alert("QBT_TR(Seeding time limit must not have a negative value.)QBT_TR[CONTEXT=HttpServer]"); return; } } - settings["max_seeding_time_enabled"] = $("maxSeedingTimeCheckbox").checked; + settings["max_seeding_time_enabled"] = document.getElementById("maxSeedingTimeCheckbox").checked; settings["max_seeding_time"] = maxSeedingTime; let maxInactiveSeedingTime = -1; - if ($("maxInactiveSeedingTimeCheckbox").checked) { - maxInactiveSeedingTime = Number($("maxInactiveSeedingTimeValue").value); - if (Number.isNaN(maxInactiveSeedingTime) || (maxInactiveSeedingTime < 0) || (maxInactiveSeedingTime > 525600)) { - alert("QBT_TR(Seeding time limit must be between 0 and 525600 minutes.)QBT_TR[CONTEXT=HttpServer]"); + if (document.getElementById("maxInactiveSeedingTimeCheckbox").checked) { + maxInactiveSeedingTime = Number(document.getElementById("maxInactiveSeedingTimeValue").value); + if (Number.isNaN(maxInactiveSeedingTime) || (maxInactiveSeedingTime < 0)) { + alert("QBT_TR(Seeding time limit must not have a negative value.)QBT_TR[CONTEXT=HttpServer]"); return; } } - settings["max_inactive_seeding_time_enabled"] = $("maxInactiveSeedingTimeCheckbox").checked; + settings["max_inactive_seeding_time_enabled"] = document.getElementById("maxInactiveSeedingTimeCheckbox").checked; settings["max_inactive_seeding_time"] = maxInactiveSeedingTime; - settings["max_ratio_act"] = Number($("maxRatioSelect").value); + settings["max_ratio_act"] = Number(document.getElementById("maxRatioSelect").value); // Add trackers - settings["add_trackers_enabled"] = $("add_trackers_checkbox").checked; - settings["add_trackers"] = $("add_trackers_textarea").value; - settings["add_trackers_from_url_enabled"] = $("addTrackersFromURLCheckbox").checked; - settings["add_trackers_url"] = $("addTrackersURL").value; + settings["add_trackers_enabled"] = document.getElementById("add_trackers_checkbox").checked; + settings["add_trackers"] = document.getElementById("add_trackers_textarea").value; + settings["add_trackers_from_url_enabled"] = document.getElementById("addTrackersFromURLCheckbox").checked; + settings["add_trackers_url"] = document.getElementById("addTrackersURL").value; // RSS Tab - settings["rss_processing_enabled"] = $("enable_fetching_rss_feeds_checkbox").checked; - settings["rss_refresh_interval"] = Number($("feed_refresh_interval").value); - settings["rss_fetch_delay"] = Number($("feedFetchDelay").value); - settings["rss_max_articles_per_feed"] = Number($("maximum_article_number").value); - settings["rss_auto_downloading_enabled"] = $("enable_auto_downloading_rss_torrents_checkbox").checked; - settings["rss_download_repack_proper_episodes"] = $("downlock_repack_proper_episodes").checked; - settings["rss_smart_episode_filters"] = $("rss_filter_textarea").value; + settings["rss_processing_enabled"] = document.getElementById("enable_fetching_rss_feeds_checkbox").checked; + settings["rss_refresh_interval"] = Number(document.getElementById("feed_refresh_interval").value); + settings["rss_fetch_delay"] = Number(document.getElementById("feedFetchDelay").value); + settings["rss_max_articles_per_feed"] = Number(document.getElementById("maximum_article_number").value); + settings["rss_auto_downloading_enabled"] = document.getElementById("enable_auto_downloading_rss_torrents_checkbox").checked; + settings["rss_download_repack_proper_episodes"] = document.getElementById("downlock_repack_proper_episodes").checked; + settings["rss_smart_episode_filters"] = document.getElementById("rss_filter_textarea").value; // WebUI tab // HTTP Server - settings["web_ui_domain_list"] = $("webuiDomainTextarea").value; - const webUIAddress = $("webuiAddressValue").value.toString(); - const webUIPort = Number($("webuiPortValue").value); + settings["web_ui_domain_list"] = document.getElementById("webuiDomainTextarea").value; + const webUIAddress = document.getElementById("webuiAddressValue").value.toString(); + const webUIPort = Number(document.getElementById("webuiPortValue").value); if (Number.isNaN(webUIPort) || (webUIPort < 1) || (webUIPort > 65535)) { alert("QBT_TR(The port used for the WebUI must be between 1 and 65535.)QBT_TR[CONTEXT=HttpServer]"); return; } settings["web_ui_address"] = webUIAddress; settings["web_ui_port"] = webUIPort; - settings["web_ui_upnp"] = $("webuiUpnpCheckbox").checked; + settings["web_ui_upnp"] = document.getElementById("webuiUpnpCheckbox").checked; - const useHTTPS = $("use_https_checkbox").checked; + const useHTTPS = document.getElementById("use_https_checkbox").checked; settings["use_https"] = useHTTPS; - const httpsCertificate = $("ssl_cert_text").value; + const httpsCertificate = document.getElementById("ssl_cert_text").value; settings["web_ui_https_cert_path"] = httpsCertificate; if (useHTTPS && (httpsCertificate.length === 0)) { alert("QBT_TR(HTTPS certificate should not be empty)QBT_TR[CONTEXT=OptionsDialog]"); return; } - const httpsKey = $("ssl_key_text").value; + const httpsKey = document.getElementById("ssl_key_text").value; settings["web_ui_https_key_path"] = httpsKey; if (useHTTPS && (httpsKey.length === 0)) { alert("QBT_TR(HTTPS key should not be empty)QBT_TR[CONTEXT=OptionsDialog]"); @@ -2989,12 +2989,12 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD } // Authentication - const web_ui_username = $("webui_username_text").value; + const web_ui_username = document.getElementById("webui_username_text").value; if (web_ui_username.length < 3) { alert("QBT_TR(The WebUI username must be at least 3 characters long.)QBT_TR[CONTEXT=OptionsDialog]"); return; } - const web_ui_password = $("webui_password_text").value; + const web_ui_password = document.getElementById("webui_password_text").value; if ((0 < web_ui_password.length) && (web_ui_password.length < 6)) { alert("QBT_TR(The WebUI password must be at least 6 characters long.)QBT_TR[CONTEXT=OptionsDialog]"); return; @@ -3003,16 +3003,16 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD settings["web_ui_username"] = web_ui_username; if (web_ui_password.length > 0) settings["web_ui_password"] = web_ui_password; - settings["bypass_local_auth"] = $("bypass_local_auth_checkbox").checked; - settings["bypass_auth_subnet_whitelist_enabled"] = $("bypass_auth_subnet_whitelist_checkbox").checked; - settings["bypass_auth_subnet_whitelist"] = $("bypass_auth_subnet_whitelist_textarea").value; - settings["web_ui_max_auth_fail_count"] = Number($("webUIMaxAuthFailCountInput").value); - settings["web_ui_ban_duration"] = Number($("webUIBanDurationInput").value); - settings["web_ui_session_timeout"] = Number($("webUISessionTimeoutInput").value); + settings["bypass_local_auth"] = document.getElementById("bypass_local_auth_checkbox").checked; + settings["bypass_auth_subnet_whitelist_enabled"] = document.getElementById("bypass_auth_subnet_whitelist_checkbox").checked; + settings["bypass_auth_subnet_whitelist"] = document.getElementById("bypass_auth_subnet_whitelist_textarea").value; + settings["web_ui_max_auth_fail_count"] = Number(document.getElementById("webUIMaxAuthFailCountInput").value); + settings["web_ui_ban_duration"] = Number(document.getElementById("webUIBanDurationInput").value); + settings["web_ui_session_timeout"] = Number(document.getElementById("webUISessionTimeoutInput").value); // Use alternative WebUI - const alternative_webui_enabled = $("use_alt_webui_checkbox").checked; - const webui_files_location_textarea = $("webui_files_location_textarea").value; + const alternative_webui_enabled = document.getElementById("use_alt_webui_checkbox").checked; + const webui_files_location_textarea = document.getElementById("webui_files_location_textarea").value; if (alternative_webui_enabled && (webui_files_location_textarea.trim() === "")) { alert("QBT_TR(The alternative WebUI files location cannot be blank.)QBT_TR[CONTEXT=OptionsDialog]"); return; @@ -3021,105 +3021,105 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD settings["alternative_webui_path"] = webui_files_location_textarea; // Security - settings["web_ui_clickjacking_protection_enabled"] = $("clickjacking_protection_checkbox").checked; - settings["web_ui_csrf_protection_enabled"] = $("csrf_protection_checkbox").checked; - settings["web_ui_secure_cookie_enabled"] = $("secureCookieCheckbox").checked; - settings["web_ui_host_header_validation_enabled"] = $("host_header_validation_checkbox").checked; + settings["web_ui_clickjacking_protection_enabled"] = document.getElementById("clickjacking_protection_checkbox").checked; + settings["web_ui_csrf_protection_enabled"] = document.getElementById("csrf_protection_checkbox").checked; + settings["web_ui_secure_cookie_enabled"] = document.getElementById("secureCookieCheckbox").checked; + settings["web_ui_host_header_validation_enabled"] = document.getElementById("host_header_validation_checkbox").checked; // Custom HTTP headers - settings["web_ui_use_custom_http_headers_enabled"] = $("webUIUseCustomHTTPHeadersCheckbox").checked; - settings["web_ui_custom_http_headers"] = $("webUICustomHTTPHeadersTextarea").value; + settings["web_ui_use_custom_http_headers_enabled"] = document.getElementById("webUIUseCustomHTTPHeadersCheckbox").checked; + settings["web_ui_custom_http_headers"] = document.getElementById("webUICustomHTTPHeadersTextarea").value; // Reverse Proxy - settings["web_ui_reverse_proxy_enabled"] = $("webUIReverseProxySupportCheckbox").checked; - settings["web_ui_reverse_proxies_list"] = $("webUIReverseProxiesListTextarea").value; + settings["web_ui_reverse_proxy_enabled"] = document.getElementById("webUIReverseProxySupportCheckbox").checked; + settings["web_ui_reverse_proxies_list"] = document.getElementById("webUIReverseProxiesListTextarea").value; // Update my dynamic domain name - settings["dyndns_enabled"] = $("use_dyndns_checkbox").checked; - settings["dyndns_service"] = Number($("dyndns_select").value); - settings["dyndns_domain"] = $("dyndns_domain_text").value; - settings["dyndns_username"] = $("dyndns_username_text").value; - settings["dyndns_password"] = $("dyndns_password_text").value; + settings["dyndns_enabled"] = document.getElementById("use_dyndns_checkbox").checked; + settings["dyndns_service"] = Number(document.getElementById("dyndns_select").value); + settings["dyndns_domain"] = document.getElementById("dyndns_domain_text").value; + settings["dyndns_username"] = document.getElementById("dyndns_username_text").value; + settings["dyndns_password"] = document.getElementById("dyndns_password_text").value; // Update advanced settings // qBittorrent section - settings["resume_data_storage_type"] = $("resumeDataStorageType").value; - settings["torrent_content_remove_option"] = $("torrentContentRemoveOption").value; - settings["memory_working_set_limit"] = Number($("memoryWorkingSetLimit").value); - settings["current_network_interface"] = $("networkInterface").value; - settings["current_interface_address"] = $("optionalIPAddressToBind").value; - settings["save_resume_data_interval"] = Number($("saveResumeDataInterval").value); - settings["save_statistics_interval"] = Number($("saveStatisticsInterval").value); - settings["torrent_file_size_limit"] = ($("torrentFileSizeLimit").value * 1024 * 1024); + settings["resume_data_storage_type"] = document.getElementById("resumeDataStorageType").value; + settings["torrent_content_remove_option"] = document.getElementById("torrentContentRemoveOption").value; + settings["memory_working_set_limit"] = Number(document.getElementById("memoryWorkingSetLimit").value); + settings["current_network_interface"] = document.getElementById("networkInterface").value; + settings["current_interface_address"] = document.getElementById("optionalIPAddressToBind").value; + settings["save_resume_data_interval"] = Number(document.getElementById("saveResumeDataInterval").value); + settings["save_statistics_interval"] = Number(document.getElementById("saveStatisticsInterval").value); + settings["torrent_file_size_limit"] = (document.getElementById("torrentFileSizeLimit").value * 1024 * 1024); settings["confirm_torrent_recheck"] = document.getElementById("confirmTorrentRecheck").checked; - settings["recheck_completed_torrents"] = $("recheckTorrentsOnCompletion").checked; - settings["app_instance_name"] = $("appInstanceName").value; - settings["refresh_interval"] = Number($("refreshInterval").value); - settings["resolve_peer_countries"] = $("resolvePeerCountries").checked; - settings["reannounce_when_address_changed"] = $("reannounceWhenAddressChanged").checked; - settings["enable_embedded_tracker"] = $("enableEmbeddedTracker").checked; - settings["embedded_tracker_port"] = Number($("embeddedTrackerPort").value); - settings["embedded_tracker_port_forwarding"] = $("embeddedTrackerPortForwarding").checked; - settings["mark_of_the_web"] = $("markOfTheWeb").checked; - settings["ignore_ssl_errors"] = $("ignoreSSLErrors").checked; - settings["python_executable_path"] = $("pythonExecutablePath").value; + settings["recheck_completed_torrents"] = document.getElementById("recheckTorrentsOnCompletion").checked; + settings["app_instance_name"] = document.getElementById("appInstanceName").value; + settings["refresh_interval"] = Number(document.getElementById("refreshInterval").value); + settings["resolve_peer_countries"] = document.getElementById("resolvePeerCountries").checked; + settings["reannounce_when_address_changed"] = document.getElementById("reannounceWhenAddressChanged").checked; + settings["enable_embedded_tracker"] = document.getElementById("enableEmbeddedTracker").checked; + settings["embedded_tracker_port"] = Number(document.getElementById("embeddedTrackerPort").value); + settings["embedded_tracker_port_forwarding"] = document.getElementById("embeddedTrackerPortForwarding").checked; + settings["mark_of_the_web"] = document.getElementById("markOfTheWeb").checked; + settings["ignore_ssl_errors"] = document.getElementById("ignoreSSLErrors").checked; + settings["python_executable_path"] = document.getElementById("pythonExecutablePath").value; // libtorrent section - settings["bdecode_depth_limit"] = Number($("bdecodeDepthLimit").value); - settings["bdecode_token_limit"] = Number($("bdecodeTokenLimit").value); - settings["async_io_threads"] = Number($("asyncIOThreads").value); - settings["hashing_threads"] = Number($("hashingThreads").value); - settings["file_pool_size"] = Number($("filePoolSize").value); - settings["checking_memory_use"] = Number($("outstandMemoryWhenCheckingTorrents").value); - settings["disk_cache"] = Number($("diskCache").value); - settings["disk_cache_ttl"] = Number($("diskCacheExpiryInterval").value); - settings["disk_queue_size"] = (Number($("diskQueueSize").value) * 1024); - settings["disk_io_type"] = Number($("diskIOType").value); - settings["disk_io_read_mode"] = Number($("diskIOReadMode").value); - settings["disk_io_write_mode"] = Number($("diskIOWriteMode").value); - settings["enable_coalesce_read_write"] = $("coalesceReadsAndWrites").checked; - settings["enable_piece_extent_affinity"] = $("pieceExtentAffinity").checked; - settings["enable_upload_suggestions"] = $("sendUploadPieceSuggestions").checked; - settings["send_buffer_watermark"] = Number($("sendBufferWatermark").value); - settings["send_buffer_low_watermark"] = Number($("sendBufferLowWatermark").value); - settings["send_buffer_watermark_factor"] = Number($("sendBufferWatermarkFactor").value); - settings["connection_speed"] = Number($("connectionSpeed").value); - settings["socket_send_buffer_size"] = ($("socketSendBufferSize").value * 1024); - settings["socket_receive_buffer_size"] = ($("socketReceiveBufferSize").value * 1024); - settings["socket_backlog_size"] = Number($("socketBacklogSize").value); - settings["outgoing_ports_min"] = Number($("outgoingPortsMin").value); - settings["outgoing_ports_max"] = Number($("outgoingPortsMax").value); - settings["upnp_lease_duration"] = Number($("UPnPLeaseDuration").value); - settings["peer_tos"] = Number($("peerToS").value); - settings["utp_tcp_mixed_mode"] = Number($("utpTCPMixedModeAlgorithm").value); - settings["hostname_cache_ttl"] = Number($("hostnameCacheTTL").value); - settings["idn_support_enabled"] = $("IDNSupportCheckbox").checked; - settings["enable_multi_connections_from_same_ip"] = $("allowMultipleConnectionsFromTheSameIPAddress").checked; - settings["validate_https_tracker_certificate"] = $("validateHTTPSTrackerCertificate").checked; - settings["ssrf_mitigation"] = $("mitigateSSRF").checked; - settings["block_peers_on_privileged_ports"] = $("blockPeersOnPrivilegedPorts").checked; - settings["upload_slots_behavior"] = Number($("uploadSlotsBehavior").value); - settings["upload_choking_algorithm"] = Number($("uploadChokingAlgorithm").value); - settings["announce_to_all_trackers"] = $("announceAllTrackers").checked; - settings["announce_to_all_tiers"] = $("announceAllTiers").checked; - settings["announce_ip"] = $("announceIP").value; - const announcePort = Number($("announcePort").value); + settings["bdecode_depth_limit"] = Number(document.getElementById("bdecodeDepthLimit").value); + settings["bdecode_token_limit"] = Number(document.getElementById("bdecodeTokenLimit").value); + settings["async_io_threads"] = Number(document.getElementById("asyncIOThreads").value); + settings["hashing_threads"] = Number(document.getElementById("hashingThreads").value); + settings["file_pool_size"] = Number(document.getElementById("filePoolSize").value); + settings["checking_memory_use"] = Number(document.getElementById("outstandMemoryWhenCheckingTorrents").value); + settings["disk_cache"] = Number(document.getElementById("diskCache").value); + settings["disk_cache_ttl"] = Number(document.getElementById("diskCacheExpiryInterval").value); + settings["disk_queue_size"] = (Number(document.getElementById("diskQueueSize").value) * 1024); + settings["disk_io_type"] = Number(document.getElementById("diskIOType").value); + settings["disk_io_read_mode"] = Number(document.getElementById("diskIOReadMode").value); + settings["disk_io_write_mode"] = Number(document.getElementById("diskIOWriteMode").value); + settings["enable_coalesce_read_write"] = document.getElementById("coalesceReadsAndWrites").checked; + settings["enable_piece_extent_affinity"] = document.getElementById("pieceExtentAffinity").checked; + settings["enable_upload_suggestions"] = document.getElementById("sendUploadPieceSuggestions").checked; + settings["send_buffer_watermark"] = Number(document.getElementById("sendBufferWatermark").value); + settings["send_buffer_low_watermark"] = Number(document.getElementById("sendBufferLowWatermark").value); + settings["send_buffer_watermark_factor"] = Number(document.getElementById("sendBufferWatermarkFactor").value); + settings["connection_speed"] = Number(document.getElementById("connectionSpeed").value); + settings["socket_send_buffer_size"] = (document.getElementById("socketSendBufferSize").value * 1024); + settings["socket_receive_buffer_size"] = (document.getElementById("socketReceiveBufferSize").value * 1024); + settings["socket_backlog_size"] = Number(document.getElementById("socketBacklogSize").value); + settings["outgoing_ports_min"] = Number(document.getElementById("outgoingPortsMin").value); + settings["outgoing_ports_max"] = Number(document.getElementById("outgoingPortsMax").value); + settings["upnp_lease_duration"] = Number(document.getElementById("UPnPLeaseDuration").value); + settings["peer_tos"] = Number(document.getElementById("peerToS").value); + settings["utp_tcp_mixed_mode"] = Number(document.getElementById("utpTCPMixedModeAlgorithm").value); + settings["hostname_cache_ttl"] = Number(document.getElementById("hostnameCacheTTL").value); + settings["idn_support_enabled"] = document.getElementById("IDNSupportCheckbox").checked; + settings["enable_multi_connections_from_same_ip"] = document.getElementById("allowMultipleConnectionsFromTheSameIPAddress").checked; + settings["validate_https_tracker_certificate"] = document.getElementById("validateHTTPSTrackerCertificate").checked; + settings["ssrf_mitigation"] = document.getElementById("mitigateSSRF").checked; + settings["block_peers_on_privileged_ports"] = document.getElementById("blockPeersOnPrivilegedPorts").checked; + settings["upload_slots_behavior"] = Number(document.getElementById("uploadSlotsBehavior").value); + settings["upload_choking_algorithm"] = Number(document.getElementById("uploadChokingAlgorithm").value); + settings["announce_to_all_trackers"] = document.getElementById("announceAllTrackers").checked; + settings["announce_to_all_tiers"] = document.getElementById("announceAllTiers").checked; + settings["announce_ip"] = document.getElementById("announceIP").value; + const announcePort = Number(document.getElementById("announcePort").value); if (Number.isNaN(announcePort) || (announcePort < 0) || (announcePort > 65535)) { alert("QBT_TR(The announce port must be between 0 and 65535.)QBT_TR[CONTEXT=OptionsDialog]"); return; } settings["announce_port"] = announcePort; - settings["max_concurrent_http_announces"] = Number($("maxConcurrentHTTPAnnounces").value); - settings["stop_tracker_timeout"] = Number($("stopTrackerTimeout").value); - settings["peer_turnover"] = Number($("peerTurnover").value); - settings["peer_turnover_cutoff"] = Number($("peerTurnoverCutoff").value); - settings["peer_turnover_interval"] = Number($("peerTurnoverInterval").value); - settings["request_queue_size"] = Number($("requestQueueSize").value); - settings["dht_bootstrap_nodes"] = $("dhtBootstrapNodes").value; - settings["i2p_inbound_quantity"] = Number($("i2pInboundQuantity").value); - settings["i2p_outbound_quantity"] = Number($("i2pOutboundQuantity").value); - settings["i2p_inbound_length"] = Number($("i2pInboundLength").value); - settings["i2p_outbound_length"] = Number($("i2pOutboundLength").value); + settings["max_concurrent_http_announces"] = Number(document.getElementById("maxConcurrentHTTPAnnounces").value); + settings["stop_tracker_timeout"] = Number(document.getElementById("stopTrackerTimeout").value); + settings["peer_turnover"] = Number(document.getElementById("peerTurnover").value); + settings["peer_turnover_cutoff"] = Number(document.getElementById("peerTurnoverCutoff").value); + settings["peer_turnover_interval"] = Number(document.getElementById("peerTurnoverInterval").value); + settings["request_queue_size"] = Number(document.getElementById("requestQueueSize").value); + settings["dht_bootstrap_nodes"] = document.getElementById("dhtBootstrapNodes").value; + settings["i2p_inbound_quantity"] = Number(document.getElementById("i2pInboundQuantity").value); + settings["i2p_outbound_quantity"] = Number(document.getElementById("i2pOutboundQuantity").value); + settings["i2p_inbound_length"] = Number(document.getElementById("i2pInboundLength").value); + settings["i2p_outbound_length"] = Number(document.getElementById("i2pOutboundLength").value); // Send it to qBT window.parent.qBittorrent.Cache.preferences.set({ @@ -3137,7 +3137,7 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD }; const setup = () => { - watchedFoldersTable = new HtmlTable($("watched_folders_tab")); + watchedFoldersTable = new HtmlTable(document.getElementById("watched_folders_tab")); const buildInfo = window.qBittorrent.Cache.buildInfo.get(); @@ -3145,30 +3145,33 @@ Use ';' to split multiple entries. Can use wildcard '*'.)QBT_TR[CONTEXT=OptionsD const libtorrentVersion = window.qBittorrent.Misc.parseVersion(buildInfo.libtorrent); if (libtorrentVersion.valid) { if (libtorrentVersion.major >= 2) { - $("rowDiskCache").style.display = "none"; - $("rowDiskCacheExpiryInterval").style.display = "none"; - $("rowCoalesceReadsAndWrites").style.display = "none"; + document.getElementById("rowDiskCache").style.display = "none"; + document.getElementById("rowDiskCacheExpiryInterval").style.display = "none"; + document.getElementById("rowCoalesceReadsAndWrites").style.display = "none"; } else { - $("fieldsetI2p").style.display = "none"; - $("rowMemoryWorkingSetLimit").style.display = "none"; - $("rowHashingThreads").style.display = "none"; - $("rowDiskIOType").style.display = "none"; - $("rowI2pInboundQuantity").style.display = "none"; - $("rowI2pOutboundQuantity").style.display = "none"; - $("rowI2pInboundLength").style.display = "none"; - $("rowI2pOutboundLength").style.display = "none"; + document.getElementById("fieldsetI2p").style.display = "none"; + document.getElementById("rowMemoryWorkingSetLimit").style.display = "none"; + document.getElementById("rowHashingThreads").style.display = "none"; + document.getElementById("rowDiskIOType").style.display = "none"; + document.getElementById("rowI2pInboundQuantity").style.display = "none"; + document.getElementById("rowI2pOutboundQuantity").style.display = "none"; + document.getElementById("rowI2pInboundLength").style.display = "none"; + document.getElementById("rowI2pOutboundLength").style.display = "none"; } if (!((libtorrentVersion.major >= 2) && (libtorrentVersion.minor >= 0) && (libtorrentVersion.fix >= 6))) - $("diskIOWriteModeWriteThrough").style.display = "none"; + document.getElementById("diskIOWriteModeWriteThrough").style.display = "none"; } - if ((buildInfo.platform !== "macos") && (buildInfo.platform !== "windows")) - $("rowMarkOfTheWeb").style.display = "none"; + if ((buildInfo.platform === "linux") || (buildInfo.platform === "macos")) + document.getElementById("rowMemoryWorkingSetLimit").style.display = "none"; - $("networkInterface").addEventListener("change", function() { - updateInterfaceAddresses($(this).value, ""); + if ((buildInfo.platform !== "macos") && (buildInfo.platform !== "windows")) + document.getElementById("rowMarkOfTheWeb").style.display = "none"; + + document.getElementById("networkInterface").addEventListener("change", function(event) { + updateInterfaceAddresses(this.value, ""); }); loadPreferences(); diff --git a/src/webui/www/private/views/preferencesToolbar.html b/src/webui/www/private/views/preferencesToolbar.html index c2ff79993..20eb487aa 100644 --- a/src/webui/www/private/views/preferencesToolbar.html +++ b/src/webui/www/private/views/preferencesToolbar.html @@ -36,37 +36,37 @@ // Tabs MochaUI.initializeTabs("preferencesTabs"); - $("PrefBehaviorLink").addEventListener("click", (e) => { + document.getElementById("PrefBehaviorLink").addEventListener("click", (e) => { Array.prototype.forEach.call(document.querySelectorAll(".PrefTab"), (tab => tab.classList.add("invisible"))); - $("BehaviorTab").classList.remove("invisible"); + document.getElementById("BehaviorTab").classList.remove("invisible"); }); - $("PrefDownloadsLink").addEventListener("click", (e) => { + document.getElementById("PrefDownloadsLink").addEventListener("click", (e) => { Array.prototype.forEach.call(document.querySelectorAll(".PrefTab"), (tab => tab.classList.add("invisible"))); - $("DownloadsTab").classList.remove("invisible"); + document.getElementById("DownloadsTab").classList.remove("invisible"); }); - $("PrefConnectionLink").addEventListener("click", (e) => { + document.getElementById("PrefConnectionLink").addEventListener("click", (e) => { Array.prototype.forEach.call(document.querySelectorAll(".PrefTab"), (tab => tab.classList.add("invisible"))); - $("ConnectionTab").classList.remove("invisible"); + document.getElementById("ConnectionTab").classList.remove("invisible"); }); - $("PrefSpeedLink").addEventListener("click", (e) => { + document.getElementById("PrefSpeedLink").addEventListener("click", (e) => { Array.prototype.forEach.call(document.querySelectorAll(".PrefTab"), (tab => tab.classList.add("invisible"))); - $("SpeedTab").classList.remove("invisible"); + document.getElementById("SpeedTab").classList.remove("invisible"); }); - $("PrefBittorrentLink").addEventListener("click", (e) => { + document.getElementById("PrefBittorrentLink").addEventListener("click", (e) => { Array.prototype.forEach.call(document.querySelectorAll(".PrefTab"), (tab => tab.classList.add("invisible"))); - $("BittorrentTab").classList.remove("invisible"); + document.getElementById("BittorrentTab").classList.remove("invisible"); }); - $("PrefRSSLink").addEventListener("click", (e) => { + document.getElementById("PrefRSSLink").addEventListener("click", (e) => { Array.prototype.forEach.call(document.querySelectorAll(".PrefTab"), (tab => tab.classList.add("invisible"))); - $("RSSTab").classList.remove("invisible"); + document.getElementById("RSSTab").classList.remove("invisible"); }); - $("PrefWebUILink").addEventListener("click", (e) => { + document.getElementById("PrefWebUILink").addEventListener("click", (e) => { Array.prototype.forEach.call(document.querySelectorAll(".PrefTab"), (tab => tab.classList.add("invisible"))); - $("WebUITab").classList.remove("invisible"); + document.getElementById("WebUITab").classList.remove("invisible"); }); - $("PrefAdvancedLink").addEventListener("click", (e) => { + document.getElementById("PrefAdvancedLink").addEventListener("click", (e) => { Array.prototype.forEach.call(document.querySelectorAll(".PrefTab"), (tab => tab.classList.add("invisible"))); - $("AdvancedTab").classList.remove("invisible"); + document.getElementById("AdvancedTab").classList.remove("invisible"); }); })(); diff --git a/src/webui/www/private/views/properties.html b/src/webui/www/private/views/properties.html index e3cb42b1b..fbcaa1d53 100644 --- a/src/webui/www/private/views/properties.html +++ b/src/webui/www/private/views/properties.html @@ -1,4 +1,4 @@ -