|
|
|
@ -48,6 +48,7 @@
@@ -48,6 +48,7 @@
|
|
|
|
|
#include <QByteArray> |
|
|
|
|
#include <QDebug> |
|
|
|
|
#include <QFile> |
|
|
|
|
#include <QPointer> |
|
|
|
|
#include <QSet> |
|
|
|
|
#include <QStringList> |
|
|
|
|
#include <QUrl> |
|
|
|
@ -236,7 +237,7 @@ namespace
@@ -236,7 +237,7 @@ namespace
|
|
|
|
|
|
|
|
|
|
TorrentImpl::TorrentImpl(SessionImpl *session, lt::session *nativeSession |
|
|
|
|
, const lt::torrent_handle &nativeHandle, const LoadTorrentParams ¶ms) |
|
|
|
|
: QObject(session) |
|
|
|
|
: Torrent(session) |
|
|
|
|
, m_session(session) |
|
|
|
|
, m_nativeSession(nativeSession) |
|
|
|
|
, m_nativeHandle(nativeHandle) |
|
|
|
@ -620,50 +621,86 @@ QVector<QUrl> TorrentImpl::urlSeeds() const
@@ -620,50 +621,86 @@ QVector<QUrl> TorrentImpl::urlSeeds() const
|
|
|
|
|
|
|
|
|
|
void TorrentImpl::addUrlSeeds(const QVector<QUrl> &urlSeeds) |
|
|
|
|
{ |
|
|
|
|
const std::set<std::string> currentSeeds = m_nativeHandle.url_seeds(); |
|
|
|
|
m_session->invokeAsync([urlSeeds, session = m_session |
|
|
|
|
, nativeHandle = m_nativeHandle |
|
|
|
|
, thisTorrent = QPointer<TorrentImpl>(this)] |
|
|
|
|
{ |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
const std::set<std::string> nativeSeeds = nativeHandle.url_seeds(); |
|
|
|
|
QVector<QUrl> currentSeeds; |
|
|
|
|
currentSeeds.reserve(static_cast<decltype(currentSeeds)::size_type>(nativeSeeds.size())); |
|
|
|
|
for (const std::string &urlSeed : nativeSeeds) |
|
|
|
|
currentSeeds.append(QString::fromStdString(urlSeed)); |
|
|
|
|
|
|
|
|
|
QVector<QUrl> addedUrlSeeds; |
|
|
|
|
addedUrlSeeds.reserve(urlSeeds.size()); |
|
|
|
|
|
|
|
|
|
for (const QUrl &url : urlSeeds) |
|
|
|
|
{ |
|
|
|
|
const std::string nativeUrl = url.toString().toStdString(); |
|
|
|
|
if (currentSeeds.find(nativeUrl) == currentSeeds.end()) |
|
|
|
|
if (!currentSeeds.contains(url)) |
|
|
|
|
{ |
|
|
|
|
m_nativeHandle.add_url_seed(nativeUrl); |
|
|
|
|
addedUrlSeeds << url; |
|
|
|
|
nativeHandle.add_url_seed(url.toString().toStdString()); |
|
|
|
|
addedUrlSeeds.append(url); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
session->invoke([session, thisTorrent, addedUrlSeeds] |
|
|
|
|
{ |
|
|
|
|
if (!thisTorrent) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
if (!addedUrlSeeds.isEmpty()) |
|
|
|
|
{ |
|
|
|
|
m_session->handleTorrentNeedSaveResumeData(this); |
|
|
|
|
m_session->handleTorrentUrlSeedsAdded(this, addedUrlSeeds); |
|
|
|
|
session->handleTorrentNeedSaveResumeData(thisTorrent); |
|
|
|
|
session->handleTorrentUrlSeedsAdded(thisTorrent, addedUrlSeeds); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
catch (const std::exception &) {} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void TorrentImpl::removeUrlSeeds(const QVector<QUrl> &urlSeeds) |
|
|
|
|
{ |
|
|
|
|
const std::set<std::string> currentSeeds = m_nativeHandle.url_seeds(); |
|
|
|
|
m_session->invokeAsync([urlSeeds, session = m_session |
|
|
|
|
, nativeHandle = m_nativeHandle |
|
|
|
|
, thisTorrent = QPointer<TorrentImpl>(this)] |
|
|
|
|
{ |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
const std::set<std::string> nativeSeeds = nativeHandle.url_seeds(); |
|
|
|
|
QVector<QUrl> currentSeeds; |
|
|
|
|
currentSeeds.reserve(static_cast<decltype(currentSeeds)::size_type>(nativeSeeds.size())); |
|
|
|
|
for (const std::string &urlSeed : nativeSeeds) |
|
|
|
|
currentSeeds.append(QString::fromStdString(urlSeed)); |
|
|
|
|
|
|
|
|
|
QVector<QUrl> removedUrlSeeds; |
|
|
|
|
removedUrlSeeds.reserve(urlSeeds.size()); |
|
|
|
|
|
|
|
|
|
for (const QUrl &url : urlSeeds) |
|
|
|
|
{ |
|
|
|
|
const std::string nativeUrl = url.toString().toStdString(); |
|
|
|
|
if (currentSeeds.find(nativeUrl) != currentSeeds.end()) |
|
|
|
|
if (currentSeeds.contains(url)) |
|
|
|
|
{ |
|
|
|
|
m_nativeHandle.remove_url_seed(nativeUrl); |
|
|
|
|
removedUrlSeeds << url; |
|
|
|
|
nativeHandle.remove_url_seed(url.toString().toStdString()); |
|
|
|
|
removedUrlSeeds.append(url); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
session->invoke([session, thisTorrent, removedUrlSeeds] |
|
|
|
|
{ |
|
|
|
|
if (!thisTorrent) |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
if (!removedUrlSeeds.isEmpty()) |
|
|
|
|
{ |
|
|
|
|
m_session->handleTorrentNeedSaveResumeData(this); |
|
|
|
|
m_session->handleTorrentUrlSeedsRemoved(this, removedUrlSeeds); |
|
|
|
|
session->handleTorrentNeedSaveResumeData(thisTorrent); |
|
|
|
|
session->handleTorrentUrlSeedsRemoved(thisTorrent, removedUrlSeeds); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
catch (const std::exception &) {} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void TorrentImpl::clearPeers() |
|
|
|
@ -1282,7 +1319,7 @@ QVector<PeerInfo> TorrentImpl::peers() const
@@ -1282,7 +1319,7 @@ QVector<PeerInfo> TorrentImpl::peers() const
|
|
|
|
|
peers.reserve(static_cast<decltype(peers)::size_type>(nativePeers.size())); |
|
|
|
|
|
|
|
|
|
for (const lt::peer_info &peer : nativePeers) |
|
|
|
|
peers << PeerInfo(this, peer); |
|
|
|
|
peers.append(PeerInfo(peer, pieces())); |
|
|
|
|
|
|
|
|
|
return peers; |
|
|
|
|
} |
|
|
|
@ -2025,7 +2062,7 @@ void TorrentImpl::handleFileCompletedAlert(const lt::file_completed_alert *p)
@@ -2025,7 +2062,7 @@ void TorrentImpl::handleFileCompletedAlert(const lt::file_completed_alert *p)
|
|
|
|
|
const int fileIndex = m_indexMap.value(p->index, -1); |
|
|
|
|
Q_ASSERT(fileIndex >= 0); |
|
|
|
|
|
|
|
|
|
m_completedFiles[fileIndex] = true; |
|
|
|
|
m_completedFiles.setBit(fileIndex); |
|
|
|
|
|
|
|
|
|
if (m_session->isAppendExtensionEnabled()) |
|
|
|
|
{ |
|
|
|
@ -2185,17 +2222,24 @@ lt::torrent_handle TorrentImpl::nativeHandle() const
@@ -2185,17 +2222,24 @@ lt::torrent_handle TorrentImpl::nativeHandle() const
|
|
|
|
|
return m_nativeHandle; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool TorrentImpl::setMetadata(const TorrentInfo &torrentInfo) |
|
|
|
|
void TorrentImpl::setMetadata(const TorrentInfo &torrentInfo) |
|
|
|
|
{ |
|
|
|
|
if (hasMetadata()) |
|
|
|
|
return false; |
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
m_session->invokeAsync([nativeHandle = m_nativeHandle, torrentInfo] |
|
|
|
|
{ |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
#ifdef QBT_USES_LIBTORRENT2 |
|
|
|
|
return m_nativeHandle.set_metadata(torrentInfo.nativeInfo()->info_section()); |
|
|
|
|
nativeHandle.set_metadata(torrentInfo.nativeInfo()->info_section()); |
|
|
|
|
#else |
|
|
|
|
const std::shared_ptr<lt::torrent_info> nativeInfo = torrentInfo.nativeInfo(); |
|
|
|
|
return m_nativeHandle.set_metadata(lt::span<const char>(nativeInfo->metadata().get(), nativeInfo->metadata_size())); |
|
|
|
|
nativeHandle.set_metadata(lt::span<const char>(nativeInfo->metadata().get(), nativeInfo->metadata_size())); |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
catch (const std::exception &) {} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Torrent::StopCondition TorrentImpl::stopCondition() const |
|
|
|
@ -2415,6 +2459,171 @@ nonstd::expected<void, QString> TorrentImpl::exportToFile(const Path &path) cons
@@ -2415,6 +2459,171 @@ nonstd::expected<void, QString> TorrentImpl::exportToFile(const Path &path) cons
|
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void TorrentImpl::fetchPeerInfo(std::function<void (QVector<PeerInfo>)> resultHandler) const |
|
|
|
|
{ |
|
|
|
|
invokeAsync([nativeHandle = m_nativeHandle, allPieces = pieces()]() -> QVector<PeerInfo> |
|
|
|
|
{ |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
std::vector<lt::peer_info> nativePeers; |
|
|
|
|
nativeHandle.get_peer_info(nativePeers); |
|
|
|
|
QVector<PeerInfo> peers; |
|
|
|
|
peers.reserve(static_cast<decltype(peers)::size_type>(nativePeers.size())); |
|
|
|
|
for (const lt::peer_info &peer : nativePeers) |
|
|
|
|
peers.append(PeerInfo(peer, allPieces)); |
|
|
|
|
return peers; |
|
|
|
|
} |
|
|
|
|
catch (const std::exception &) {} |
|
|
|
|
|
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
, std::move(resultHandler)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void TorrentImpl::fetchURLSeeds(std::function<void (QVector<QUrl>)> resultHandler) const |
|
|
|
|
{ |
|
|
|
|
invokeAsync([nativeHandle = m_nativeHandle]() -> QVector<QUrl> |
|
|
|
|
{ |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
const std::set<std::string> currentSeeds = nativeHandle.url_seeds(); |
|
|
|
|
QVector<QUrl> urlSeeds; |
|
|
|
|
urlSeeds.reserve(static_cast<decltype(urlSeeds)::size_type>(currentSeeds.size())); |
|
|
|
|
for (const std::string &urlSeed : currentSeeds) |
|
|
|
|
urlSeeds.append(QString::fromStdString(urlSeed)); |
|
|
|
|
return urlSeeds; |
|
|
|
|
} |
|
|
|
|
catch (const std::exception &) {} |
|
|
|
|
|
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
, std::move(resultHandler)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void TorrentImpl::fetchFilesProgress(std::function<void (QVector<qreal>)> resultHandler) const |
|
|
|
|
{ |
|
|
|
|
invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo |
|
|
|
|
, completedFiles = m_completedFiles]() -> QVector<qreal> |
|
|
|
|
{ |
|
|
|
|
if (!torrentInfo.isValid()) |
|
|
|
|
return {}; |
|
|
|
|
|
|
|
|
|
const int filesCount = torrentInfo.filesCount(); |
|
|
|
|
if (completedFiles.count(true) == filesCount) |
|
|
|
|
return QVector<qreal>(filesCount, 1); |
|
|
|
|
|
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
#ifdef QBT_USES_LIBTORRENT2 |
|
|
|
|
const std::vector<int64_t> fp = nativeHandle.file_progress(lt::torrent_handle::piece_granularity); |
|
|
|
|
#else |
|
|
|
|
std::vector<int64_t> fp; |
|
|
|
|
nativeHandle.file_progress(fp, lt::torrent_handle::piece_granularity); |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
const auto nativeIndexes = torrentInfo.nativeIndexes(); |
|
|
|
|
QVector<qreal> result; |
|
|
|
|
result.reserve(filesCount); |
|
|
|
|
for (int i = 0; i < filesCount; ++i) |
|
|
|
|
{ |
|
|
|
|
const int64_t progress = fp[LT::toUnderlyingType(nativeIndexes[i])]; |
|
|
|
|
const qlonglong size = torrentInfo.fileSize(i); |
|
|
|
|
if ((size <= 0) || (progress == size)) |
|
|
|
|
result.append(1); |
|
|
|
|
else |
|
|
|
|
result.append(progress / static_cast<qreal>(size)); |
|
|
|
|
} |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
catch (const std::exception &) {} |
|
|
|
|
|
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
, std::move(resultHandler)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void TorrentImpl::fetchPieceAvailability(std::function<void (QVector<int>)> resultHandler) const |
|
|
|
|
{ |
|
|
|
|
invokeAsync([nativeHandle = m_nativeHandle]() -> QVector<int> |
|
|
|
|
{ |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
std::vector<int> piecesAvailability; |
|
|
|
|
nativeHandle.piece_availability(piecesAvailability); |
|
|
|
|
return QVector<int>(piecesAvailability.cbegin(), piecesAvailability.cend()); |
|
|
|
|
} |
|
|
|
|
catch (const std::exception &) {} |
|
|
|
|
|
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
, std::move(resultHandler)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void TorrentImpl::fetchDownloadingPieces(std::function<void (QBitArray)> resultHandler) const |
|
|
|
|
{ |
|
|
|
|
invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QBitArray |
|
|
|
|
{ |
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
#ifdef QBT_USES_LIBTORRENT2 |
|
|
|
|
const std::vector<lt::partial_piece_info> queue = nativeHandle.get_download_queue(); |
|
|
|
|
#else |
|
|
|
|
std::vector<lt::partial_piece_info> queue; |
|
|
|
|
nativeHandle.get_download_queue(queue); |
|
|
|
|
#endif |
|
|
|
|
QBitArray result; |
|
|
|
|
result.resize(torrentInfo.piecesCount()); |
|
|
|
|
for (const lt::partial_piece_info &info : queue) |
|
|
|
|
result.setBit(LT::toUnderlyingType(info.piece_index)); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
catch (const std::exception &) {} |
|
|
|
|
|
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
, std::move(resultHandler)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void TorrentImpl::fetchAvailableFileFractions(std::function<void (QVector<qreal>)> resultHandler) const |
|
|
|
|
{ |
|
|
|
|
invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QVector<qreal> |
|
|
|
|
{ |
|
|
|
|
if (!torrentInfo.isValid() || (torrentInfo.filesCount() <= 0)) |
|
|
|
|
return {}; |
|
|
|
|
|
|
|
|
|
try |
|
|
|
|
{ |
|
|
|
|
std::vector<int> piecesAvailability; |
|
|
|
|
nativeHandle.piece_availability(piecesAvailability); |
|
|
|
|
const int filesCount = torrentInfo.filesCount(); |
|
|
|
|
// libtorrent returns empty array for seeding only torrents
|
|
|
|
|
if (piecesAvailability.empty()) |
|
|
|
|
return QVector<qreal>(filesCount, -1); |
|
|
|
|
|
|
|
|
|
QVector<qreal> result; |
|
|
|
|
result.reserve(filesCount); |
|
|
|
|
for (int i = 0; i < filesCount; ++i) |
|
|
|
|
{ |
|
|
|
|
const TorrentInfo::PieceRange filePieces = 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<qreal>(availablePieces) / filePieces.size(); |
|
|
|
|
result.append(availability); |
|
|
|
|
} |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
catch (const std::exception &) {} |
|
|
|
|
|
|
|
|
|
return {}; |
|
|
|
|
} |
|
|
|
|
, std::move(resultHandler)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void TorrentImpl::prioritizeFiles(const QVector<DownloadPriority> &priorities) |
|
|
|
|
{ |
|
|
|
|
if (!hasMetadata()) return; |
|
|
|
@ -2478,3 +2687,19 @@ QVector<qreal> TorrentImpl::availableFileFractions() const
@@ -2478,3 +2687,19 @@ QVector<qreal> TorrentImpl::availableFileFractions() const
|
|
|
|
|
} |
|
|
|
|
return res; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
template <typename Func, typename Callback> |
|
|
|
|
void TorrentImpl::invokeAsync(Func func, Callback resultHandler) const |
|
|
|
|
{ |
|
|
|
|
m_session->invokeAsync([session = m_session |
|
|
|
|
, func = std::move(func) |
|
|
|
|
, resultHandler = std::move(resultHandler) |
|
|
|
|
, thisTorrent = QPointer<const TorrentImpl>(this)]() mutable |
|
|
|
|
{ |
|
|
|
|
session->invoke([result = func(), thisTorrent, resultHandler = std::move(resultHandler)] |
|
|
|
|
{ |
|
|
|
|
if (thisTorrent) |
|
|
|
|
resultHandler(result); |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|