2015-04-19 18:17:47 +03:00
|
|
|
/*
|
|
|
|
* Bittorrent Client using Qt and libtorrent.
|
|
|
|
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
|
|
|
|
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
* In addition, as a special exception, the copyright holders give permission to
|
|
|
|
* link this program with the OpenSSL project's "OpenSSL" library (or with
|
|
|
|
* modified versions of it that use the same license as the "OpenSSL" library),
|
|
|
|
* and distribute the linked executables. You must obey the GNU General Public
|
|
|
|
* License in all respects for all of the code used other than "OpenSSL". If you
|
|
|
|
* modify file(s), you may extend this exception to your version of the file(s),
|
|
|
|
* but you are not obligated to do so. If you do not wish to do so, delete this
|
|
|
|
* exception statement from your version.
|
|
|
|
*/
|
|
|
|
|
2017-09-07 03:00:04 +03:00
|
|
|
#include "torrenthandle.h"
|
|
|
|
|
2017-09-22 07:42:24 +03:00
|
|
|
#include <algorithm>
|
2017-02-21 00:55:44 +03:00
|
|
|
#include <type_traits>
|
|
|
|
|
2019-03-02 08:22:13 +03:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
#include <windows.h>
|
|
|
|
#endif
|
2015-04-19 18:17:47 +03:00
|
|
|
|
|
|
|
#include <libtorrent/address.hpp>
|
|
|
|
#include <libtorrent/alert_types.hpp>
|
2017-09-07 03:00:04 +03:00
|
|
|
#include <libtorrent/bencode.hpp>
|
2015-06-17 11:21:41 +03:00
|
|
|
#include <libtorrent/create_torrent.hpp>
|
2017-09-07 03:00:04 +03:00
|
|
|
#include <libtorrent/entry.hpp>
|
2015-06-04 11:03:19 +03:00
|
|
|
#include <libtorrent/magnet_uri.hpp>
|
2016-01-20 10:15:10 +03:00
|
|
|
#include <libtorrent/time.hpp>
|
2019-03-06 08:58:07 +03:00
|
|
|
#include <libtorrent/version.hpp>
|
2016-01-20 09:57:02 +03:00
|
|
|
|
2019-06-28 15:22:14 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM >= 10200)
|
|
|
|
#include <libtorrent/write_resume_data.hpp>
|
|
|
|
#endif
|
|
|
|
|
2019-03-02 08:22:13 +03:00
|
|
|
#include <QBitArray>
|
|
|
|
#include <QDebug>
|
|
|
|
#include <QDir>
|
|
|
|
#include <QFile>
|
|
|
|
#include <QStringList>
|
|
|
|
#include <QUrl>
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2018-11-18 21:40:37 +03:00
|
|
|
#include "base/global.h"
|
2015-09-25 11:10:05 +03:00
|
|
|
#include "base/logger.h"
|
|
|
|
#include "base/preferences.h"
|
2016-05-13 21:32:47 +03:00
|
|
|
#include "base/profile.h"
|
2019-03-02 08:22:13 +03:00
|
|
|
#include "base/tristatebool.h"
|
2015-09-25 11:10:05 +03:00
|
|
|
#include "base/utils/fs.h"
|
2019-06-04 15:46:41 +03:00
|
|
|
#include "base/utils/string.h"
|
2019-06-02 12:13:34 +03:00
|
|
|
#include "downloadpriority.h"
|
2019-07-22 19:50:42 +03:00
|
|
|
#include "peeraddress.h"
|
2015-04-19 18:17:47 +03:00
|
|
|
#include "peerinfo.h"
|
2019-07-18 19:53:04 +03:00
|
|
|
#include "private/ltunderlyingtype.h"
|
2017-09-07 03:00:04 +03:00
|
|
|
#include "session.h"
|
2015-04-19 18:17:47 +03:00
|
|
|
#include "trackerentry.h"
|
|
|
|
|
2018-05-17 04:50:58 +03:00
|
|
|
const QString QB_EXT {QStringLiteral(".!qB")};
|
2015-04-19 18:17:47 +03:00
|
|
|
|
|
|
|
using namespace BitTorrent;
|
|
|
|
|
2019-07-21 19:53:16 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM >= 10200)
|
|
|
|
namespace libtorrent
|
|
|
|
{
|
|
|
|
namespace aux
|
|
|
|
{
|
|
|
|
template <typename T, typename Tag>
|
|
|
|
uint qHash(const strong_typedef<T, Tag> &key, const uint seed)
|
|
|
|
{
|
2020-01-26 22:51:58 +03:00
|
|
|
return ::qHash((std::hash<strong_typedef<T, Tag>> {})(key), seed);
|
2019-07-21 19:53:16 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-06-05 03:22:17 +03:00
|
|
|
namespace
|
|
|
|
{
|
2019-03-06 08:58:07 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
|
|
|
using LTDownloadPriority = int;
|
2019-07-18 19:53:04 +03:00
|
|
|
using LTPieceIndex = int;
|
2019-03-06 08:58:07 +03:00
|
|
|
using LTQueuePosition = int;
|
|
|
|
#else
|
|
|
|
using LTDownloadPriority = lt::download_priority_t;
|
2019-07-18 19:53:04 +03:00
|
|
|
using LTPieceIndex = lt::piece_index_t;
|
2019-03-06 08:58:07 +03:00
|
|
|
using LTQueuePosition = lt::queue_position_t;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
std::vector<LTDownloadPriority> toLTDownloadPriorities(const QVector<DownloadPriority> &priorities)
|
|
|
|
{
|
|
|
|
std::vector<LTDownloadPriority> out;
|
2019-08-07 13:22:19 +03:00
|
|
|
out.reserve(priorities.size());
|
|
|
|
|
2019-03-06 08:58:07 +03:00
|
|
|
std::transform(priorities.cbegin(), priorities.cend()
|
|
|
|
, std::back_inserter(out), [](BitTorrent::DownloadPriority priority)
|
|
|
|
{
|
|
|
|
return static_cast<LTDownloadPriority>(
|
2019-07-18 19:53:04 +03:00
|
|
|
static_cast<LTUnderlyingType<LTDownloadPriority>>(priority));
|
2019-03-06 08:58:07 +03:00
|
|
|
});
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2019-05-06 15:25:06 +03:00
|
|
|
using ListType = lt::entry::list_type;
|
2017-06-05 03:22:17 +03:00
|
|
|
|
|
|
|
ListType setToEntryList(const QSet<QString> &input)
|
|
|
|
{
|
|
|
|
ListType entryList;
|
2018-11-18 21:40:37 +03:00
|
|
|
for (const QString &setValue : input)
|
2017-06-05 03:22:17 +03:00
|
|
|
entryList.emplace_back(setValue.toStdString());
|
|
|
|
return entryList;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-01 16:28:40 +03:00
|
|
|
// AddTorrentData
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2018-07-11 15:44:15 +03:00
|
|
|
CreateTorrentParams::CreateTorrentParams()
|
|
|
|
: restored(false)
|
2016-01-01 16:28:40 +03:00
|
|
|
, disableTempPath(false)
|
|
|
|
, sequential(false)
|
2017-05-09 08:51:12 +03:00
|
|
|
, firstLastPiecePriority(false)
|
2016-01-01 16:28:40 +03:00
|
|
|
, hasSeedStatus(false)
|
|
|
|
, skipChecking(false)
|
2017-04-24 11:59:16 +03:00
|
|
|
, hasRootFolder(true)
|
2018-07-11 15:44:15 +03:00
|
|
|
, forced(false)
|
|
|
|
, paused(false)
|
2017-08-14 14:44:56 +03:00
|
|
|
, uploadLimit(-1)
|
|
|
|
, downloadLimit(-1)
|
2016-01-01 16:28:40 +03:00
|
|
|
, ratioLimit(TorrentHandle::USE_GLOBAL_RATIO)
|
2016-02-07 20:31:50 +03:00
|
|
|
, seedingTimeLimit(TorrentHandle::USE_GLOBAL_SEEDING_TIME)
|
2016-01-01 16:28:40 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-07-11 15:44:15 +03:00
|
|
|
CreateTorrentParams::CreateTorrentParams(const AddTorrentParams ¶ms)
|
|
|
|
: restored(false)
|
2016-01-01 16:28:40 +03:00
|
|
|
, name(params.name)
|
2016-02-09 11:56:48 +03:00
|
|
|
, category(params.category)
|
2017-06-05 03:22:17 +03:00
|
|
|
, tags(params.tags)
|
2016-01-01 16:28:40 +03:00
|
|
|
, savePath(params.savePath)
|
|
|
|
, disableTempPath(params.disableTempPath)
|
|
|
|
, sequential(params.sequential)
|
2016-07-15 05:15:10 +03:00
|
|
|
, firstLastPiecePriority(params.firstLastPiecePriority)
|
2016-01-01 16:28:40 +03:00
|
|
|
, hasSeedStatus(params.skipChecking) // do not react on 'torrent_finished_alert' when skipping
|
|
|
|
, skipChecking(params.skipChecking)
|
2017-04-24 12:03:35 +03:00
|
|
|
, hasRootFolder(params.createSubfolder == TriStateBool::Undefined
|
|
|
|
? Session::instance()->isCreateTorrentSubfolder()
|
|
|
|
: params.createSubfolder == TriStateBool::True)
|
2018-07-11 15:44:15 +03:00
|
|
|
, forced(params.addForced == TriStateBool::True)
|
|
|
|
, paused(params.addPaused == TriStateBool::Undefined
|
2017-04-24 11:59:16 +03:00
|
|
|
? Session::instance()->isAddTorrentPaused()
|
|
|
|
: params.addPaused == TriStateBool::True)
|
2017-08-14 14:44:56 +03:00
|
|
|
, uploadLimit(params.uploadLimit)
|
|
|
|
, downloadLimit(params.downloadLimit)
|
2016-01-01 16:28:40 +03:00
|
|
|
, filePriorities(params.filePriorities)
|
2016-02-07 20:31:50 +03:00
|
|
|
, ratioLimit(params.ignoreShareLimits ? TorrentHandle::NO_RATIO_LIMIT : TorrentHandle::USE_GLOBAL_RATIO)
|
|
|
|
, seedingTimeLimit(params.ignoreShareLimits ? TorrentHandle::NO_SEEDING_TIME_LIMIT : TorrentHandle::USE_GLOBAL_SEEDING_TIME)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2017-08-05 23:11:40 +03:00
|
|
|
bool useAutoTMM = (params.useAutoTMM == TriStateBool::Undefined
|
|
|
|
? !Session::instance()->isAutoTMMDisabledByDefault()
|
|
|
|
: params.useAutoTMM == TriStateBool::True);
|
|
|
|
if (useAutoTMM)
|
|
|
|
savePath = "";
|
|
|
|
else if (savePath.trimmed().isEmpty())
|
|
|
|
savePath = Session::instance()->defaultSavePath();
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-12-16 19:45:08 +03:00
|
|
|
uint BitTorrent::qHash(const BitTorrent::TorrentState key, const uint seed)
|
|
|
|
{
|
|
|
|
return ::qHash(static_cast<std::underlying_type_t<TorrentState>>(key), seed);
|
|
|
|
}
|
|
|
|
|
2015-04-19 18:17:47 +03:00
|
|
|
// TorrentHandle
|
|
|
|
|
|
|
|
const qreal TorrentHandle::USE_GLOBAL_RATIO = -2.;
|
|
|
|
const qreal TorrentHandle::NO_RATIO_LIMIT = -1.;
|
|
|
|
|
2016-02-07 20:31:50 +03:00
|
|
|
const int TorrentHandle::USE_GLOBAL_SEEDING_TIME = -2;
|
|
|
|
const int TorrentHandle::NO_SEEDING_TIME_LIMIT = -1;
|
|
|
|
|
2015-04-19 18:17:47 +03:00
|
|
|
const qreal TorrentHandle::MAX_RATIO = 9999.;
|
2016-02-07 20:31:50 +03:00
|
|
|
const int TorrentHandle::MAX_SEEDING_TIME = 525600;
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
TorrentHandle::TorrentHandle(Session *session, const lt::torrent_handle &nativeHandle,
|
2018-07-11 15:44:15 +03:00
|
|
|
const CreateTorrentParams ¶ms)
|
2015-04-19 18:17:47 +03:00
|
|
|
: QObject(session)
|
|
|
|
, m_session(session)
|
|
|
|
, m_nativeHandle(nativeHandle)
|
|
|
|
, m_state(TorrentState::Unknown)
|
2015-07-22 11:41:00 +03:00
|
|
|
, m_renameCount(0)
|
2018-07-11 15:44:15 +03:00
|
|
|
, m_useAutoTMM(params.savePath.isEmpty())
|
|
|
|
, m_name(params.name)
|
|
|
|
, m_savePath(Utils::Fs::toNativePath(params.savePath))
|
|
|
|
, m_category(params.category)
|
|
|
|
, m_tags(params.tags)
|
|
|
|
, m_hasSeedStatus(params.hasSeedStatus)
|
|
|
|
, m_ratioLimit(params.ratioLimit)
|
|
|
|
, m_seedingTimeLimit(params.seedingTimeLimit)
|
|
|
|
, m_tempPathDisabled(params.disableTempPath)
|
2019-07-22 14:29:14 +03:00
|
|
|
, m_fastresumeDataRejected(false)
|
2015-04-19 18:17:47 +03:00
|
|
|
, m_hasMissingFiles(false)
|
2018-07-11 15:44:15 +03:00
|
|
|
, m_hasRootFolder(params.hasRootFolder)
|
2016-07-15 05:15:10 +03:00
|
|
|
, m_needsToSetFirstLastPiecePriority(false)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2016-05-08 22:47:50 +03:00
|
|
|
if (m_useAutoTMM)
|
2016-02-09 11:56:48 +03:00
|
|
|
m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category));
|
2016-01-01 16:28:40 +03:00
|
|
|
|
|
|
|
updateStatus();
|
|
|
|
m_hash = InfoHash(m_nativeStatus.info_hash);
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2016-07-15 05:15:10 +03:00
|
|
|
// NB: the following two if statements are present because we don't want
|
|
|
|
// to set either sequential download or first/last piece priority to false
|
|
|
|
// if their respective flags in data are false when a torrent is being
|
|
|
|
// resumed. This is because, in that circumstance, this constructor is
|
|
|
|
// called with those flags set to false, even if the torrent was set to
|
|
|
|
// download sequentially or have first/last piece priority enabled when
|
|
|
|
// its resume data was saved. These two settings are restored later. But
|
|
|
|
// if we set them to false now, both will erroneously not be restored.
|
2018-07-11 15:44:15 +03:00
|
|
|
if (!params.restored || params.sequential)
|
|
|
|
setSequentialDownload(params.sequential);
|
|
|
|
if (!params.restored || params.firstLastPiecePriority)
|
|
|
|
setFirstLastPiecePriority(params.firstLastPiecePriority);
|
2016-07-15 05:15:10 +03:00
|
|
|
|
2018-07-11 15:44:15 +03:00
|
|
|
if (!params.restored && hasMetadata()) {
|
2016-07-15 05:15:10 +03:00
|
|
|
if (filesCount() == 1)
|
|
|
|
m_hasRootFolder = false;
|
2016-01-04 16:00:50 +03:00
|
|
|
}
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
TorrentHandle::~TorrentHandle() {}
|
|
|
|
|
|
|
|
bool TorrentHandle::isValid() const
|
|
|
|
{
|
|
|
|
return m_nativeHandle.is_valid();
|
|
|
|
}
|
|
|
|
|
|
|
|
InfoHash TorrentHandle::hash() const
|
|
|
|
{
|
|
|
|
return m_hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString TorrentHandle::name() const
|
|
|
|
{
|
|
|
|
QString name = m_name;
|
2019-08-07 13:22:19 +03:00
|
|
|
if (!name.isEmpty()) return name;
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-08-07 13:22:19 +03:00
|
|
|
name = QString::fromStdString(m_nativeStatus.name);
|
|
|
|
if (!name.isEmpty()) return name;
|
2015-11-07 21:44:53 +03:00
|
|
|
|
2019-08-07 13:22:19 +03:00
|
|
|
if (hasMetadata()) {
|
|
|
|
name = QString::fromStdString(m_torrentInfo.nativeInfo()->orig_files().name());
|
|
|
|
if (!name.isEmpty()) return name;
|
|
|
|
}
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-08-07 13:22:19 +03:00
|
|
|
return m_hash;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
QDateTime TorrentHandle::creationDate() const
|
|
|
|
{
|
|
|
|
return m_torrentInfo.creationDate();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString TorrentHandle::creator() const
|
|
|
|
{
|
|
|
|
return m_torrentInfo.creator();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString TorrentHandle::comment() const
|
|
|
|
{
|
|
|
|
return m_torrentInfo.comment();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::isPrivate() const
|
|
|
|
{
|
|
|
|
return m_torrentInfo.isPrivate();
|
|
|
|
}
|
|
|
|
|
|
|
|
qlonglong TorrentHandle::totalSize() const
|
|
|
|
{
|
|
|
|
return m_torrentInfo.totalSize();
|
|
|
|
}
|
|
|
|
|
2019-11-06 10:30:16 +03:00
|
|
|
// size without the "don't download" files
|
2015-04-19 18:17:47 +03:00
|
|
|
qlonglong TorrentHandle::wantedSize() const
|
|
|
|
{
|
|
|
|
return m_nativeStatus.total_wanted;
|
|
|
|
}
|
|
|
|
|
|
|
|
qlonglong TorrentHandle::completedSize() const
|
|
|
|
{
|
|
|
|
return m_nativeStatus.total_wanted_done;
|
|
|
|
}
|
|
|
|
|
|
|
|
qlonglong TorrentHandle::incompletedSize() const
|
|
|
|
{
|
|
|
|
return (m_nativeStatus.total_wanted - m_nativeStatus.total_wanted_done);
|
|
|
|
}
|
|
|
|
|
|
|
|
qlonglong TorrentHandle::pieceLength() const
|
|
|
|
{
|
|
|
|
return m_torrentInfo.pieceLength();
|
|
|
|
}
|
|
|
|
|
|
|
|
qlonglong TorrentHandle::wastedSize() const
|
|
|
|
{
|
|
|
|
return (m_nativeStatus.total_failed_bytes + m_nativeStatus.total_redundant_bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
QString TorrentHandle::currentTracker() const
|
|
|
|
{
|
2017-03-07 14:41:38 +03:00
|
|
|
return QString::fromStdString(m_nativeStatus.current_tracker);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2015-10-24 15:28:29 +03:00
|
|
|
QString TorrentHandle::savePath(bool actual) const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2015-10-24 15:28:29 +03:00
|
|
|
if (actual)
|
2019-06-16 20:14:15 +03:00
|
|
|
return Utils::Fs::toUniformPath(nativeActualSavePath());
|
2015-10-24 15:28:29 +03:00
|
|
|
else
|
2019-06-16 20:14:15 +03:00
|
|
|
return Utils::Fs::toUniformPath(m_savePath);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2015-10-24 15:28:29 +03:00
|
|
|
QString TorrentHandle::rootPath(bool actual) const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2016-01-06 10:26:57 +03:00
|
|
|
if ((filesCount() > 1) && !hasRootFolder())
|
2019-02-14 20:16:42 +03:00
|
|
|
return {};
|
2016-01-06 10:26:57 +03:00
|
|
|
|
2019-02-09 18:40:14 +03:00
|
|
|
const QString firstFilePath = filePath(0);
|
2018-07-21 08:28:13 +03:00
|
|
|
const int slashIndex = firstFilePath.indexOf('/');
|
2015-10-24 15:28:29 +03:00
|
|
|
if (slashIndex >= 0)
|
|
|
|
return QDir(savePath(actual)).absoluteFilePath(firstFilePath.left(slashIndex));
|
|
|
|
else
|
|
|
|
return QDir(savePath(actual)).absoluteFilePath(firstFilePath);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-08-07 13:22:19 +03:00
|
|
|
QString TorrentHandle::contentPath(const bool actual) const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2015-10-24 15:28:29 +03:00
|
|
|
if (filesCount() == 1)
|
|
|
|
return QDir(savePath(actual)).absoluteFilePath(filePath(0));
|
2019-08-07 13:22:19 +03:00
|
|
|
|
|
|
|
if (hasRootFolder())
|
2015-10-24 15:28:29 +03:00
|
|
|
return rootPath(actual);
|
2019-08-07 13:22:19 +03:00
|
|
|
|
|
|
|
return savePath(actual);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2016-05-08 22:47:50 +03:00
|
|
|
bool TorrentHandle::isAutoTMMEnabled() const
|
2016-02-09 11:56:48 +03:00
|
|
|
{
|
2016-05-08 22:47:50 +03:00
|
|
|
return m_useAutoTMM;
|
2016-02-09 11:56:48 +03:00
|
|
|
}
|
|
|
|
|
2016-05-08 22:47:50 +03:00
|
|
|
void TorrentHandle::setAutoTMMEnabled(bool enabled)
|
2016-02-09 11:56:48 +03:00
|
|
|
{
|
2016-05-08 22:47:50 +03:00
|
|
|
if (m_useAutoTMM == enabled) return;
|
2016-02-09 11:56:48 +03:00
|
|
|
|
2016-05-08 22:47:50 +03:00
|
|
|
m_useAutoTMM = enabled;
|
2016-02-09 11:56:48 +03:00
|
|
|
m_session->handleTorrentSavingModeChanged(this);
|
|
|
|
|
2016-05-08 22:47:50 +03:00
|
|
|
if (m_useAutoTMM)
|
2017-08-05 15:51:53 +03:00
|
|
|
move_impl(m_session->categorySavePath(m_category), true);
|
2016-02-09 11:56:48 +03:00
|
|
|
}
|
|
|
|
|
2016-01-04 16:00:50 +03:00
|
|
|
bool TorrentHandle::hasRootFolder() const
|
|
|
|
{
|
|
|
|
return m_hasRootFolder;
|
|
|
|
}
|
|
|
|
|
2015-10-24 15:28:29 +03:00
|
|
|
QString TorrentHandle::nativeActualSavePath() const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2017-03-07 14:41:38 +03:00
|
|
|
return QString::fromStdString(m_nativeStatus.save_path);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-04-01 11:39:29 +03:00
|
|
|
bool TorrentHandle::isAutoManaged() const
|
|
|
|
{
|
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
|
|
|
return m_nativeStatus.auto_managed;
|
|
|
|
#else
|
2019-07-18 19:53:04 +03:00
|
|
|
return bool {m_nativeStatus.flags & lt::torrent_flags::auto_managed};
|
2019-04-01 11:39:29 +03:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void TorrentHandle::setAutoManaged(const bool enable)
|
|
|
|
{
|
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
|
|
|
m_nativeHandle.auto_managed(enable);
|
|
|
|
#else
|
|
|
|
if (enable)
|
2019-05-06 15:25:06 +03:00
|
|
|
m_nativeHandle.set_flags(lt::torrent_flags::auto_managed);
|
2019-04-01 11:39:29 +03:00
|
|
|
else
|
2019-05-06 15:25:06 +03:00
|
|
|
m_nativeHandle.unset_flags(lt::torrent_flags::auto_managed);
|
2019-04-01 11:39:29 +03:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2019-05-21 07:21:17 +03:00
|
|
|
QVector<TrackerEntry> TorrentHandle::trackers() const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-08-07 13:22:19 +03:00
|
|
|
const std::vector<lt::announce_entry> nativeTrackers = m_nativeHandle.trackers();
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-05-21 07:21:17 +03:00
|
|
|
QVector<TrackerEntry> entries;
|
2019-08-07 13:22:19 +03:00
|
|
|
entries.reserve(nativeTrackers.size());
|
|
|
|
|
|
|
|
for (const lt::announce_entry &tracker : nativeTrackers)
|
2015-04-19 18:17:47 +03:00
|
|
|
entries << tracker;
|
2019-08-07 13:22:19 +03:00
|
|
|
|
2015-04-19 18:17:47 +03:00
|
|
|
return entries;
|
|
|
|
}
|
|
|
|
|
|
|
|
QHash<QString, TrackerInfo> TorrentHandle::trackerInfos() const
|
|
|
|
{
|
|
|
|
return m_trackerInfos;
|
|
|
|
}
|
|
|
|
|
2019-05-21 07:21:17 +03:00
|
|
|
void TorrentHandle::addTrackers(const QVector<TrackerEntry> &trackers)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-08-07 13:22:19 +03:00
|
|
|
QSet<TrackerEntry> currentTrackers;
|
|
|
|
for (const lt::announce_entry &entry : m_nativeHandle.trackers())
|
|
|
|
currentTrackers << entry;
|
2019-05-21 07:21:17 +03:00
|
|
|
|
|
|
|
QVector<TrackerEntry> newTrackers;
|
|
|
|
newTrackers.reserve(trackers.size());
|
|
|
|
|
2018-11-18 21:40:37 +03:00
|
|
|
for (const TrackerEntry &tracker : trackers) {
|
2019-05-21 07:21:17 +03:00
|
|
|
if (!currentTrackers.contains(tracker)) {
|
|
|
|
m_nativeHandle.add_tracker(tracker.nativeEntry());
|
|
|
|
newTrackers << tracker;
|
|
|
|
}
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-05-21 07:21:17 +03:00
|
|
|
if (!newTrackers.isEmpty())
|
|
|
|
m_session->handleTorrentTrackersAdded(this, newTrackers);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-05-21 07:21:17 +03:00
|
|
|
void TorrentHandle::replaceTrackers(const QVector<TrackerEntry> &trackers)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-05-21 07:21:17 +03:00
|
|
|
QVector<TrackerEntry> currentTrackers = this->trackers();
|
|
|
|
|
|
|
|
QVector<TrackerEntry> newTrackers;
|
|
|
|
newTrackers.reserve(trackers.size());
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-08-07 13:22:19 +03:00
|
|
|
std::vector<lt::announce_entry> nativeTrackers;
|
|
|
|
nativeTrackers.reserve(trackers.size());
|
2019-05-21 07:21:17 +03:00
|
|
|
|
2018-11-18 21:40:37 +03:00
|
|
|
for (const TrackerEntry &tracker : trackers) {
|
2019-08-07 13:22:19 +03:00
|
|
|
nativeTrackers.emplace_back(tracker.nativeEntry());
|
2019-05-21 07:21:17 +03:00
|
|
|
|
|
|
|
if (!currentTrackers.removeOne(tracker))
|
|
|
|
newTrackers << tracker;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-08-07 13:22:19 +03:00
|
|
|
m_nativeHandle.replace_trackers(nativeTrackers);
|
2019-05-21 07:21:17 +03:00
|
|
|
|
|
|
|
if (newTrackers.isEmpty() && currentTrackers.isEmpty()) {
|
|
|
|
// when existing tracker reorders
|
2017-02-14 20:52:57 +03:00
|
|
|
m_session->handleTorrentTrackersChanged(this);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
2017-02-14 20:52:57 +03:00
|
|
|
else {
|
2019-05-21 07:21:17 +03:00
|
|
|
if (!currentTrackers.isEmpty())
|
|
|
|
m_session->handleTorrentTrackersRemoved(this, currentTrackers);
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-05-21 07:21:17 +03:00
|
|
|
if (!newTrackers.isEmpty())
|
|
|
|
m_session->handleTorrentTrackersAdded(this, newTrackers);
|
|
|
|
}
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-08-02 07:55:06 +03:00
|
|
|
QVector<QUrl> TorrentHandle::urlSeeds() const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-08-07 13:22:19 +03:00
|
|
|
const std::set<std::string> currentSeeds = m_nativeHandle.url_seeds();
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-08-02 07:55:06 +03:00
|
|
|
QVector<QUrl> urlSeeds;
|
2019-08-07 13:22:19 +03:00
|
|
|
urlSeeds.reserve(currentSeeds.size());
|
2019-08-02 07:55:06 +03:00
|
|
|
|
2019-08-07 13:22:19 +03:00
|
|
|
for (const std::string &urlSeed : currentSeeds)
|
2015-04-19 18:17:47 +03:00
|
|
|
urlSeeds.append(QUrl(urlSeed.c_str()));
|
|
|
|
|
|
|
|
return urlSeeds;
|
|
|
|
}
|
|
|
|
|
2019-08-02 07:55:06 +03:00
|
|
|
void TorrentHandle::addUrlSeeds(const QVector<QUrl> &urlSeeds)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-08-07 13:22:19 +03:00
|
|
|
const std::set<std::string> currentSeeds = m_nativeHandle.url_seeds();
|
|
|
|
|
2019-08-02 07:55:06 +03:00
|
|
|
QVector<QUrl> addedUrlSeeds;
|
|
|
|
addedUrlSeeds.reserve(urlSeeds.size());
|
2019-08-07 13:22:19 +03:00
|
|
|
|
|
|
|
for (const QUrl &url : urlSeeds) {
|
|
|
|
const std::string nativeUrl = url.toString().toStdString();
|
|
|
|
if (currentSeeds.find(nativeUrl) == currentSeeds.end()) {
|
|
|
|
m_nativeHandle.add_url_seed(nativeUrl);
|
|
|
|
addedUrlSeeds << url;
|
|
|
|
}
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!addedUrlSeeds.isEmpty())
|
|
|
|
m_session->handleTorrentUrlSeedsAdded(this, addedUrlSeeds);
|
|
|
|
}
|
|
|
|
|
2019-08-02 07:55:06 +03:00
|
|
|
void TorrentHandle::removeUrlSeeds(const QVector<QUrl> &urlSeeds)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-08-07 13:22:19 +03:00
|
|
|
const std::set<std::string> currentSeeds = m_nativeHandle.url_seeds();
|
|
|
|
|
2019-08-02 07:55:06 +03:00
|
|
|
QVector<QUrl> removedUrlSeeds;
|
|
|
|
removedUrlSeeds.reserve(urlSeeds.size());
|
2019-08-07 13:22:19 +03:00
|
|
|
|
|
|
|
for (const QUrl &url : urlSeeds) {
|
|
|
|
const std::string nativeUrl = url.toString().toStdString();
|
|
|
|
if (currentSeeds.find(nativeUrl) != currentSeeds.end()) {
|
|
|
|
m_nativeHandle.remove_url_seed(nativeUrl);
|
|
|
|
removedUrlSeeds << url;
|
|
|
|
}
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!removedUrlSeeds.isEmpty())
|
|
|
|
m_session->handleTorrentUrlSeedsRemoved(this, removedUrlSeeds);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::connectPeer(const PeerAddress &peerAddress)
|
|
|
|
{
|
2019-05-06 15:25:06 +03:00
|
|
|
lt::error_code ec;
|
2019-09-03 08:58:35 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
2019-05-06 15:25:06 +03:00
|
|
|
const lt::address addr = lt::address::from_string(peerAddress.ip.toString().toStdString(), ec);
|
2019-09-03 08:58:35 +03:00
|
|
|
#else
|
|
|
|
const lt::address addr = lt::make_address(peerAddress.ip.toString().toStdString(), ec);
|
|
|
|
#endif
|
2015-04-19 18:17:47 +03:00
|
|
|
if (ec) return false;
|
|
|
|
|
2019-09-03 08:58:35 +03:00
|
|
|
const lt::tcp::endpoint endpoint(addr, peerAddress.port);
|
|
|
|
try {
|
|
|
|
m_nativeHandle.connect_peer(endpoint);
|
|
|
|
}
|
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
|
|
|
catch (const boost::system::system_error &err) {
|
|
|
|
#else
|
|
|
|
catch (const lt::system_error &err) {
|
|
|
|
#endif
|
|
|
|
LogMsg(tr("Failed to add peer \"%1\" to torrent \"%2\". Reason: %3")
|
|
|
|
.arg(peerAddress.toString(), name(), QString::fromLocal8Bit(err.what())), Log::WARNING);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
LogMsg(tr("Peer \"%1\" is added to torrent \"%2\"").arg(peerAddress.toString(), name()));
|
2017-02-14 20:52:57 +03:00
|
|
|
return true;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::needSaveResumeData() const
|
|
|
|
{
|
2017-02-14 20:52:57 +03:00
|
|
|
return m_nativeHandle.need_save_resume_data();
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2018-08-23 14:55:27 +03:00
|
|
|
void TorrentHandle::saveResumeData()
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-11-25 16:35:06 +03:00
|
|
|
m_nativeHandle.save_resume_data();
|
2019-07-22 14:22:26 +03:00
|
|
|
m_session->handleTorrentSaveResumeDataRequested(this);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int TorrentHandle::filesCount() const
|
|
|
|
{
|
|
|
|
return m_torrentInfo.filesCount();
|
|
|
|
}
|
|
|
|
|
|
|
|
int TorrentHandle::piecesCount() const
|
|
|
|
{
|
|
|
|
return m_torrentInfo.piecesCount();
|
|
|
|
}
|
|
|
|
|
2015-06-17 00:42:25 +03:00
|
|
|
int TorrentHandle::piecesHave() const
|
|
|
|
{
|
|
|
|
return m_nativeStatus.num_pieces;
|
|
|
|
}
|
|
|
|
|
2015-04-19 18:17:47 +03:00
|
|
|
qreal TorrentHandle::progress() const
|
|
|
|
{
|
2018-04-09 16:52:37 +03:00
|
|
|
if (!isChecking()) {
|
|
|
|
if (!m_nativeStatus.total_wanted)
|
|
|
|
return 0.;
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2018-04-09 16:52:37 +03:00
|
|
|
if (m_nativeStatus.total_wanted_done == m_nativeStatus.total_wanted)
|
|
|
|
return 1.;
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-02-09 18:40:14 +03:00
|
|
|
const qreal progress = static_cast<qreal>(m_nativeStatus.total_wanted_done) / m_nativeStatus.total_wanted;
|
2018-04-09 16:52:37 +03:00
|
|
|
Q_ASSERT((progress >= 0.f) && (progress <= 1.f));
|
|
|
|
return progress;
|
|
|
|
}
|
|
|
|
|
|
|
|
return m_nativeStatus.progress;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2016-02-09 11:56:48 +03:00
|
|
|
QString TorrentHandle::category() const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2016-02-09 11:56:48 +03:00
|
|
|
return m_category;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::belongsToCategory(const QString &category) const
|
|
|
|
{
|
|
|
|
if (m_category.isEmpty()) return category.isEmpty();
|
|
|
|
if (!Session::isValidCategoryName(category)) return false;
|
|
|
|
|
|
|
|
if (m_category == category) return true;
|
|
|
|
|
2018-07-21 08:28:13 +03:00
|
|
|
if (m_session->isSubcategoriesEnabled() && m_category.startsWith(category + '/'))
|
2016-02-09 11:56:48 +03:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2017-06-05 03:22:17 +03:00
|
|
|
QSet<QString> TorrentHandle::tags() const
|
|
|
|
{
|
|
|
|
return m_tags;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::hasTag(const QString &tag) const
|
|
|
|
{
|
|
|
|
return m_tags.contains(tag);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::addTag(const QString &tag)
|
|
|
|
{
|
|
|
|
if (!Session::isValidTag(tag))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!hasTag(tag)) {
|
|
|
|
if (!m_session->hasTag(tag))
|
|
|
|
if (!m_session->addTag(tag))
|
|
|
|
return false;
|
|
|
|
m_tags.insert(tag);
|
|
|
|
m_session->handleTorrentTagAdded(this, tag);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::removeTag(const QString &tag)
|
|
|
|
{
|
|
|
|
if (m_tags.remove(tag)) {
|
|
|
|
m_session->handleTorrentTagRemoved(this, tag);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TorrentHandle::removeAllTags()
|
|
|
|
{
|
2018-11-27 23:15:04 +03:00
|
|
|
for (const QString &tag : asConst(tags()))
|
2017-06-05 03:22:17 +03:00
|
|
|
removeTag(tag);
|
|
|
|
}
|
|
|
|
|
2015-04-19 18:17:47 +03:00
|
|
|
QDateTime TorrentHandle::addedTime() const
|
|
|
|
{
|
2019-04-01 17:48:32 +03:00
|
|
|
return QDateTime::fromSecsSinceEpoch(m_nativeStatus.added_time);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
qreal TorrentHandle::ratioLimit() const
|
|
|
|
{
|
|
|
|
return m_ratioLimit;
|
|
|
|
}
|
|
|
|
|
2016-02-07 20:31:50 +03:00
|
|
|
int TorrentHandle::seedingTimeLimit() const
|
|
|
|
{
|
|
|
|
return m_seedingTimeLimit;
|
|
|
|
}
|
|
|
|
|
2015-04-19 18:17:47 +03:00
|
|
|
QString TorrentHandle::filePath(int index) const
|
|
|
|
{
|
|
|
|
return m_torrentInfo.filePath(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
QString TorrentHandle::fileName(int index) const
|
|
|
|
{
|
2019-02-14 20:16:42 +03:00
|
|
|
if (!hasMetadata()) return {};
|
2015-05-06 14:53:27 +03:00
|
|
|
return Utils::Fs::fileName(filePath(index));
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
qlonglong TorrentHandle::fileSize(int index) const
|
|
|
|
{
|
|
|
|
return m_torrentInfo.fileSize(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return a list of absolute paths corresponding
|
|
|
|
// to all files in a torrent
|
|
|
|
QStringList TorrentHandle::absoluteFilePaths() const
|
|
|
|
{
|
2019-02-14 20:16:42 +03:00
|
|
|
if (!hasMetadata()) return {};
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-02-09 18:40:14 +03:00
|
|
|
const QDir saveDir(savePath(true));
|
2015-04-19 18:17:47 +03:00
|
|
|
QStringList res;
|
|
|
|
for (int i = 0; i < filesCount(); ++i)
|
2015-05-06 14:53:27 +03:00
|
|
|
res << Utils::Fs::expandPathAbs(saveDir.absoluteFilePath(filePath(i)));
|
2015-04-19 18:17:47 +03:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
QStringList TorrentHandle::absoluteFilePathsUnwanted() const
|
|
|
|
{
|
2019-02-14 20:16:42 +03:00
|
|
|
if (!hasMetadata()) return {};
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-02-09 18:40:14 +03:00
|
|
|
const QDir saveDir(savePath(true));
|
2019-04-01 11:39:29 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
|
|
|
const std::vector<LTDownloadPriority> fp = m_nativeHandle.file_priorities();
|
|
|
|
#else
|
|
|
|
const std::vector<LTDownloadPriority> fp = m_nativeHandle.get_file_priorities();
|
|
|
|
#endif
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-04-01 11:39:29 +03:00
|
|
|
QStringList res;
|
|
|
|
for (int i = 0; i < static_cast<int>(fp.size()); ++i) {
|
2019-07-18 19:53:04 +03:00
|
|
|
if (fp[i] == LTDownloadPriority {0}) {
|
2015-05-06 14:53:27 +03:00
|
|
|
const QString path = Utils::Fs::expandPathAbs(saveDir.absoluteFilePath(filePath(i)));
|
2015-04-19 18:17:47 +03:00
|
|
|
if (path.contains(".unwanted"))
|
|
|
|
res << path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2019-03-06 08:58:07 +03:00
|
|
|
QVector<DownloadPriority> TorrentHandle::filePriorities() const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-03-06 08:58:07 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
|
|
|
const std::vector<LTDownloadPriority> fp = m_nativeHandle.file_priorities();
|
|
|
|
#else
|
|
|
|
const std::vector<LTDownloadPriority> fp = m_nativeHandle.get_file_priorities();
|
|
|
|
#endif
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-03-06 08:58:07 +03:00
|
|
|
QVector<DownloadPriority> ret;
|
|
|
|
std::transform(fp.cbegin(), fp.cend(), std::back_inserter(ret), [](LTDownloadPriority priority)
|
|
|
|
{
|
2019-07-18 19:53:04 +03:00
|
|
|
return static_cast<DownloadPriority>(LTUnderlyingType<LTDownloadPriority> {priority});
|
2019-03-06 08:58:07 +03:00
|
|
|
});
|
|
|
|
return ret;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
TorrentInfo TorrentHandle::info() const
|
|
|
|
{
|
|
|
|
return m_torrentInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::isPaused() const
|
|
|
|
{
|
2019-04-01 11:39:29 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
|
|
|
return (m_nativeStatus.paused && !isAutoManaged());
|
|
|
|
#else
|
2019-05-06 15:25:06 +03:00
|
|
|
return ((m_nativeStatus.flags & lt::torrent_flags::paused)
|
2019-04-01 11:39:29 +03:00
|
|
|
&& !isAutoManaged());
|
|
|
|
#endif
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::isResumed() const
|
|
|
|
{
|
|
|
|
return !isPaused();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::isQueued() const
|
|
|
|
{
|
2019-04-01 11:39:29 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
|
|
|
return (m_nativeStatus.paused && isAutoManaged());
|
|
|
|
#else
|
2019-05-06 15:25:06 +03:00
|
|
|
return ((m_nativeStatus.flags & lt::torrent_flags::paused)
|
2019-04-01 11:39:29 +03:00
|
|
|
&& isAutoManaged());
|
|
|
|
#endif
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::isChecking() const
|
|
|
|
{
|
2019-05-06 15:25:06 +03:00
|
|
|
return ((m_nativeStatus.state == lt::torrent_status::checking_files)
|
|
|
|
|| (m_nativeStatus.state == lt::torrent_status::checking_resume_data));
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::isDownloading() const
|
|
|
|
{
|
|
|
|
return m_state == TorrentState::Downloading
|
|
|
|
|| m_state == TorrentState::DownloadingMetadata
|
|
|
|
|| m_state == TorrentState::StalledDownloading
|
|
|
|
|| m_state == TorrentState::CheckingDownloading
|
|
|
|
|| m_state == TorrentState::PausedDownloading
|
|
|
|
|| m_state == TorrentState::QueuedDownloading
|
2015-11-11 09:51:22 +03:00
|
|
|
|| m_state == TorrentState::ForcedDownloading;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::isUploading() const
|
|
|
|
{
|
|
|
|
return m_state == TorrentState::Uploading
|
|
|
|
|| m_state == TorrentState::StalledUploading
|
|
|
|
|| m_state == TorrentState::CheckingUploading
|
|
|
|
|| m_state == TorrentState::QueuedUploading
|
|
|
|
|| m_state == TorrentState::ForcedUploading;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::isCompleted() const
|
|
|
|
{
|
|
|
|
return m_state == TorrentState::Uploading
|
|
|
|
|| m_state == TorrentState::StalledUploading
|
|
|
|
|| m_state == TorrentState::CheckingUploading
|
|
|
|
|| m_state == TorrentState::PausedUploading
|
2015-07-08 17:01:10 +03:00
|
|
|
|| m_state == TorrentState::QueuedUploading
|
|
|
|
|| m_state == TorrentState::ForcedUploading;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::isActive() const
|
|
|
|
{
|
|
|
|
if (m_state == TorrentState::StalledDownloading)
|
|
|
|
return (uploadPayloadRate() > 0);
|
|
|
|
|
|
|
|
return m_state == TorrentState::DownloadingMetadata
|
|
|
|
|| m_state == TorrentState::Downloading
|
|
|
|
|| m_state == TorrentState::ForcedDownloading
|
|
|
|
|| m_state == TorrentState::Uploading
|
2018-04-09 18:19:33 +03:00
|
|
|
|| m_state == TorrentState::ForcedUploading
|
|
|
|
|| m_state == TorrentState::Moving;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::isInactive() const
|
|
|
|
{
|
|
|
|
return !isActive();
|
|
|
|
}
|
|
|
|
|
2015-11-11 09:51:22 +03:00
|
|
|
bool TorrentHandle::isErrored() const
|
|
|
|
{
|
|
|
|
return m_state == TorrentState::MissingFiles
|
|
|
|
|| m_state == TorrentState::Error;
|
|
|
|
}
|
|
|
|
|
2015-04-19 18:17:47 +03:00
|
|
|
bool TorrentHandle::isSeed() const
|
|
|
|
{
|
|
|
|
// Affected by bug http://code.rasterbar.com/libtorrent/ticket/402
|
2017-02-14 20:52:57 +03:00
|
|
|
//bool result;
|
|
|
|
//result = m_nativeHandle.is_seed());
|
|
|
|
//return result;
|
2015-04-19 18:17:47 +03:00
|
|
|
// May suffer from approximation problems
|
|
|
|
//return (progress() == 1.);
|
|
|
|
// This looks safe
|
2019-05-06 15:25:06 +03:00
|
|
|
return ((m_nativeStatus.state == lt::torrent_status::finished)
|
|
|
|
|| (m_nativeStatus.state == lt::torrent_status::seeding));
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::isForced() const
|
|
|
|
{
|
2019-04-01 11:39:29 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
|
|
|
return (!m_nativeStatus.paused && !isAutoManaged());
|
|
|
|
#else
|
2019-05-06 15:25:06 +03:00
|
|
|
return (!(m_nativeStatus.flags & lt::torrent_flags::paused)
|
2019-04-01 11:39:29 +03:00
|
|
|
&& !isAutoManaged());
|
|
|
|
#endif
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::isSequentialDownload() const
|
|
|
|
{
|
2019-04-01 11:39:29 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
2015-04-19 18:17:47 +03:00
|
|
|
return m_nativeStatus.sequential_download;
|
2019-04-01 11:39:29 +03:00
|
|
|
#else
|
2019-07-18 19:53:04 +03:00
|
|
|
return bool {m_nativeStatus.flags & lt::torrent_flags::sequential_download};
|
2019-04-01 11:39:29 +03:00
|
|
|
#endif
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::hasFirstLastPiecePriority() const
|
|
|
|
{
|
2016-07-15 05:15:10 +03:00
|
|
|
if (!hasMetadata())
|
|
|
|
return m_needsToSetFirstLastPiecePriority;
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-04-01 11:39:29 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
|
|
|
const std::vector<LTDownloadPriority> filePriorities = nativeHandle().file_priorities();
|
|
|
|
#else
|
|
|
|
const std::vector<LTDownloadPriority> filePriorities = nativeHandle().get_file_priorities();
|
|
|
|
#endif
|
2018-05-09 17:10:47 +03:00
|
|
|
for (int i = 0; i < static_cast<int>(filePriorities.size()); ++i) {
|
2019-07-18 19:53:04 +03:00
|
|
|
if (filePriorities[i] <= LTDownloadPriority {0})
|
2018-05-09 17:10:47 +03:00
|
|
|
continue;
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2018-05-09 17:10:47 +03:00
|
|
|
const TorrentInfo::PieceRange extremities = info().filePieces(i);
|
2019-07-18 19:53:04 +03:00
|
|
|
const LTDownloadPriority firstPiecePrio = nativeHandle().piece_priority(LTPieceIndex {extremities.first()});
|
|
|
|
const LTDownloadPriority lastPiecePrio = nativeHandle().piece_priority(LTPieceIndex {extremities.last()});
|
|
|
|
return ((firstPiecePrio == LTDownloadPriority {7}) && (lastPiecePrio == LTDownloadPriority {7}));
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2018-05-09 17:10:47 +03:00
|
|
|
return false;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
TorrentState TorrentHandle::state() const
|
|
|
|
{
|
|
|
|
return m_state;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TorrentHandle::updateState()
|
|
|
|
{
|
2019-11-25 16:37:32 +03:00
|
|
|
if (hasError()) {
|
|
|
|
m_state = TorrentState::Error;
|
|
|
|
}
|
|
|
|
else if (m_nativeStatus.state == lt::torrent_status::checking_resume_data) {
|
2018-09-11 14:43:08 +03:00
|
|
|
m_state = TorrentState::CheckingResumeData;
|
|
|
|
}
|
|
|
|
else if (isMoveInProgress()) {
|
2018-04-09 18:19:33 +03:00
|
|
|
m_state = TorrentState::Moving;
|
|
|
|
}
|
|
|
|
else if (isPaused()) {
|
2015-11-11 01:32:49 +03:00
|
|
|
if (hasMissingFiles())
|
|
|
|
m_state = TorrentState::MissingFiles;
|
2015-04-19 18:17:47 +03:00
|
|
|
else
|
|
|
|
m_state = isSeed() ? TorrentState::PausedUploading : TorrentState::PausedDownloading;
|
|
|
|
}
|
|
|
|
else {
|
2016-05-01 11:05:52 +03:00
|
|
|
if (m_session->isQueueingSystemEnabled() && isQueued() && !isChecking()) {
|
2015-04-19 18:17:47 +03:00
|
|
|
m_state = isSeed() ? TorrentState::QueuedUploading : TorrentState::QueuedDownloading;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
switch (m_nativeStatus.state) {
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::torrent_status::finished:
|
|
|
|
case lt::torrent_status::seeding:
|
2015-04-19 18:17:47 +03:00
|
|
|
if (isForced())
|
|
|
|
m_state = TorrentState::ForcedUploading;
|
|
|
|
else
|
|
|
|
m_state = m_nativeStatus.upload_payload_rate > 0 ? TorrentState::Uploading : TorrentState::StalledUploading;
|
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::torrent_status::allocating:
|
2015-04-19 18:17:47 +03:00
|
|
|
m_state = TorrentState::Allocating;
|
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::torrent_status::checking_files:
|
2015-04-19 18:17:47 +03:00
|
|
|
m_state = m_hasSeedStatus ? TorrentState::CheckingUploading : TorrentState::CheckingDownloading;
|
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::torrent_status::downloading_metadata:
|
2015-04-19 18:17:47 +03:00
|
|
|
m_state = TorrentState::DownloadingMetadata;
|
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::torrent_status::downloading:
|
2015-04-19 18:17:47 +03:00
|
|
|
if (isForced())
|
|
|
|
m_state = TorrentState::ForcedDownloading;
|
|
|
|
else
|
|
|
|
m_state = m_nativeStatus.download_payload_rate > 0 ? TorrentState::Downloading : TorrentState::StalledDownloading;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
qWarning("Unrecognized torrent status, should not happen!!! status was %d", m_nativeStatus.state);
|
|
|
|
m_state = TorrentState::Unknown;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::hasMetadata() const
|
|
|
|
{
|
|
|
|
return m_nativeStatus.has_metadata;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::hasMissingFiles() const
|
|
|
|
{
|
|
|
|
return m_hasMissingFiles;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::hasError() const
|
|
|
|
{
|
2019-11-25 16:37:32 +03:00
|
|
|
return static_cast<bool>(m_nativeStatus.errc);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::hasFilteredPieces() const
|
|
|
|
{
|
2019-03-06 08:58:07 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
|
|
|
const std::vector<LTDownloadPriority> pp = m_nativeHandle.piece_priorities();
|
|
|
|
#else
|
|
|
|
const std::vector<LTDownloadPriority> pp = m_nativeHandle.get_piece_priorities();
|
|
|
|
#endif
|
|
|
|
return std::any_of(pp.cbegin(), pp.cend(), [](const LTDownloadPriority priority)
|
2019-01-11 11:05:57 +03:00
|
|
|
{
|
2019-03-06 08:58:07 +03:00
|
|
|
return (priority == LTDownloadPriority {0});
|
2019-01-11 11:05:57 +03:00
|
|
|
});
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int TorrentHandle::queuePosition() const
|
|
|
|
{
|
2019-03-06 08:58:07 +03:00
|
|
|
if (m_nativeStatus.queue_position < LTQueuePosition {0}) return 0;
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-03-06 08:58:07 +03:00
|
|
|
return static_cast<int>(m_nativeStatus.queue_position) + 1;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
QString TorrentHandle::error() const
|
|
|
|
{
|
2017-05-01 19:45:08 +03:00
|
|
|
return QString::fromStdString(m_nativeStatus.errc.message());
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
qlonglong TorrentHandle::totalDownload() const
|
|
|
|
{
|
|
|
|
return m_nativeStatus.all_time_download;
|
|
|
|
}
|
|
|
|
|
|
|
|
qlonglong TorrentHandle::totalUpload() const
|
|
|
|
{
|
|
|
|
return m_nativeStatus.all_time_upload;
|
|
|
|
}
|
|
|
|
|
2019-04-01 11:39:29 +03:00
|
|
|
qlonglong TorrentHandle::activeTime() const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-04-01 11:39:29 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
2015-04-19 18:17:47 +03:00
|
|
|
return m_nativeStatus.active_time;
|
2019-04-01 11:39:29 +03:00
|
|
|
#else
|
2019-05-06 15:25:06 +03:00
|
|
|
return lt::total_seconds(m_nativeStatus.active_duration);
|
2019-04-01 11:39:29 +03:00
|
|
|
#endif
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-04-01 11:39:29 +03:00
|
|
|
qlonglong TorrentHandle::finishedTime() const
|
2015-06-17 00:42:25 +03:00
|
|
|
{
|
2019-04-01 11:39:29 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
2015-06-17 00:42:25 +03:00
|
|
|
return m_nativeStatus.finished_time;
|
2019-04-01 11:39:29 +03:00
|
|
|
#else
|
2019-05-06 15:25:06 +03:00
|
|
|
return lt::total_seconds(m_nativeStatus.finished_duration);
|
2019-04-01 11:39:29 +03:00
|
|
|
#endif
|
2015-06-17 00:42:25 +03:00
|
|
|
}
|
|
|
|
|
2019-04-01 11:39:29 +03:00
|
|
|
qlonglong TorrentHandle::seedingTime() const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-04-01 11:39:29 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
2015-04-19 18:17:47 +03:00
|
|
|
return m_nativeStatus.seeding_time;
|
2019-04-01 11:39:29 +03:00
|
|
|
#else
|
2019-05-06 15:25:06 +03:00
|
|
|
return lt::total_seconds(m_nativeStatus.seeding_duration);
|
2019-04-01 11:39:29 +03:00
|
|
|
#endif
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-11-06 10:30:16 +03:00
|
|
|
qlonglong TorrentHandle::eta() const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
|
|
|
if (isPaused()) return MAX_ETA;
|
|
|
|
|
2016-02-07 20:31:50 +03:00
|
|
|
const SpeedSampleAvg speedAverage = m_speedMonitor.average();
|
2015-04-19 18:17:47 +03:00
|
|
|
|
|
|
|
if (isSeed()) {
|
2019-02-09 18:40:14 +03:00
|
|
|
const qreal maxRatioValue = maxRatio();
|
|
|
|
const int maxSeedingTimeValue = maxSeedingTime();
|
2016-02-07 20:31:50 +03:00
|
|
|
if ((maxRatioValue < 0) && (maxSeedingTimeValue < 0)) return MAX_ETA;
|
|
|
|
|
|
|
|
qlonglong ratioEta = MAX_ETA;
|
|
|
|
|
|
|
|
if ((speedAverage.upload > 0) && (maxRatioValue >= 0)) {
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2016-02-07 20:31:50 +03:00
|
|
|
qlonglong realDL = totalDownload();
|
|
|
|
if (realDL <= 0)
|
|
|
|
realDL = wantedSize();
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2016-02-07 20:31:50 +03:00
|
|
|
ratioEta = ((realDL * maxRatioValue) - totalUpload()) / speedAverage.upload;
|
|
|
|
}
|
|
|
|
|
|
|
|
qlonglong seedingTimeEta = MAX_ETA;
|
|
|
|
|
|
|
|
if (maxSeedingTimeValue >= 0) {
|
|
|
|
seedingTimeEta = (maxSeedingTimeValue * 60) - seedingTime();
|
|
|
|
if (seedingTimeEta < 0)
|
|
|
|
seedingTimeEta = 0;
|
|
|
|
}
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2016-02-07 20:31:50 +03:00
|
|
|
return qMin(ratioEta, seedingTimeEta);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2016-02-07 20:31:50 +03:00
|
|
|
if (!speedAverage.download) return MAX_ETA;
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2016-02-07 20:31:50 +03:00
|
|
|
return (wantedSize() - completedSize()) / speedAverage.download;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
QVector<qreal> TorrentHandle::filesProgress() const
|
|
|
|
{
|
2019-10-17 07:09:25 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
2016-01-20 09:57:02 +03:00
|
|
|
std::vector<boost::int64_t> fp;
|
2019-10-17 07:09:25 +03:00
|
|
|
#else
|
|
|
|
std::vector<int64_t> fp;
|
|
|
|
#endif
|
2019-05-06 15:25:06 +03:00
|
|
|
m_nativeHandle.file_progress(fp, lt::torrent_handle::piece_granularity);
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2018-11-19 07:00:50 +03:00
|
|
|
const int count = static_cast<int>(fp.size());
|
|
|
|
QVector<qreal> result;
|
|
|
|
result.reserve(count);
|
2015-04-19 18:17:47 +03:00
|
|
|
for (int i = 0; i < count; ++i) {
|
2018-11-19 07:00:50 +03:00
|
|
|
const qlonglong size = fileSize(i);
|
2015-04-19 18:17:47 +03:00
|
|
|
if ((size <= 0) || (fp[i] == size))
|
|
|
|
result << 1;
|
|
|
|
else
|
|
|
|
result << (fp[i] / static_cast<qreal>(size));
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
int TorrentHandle::seedsCount() const
|
|
|
|
{
|
|
|
|
return m_nativeStatus.num_seeds;
|
|
|
|
}
|
|
|
|
|
|
|
|
int TorrentHandle::peersCount() const
|
|
|
|
{
|
|
|
|
return m_nativeStatus.num_peers;
|
|
|
|
}
|
|
|
|
|
|
|
|
int TorrentHandle::leechsCount() const
|
|
|
|
{
|
|
|
|
return (m_nativeStatus.num_peers - m_nativeStatus.num_seeds);
|
|
|
|
}
|
|
|
|
|
2015-06-17 00:42:25 +03:00
|
|
|
int TorrentHandle::totalSeedsCount() const
|
|
|
|
{
|
2016-05-25 13:47:28 +03:00
|
|
|
return (m_nativeStatus.num_complete > 0) ? m_nativeStatus.num_complete : m_nativeStatus.list_seeds;
|
2015-06-17 00:42:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int TorrentHandle::totalPeersCount() const
|
|
|
|
{
|
2019-02-09 18:40:14 +03:00
|
|
|
const int peers = m_nativeStatus.num_complete + m_nativeStatus.num_incomplete;
|
2016-05-25 13:47:28 +03:00
|
|
|
return (peers > 0) ? peers : m_nativeStatus.list_peers;
|
2015-06-17 00:42:25 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int TorrentHandle::totalLeechersCount() const
|
|
|
|
{
|
2016-05-25 13:47:28 +03:00
|
|
|
return (m_nativeStatus.num_incomplete > 0) ? m_nativeStatus.num_incomplete : (m_nativeStatus.list_peers - m_nativeStatus.list_seeds);
|
2015-06-17 00:42:25 +03:00
|
|
|
}
|
|
|
|
|
2015-04-19 18:17:47 +03:00
|
|
|
int TorrentHandle::completeCount() const
|
|
|
|
{
|
2016-05-25 13:47:28 +03:00
|
|
|
// additional info: https://github.com/qbittorrent/qBittorrent/pull/5300#issuecomment-267783646
|
2015-04-19 18:17:47 +03:00
|
|
|
return m_nativeStatus.num_complete;
|
|
|
|
}
|
|
|
|
|
|
|
|
int TorrentHandle::incompleteCount() const
|
|
|
|
{
|
2016-05-25 13:47:28 +03:00
|
|
|
// additional info: https://github.com/qbittorrent/qBittorrent/pull/5300#issuecomment-267783646
|
2015-04-19 18:17:47 +03:00
|
|
|
return m_nativeStatus.num_incomplete;
|
|
|
|
}
|
|
|
|
|
|
|
|
QDateTime TorrentHandle::lastSeenComplete() const
|
|
|
|
{
|
2015-06-12 09:36:01 +03:00
|
|
|
if (m_nativeStatus.last_seen_complete > 0)
|
2019-04-01 17:48:32 +03:00
|
|
|
return QDateTime::fromSecsSinceEpoch(m_nativeStatus.last_seen_complete);
|
2015-06-12 09:36:01 +03:00
|
|
|
else
|
2019-02-14 20:16:42 +03:00
|
|
|
return {};
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
QDateTime TorrentHandle::completedTime() const
|
|
|
|
{
|
2015-06-12 09:36:01 +03:00
|
|
|
if (m_nativeStatus.completed_time > 0)
|
2019-04-01 17:48:32 +03:00
|
|
|
return QDateTime::fromSecsSinceEpoch(m_nativeStatus.completed_time);
|
2015-06-12 09:36:01 +03:00
|
|
|
else
|
2019-02-14 20:16:42 +03:00
|
|
|
return {};
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-04-01 11:39:29 +03:00
|
|
|
qlonglong TorrentHandle::timeSinceUpload() const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-04-01 11:39:29 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
2015-04-19 18:17:47 +03:00
|
|
|
return m_nativeStatus.time_since_upload;
|
2019-04-01 11:39:29 +03:00
|
|
|
#else
|
2019-08-21 12:37:46 +03:00
|
|
|
if (m_nativeStatus.last_upload.time_since_epoch().count() == 0)
|
|
|
|
return -1;
|
2019-05-06 15:25:06 +03:00
|
|
|
return lt::total_seconds(lt::clock_type::now() - m_nativeStatus.last_upload);
|
2019-04-01 11:39:29 +03:00
|
|
|
#endif
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-04-01 11:39:29 +03:00
|
|
|
qlonglong TorrentHandle::timeSinceDownload() const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-04-01 11:39:29 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
2015-04-19 18:17:47 +03:00
|
|
|
return m_nativeStatus.time_since_download;
|
2019-04-01 11:39:29 +03:00
|
|
|
#else
|
2019-08-21 12:37:46 +03:00
|
|
|
if (m_nativeStatus.last_download.time_since_epoch().count() == 0)
|
|
|
|
return -1;
|
2019-05-06 15:25:06 +03:00
|
|
|
return lt::total_seconds(lt::clock_type::now() - m_nativeStatus.last_download);
|
2019-04-01 11:39:29 +03:00
|
|
|
#endif
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-04-01 11:39:29 +03:00
|
|
|
qlonglong TorrentHandle::timeSinceActivity() const
|
2015-06-07 15:03:30 +03:00
|
|
|
{
|
2019-04-01 11:39:29 +03:00
|
|
|
const qlonglong upTime = timeSinceUpload();
|
|
|
|
const qlonglong downTime = timeSinceDownload();
|
|
|
|
return ((upTime < 0) != (downTime < 0))
|
|
|
|
? std::max(upTime, downTime)
|
|
|
|
: std::min(upTime, downTime);
|
2015-06-07 15:03:30 +03:00
|
|
|
}
|
|
|
|
|
2015-04-19 18:17:47 +03:00
|
|
|
int TorrentHandle::downloadLimit() const
|
|
|
|
{
|
2017-02-14 20:52:57 +03:00
|
|
|
return m_nativeHandle.download_limit();
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int TorrentHandle::uploadLimit() const
|
|
|
|
{
|
2017-02-14 20:52:57 +03:00
|
|
|
return m_nativeHandle.upload_limit();
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::superSeeding() const
|
|
|
|
{
|
2019-04-01 11:39:29 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
2015-04-19 18:17:47 +03:00
|
|
|
return m_nativeStatus.super_seeding;
|
2019-04-01 11:39:29 +03:00
|
|
|
#else
|
2019-07-18 19:53:04 +03:00
|
|
|
return bool {m_nativeStatus.flags & lt::torrent_flags::super_seeding};
|
2019-04-01 11:39:29 +03:00
|
|
|
#endif
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-08-02 07:55:06 +03:00
|
|
|
QVector<PeerInfo> TorrentHandle::peers() const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-05-06 15:25:06 +03:00
|
|
|
std::vector<lt::peer_info> nativePeers;
|
2017-02-14 20:52:57 +03:00
|
|
|
m_nativeHandle.get_peer_info(nativePeers);
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-08-02 07:55:06 +03:00
|
|
|
QVector<PeerInfo> peers;
|
|
|
|
peers.reserve(nativePeers.size());
|
2019-05-06 15:25:06 +03:00
|
|
|
for (const lt::peer_info &peer : nativePeers)
|
2015-11-12 22:19:44 +03:00
|
|
|
peers << PeerInfo(this, peer);
|
2015-04-19 18:17:47 +03:00
|
|
|
return peers;
|
|
|
|
}
|
|
|
|
|
|
|
|
QBitArray TorrentHandle::pieces() const
|
|
|
|
{
|
|
|
|
QBitArray result(m_nativeStatus.pieces.size());
|
|
|
|
|
2015-07-25 15:40:15 +03:00
|
|
|
for (int i = 0; i < m_nativeStatus.pieces.size(); ++i)
|
2019-07-18 19:53:04 +03:00
|
|
|
result.setBit(i, m_nativeStatus.pieces.get_bit(LTPieceIndex {i}));
|
2015-04-19 18:17:47 +03:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
QBitArray TorrentHandle::downloadingPieces() const
|
|
|
|
{
|
|
|
|
QBitArray result(piecesCount());
|
|
|
|
|
2019-05-06 15:25:06 +03:00
|
|
|
std::vector<lt::partial_piece_info> queue;
|
2017-02-14 20:52:57 +03:00
|
|
|
m_nativeHandle.get_download_queue(queue);
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-05-17 06:24:01 +03:00
|
|
|
for (const lt::partial_piece_info &info : queue)
|
2019-07-18 19:53:04 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
2019-05-17 06:24:01 +03:00
|
|
|
result.setBit(info.piece_index);
|
2019-07-18 19:53:04 +03:00
|
|
|
#else
|
|
|
|
result.setBit(LTUnderlyingType<LTPieceIndex> {info.piece_index});
|
|
|
|
#endif
|
2015-04-19 18:17:47 +03:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVector<int> TorrentHandle::pieceAvailability() const
|
|
|
|
{
|
|
|
|
std::vector<int> avail;
|
2017-02-14 20:52:57 +03:00
|
|
|
m_nativeHandle.piece_availability(avail);
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-12-23 04:58:28 +03:00
|
|
|
return Vector::fromStdVector(avail);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
qreal TorrentHandle::distributedCopies() const
|
|
|
|
{
|
|
|
|
return m_nativeStatus.distributed_copies;
|
|
|
|
}
|
|
|
|
|
2017-09-06 22:47:05 +03:00
|
|
|
qreal TorrentHandle::maxRatio() const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2017-09-06 22:47:05 +03:00
|
|
|
if (m_ratioLimit == USE_GLOBAL_RATIO)
|
|
|
|
return m_session->globalMaxRatio();
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2017-09-06 22:47:05 +03:00
|
|
|
return m_ratioLimit;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2017-09-06 22:47:05 +03:00
|
|
|
int TorrentHandle::maxSeedingTime() const
|
2016-02-07 20:31:50 +03:00
|
|
|
{
|
2017-09-06 22:47:05 +03:00
|
|
|
if (m_seedingTimeLimit == USE_GLOBAL_SEEDING_TIME)
|
|
|
|
return m_session->globalMaxSeedingMinutes();
|
2016-02-07 20:31:50 +03:00
|
|
|
|
2017-09-06 22:47:05 +03:00
|
|
|
return m_seedingTimeLimit;
|
2016-02-07 20:31:50 +03:00
|
|
|
}
|
|
|
|
|
2015-04-19 18:17:47 +03:00
|
|
|
qreal TorrentHandle::realRatio() const
|
|
|
|
{
|
2019-10-17 07:09:25 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
2019-02-09 18:40:14 +03:00
|
|
|
const boost::int64_t upload = m_nativeStatus.all_time_upload;
|
2015-06-12 11:18:28 +03:00
|
|
|
// special case for a seeder who lost its stats, also assume nobody will import a 99% done torrent
|
2019-10-17 07:09:25 +03:00
|
|
|
const boost::int64_t download = (m_nativeStatus.all_time_download < (m_nativeStatus.total_done * 0.01))
|
|
|
|
? m_nativeStatus.total_done
|
|
|
|
: m_nativeStatus.all_time_download;
|
|
|
|
#else
|
|
|
|
const int64_t upload = m_nativeStatus.all_time_upload;
|
|
|
|
// special case for a seeder who lost its stats, also assume nobody will import a 99% done torrent
|
|
|
|
const int64_t download = (m_nativeStatus.all_time_download < (m_nativeStatus.total_done * 0.01))
|
|
|
|
? m_nativeStatus.total_done
|
|
|
|
: m_nativeStatus.all_time_download;
|
|
|
|
#endif
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2015-06-12 11:18:28 +03:00
|
|
|
if (download == 0)
|
2019-10-17 07:09:25 +03:00
|
|
|
return (upload == 0) ? 0 : MAX_RATIO;
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-02-09 18:40:14 +03:00
|
|
|
const qreal ratio = upload / static_cast<qreal>(download);
|
2019-10-17 07:09:25 +03:00
|
|
|
Q_ASSERT(ratio >= 0);
|
2015-06-12 11:18:28 +03:00
|
|
|
return (ratio > MAX_RATIO) ? MAX_RATIO : ratio;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int TorrentHandle::uploadPayloadRate() const
|
|
|
|
{
|
|
|
|
return m_nativeStatus.upload_payload_rate;
|
|
|
|
}
|
|
|
|
|
|
|
|
int TorrentHandle::downloadPayloadRate() const
|
|
|
|
{
|
|
|
|
return m_nativeStatus.download_payload_rate;
|
|
|
|
}
|
|
|
|
|
2015-10-25 21:59:12 +03:00
|
|
|
qlonglong TorrentHandle::totalPayloadUpload() const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
|
|
|
return m_nativeStatus.total_payload_upload;
|
|
|
|
}
|
|
|
|
|
2015-10-25 21:59:12 +03:00
|
|
|
qlonglong TorrentHandle::totalPayloadDownload() const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
|
|
|
return m_nativeStatus.total_payload_download;
|
|
|
|
}
|
|
|
|
|
|
|
|
int TorrentHandle::connectionsCount() const
|
|
|
|
{
|
|
|
|
return m_nativeStatus.num_connections;
|
|
|
|
}
|
|
|
|
|
|
|
|
int TorrentHandle::connectionsLimit() const
|
|
|
|
{
|
|
|
|
return m_nativeStatus.connections_limit;
|
|
|
|
}
|
|
|
|
|
|
|
|
qlonglong TorrentHandle::nextAnnounce() const
|
|
|
|
{
|
2019-05-06 15:25:06 +03:00
|
|
|
return lt::total_seconds(m_nativeStatus.next_announce);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void TorrentHandle::setName(const QString &name)
|
|
|
|
{
|
|
|
|
if (m_name != name) {
|
|
|
|
m_name = name;
|
2018-07-10 14:37:45 +03:00
|
|
|
m_session->handleTorrentNameChanged(this);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-09 11:56:48 +03:00
|
|
|
bool TorrentHandle::setCategory(const QString &category)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2016-02-09 11:56:48 +03:00
|
|
|
if (m_category != category) {
|
2018-07-23 08:49:34 +03:00
|
|
|
if (!category.isEmpty() && !m_session->categories().contains(category))
|
|
|
|
return false;
|
2016-02-09 11:56:48 +03:00
|
|
|
|
2019-02-09 18:40:14 +03:00
|
|
|
const QString oldCategory = m_category;
|
2016-02-09 11:56:48 +03:00
|
|
|
m_category = category;
|
|
|
|
m_session->handleTorrentCategoryChanged(this, oldCategory);
|
|
|
|
|
2016-05-08 22:47:50 +03:00
|
|
|
if (m_useAutoTMM) {
|
|
|
|
if (!m_session->isDisableAutoTMMWhenCategoryChanged())
|
2017-08-05 15:51:53 +03:00
|
|
|
move_impl(m_session->categorySavePath(m_category), true);
|
2016-02-09 11:56:48 +03:00
|
|
|
else
|
2016-05-08 22:47:50 +03:00
|
|
|
setAutoTMMEnabled(false);
|
2016-02-09 11:56:48 +03:00
|
|
|
}
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
2016-02-09 11:56:48 +03:00
|
|
|
|
|
|
|
return true;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void TorrentHandle::move(QString path)
|
2016-02-09 11:56:48 +03:00
|
|
|
{
|
2016-05-08 22:47:50 +03:00
|
|
|
m_useAutoTMM = false;
|
2016-02-09 11:56:48 +03:00
|
|
|
m_session->handleTorrentSavingModeChanged(this);
|
|
|
|
|
2019-06-16 20:14:15 +03:00
|
|
|
path = Utils::Fs::toUniformPath(path.trimmed());
|
2016-03-06 09:25:55 +03:00
|
|
|
if (path.isEmpty())
|
|
|
|
path = m_session->defaultSavePath();
|
|
|
|
if (!path.endsWith('/'))
|
|
|
|
path += '/';
|
|
|
|
|
2017-08-05 15:51:53 +03:00
|
|
|
move_impl(path, false);
|
2016-02-09 11:56:48 +03:00
|
|
|
}
|
|
|
|
|
2017-08-05 15:51:53 +03:00
|
|
|
void TorrentHandle::move_impl(QString path, bool overwrite)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
|
|
|
if (path == savePath()) return;
|
2017-08-05 15:51:53 +03:00
|
|
|
path = Utils::Fs::toNativePath(path);
|
2015-04-19 18:17:47 +03:00
|
|
|
|
|
|
|
if (!useTempPath()) {
|
2017-08-05 15:51:53 +03:00
|
|
|
moveStorage(path, overwrite);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_savePath = path;
|
|
|
|
m_session->handleTorrentSavePathChanged(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TorrentHandle::forceReannounce(int index)
|
|
|
|
{
|
2017-02-14 20:52:57 +03:00
|
|
|
m_nativeHandle.force_reannounce(0, index);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void TorrentHandle::forceDHTAnnounce()
|
|
|
|
{
|
2017-02-14 20:52:57 +03:00
|
|
|
m_nativeHandle.force_dht_announce();
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void TorrentHandle::forceRecheck()
|
|
|
|
{
|
|
|
|
if (!hasMetadata()) return;
|
|
|
|
|
2018-09-11 14:43:08 +03:00
|
|
|
m_nativeHandle.force_recheck();
|
|
|
|
m_unchecked = false;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-04-01 11:39:29 +03:00
|
|
|
void TorrentHandle::setSequentialDownload(const bool enable)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-04-01 11:39:29 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
|
|
|
m_nativeHandle.set_sequential_download(enable);
|
|
|
|
m_nativeStatus.sequential_download = enable; // prevent return cached value
|
|
|
|
#else
|
|
|
|
if (enable) {
|
2019-05-06 15:25:06 +03:00
|
|
|
m_nativeHandle.set_flags(lt::torrent_flags::sequential_download);
|
|
|
|
m_nativeStatus.flags |= lt::torrent_flags::sequential_download; // prevent return cached value
|
2019-04-01 11:39:29 +03:00
|
|
|
}
|
|
|
|
else {
|
2019-05-06 15:25:06 +03:00
|
|
|
m_nativeHandle.unset_flags(lt::torrent_flags::sequential_download);
|
|
|
|
m_nativeStatus.flags &= ~lt::torrent_flags::sequential_download; // prevent return cached value
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
2019-04-01 11:39:29 +03:00
|
|
|
#endif
|
2019-06-29 14:56:36 +03:00
|
|
|
|
|
|
|
saveResumeData();
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2015-11-26 15:08:01 +03:00
|
|
|
void TorrentHandle::toggleSequentialDownload()
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2015-11-26 15:08:01 +03:00
|
|
|
setSequentialDownload(!isSequentialDownload());
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2018-05-09 17:10:47 +03:00
|
|
|
void TorrentHandle::setFirstLastPiecePriority(const bool enabled)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2018-07-29 11:14:17 +03:00
|
|
|
setFirstLastPiecePriorityImpl(enabled);
|
|
|
|
}
|
|
|
|
|
2019-03-06 08:58:07 +03:00
|
|
|
void TorrentHandle::setFirstLastPiecePriorityImpl(const bool enabled, const QVector<DownloadPriority> &updatedFilePrio)
|
2018-07-29 11:14:17 +03:00
|
|
|
{
|
|
|
|
// Download first and last pieces first for every file in the torrent
|
|
|
|
|
2016-07-15 05:15:10 +03:00
|
|
|
if (!hasMetadata()) {
|
2018-05-09 17:10:47 +03:00
|
|
|
m_needsToSetFirstLastPiecePriority = enabled;
|
2016-07-15 05:15:10 +03:00
|
|
|
return;
|
|
|
|
}
|
2015-11-26 15:08:01 +03:00
|
|
|
|
2019-03-06 08:58:07 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
|
|
|
const std::vector<LTDownloadPriority> filePriorities = !updatedFilePrio.isEmpty() ? toLTDownloadPriorities(updatedFilePrio)
|
|
|
|
: nativeHandle().file_priorities();
|
|
|
|
std::vector<LTDownloadPriority> piecePriorities = nativeHandle().piece_priorities();
|
|
|
|
#else
|
|
|
|
const std::vector<LTDownloadPriority> filePriorities = !updatedFilePrio.isEmpty() ? toLTDownloadPriorities(updatedFilePrio)
|
|
|
|
: nativeHandle().get_file_priorities();
|
|
|
|
std::vector<LTDownloadPriority> piecePriorities = nativeHandle().get_piece_priorities();
|
|
|
|
#endif
|
2018-07-29 11:14:17 +03:00
|
|
|
// 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.
|
2018-05-09 17:10:47 +03:00
|
|
|
for (int index = 0; index < static_cast<int>(filePriorities.size()); ++index) {
|
2019-03-06 08:58:07 +03:00
|
|
|
const LTDownloadPriority filePrio = filePriorities[index];
|
|
|
|
if (filePrio <= LTDownloadPriority {0})
|
2018-05-09 17:10:47 +03:00
|
|
|
continue;
|
|
|
|
|
|
|
|
// Determine the priority to set
|
2019-07-18 19:53:04 +03:00
|
|
|
const LTDownloadPriority newPrio = enabled ? LTDownloadPriority {7} : filePrio;
|
2018-05-09 17:10:47 +03:00
|
|
|
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;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
}
|
2015-09-18 05:58:16 +03:00
|
|
|
|
2018-05-09 17:10:47 +03:00
|
|
|
m_nativeHandle.prioritize_pieces(piecePriorities);
|
|
|
|
|
|
|
|
LogMsg(tr("Download first and last piece first: %1, torrent: '%2'")
|
|
|
|
.arg((enabled ? tr("On") : tr("Off")), name()));
|
2019-06-29 14:56:36 +03:00
|
|
|
|
|
|
|
saveResumeData();
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2015-11-26 15:08:01 +03:00
|
|
|
void TorrentHandle::toggleFirstLastPiecePriority()
|
|
|
|
{
|
|
|
|
setFirstLastPiecePriority(!hasFirstLastPiecePriority());
|
|
|
|
}
|
|
|
|
|
2015-04-19 18:17:47 +03:00
|
|
|
void TorrentHandle::pause()
|
|
|
|
{
|
|
|
|
if (isPaused()) return;
|
|
|
|
|
2019-04-01 11:39:29 +03:00
|
|
|
setAutoManaged(false);
|
2017-02-14 20:52:57 +03:00
|
|
|
m_nativeHandle.pause();
|
2018-09-11 21:00:04 +03:00
|
|
|
|
2020-01-03 18:57:41 +03:00
|
|
|
// Libtorrent doesn't emit a torrent_paused_alert when the
|
|
|
|
// torrent is queued (no I/O)
|
|
|
|
// We test on the cached m_nativeStatus
|
|
|
|
if (isQueued())
|
|
|
|
m_session->handleTorrentPaused(this);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void TorrentHandle::resume(bool forced)
|
2018-04-09 05:55:04 +03:00
|
|
|
{
|
2019-02-01 08:49:33 +03:00
|
|
|
resume_impl(forced);
|
2018-04-09 05:55:04 +03:00
|
|
|
}
|
|
|
|
|
2019-02-01 08:49:33 +03:00
|
|
|
void TorrentHandle::resume_impl(bool forced)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2017-02-14 20:52:57 +03:00
|
|
|
if (hasError())
|
|
|
|
m_nativeHandle.clear_error();
|
2018-09-05 15:59:22 +03:00
|
|
|
|
|
|
|
if (m_hasMissingFiles) {
|
|
|
|
m_hasMissingFiles = false;
|
|
|
|
m_nativeHandle.force_recheck();
|
|
|
|
}
|
|
|
|
|
2019-04-01 11:39:29 +03:00
|
|
|
setAutoManaged(!forced);
|
2017-02-14 20:52:57 +03:00
|
|
|
m_nativeHandle.resume();
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2017-08-05 15:51:53 +03:00
|
|
|
void TorrentHandle::moveStorage(const QString &newPath, bool overwrite)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
|
|
|
if (isMoveInProgress()) {
|
2017-08-13 13:56:03 +03:00
|
|
|
qDebug("enqueue move storage to %s", qUtf8Printable(newPath));
|
2017-08-05 15:51:53 +03:00
|
|
|
m_moveStorageInfo.queuedPath = newPath;
|
|
|
|
m_moveStorageInfo.queuedOverwrite = overwrite;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
else {
|
2016-12-27 17:14:49 +03:00
|
|
|
const QString oldPath = nativeActualSavePath();
|
2015-04-19 18:17:47 +03:00
|
|
|
if (QDir(oldPath) == QDir(newPath)) return;
|
|
|
|
|
2017-08-13 13:56:03 +03:00
|
|
|
qDebug("move storage: %s to %s", qUtf8Printable(oldPath), qUtf8Printable(newPath));
|
2017-02-14 20:52:57 +03:00
|
|
|
// Actually move the storage
|
2019-04-01 11:39:29 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
2017-08-05 15:51:53 +03:00
|
|
|
m_nativeHandle.move_storage(newPath.toUtf8().constData()
|
2019-05-06 15:25:06 +03:00
|
|
|
, (overwrite ? lt::always_replace_files : lt::dont_replace));
|
2019-04-01 11:39:29 +03:00
|
|
|
#else
|
|
|
|
m_nativeHandle.move_storage(newPath.toUtf8().constData()
|
2019-05-06 15:25:06 +03:00
|
|
|
, (overwrite ? lt::move_flags_t::always_replace_files : lt::move_flags_t::dont_replace));
|
2019-04-01 11:39:29 +03:00
|
|
|
#endif
|
2017-08-05 15:51:53 +03:00
|
|
|
m_moveStorageInfo.oldPath = oldPath;
|
|
|
|
m_moveStorageInfo.newPath = newPath;
|
2018-04-09 18:19:33 +03:00
|
|
|
updateState();
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-04 13:49:51 +03:00
|
|
|
void TorrentHandle::renameFile(const int index, const QString &name)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-06-04 15:46:41 +03:00
|
|
|
m_oldPath[LTFileIndex {index}].push_back(filePath(index));
|
2015-07-22 11:41:00 +03:00
|
|
|
++m_renameCount;
|
2019-07-18 19:53:04 +03:00
|
|
|
m_nativeHandle.rename_file(LTFileIndex {index}, Utils::Fs::toNativePath(name).toStdString());
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::saveTorrentFile(const QString &path)
|
|
|
|
{
|
|
|
|
if (!m_torrentInfo.isValid()) return false;
|
2019-03-06 08:58:07 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
2019-05-06 15:25:06 +03:00
|
|
|
const lt::create_torrent torrentCreator = lt::create_torrent(*(m_torrentInfo.nativeInfo()), true);
|
2019-03-06 08:58:07 +03:00
|
|
|
#else
|
2019-05-06 15:25:06 +03:00
|
|
|
const lt::create_torrent torrentCreator = lt::create_torrent(*(m_torrentInfo.nativeInfo()));
|
2019-03-06 08:58:07 +03:00
|
|
|
#endif
|
2019-05-06 15:25:06 +03:00
|
|
|
const lt::entry torrentEntry = torrentCreator.generate();
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-10-27 16:37:23 +03:00
|
|
|
QByteArray out;
|
|
|
|
out.reserve(1024 * 1024); // most torrent file sizes are under 1 MB
|
2019-05-06 15:25:06 +03:00
|
|
|
lt::bencode(std::back_inserter(out), torrentEntry);
|
2019-10-27 16:37:23 +03:00
|
|
|
if (out.isEmpty())
|
|
|
|
return false;
|
|
|
|
|
2015-04-19 18:17:47 +03:00
|
|
|
QFile torrentFile(path);
|
2019-10-27 16:37:23 +03:00
|
|
|
if (torrentFile.open(QIODevice::WriteOnly))
|
|
|
|
return (torrentFile.write(out) == out.size());
|
2015-04-19 18:17:47 +03:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-05-06 15:25:06 +03:00
|
|
|
void TorrentHandle::handleStateUpdate(const lt::torrent_status &nativeStatus)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
|
|
|
updateStatus(nativeStatus);
|
|
|
|
}
|
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
void TorrentHandle::handleStorageMovedAlert(const lt::storage_moved_alert *p)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
|
|
|
if (!isMoveInProgress()) {
|
2015-10-17 17:41:53 +03:00
|
|
|
qWarning() << "Unexpected " << Q_FUNC_INFO << " call.";
|
2015-04-19 18:17:47 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-05-01 19:58:40 +03:00
|
|
|
const QString newPath(p->storage_path());
|
2017-08-05 15:51:53 +03:00
|
|
|
if (newPath != m_moveStorageInfo.newPath) {
|
2015-10-17 17:41:53 +03:00
|
|
|
qWarning() << Q_FUNC_INFO << ": New path doesn't match a path in a queue.";
|
2015-04-19 18:17:47 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-04-09 18:26:01 +03:00
|
|
|
LogMsg(tr("Successfully moved torrent: %1. New path: %2").arg(name(), m_moveStorageInfo.newPath));
|
|
|
|
|
2017-08-13 14:17:12 +03:00
|
|
|
const QDir oldDir {m_moveStorageInfo.oldPath};
|
|
|
|
if ((oldDir == QDir(m_session->torrentTempPath(info())))
|
|
|
|
&& (oldDir != QDir(m_session->tempPath()))) {
|
|
|
|
// torrent without root folder still has it in its temporary save path
|
|
|
|
// so its temp path isn't equal to temp path root
|
2017-08-05 15:51:53 +03:00
|
|
|
qDebug() << "Removing torrent temp folder:" << m_moveStorageInfo.oldPath;
|
|
|
|
Utils::Fs::smartRemoveEmptyFolderTree(m_moveStorageInfo.oldPath);
|
2017-04-26 11:38:50 +03:00
|
|
|
}
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2017-08-05 15:51:53 +03:00
|
|
|
m_moveStorageInfo.newPath.clear();
|
2018-04-09 18:19:33 +03:00
|
|
|
updateStatus();
|
|
|
|
|
2017-08-05 15:51:53 +03:00
|
|
|
if (!m_moveStorageInfo.queuedPath.isEmpty()) {
|
|
|
|
moveStorage(m_moveStorageInfo.queuedPath, m_moveStorageInfo.queuedOverwrite);
|
|
|
|
m_moveStorageInfo.queuedPath.clear();
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!useTempPath()) {
|
|
|
|
m_savePath = newPath;
|
|
|
|
m_session->handleTorrentSavePathChanged(this);
|
|
|
|
}
|
|
|
|
|
2015-07-22 11:41:00 +03:00
|
|
|
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
|
|
|
|
m_moveFinishedTriggers.takeFirst()();
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
void TorrentHandle::handleStorageMovedFailedAlert(const lt::storage_moved_failed_alert *p)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
|
|
|
if (!isMoveInProgress()) {
|
2015-10-17 17:41:53 +03:00
|
|
|
qWarning() << "Unexpected " << Q_FUNC_INFO << " call.";
|
2015-04-19 18:17:47 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-05-05 16:21:33 +03:00
|
|
|
LogMsg(tr("Could not move torrent: '%1'. Reason: %2")
|
|
|
|
.arg(name(), QString::fromStdString(p->message())), Log::CRITICAL);
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2017-08-05 15:51:53 +03:00
|
|
|
m_moveStorageInfo.newPath.clear();
|
2018-04-09 18:19:33 +03:00
|
|
|
updateStatus();
|
|
|
|
|
2017-08-05 15:51:53 +03:00
|
|
|
if (!m_moveStorageInfo.queuedPath.isEmpty()) {
|
|
|
|
moveStorage(m_moveStorageInfo.queuedPath, m_moveStorageInfo.queuedOverwrite);
|
|
|
|
m_moveStorageInfo.queuedPath.clear();
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2015-07-22 11:41:00 +03:00
|
|
|
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
|
|
|
|
m_moveFinishedTriggers.takeFirst()();
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
void TorrentHandle::handleTrackerReplyAlert(const lt::tracker_reply_alert *p)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-02-09 18:40:14 +03:00
|
|
|
const QString trackerUrl(p->tracker_url());
|
2017-08-13 13:56:03 +03:00
|
|
|
qDebug("Received a tracker reply from %s (Num_peers = %d)", qUtf8Printable(trackerUrl), p->num_peers);
|
2015-04-19 18:17:47 +03:00
|
|
|
// Connection was successful now. Remove possible old errors
|
2019-05-17 06:20:47 +03:00
|
|
|
m_trackerInfos[trackerUrl] = {{}, p->num_peers};
|
2015-04-19 18:17:47 +03:00
|
|
|
|
|
|
|
m_session->handleTorrentTrackerReply(this, trackerUrl);
|
|
|
|
}
|
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
void TorrentHandle::handleTrackerWarningAlert(const lt::tracker_warning_alert *p)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2018-04-30 07:26:32 +03:00
|
|
|
const QString trackerUrl = p->tracker_url();
|
2018-04-30 07:27:57 +03:00
|
|
|
const QString message = p->warning_message();
|
2018-04-30 07:26:32 +03:00
|
|
|
|
2015-04-19 18:17:47 +03:00
|
|
|
// Connection was successful now but there is a warning message
|
|
|
|
m_trackerInfos[trackerUrl].lastMessage = message; // Store warning message
|
|
|
|
|
|
|
|
m_session->handleTorrentTrackerWarning(this, trackerUrl);
|
|
|
|
}
|
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
void TorrentHandle::handleTrackerErrorAlert(const lt::tracker_error_alert *p)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2018-04-30 07:26:32 +03:00
|
|
|
const QString trackerUrl = p->tracker_url();
|
2018-04-30 07:27:57 +03:00
|
|
|
const QString message = p->error_message();
|
2018-04-30 07:26:32 +03:00
|
|
|
|
2015-04-19 18:17:47 +03:00
|
|
|
m_trackerInfos[trackerUrl].lastMessage = message;
|
|
|
|
|
2019-12-23 01:28:00 +03:00
|
|
|
// Starting with libtorrent 1.2.x each tracker has multiple local endpoints from which
|
|
|
|
// an announce is attempted. Some endpoints might succeed while others might fail.
|
2020-02-14 12:39:57 +03:00
|
|
|
// Emit the signal only if all endpoints have failed.
|
2019-12-23 01:28:00 +03:00
|
|
|
const QVector<TrackerEntry> trackerList = trackers();
|
|
|
|
const auto iter = std::find_if(trackerList.cbegin(), trackerList.cend(), [&trackerUrl](const TrackerEntry &entry)
|
|
|
|
{
|
|
|
|
return (entry.url() == trackerUrl);
|
|
|
|
});
|
2020-02-14 12:39:57 +03:00
|
|
|
if ((iter != trackerList.cend()) && (iter->status() == TrackerEntry::NotWorking))
|
2019-12-23 01:28:00 +03:00
|
|
|
m_session->handleTorrentTrackerError(this, trackerUrl);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
void TorrentHandle::handleTorrentCheckedAlert(const lt::torrent_checked_alert *p)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
|
|
|
Q_UNUSED(p);
|
2018-09-05 15:59:22 +03:00
|
|
|
qDebug("\"%s\" have just finished checking", qUtf8Printable(name()));
|
|
|
|
|
2020-01-03 18:57:41 +03:00
|
|
|
if (m_fastresumeDataRejected && !m_hasMissingFiles) {
|
|
|
|
saveResumeData();
|
|
|
|
m_fastresumeDataRejected = false;
|
2018-09-05 15:59:22 +03:00
|
|
|
}
|
2015-04-19 18:17:47 +03:00
|
|
|
|
|
|
|
updateStatus();
|
|
|
|
|
2018-09-05 15:59:22 +03:00
|
|
|
if (!m_hasMissingFiles) {
|
|
|
|
if ((progress() < 1.0) && (wantedSize() > 0))
|
|
|
|
m_hasSeedStatus = false;
|
|
|
|
else if (progress() == 1.0)
|
|
|
|
m_hasSeedStatus = true;
|
2015-10-26 01:53:04 +03:00
|
|
|
|
2018-09-05 15:59:22 +03:00
|
|
|
adjustActualSavePath();
|
|
|
|
manageIncompleteFiles();
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
m_session->handleTorrentChecked(this);
|
|
|
|
}
|
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
void TorrentHandle::handleTorrentFinishedAlert(const lt::torrent_finished_alert *p)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
|
|
|
Q_UNUSED(p);
|
2018-09-05 15:59:22 +03:00
|
|
|
qDebug("Got a torrent finished alert for \"%s\"", qUtf8Printable(name()));
|
2015-04-19 18:17:47 +03:00
|
|
|
qDebug("Torrent has seed status: %s", m_hasSeedStatus ? "yes" : "no");
|
2019-06-28 21:24:39 +03:00
|
|
|
m_hasMissingFiles = false;
|
2015-04-19 18:17:47 +03:00
|
|
|
if (m_hasSeedStatus) return;
|
|
|
|
|
|
|
|
updateStatus();
|
|
|
|
m_hasSeedStatus = true;
|
|
|
|
|
|
|
|
adjustActualSavePath();
|
2016-04-25 15:30:41 +03:00
|
|
|
manageIncompleteFiles();
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2016-04-20 11:36:07 +03:00
|
|
|
const bool recheckTorrentsOnCompletion = Preferences::instance()->recheckTorrentsOnCompletion();
|
2018-09-11 14:43:08 +03:00
|
|
|
if (isMoveInProgress() || (m_renameCount > 0)) {
|
2016-04-20 11:36:07 +03:00
|
|
|
if (recheckTorrentsOnCompletion)
|
2019-01-06 14:11:05 +03:00
|
|
|
m_moveFinishedTriggers.append([this]() { forceRecheck(); });
|
|
|
|
m_moveFinishedTriggers.append([this]() { m_session->handleTorrentFinished(this); });
|
2016-04-20 11:36:07 +03:00
|
|
|
}
|
|
|
|
else {
|
2018-09-11 14:43:08 +03:00
|
|
|
if (recheckTorrentsOnCompletion && m_unchecked)
|
2016-04-20 11:36:07 +03:00
|
|
|
forceRecheck();
|
2015-07-22 11:41:00 +03:00
|
|
|
m_session->handleTorrentFinished(this);
|
2016-04-20 11:36:07 +03:00
|
|
|
}
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
void TorrentHandle::handleTorrentPausedAlert(const lt::torrent_paused_alert *p)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
|
|
|
Q_UNUSED(p);
|
2018-09-05 15:59:22 +03:00
|
|
|
|
2020-01-03 18:57:41 +03:00
|
|
|
updateStatus();
|
|
|
|
m_speedMonitor.reset();
|
2020-01-01 14:29:58 +03:00
|
|
|
|
2020-01-03 18:57:41 +03:00
|
|
|
m_session->handleTorrentPaused(this);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
void TorrentHandle::handleTorrentResumedAlert(const lt::torrent_resumed_alert *p)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
|
|
|
Q_UNUSED(p);
|
2018-07-11 15:44:15 +03:00
|
|
|
|
2020-01-03 18:57:41 +03:00
|
|
|
m_session->handleTorrentResumed(this);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
void TorrentHandle::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-06-28 15:22:14 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
2015-06-04 11:03:19 +03:00
|
|
|
const bool useDummyResumeData = !(p && p->resume_data);
|
2019-05-09 07:45:52 +03:00
|
|
|
lt::entry dummyEntry;
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
lt::entry &resumeData = useDummyResumeData ? dummyEntry : *(p->resume_data);
|
2019-06-28 15:22:14 +03:00
|
|
|
#else
|
|
|
|
const bool useDummyResumeData = !p;
|
|
|
|
|
|
|
|
lt::entry resumeData = useDummyResumeData ? lt::entry() : lt::write_resume_data(p->params);
|
|
|
|
#endif
|
|
|
|
|
2020-01-03 18:57:41 +03:00
|
|
|
updateStatus();
|
|
|
|
|
2015-06-04 11:03:19 +03:00
|
|
|
if (useDummyResumeData) {
|
2017-03-06 18:51:35 +03:00
|
|
|
resumeData["qBt-magnetUri"] = toMagnetUri().toStdString();
|
2019-06-28 21:24:39 +03:00
|
|
|
resumeData["paused"] = isPaused();
|
|
|
|
resumeData["auto_managed"] = isAutoManaged();
|
2016-07-15 05:15:10 +03:00
|
|
|
// Both firstLastPiecePriority and sequential need to be stored in the
|
|
|
|
// resume data if there is no metadata, otherwise they won't be
|
|
|
|
// restored if qBittorrent quits before the metadata are retrieved:
|
|
|
|
resumeData["qBt-firstLastPiecePriority"] = hasFirstLastPiecePriority();
|
|
|
|
resumeData["qBt-sequential"] = isSequentialDownload();
|
2019-08-26 07:30:22 +03:00
|
|
|
|
|
|
|
resumeData["qBt-addedTime"] = addedTime().toSecsSinceEpoch();
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
2016-05-13 21:32:47 +03:00
|
|
|
else {
|
2019-02-09 18:40:14 +03:00
|
|
|
const auto savePath = resumeData.find_key("save_path")->string();
|
2020-02-11 05:56:04 +03:00
|
|
|
resumeData["save_path"] = Profile::instance()->toPortablePath(QString::fromStdString(savePath)).toStdString();
|
2016-05-13 21:32:47 +03:00
|
|
|
}
|
2020-02-11 05:56:04 +03:00
|
|
|
resumeData["qBt-savePath"] = m_useAutoTMM ? "" : Profile::instance()->toPortablePath(m_savePath).toStdString();
|
2017-10-08 09:59:52 +03:00
|
|
|
resumeData["qBt-ratioLimit"] = static_cast<int>(m_ratioLimit * 1000);
|
2017-09-06 22:47:05 +03:00
|
|
|
resumeData["qBt-seedingTimeLimit"] = m_seedingTimeLimit;
|
2017-03-06 18:51:35 +03:00
|
|
|
resumeData["qBt-category"] = m_category.toStdString();
|
2017-06-05 03:22:17 +03:00
|
|
|
resumeData["qBt-tags"] = setToEntryList(m_tags);
|
2017-03-06 18:51:35 +03:00
|
|
|
resumeData["qBt-name"] = m_name.toStdString();
|
2015-06-04 11:03:19 +03:00
|
|
|
resumeData["qBt-seedStatus"] = m_hasSeedStatus;
|
|
|
|
resumeData["qBt-tempPathDisabled"] = m_tempPathDisabled;
|
2019-03-06 08:58:07 +03:00
|
|
|
resumeData["qBt-queuePosition"] = (static_cast<int>(nativeHandle().queue_position()) + 1); // qBt starts queue at 1
|
2016-01-04 16:00:50 +03:00
|
|
|
resumeData["qBt-hasRootFolder"] = m_hasRootFolder;
|
2015-06-04 11:03:19 +03:00
|
|
|
|
2020-01-03 18:57:41 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
|
|
|
if (m_nativeStatus.stop_when_ready) {
|
|
|
|
#else
|
|
|
|
if (m_nativeStatus.flags & lt::torrent_flags::stop_when_ready) {
|
|
|
|
#endif
|
2019-06-28 21:24:39 +03:00
|
|
|
// We need to redefine these values when torrent starting/rechecking
|
|
|
|
// in "paused" state since native values can be logically wrong
|
|
|
|
// (torrent can be not paused and auto_managed when it is checking).
|
|
|
|
resumeData["paused"] = true;
|
|
|
|
resumeData["auto_managed"] = false;
|
|
|
|
}
|
|
|
|
|
2015-06-04 11:03:19 +03:00
|
|
|
m_session->handleTorrentResumeDataReady(this, resumeData);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
void TorrentHandle::handleSaveResumeDataFailedAlert(const lt::save_resume_data_failed_alert *p)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2015-06-04 11:03:19 +03:00
|
|
|
// if torrent has no metadata we should save dummy fastresume data
|
|
|
|
// containing Magnet URI and qBittorrent own resume data only
|
2019-06-10 21:11:28 +03:00
|
|
|
if (p->error.value() == lt::errors::no_metadata) {
|
2018-04-15 13:06:31 +03:00
|
|
|
handleSaveResumeDataAlert(nullptr);
|
2019-06-10 21:11:28 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
LogMsg(tr("Save resume data failed. Torrent: \"%1\", error: \"%2\"")
|
2019-07-26 19:24:36 +03:00
|
|
|
.arg(name(), QString::fromLocal8Bit(p->error.message().c_str())), Log::CRITICAL);
|
2015-06-04 11:03:19 +03:00
|
|
|
m_session->handleTorrentResumeDataFailed(this);
|
2019-06-10 21:11:28 +03:00
|
|
|
}
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
void TorrentHandle::handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-07-22 14:29:14 +03:00
|
|
|
m_fastresumeDataRejected = true;
|
|
|
|
|
2019-05-06 15:25:06 +03:00
|
|
|
if (p->error.value() == lt::errors::mismatching_file_size) {
|
2015-04-19 18:17:47 +03:00
|
|
|
// Mismatching file size (files were probably moved)
|
|
|
|
m_hasMissingFiles = true;
|
2018-05-28 22:42:01 +03:00
|
|
|
LogMsg(tr("File sizes mismatch for torrent '%1', pausing it.").arg(name()), Log::CRITICAL);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
else {
|
2018-05-05 16:21:33 +03:00
|
|
|
LogMsg(tr("Fast resume data was rejected for torrent '%1'. Reason: %2. Checking again...")
|
2019-07-22 14:29:14 +03:00
|
|
|
.arg(name(), QString::fromStdString(p->message())), Log::WARNING);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
void TorrentHandle::handleFileRenamedAlert(const lt::file_renamed_alert *p)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2018-09-30 12:19:42 +03:00
|
|
|
// We don't really need to call updateStatus() in this place.
|
|
|
|
// All we need to do is make sure we have a valid instance of the TorrentInfo object.
|
|
|
|
m_torrentInfo = TorrentInfo {m_nativeHandle.torrent_file()};
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2019-06-04 15:46:41 +03:00
|
|
|
// 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
|
2019-07-21 19:53:16 +03:00
|
|
|
const QString oldFilePath = m_oldPath[p->index].takeFirst();
|
2019-06-16 20:14:15 +03:00
|
|
|
const QString newFilePath = Utils::Fs::toUniformPath(p->new_name());
|
2019-06-04 15:46:41 +03:00
|
|
|
|
2019-07-21 19:53:16 +03:00
|
|
|
if (m_oldPath[p->index].isEmpty())
|
|
|
|
m_oldPath.remove(p->index);
|
2019-06-04 15:46:41 +03:00
|
|
|
|
|
|
|
QVector<QStringRef> oldPathParts = oldFilePath.splitRef('/', QString::SkipEmptyParts);
|
|
|
|
oldPathParts.removeLast(); // drop file name part
|
|
|
|
QVector<QStringRef> newPathParts = newFilePath.splitRef('/', QString::SkipEmptyParts);
|
|
|
|
newPathParts.removeLast(); // drop file name part
|
|
|
|
|
|
|
|
#if defined(Q_OS_WIN)
|
|
|
|
const Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive;
|
|
|
|
#else
|
|
|
|
const Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
int pathIdx = 0;
|
|
|
|
while ((pathIdx < oldPathParts.size()) && (pathIdx < newPathParts.size())) {
|
|
|
|
if (oldPathParts[pathIdx].compare(newPathParts[pathIdx], caseSensitivity) != 0)
|
|
|
|
break;
|
|
|
|
++pathIdx;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = (oldPathParts.size() - 1); i >= pathIdx; --i) {
|
|
|
|
QDir().rmdir(savePath() + Utils::String::join(oldPathParts, QLatin1String("/")));
|
|
|
|
oldPathParts.removeLast();
|
|
|
|
}
|
|
|
|
|
2015-07-22 11:41:00 +03:00
|
|
|
--m_renameCount;
|
|
|
|
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
|
|
|
|
m_moveFinishedTriggers.takeFirst()();
|
2019-06-11 06:59:08 +03:00
|
|
|
|
|
|
|
if (isPaused() && (m_renameCount == 0))
|
|
|
|
saveResumeData(); // otherwise the new path will not be saved
|
2015-07-22 11:41:00 +03:00
|
|
|
}
|
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
void TorrentHandle::handleFileRenameFailedAlert(const lt::file_rename_failed_alert *p)
|
2015-07-22 11:41:00 +03:00
|
|
|
{
|
2019-06-04 13:49:51 +03:00
|
|
|
LogMsg(tr("File rename failed. Torrent: \"%1\", file: \"%2\", reason: \"%3\"")
|
2019-07-18 19:53:04 +03:00
|
|
|
.arg(name(), filePath(LTUnderlyingType<LTFileIndex> {p->index})
|
2019-07-26 19:24:36 +03:00
|
|
|
, QString::fromLocal8Bit(p->error.message().c_str())), Log::WARNING);
|
2019-06-04 13:49:51 +03:00
|
|
|
|
2019-07-21 19:53:16 +03:00
|
|
|
m_oldPath[p->index].removeFirst();
|
|
|
|
if (m_oldPath[p->index].isEmpty())
|
|
|
|
m_oldPath.remove(p->index);
|
2019-06-04 15:46:41 +03:00
|
|
|
|
2015-07-22 11:41:00 +03:00
|
|
|
--m_renameCount;
|
|
|
|
while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
|
|
|
|
m_moveFinishedTriggers.takeFirst()();
|
2019-06-11 06:59:08 +03:00
|
|
|
|
|
|
|
if (isPaused() && (m_renameCount == 0))
|
|
|
|
saveResumeData(); // otherwise the new path will not be saved
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
void TorrentHandle::handleFileCompletedAlert(const lt::file_completed_alert *p)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2018-09-30 12:19:42 +03:00
|
|
|
// We don't really need to call updateStatus() in this place.
|
|
|
|
// All we need to do is make sure we have a valid instance of the TorrentInfo object.
|
|
|
|
m_torrentInfo = TorrentInfo {m_nativeHandle.torrent_file()};
|
2015-04-19 18:17:47 +03:00
|
|
|
|
2017-08-13 13:56:03 +03:00
|
|
|
qDebug("A file completed download in torrent \"%s\"", qUtf8Printable(name()));
|
2015-04-19 18:17:47 +03:00
|
|
|
if (m_session->isAppendExtensionEnabled()) {
|
2019-07-18 19:53:04 +03:00
|
|
|
QString name = filePath(LTUnderlyingType<LTFileIndex> {p->index});
|
2015-04-19 18:17:47 +03:00
|
|
|
if (name.endsWith(QB_EXT)) {
|
|
|
|
const QString oldName = name;
|
2016-04-19 09:54:48 +03:00
|
|
|
name.chop(QB_EXT.size());
|
2017-08-13 13:56:03 +03:00
|
|
|
qDebug("Renaming %s to %s", qUtf8Printable(oldName), qUtf8Printable(name));
|
2019-07-18 19:53:04 +03:00
|
|
|
renameFile(LTUnderlyingType<LTFileIndex> {p->index}, name);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-06 15:25:06 +03:00
|
|
|
void TorrentHandle::handleMetadataReceivedAlert(const lt::metadata_received_alert *p)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
|
|
|
Q_UNUSED(p);
|
2017-08-13 13:56:03 +03:00
|
|
|
qDebug("Metadata received for torrent %s.", qUtf8Printable(name()));
|
2015-04-19 18:17:47 +03:00
|
|
|
updateStatus();
|
2015-06-12 15:35:20 +03:00
|
|
|
if (m_session->isAppendExtensionEnabled())
|
2016-04-25 15:30:41 +03:00
|
|
|
manageIncompleteFiles();
|
2016-01-04 16:00:50 +03:00
|
|
|
if (!m_hasRootFolder)
|
|
|
|
m_torrentInfo.stripRootFolder();
|
|
|
|
if (filesCount() == 1)
|
|
|
|
m_hasRootFolder = false;
|
2015-04-19 18:17:47 +03:00
|
|
|
m_session->handleTorrentMetadataReceived(this);
|
|
|
|
|
|
|
|
if (isPaused()) {
|
|
|
|
// XXX: Unfortunately libtorrent-rasterbar does not send a torrent_paused_alert
|
|
|
|
// and the torrent can be paused when metadata is received
|
|
|
|
m_speedMonitor.reset();
|
|
|
|
m_session->handleTorrentPaused(this);
|
|
|
|
}
|
2017-05-09 08:51:12 +03:00
|
|
|
|
2016-07-15 05:15:10 +03:00
|
|
|
// If first/last piece priority was specified when adding this torrent, we can set it
|
|
|
|
// now that we have metadata:
|
|
|
|
if (m_needsToSetFirstLastPiecePriority) {
|
|
|
|
setFirstLastPiecePriority(true);
|
|
|
|
m_needsToSetFirstLastPiecePriority = false;
|
|
|
|
}
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-05-27 04:30:49 +03:00
|
|
|
void TorrentHandle::handlePerformanceAlert(const lt::performance_alert *p) const
|
|
|
|
{
|
|
|
|
LogMsg((tr("Performance alert: ") + QString::fromStdString(p->message()))
|
|
|
|
, Log::INFO);
|
|
|
|
}
|
|
|
|
|
2015-04-19 18:17:47 +03:00
|
|
|
void TorrentHandle::handleTempPathChanged()
|
|
|
|
{
|
|
|
|
adjustActualSavePath();
|
|
|
|
}
|
|
|
|
|
2016-02-09 11:56:48 +03:00
|
|
|
void TorrentHandle::handleCategorySavePathChanged()
|
|
|
|
{
|
2016-05-08 22:47:50 +03:00
|
|
|
if (m_useAutoTMM)
|
2017-08-05 15:51:53 +03:00
|
|
|
move_impl(m_session->categorySavePath(m_category), true);
|
2016-02-09 11:56:48 +03:00
|
|
|
}
|
|
|
|
|
2015-04-19 18:17:47 +03:00
|
|
|
void TorrentHandle::handleAppendExtensionToggled()
|
|
|
|
{
|
|
|
|
if (!hasMetadata()) return;
|
|
|
|
|
2016-04-25 15:30:41 +03:00
|
|
|
manageIncompleteFiles();
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
void TorrentHandle::handleAlert(const lt::alert *a)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
|
|
|
switch (a->type()) {
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::file_renamed_alert::alert_type:
|
|
|
|
handleFileRenamedAlert(static_cast<const lt::file_renamed_alert*>(a));
|
2015-04-19 18:17:47 +03:00
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::file_rename_failed_alert::alert_type:
|
|
|
|
handleFileRenameFailedAlert(static_cast<const lt::file_rename_failed_alert*>(a));
|
2015-07-22 11:41:00 +03:00
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::file_completed_alert::alert_type:
|
|
|
|
handleFileCompletedAlert(static_cast<const lt::file_completed_alert*>(a));
|
2015-04-19 18:17:47 +03:00
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::torrent_finished_alert::alert_type:
|
|
|
|
handleTorrentFinishedAlert(static_cast<const lt::torrent_finished_alert*>(a));
|
2015-04-19 18:17:47 +03:00
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::save_resume_data_alert::alert_type:
|
|
|
|
handleSaveResumeDataAlert(static_cast<const lt::save_resume_data_alert*>(a));
|
2015-04-19 18:17:47 +03:00
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::save_resume_data_failed_alert::alert_type:
|
|
|
|
handleSaveResumeDataFailedAlert(static_cast<const lt::save_resume_data_failed_alert*>(a));
|
2015-04-19 18:17:47 +03:00
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::storage_moved_alert::alert_type:
|
|
|
|
handleStorageMovedAlert(static_cast<const lt::storage_moved_alert*>(a));
|
2015-04-19 18:17:47 +03:00
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::storage_moved_failed_alert::alert_type:
|
|
|
|
handleStorageMovedFailedAlert(static_cast<const lt::storage_moved_failed_alert*>(a));
|
2015-04-19 18:17:47 +03:00
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::torrent_paused_alert::alert_type:
|
|
|
|
handleTorrentPausedAlert(static_cast<const lt::torrent_paused_alert*>(a));
|
2015-04-19 18:17:47 +03:00
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::torrent_resumed_alert::alert_type:
|
|
|
|
handleTorrentResumedAlert(static_cast<const lt::torrent_resumed_alert*>(a));
|
2018-07-10 14:37:45 +03:00
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::tracker_error_alert::alert_type:
|
|
|
|
handleTrackerErrorAlert(static_cast<const lt::tracker_error_alert*>(a));
|
2015-04-19 18:17:47 +03:00
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::tracker_reply_alert::alert_type:
|
|
|
|
handleTrackerReplyAlert(static_cast<const lt::tracker_reply_alert*>(a));
|
2015-04-19 18:17:47 +03:00
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::tracker_warning_alert::alert_type:
|
|
|
|
handleTrackerWarningAlert(static_cast<const lt::tracker_warning_alert*>(a));
|
2015-04-19 18:17:47 +03:00
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::metadata_received_alert::alert_type:
|
|
|
|
handleMetadataReceivedAlert(static_cast<const lt::metadata_received_alert*>(a));
|
2015-04-19 18:17:47 +03:00
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::fastresume_rejected_alert::alert_type:
|
|
|
|
handleFastResumeRejectedAlert(static_cast<const lt::fastresume_rejected_alert*>(a));
|
2015-04-19 18:17:47 +03:00
|
|
|
break;
|
2019-05-06 15:25:06 +03:00
|
|
|
case lt::torrent_checked_alert::alert_type:
|
|
|
|
handleTorrentCheckedAlert(static_cast<const lt::torrent_checked_alert*>(a));
|
2015-04-19 18:17:47 +03:00
|
|
|
break;
|
2019-05-27 04:30:49 +03:00
|
|
|
case lt::performance_alert::alert_type:
|
|
|
|
handlePerformanceAlert(static_cast<const lt::performance_alert*>(a));
|
|
|
|
break;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-25 15:30:41 +03:00
|
|
|
void TorrentHandle::manageIncompleteFiles()
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2016-04-25 15:30:41 +03:00
|
|
|
const bool isAppendExtensionEnabled = m_session->isAppendExtensionEnabled();
|
2019-02-09 18:40:14 +03:00
|
|
|
const QVector<qreal> fp = filesProgress();
|
2018-06-06 16:48:17 +03:00
|
|
|
if (fp.size() != filesCount()) {
|
2017-01-21 16:35:09 +03:00
|
|
|
qDebug() << "skip manageIncompleteFiles because of invalid torrent meta-data or empty file-progress";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-04-19 18:17:47 +03:00
|
|
|
for (int i = 0; i < filesCount(); ++i) {
|
2016-04-14 11:06:34 +03:00
|
|
|
QString name = filePath(i);
|
2016-04-25 15:30:41 +03:00
|
|
|
if (isAppendExtensionEnabled && (fileSize(i) > 0) && (fp[i] < 1)) {
|
2015-04-19 18:17:47 +03:00
|
|
|
if (!name.endsWith(QB_EXT)) {
|
|
|
|
const QString newName = name + QB_EXT;
|
2016-04-14 11:06:34 +03:00
|
|
|
qDebug() << "Renaming" << name << "to" << newName;
|
2015-04-19 18:17:47 +03:00
|
|
|
renameFile(i, newName);
|
|
|
|
}
|
|
|
|
}
|
2016-04-14 11:06:34 +03:00
|
|
|
else {
|
|
|
|
if (name.endsWith(QB_EXT)) {
|
|
|
|
const QString oldName = name;
|
2016-04-19 09:54:48 +03:00
|
|
|
name.chop(QB_EXT.size());
|
2016-04-14 11:06:34 +03:00
|
|
|
qDebug() << "Renaming" << oldName << "to" << name;
|
|
|
|
renameFile(i, name);
|
|
|
|
}
|
|
|
|
}
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TorrentHandle::adjustActualSavePath()
|
2015-06-12 15:37:52 +03:00
|
|
|
{
|
|
|
|
if (!isMoveInProgress())
|
|
|
|
adjustActualSavePath_impl();
|
|
|
|
else
|
2019-01-06 14:11:05 +03:00
|
|
|
m_moveFinishedTriggers.append([this]() { adjustActualSavePath_impl(); });
|
2015-06-12 15:37:52 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void TorrentHandle::adjustActualSavePath_impl()
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
|
|
|
QString path;
|
|
|
|
if (!useTempPath()) {
|
|
|
|
// Disabling temp dir
|
|
|
|
// Moving all torrents to their destination folder
|
|
|
|
path = savePath();
|
|
|
|
}
|
|
|
|
else {
|
2017-07-28 12:13:57 +03:00
|
|
|
// Moving all downloading torrents to temporary folder
|
|
|
|
path = m_session->torrentTempPath(info());
|
|
|
|
qDebug() << "Moving torrent to its temporary folder:" << path;
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2017-08-05 15:51:53 +03:00
|
|
|
moveStorage(Utils::Fs::toNativePath(path), true);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
lt::torrent_handle TorrentHandle::nativeHandle() const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
|
|
|
return m_nativeHandle;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TorrentHandle::updateTorrentInfo()
|
|
|
|
{
|
|
|
|
if (!hasMetadata()) return;
|
2019-01-08 15:52:12 +03:00
|
|
|
|
2016-01-20 10:15:10 +03:00
|
|
|
m_torrentInfo = TorrentInfo(m_nativeStatus.torrent_file.lock());
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::isMoveInProgress() const
|
|
|
|
{
|
2017-08-05 15:51:53 +03:00
|
|
|
return !m_moveStorageInfo.newPath.isEmpty();
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TorrentHandle::useTempPath() const
|
|
|
|
{
|
2015-12-23 14:13:49 +03:00
|
|
|
return !m_tempPathDisabled && m_session->isTempPathEnabled() && !(isSeed() || m_hasSeedStatus);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void TorrentHandle::updateStatus()
|
|
|
|
{
|
2017-02-14 20:52:57 +03:00
|
|
|
updateStatus(m_nativeHandle.status());
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-05-09 07:45:52 +03:00
|
|
|
void TorrentHandle::updateStatus(const lt::torrent_status &nativeStatus)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
|
|
|
m_nativeStatus = nativeStatus;
|
|
|
|
|
|
|
|
updateState();
|
|
|
|
updateTorrentInfo();
|
2018-09-11 14:43:08 +03:00
|
|
|
|
|
|
|
// NOTE: Don't change the order of these conditionals!
|
|
|
|
// Otherwise it will not work properly since torrent can be CheckingDownloading.
|
|
|
|
if (isChecking())
|
|
|
|
m_unchecked = false;
|
|
|
|
else if (isDownloading())
|
|
|
|
m_unchecked = true;
|
2019-05-27 10:15:59 +03:00
|
|
|
|
|
|
|
m_speedMonitor.addSample({nativeStatus.download_payload_rate
|
|
|
|
, nativeStatus.upload_payload_rate});
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void TorrentHandle::setRatioLimit(qreal limit)
|
|
|
|
{
|
|
|
|
if (limit < USE_GLOBAL_RATIO)
|
|
|
|
limit = NO_RATIO_LIMIT;
|
|
|
|
else if (limit > MAX_RATIO)
|
|
|
|
limit = MAX_RATIO;
|
|
|
|
|
|
|
|
if (m_ratioLimit != limit) {
|
|
|
|
m_ratioLimit = limit;
|
2016-02-07 20:31:50 +03:00
|
|
|
m_session->handleTorrentShareLimitChanged(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TorrentHandle::setSeedingTimeLimit(int limit)
|
|
|
|
{
|
|
|
|
if (limit < USE_GLOBAL_SEEDING_TIME)
|
|
|
|
limit = NO_SEEDING_TIME_LIMIT;
|
|
|
|
else if (limit > MAX_SEEDING_TIME)
|
|
|
|
limit = MAX_SEEDING_TIME;
|
|
|
|
|
|
|
|
if (m_seedingTimeLimit != limit) {
|
|
|
|
m_seedingTimeLimit = limit;
|
|
|
|
m_session->handleTorrentShareLimitChanged(this);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-09 18:40:14 +03:00
|
|
|
void TorrentHandle::setUploadLimit(const int limit)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2017-02-14 20:52:57 +03:00
|
|
|
m_nativeHandle.set_upload_limit(limit);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-02-09 18:40:14 +03:00
|
|
|
void TorrentHandle::setDownloadLimit(const int limit)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2017-02-14 20:52:57 +03:00
|
|
|
m_nativeHandle.set_download_limit(limit);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-02-09 18:40:14 +03:00
|
|
|
void TorrentHandle::setSuperSeeding(const bool enable)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2019-04-01 11:39:29 +03:00
|
|
|
#if (LIBTORRENT_VERSION_NUM < 10200)
|
2017-02-14 20:52:57 +03:00
|
|
|
m_nativeHandle.super_seeding(enable);
|
2019-04-01 11:39:29 +03:00
|
|
|
#else
|
|
|
|
if (enable)
|
2019-05-06 15:25:06 +03:00
|
|
|
m_nativeHandle.set_flags(lt::torrent_flags::super_seeding);
|
2019-04-01 11:39:29 +03:00
|
|
|
else
|
2019-05-06 15:25:06 +03:00
|
|
|
m_nativeHandle.unset_flags(lt::torrent_flags::super_seeding);
|
2019-04-01 11:39:29 +03:00
|
|
|
#endif
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-10-02 14:19:33 +03:00
|
|
|
void TorrentHandle::flushCache() const
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2017-02-14 20:52:57 +03:00
|
|
|
m_nativeHandle.flush_cache();
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
QString TorrentHandle::toMagnetUri() const
|
|
|
|
{
|
2019-05-06 15:25:06 +03:00
|
|
|
return QString::fromStdString(lt::make_magnet_uri(m_nativeHandle));
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
|
2019-03-06 08:58:07 +03:00
|
|
|
void TorrentHandle::prioritizeFiles(const QVector<DownloadPriority> &priorities)
|
2015-04-19 18:17:47 +03:00
|
|
|
{
|
2015-11-26 15:08:01 +03:00
|
|
|
if (!hasMetadata()) return;
|
2015-04-19 18:17:47 +03:00
|
|
|
if (priorities.size() != filesCount()) return;
|
|
|
|
|
2015-11-26 15:08:01 +03:00
|
|
|
// Save first/last piece first option state
|
2018-07-29 11:02:57 +03:00
|
|
|
const bool firstLastPieceFirst = hasFirstLastPiecePriority();
|
2015-11-26 15:08:01 +03:00
|
|
|
|
2015-10-25 02:40:26 +03:00
|
|
|
// Reset 'm_hasSeedStatus' if needed in order to react again to
|
|
|
|
// 'torrent_finished_alert' and eg show tray notifications
|
2019-02-09 18:40:14 +03:00
|
|
|
const QVector<qreal> progress = filesProgress();
|
2019-03-06 08:58:07 +03:00
|
|
|
const QVector<DownloadPriority> oldPriorities = filePriorities();
|
2015-10-25 02:40:26 +03:00
|
|
|
for (int i = 0; i < oldPriorities.size(); ++i) {
|
2019-03-06 08:58:07 +03:00
|
|
|
if ((oldPriorities[i] == DownloadPriority::Ignored)
|
|
|
|
&& (priorities[i] > DownloadPriority::Ignored)
|
|
|
|
&& (progress[i] < 1.0)) {
|
2015-10-25 02:40:26 +03:00
|
|
|
m_hasSeedStatus = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-19 18:17:47 +03:00
|
|
|
qDebug() << Q_FUNC_INFO << "Changing files priorities...";
|
2019-03-06 08:58:07 +03:00
|
|
|
m_nativeHandle.prioritize_files(toLTDownloadPriorities(priorities));
|
2015-04-19 18:17:47 +03:00
|
|
|
|
|
|
|
qDebug() << Q_FUNC_INFO << "Moving unwanted files to .unwanted folder and conversely...";
|
2019-02-09 18:40:14 +03:00
|
|
|
const QString spath = savePath(true);
|
2015-04-19 18:17:47 +03:00
|
|
|
for (int i = 0; i < priorities.size(); ++i) {
|
2019-02-09 18:40:14 +03:00
|
|
|
const QString filepath = filePath(i);
|
2015-04-19 18:17:47 +03:00
|
|
|
// Move unwanted files to a .unwanted subfolder
|
2019-03-06 08:58:07 +03:00
|
|
|
if (priorities[i] == DownloadPriority::Ignored) {
|
2019-02-09 18:40:14 +03:00
|
|
|
const QString oldAbsPath = QDir(spath).absoluteFilePath(filepath);
|
|
|
|
const QString parentAbsPath = Utils::Fs::branchPath(oldAbsPath);
|
2015-04-19 18:17:47 +03:00
|
|
|
// Make sure the file does not already exists
|
|
|
|
if (QDir(parentAbsPath).dirName() != ".unwanted") {
|
2019-02-09 18:40:14 +03:00
|
|
|
const QString unwantedAbsPath = parentAbsPath + "/.unwanted";
|
|
|
|
const QString newAbsPath = unwantedAbsPath + '/' + Utils::Fs::fileName(filepath);
|
2015-04-19 18:17:47 +03:00
|
|
|
qDebug() << "Unwanted path is" << unwantedAbsPath;
|
|
|
|
if (QFile::exists(newAbsPath)) {
|
|
|
|
qWarning() << "File" << newAbsPath << "already exists at destination.";
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-02-09 18:40:14 +03:00
|
|
|
const bool created = QDir().mkpath(unwantedAbsPath);
|
2015-04-19 18:17:47 +03:00
|
|
|
qDebug() << "unwanted folder was created:" << created;
|
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
if (created) {
|
|
|
|
// Hide the folder on Windows
|
|
|
|
qDebug() << "Hiding folder (Windows)";
|
2018-11-06 18:49:17 +03:00
|
|
|
std::wstring winPath = Utils::Fs::toNativePath(unwantedAbsPath).toStdWString();
|
2015-04-19 18:17:47 +03:00
|
|
|
DWORD dwAttrs = ::GetFileAttributesW(winPath.c_str());
|
|
|
|
bool ret = ::SetFileAttributesW(winPath.c_str(), dwAttrs | FILE_ATTRIBUTE_HIDDEN);
|
|
|
|
Q_ASSERT(ret != 0); Q_UNUSED(ret);
|
|
|
|
}
|
|
|
|
#endif
|
2015-05-06 14:53:27 +03:00
|
|
|
QString parentPath = Utils::Fs::branchPath(filepath);
|
2018-07-21 08:28:13 +03:00
|
|
|
if (!parentPath.isEmpty() && !parentPath.endsWith('/'))
|
|
|
|
parentPath += '/';
|
2015-05-06 14:53:27 +03:00
|
|
|
renameFile(i, parentPath + ".unwanted/" + Utils::Fs::fileName(filepath));
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move wanted files back to their original folder
|
2019-03-06 08:58:07 +03:00
|
|
|
if (priorities[i] > DownloadPriority::Ignored) {
|
2019-02-09 18:40:14 +03:00
|
|
|
const QString parentRelPath = Utils::Fs::branchPath(filepath);
|
2015-04-19 18:17:47 +03:00
|
|
|
if (QDir(parentRelPath).dirName() == ".unwanted") {
|
2019-02-09 18:40:14 +03:00
|
|
|
const QString oldName = Utils::Fs::fileName(filepath);
|
|
|
|
const QString newRelPath = Utils::Fs::branchPath(parentRelPath);
|
2015-04-19 18:17:47 +03:00
|
|
|
if (newRelPath.isEmpty())
|
|
|
|
renameFile(i, oldName);
|
|
|
|
else
|
|
|
|
renameFile(i, QDir(newRelPath).filePath(oldName));
|
|
|
|
|
|
|
|
// Remove .unwanted directory if empty
|
2018-07-21 08:28:13 +03:00
|
|
|
qDebug() << "Attempting to remove .unwanted folder at " << QDir(spath + '/' + newRelPath).absoluteFilePath(".unwanted");
|
|
|
|
QDir(spath + '/' + newRelPath).rmdir(".unwanted");
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-26 15:08:01 +03:00
|
|
|
// Restore first/last piece first option if necessary
|
|
|
|
if (firstLastPieceFirst)
|
2018-07-29 11:14:17 +03:00
|
|
|
setFirstLastPiecePriorityImpl(true, priorities);
|
2015-04-19 18:17:47 +03:00
|
|
|
}
|
2016-03-22 20:10:12 +03:00
|
|
|
|
|
|
|
QVector<qreal> TorrentHandle::availableFileFractions() const
|
|
|
|
{
|
2018-11-19 07:00:50 +03:00
|
|
|
const int filesCount = this->filesCount();
|
2017-05-20 09:01:58 +03:00
|
|
|
if (filesCount < 0) return {};
|
|
|
|
|
|
|
|
const QVector<int> piecesAvailability = pieceAvailability();
|
2016-03-22 20:10:12 +03:00
|
|
|
// libtorrent returns empty array for seeding only torrents
|
|
|
|
if (piecesAvailability.empty()) return QVector<qreal>(filesCount, -1.);
|
|
|
|
|
|
|
|
QVector<qreal> res;
|
|
|
|
res.reserve(filesCount);
|
2018-11-19 07:00:50 +03:00
|
|
|
const TorrentInfo info = this->info();
|
|
|
|
for (int i = 0; i < filesCount; ++i) {
|
|
|
|
const TorrentInfo::PieceRange filePieces = info.filePieces(i);
|
|
|
|
|
2016-03-22 20:10:12 +03:00
|
|
|
int availablePieces = 0;
|
|
|
|
for (int piece = filePieces.first(); piece <= filePieces.last(); ++piece) {
|
2018-11-19 07:00:50 +03:00
|
|
|
availablePieces += (piecesAvailability[piece] > 0) ? 1 : 0;
|
2016-03-22 20:10:12 +03:00
|
|
|
}
|
|
|
|
res.push_back(static_cast<qreal>(availablePieces) / filePieces.size());
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|