Browse Source

Improve torrent content handling

Hide .pad files.

PR #15468.
adaptive-webui-19844
Vladimir Golovnev 3 years ago committed by GitHub
parent
commit
bc71827c01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/base/CMakeLists.txt
  2. 2
      src/base/base.pri
  3. 1
      src/base/bittorrent/abstractfilestorage.h
  4. 23
      src/base/bittorrent/lttypecast.h
  5. 56
      src/base/bittorrent/session.cpp
  6. 2
      src/base/bittorrent/torrent.h
  7. 4
      src/base/bittorrent/torrentcreatorthread.cpp
  8. 134
      src/base/bittorrent/torrentimpl.cpp
  9. 5
      src/base/bittorrent/torrentimpl.h
  10. 59
      src/base/bittorrent/torrentinfo.cpp
  11. 6
      src/base/bittorrent/torrentinfo.h
  12. 2
      src/gui/addnewtorrentdialog.cpp
  13. 5
      src/gui/previewselectdialog.cpp
  14. 5
      src/gui/torrentcontentmodel.cpp
  15. 2
      src/gui/torrentcontentmodelfolder.cpp
  16. 4
      src/gui/transferlistwidget.cpp

2
src/base/CMakeLists.txt

@ -17,7 +17,7 @@ add_library(qbt_base STATIC
bittorrent/infohash.h bittorrent/infohash.h
bittorrent/loadtorrentparams.h bittorrent/loadtorrentparams.h
bittorrent/ltqhash.h bittorrent/ltqhash.h
bittorrent/ltunderlyingtype.h bittorrent/lttypecast.h
bittorrent/magneturi.h bittorrent/magneturi.h
bittorrent/nativesessionextension.h bittorrent/nativesessionextension.h
bittorrent/nativetorrentextension.h bittorrent/nativetorrentextension.h

2
src/base/base.pri

@ -16,7 +16,7 @@ HEADERS += \
$$PWD/bittorrent/infohash.h \ $$PWD/bittorrent/infohash.h \
$$PWD/bittorrent/loadtorrentparams.h \ $$PWD/bittorrent/loadtorrentparams.h \
$$PWD/bittorrent/ltqhash.h \ $$PWD/bittorrent/ltqhash.h \
$$PWD/bittorrent/ltunderlyingtype.h \ $$PWD/bittorrent/lttypecast.h \
$$PWD/bittorrent/magneturi.h \ $$PWD/bittorrent/magneturi.h \
$$PWD/bittorrent/nativesessionextension.h \ $$PWD/bittorrent/nativesessionextension.h \
$$PWD/bittorrent/nativetorrentextension.h \ $$PWD/bittorrent/nativetorrentextension.h \

1
src/base/bittorrent/abstractfilestorage.h

@ -44,7 +44,6 @@ namespace BitTorrent
virtual int filesCount() const = 0; virtual int filesCount() const = 0;
virtual QString filePath(int index) const = 0; virtual QString filePath(int index) const = 0;
virtual QString fileName(int index) const = 0;
virtual qlonglong fileSize(int index) const = 0; virtual qlonglong fileSize(int index) const = 0;
virtual void renameFile(int index, const QString &name) = 0; virtual void renameFile(int index, const QString &name) = 0;

23
src/base/bittorrent/ltunderlyingtype.h → src/base/bittorrent/lttypecast.h

@ -1,6 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2019 Vladimir Golovnev <glassez@yandex.ru> * Copyright (C) 2019, 2021 Vladimir Golovnev <glassez@yandex.ru>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -28,8 +28,25 @@
#pragma once #pragma once
template <typename T> #include <libtorrent/download_priority.hpp>
typename T::underlying_type toLTUnderlyingType(const T &t)
#include "downloadpriority.h"
namespace BitTorrent::LT
{ {
template <typename T>
constexpr typename T::underlying_type toUnderlyingType(const T &t) noexcept
{
return static_cast<typename T::underlying_type>(t); return static_cast<typename T::underlying_type>(t);
}
constexpr lt::download_priority_t toNative(const DownloadPriority priority) noexcept
{
return static_cast<lt::download_priority_t>(static_cast<lt::download_priority_t::underlying_type>(priority));
}
constexpr DownloadPriority fromNative(const lt::download_priority_t priority) noexcept
{
return static_cast<DownloadPriority>(toUnderlyingType(priority));
}
} }

56
src/base/bittorrent/session.cpp

@ -93,7 +93,7 @@
#include "filesearcher.h" #include "filesearcher.h"
#include "filterparserthread.h" #include "filterparserthread.h"
#include "loadtorrentparams.h" #include "loadtorrentparams.h"
#include "ltunderlyingtype.h" #include "lttypecast.h"
#include "magneturi.h" #include "magneturi.h"
#include "nativesessionextension.h" #include "nativesessionextension.h"
#include "portforwarderimpl.h" #include "portforwarderimpl.h"
@ -1491,7 +1491,7 @@ void Session::configurePeerClasses()
// Proactively do the same for 0.0.0.0 and address_v4::any() // Proactively do the same for 0.0.0.0 and address_v4::any()
f.add_rule(lt::address_v4::any() f.add_rule(lt::address_v4::any()
, lt::address_v4::broadcast() , 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 // IPv6 may not be available on OS and the parsing
// would result in an exception -> abnormal program termination // would result in an exception -> abnormal program termination
@ -1500,7 +1500,7 @@ void Session::configurePeerClasses()
{ {
f.add_rule(lt::address_v6::any() f.add_rule(lt::address_v6::any()
, lt::make_address("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") , 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 &) {} catch (const std::exception &) {}
@ -1509,21 +1509,21 @@ void Session::configurePeerClasses()
// local networks // local networks
f.add_rule(lt::make_address("10.0.0.0") f.add_rule(lt::make_address("10.0.0.0")
, lt::make_address("10.255.255.255") , 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") f.add_rule(lt::make_address("172.16.0.0")
, lt::make_address("172.31.255.255") , 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") f.add_rule(lt::make_address("192.168.0.0")
, lt::make_address("192.168.255.255") , 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 // link local
f.add_rule(lt::make_address("169.254.0.0") f.add_rule(lt::make_address("169.254.0.0")
, lt::make_address("169.254.255.255") , 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 // loopback
f.add_rule(lt::make_address("127.0.0.0") f.add_rule(lt::make_address("127.0.0.0")
, lt::make_address("127.255.255.255") , 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 // IPv6 may not be available on OS and the parsing
// would result in an exception -> abnormal program termination // would result in an exception -> abnormal program termination
@ -1533,15 +1533,15 @@ void Session::configurePeerClasses()
// link local // link local
f.add_rule(lt::make_address("fe80::") f.add_rule(lt::make_address("fe80::")
, lt::make_address("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff") , 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 // unique local addresses
f.add_rule(lt::make_address("fc00::") f.add_rule(lt::make_address("fc00::")
, lt::make_address("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") , 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 // loopback
f.add_rule(lt::address_v6::loopback() f.add_rule(lt::address_v6::loopback()
, 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 &) {} 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; lt::add_torrent_params &p = params.ltAddTorrentParams;
p.save_path = Utils::Fs::toNativePath(savePath).toStdString(); 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) 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); loadTorrent(params);
} }
@ -2141,28 +2143,20 @@ bool Session::addTorrent_impl(const std::variant<MagnetUri, TorrentInfo> &source
{ {
QString contentName = metadata.rootFolder(); QString contentName = metadata.rootFolder();
if (contentName.isEmpty() && (metadata.filesCount() == 1)) if (contentName.isEmpty() && (metadata.filesCount() == 1))
contentName = metadata.fileName(0); contentName = Utils::Fs::fileName(metadata.filePath(0));
if (!contentName.isEmpty() && (contentName != metadata.name())) if (!contentName.isEmpty() && (contentName != metadata.name()))
loadTorrentParams.name = contentName; loadTorrentParams.name = contentName;
} }
Q_ASSERT(p.file_priorities.empty()); Q_ASSERT(p.file_priorities.empty());
if (addTorrentParams.filePriorities.empty()) const int internalFilesCount = metadata.nativeInfo()->files().num_files(); // including .pad files
{
// Use qBittorrent default priority rather than libtorrent's (4) // Use qBittorrent default priority rather than libtorrent's (4)
p.file_priorities = std::vector(metadata.filesCount(), static_cast<lt::download_priority_t>( p.file_priorities = std::vector(internalFilesCount, LT::toNative(DownloadPriority::Normal));
static_cast<lt::download_priority_t::underlying_type>(DownloadPriority::Normal))); const auto nativeIndexes = metadata.nativeIndexes();
} Q_ASSERT(addTorrentParams.filePriorities.isEmpty() || (addTorrentParams.filePriorities.size() == nativeIndexes.size()));
else for (int i = 0; i < addTorrentParams.filePriorities.size(); ++i)
{ p.file_priorities[LT::toUnderlyingType(nativeIndexes[i])] = LT::toNative(addTorrentParams.filePriorities[i]);
std::transform(addTorrentParams.filePriorities.cbegin(), addTorrentParams.filePriorities.cend()
, std::back_inserter(p.file_priorities), [](const DownloadPriority priority)
{
return static_cast<lt::download_priority_t>(
static_cast<lt::download_priority_t::underlying_type>(priority));
});
}
p.ti = metadata.nativeInfo(); p.ti = metadata.nativeInfo();
} }
@ -2388,7 +2382,7 @@ void Session::saveTorrentsQueue() const
for (const TorrentImpl *torrent : asConst(m_torrents)) for (const TorrentImpl *torrent : asConst(m_torrents))
{ {
// We require actual (non-cached) queue position here! // 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 >= 0)
{ {
if (queuePos >= queue.size()) if (queuePos >= queue.size())
@ -3937,9 +3931,8 @@ void Session::handleTorrentFinished(TorrentImpl *const torrent)
qDebug("Checking if the torrent contains torrent files to download"); qDebug("Checking if the torrent contains torrent files to download");
// Check if there are torrent files inside // 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)) if (torrentRelpath.endsWith(".torrent", Qt::CaseInsensitive))
{ {
qDebug("Found possible recursive torrent download."); qDebug("Found possible recursive torrent download.");
@ -4162,9 +4155,8 @@ void Session::recursiveTorrentDownload(const TorrentID &id)
TorrentImpl *const torrent = m_torrents.value(id); TorrentImpl *const torrent = m_torrents.value(id);
if (!torrent) return; 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")) if (torrentRelpath.endsWith(".torrent"))
{ {
LogMsg(tr("Recursive download of file '%1' embedded in torrent '%2'" LogMsg(tr("Recursive download of file '%1' embedded in torrent '%2'"

2
src/base/bittorrent/torrent.h

@ -193,6 +193,7 @@ namespace BitTorrent
virtual qreal ratioLimit() const = 0; virtual qreal ratioLimit() const = 0;
virtual int seedingTimeLimit() const = 0; virtual int seedingTimeLimit() const = 0;
virtual QStringList filePaths() const = 0;
virtual QStringList absoluteFilePaths() const = 0; virtual QStringList absoluteFilePaths() const = 0;
virtual QVector<DownloadPriority> filePriorities() const = 0; virtual QVector<DownloadPriority> filePriorities() const = 0;
@ -214,7 +215,6 @@ namespace BitTorrent
virtual bool hasMetadata() const = 0; virtual bool hasMetadata() const = 0;
virtual bool hasMissingFiles() const = 0; virtual bool hasMissingFiles() const = 0;
virtual bool hasError() const = 0; virtual bool hasError() const = 0;
virtual bool hasFilteredPieces() const = 0;
virtual int queuePosition() const = 0; virtual int queuePosition() const = 0;
virtual QVector<TrackerEntry> trackers() const = 0; virtual QVector<TrackerEntry> trackers() const = 0;
virtual QVector<QUrl> urlSeeds() const = 0; virtual QVector<QUrl> urlSeeds() const = 0;

4
src/base/bittorrent/torrentcreatorthread.cpp

@ -44,7 +44,7 @@
#include "base/utils/fs.h" #include "base/utils/fs.h"
#include "base/utils/io.h" #include "base/utils/io.h"
#include "base/version.h" #include "base/version.h"
#include "ltunderlyingtype.h" #include "lttypecast.h"
namespace namespace
{ {
@ -186,7 +186,7 @@ void TorrentCreatorThread::run()
, [this, &newTorrent](const lt::piece_index_t n) , [this, &newTorrent](const lt::piece_index_t n)
{ {
checkInterruptionRequested(); checkInterruptionRequested();
sendProgressSignal(toLTUnderlyingType(n), newTorrent.num_pieces()); sendProgressSignal(LT::toUnderlyingType(n), newTorrent.num_pieces());
}); });
// Set qBittorrent as creator and add user comment to // Set qBittorrent as creator and add user comment to

134
src/base/bittorrent/torrentimpl.cpp

@ -64,7 +64,7 @@
#include "downloadpriority.h" #include "downloadpriority.h"
#include "loadtorrentparams.h" #include "loadtorrentparams.h"
#include "ltqhash.h" #include "ltqhash.h"
#include "ltunderlyingtype.h" #include "lttypecast.h"
#include "peeraddress.h" #include "peeraddress.h"
#include "peerinfo.h" #include "peerinfo.h"
#include "session.h" #include "session.h"
@ -74,20 +74,6 @@ using namespace BitTorrent;
namespace namespace
{ {
std::vector<lt::download_priority_t> toLTDownloadPriorities(const QVector<DownloadPriority> &priorities)
{
std::vector<lt::download_priority_t> out;
out.reserve(priorities.size());
std::transform(priorities.cbegin(), priorities.cend()
, std::back_inserter(out), [](const DownloadPriority priority)
{
return static_cast<lt::download_priority_t>(
static_cast<lt::download_priority_t::underlying_type>(priority));
});
return out;
}
lt::announce_entry makeNativeAnnouncerEntry(const QString &url, const int tier) lt::announce_entry makeNativeAnnouncerEntry(const QString &url, const int tier)
{ {
lt::announce_entry entry {url.toStdString()}; lt::announce_entry entry {url.toStdString()};
@ -762,20 +748,19 @@ int TorrentImpl::seedingTimeLimit() const
return m_seedingTimeLimit; return m_seedingTimeLimit;
} }
QString TorrentImpl::filePath(int index) const QString TorrentImpl::filePath(const int index) const
{ {
return m_torrentInfo.filePath(index); return m_torrentInfo.filePath(index);
} }
QString TorrentImpl::fileName(int index) const qlonglong TorrentImpl::fileSize(const int index) const
{ {
if (!hasMetadata()) return {}; return m_torrentInfo.fileSize(index);
return Utils::Fs::fileName(filePath(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 // Return a list of absolute paths corresponding
@ -784,8 +769,9 @@ QStringList TorrentImpl::absoluteFilePaths() const
{ {
if (!hasMetadata()) return {}; if (!hasMetadata()) return {};
const QDir saveDir(savePath(true)); const QDir saveDir {savePath(true)};
QStringList res; QStringList res;
res.reserve(filesCount());
for (int i = 0; i < filesCount(); ++i) for (int i = 0; i < filesCount(); ++i)
res << Utils::Fs::expandPathAbs(saveDir.absoluteFilePath(filePath(i))); res << Utils::Fs::expandPathAbs(saveDir.absoluteFilePath(filePath(i)));
return res; return res;
@ -793,14 +779,19 @@ QStringList TorrentImpl::absoluteFilePaths() const
QVector<DownloadPriority> TorrentImpl::filePriorities() const QVector<DownloadPriority> TorrentImpl::filePriorities() const
{ {
if (!hasMetadata())
return {};
const std::vector<lt::download_priority_t> fp = m_nativeHandle.get_file_priorities(); const std::vector<lt::download_priority_t> fp = m_nativeHandle.get_file_priorities();
QVector<DownloadPriority> ret; QVector<DownloadPriority> ret;
ret.reserve(static_cast<decltype(ret)::size_type>(fp.size())); ret.reserve(filesCount());
std::transform(fp.cbegin(), fp.cend(), std::back_inserter(ret), [](const lt::download_priority_t priority) for (const lt::file_index_t nativeIndex : asConst(m_torrentInfo.nativeIndexes()))
{ {
return static_cast<DownloadPriority>(toLTUnderlyingType(priority)); const auto priority = LT::fromNative(fp[LT::toUnderlyingType(nativeIndex)]);
}); ret.append(priority);
}
return ret; return ret;
} }
@ -987,15 +978,6 @@ bool TorrentImpl::hasError() const
return (m_nativeStatus.errc || (m_nativeStatus.flags & lt::torrent_flags::upload_mode)); return (m_nativeStatus.errc || (m_nativeStatus.flags & lt::torrent_flags::upload_mode));
} }
bool TorrentImpl::hasFilteredPieces() const
{
const std::vector<lt::download_priority_t> 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 int TorrentImpl::queuePosition() const
{ {
return static_cast<int>(m_nativeStatus.queue_position); return static_cast<int>(m_nativeStatus.queue_position);
@ -1092,16 +1074,18 @@ QVector<qreal> TorrentImpl::filesProgress() const
std::vector<int64_t> fp; std::vector<int64_t> fp;
m_nativeHandle.file_progress(fp, lt::torrent_handle::piece_granularity); m_nativeHandle.file_progress(fp, lt::torrent_handle::piece_granularity);
const int count = static_cast<int>(fp.size()); const int count = filesCount();
const auto nativeIndexes = m_torrentInfo.nativeIndexes();
QVector<qreal> result; QVector<qreal> result;
result.reserve(count); result.reserve(count);
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
{ {
const int64_t progress = fp[LT::toUnderlyingType(nativeIndexes[i])];
const qlonglong size = fileSize(i); const qlonglong size = fileSize(i);
if ((size <= 0) || (fp[i] == size)) if ((size <= 0) || (progress == size))
result << 1; result << 1;
else else
result << (fp[i] / static_cast<qreal>(size)); result << (progress / static_cast<qreal>(size));
} }
return result; return result;
@ -1252,7 +1236,7 @@ QBitArray TorrentImpl::downloadingPieces() const
m_nativeHandle.get_download_queue(queue); m_nativeHandle.get_download_queue(queue);
for (const lt::partial_piece_info &info : queue) for (const lt::partial_piece_info &info : queue)
result.setBit(toLTUnderlyingType(info.piece_index)); result.setBit(LT::toUnderlyingType(info.piece_index));
return result; 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); 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 // Download first and last pieces first for every file in the torrent
const std::vector<lt::download_priority_t> filePriorities = !updatedFilePrio.isEmpty() ? toLTDownloadPriorities(updatedFilePrio) const QVector<DownloadPriority> filePriorities =
: nativeHandle().get_file_priorities(); !updatedFilePrio.isEmpty() ? updatedFilePrio : this->filePriorities();
std::vector<lt::download_priority_t> piecePriorities = nativeHandle().get_piece_priorities(); std::vector<lt::download_priority_t> piecePriorities = nativeHandle().get_piece_priorities();
// Updating file priorities is an async operation in libtorrent, when we just updated it and immediately query it // 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. // we might get the old/wrong values, so we rely on `updatedFilePrio` in this case.
for (int index = 0; index < static_cast<int>(filePriorities.size()); ++index) for (int index = 0; index < filePriorities.size(); ++index)
{ {
const lt::download_priority_t filePrio = filePriorities[index]; const DownloadPriority filePrio = filePriorities[index];
if (filePrio <= lt::download_priority_t {0}) if (filePrio <= DownloadPriority::Ignored)
continue; continue;
// Determine the priority to set // 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<lt::download_priority_t>(static_cast<int>(newPrio));
const TorrentInfo::PieceRange extremities = info().filePieces(index); const TorrentInfo::PieceRange extremities = info().filePieces(index);
// worst case: AVI index = 1% of total file size (at the end of the file) // 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()); const int nNumPieces = std::ceil(fileSize(index) * 0.01 / pieceLength());
for (int i = 0; i < nNumPieces; ++i) for (int i = 0; i < nNumPieces; ++i)
{ {
piecePriorities[extremities.first() + i] = newPrio; piecePriorities[extremities.first() + i] = piecePrio;
piecePriorities[extremities.last() - i] = newPrio; piecePriorities[extremities.last() - i] = piecePrio;
} }
} }
@ -1506,14 +1491,16 @@ void TorrentImpl::endReceivedMetadataHandling(const QString &savePath, const QSt
{ {
lt::add_torrent_params &p = m_ltAddTorrentParams; lt::add_torrent_params &p = m_ltAddTorrentParams;
p.ti = std::const_pointer_cast<lt::torrent_info>(m_nativeHandle.torrent_file()); const TorrentInfo torrentInfo {m_nativeHandle.torrent_file()};
const auto nativeIndexes = torrentInfo.nativeIndexes();
for (int i = 0; i < fileNames.size(); ++i) 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.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) // Use qBittorrent default priority rather than libtorrent's (4)
p.file_priorities = std::vector(fileNames.size(), static_cast<lt::download_priority_t>( p.file_priorities = std::vector(internalFilesCount, LT::toNative(DownloadPriority::Normal));
static_cast<lt::download_priority_t::underlying_type>(DownloadPriority::Normal)));
reload(); reload();
@ -1630,10 +1617,10 @@ void TorrentImpl::renameFile(const int index, const QString &path)
{ {
#ifndef QBT_USES_LIBTORRENT2 #ifndef QBT_USES_LIBTORRENT2
const QString oldPath = filePath(index); const QString oldPath = filePath(index);
m_oldPath[lt::file_index_t {index}].push_back(oldPath); m_oldPath[index].push_back(oldPath);
#endif #endif
++m_renameCount; ++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) 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) 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 // Remove empty leftover folders
// For example renaming "a/b/c" to "d/b/c", then folders "a/b" and "a" will // For example renaming "a/b/c" to "d/b/c", then folders "a/b" and "a" will
// be removed if they are empty // be removed if they are empty
#ifndef QBT_USES_LIBTORRENT2 #ifndef QBT_USES_LIBTORRENT2
const QString oldFilePath = m_oldPath[p->index].takeFirst(); const QString oldFilePath = m_oldPath[fileIndex].takeFirst();
if (m_oldPath[p->index].isEmpty()) if (m_oldPath[fileIndex].isEmpty())
m_oldPath.remove(p->index); m_oldPath.remove(fileIndex);
#else #else
const QString oldFilePath = Utils::Fs::toUniformPath(p->old_name()); const QString oldFilePath = Utils::Fs::toUniformPath(p->old_name());
#endif #endif
@ -1910,14 +1900,16 @@ void TorrentImpl::handleFileRenamedAlert(const lt::file_renamed_alert *p)
void TorrentImpl::handleFileRenameFailedAlert(const lt::file_rename_failed_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\"") LogMsg(tr("File rename failed. Torrent: \"%1\", file: \"%2\", reason: \"%3\"")
.arg(name(), filePath(toLTUnderlyingType(p->index)) .arg(name(), filePath(fileIndex), QString::fromLocal8Bit(p->error.message().c_str())), Log::WARNING);
, QString::fromLocal8Bit(p->error.message().c_str())), Log::WARNING);
#ifndef QBT_USES_LIBTORRENT2 #ifndef QBT_USES_LIBTORRENT2
m_oldPath[p->index].removeFirst(); m_oldPath[fileIndex].removeFirst();
if (m_oldPath[p->index].isEmpty()) if (m_oldPath[fileIndex].isEmpty())
m_oldPath.remove(p->index); m_oldPath.remove(fileIndex);
#endif #endif
--m_renameCount; --m_renameCount;
@ -1929,16 +1921,19 @@ void TorrentImpl::handleFileRenameFailedAlert(const lt::file_rename_failed_alert
void TorrentImpl::handleFileCompletedAlert(const lt::file_completed_alert *p) 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())); qDebug("A file completed download in torrent \"%s\"", qUtf8Printable(name()));
if (m_session->isAppendExtensionEnabled()) if (m_session->isAppendExtensionEnabled())
{ {
QString name = filePath(toLTUnderlyingType(p->index)); QString name = filePath(fileIndex);
if (name.endsWith(QB_EXT)) if (name.endsWith(QB_EXT))
{ {
const QString oldName = name; const QString oldName = name;
name.chop(QB_EXT.size()); name.chop(QB_EXT.size());
qDebug("Renaming %s to %s", qUtf8Printable(oldName), qUtf8Printable(name)); 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<DownloadPriority> &priorities) void TorrentImpl::prioritizeFiles(const QVector<DownloadPriority> &priorities)
{ {
if (!hasMetadata()) return; if (!hasMetadata()) return;
if (priorities.size() != filesCount()) return;
Q_ASSERT(priorities.size() == filesCount());
// Reset 'm_hasSeedStatus' if needed in order to react again to // Reset 'm_hasSeedStatus' if needed in order to react again to
// 'torrent_finished_alert' and eg show tray notifications // 'torrent_finished_alert' and eg show tray notifications
@ -2288,8 +2284,14 @@ void TorrentImpl::prioritizeFiles(const QVector<DownloadPriority> &priorities)
} }
} }
const int internalFilesCount = m_torrentInfo.nativeInfo()->files().num_files(); // including .pad files
auto nativePriorities = std::vector<lt::download_priority_t>(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..."; 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 // Restore first/last piece first option if necessary
if (m_hasFirstLastPiecePriority) if (m_hasFirstLastPiecePriority)

5
src/base/bittorrent/torrentimpl.h

@ -126,8 +126,8 @@ namespace BitTorrent
int seedingTimeLimit() const override; int seedingTimeLimit() const override;
QString filePath(int index) const override; QString filePath(int index) const override;
QString fileName(int index) const override;
qlonglong fileSize(int index) const override; qlonglong fileSize(int index) const override;
QStringList filePaths() const override;
QStringList absoluteFilePaths() const override; QStringList absoluteFilePaths() const override;
QVector<DownloadPriority> filePriorities() const override; QVector<DownloadPriority> filePriorities() const override;
@ -149,7 +149,6 @@ namespace BitTorrent
bool hasMetadata() const override; bool hasMetadata() const override;
bool hasMissingFiles() const override; bool hasMissingFiles() const override;
bool hasError() const override; bool hasError() const override;
bool hasFilteredPieces() const override;
int queuePosition() const override; int queuePosition() const override;
QVector<TrackerEntry> trackers() const override; QVector<TrackerEntry> trackers() const override;
QVector<QUrl> urlSeeds() const override; QVector<QUrl> urlSeeds() const override;
@ -305,7 +304,7 @@ namespace BitTorrent
#ifndef QBT_USES_LIBTORRENT2 #ifndef QBT_USES_LIBTORRENT2
// Until libtorrent provided an "old_name" field in `file_renamed_alert` // Until libtorrent provided an "old_name" field in `file_renamed_alert`
// we relied on this workaround to remove empty leftover folders // we relied on this workaround to remove empty leftover folders
QHash<lt::file_index_t, QVector<QString>> m_oldPath; QHash<int, QVector<QString>> m_oldPath;
#endif #endif
QHash<QString, QMap<lt::tcp::endpoint, int>> m_trackerPeerCounts; QHash<QString, QMap<lt::tcp::endpoint, int>> m_trackerPeerCounts;

59
src/base/bittorrent/torrentinfo.cpp

@ -76,12 +76,23 @@ namespace
const int torrentInfoId = qRegisterMetaType<TorrentInfo>(); const int torrentInfoId = qRegisterMetaType<TorrentInfo>();
TorrentInfo::TorrentInfo(std::shared_ptr<const lt::torrent_info> nativeInfo) TorrentInfo::TorrentInfo(std::shared_ptr<const lt::torrent_info> nativeInfo)
: m_nativeInfo {std::const_pointer_cast<lt::torrent_info>(nativeInfo)}
{ {
m_nativeInfo = std::const_pointer_cast<lt::torrent_info>(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) 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) if (this != &other)
{ {
m_nativeInfo = other.m_nativeInfo; m_nativeInfo = other.m_nativeInfo;
m_nativeIndexes = other.m_nativeIndexes;
} }
return *this; return *this;
} }
@ -241,7 +253,7 @@ qlonglong TorrentInfo::totalSize() const
int TorrentInfo::filesCount() const int TorrentInfo::filesCount() const
{ {
if (!isValid()) return -1; if (!isValid()) return -1;
return m_nativeInfo->num_files(); return m_nativeIndexes.size();
} }
int TorrentInfo::pieceLength() const int TorrentInfo::pieceLength() const
@ -266,40 +278,36 @@ QString TorrentInfo::filePath(const int index) const
{ {
if (!isValid()) return {}; if (!isValid()) return {};
return Utils::Fs::toUniformPath( 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 TorrentInfo::filePaths() const
{ {
QStringList list; QStringList list;
list.reserve(filesCount());
for (int i = 0; i < filesCount(); ++i) for (int i = 0; i < filesCount(); ++i)
list << filePath(i); list << filePath(i);
return list; return list;
} }
QString TorrentInfo::fileName(const int index) const
{
return Utils::Fs::fileName(filePath(index));
}
QString TorrentInfo::origFilePath(const int index) const QString TorrentInfo::origFilePath(const int index) const
{ {
if (!isValid()) return {}; if (!isValid()) return {};
return Utils::Fs::toUniformPath( 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 qlonglong TorrentInfo::fileSize(const int index) const
{ {
if (!isValid()) return -1; 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 qlonglong TorrentInfo::fileOffset(const int index) const
{ {
if (!isValid()) return -1; 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<TrackerEntry> TorrentInfo::trackers() const QVector<TrackerEntry> TorrentInfo::trackers() const
@ -368,8 +376,12 @@ QVector<int> TorrentInfo::fileIndicesForPiece(const int pieceIndex) const
lt::piece_index_t {pieceIndex}, 0, nativeInfo()->piece_size(lt::piece_index_t {pieceIndex})); lt::piece_index_t {pieceIndex}, 0, nativeInfo()->piece_size(lt::piece_index_t {pieceIndex}));
QVector<int> res; QVector<int> res;
res.reserve(static_cast<decltype(res)::size_type>(files.size())); res.reserve(static_cast<decltype(res)::size_type>(files.size()));
std::transform(files.begin(), files.end(), std::back_inserter(res), for (const lt::file_slice &fileSlice : files)
[](const lt::file_slice &s) { return static_cast<int>(s.file_index); }); {
const int index = m_nativeIndexes.indexOf(fileSlice.file_index);
if (index >= 0)
res.append(index);
}
return res; return res;
} }
@ -415,8 +427,8 @@ TorrentInfo::PieceRange TorrentInfo::filePieces(const int fileIndex) const
} }
const lt::file_storage &files = nativeInfo()->files(); const lt::file_storage &files = nativeInfo()->files();
const auto fileSize = files.file_size(lt::file_index_t {fileIndex}); const auto fileSize = files.file_size(m_nativeIndexes[fileIndex]);
const auto fileOffset = files.file_offset(lt::file_index_t {fileIndex}); const auto fileOffset = files.file_offset(m_nativeIndexes[fileIndex]);
const int beginIdx = (fileOffset / pieceLength()); const int beginIdx = (fileOffset / pieceLength());
const int endIdx = ((fileOffset + fileSize - 1) / 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) void TorrentInfo::renameFile(const int index, const QString &newPath)
{ {
if (!isValid()) return; 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 int TorrentInfo::fileIndex(const QString &fileName) const
@ -481,8 +493,8 @@ void TorrentInfo::stripRootFolder()
if (files.name() != newName) if (files.name() != newName)
{ {
files.set_name(newName); files.set_name(newName);
for (int i = 0; i < files.num_files(); ++i) for (const lt::file_index_t nativeIndex : files.file_range())
files.rename_file(lt::file_index_t {i}, files.file_path(lt::file_index_t {i})); files.rename_file(nativeIndex, files.file_path(nativeIndex));
} }
files.set_name(""); files.set_name("");
@ -501,8 +513,8 @@ void TorrentInfo::addRootFolder()
const std::string rootPrefix = Utils::Fs::toNativePath(rootFolder + QLatin1Char {'/'}).toStdString(); const std::string rootPrefix = Utils::Fs::toNativePath(rootFolder + QLatin1Char {'/'}).toStdString();
lt::file_storage files = m_nativeInfo->files(); lt::file_storage files = m_nativeInfo->files();
files.set_name(rootFolder.toStdString()); files.set_name(rootFolder.toStdString());
for (int i = 0; i < files.num_files(); ++i) for (const lt::file_index_t nativeIndex : files.file_range())
files.rename_file(lt::file_index_t {i}, rootPrefix + files.file_path(lt::file_index_t {i})); files.rename_file(nativeIndex, rootPrefix + files.file_path(nativeIndex));
m_nativeInfo->remap_files(files); m_nativeInfo->remap_files(files);
} }
@ -523,3 +535,8 @@ std::shared_ptr<lt::torrent_info> TorrentInfo::nativeInfo() const
{ {
return m_nativeInfo; return m_nativeInfo;
} }
QVector<lt::file_index_t> TorrentInfo::nativeIndexes() const
{
return m_nativeIndexes;
}

6
src/base/bittorrent/torrentinfo.h

@ -75,7 +75,6 @@ namespace BitTorrent
int piecesCount() const; int piecesCount() const;
QString filePath(int index) const override; QString filePath(int index) const override;
QStringList filePaths() const; QStringList filePaths() const;
QString fileName(int index) const override;
QString origFilePath(int index) const; QString origFilePath(int index) const;
qlonglong fileSize(int index) const override; qlonglong fileSize(int index) const override;
qlonglong fileOffset(int index) const; qlonglong fileOffset(int index) const;
@ -99,6 +98,7 @@ namespace BitTorrent
void setContentLayout(TorrentContentLayout layout); void setContentLayout(TorrentContentLayout layout);
std::shared_ptr<lt::torrent_info> nativeInfo() const; std::shared_ptr<lt::torrent_info> nativeInfo() const;
QVector<lt::file_index_t> nativeIndexes() const;
private: private:
// returns file index or -1 if fileName is not found // returns file index or -1 if fileName is not found
@ -108,6 +108,10 @@ namespace BitTorrent
TorrentContentLayout defaultContentLayout() const; TorrentContentLayout defaultContentLayout() const;
std::shared_ptr<lt::torrent_info> m_nativeInfo; std::shared_ptr<lt::torrent_info> m_nativeInfo;
// internal indexes of files (payload only, excluding any .pad files)
// by which they are addressed in libtorrent
QVector<lt::file_index_t> m_nativeIndexes;
}; };
} }

2
src/gui/addnewtorrentdialog.cpp

@ -407,9 +407,11 @@ void AddNewTorrentDialog::updateDiskSpaceLabel()
const QVector<BitTorrent::DownloadPriority> priorities = m_contentModel->model()->getFilePriorities(); const QVector<BitTorrent::DownloadPriority> priorities = m_contentModel->model()->getFilePriorities();
Q_ASSERT(priorities.size() == m_torrentInfo.filesCount()); Q_ASSERT(priorities.size() == m_torrentInfo.filesCount());
for (int i = 0; i < priorities.size(); ++i) for (int i = 0; i < priorities.size(); ++i)
{
if (priorities[i] > BitTorrent::DownloadPriority::Ignored) if (priorities[i] > BitTorrent::DownloadPriority::Ignored)
torrentSize += m_torrentInfo.fileSize(i); torrentSize += m_torrentInfo.fileSize(i);
} }
}
else else
{ {
torrentSize = m_torrentInfo.totalSize(); torrentSize = m_torrentInfo.totalSize();

5
src/gui/previewselectdialog.cpp

@ -85,10 +85,9 @@ PreviewSelectDialog::PreviewSelectDialog(QWidget *parent, const BitTorrent::Torr
m_ui->previewList->setAlternatingRowColors(pref->useAlternatingRowColors()); m_ui->previewList->setAlternatingRowColors(pref->useAlternatingRowColors());
// Fill list in // Fill list in
const QVector<qreal> fp = torrent->filesProgress(); const QVector<qreal> fp = torrent->filesProgress();
int nbFiles = torrent->filesCount(); for (int i = 0; i <= torrent->filesCount(); ++i)
for (int i = 0; i < nbFiles; ++i)
{ {
QString fileName = torrent->fileName(i); QString fileName = Utils::Fs::fileName(torrent->filePath(i));
if (fileName.endsWith(QB_EXT)) if (fileName.endsWith(QB_EXT))
fileName.chop(QB_EXT.length()); fileName.chop(QB_EXT.length());
if (Utils::Misc::isPreviewable(fileName)) if (Utils::Misc::isPreviewable(fileName))

5
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)) if ((index.column() == TorrentContentModelItem::COL_NAME) && (role == Qt::CheckStateRole))
{ {
auto *item = static_cast<TorrentContentModelItem*>(index.internalPointer()); auto *item = static_cast<TorrentContentModelItem*>(index.internalPointer());
qDebug("setData(%s, %d", qUtf8Printable(item->name()), value.toInt()); qDebug("setData(%s, %d)", qUtf8Printable(item->name()), value.toInt());
if (static_cast<int>(item->priority()) != value.toInt()) if (static_cast<int>(item->priority()) != value.toInt())
{ {
BitTorrent::DownloadPriority prio = BitTorrent::DownloadPriority::Normal; BitTorrent::DownloadPriority prio = BitTorrent::DownloadPriority::Normal;
@ -519,7 +519,8 @@ void TorrentContentModel::setupModelData(const BitTorrent::TorrentInfo &info)
currentParent = newParent; currentParent = newParent;
} }
// Actually create the file // 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); currentParent->appendChild(fileItem);
m_filesIndex.push_back(fileItem); m_filesIndex.push_back(fileItem);
} }

2
src/gui/torrentcontentmodelfolder.cpp

@ -139,8 +139,10 @@ void TorrentContentModelFolder::setPriority(BitTorrent::DownloadPriority newPrio
// Update children // Update children
if (m_priority != BitTorrent::DownloadPriority::Mixed) if (m_priority != BitTorrent::DownloadPriority::Mixed)
{
for (TorrentContentModelItem *child : asConst(m_childItems)) for (TorrentContentModelItem *child : asConst(m_childItems))
child->setPriority(m_priority, false); child->setPriority(m_priority, false);
}
} }
void TorrentContentModelFolder::recalculateProgress() void TorrentContentModelFolder::recalculateProgress()

4
src/gui/transferlistwidget.cpp

@ -91,9 +91,9 @@ namespace
if (!torrent->hasMetadata()) if (!torrent->hasMetadata())
return false; 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)) if (fileName.endsWith(QB_EXT))
fileName.chop(QB_EXT.length()); fileName.chop(QB_EXT.length());
if (Utils::Misc::isPreviewable(fileName)) if (Utils::Misc::isPreviewable(fileName))

Loading…
Cancel
Save