diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index 67238b4e8..1b96f61f2 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -17,7 +17,7 @@ add_library(qbt_base STATIC bittorrent/infohash.h bittorrent/loadtorrentparams.h bittorrent/ltqhash.h - bittorrent/ltunderlyingtype.h + bittorrent/lttypecast.h bittorrent/magneturi.h bittorrent/nativesessionextension.h bittorrent/nativetorrentextension.h diff --git a/src/base/base.pri b/src/base/base.pri index 401f7b9bd..57af3dbd6 100644 --- a/src/base/base.pri +++ b/src/base/base.pri @@ -16,7 +16,7 @@ HEADERS += \ $$PWD/bittorrent/infohash.h \ $$PWD/bittorrent/loadtorrentparams.h \ $$PWD/bittorrent/ltqhash.h \ - $$PWD/bittorrent/ltunderlyingtype.h \ + $$PWD/bittorrent/lttypecast.h \ $$PWD/bittorrent/magneturi.h \ $$PWD/bittorrent/nativesessionextension.h \ $$PWD/bittorrent/nativetorrentextension.h \ diff --git a/src/base/bittorrent/abstractfilestorage.h b/src/base/bittorrent/abstractfilestorage.h index b13cb5b52..74afda94f 100644 --- a/src/base/bittorrent/abstractfilestorage.h +++ b/src/base/bittorrent/abstractfilestorage.h @@ -44,7 +44,6 @@ namespace BitTorrent virtual int filesCount() const = 0; virtual QString filePath(int index) const = 0; - virtual QString fileName(int index) const = 0; virtual qlonglong fileSize(int index) const = 0; virtual void renameFile(int index, const QString &name) = 0; diff --git a/src/base/bittorrent/ltunderlyingtype.h b/src/base/bittorrent/lttypecast.h similarity index 65% rename from src/base/bittorrent/ltunderlyingtype.h rename to src/base/bittorrent/lttypecast.h index 6b3993b2d..53d96f155 100644 --- a/src/base/bittorrent/ltunderlyingtype.h +++ b/src/base/bittorrent/lttypecast.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2019 Vladimir Golovnev + * Copyright (C) 2019, 2021 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,8 +28,25 @@ #pragma once -template -typename T::underlying_type toLTUnderlyingType(const T &t) +#include + +#include "downloadpriority.h" + +namespace BitTorrent::LT { - return static_cast(t); + template + constexpr typename T::underlying_type toUnderlyingType(const T &t) noexcept + { + return static_cast(t); + } + + constexpr lt::download_priority_t toNative(const DownloadPriority priority) noexcept + { + return static_cast(static_cast(priority)); + } + + constexpr DownloadPriority fromNative(const lt::download_priority_t priority) noexcept + { + return static_cast(toUnderlyingType(priority)); + } } diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index 22726ff07..dd846fedb 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -93,7 +93,7 @@ #include "filesearcher.h" #include "filterparserthread.h" #include "loadtorrentparams.h" -#include "ltunderlyingtype.h" +#include "lttypecast.h" #include "magneturi.h" #include "nativesessionextension.h" #include "portforwarderimpl.h" @@ -1491,7 +1491,7 @@ void Session::configurePeerClasses() // Proactively do the same for 0.0.0.0 and address_v4::any() f.add_rule(lt::address_v4::any() , lt::address_v4::broadcast() - , 1 << toLTUnderlyingType(lt::session::global_peer_class_id)); + , 1 << LT::toUnderlyingType(lt::session::global_peer_class_id)); // IPv6 may not be available on OS and the parsing // would result in an exception -> abnormal program termination @@ -1500,7 +1500,7 @@ void Session::configurePeerClasses() { f.add_rule(lt::address_v6::any() , lt::make_address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") - , 1 << toLTUnderlyingType(lt::session::global_peer_class_id)); + , 1 << LT::toUnderlyingType(lt::session::global_peer_class_id)); } catch (const std::exception &) {} @@ -1509,21 +1509,21 @@ void Session::configurePeerClasses() // local networks f.add_rule(lt::make_address("10.0.0.0") , lt::make_address("10.255.255.255") - , 1 << toLTUnderlyingType(lt::session::local_peer_class_id)); + , 1 << LT::toUnderlyingType(lt::session::local_peer_class_id)); f.add_rule(lt::make_address("172.16.0.0") , lt::make_address("172.31.255.255") - , 1 << toLTUnderlyingType(lt::session::local_peer_class_id)); + , 1 << LT::toUnderlyingType(lt::session::local_peer_class_id)); f.add_rule(lt::make_address("192.168.0.0") , lt::make_address("192.168.255.255") - , 1 << toLTUnderlyingType(lt::session::local_peer_class_id)); + , 1 << LT::toUnderlyingType(lt::session::local_peer_class_id)); // link local f.add_rule(lt::make_address("169.254.0.0") , lt::make_address("169.254.255.255") - , 1 << toLTUnderlyingType(lt::session::local_peer_class_id)); + , 1 << LT::toUnderlyingType(lt::session::local_peer_class_id)); // loopback f.add_rule(lt::make_address("127.0.0.0") , lt::make_address("127.255.255.255") - , 1 << toLTUnderlyingType(lt::session::local_peer_class_id)); + , 1 << LT::toUnderlyingType(lt::session::local_peer_class_id)); // IPv6 may not be available on OS and the parsing // would result in an exception -> abnormal program termination @@ -1533,15 +1533,15 @@ void Session::configurePeerClasses() // link local f.add_rule(lt::make_address("fe80::") , lt::make_address("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff") - , 1 << toLTUnderlyingType(lt::session::local_peer_class_id)); + , 1 << LT::toUnderlyingType(lt::session::local_peer_class_id)); // unique local addresses f.add_rule(lt::make_address("fc00::") , lt::make_address("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") - , 1 << toLTUnderlyingType(lt::session::local_peer_class_id)); + , 1 << LT::toUnderlyingType(lt::session::local_peer_class_id)); // loopback f.add_rule(lt::address_v6::loopback() , lt::address_v6::loopback() - , 1 << toLTUnderlyingType(lt::session::local_peer_class_id)); + , 1 << LT::toUnderlyingType(lt::session::local_peer_class_id)); } catch (const std::exception &) {} } @@ -1727,8 +1727,10 @@ void Session::fileSearchFinished(const TorrentID &id, const QString &savePath, c lt::add_torrent_params &p = params.ltAddTorrentParams; p.save_path = Utils::Fs::toNativePath(savePath).toStdString(); + const TorrentInfo torrentInfo {p.ti}; + const auto nativeIndexes = torrentInfo.nativeIndexes(); for (int i = 0; i < fileNames.size(); ++i) - p.renamed_files[lt::file_index_t {i}] = fileNames[i].toStdString(); + p.renamed_files[nativeIndexes[i]] = fileNames[i].toStdString(); loadTorrent(params); } @@ -2141,28 +2143,20 @@ bool Session::addTorrent_impl(const std::variant &source { QString contentName = metadata.rootFolder(); if (contentName.isEmpty() && (metadata.filesCount() == 1)) - contentName = metadata.fileName(0); + contentName = Utils::Fs::fileName(metadata.filePath(0)); if (!contentName.isEmpty() && (contentName != metadata.name())) loadTorrentParams.name = contentName; } Q_ASSERT(p.file_priorities.empty()); - if (addTorrentParams.filePriorities.empty()) - { - // Use qBittorrent default priority rather than libtorrent's (4) - p.file_priorities = std::vector(metadata.filesCount(), static_cast( - static_cast(DownloadPriority::Normal))); - } - else - { - std::transform(addTorrentParams.filePriorities.cbegin(), addTorrentParams.filePriorities.cend() - , std::back_inserter(p.file_priorities), [](const DownloadPriority priority) - { - return static_cast( - static_cast(priority)); - }); - } + const int internalFilesCount = metadata.nativeInfo()->files().num_files(); // including .pad files + // Use qBittorrent default priority rather than libtorrent's (4) + p.file_priorities = std::vector(internalFilesCount, LT::toNative(DownloadPriority::Normal)); + const auto nativeIndexes = metadata.nativeIndexes(); + Q_ASSERT(addTorrentParams.filePriorities.isEmpty() || (addTorrentParams.filePriorities.size() == nativeIndexes.size())); + for (int i = 0; i < addTorrentParams.filePriorities.size(); ++i) + p.file_priorities[LT::toUnderlyingType(nativeIndexes[i])] = LT::toNative(addTorrentParams.filePriorities[i]); p.ti = metadata.nativeInfo(); } @@ -2388,7 +2382,7 @@ void Session::saveTorrentsQueue() const for (const TorrentImpl *torrent : asConst(m_torrents)) { // We require actual (non-cached) queue position here! - const int queuePos = toLTUnderlyingType(torrent->nativeHandle().queue_position()); + const int queuePos = LT::toUnderlyingType(torrent->nativeHandle().queue_position()); if (queuePos >= 0) { if (queuePos >= queue.size()) @@ -3937,9 +3931,8 @@ void Session::handleTorrentFinished(TorrentImpl *const torrent) qDebug("Checking if the torrent contains torrent files to download"); // Check if there are torrent files inside - for (int i = 0; i < torrent->filesCount(); ++i) + for (const QString &torrentRelpath : asConst(torrent->filePaths())) { - const QString torrentRelpath = torrent->filePath(i); if (torrentRelpath.endsWith(".torrent", Qt::CaseInsensitive)) { qDebug("Found possible recursive torrent download."); @@ -4162,9 +4155,8 @@ void Session::recursiveTorrentDownload(const TorrentID &id) TorrentImpl *const torrent = m_torrents.value(id); if (!torrent) return; - for (int i = 0; i < torrent->filesCount(); ++i) + for (const QString &torrentRelpath : asConst(torrent->filePaths())) { - const QString torrentRelpath = torrent->filePath(i); if (torrentRelpath.endsWith(".torrent")) { LogMsg(tr("Recursive download of file '%1' embedded in torrent '%2'" diff --git a/src/base/bittorrent/torrent.h b/src/base/bittorrent/torrent.h index 0f40dff3f..3c8869d6e 100644 --- a/src/base/bittorrent/torrent.h +++ b/src/base/bittorrent/torrent.h @@ -193,6 +193,7 @@ namespace BitTorrent virtual qreal ratioLimit() const = 0; virtual int seedingTimeLimit() const = 0; + virtual QStringList filePaths() const = 0; virtual QStringList absoluteFilePaths() const = 0; virtual QVector filePriorities() const = 0; @@ -214,7 +215,6 @@ namespace BitTorrent virtual bool hasMetadata() const = 0; virtual bool hasMissingFiles() const = 0; virtual bool hasError() const = 0; - virtual bool hasFilteredPieces() const = 0; virtual int queuePosition() const = 0; virtual QVector trackers() const = 0; virtual QVector urlSeeds() const = 0; diff --git a/src/base/bittorrent/torrentcreatorthread.cpp b/src/base/bittorrent/torrentcreatorthread.cpp index 53aaa3885..80b6cc9b2 100644 --- a/src/base/bittorrent/torrentcreatorthread.cpp +++ b/src/base/bittorrent/torrentcreatorthread.cpp @@ -44,7 +44,7 @@ #include "base/utils/fs.h" #include "base/utils/io.h" #include "base/version.h" -#include "ltunderlyingtype.h" +#include "lttypecast.h" namespace { @@ -186,7 +186,7 @@ void TorrentCreatorThread::run() , [this, &newTorrent](const lt::piece_index_t n) { checkInterruptionRequested(); - sendProgressSignal(toLTUnderlyingType(n), newTorrent.num_pieces()); + sendProgressSignal(LT::toUnderlyingType(n), newTorrent.num_pieces()); }); // Set qBittorrent as creator and add user comment to diff --git a/src/base/bittorrent/torrentimpl.cpp b/src/base/bittorrent/torrentimpl.cpp index 356b14d02..4ffa3ee0b 100644 --- a/src/base/bittorrent/torrentimpl.cpp +++ b/src/base/bittorrent/torrentimpl.cpp @@ -64,7 +64,7 @@ #include "downloadpriority.h" #include "loadtorrentparams.h" #include "ltqhash.h" -#include "ltunderlyingtype.h" +#include "lttypecast.h" #include "peeraddress.h" #include "peerinfo.h" #include "session.h" @@ -74,20 +74,6 @@ using namespace BitTorrent; namespace { - std::vector toLTDownloadPriorities(const QVector &priorities) - { - std::vector out; - out.reserve(priorities.size()); - - std::transform(priorities.cbegin(), priorities.cend() - , std::back_inserter(out), [](const DownloadPriority priority) - { - return static_cast( - static_cast(priority)); - }); - return out; - } - lt::announce_entry makeNativeAnnouncerEntry(const QString &url, const int tier) { lt::announce_entry entry {url.toStdString()}; @@ -762,20 +748,19 @@ int TorrentImpl::seedingTimeLimit() const return m_seedingTimeLimit; } -QString TorrentImpl::filePath(int index) const +QString TorrentImpl::filePath(const int index) const { return m_torrentInfo.filePath(index); } -QString TorrentImpl::fileName(int index) const +qlonglong TorrentImpl::fileSize(const int index) const { - if (!hasMetadata()) return {}; - return Utils::Fs::fileName(filePath(index)); + return m_torrentInfo.fileSize(index); } -qlonglong TorrentImpl::fileSize(int index) const +QStringList TorrentImpl::filePaths() const { - return m_torrentInfo.fileSize(index); + return m_torrentInfo.filePaths(); } // Return a list of absolute paths corresponding @@ -784,8 +769,9 @@ QStringList TorrentImpl::absoluteFilePaths() const { if (!hasMetadata()) return {}; - const QDir saveDir(savePath(true)); + const QDir saveDir {savePath(true)}; QStringList res; + res.reserve(filesCount()); for (int i = 0; i < filesCount(); ++i) res << Utils::Fs::expandPathAbs(saveDir.absoluteFilePath(filePath(i))); return res; @@ -793,14 +779,19 @@ QStringList TorrentImpl::absoluteFilePaths() const QVector TorrentImpl::filePriorities() const { + if (!hasMetadata()) + return {}; + const std::vector fp = m_nativeHandle.get_file_priorities(); QVector ret; - ret.reserve(static_cast(fp.size())); - std::transform(fp.cbegin(), fp.cend(), std::back_inserter(ret), [](const lt::download_priority_t priority) + ret.reserve(filesCount()); + for (const lt::file_index_t nativeIndex : asConst(m_torrentInfo.nativeIndexes())) { - return static_cast(toLTUnderlyingType(priority)); - }); + const auto priority = LT::fromNative(fp[LT::toUnderlyingType(nativeIndex)]); + ret.append(priority); + } + return ret; } @@ -987,15 +978,6 @@ bool TorrentImpl::hasError() const return (m_nativeStatus.errc || (m_nativeStatus.flags & lt::torrent_flags::upload_mode)); } -bool TorrentImpl::hasFilteredPieces() const -{ - const std::vector pp = m_nativeHandle.get_piece_priorities(); - return std::any_of(pp.cbegin(), pp.cend(), [](const lt::download_priority_t priority) - { - return (priority == lt::download_priority_t {0}); - }); -} - int TorrentImpl::queuePosition() const { return static_cast(m_nativeStatus.queue_position); @@ -1092,16 +1074,18 @@ QVector TorrentImpl::filesProgress() const std::vector fp; m_nativeHandle.file_progress(fp, lt::torrent_handle::piece_granularity); - const int count = static_cast(fp.size()); + const int count = filesCount(); + const auto nativeIndexes = m_torrentInfo.nativeIndexes(); QVector result; result.reserve(count); for (int i = 0; i < count; ++i) { + const int64_t progress = fp[LT::toUnderlyingType(nativeIndexes[i])]; const qlonglong size = fileSize(i); - if ((size <= 0) || (fp[i] == size)) + if ((size <= 0) || (progress == size)) result << 1; else - result << (fp[i] / static_cast(size)); + result << (progress / static_cast(size)); } return result; @@ -1252,7 +1236,7 @@ QBitArray TorrentImpl::downloadingPieces() const m_nativeHandle.get_download_queue(queue); for (const lt::partial_piece_info &info : queue) - result.setBit(toLTUnderlyingType(info.piece_index)); + result.setBit(LT::toUnderlyingType(info.piece_index)); return result; } @@ -1406,7 +1390,7 @@ void TorrentImpl::move_impl(QString path, const MoveStorageMode mode) } } -void TorrentImpl::forceReannounce(int index) +void TorrentImpl::forceReannounce(const int index) { m_nativeHandle.force_reannounce(0, index); } @@ -1469,28 +1453,29 @@ void TorrentImpl::applyFirstLastPiecePriority(const bool enabled, const QVector< // Download first and last pieces first for every file in the torrent - const std::vector filePriorities = !updatedFilePrio.isEmpty() ? toLTDownloadPriorities(updatedFilePrio) - : nativeHandle().get_file_priorities(); + const QVector filePriorities = + !updatedFilePrio.isEmpty() ? updatedFilePrio : this->filePriorities(); std::vector piecePriorities = nativeHandle().get_piece_priorities(); // Updating file priorities is an async operation in libtorrent, when we just updated it and immediately query it // we might get the old/wrong values, so we rely on `updatedFilePrio` in this case. - for (int index = 0; index < static_cast(filePriorities.size()); ++index) + for (int index = 0; index < filePriorities.size(); ++index) { - const lt::download_priority_t filePrio = filePriorities[index]; - if (filePrio <= lt::download_priority_t {0}) + const DownloadPriority filePrio = filePriorities[index]; + if (filePrio <= DownloadPriority::Ignored) continue; // Determine the priority to set - const lt::download_priority_t newPrio = enabled ? lt::download_priority_t {7} : filePrio; + const DownloadPriority newPrio = enabled ? DownloadPriority::Maximum : filePrio; + const auto piecePrio = static_cast(static_cast(newPrio)); const TorrentInfo::PieceRange extremities = info().filePieces(index); // worst case: AVI index = 1% of total file size (at the end of the file) const int nNumPieces = std::ceil(fileSize(index) * 0.01 / pieceLength()); for (int i = 0; i < nNumPieces; ++i) { - piecePriorities[extremities.first() + i] = newPrio; - piecePriorities[extremities.last() - i] = newPrio; + piecePriorities[extremities.first() + i] = piecePrio; + piecePriorities[extremities.last() - i] = piecePrio; } } @@ -1506,14 +1491,16 @@ void TorrentImpl::endReceivedMetadataHandling(const QString &savePath, const QSt { lt::add_torrent_params &p = m_ltAddTorrentParams; - p.ti = std::const_pointer_cast(m_nativeHandle.torrent_file()); + const TorrentInfo torrentInfo {m_nativeHandle.torrent_file()}; + const auto nativeIndexes = torrentInfo.nativeIndexes(); for (int i = 0; i < fileNames.size(); ++i) - p.renamed_files[lt::file_index_t {i}] = fileNames[i].toStdString(); + p.renamed_files[nativeIndexes[i]] = fileNames[i].toStdString(); p.save_path = Utils::Fs::toNativePath(savePath).toStdString(); + p.ti = torrentInfo.nativeInfo(); + const int internalFilesCount = p.ti->files().num_files(); // including .pad files // Use qBittorrent default priority rather than libtorrent's (4) - p.file_priorities = std::vector(fileNames.size(), static_cast( - static_cast(DownloadPriority::Normal))); + p.file_priorities = std::vector(internalFilesCount, LT::toNative(DownloadPriority::Normal)); reload(); @@ -1630,10 +1617,10 @@ void TorrentImpl::renameFile(const int index, const QString &path) { #ifndef QBT_USES_LIBTORRENT2 const QString oldPath = filePath(index); - m_oldPath[lt::file_index_t {index}].push_back(oldPath); + m_oldPath[index].push_back(oldPath); #endif ++m_renameCount; - m_nativeHandle.rename_file(lt::file_index_t {index}, Utils::Fs::toNativePath(path).toStdString()); + m_nativeHandle.rename_file(m_torrentInfo.nativeIndexes()[index], Utils::Fs::toNativePath(path).toStdString()); } void TorrentImpl::handleStateUpdate(const lt::torrent_status &nativeStatus) @@ -1864,13 +1851,16 @@ void TorrentImpl::handleFastResumeRejectedAlert(const lt::fastresume_rejected_al void TorrentImpl::handleFileRenamedAlert(const lt::file_renamed_alert *p) { + const int fileIndex = m_torrentInfo.nativeIndexes().indexOf(p->index); + Q_ASSERT(fileIndex >= 0); + // Remove empty leftover folders // For example renaming "a/b/c" to "d/b/c", then folders "a/b" and "a" will // be removed if they are empty #ifndef QBT_USES_LIBTORRENT2 - const QString oldFilePath = m_oldPath[p->index].takeFirst(); - if (m_oldPath[p->index].isEmpty()) - m_oldPath.remove(p->index); + const QString oldFilePath = m_oldPath[fileIndex].takeFirst(); + if (m_oldPath[fileIndex].isEmpty()) + m_oldPath.remove(fileIndex); #else const QString oldFilePath = Utils::Fs::toUniformPath(p->old_name()); #endif @@ -1910,14 +1900,16 @@ void TorrentImpl::handleFileRenamedAlert(const lt::file_renamed_alert *p) void TorrentImpl::handleFileRenameFailedAlert(const lt::file_rename_failed_alert *p) { + const int fileIndex = m_torrentInfo.nativeIndexes().indexOf(p->index); + Q_ASSERT(fileIndex >= 0); + LogMsg(tr("File rename failed. Torrent: \"%1\", file: \"%2\", reason: \"%3\"") - .arg(name(), filePath(toLTUnderlyingType(p->index)) - , QString::fromLocal8Bit(p->error.message().c_str())), Log::WARNING); + .arg(name(), filePath(fileIndex), QString::fromLocal8Bit(p->error.message().c_str())), Log::WARNING); #ifndef QBT_USES_LIBTORRENT2 - m_oldPath[p->index].removeFirst(); - if (m_oldPath[p->index].isEmpty()) - m_oldPath.remove(p->index); + m_oldPath[fileIndex].removeFirst(); + if (m_oldPath[fileIndex].isEmpty()) + m_oldPath.remove(fileIndex); #endif --m_renameCount; @@ -1929,16 +1921,19 @@ void TorrentImpl::handleFileRenameFailedAlert(const lt::file_rename_failed_alert void TorrentImpl::handleFileCompletedAlert(const lt::file_completed_alert *p) { + const int fileIndex = m_torrentInfo.nativeIndexes().indexOf(p->index); + Q_ASSERT(fileIndex >= 0); + qDebug("A file completed download in torrent \"%s\"", qUtf8Printable(name())); if (m_session->isAppendExtensionEnabled()) { - QString name = filePath(toLTUnderlyingType(p->index)); + QString name = filePath(fileIndex); if (name.endsWith(QB_EXT)) { const QString oldName = name; name.chop(QB_EXT.size()); qDebug("Renaming %s to %s", qUtf8Printable(oldName), qUtf8Printable(name)); - renameFile(toLTUnderlyingType(p->index), name); + renameFile(fileIndex, name); } } } @@ -2271,7 +2266,8 @@ QString TorrentImpl::createMagnetURI() const void TorrentImpl::prioritizeFiles(const QVector &priorities) { if (!hasMetadata()) return; - if (priorities.size() != filesCount()) return; + + Q_ASSERT(priorities.size() == filesCount()); // Reset 'm_hasSeedStatus' if needed in order to react again to // 'torrent_finished_alert' and eg show tray notifications @@ -2288,8 +2284,14 @@ void TorrentImpl::prioritizeFiles(const QVector &priorities) } } + const int internalFilesCount = m_torrentInfo.nativeInfo()->files().num_files(); // including .pad files + auto nativePriorities = std::vector(internalFilesCount, LT::toNative(DownloadPriority::Normal)); + const auto nativeIndexes = m_torrentInfo.nativeIndexes(); + for (int i = 0; i < priorities.size(); ++i) + nativePriorities[LT::toUnderlyingType(nativeIndexes[i])] = LT::toNative(priorities[i]); + qDebug() << Q_FUNC_INFO << "Changing files priorities..."; - m_nativeHandle.prioritize_files(toLTDownloadPriorities(priorities)); + m_nativeHandle.prioritize_files(nativePriorities); // Restore first/last piece first option if necessary if (m_hasFirstLastPiecePriority) diff --git a/src/base/bittorrent/torrentimpl.h b/src/base/bittorrent/torrentimpl.h index 696017878..14721e935 100644 --- a/src/base/bittorrent/torrentimpl.h +++ b/src/base/bittorrent/torrentimpl.h @@ -126,8 +126,8 @@ namespace BitTorrent int seedingTimeLimit() const override; QString filePath(int index) const override; - QString fileName(int index) const override; qlonglong fileSize(int index) const override; + QStringList filePaths() const override; QStringList absoluteFilePaths() const override; QVector filePriorities() const override; @@ -149,7 +149,6 @@ namespace BitTorrent bool hasMetadata() const override; bool hasMissingFiles() const override; bool hasError() const override; - bool hasFilteredPieces() const override; int queuePosition() const override; QVector trackers() const override; QVector urlSeeds() const override; @@ -305,7 +304,7 @@ namespace BitTorrent #ifndef QBT_USES_LIBTORRENT2 // Until libtorrent provided an "old_name" field in `file_renamed_alert` // we relied on this workaround to remove empty leftover folders - QHash> m_oldPath; + QHash> m_oldPath; #endif QHash> m_trackerPeerCounts; diff --git a/src/base/bittorrent/torrentinfo.cpp b/src/base/bittorrent/torrentinfo.cpp index 5a1c7bdc7..499c0c96e 100644 --- a/src/base/bittorrent/torrentinfo.cpp +++ b/src/base/bittorrent/torrentinfo.cpp @@ -76,12 +76,23 @@ namespace const int torrentInfoId = qRegisterMetaType(); TorrentInfo::TorrentInfo(std::shared_ptr nativeInfo) + : m_nativeInfo {std::const_pointer_cast(nativeInfo)} { - m_nativeInfo = std::const_pointer_cast(nativeInfo); + if (!m_nativeInfo) + return; + + const lt::file_storage &fileStorage = m_nativeInfo->files(); + m_nativeIndexes.reserve(fileStorage.num_files()); + for (const lt::file_index_t nativeIndex : fileStorage.file_range()) + { + if (!fileStorage.pad_file_at(nativeIndex)) + m_nativeIndexes.append(nativeIndex); + } } TorrentInfo::TorrentInfo(const TorrentInfo &other) - : m_nativeInfo(other.m_nativeInfo) + : m_nativeInfo {other.m_nativeInfo} + , m_nativeIndexes {other.m_nativeIndexes} { } @@ -90,6 +101,7 @@ TorrentInfo &TorrentInfo::operator=(const TorrentInfo &other) if (this != &other) { m_nativeInfo = other.m_nativeInfo; + m_nativeIndexes = other.m_nativeIndexes; } return *this; } @@ -241,7 +253,7 @@ qlonglong TorrentInfo::totalSize() const int TorrentInfo::filesCount() const { if (!isValid()) return -1; - return m_nativeInfo->num_files(); + return m_nativeIndexes.size(); } int TorrentInfo::pieceLength() const @@ -266,40 +278,36 @@ QString TorrentInfo::filePath(const int index) const { if (!isValid()) return {}; return Utils::Fs::toUniformPath( - QString::fromStdString(m_nativeInfo->files().file_path(lt::file_index_t {index}))); + QString::fromStdString(m_nativeInfo->files().file_path(m_nativeIndexes[index]))); } QStringList TorrentInfo::filePaths() const { QStringList list; + list.reserve(filesCount()); for (int i = 0; i < filesCount(); ++i) list << filePath(i); return list; } -QString TorrentInfo::fileName(const int index) const -{ - return Utils::Fs::fileName(filePath(index)); -} - QString TorrentInfo::origFilePath(const int index) const { if (!isValid()) return {}; return Utils::Fs::toUniformPath( - QString::fromStdString(m_nativeInfo->orig_files().file_path(lt::file_index_t {index}))); + QString::fromStdString(m_nativeInfo->orig_files().file_path(m_nativeIndexes[index]))); } qlonglong TorrentInfo::fileSize(const int index) const { if (!isValid()) return -1; - return m_nativeInfo->files().file_size(lt::file_index_t {index}); + return m_nativeInfo->files().file_size(m_nativeIndexes[index]); } qlonglong TorrentInfo::fileOffset(const int index) const { if (!isValid()) return -1; - return m_nativeInfo->files().file_offset(lt::file_index_t {index}); + return m_nativeInfo->files().file_offset(m_nativeIndexes[index]); } QVector TorrentInfo::trackers() const @@ -368,8 +376,12 @@ QVector TorrentInfo::fileIndicesForPiece(const int pieceIndex) const lt::piece_index_t {pieceIndex}, 0, nativeInfo()->piece_size(lt::piece_index_t {pieceIndex})); QVector res; res.reserve(static_cast(files.size())); - std::transform(files.begin(), files.end(), std::back_inserter(res), - [](const lt::file_slice &s) { return static_cast(s.file_index); }); + for (const lt::file_slice &fileSlice : files) + { + const int index = m_nativeIndexes.indexOf(fileSlice.file_index); + if (index >= 0) + res.append(index); + } return res; } @@ -415,8 +427,8 @@ TorrentInfo::PieceRange TorrentInfo::filePieces(const int fileIndex) const } const lt::file_storage &files = nativeInfo()->files(); - const auto fileSize = files.file_size(lt::file_index_t {fileIndex}); - const auto fileOffset = files.file_offset(lt::file_index_t {fileIndex}); + const auto fileSize = files.file_size(m_nativeIndexes[fileIndex]); + const auto fileOffset = files.file_offset(m_nativeIndexes[fileIndex]); const int beginIdx = (fileOffset / pieceLength()); const int endIdx = ((fileOffset + fileSize - 1) / pieceLength()); @@ -429,7 +441,7 @@ TorrentInfo::PieceRange TorrentInfo::filePieces(const int fileIndex) const void TorrentInfo::renameFile(const int index, const QString &newPath) { if (!isValid()) return; - nativeInfo()->rename_file(lt::file_index_t {index}, Utils::Fs::toNativePath(newPath).toStdString()); + nativeInfo()->rename_file(m_nativeIndexes[index], Utils::Fs::toNativePath(newPath).toStdString()); } int TorrentInfo::fileIndex(const QString &fileName) const @@ -481,8 +493,8 @@ void TorrentInfo::stripRootFolder() if (files.name() != newName) { files.set_name(newName); - for (int i = 0; i < files.num_files(); ++i) - files.rename_file(lt::file_index_t {i}, files.file_path(lt::file_index_t {i})); + for (const lt::file_index_t nativeIndex : files.file_range()) + files.rename_file(nativeIndex, files.file_path(nativeIndex)); } files.set_name(""); @@ -501,8 +513,8 @@ void TorrentInfo::addRootFolder() const std::string rootPrefix = Utils::Fs::toNativePath(rootFolder + QLatin1Char {'/'}).toStdString(); lt::file_storage files = m_nativeInfo->files(); files.set_name(rootFolder.toStdString()); - for (int i = 0; i < files.num_files(); ++i) - files.rename_file(lt::file_index_t {i}, rootPrefix + files.file_path(lt::file_index_t {i})); + for (const lt::file_index_t nativeIndex : files.file_range()) + files.rename_file(nativeIndex, rootPrefix + files.file_path(nativeIndex)); m_nativeInfo->remap_files(files); } @@ -523,3 +535,8 @@ std::shared_ptr TorrentInfo::nativeInfo() const { return m_nativeInfo; } + +QVector TorrentInfo::nativeIndexes() const +{ + return m_nativeIndexes; +} diff --git a/src/base/bittorrent/torrentinfo.h b/src/base/bittorrent/torrentinfo.h index 781d81952..7aa1cb560 100644 --- a/src/base/bittorrent/torrentinfo.h +++ b/src/base/bittorrent/torrentinfo.h @@ -75,7 +75,6 @@ namespace BitTorrent int piecesCount() const; QString filePath(int index) const override; QStringList filePaths() const; - QString fileName(int index) const override; QString origFilePath(int index) const; qlonglong fileSize(int index) const override; qlonglong fileOffset(int index) const; @@ -99,6 +98,7 @@ namespace BitTorrent void setContentLayout(TorrentContentLayout layout); std::shared_ptr nativeInfo() const; + QVector nativeIndexes() const; private: // returns file index or -1 if fileName is not found @@ -108,6 +108,10 @@ namespace BitTorrent TorrentContentLayout defaultContentLayout() const; std::shared_ptr m_nativeInfo; + + // internal indexes of files (payload only, excluding any .pad files) + // by which they are addressed in libtorrent + QVector m_nativeIndexes; }; } diff --git a/src/gui/addnewtorrentdialog.cpp b/src/gui/addnewtorrentdialog.cpp index c9d466313..5dfebf114 100644 --- a/src/gui/addnewtorrentdialog.cpp +++ b/src/gui/addnewtorrentdialog.cpp @@ -407,8 +407,10 @@ void AddNewTorrentDialog::updateDiskSpaceLabel() const QVector priorities = m_contentModel->model()->getFilePriorities(); Q_ASSERT(priorities.size() == m_torrentInfo.filesCount()); for (int i = 0; i < priorities.size(); ++i) + { if (priorities[i] > BitTorrent::DownloadPriority::Ignored) torrentSize += m_torrentInfo.fileSize(i); + } } else { diff --git a/src/gui/previewselectdialog.cpp b/src/gui/previewselectdialog.cpp index 73f326edb..78bcafa61 100644 --- a/src/gui/previewselectdialog.cpp +++ b/src/gui/previewselectdialog.cpp @@ -85,10 +85,9 @@ PreviewSelectDialog::PreviewSelectDialog(QWidget *parent, const BitTorrent::Torr m_ui->previewList->setAlternatingRowColors(pref->useAlternatingRowColors()); // Fill list in const QVector fp = torrent->filesProgress(); - int nbFiles = torrent->filesCount(); - for (int i = 0; i < nbFiles; ++i) + for (int i = 0; i <= torrent->filesCount(); ++i) { - QString fileName = torrent->fileName(i); + QString fileName = Utils::Fs::fileName(torrent->filePath(i)); if (fileName.endsWith(QB_EXT)) fileName.chop(QB_EXT.length()); if (Utils::Misc::isPreviewable(fileName)) diff --git a/src/gui/torrentcontentmodel.cpp b/src/gui/torrentcontentmodel.cpp index 71a3374f7..d99064eb0 100644 --- a/src/gui/torrentcontentmodel.cpp +++ b/src/gui/torrentcontentmodel.cpp @@ -283,7 +283,7 @@ bool TorrentContentModel::setData(const QModelIndex &index, const QVariant &valu if ((index.column() == TorrentContentModelItem::COL_NAME) && (role == Qt::CheckStateRole)) { auto *item = static_cast(index.internalPointer()); - qDebug("setData(%s, %d", qUtf8Printable(item->name()), value.toInt()); + qDebug("setData(%s, %d)", qUtf8Printable(item->name()), value.toInt()); if (static_cast(item->priority()) != value.toInt()) { BitTorrent::DownloadPriority prio = BitTorrent::DownloadPriority::Normal; @@ -349,7 +349,7 @@ QVariant TorrentContentModel::data(const QModelIndex &index, const int role) con switch (role) { case Qt::DecorationRole: - { + { if (index.column() != TorrentContentModelItem::COL_NAME) return {}; @@ -358,7 +358,7 @@ QVariant TorrentContentModel::data(const QModelIndex &index, const int role) con return m_fileIconProvider->icon(QFileInfo(item->name())); } case Qt::CheckStateRole: - { + { if (index.column() != TorrentContentModelItem::COL_NAME) return {}; @@ -519,7 +519,8 @@ void TorrentContentModel::setupModelData(const BitTorrent::TorrentInfo &info) currentParent = newParent; } // Actually create the file - TorrentContentModelFile *fileItem = new TorrentContentModelFile(info.fileName(i), info.fileSize(i), currentParent, i); + TorrentContentModelFile *fileItem = new TorrentContentModelFile( + Utils::Fs::fileName(info.filePath(i)), info.fileSize(i), currentParent, i); currentParent->appendChild(fileItem); m_filesIndex.push_back(fileItem); } diff --git a/src/gui/torrentcontentmodelfolder.cpp b/src/gui/torrentcontentmodelfolder.cpp index f0b073301..725abc659 100644 --- a/src/gui/torrentcontentmodelfolder.cpp +++ b/src/gui/torrentcontentmodelfolder.cpp @@ -139,8 +139,10 @@ void TorrentContentModelFolder::setPriority(BitTorrent::DownloadPriority newPrio // Update children if (m_priority != BitTorrent::DownloadPriority::Mixed) + { for (TorrentContentModelItem *child : asConst(m_childItems)) child->setPriority(m_priority, false); + } } void TorrentContentModelFolder::recalculateProgress() diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index e42a144c9..4592ca413 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -91,9 +91,9 @@ namespace if (!torrent->hasMetadata()) return false; - for (int i = 0; i < torrent->filesCount(); ++i) + for (const QString &filePath : asConst(torrent->filePaths())) { - QString fileName = torrent->fileName(i); + QString fileName = Utils::Fs::fileName(filePath); if (fileName.endsWith(QB_EXT)) fileName.chop(QB_EXT.length()); if (Utils::Misc::isPreviewable(fileName))