qBittorrent
torrentimpl.cpp
Go to the documentation of this file.
1 /*
2  * Bittorrent Client using Qt and libtorrent.
3  * Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru>
4  * Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * In addition, as a special exception, the copyright holders give permission to
21  * link this program with the OpenSSL project's "OpenSSL" library (or with
22  * modified versions of it that use the same license as the "OpenSSL" library),
23  * and distribute the linked executables. You must obey the GNU General Public
24  * License in all respects for all of the code used other than "OpenSSL". If you
25  * modify file(s), you may extend this exception to your version of the file(s),
26  * but you are not obligated to do so. If you do not wish to do so, delete this
27  * exception statement from your version.
28  */
29 
30 #include "torrentimpl.h"
31 
32 #include <algorithm>
33 #include <memory>
34 #include <type_traits>
35 
36 #include <libtorrent/address.hpp>
37 #include <libtorrent/alert_types.hpp>
38 #include <libtorrent/magnet_uri.hpp>
39 #include <libtorrent/session.hpp>
40 #include <libtorrent/storage_defs.hpp>
41 #include <libtorrent/time.hpp>
42 
43 #ifdef QBT_USES_LIBTORRENT2
44 #include <libtorrent/info_hash.hpp>
45 #endif
46 
47 #include <QBitArray>
48 #include <QDebug>
49 #include <QDir>
50 #include <QFile>
51 #include <QStringList>
52 #include <QUrl>
53 
54 #include "base/global.h"
55 #include "base/logger.h"
56 #include "base/preferences.h"
57 #include "base/utils/fs.h"
58 #include "base/utils/string.h"
59 #include "common.h"
60 #include "downloadpriority.h"
61 #include "loadtorrentparams.h"
62 #include "ltqhash.h"
63 #include "lttypecast.h"
64 #include "peeraddress.h"
65 #include "peerinfo.h"
66 #include "session.h"
67 #include "trackerentry.h"
68 
69 using namespace BitTorrent;
70 
71 namespace
72 {
73  lt::announce_entry makeNativeAnnouncerEntry(const QString &url, const int tier)
74  {
75  lt::announce_entry entry {url.toStdString()};
76  entry.tier = tier;
77  return entry;
78  }
79 
80 #ifdef QBT_USES_LIBTORRENT2
81  TrackerEntry fromNativeAnnouncerEntry(const lt::announce_entry &nativeEntry
82  , const lt::info_hash_t &hashes, const QMap<lt::tcp::endpoint, int> &trackerPeerCounts)
83 #else
84  TrackerEntry fromNativeAnnouncerEntry(const lt::announce_entry &nativeEntry
85  , const QMap<lt::tcp::endpoint, int> &trackerPeerCounts)
86 #endif
87  {
88  TrackerEntry trackerEntry {QString::fromStdString(nativeEntry.url), nativeEntry.tier};
89 
90  int numUpdating = 0;
91  int numWorking = 0;
92  int numNotWorking = 0;
93  QString firstTrackerMessage;
94  QString firstErrorMessage;
95 #ifdef QBT_USES_LIBTORRENT2
96  const auto numEndpoints = static_cast<qsizetype>(nativeEntry.endpoints.size() * ((hashes.has_v1() && hashes.has_v2()) ? 2 : 1));
97  trackerEntry.endpoints.reserve(static_cast<decltype(trackerEntry.endpoints)::size_type>(numEndpoints));
98  for (const lt::announce_endpoint &endpoint : nativeEntry.endpoints)
99  {
100  for (const auto protocolVersion : {lt::protocol_version::V1, lt::protocol_version::V2})
101  {
102  if (hashes.has(protocolVersion))
103  {
104  const lt::announce_infohash &infoHash = endpoint.info_hashes[protocolVersion];
105 
106  TrackerEntry::EndpointStats trackerEndpoint;
107  trackerEndpoint.protocolVersion = (protocolVersion == lt::protocol_version::V1) ? 1 : 2;
108  trackerEndpoint.numPeers = trackerPeerCounts.value(endpoint.local_endpoint, -1);
109  trackerEndpoint.numSeeds = infoHash.scrape_complete;
110  trackerEndpoint.numLeeches = infoHash.scrape_incomplete;
111  trackerEndpoint.numDownloaded = infoHash.scrape_downloaded;
112 
113  if (infoHash.updating)
114  {
115  trackerEndpoint.status = TrackerEntry::Updating;
116  ++numUpdating;
117  }
118  else if (infoHash.fails > 0)
119  {
120  trackerEndpoint.status = TrackerEntry::NotWorking;
121  ++numNotWorking;
122  }
123  else if (nativeEntry.verified)
124  {
125  trackerEndpoint.status = TrackerEntry::Working;
126  ++numWorking;
127  }
128  else
129  {
130  trackerEndpoint.status = TrackerEntry::NotContacted;
131  }
132 
133  const QString trackerMessage = QString::fromStdString(infoHash.message);
134  const QString errorMessage = QString::fromLocal8Bit(infoHash.last_error.message().c_str());
135  trackerEndpoint.message = (!trackerMessage.isEmpty() ? trackerMessage : errorMessage);
136 
137  trackerEntry.endpoints.append(trackerEndpoint);
138  trackerEntry.numPeers = std::max(trackerEntry.numPeers, trackerEndpoint.numPeers);
139  trackerEntry.numSeeds = std::max(trackerEntry.numSeeds, trackerEndpoint.numSeeds);
140  trackerEntry.numLeeches = std::max(trackerEntry.numLeeches, trackerEndpoint.numLeeches);
141  trackerEntry.numDownloaded = std::max(trackerEntry.numDownloaded, trackerEndpoint.numDownloaded);
142 
143  if (firstTrackerMessage.isEmpty())
144  firstTrackerMessage = trackerMessage;
145  if (firstErrorMessage.isEmpty())
146  firstErrorMessage = errorMessage;
147  }
148  }
149  }
150 #else
151  const auto numEndpoints = static_cast<qsizetype>(nativeEntry.endpoints.size());
152  trackerEntry.endpoints.reserve(static_cast<decltype(trackerEntry.endpoints)::size_type>(numEndpoints));
153  for (const lt::announce_endpoint &endpoint : nativeEntry.endpoints)
154  {
155  TrackerEntry::EndpointStats trackerEndpoint;
156  trackerEndpoint.numPeers = trackerPeerCounts.value(endpoint.local_endpoint, -1);
157  trackerEndpoint.numSeeds = endpoint.scrape_complete;
158  trackerEndpoint.numLeeches = endpoint.scrape_incomplete;
159  trackerEndpoint.numDownloaded = endpoint.scrape_downloaded;
160 
161  if (endpoint.updating)
162  {
163  trackerEndpoint.status = TrackerEntry::Updating;
164  ++numUpdating;
165  }
166  else if (endpoint.fails > 0)
167  {
168  trackerEndpoint.status = TrackerEntry::NotWorking;
169  ++numNotWorking;
170  }
171  else if (nativeEntry.verified)
172  {
173  trackerEndpoint.status = TrackerEntry::Working;
174  ++numWorking;
175  }
176  else
177  {
178  trackerEndpoint.status = TrackerEntry::NotContacted;
179  }
180 
181  const QString trackerMessage = QString::fromStdString(endpoint.message);
182  const QString errorMessage = QString::fromLocal8Bit(endpoint.last_error.message().c_str());
183  trackerEndpoint.message = (!trackerMessage.isEmpty() ? trackerMessage : errorMessage);
184 
185  trackerEntry.endpoints.append(trackerEndpoint);
186  trackerEntry.numPeers = std::max(trackerEntry.numPeers, trackerEndpoint.numPeers);
187  trackerEntry.numSeeds = std::max(trackerEntry.numSeeds, trackerEndpoint.numSeeds);
188  trackerEntry.numLeeches = std::max(trackerEntry.numLeeches, trackerEndpoint.numLeeches);
189  trackerEntry.numDownloaded = std::max(trackerEntry.numDownloaded, trackerEndpoint.numDownloaded);
190 
191  if (firstTrackerMessage.isEmpty())
192  firstTrackerMessage = trackerMessage;
193  if (firstErrorMessage.isEmpty())
194  firstErrorMessage = errorMessage;
195  }
196 #endif
197 
198  if (numEndpoints > 0)
199  {
200  if (numUpdating > 0)
201  {
202  trackerEntry.status = TrackerEntry::Updating;
203  }
204  else if (numWorking > 0)
205  {
206  trackerEntry.status = TrackerEntry::Working;
207  trackerEntry.message = firstTrackerMessage;
208  }
209  else if (numNotWorking == numEndpoints)
210  {
211  trackerEntry.status = TrackerEntry::NotWorking;
212  trackerEntry.message = (!firstTrackerMessage.isEmpty() ? firstTrackerMessage : firstErrorMessage);
213  }
214  }
215 
216  return trackerEntry;
217  }
218 
219  void initializeStatus(lt::torrent_status &status, const lt::add_torrent_params &params)
220  {
221  status.flags = params.flags;
222  status.active_duration = lt::seconds {params.active_time};
223  status.finished_duration = lt::seconds {params.finished_time};
224  status.num_complete = params.num_complete;
225  status.num_incomplete = params.num_incomplete;
226  status.all_time_download = params.total_downloaded;
227  status.all_time_upload = params.total_uploaded;
228  status.added_time = params.added_time;
229  status.last_seen_complete = params.last_seen_complete;
230  status.last_download = lt::time_point {lt::seconds {params.last_download}};
231  status.last_upload = lt::time_point {lt::seconds {params.last_upload}};
232  status.completed_time = params.completed_time;
233  status.save_path = params.save_path;
234  status.connections_limit = params.max_connections;
235  status.pieces = params.have_pieces;
236  status.verified_pieces = params.verified_pieces;
237  }
238 }
239 
240 // TorrentImpl
241 
242 TorrentImpl::TorrentImpl(Session *session, lt::session *nativeSession
243  , const lt::torrent_handle &nativeHandle, const LoadTorrentParams &params)
244  : QObject(session)
245  , m_session(session)
246  , m_nativeSession(nativeSession)
247  , m_nativeHandle(nativeHandle)
248 #ifdef QBT_USES_LIBTORRENT2
249  , m_infoHash(m_nativeHandle.info_hashes())
250 #else
251  , m_infoHash(m_nativeHandle.info_hash())
252 #endif
253  , m_name(params.name)
254  , m_savePath(params.savePath)
255  , m_downloadPath(params.downloadPath)
256  , m_category(params.category)
257  , m_tags(params.tags)
258  , m_ratioLimit(params.ratioLimit)
259  , m_seedingTimeLimit(params.seedingTimeLimit)
260  , m_operatingMode(params.operatingMode)
261  , m_contentLayout(params.contentLayout)
262  , m_hasSeedStatus(params.hasSeedStatus)
263  , m_hasFirstLastPiecePriority(params.firstLastPiecePriority)
264  , m_useAutoTMM(params.useAutoTMM)
265  , m_isStopped(params.stopped)
266  , m_ltAddTorrentParams(params.ltAddTorrentParams)
267 {
268  if (m_ltAddTorrentParams.ti)
269  {
270  // Initialize it only if torrent is added with metadata.
271  // Otherwise it should be initialized in "Metadata received" handler.
273 
274  Q_ASSERT(m_filePaths.isEmpty());
275  const int filesCount = m_torrentInfo.filesCount();
276  m_filePaths.reserve(filesCount);
277  const std::shared_ptr<const lt::torrent_info> currentInfo = m_nativeHandle.torrent_file();
278  const lt::file_storage &fileStorage = currentInfo->files();
279  for (int i = 0; i < filesCount; ++i)
280  {
281  const lt::file_index_t nativeIndex = m_torrentInfo.nativeIndexes().at(i);
282  const QString filePath = Utils::Fs::toUniformPath(QString::fromStdString(fileStorage.file_path(nativeIndex)));
283  m_filePaths.append(filePath.endsWith(QB_EXT, Qt::CaseInsensitive) ? filePath.chopped(QB_EXT.size()) : filePath);
284  }
285  }
286 
288  updateState();
289 
290  if (hasMetadata())
292 
293  // TODO: Remove the following upgrade code in v4.4
294  // == BEGIN UPGRADE CODE ==
295  const QString spath = actualStorageLocation();
296  for (int i = 0; i < filesCount(); ++i)
297  {
298  const QString filepath = filePath(i);
299  // Move "unwanted" files back to their original folder
300  const QString parentRelPath = Utils::Fs::branchPath(filepath);
301  if (QDir(parentRelPath).dirName() == ".unwanted")
302  {
303  const QString oldName = Utils::Fs::fileName(filepath);
304  const QString newRelPath = Utils::Fs::branchPath(parentRelPath);
305  if (newRelPath.isEmpty())
306  renameFile(i, oldName);
307  else
308  renameFile(i, QDir(newRelPath).filePath(oldName));
309 
310  // Remove .unwanted directory if empty
311  qDebug() << "Attempting to remove \".unwanted\" folder at " << QDir(spath + '/' + newRelPath).absoluteFilePath(".unwanted");
312  QDir(spath + '/' + newRelPath).rmdir(".unwanted");
313  }
314  }
315  // == END UPGRADE CODE ==
316 }
317 
319 
321 {
322  return m_nativeHandle.is_valid();
323 }
324 
326 {
327  return m_infoHash;
328 }
329 
330 QString TorrentImpl::name() const
331 {
332  if (!m_name.isEmpty())
333  return m_name;
334 
335  if (hasMetadata())
336  return m_torrentInfo.name();
337 
338  const QString name = QString::fromStdString(m_nativeStatus.name);
339  if (!name.isEmpty())
340  return name;
341 
342  return id().toString();
343 }
344 
345 QDateTime TorrentImpl::creationDate() const
346 {
347  return m_torrentInfo.creationDate();
348 }
349 
350 QString TorrentImpl::creator() const
351 {
352  return m_torrentInfo.creator();
353 }
354 
355 QString TorrentImpl::comment() const
356 {
357  return m_torrentInfo.comment();
358 }
359 
361 {
362  return m_torrentInfo.isPrivate();
363 }
364 
365 qlonglong TorrentImpl::totalSize() const
366 {
367  return m_torrentInfo.totalSize();
368 }
369 
370 // size without the "don't download" files
371 qlonglong TorrentImpl::wantedSize() const
372 {
373  return m_nativeStatus.total_wanted;
374 }
375 
376 qlonglong TorrentImpl::completedSize() const
377 {
378  return m_nativeStatus.total_wanted_done;
379 }
380 
381 qlonglong TorrentImpl::pieceLength() const
382 {
383  return m_torrentInfo.pieceLength();
384 }
385 
386 qlonglong TorrentImpl::wastedSize() const
387 {
388  return (m_nativeStatus.total_failed_bytes + m_nativeStatus.total_redundant_bytes);
389 }
390 
392 {
393  return QString::fromStdString(m_nativeStatus.current_tracker);
394 }
395 
396 QString TorrentImpl::savePath() const
397 {
399 }
400 
401 void TorrentImpl::setSavePath(const QString &path)
402 {
403  Q_ASSERT(!isAutoTMMEnabled());
404 
405  const QString resolvedPath = (QDir::isAbsolutePath(path) ? path : Utils::Fs::resolvePath(path, m_session->savePath()));
406  if (resolvedPath == savePath())
407  return;
408 
409  m_savePath = resolvedPath;
410 
412 
413  const bool isFinished = isSeed() || m_hasSeedStatus;
414  if (isFinished)
416 }
417 
419 {
421 }
422 
423 void TorrentImpl::setDownloadPath(const QString &path)
424 {
425  Q_ASSERT(!isAutoTMMEnabled());
426 
427  const QString resolvedPath = ((path.isEmpty() || QDir::isAbsolutePath(path))
428  ? path : Utils::Fs::resolvePath(path, m_session->downloadPath()));
429  if (resolvedPath == m_downloadPath)
430  return;
431 
432  m_downloadPath = resolvedPath;
433 
435 
436  const bool isFinished = isSeed() || m_hasSeedStatus;
437  if (!isFinished)
439 }
440 
441 QString TorrentImpl::rootPath() const
442 {
443  if (!hasMetadata())
444  return {};
445 
446  const QString relativeRootPath = m_torrentInfo.rootFolder();
447  if (relativeRootPath.isEmpty())
448  return {};
449 
450  return QDir(actualStorageLocation()).absoluteFilePath(relativeRootPath);
451 }
452 
454 {
455  if (!hasMetadata())
456  return {};
457 
458  if (filesCount() == 1)
459  return QDir(actualStorageLocation()).absoluteFilePath(filePath(0));
460 
461  const QString rootPath = this->rootPath();
462  return (rootPath.isEmpty() ? actualStorageLocation() : rootPath);
463 }
464 
466 {
467  return m_useAutoTMM;
468 }
469 
471 {
472  if (m_useAutoTMM == enabled)
473  return;
474 
475  m_useAutoTMM = enabled;
476  if (!m_useAutoTMM)
477  {
480  }
481 
484 
486 }
487 
489 {
490  return Utils::Fs::toUniformPath(QString::fromStdString(m_nativeStatus.save_path));
491 }
492 
493 void TorrentImpl::setAutoManaged(const bool enable)
494 {
495  if (enable)
496  m_nativeHandle.set_flags(lt::torrent_flags::auto_managed);
497  else
498  m_nativeHandle.unset_flags(lt::torrent_flags::auto_managed);
499 }
500 
501 QVector<TrackerEntry> TorrentImpl::trackers() const
502 {
503  const std::vector<lt::announce_entry> nativeTrackers = m_nativeHandle.trackers();
504 
505  QVector<TrackerEntry> entries;
506  entries.reserve(static_cast<decltype(entries)::size_type>(nativeTrackers.size()));
507 
508  for (const lt::announce_entry &tracker : nativeTrackers)
509  {
510  const QString trackerURL = QString::fromStdString(tracker.url);
511 #ifdef QBT_USES_LIBTORRENT2
512  entries << fromNativeAnnouncerEntry(tracker, m_nativeHandle.info_hashes(), m_trackerPeerCounts[trackerURL]);
513 #else
514  entries << fromNativeAnnouncerEntry(tracker, m_trackerPeerCounts[trackerURL]);
515 #endif
516  }
517 
518  return entries;
519 }
520 
521 void TorrentImpl::addTrackers(const QVector<TrackerEntry> &trackers)
522 {
523  QSet<TrackerEntry> currentTrackers;
524  for (const lt::announce_entry &entry : m_nativeHandle.trackers())
525  currentTrackers.insert({QString::fromStdString(entry.url), entry.tier});
526 
527  QVector<TrackerEntry> newTrackers;
528  newTrackers.reserve(trackers.size());
529 
530  for (const TrackerEntry &tracker : trackers)
531  {
532  if (!currentTrackers.contains(tracker))
533  {
534  m_nativeHandle.add_tracker(makeNativeAnnouncerEntry(tracker.url, tracker.tier));
535  newTrackers << tracker;
536  }
537  }
538 
539  if (!newTrackers.isEmpty())
540  {
542  m_session->handleTorrentTrackersAdded(this, newTrackers);
543  }
544 }
545 
546 void TorrentImpl::replaceTrackers(const QVector<TrackerEntry> &trackers)
547 {
548  QVector<TrackerEntry> currentTrackers = this->trackers();
549 
550  QVector<TrackerEntry> newTrackers;
551  newTrackers.reserve(trackers.size());
552 
553  std::vector<lt::announce_entry> nativeTrackers;
554  nativeTrackers.reserve(trackers.size());
555 
556  for (const TrackerEntry &tracker : trackers)
557  {
558  nativeTrackers.emplace_back(makeNativeAnnouncerEntry(tracker.url, tracker.tier));
559 
560  if (!currentTrackers.removeOne(tracker))
561  newTrackers << tracker;
562  }
563 
564  m_nativeHandle.replace_trackers(nativeTrackers);
565 
567 
568  if (newTrackers.isEmpty() && currentTrackers.isEmpty())
569  {
570  // when existing tracker reorders
572  }
573  else
574  {
575  if (!currentTrackers.isEmpty())
576  m_session->handleTorrentTrackersRemoved(this, currentTrackers);
577 
578  if (!newTrackers.isEmpty())
579  m_session->handleTorrentTrackersAdded(this, newTrackers);
580 
581  // Clear the peer list if it's a private torrent since
582  // we do not want to keep connecting with peers from old tracker.
583  if (isPrivate())
584  clearPeers();
585  }
586 }
587 
588 QVector<QUrl> TorrentImpl::urlSeeds() const
589 {
590  const std::set<std::string> currentSeeds = m_nativeHandle.url_seeds();
591 
592  QVector<QUrl> urlSeeds;
593  urlSeeds.reserve(static_cast<decltype(urlSeeds)::size_type>(currentSeeds.size()));
594 
595  for (const std::string &urlSeed : currentSeeds)
596  urlSeeds.append(QString::fromStdString(urlSeed));
597 
598  return urlSeeds;
599 }
600 
601 void TorrentImpl::addUrlSeeds(const QVector<QUrl> &urlSeeds)
602 {
603  const std::set<std::string> currentSeeds = m_nativeHandle.url_seeds();
604 
605  QVector<QUrl> addedUrlSeeds;
606  addedUrlSeeds.reserve(urlSeeds.size());
607 
608  for (const QUrl &url : urlSeeds)
609  {
610  const std::string nativeUrl = url.toString().toStdString();
611  if (currentSeeds.find(nativeUrl) == currentSeeds.end())
612  {
613  m_nativeHandle.add_url_seed(nativeUrl);
614  addedUrlSeeds << url;
615  }
616  }
617 
618  if (!addedUrlSeeds.isEmpty())
619  {
621  m_session->handleTorrentUrlSeedsAdded(this, addedUrlSeeds);
622  }
623 }
624 
625 void TorrentImpl::removeUrlSeeds(const QVector<QUrl> &urlSeeds)
626 {
627  const std::set<std::string> currentSeeds = m_nativeHandle.url_seeds();
628 
629  QVector<QUrl> removedUrlSeeds;
630  removedUrlSeeds.reserve(urlSeeds.size());
631 
632  for (const QUrl &url : urlSeeds)
633  {
634  const std::string nativeUrl = url.toString().toStdString();
635  if (currentSeeds.find(nativeUrl) != currentSeeds.end())
636  {
637  m_nativeHandle.remove_url_seed(nativeUrl);
638  removedUrlSeeds << url;
639  }
640  }
641 
642  if (!removedUrlSeeds.isEmpty())
643  {
645  m_session->handleTorrentUrlSeedsRemoved(this, removedUrlSeeds);
646  }
647 }
648 
650 {
651  m_nativeHandle.clear_peers();
652 }
653 
654 bool TorrentImpl::connectPeer(const PeerAddress &peerAddress)
655 {
656  lt::error_code ec;
657  const lt::address addr = lt::make_address(peerAddress.ip.toString().toStdString(), ec);
658  if (ec) return false;
659 
660  const lt::tcp::endpoint endpoint(addr, peerAddress.port);
661  try
662  {
663  m_nativeHandle.connect_peer(endpoint);
664  }
665  catch (const lt::system_error &err)
666  {
667  LogMsg(tr("Failed to add peer \"%1\" to torrent \"%2\". Reason: %3")
668  .arg(peerAddress.toString(), name(), QString::fromLocal8Bit(err.what())), Log::WARNING);
669  return false;
670  }
671 
672  LogMsg(tr("Peer \"%1\" is added to torrent \"%2\"").arg(peerAddress.toString(), name()));
673  return true;
674 }
675 
677 {
678  return m_nativeHandle.need_save_resume_data();
679 }
680 
682 {
683  m_nativeHandle.save_resume_data();
685 }
686 
688 {
689  return m_torrentInfo.filesCount();
690 }
691 
693 {
694  return m_torrentInfo.piecesCount();
695 }
696 
698 {
699  return m_nativeStatus.num_pieces;
700 }
701 
703 {
704  if (isChecking())
705  return m_nativeStatus.progress;
706 
707  if (m_nativeStatus.total_wanted == 0)
708  return 0.;
709 
710  if (m_nativeStatus.total_wanted_done == m_nativeStatus.total_wanted)
711  return 1.;
712 
713  const qreal progress = static_cast<qreal>(m_nativeStatus.total_wanted_done) / m_nativeStatus.total_wanted;
714  Q_ASSERT((progress >= 0.f) && (progress <= 1.f));
715  return progress;
716 }
717 
718 QString TorrentImpl::category() const
719 {
720  return m_category;
721 }
722 
723 bool TorrentImpl::belongsToCategory(const QString &category) const
724 {
725  if (m_category.isEmpty()) return category.isEmpty();
726  if (!Session::isValidCategoryName(category)) return false;
727 
728  if (m_category == category) return true;
729 
730  if (m_session->isSubcategoriesEnabled() && m_category.startsWith(category + '/'))
731  return true;
732 
733  return false;
734 }
735 
737 {
738  return m_tags;
739 }
740 
741 bool TorrentImpl::hasTag(const QString &tag) const
742 {
743  return m_tags.contains(tag);
744 }
745 
746 bool TorrentImpl::addTag(const QString &tag)
747 {
748  if (!Session::isValidTag(tag))
749  return false;
750  if (hasTag(tag))
751  return false;
752 
753  if (!m_session->hasTag(tag))
754  {
755  if (!m_session->addTag(tag))
756  return false;
757  }
758  m_tags.insert(tag);
760  m_session->handleTorrentTagAdded(this, tag);
761  return true;
762 }
763 
764 bool TorrentImpl::removeTag(const QString &tag)
765 {
766  if (m_tags.remove(tag))
767  {
770  return true;
771  }
772  return false;
773 }
774 
776 {
777  for (const QString &tag : asConst(tags()))
778  removeTag(tag);
779 }
780 
781 QDateTime TorrentImpl::addedTime() const
782 {
783  return QDateTime::fromSecsSinceEpoch(m_nativeStatus.added_time);
784 }
785 
787 {
788  return m_ratioLimit;
789 }
790 
792 {
793  return m_seedingTimeLimit;
794 }
795 
796 QString TorrentImpl::filePath(const int index) const
797 {
798  return m_filePaths.at(index);
799 }
800 
801 QString TorrentImpl::actualFilePath(const int index) const
802 {
803  const auto nativeIndex = m_torrentInfo.nativeIndexes().at(index);
804  return QString::fromStdString(m_nativeHandle.torrent_file()->files().file_path(nativeIndex));
805 }
806 
807 qlonglong TorrentImpl::fileSize(const int index) const
808 {
809  return m_torrentInfo.fileSize(index);
810 }
811 
812 QStringList TorrentImpl::filePaths() const
813 {
814  return m_filePaths;
815 }
816 
817 QVector<DownloadPriority> TorrentImpl::filePriorities() const
818 {
819  if (!hasMetadata())
820  return {};
821 
822  const std::vector<lt::download_priority_t> fp = m_nativeHandle.get_file_priorities();
823 
824  QVector<DownloadPriority> ret;
825  ret.reserve(filesCount());
826  for (const lt::file_index_t nativeIndex : asConst(m_torrentInfo.nativeIndexes()))
827  {
828  const auto priority = LT::fromNative(fp[LT::toUnderlyingType(nativeIndex)]);
829  ret.append(priority);
830  }
831 
832  return ret;
833 }
834 
836 {
837  return m_torrentInfo;
838 }
839 
841 {
842  return m_isStopped;
843 }
844 
846 {
847  // Torrent is Queued if it isn't in Paused state but paused internally
848  return (!isPaused()
849  && (m_nativeStatus.flags & lt::torrent_flags::auto_managed)
850  && (m_nativeStatus.flags & lt::torrent_flags::paused));
851 }
852 
854 {
855  return ((m_nativeStatus.state == lt::torrent_status::checking_files)
856  || (m_nativeStatus.state == lt::torrent_status::checking_resume_data));
857 }
858 
860 {
869 }
870 
872 {
878 }
879 
881 {
888 }
889 
891 {
893  return (uploadPayloadRate() > 0);
894 
902 }
903 
905 {
906  return !isActive();
907 }
908 
910 {
913 }
914 
916 {
917  return ((m_nativeStatus.state == lt::torrent_status::finished)
918  || (m_nativeStatus.state == lt::torrent_status::seeding));
919 }
920 
922 {
923  return (!isPaused() && (m_operatingMode == TorrentOperatingMode::Forced));
924 }
925 
927 {
928  return static_cast<bool>(m_nativeStatus.flags & lt::torrent_flags::sequential_download);
929 }
930 
932 {
934 }
935 
937 {
938  return m_state;
939 }
940 
942 {
943  if (m_nativeStatus.state == lt::torrent_status::checking_resume_data)
944  {
946  }
947  else if (isMoveInProgress())
948  {
950  }
951  else if (hasMissingFiles())
952  {
954  }
955  else if (hasError())
956  {
958  }
959  else if (!hasMetadata())
960  {
961  if (isPaused())
963  else if (m_session->isQueueingSystemEnabled() && isQueued())
965  else
967  }
968  else if ((m_nativeStatus.state == lt::torrent_status::checking_files)
969  && (!isPaused() || (m_nativeStatus.flags & lt::torrent_flags::auto_managed)
970  || !(m_nativeStatus.flags & lt::torrent_flags::paused)))
971  {
972  // If the torrent is not just in the "checking" state, but is being actually checked
974  }
975  else if (isSeed())
976  {
977  if (isPaused())
979  else if (m_session->isQueueingSystemEnabled() && isQueued())
981  else if (isForced())
983  else if (m_nativeStatus.upload_payload_rate > 0)
985  else
987  }
988  else
989  {
990  if (isPaused())
992  else if (m_session->isQueueingSystemEnabled() && isQueued())
994  else if (isForced())
996  else if (m_nativeStatus.download_payload_rate > 0)
998  else
1000  }
1001 }
1002 
1004 {
1005  return m_torrentInfo.isValid();
1006 }
1007 
1009 {
1010  return m_hasMissingFiles;
1011 }
1012 
1014 {
1015  return (m_nativeStatus.errc || (m_nativeStatus.flags & lt::torrent_flags::upload_mode));
1016 }
1017 
1019 {
1020  return static_cast<int>(m_nativeStatus.queue_position);
1021 }
1022 
1023 QString TorrentImpl::error() const
1024 {
1025  if (m_nativeStatus.errc)
1026  return QString::fromLocal8Bit(m_nativeStatus.errc.message().c_str());
1027 
1028  if (m_nativeStatus.flags & lt::torrent_flags::upload_mode)
1029  {
1030  const QString writeErrorStr = tr("Couldn't write to file.");
1031  const QString uploadModeStr = tr("Torrent is currently in \"upload only\" mode.");
1032  const QString errorMessage = QString::fromLocal8Bit(m_lastFileError.error.message().c_str());
1033 
1034  return writeErrorStr + QLatin1Char(' ') + errorMessage + QLatin1String(". ") + uploadModeStr;
1035  }
1036 
1037  return {};
1038 }
1039 
1041 {
1042  return m_nativeStatus.all_time_download;
1043 }
1044 
1045 qlonglong TorrentImpl::totalUpload() const
1046 {
1047  return m_nativeStatus.all_time_upload;
1048 }
1049 
1050 qlonglong TorrentImpl::activeTime() const
1051 {
1052  return lt::total_seconds(m_nativeStatus.active_duration);
1053 }
1054 
1055 qlonglong TorrentImpl::finishedTime() const
1056 {
1057  return lt::total_seconds(m_nativeStatus.finished_duration);
1058 }
1059 
1060 qlonglong TorrentImpl::eta() const
1061 {
1062  if (isPaused()) return MAX_ETA;
1063 
1064  const SpeedSampleAvg speedAverage = m_speedMonitor.average();
1065 
1066  if (isSeed())
1067  {
1068  const qreal maxRatioValue = maxRatio();
1069  const int maxSeedingTimeValue = maxSeedingTime();
1070  if ((maxRatioValue < 0) && (maxSeedingTimeValue < 0)) return MAX_ETA;
1071 
1072  qlonglong ratioEta = MAX_ETA;
1073 
1074  if ((speedAverage.upload > 0) && (maxRatioValue >= 0))
1075  {
1076 
1077  qlonglong realDL = totalDownload();
1078  if (realDL <= 0)
1079  realDL = wantedSize();
1080 
1081  ratioEta = ((realDL * maxRatioValue) - totalUpload()) / speedAverage.upload;
1082  }
1083 
1084  qlonglong seedingTimeEta = MAX_ETA;
1085 
1086  if (maxSeedingTimeValue >= 0)
1087  {
1088  seedingTimeEta = (maxSeedingTimeValue * 60) - finishedTime();
1089  if (seedingTimeEta < 0)
1090  seedingTimeEta = 0;
1091  }
1092 
1093  return std::min(ratioEta, seedingTimeEta);
1094  }
1095 
1096  if (!speedAverage.download) return MAX_ETA;
1097 
1098  return (wantedSize() - completedSize()) / speedAverage.download;
1099 }
1100 
1101 QVector<qreal> TorrentImpl::filesProgress() const
1102 {
1103  if (!hasMetadata())
1104  return {};
1105 
1106  std::vector<int64_t> fp;
1107  m_nativeHandle.file_progress(fp, lt::torrent_handle::piece_granularity);
1108 
1109  const int count = filesCount();
1110  const auto nativeIndexes = m_torrentInfo.nativeIndexes();
1111  QVector<qreal> result;
1112  result.reserve(count);
1113  for (int i = 0; i < count; ++i)
1114  {
1115  const int64_t progress = fp[LT::toUnderlyingType(nativeIndexes[i])];
1116  const qlonglong size = fileSize(i);
1117  if ((size <= 0) || (progress == size))
1118  result << 1;
1119  else
1120  result << (progress / static_cast<qreal>(size));
1121  }
1122 
1123  return result;
1124 }
1125 
1127 {
1128  return m_nativeStatus.num_seeds;
1129 }
1130 
1132 {
1133  return m_nativeStatus.num_peers;
1134 }
1135 
1137 {
1138  return (m_nativeStatus.num_peers - m_nativeStatus.num_seeds);
1139 }
1140 
1142 {
1143  return (m_nativeStatus.num_complete > 0) ? m_nativeStatus.num_complete : m_nativeStatus.list_seeds;
1144 }
1145 
1147 {
1148  const int peers = m_nativeStatus.num_complete + m_nativeStatus.num_incomplete;
1149  return (peers > 0) ? peers : m_nativeStatus.list_peers;
1150 }
1151 
1153 {
1154  return (m_nativeStatus.num_incomplete > 0) ? m_nativeStatus.num_incomplete : (m_nativeStatus.list_peers - m_nativeStatus.list_seeds);
1155 }
1156 
1158 {
1159  // additional info: https://github.com/qbittorrent/qBittorrent/pull/5300#issuecomment-267783646
1160  return m_nativeStatus.num_complete;
1161 }
1162 
1164 {
1165  // additional info: https://github.com/qbittorrent/qBittorrent/pull/5300#issuecomment-267783646
1166  return m_nativeStatus.num_incomplete;
1167 }
1168 
1170 {
1171  if (m_nativeStatus.last_seen_complete > 0)
1172  return QDateTime::fromSecsSinceEpoch(m_nativeStatus.last_seen_complete);
1173  else
1174  return {};
1175 }
1176 
1178 {
1179  if (m_nativeStatus.completed_time > 0)
1180  return QDateTime::fromSecsSinceEpoch(m_nativeStatus.completed_time);
1181  else
1182  return {};
1183 }
1184 
1186 {
1187  if (m_nativeStatus.last_upload.time_since_epoch().count() == 0)
1188  return -1;
1189  return lt::total_seconds(lt::clock_type::now() - m_nativeStatus.last_upload);
1190 }
1191 
1193 {
1194  if (m_nativeStatus.last_download.time_since_epoch().count() == 0)
1195  return -1;
1196  return lt::total_seconds(lt::clock_type::now() - m_nativeStatus.last_download);
1197 }
1198 
1200 {
1201  const qlonglong upTime = timeSinceUpload();
1202  const qlonglong downTime = timeSinceDownload();
1203  return ((upTime < 0) != (downTime < 0))
1204  ? std::max(upTime, downTime)
1205  : std::min(upTime, downTime);
1206 }
1207 
1209 {
1210  return m_nativeHandle.download_limit();
1211 }
1212 
1214 {
1215  return m_nativeHandle.upload_limit();
1216 }
1217 
1219 {
1220  return static_cast<bool>(m_nativeStatus.flags & lt::torrent_flags::super_seeding);
1221 }
1222 
1224 {
1225  return static_cast<bool>(m_nativeStatus.flags & lt::torrent_flags::disable_dht);
1226 }
1227 
1229 {
1230  return static_cast<bool>(m_nativeStatus.flags & lt::torrent_flags::disable_pex);
1231 }
1232 
1234 {
1235  return static_cast<bool>(m_nativeStatus.flags & lt::torrent_flags::disable_lsd);
1236 }
1237 
1238 QVector<PeerInfo> TorrentImpl::peers() const
1239 {
1240  std::vector<lt::peer_info> nativePeers;
1241  m_nativeHandle.get_peer_info(nativePeers);
1242 
1243  QVector<PeerInfo> peers;
1244  peers.reserve(static_cast<decltype(peers)::size_type>(nativePeers.size()));
1245 
1246  for (const lt::peer_info &peer : nativePeers)
1247  peers << PeerInfo(this, peer);
1248 
1249  return peers;
1250 }
1251 
1252 QBitArray TorrentImpl::pieces() const
1253 {
1254  QBitArray result(m_nativeStatus.pieces.size());
1255  for (int i = 0; i < result.size(); ++i)
1256  {
1257  if (m_nativeStatus.pieces[lt::piece_index_t {i}])
1258  result.setBit(i, true);
1259  }
1260  return result;
1261 }
1262 
1264 {
1265  QBitArray result(piecesCount());
1266 
1267  std::vector<lt::partial_piece_info> queue;
1268  m_nativeHandle.get_download_queue(queue);
1269 
1270  for (const lt::partial_piece_info &info : queue)
1271  result.setBit(LT::toUnderlyingType(info.piece_index));
1272 
1273  return result;
1274 }
1275 
1277 {
1278  std::vector<int> avail;
1279  m_nativeHandle.piece_availability(avail);
1280 
1281  return {avail.cbegin(), avail.cend()};
1282 }
1283 
1285 {
1286  return m_nativeStatus.distributed_copies;
1287 }
1288 
1290 {
1292  return m_session->globalMaxRatio();
1293 
1294  return m_ratioLimit;
1295 }
1296 
1298 {
1301 
1302  return m_seedingTimeLimit;
1303 }
1304 
1306 {
1307  const int64_t upload = m_nativeStatus.all_time_upload;
1308  // special case for a seeder who lost its stats, also assume nobody will import a 99% done torrent
1309  const int64_t download = (m_nativeStatus.all_time_download < (m_nativeStatus.total_done * 0.01))
1310  ? m_nativeStatus.total_done
1311  : m_nativeStatus.all_time_download;
1312 
1313  if (download == 0)
1314  return (upload == 0) ? 0 : MAX_RATIO;
1315 
1316  const qreal ratio = upload / static_cast<qreal>(download);
1317  Q_ASSERT(ratio >= 0);
1318  return (ratio > MAX_RATIO) ? MAX_RATIO : ratio;
1319 }
1320 
1322 {
1323  return m_nativeStatus.upload_payload_rate;
1324 }
1325 
1327 {
1328  return m_nativeStatus.download_payload_rate;
1329 }
1330 
1332 {
1333  return m_nativeStatus.total_payload_upload;
1334 }
1335 
1337 {
1338  return m_nativeStatus.total_payload_download;
1339 }
1340 
1342 {
1343  return m_nativeStatus.num_connections;
1344 }
1345 
1347 {
1348  return m_nativeStatus.connections_limit;
1349 }
1350 
1351 qlonglong TorrentImpl::nextAnnounce() const
1352 {
1353  return lt::total_seconds(m_nativeStatus.next_announce);
1354 }
1355 
1356 void TorrentImpl::setName(const QString &name)
1357 {
1358  if (m_name != name)
1359  {
1360  m_name = name;
1363  }
1364 }
1365 
1366 bool TorrentImpl::setCategory(const QString &category)
1367 {
1368  if (m_category != category)
1369  {
1370  if (!category.isEmpty() && !m_session->categories().contains(category))
1371  return false;
1372 
1373  const QString oldCategory = m_category;
1374  m_category = category;
1376  m_session->handleTorrentCategoryChanged(this, oldCategory);
1377 
1378  if (m_useAutoTMM)
1379  {
1382  else
1383  setAutoTMMEnabled(false);
1384  }
1385  }
1386 
1387  return true;
1388 }
1389 
1390 void TorrentImpl::forceReannounce(const int index)
1391 {
1392  m_nativeHandle.force_reannounce(0, index);
1393 }
1394 
1396 {
1397  m_nativeHandle.force_dht_announce();
1398 }
1399 
1401 {
1402  if (!hasMetadata()) return;
1403 
1404  m_nativeHandle.force_recheck();
1405  m_hasMissingFiles = false;
1406  m_unchecked = false;
1407 
1408  if (isPaused())
1409  {
1410  // When "force recheck" is applied on paused torrent, we temporarily resume it
1411  // (really we just allow libtorrent to resume it by enabling auto management for it).
1412  m_nativeHandle.set_flags(lt::torrent_flags::stop_when_ready | lt::torrent_flags::auto_managed);
1413  }
1414 }
1415 
1416 void TorrentImpl::setSequentialDownload(const bool enable)
1417 {
1418  if (enable)
1419  {
1420  m_nativeHandle.set_flags(lt::torrent_flags::sequential_download);
1421  m_nativeStatus.flags |= lt::torrent_flags::sequential_download; // prevent return cached value
1422  }
1423  else
1424  {
1425  m_nativeHandle.unset_flags(lt::torrent_flags::sequential_download);
1426  m_nativeStatus.flags &= ~lt::torrent_flags::sequential_download; // prevent return cached value
1427  }
1428 
1430 }
1431 
1433 {
1434  if (m_hasFirstLastPiecePriority == enabled)
1435  return;
1436 
1437  m_hasFirstLastPiecePriority = enabled;
1438  if (hasMetadata())
1439  applyFirstLastPiecePriority(enabled);
1440 
1441  LogMsg(tr("Download first and last piece first: %1, torrent: '%2'")
1442  .arg((enabled ? tr("On") : tr("Off")), name()));
1443 
1445 }
1446 
1447 void TorrentImpl::applyFirstLastPiecePriority(const bool enabled, const QVector<DownloadPriority> &updatedFilePrio)
1448 {
1449  Q_ASSERT(hasMetadata());
1450 
1451  // Download first and last pieces first for every file in the torrent
1452 
1453  const QVector<DownloadPriority> filePriorities =
1454  !updatedFilePrio.isEmpty() ? updatedFilePrio : this->filePriorities();
1455  std::vector<lt::download_priority_t> piecePriorities = nativeHandle().get_piece_priorities();
1456 
1457  // Updating file priorities is an async operation in libtorrent, when we just updated it and immediately query it
1458  // we might get the old/wrong values, so we rely on `updatedFilePrio` in this case.
1459  for (int index = 0; index < filePriorities.size(); ++index)
1460  {
1461  const DownloadPriority filePrio = filePriorities[index];
1462  if (filePrio <= DownloadPriority::Ignored)
1463  continue;
1464 
1465  // Determine the priority to set
1466  const DownloadPriority newPrio = enabled ? DownloadPriority::Maximum : filePrio;
1467  const auto piecePrio = static_cast<lt::download_priority_t>(static_cast<int>(newPrio));
1468  const TorrentInfo::PieceRange extremities = m_torrentInfo.filePieces(index);
1469 
1470  // worst case: AVI index = 1% of total file size (at the end of the file)
1471  const int nNumPieces = std::ceil(fileSize(index) * 0.01 / pieceLength());
1472  for (int i = 0; i < nNumPieces; ++i)
1473  {
1474  piecePriorities[extremities.first() + i] = piecePrio;
1475  piecePriorities[extremities.last() - i] = piecePrio;
1476  }
1477  }
1478 
1479  m_nativeHandle.prioritize_pieces(piecePriorities);
1480 }
1481 
1482 void TorrentImpl::fileSearchFinished(const QString &savePath, const QStringList &fileNames)
1483 {
1485 }
1486 
1487 void TorrentImpl::endReceivedMetadataHandling(const QString &savePath, const QStringList &fileNames)
1488 {
1489  Q_ASSERT(m_filePaths.isEmpty());
1490 
1491  lt::add_torrent_params &p = m_ltAddTorrentParams;
1492 
1493  const std::shared_ptr<lt::torrent_info> metadata = std::const_pointer_cast<lt::torrent_info>(m_nativeHandle.torrent_file());
1494  m_torrentInfo = TorrentInfo(*metadata);
1495  const auto nativeIndexes = m_torrentInfo.nativeIndexes();
1496  for (int i = 0; i < fileNames.size(); ++i)
1497  {
1498  const QString filePath = fileNames.at(i);
1499  m_filePaths.append(filePath.endsWith(QB_EXT, Qt::CaseInsensitive) ? filePath.chopped(QB_EXT.size()) : filePath);
1500  p.renamed_files[nativeIndexes[i]] = filePath.toStdString();
1501  }
1502  p.save_path = Utils::Fs::toNativePath(savePath).toStdString();
1503  p.ti = metadata;
1504 
1505  const int internalFilesCount = p.ti->files().num_files(); // including .pad files
1506  // Use qBittorrent default priority rather than libtorrent's (4)
1507  p.file_priorities = std::vector(internalFilesCount, LT::toNative(DownloadPriority::Normal));
1508 
1509  reload();
1510 
1511  // If first/last piece priority was specified when adding this torrent,
1512  // we should apply it now that we have metadata:
1515 
1517  updateStatus();
1518  prepareResumeData(p);
1519 
1521 }
1522 
1524 {
1525  const auto queuePos = m_nativeHandle.queue_position();
1526 
1527  m_nativeSession->remove_torrent(m_nativeHandle, lt::session::delete_partfile);
1528 
1529  lt::add_torrent_params p = m_ltAddTorrentParams;
1530  p.flags |= lt::torrent_flags::update_subscribe
1531  | lt::torrent_flags::override_trackers
1532  | lt::torrent_flags::override_web_seeds;
1533 
1534  if (m_isStopped)
1535  {
1536  p.flags |= lt::torrent_flags::paused;
1537  p.flags &= ~lt::torrent_flags::auto_managed;
1538  }
1539  else if (m_operatingMode == TorrentOperatingMode::AutoManaged)
1540  {
1541  p.flags |= (lt::torrent_flags::auto_managed | lt::torrent_flags::paused);
1542  }
1543  else
1544  {
1545  p.flags &= ~(lt::torrent_flags::auto_managed | lt::torrent_flags::paused);
1546  }
1547 
1548  m_nativeHandle = m_nativeSession->add_torrent(p);
1549  m_nativeHandle.queue_position_set(queuePos);
1550 }
1551 
1553 {
1554  if (!m_isStopped)
1555  {
1556  m_isStopped = true;
1559  }
1560 
1562  {
1563  setAutoManaged(false);
1564  m_nativeHandle.pause();
1565 
1567  }
1568 }
1569 
1571 {
1572  if (hasError())
1573  {
1574  m_nativeHandle.clear_error();
1575  m_nativeHandle.unset_flags(lt::torrent_flags::upload_mode);
1576  }
1577 
1578  m_operatingMode = mode;
1579 
1580  if (m_hasMissingFiles)
1581  {
1582  m_hasMissingFiles = false;
1583  m_isStopped = false;
1584  m_ltAddTorrentParams.ti = std::const_pointer_cast<lt::torrent_info>(m_nativeHandle.torrent_file());
1585  reload();
1586  updateStatus();
1587  return;
1588  }
1589 
1590  if (m_isStopped)
1591  {
1592  // Torrent may have been temporarily resumed to perform checking files
1593  // so we have to ensure it will not pause after checking is done.
1594  m_nativeHandle.unset_flags(lt::torrent_flags::stop_when_ready);
1595 
1596  m_isStopped = false;
1599  }
1600 
1602  {
1603  setAutoManaged(m_operatingMode == TorrentOperatingMode::AutoManaged);
1604  if (m_operatingMode == TorrentOperatingMode::Forced)
1605  m_nativeHandle.resume();
1606  }
1607 }
1608 
1609 void TorrentImpl::moveStorage(const QString &newPath, const MoveStorageMode mode)
1610 {
1611  if (m_session->addMoveTorrentStorageJob(this, Utils::Fs::toNativePath(newPath), mode))
1612  {
1613  m_storageIsMoving = true;
1614  updateStatus();
1615  }
1616 }
1617 
1618 void TorrentImpl::renameFile(const int index, const QString &path)
1619 {
1620  ++m_renameCount;
1621  m_nativeHandle.rename_file(m_torrentInfo.nativeIndexes().at(index), Utils::Fs::toNativePath(path).toStdString());
1622 }
1623 
1624 void TorrentImpl::handleStateUpdate(const lt::torrent_status &nativeStatus)
1625 {
1626  updateStatus(nativeStatus);
1627 }
1628 
1630 {
1632 }
1633 
1634 void TorrentImpl::handleMoveStorageJobFinished(const bool hasOutstandingJob)
1635 {
1637  m_storageIsMoving = hasOutstandingJob;
1638 
1639  updateStatus();
1641 
1642  if (!m_storageIsMoving)
1643  {
1644  if (m_hasMissingFiles)
1645  {
1646  // it can be moved to the proper location
1647  m_hasMissingFiles = false;
1648  m_ltAddTorrentParams.save_path = m_nativeStatus.save_path;
1649  m_ltAddTorrentParams.ti = std::const_pointer_cast<lt::torrent_info>(m_nativeHandle.torrent_file());
1650  reload();
1651  updateStatus();
1652  }
1653 
1654  while ((m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
1655  m_moveFinishedTriggers.takeFirst()();
1656  }
1657 }
1658 
1659 void TorrentImpl::handleTrackerReplyAlert(const lt::tracker_reply_alert *p)
1660 {
1661  const QString trackerUrl = p->tracker_url();
1662  m_trackerPeerCounts[trackerUrl][p->local_endpoint] = p->num_peers;
1663 
1664  m_session->handleTorrentTrackerReply(this, trackerUrl);
1665 }
1666 
1667 void TorrentImpl::handleTrackerWarningAlert(const lt::tracker_warning_alert *p)
1668 {
1669  const QString trackerUrl = p->tracker_url();
1670  m_session->handleTorrentTrackerWarning(this, trackerUrl);
1671 }
1672 
1673 void TorrentImpl::handleTrackerErrorAlert(const lt::tracker_error_alert *p)
1674 {
1675  const QString trackerUrl = p->tracker_url();
1676 
1677  // Starting with libtorrent 1.2.x each tracker has multiple local endpoints from which
1678  // an announce is attempted. Some endpoints might succeed while others might fail.
1679  // Emit the signal only if all endpoints have failed.
1680  const QVector<TrackerEntry> trackerList = trackers();
1681  const auto iter = std::find_if(trackerList.cbegin(), trackerList.cend(), [&trackerUrl](const TrackerEntry &entry)
1682  {
1683  return (entry.url == trackerUrl);
1684  });
1685  if ((iter != trackerList.cend()) && (iter->status == TrackerEntry::NotWorking))
1686  m_session->handleTorrentTrackerError(this, trackerUrl);
1687 }
1688 
1689 void TorrentImpl::handleTorrentCheckedAlert(const lt::torrent_checked_alert *p)
1690 {
1691  Q_UNUSED(p);
1692  qDebug("\"%s\" have just finished checking.", qUtf8Printable(name()));
1693 
1694 
1695  if (!hasMetadata())
1696  {
1697  // The torrent is checked due to metadata received, but we should not process
1698  // this event until the torrent is reloaded using the received metadata.
1699  return;
1700  }
1701 
1702  if (m_nativeHandle.need_save_resume_data())
1704 
1706  m_fastresumeDataRejected = false;
1707 
1708  updateStatus();
1709 
1710  if (!m_hasMissingFiles)
1711  {
1712  if ((progress() < 1.0) && (wantedSize() > 0))
1713  m_hasSeedStatus = false;
1714  else if (progress() == 1.0)
1715  m_hasSeedStatus = true;
1716 
1719  }
1720 
1722 }
1723 
1724 void TorrentImpl::handleTorrentFinishedAlert(const lt::torrent_finished_alert *p)
1725 {
1726  Q_UNUSED(p);
1727  qDebug("Got a torrent finished alert for \"%s\"", qUtf8Printable(name()));
1728  qDebug("Torrent has seed status: %s", m_hasSeedStatus ? "yes" : "no");
1729  m_hasMissingFiles = false;
1730  if (m_hasSeedStatus) return;
1731 
1732  updateStatus();
1733  m_hasSeedStatus = true;
1734 
1737 
1739 
1740  const bool recheckTorrentsOnCompletion = Preferences::instance()->recheckTorrentsOnCompletion();
1741  if (isMoveInProgress() || (m_renameCount > 0))
1742  {
1743  if (recheckTorrentsOnCompletion)
1744  m_moveFinishedTriggers.append([this]() { forceRecheck(); });
1745  m_moveFinishedTriggers.append([this]() { m_session->handleTorrentFinished(this); });
1746  }
1747  else
1748  {
1749  if (recheckTorrentsOnCompletion && m_unchecked)
1750  forceRecheck();
1752  }
1753 }
1754 
1755 void TorrentImpl::handleTorrentPausedAlert(const lt::torrent_paused_alert *p)
1756 {
1757  Q_UNUSED(p);
1758 }
1759 
1760 void TorrentImpl::handleTorrentResumedAlert(const lt::torrent_resumed_alert *p)
1761 {
1762  Q_UNUSED(p);
1763 }
1764 
1765 void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
1766 {
1768  {
1769  m_ltAddTorrentParams = p->params;
1770 
1771  m_ltAddTorrentParams.have_pieces.clear();
1772  m_ltAddTorrentParams.verified_pieces.clear();
1773 
1774  TorrentInfo metadata = TorrentInfo(*m_nativeHandle.torrent_file());
1775 
1776  QStringList filePaths = metadata.filePaths();
1779  }
1780  else
1781  {
1782  prepareResumeData(p->params);
1783  }
1784 }
1785 
1786 void TorrentImpl::prepareResumeData(const lt::add_torrent_params &params)
1787 {
1788  if (m_hasMissingFiles)
1789  {
1790  const auto havePieces = m_ltAddTorrentParams.have_pieces;
1791  const auto unfinishedPieces = m_ltAddTorrentParams.unfinished_pieces;
1792  const auto verifiedPieces = m_ltAddTorrentParams.verified_pieces;
1793 
1794  // Update recent resume data but preserve existing progress
1795  m_ltAddTorrentParams = params;
1796  m_ltAddTorrentParams.have_pieces = havePieces;
1797  m_ltAddTorrentParams.unfinished_pieces = unfinishedPieces;
1798  m_ltAddTorrentParams.verified_pieces = verifiedPieces;
1799  }
1800  else
1801  {
1802  // Update recent resume data
1803  m_ltAddTorrentParams = params;
1804  }
1805 
1806  // We shouldn't save upload_mode flag to allow torrent operate normally on next run
1807  m_ltAddTorrentParams.flags &= ~lt::torrent_flags::upload_mode;
1808 
1809  LoadTorrentParams resumeData;
1810  resumeData.name = m_name;
1811  resumeData.category = m_category;
1812  resumeData.tags = m_tags;
1813  resumeData.contentLayout = m_contentLayout;
1814  resumeData.ratioLimit = m_ratioLimit;
1815  resumeData.seedingTimeLimit = m_seedingTimeLimit;
1817  resumeData.hasSeedStatus = m_hasSeedStatus;
1818  resumeData.stopped = m_isStopped;
1819  resumeData.operatingMode = m_operatingMode;
1821  resumeData.useAutoTMM = m_useAutoTMM;
1822  if (!resumeData.useAutoTMM)
1823  {
1824  resumeData.savePath = m_savePath;
1825  resumeData.downloadPath = m_downloadPath;
1826  }
1827 
1828  m_session->handleTorrentResumeDataReady(this, resumeData);
1829 }
1830 
1831 void TorrentImpl::handleSaveResumeDataFailedAlert(const lt::save_resume_data_failed_alert *p)
1832 {
1833  Q_UNUSED(p);
1834  Q_ASSERT_X(false, Q_FUNC_INFO, "This point should be unreachable since libtorrent 1.2.11");
1835 }
1836 
1837 void TorrentImpl::handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p)
1838 {
1839  m_fastresumeDataRejected = true;
1840 
1841  if (p->error.value() == lt::errors::mismatching_file_size)
1842  {
1843  // Mismatching file size (files were probably moved)
1844  m_hasMissingFiles = true;
1845  LogMsg(tr("File sizes mismatch for torrent '%1'. Cannot proceed further.").arg(name()), Log::CRITICAL);
1846  }
1847  else
1848  {
1849  LogMsg(tr("Fast resume data was rejected for torrent '%1'. Reason: %2. Checking again...")
1850  .arg(name(), QString::fromStdString(p->message())), Log::WARNING);
1851  }
1852 }
1853 
1854 void TorrentImpl::handleFileRenamedAlert(const lt::file_renamed_alert *p)
1855 {
1856  const int fileIndex = m_torrentInfo.nativeIndexes().indexOf(p->index);
1857  Q_ASSERT(fileIndex >= 0);
1858 
1859  // Remove empty leftover folders
1860  // For example renaming "a/b/c" to "d/b/c", then folders "a/b" and "a" will
1861  // be removed if they are empty
1862  const QString oldFilePath = m_filePaths.at(fileIndex);
1863  const QString newFilePath = Utils::Fs::toUniformPath(p->new_name());
1864 
1865  // Check if ".!qB" extension was just added or removed
1866  if ((oldFilePath != newFilePath) && (oldFilePath != newFilePath.chopped(QB_EXT.size())))
1867  {
1868  m_filePaths[fileIndex] = newFilePath;
1869 
1870  QList<QStringView> oldPathParts = QStringView(oldFilePath).split('/', Qt::SkipEmptyParts);
1871  oldPathParts.removeLast(); // drop file name part
1872  QList<QStringView> newPathParts = QStringView(newFilePath).split('/', Qt::SkipEmptyParts);
1873  newPathParts.removeLast(); // drop file name part
1874 
1875 #if defined(Q_OS_WIN)
1876  const Qt::CaseSensitivity caseSensitivity = Qt::CaseInsensitive;
1877 #else
1878  const Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive;
1879 #endif
1880 
1881  int pathIdx = 0;
1882  while ((pathIdx < oldPathParts.size()) && (pathIdx < newPathParts.size()))
1883  {
1884  if (oldPathParts[pathIdx].compare(newPathParts[pathIdx], caseSensitivity) != 0)
1885  break;
1886  ++pathIdx;
1887  }
1888 
1889  for (int i = (oldPathParts.size() - 1); i >= pathIdx; --i)
1890  {
1891  QDir().rmdir(savePath() + Utils::String::join(oldPathParts, QString::fromLatin1("/")));
1892  oldPathParts.removeLast();
1893  }
1894  }
1895 
1896  --m_renameCount;
1897  while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
1898  m_moveFinishedTriggers.takeFirst()();
1899 
1901 }
1902 
1903 void TorrentImpl::handleFileRenameFailedAlert(const lt::file_rename_failed_alert *p)
1904 {
1905  const int fileIndex = m_torrentInfo.nativeIndexes().indexOf(p->index);
1906  Q_ASSERT(fileIndex >= 0);
1907 
1908  LogMsg(tr("File rename failed. Torrent: \"%1\", file: \"%2\", reason: \"%3\"")
1909  .arg(name(), filePath(fileIndex), QString::fromLocal8Bit(p->error.message().c_str())), Log::WARNING);
1910 
1911  --m_renameCount;
1912  while (!isMoveInProgress() && (m_renameCount == 0) && !m_moveFinishedTriggers.isEmpty())
1913  m_moveFinishedTriggers.takeFirst()();
1914 
1916 }
1917 
1918 void TorrentImpl::handleFileCompletedAlert(const lt::file_completed_alert *p)
1919 {
1920  const int fileIndex = m_torrentInfo.nativeIndexes().indexOf(p->index);
1921  Q_ASSERT(fileIndex >= 0);
1922 
1923  qDebug("A file completed download in torrent \"%s\"", qUtf8Printable(name()));
1925  {
1926  const QString path = filePath(fileIndex);
1927  const QString actualPath = actualFilePath(fileIndex);
1928  if (actualPath != path)
1929  {
1930  qDebug("Renaming %s to %s", qUtf8Printable(actualPath), qUtf8Printable(path));
1931  renameFile(fileIndex, path);
1932  }
1933  }
1934 }
1935 
1936 void TorrentImpl::handleFileErrorAlert(const lt::file_error_alert *p)
1937 {
1938  m_lastFileError = {p->error, p->op};
1939 }
1940 
1941 #ifdef QBT_USES_LIBTORRENT2
1942 void TorrentImpl::handleFilePrioAlert(const lt::file_prio_alert *)
1943 {
1944  if (m_nativeHandle.need_save_resume_data())
1946 }
1947 #endif
1948 
1949 void TorrentImpl::handleMetadataReceivedAlert(const lt::metadata_received_alert *p)
1950 {
1951  Q_UNUSED(p);
1952  qDebug("Metadata received for torrent %s.", qUtf8Printable(name()));
1953 
1956 }
1957 
1958 void TorrentImpl::handlePerformanceAlert(const lt::performance_alert *p) const
1959 {
1960  LogMsg((tr("Performance alert: ") + QString::fromStdString(p->message()))
1961  , Log::INFO);
1962 }
1963 
1965 {
1966  if (m_useAutoTMM)
1968 }
1969 
1971 {
1972  if (!hasMetadata()) return;
1973 
1975 }
1976 
1977 void TorrentImpl::handleAlert(const lt::alert *a)
1978 {
1979  switch (a->type())
1980  {
1981 #ifdef QBT_USES_LIBTORRENT2
1982  case lt::file_prio_alert::alert_type:
1983  handleFilePrioAlert(static_cast<const lt::file_prio_alert*>(a));
1984  break;
1985 #endif
1986  case lt::file_renamed_alert::alert_type:
1987  handleFileRenamedAlert(static_cast<const lt::file_renamed_alert*>(a));
1988  break;
1989  case lt::file_rename_failed_alert::alert_type:
1990  handleFileRenameFailedAlert(static_cast<const lt::file_rename_failed_alert*>(a));
1991  break;
1992  case lt::file_completed_alert::alert_type:
1993  handleFileCompletedAlert(static_cast<const lt::file_completed_alert*>(a));
1994  break;
1995  case lt::file_error_alert::alert_type:
1996  handleFileErrorAlert(static_cast<const lt::file_error_alert*>(a));
1997  break;
1998  case lt::torrent_finished_alert::alert_type:
1999  handleTorrentFinishedAlert(static_cast<const lt::torrent_finished_alert*>(a));
2000  break;
2001  case lt::save_resume_data_alert::alert_type:
2002  handleSaveResumeDataAlert(static_cast<const lt::save_resume_data_alert*>(a));
2003  break;
2004  case lt::save_resume_data_failed_alert::alert_type:
2005  handleSaveResumeDataFailedAlert(static_cast<const lt::save_resume_data_failed_alert*>(a));
2006  break;
2007  case lt::torrent_paused_alert::alert_type:
2008  handleTorrentPausedAlert(static_cast<const lt::torrent_paused_alert*>(a));
2009  break;
2010  case lt::torrent_resumed_alert::alert_type:
2011  handleTorrentResumedAlert(static_cast<const lt::torrent_resumed_alert*>(a));
2012  break;
2013  case lt::tracker_error_alert::alert_type:
2014  handleTrackerErrorAlert(static_cast<const lt::tracker_error_alert*>(a));
2015  break;
2016  case lt::tracker_reply_alert::alert_type:
2017  handleTrackerReplyAlert(static_cast<const lt::tracker_reply_alert*>(a));
2018  break;
2019  case lt::tracker_warning_alert::alert_type:
2020  handleTrackerWarningAlert(static_cast<const lt::tracker_warning_alert*>(a));
2021  break;
2022  case lt::metadata_received_alert::alert_type:
2023  handleMetadataReceivedAlert(static_cast<const lt::metadata_received_alert*>(a));
2024  break;
2025  case lt::fastresume_rejected_alert::alert_type:
2026  handleFastResumeRejectedAlert(static_cast<const lt::fastresume_rejected_alert*>(a));
2027  break;
2028  case lt::torrent_checked_alert::alert_type:
2029  handleTorrentCheckedAlert(static_cast<const lt::torrent_checked_alert*>(a));
2030  break;
2031  case lt::performance_alert::alert_type:
2032  handlePerformanceAlert(static_cast<const lt::performance_alert*>(a));
2033  break;
2034  }
2035 }
2036 
2038 {
2039  const bool isAppendExtensionEnabled = m_session->isAppendExtensionEnabled();
2040  const QVector<qreal> fp = filesProgress();
2041  if (fp.size() != filesCount())
2042  {
2043  qDebug() << "skip manageIncompleteFiles because of invalid torrent meta-data or empty file-progress";
2044  return;
2045  }
2046 
2047  for (int i = 0; i < filesCount(); ++i)
2048  {
2049  const QString path = filePath(i);
2050  const QString actualPath = actualFilePath(i);
2051  if (isAppendExtensionEnabled && (fileSize(i) > 0) && (fp[i] < 1))
2052  {
2053  const QString wantedPath = path + QB_EXT;
2054  if (actualPath != wantedPath)
2055  {
2056  qDebug() << "Renaming" << actualPath << "to" << wantedPath;
2057  renameFile(i, wantedPath);
2058  }
2059  }
2060  else
2061  {
2062  if (actualPath != path)
2063  {
2064  qDebug() << "Renaming" << actualPath << "to" << path;
2065  renameFile(i, path);
2066  }
2067  }
2068  }
2069 }
2070 
2072 {
2073  const QString downloadPath = this->downloadPath();
2074  const bool isFinished = isSeed() || m_hasSeedStatus;
2075  const QDir targetDir {((isFinished || downloadPath.isEmpty()) ? savePath() : downloadPath)};
2076 
2077  moveStorage(targetDir.absolutePath(), MoveStorageMode::Overwrite);
2078 }
2079 
2080 lt::torrent_handle TorrentImpl::nativeHandle() const
2081 {
2082  return m_nativeHandle;
2083 }
2084 
2086 {
2087  return m_storageIsMoving;
2088 }
2089 
2091 {
2092  updateStatus(m_nativeHandle.status());
2093 }
2094 
2095 void TorrentImpl::updateStatus(const lt::torrent_status &nativeStatus)
2096 {
2097  m_nativeStatus = nativeStatus;
2098  updateState();
2099 
2100  m_speedMonitor.addSample({nativeStatus.download_payload_rate
2101  , nativeStatus.upload_payload_rate});
2102 
2103  if (hasMetadata())
2104  {
2105  // NOTE: Don't change the order of these conditionals!
2106  // Otherwise it will not work properly since torrent can be CheckingDownloading.
2107  if (isChecking())
2108  m_unchecked = false;
2109  else if (isDownloading())
2110  m_unchecked = true;
2111  }
2112 }
2113 
2115 {
2116  if (limit < USE_GLOBAL_RATIO)
2117  limit = NO_RATIO_LIMIT;
2118  else if (limit > MAX_RATIO)
2119  limit = MAX_RATIO;
2120 
2121  if (m_ratioLimit != limit)
2122  {
2123  m_ratioLimit = limit;
2126  }
2127 }
2128 
2130 {
2131  if (limit < USE_GLOBAL_SEEDING_TIME)
2132  limit = NO_SEEDING_TIME_LIMIT;
2133  else if (limit > MAX_SEEDING_TIME)
2134  limit = MAX_SEEDING_TIME;
2135 
2136  if (m_seedingTimeLimit != limit)
2137  {
2138  m_seedingTimeLimit = limit;
2141  }
2142 }
2143 
2144 void TorrentImpl::setUploadLimit(const int limit)
2145 {
2146  if (limit == uploadLimit())
2147  return;
2148 
2149  m_nativeHandle.set_upload_limit(limit);
2151 }
2152 
2153 void TorrentImpl::setDownloadLimit(const int limit)
2154 {
2155  if (limit == downloadLimit())
2156  return;
2157 
2158  m_nativeHandle.set_download_limit(limit);
2160 }
2161 
2162 void TorrentImpl::setSuperSeeding(const bool enable)
2163 {
2164  if (enable == superSeeding())
2165  return;
2166 
2167  if (enable)
2168  m_nativeHandle.set_flags(lt::torrent_flags::super_seeding);
2169  else
2170  m_nativeHandle.unset_flags(lt::torrent_flags::super_seeding);
2171 
2173 }
2174 
2175 void TorrentImpl::setDHTDisabled(const bool disable)
2176 {
2177  if (disable == isDHTDisabled())
2178  return;
2179 
2180  if (disable)
2181  m_nativeHandle.set_flags(lt::torrent_flags::disable_dht);
2182  else
2183  m_nativeHandle.unset_flags(lt::torrent_flags::disable_dht);
2184 
2186 }
2187 
2188 void TorrentImpl::setPEXDisabled(const bool disable)
2189 {
2190  if (disable == isPEXDisabled())
2191  return;
2192 
2193  if (disable)
2194  m_nativeHandle.set_flags(lt::torrent_flags::disable_pex);
2195  else
2196  m_nativeHandle.unset_flags(lt::torrent_flags::disable_pex);
2197 
2199 }
2200 
2201 void TorrentImpl::setLSDDisabled(const bool disable)
2202 {
2203  if (disable == isLSDDisabled())
2204  return;
2205 
2206  if (disable)
2207  m_nativeHandle.set_flags(lt::torrent_flags::disable_lsd);
2208  else
2209  m_nativeHandle.unset_flags(lt::torrent_flags::disable_lsd);
2210 
2212 }
2213 
2215 {
2216  m_nativeHandle.flush_cache();
2217 }
2218 
2220 {
2221  return QString::fromStdString(lt::make_magnet_uri(m_nativeHandle));
2222 }
2223 
2224 void TorrentImpl::prioritizeFiles(const QVector<DownloadPriority> &priorities)
2225 {
2226  if (!hasMetadata()) return;
2227 
2228  Q_ASSERT(priorities.size() == filesCount());
2229 
2230  // Reset 'm_hasSeedStatus' if needed in order to react again to
2231  // 'torrent_finished_alert' and eg show tray notifications
2232  const QVector<qreal> progress = filesProgress();
2233  const QVector<DownloadPriority> oldPriorities = filePriorities();
2234  for (int i = 0; i < oldPriorities.size(); ++i)
2235  {
2236  if ((oldPriorities[i] == DownloadPriority::Ignored)
2237  && (priorities[i] > DownloadPriority::Ignored)
2238  && (progress[i] < 1.0))
2239  {
2240  m_hasSeedStatus = false;
2241  break;
2242  }
2243  }
2244 
2245  const int internalFilesCount = m_torrentInfo.nativeInfo()->files().num_files(); // including .pad files
2246  auto nativePriorities = std::vector<lt::download_priority_t>(internalFilesCount, LT::toNative(DownloadPriority::Normal));
2247  const auto nativeIndexes = m_torrentInfo.nativeIndexes();
2248  for (int i = 0; i < priorities.size(); ++i)
2249  nativePriorities[LT::toUnderlyingType(nativeIndexes[i])] = LT::toNative(priorities[i]);
2250 
2251  qDebug() << Q_FUNC_INFO << "Changing files priorities...";
2252  m_nativeHandle.prioritize_files(nativePriorities);
2253 
2254  // Restore first/last piece first option if necessary
2256  applyFirstLastPiecePriority(true, priorities);
2257 }
2258 
2260 {
2261  Q_ASSERT(hasMetadata());
2262 
2263  const int filesCount = this->filesCount();
2264  if (filesCount <= 0) return {};
2265 
2266  const QVector<int> piecesAvailability = pieceAvailability();
2267  // libtorrent returns empty array for seeding only torrents
2268  if (piecesAvailability.empty()) return QVector<qreal>(filesCount, -1);
2269 
2270  QVector<qreal> res;
2271  res.reserve(filesCount);
2272  for (int i = 0; i < filesCount; ++i)
2273  {
2274  const TorrentInfo::PieceRange filePieces = m_torrentInfo.filePieces(i);
2275 
2276  int availablePieces = 0;
2277  for (const int piece : filePieces)
2278  availablePieces += (piecesAvailability[piece] > 0) ? 1 : 0;
2279 
2280  const qreal availability = filePieces.isEmpty()
2281  ? 1 // the file has no pieces, so it is available by default
2282  : static_cast<qreal>(availablePieces) / filePieces.size();
2283  res.push_back(availability);
2284  }
2285  return res;
2286 }
void handleTorrentUrlSeedsAdded(TorrentImpl *const torrent, const QVector< QUrl > &newUrlSeeds)
Definition: session.cpp:3925
void handleTorrentChecked(TorrentImpl *const torrent)
Definition: session.cpp:3963
void handleTorrentTagAdded(TorrentImpl *const torrent, const QString &tag)
Definition: session.cpp:3885
void handleTorrentCategoryChanged(TorrentImpl *const torrent, const QString &oldCategory)
Definition: session.cpp:3880
qreal globalMaxRatio() const
Definition: session.cpp:928
void handleTorrentTrackersRemoved(TorrentImpl *const torrent, const QVector< TrackerEntry > &deletedTrackers)
Definition: session.cpp:3910
bool hasTag(const QString &tag) const
Definition: session.cpp:834
int globalMaxSeedingMinutes() const
Definition: session.cpp:947
bool addMoveTorrentStorageJob(TorrentImpl *torrent, const QString &newPath, MoveStorageMode mode)
Definition: session.cpp:4027
void handleTorrentTrackerWarning(TorrentImpl *const torrent, const QString &trackerUrl)
Definition: session.cpp:4209
void handleTorrentSavingModeChanged(TorrentImpl *const torrent)
Definition: session.cpp:3895
static bool isValidCategoryName(const QString &name)
Definition: session.cpp:630
void handleTorrentMetadataReceived(TorrentImpl *const torrent)
Definition: session.cpp:3937
bool isDisableAutoTMMWhenCategoryChanged() const
Definition: session.cpp:873
QString downloadPath() const
Definition: session.cpp:625
bool addTag(const QString &tag)
Definition: session.cpp:839
bool isSubcategoriesEnabled() const
Definition: session.cpp:798
void handleTorrentSaveResumeDataRequested(const TorrentImpl *torrent)
Definition: session.cpp:1991
void findIncompleteFiles(const TorrentInfo &torrentInfo, const QString &savePath, const QString &downloadPath, const QStringList &filePaths={}) const
Definition: session.cpp:2268
void handleTorrentSavePathChanged(TorrentImpl *const torrent)
Definition: session.cpp:3875
void handleTorrentNameChanged(TorrentImpl *const torrent)
Definition: session.cpp:3871
void handleTorrentPaused(TorrentImpl *const torrent)
Definition: session.cpp:3953
bool isQueueingSystemEnabled() const
Definition: session.cpp:3411
void handleTorrentTrackersAdded(TorrentImpl *const torrent, const QVector< TrackerEntry > &newTrackers)
Definition: session.cpp:3900
QString savePath() const
Definition: session.cpp:620
void handleTorrentTrackerError(TorrentImpl *const torrent, const QString &trackerUrl)
Definition: session.cpp:4022
void handleTorrentTagRemoved(TorrentImpl *const torrent, const QString &tag)
Definition: session.cpp:3890
void handleTorrentResumed(TorrentImpl *const torrent)
Definition: session.cpp:3958
void handleTorrentNeedSaveResumeData(const TorrentImpl *torrent)
Definition: session.cpp:1972
void handleTorrentShareLimitChanged(TorrentImpl *const torrent)
Definition: session.cpp:3866
static bool isValidTag(const QString &tag)
Definition: session.cpp:829
void handleTorrentResumeDataReady(TorrentImpl *const torrent, const LoadTorrentParams &data)
Definition: session.cpp:4010
void handleTorrentTrackerReply(TorrentImpl *const torrent, const QString &trackerUrl)
Definition: session.cpp:4017
QString categorySavePath(const QString &categoryName) const
Definition: session.cpp:669
void handleTorrentFinished(TorrentImpl *const torrent)
Definition: session.cpp:3968
bool isAppendExtensionEnabled() const
Definition: session.cpp:556
QStringList categories() const
Definition: session.cpp:659
void handleTorrentUrlSeedsRemoved(TorrentImpl *const torrent, const QVector< QUrl > &urlSeeds)
Definition: session.cpp:3931
QString categoryDownloadPath(const QString &categoryName) const
Definition: session.cpp:682
void handleTorrentTrackersChanged(TorrentImpl *const torrent)
Definition: session.cpp:3920
static const int NO_SEEDING_TIME_LIMIT
Definition: torrent.h:108
TorrentID id() const
Definition: torrent.cpp:54
static const int USE_GLOBAL_SEEDING_TIME
Definition: torrent.h:107
static const qreal NO_RATIO_LIMIT
Definition: torrent.h:105
static const int MAX_SEEDING_TIME
Definition: torrent.h:111
static const qreal USE_GLOBAL_RATIO
Definition: torrent.h:104
static const qreal MAX_RATIO
Definition: torrent.h:110
QHash< QString, QMap< lt::tcp::endpoint, int > > m_trackerPeerCounts
Definition: torrentimpl.h:300
qlonglong activeTime() const override
qreal distributedCopies() const override
bool isPEXDisabled() const override
void handleFastResumeRejectedAlert(const lt::fastresume_rejected_alert *p)
QVector< qreal > filesProgress() const override
bool isQueued() const override
void clearPeers() override
bool setCategory(const QString &category) override
bool connectPeer(const PeerAddress &peerAddress) override
QVector< qreal > availableFileFractions() const override
fraction of file pieces that are available at least from one peer
void handleStateUpdate(const lt::torrent_status &nativeStatus)
qlonglong finishedTime() const override
int uploadPayloadRate() const override
void addTrackers(const QVector< TrackerEntry > &trackers) override
bool isDHTDisabled() const override
void setAutoManaged(bool enable)
int totalPeersCount() const override
QVector< QUrl > urlSeeds() const override
bool isErrored() const override
QString currentTracker() const override
QString contentPath() const override
void setFirstLastPiecePriority(bool enabled) override
bool belongsToCategory(const QString &category) const override
void handleTrackerReplyAlert(const lt::tracker_reply_alert *p)
void replaceTrackers(const QVector< TrackerEntry > &trackers) override
void handleSaveResumeDataAlert(const lt::save_resume_data_alert *p)
bool isDownloading() const override
qlonglong wantedSize() const override
TorrentImpl(Session *session, lt::session *nativeSession, const lt::torrent_handle &nativeHandle, const LoadTorrentParams &params)
void setUploadLimit(int limit) override
QDateTime addedTime() const override
lt::torrent_handle m_nativeHandle
Definition: torrentimpl.h:283
void setName(const QString &name) override
qreal progress() const override
void setPEXDisabled(bool disable) override
void setAutoTMMEnabled(bool enabled) override
void handleFileCompletedAlert(const lt::file_completed_alert *p)
QString category() const override
qlonglong totalSize() const override
void resume(TorrentOperatingMode mode=TorrentOperatingMode::AutoManaged) override
bool isUploading() const override
lt::session * m_nativeSession
Definition: torrentimpl.h:282
qlonglong eta() const override
FileErrorInfo m_lastFileError
Definition: torrentimpl.h:301
int downloadLimit() const override
bool hasFirstLastPiecePriority() const override
int piecesCount() const override
void renameFile(int index, const QString &path) override
void setSequentialDownload(bool enable) override
int completeCount() const override
int connectionsCount() const override
bool isCompleted() const override
qlonglong totalUpload() const override
QQueue< EventTrigger > m_moveFinishedTriggers
Definition: torrentimpl.h:294
qreal maxRatio() const override
TorrentInfo info() const override
void setSuperSeeding(bool enable) override
qlonglong fileSize(int index) const override
qlonglong timeSinceActivity() const override
QString downloadPath() const override
QVector< TrackerEntry > trackers() const override
void removeAllTags() override
void handleTorrentCheckedAlert(const lt::torrent_checked_alert *p)
TagSet tags() const override
qlonglong timeSinceDownload() const override
lt::torrent_status m_nativeStatus
Definition: torrentimpl.h:284
int queuePosition() const override
void prioritizeFiles(const QVector< DownloadPriority > &priorities) override
void handleTorrentPausedAlert(const lt::torrent_paused_alert *p)
qreal realRatio() const override
lt::add_torrent_params m_ltAddTorrentParams
Definition: torrentimpl.h:322
int incompleteCount() const override
QVector< int > pieceAvailability() const override
void handleFileErrorAlert(const lt::file_error_alert *p)
QString error() const override
int totalSeedsCount() const override
bool isForced() const override
int seedsCount() const override
void setDHTDisabled(bool disable) override
void handleAlert(const lt::alert *a)
void handleSaveResumeDataFailedAlert(const lt::save_resume_data_failed_alert *p)
qlonglong pieceLength() const override
QDateTime creationDate() const override
qlonglong totalDownload() const override
SpeedMonitor m_speedMonitor
Definition: torrentimpl.h:288
int filesCount() const override
void handleMoveStorageJobFinished(bool hasOutstandingJob)
QString filePath(int index) const override
void setDownloadPath(const QString &path) override
void forceRecheck() override
bool isLSDDisabled() const override
bool isChecking() const override
qlonglong wastedSize() const override
QString actualFilePath(int index) const override
void handleTorrentResumedAlert(const lt::torrent_resumed_alert *p)
lt::torrent_handle nativeHandle() const
TorrentInfo m_torrentInfo
Definition: torrentimpl.h:286
bool isActive() const override
bool isMoveInProgress() const
QString savePath() const override
bool isSeed() const override
bool removeTag(const QString &tag) override
bool hasTag(const QString &tag) const override
void forceReannounce(int index=-1) override
QVector< PeerInfo > peers() const override
bool needSaveResumeData() const
int leechsCount() const override
void forceDHTAnnounce() override
void setSavePath(const QString &path) override
void handleFileRenameFailedAlert(const lt::file_rename_failed_alert *p)
bool isPrivate() const override
void addUrlSeeds(const QVector< QUrl > &urlSeeds) override
TorrentOperatingMode m_operatingMode
Definition: torrentimpl.h:311
TorrentState state() const override
bool isInactive() const override
void endReceivedMetadataHandling(const QString &savePath, const QStringList &fileNames)
QString name() const override
QString createMagnetURI() const override
void removeUrlSeeds(const QVector< QUrl > &urlSeeds) override
TorrentContentLayout m_contentLayout
Definition: torrentimpl.h:312
bool isAutoTMMEnabled() const override
int downloadPayloadRate() const override
int maxSeedingTime() const override
bool hasError() const override
bool hasMissingFiles() const override
QString creator() const override
void handleTrackerErrorAlert(const lt::tracker_error_alert *p)
qreal ratioLimit() const override
void setDownloadLimit(int limit) override
void applyFirstLastPiecePriority(bool enabled, const QVector< DownloadPriority > &updatedFilePrio={})
qlonglong totalPayloadUpload() const override
int uploadLimit() const override
void setLSDDisabled(bool disable) override
int connectionsLimit() const override
int piecesHave() const override
int peersCount() const override
void fileSearchFinished(const QString &savePath, const QStringList &fileNames)
QString rootPath() const override
bool isSequentialDownload() const override
void flushCache() const override
void moveStorage(const QString &newPath, MoveStorageMode mode)
int seedingTimeLimit() const override
qlonglong totalPayloadDownload() const override
void handleFileRenamedAlert(const lt::file_renamed_alert *p)
void handlePerformanceAlert(const lt::performance_alert *p) const
void prepareResumeData(const lt::add_torrent_params &params)
QDateTime lastSeenComplete() const override
qlonglong completedSize() const override
QStringList filePaths() const override
void handleMetadataReceivedAlert(const lt::metadata_received_alert *p)
void setSeedingTimeLimit(int limit) override
MaintenanceJob m_maintenanceJob
Definition: torrentimpl.h:298
bool isPaused() const override
Session *const m_session
Definition: torrentimpl.h:281
qlonglong nextAnnounce() const override
bool addTag(const QString &tag) override
void handleTrackerWarningAlert(const lt::tracker_warning_alert *p)
void handleTorrentFinishedAlert(const lt::torrent_finished_alert *p)
bool hasMetadata() const override
QString comment() const override
qlonglong timeSinceUpload() const override
void setRatioLimit(qreal limit) override
QVector< DownloadPriority > filePriorities() const override
QBitArray downloadingPieces() const override
QDateTime completedTime() const override
int totalLeechersCount() const override
QBitArray pieces() const override
InfoHash infoHash() const override
QString actualStorageLocation() const override
bool superSeeding() const override
qlonglong totalSize() const
QVector< lt::file_index_t > nativeIndexes() const
QString comment() const
PieceRange filePieces(const QString &file) const
std::shared_ptr< lt::torrent_info > nativeInfo() const
QStringList filePaths() const
QDateTime creationDate() const
QString name() const
QString rootFolder() const
QString creator() const
qlonglong fileSize(int index) const
QString toString() const
Definition: digest32.h:85
constexpr IndexDiffType size() const
Definition: indexrange.h:146
constexpr bool isEmpty() const
Definition: indexrange.h:161
constexpr IndexType last() const
Definition: indexrange.h:156
constexpr IndexType first() const
Definition: indexrange.h:151
bool contains(const key_type &value) const
Definition: orderedset.h:54
bool remove(const key_type &value)
Definition: orderedset.h:98
static Preferences * instance()
bool recheckTorrentsOnCompletion() const
void addSample(const SpeedSample &sample)
SpeedSampleAvg average() const
const QString QB_EXT
Definition: common.h:33
constexpr std::add_const_t< T > & asConst(T &t) noexcept
Definition: global.h:42
void LogMsg(const QString &message, const Log::MsgType &type)
Definition: logger.cpp:125
constexpr lt::download_priority_t toNative(const DownloadPriority priority) noexcept
Definition: lttypecast.h:43
constexpr T::underlying_type toUnderlyingType(const T &t) noexcept
Definition: lttypecast.h:38
constexpr DownloadPriority fromNative(const lt::download_priority_t priority) noexcept
Definition: lttypecast.h:48
TorrentState
Definition: torrent.h:70
void applyContentLayout(QStringList &filePaths, TorrentContentLayout contentLayout, const QString &rootFolder={})
@ WARNING
Definition: logger.h:47
@ CRITICAL
Definition: logger.h:48
@ INFO
Definition: logger.h:46
QString fileName(const QString &filePath)
Definition: fs.cpp:87
QString resolvePath(const QString &relativePath, const QString &basePath)
Definition: fs.cpp:74
QString toUniformPath(const QString &path)
Definition: fs.cpp:69
QString branchPath(const QString &filePath, QString *removed=nullptr)
Definition: fs.cpp:276
QString toNativePath(const QString &path)
Definition: fs.cpp:64
QString join(const QList< QStringView > &strings, QStringView separator)
Definition: string.cpp:102
lt::announce_entry makeNativeAnnouncerEntry(const QString &url, const int tier)
Definition: torrentimpl.cpp:73
void initializeStatus(lt::torrent_status &status, const lt::add_torrent_params &params)
TrackerEntry fromNativeAnnouncerEntry(const lt::announce_entry &nativeEntry, const QMap< lt::tcp::endpoint, int > &trackerPeerCounts)
Definition: torrentimpl.cpp:84
lt::error_code error
Definition: torrentimpl.h:73
TorrentContentLayout contentLayout
lt::add_torrent_params ltAddTorrentParams
TorrentOperatingMode operatingMode
QString toString() const
Definition: peeraddress.cpp:64
Definition: trackerentry.h:48
int numDownloaded
Definition: trackerentry.h:55
int numPeers
Definition: trackerentry.h:52
int protocolVersion
Definition: trackerentry.h:49
int numSeeds
Definition: trackerentry.h:53
QString message
Definition: trackerentry.h:56
Status status
Definition: trackerentry.h:51
int numLeeches
Definition: trackerentry.h:54
Definition: trackerentry.h:38
int tier
Definition: trackerentry.h:60
@ Working
Definition: trackerentry.h:42
@ NotWorking
Definition: trackerentry.h:44
@ Updating
Definition: trackerentry.h:43
@ NotContacted
Definition: trackerentry.h:41
T download
Definition: speedmonitor.h:63
const qlonglong MAX_ETA
Definition: types.h:33