transmission/qt/Torrent.h

703 lines
16 KiB
C++

// This file Copyright © 2009-2023 Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#pragma once
#include <bitset>
#include <cstddef> // size_t
#include <cstdint> // uint64_t
#include <ctime> // time_t
#include <optional>
#include <vector>
#include <QIcon>
#include <QMetaType>
#include <QObject>
#include <QString>
#include <libtransmission/transmission.h>
#include <libtransmission/crypto-utils.h>
#include <libtransmission/quark.h>
#include <libtransmission/tr-macros.h>
#include "FaviconCache.h"
#include "IconCache.h"
#include "Speed.h"
#ifdef ERROR
#undef ERROR
#endif
class QPixmap;
class Prefs;
extern "C"
{
struct tr_variant;
}
struct Peer
{
bool client_is_choked;
bool client_is_interested;
bool is_downloading_from;
bool is_encrypted;
bool is_incoming;
bool is_uploading_to;
bool peer_is_choked;
bool peer_is_interested;
QString address;
QString client_name;
QString flags;
int port;
Speed rate_to_client;
Speed rate_to_peer;
double progress;
};
using PeerList = std::vector<Peer>;
struct TrackerStat
{
QPixmap getFavicon() const;
bool has_announced;
bool has_scraped;
bool is_backup;
bool last_announce_succeeded;
bool last_announce_timed_out;
bool last_scrape_succeeded;
bool last_scrape_timed_out;
int announce_state;
int download_count;
int id;
int last_announce_peer_count;
int last_announce_start_time;
int last_announce_time;
int last_scrape_start_time;
int last_scrape_time;
int leecher_count;
int next_announce_time;
int next_scrape_time;
int scrape_state;
int seeder_count;
int tier;
QString announce;
QString last_announce_result;
QString last_scrape_result;
QString sitename;
};
using TrackerStatsList = std::vector<TrackerStat>;
struct TorrentFile
{
bool wanted = true;
int index = -1;
int priority = 0;
QString filename;
uint64_t size = 0;
uint64_t have = 0;
};
using FileList = std::vector<TorrentFile>;
class TorrentHash
{
private:
tr_sha1_digest_t data_ = {};
public:
TorrentHash() = default;
explicit TorrentHash(tr_sha1_digest_t const& data)
: data_{ data }
{
}
explicit TorrentHash(char const* str)
{
if (auto const hash = tr_sha1_from_string(str != nullptr ? str : ""); hash)
{
data_ = *hash;
}
}
explicit TorrentHash(QString const& str)
{
if (auto const hash = tr_sha1_from_string(str.toStdString()); hash)
{
data_ = *hash;
}
}
[[nodiscard]] TR_CONSTEXPR20 auto operator==(TorrentHash const& that) const
{
return data_ == that.data_;
}
[[nodiscard]] TR_CONSTEXPR20 auto operator!=(TorrentHash const& that) const
{
return !(*this == that);
}
[[nodiscard]] auto operator<(TorrentHash const& that) const
{
return data_ < that.data_;
}
QString toString() const
{
return QString::fromStdString(tr_sha1_to_string(data_));
}
};
class Torrent : public QObject
{
Q_OBJECT
TR_DISABLE_COPY_MOVE(Torrent)
public:
Torrent(Prefs const&, int id);
[[nodiscard]] constexpr auto getBandwidthPriority() const noexcept
{
return bandwidth_priority_;
}
[[nodiscard]] constexpr auto id() const noexcept
{
return id_;
}
[[nodiscard]] constexpr auto const& name() const noexcept
{
return name_;
}
bool hasName() const
{
return !name_.isEmpty();
}
[[nodiscard]] constexpr auto const& creator() const noexcept
{
return creator_;
}
[[nodiscard]] constexpr auto const& comment() const noexcept
{
return comment_;
}
[[nodiscard]] constexpr auto const& getPath() const noexcept
{
return download_dir_;
}
QString getError() const;
[[nodiscard]] constexpr auto const& trackerList() const noexcept
{
return tracker_list_;
}
[[nodiscard]] constexpr auto const& hash() const noexcept
{
return hash_;
}
[[nodiscard]] constexpr auto hasError() const noexcept
{
return error_ != TR_STAT_OK;
}
[[nodiscard]] constexpr auto leftUntilDone() const noexcept
{
return left_until_done_;
}
[[nodiscard]] constexpr auto isDone() const noexcept
{
return leftUntilDone() == 0;
}
[[nodiscard]] constexpr auto haveVerified() const noexcept
{
return have_verified_;
}
[[nodiscard]] constexpr auto totalSize() const noexcept
{
return total_size_;
}
[[nodiscard]] constexpr auto isSeed() const noexcept
{
return haveVerified() >= totalSize();
}
[[nodiscard]] constexpr auto isPrivate() const noexcept
{
return is_private_;
}
std::optional<double> getSeedRatioLimit() const;
[[nodiscard]] constexpr auto haveUnverified() const noexcept
{
return have_unchecked_;
}
[[nodiscard]] constexpr auto desiredAvailable() const noexcept
{
return desired_available_;
}
[[nodiscard]] constexpr auto haveTotal() const noexcept
{
return haveVerified() + haveUnverified();
}
[[nodiscard]] constexpr auto sizeWhenDone() const noexcept
{
return size_when_done_;
}
[[nodiscard]] constexpr auto pieceSize() const noexcept
{
return piece_size_;
}
[[nodiscard]] constexpr auto metadataPercentDone() const noexcept
{
return metadata_percent_complete_;
}
[[nodiscard]] constexpr auto hasMetadata() const noexcept
{
return metadataPercentDone() >= 1.0;
}
[[nodiscard]] constexpr auto pieceCount() const noexcept
{
return piece_count_;
}
[[nodiscard]] constexpr auto downloadedEver() const noexcept
{
return downloaded_ever_;
}
[[nodiscard]] constexpr auto uploadedEver() const noexcept
{
return uploaded_ever_;
}
[[nodiscard]] constexpr auto ratio() const noexcept
{
auto const numerator = static_cast<double>(uploadedEver());
auto const denominator = sizeWhenDone();
return denominator > 0U ? numerator / denominator : double{};
}
[[nodiscard]] constexpr double percentComplete() const noexcept
{
return totalSize() != 0 ? haveTotal() / static_cast<double>(totalSize()) : 0;
}
[[nodiscard]] constexpr double percentDone() const noexcept
{
auto const l = leftUntilDone();
auto const s = sizeWhenDone();
return s ? static_cast<double>(s - l) / static_cast<double>(s) : 0.0;
}
[[nodiscard]] constexpr auto failedEver() const noexcept
{
return failed_ever_;
}
int compareSeedProgress(Torrent const&) const;
int compareRatio(Torrent const&) const;
int compareETA(Torrent const&) const;
[[nodiscard]] constexpr auto getETA() const noexcept
{
return eta_;
}
[[nodiscard]] constexpr auto hasETA() const noexcept
{
return getETA() >= 0;
}
[[nodiscard]] constexpr auto lastActivity() const noexcept
{
return activity_date_;
}
[[nodiscard]] constexpr auto lastStarted() const noexcept
{
return start_date_;
}
[[nodiscard]] constexpr auto dateAdded() const noexcept
{
return added_date_;
}
[[nodiscard]] constexpr auto dateCreated() const noexcept
{
return date_created_;
}
[[nodiscard]] constexpr auto dateEdited() const noexcept
{
return edit_date_;
}
[[nodiscard]] constexpr auto manualAnnounceTime() const noexcept
{
return manual_announce_time_;
}
[[nodiscard]] constexpr auto peersWeAreDownloadingFrom() const noexcept
{
return peers_sending_to_us_;
}
[[nodiscard]] constexpr auto webseedsWeAreDownloadingFrom() const noexcept
{
return webseeds_sending_to_us_;
}
[[nodiscard]] constexpr auto peersWeAreUploadingTo() const noexcept
{
return peers_getting_from_us_;
}
[[nodiscard]] constexpr auto isUploading() const noexcept
{
return peersWeAreUploadingTo() > 0;
}
[[nodiscard]] constexpr auto connectedPeers() const noexcept
{
return peers_connected_;
}
[[nodiscard]] constexpr auto connectedPeersAndWebseeds() const noexcept
{
return connectedPeers() + webseedsWeAreDownloadingFrom();
}
[[nodiscard]] constexpr auto const& downloadSpeed() const noexcept
{
return download_speed_;
}
[[nodiscard]] constexpr auto const& uploadSpeed() const noexcept
{
return upload_speed_;
}
[[nodiscard]] constexpr auto getVerifyProgress() const noexcept
{
return recheck_progress_;
}
bool includesTracker(QString const& sitename) const;
[[nodiscard]] constexpr auto const& sitenames() const noexcept
{
return sitenames_;
}
[[nodiscard]] Speed uploadLimit() const
{
return Speed::fromKBps(upload_limit_);
}
[[nodiscard]] Speed downloadLimit() const
{
return Speed::fromKBps(download_limit_);
}
[[nodiscard]] constexpr auto uploadIsLimited() const noexcept
{
return upload_limited_;
}
[[nodiscard]] constexpr auto downloadIsLimited() const noexcept
{
return download_limited_;
}
[[nodiscard]] constexpr auto honorsSessionLimits() const noexcept
{
return honors_session_limits_;
}
[[nodiscard]] constexpr auto peerLimit() const noexcept
{
return peer_limit_;
}
[[nodiscard]] constexpr auto seedRatioLimit() const noexcept
{
return seed_ratio_limit_;
}
[[nodiscard]] constexpr auto seedRatioMode() const noexcept
{
return static_cast<tr_ratiolimit>(seed_ratio_mode_);
}
[[nodiscard]] constexpr auto seedIdleLimit() const noexcept
{
return seed_idle_limit_;
}
[[nodiscard]] constexpr auto seedIdleMode() const noexcept
{
return static_cast<tr_idlelimit>(seed_idle_mode_);
}
[[nodiscard]] constexpr auto const& trackerStats() const noexcept
{
return tracker_stats_;
}
[[nodiscard]] constexpr auto const& peers() const noexcept
{
return peers_;
}
[[nodiscard]] constexpr auto const& files() const noexcept
{
return files_;
}
[[nodiscard]] auto constexpr queuePosition() const noexcept
{
return queue_position_;
}
[[nodiscard]] auto constexpr isStalled() const noexcept
{
return is_stalled_;
}
QString activityString() const;
[[nodiscard]] auto constexpr getActivity() const noexcept
{
return static_cast<tr_torrent_activity>(status_);
}
[[nodiscard]] auto constexpr isFinished() const noexcept
{
return is_finished_;
}
[[nodiscard]] auto constexpr isPaused() const noexcept
{
return getActivity() == TR_STATUS_STOPPED;
}
[[nodiscard]] auto constexpr isWaitingToVerify() const noexcept
{
return getActivity() == TR_STATUS_CHECK_WAIT;
}
[[nodiscard]] auto constexpr isVerifying() const noexcept
{
return getActivity() == TR_STATUS_CHECK;
}
[[nodiscard]] auto constexpr isDownloading() const noexcept
{
return getActivity() == TR_STATUS_DOWNLOAD;
}
[[nodiscard]] auto constexpr isWaitingToDownload() const noexcept
{
return getActivity() == TR_STATUS_DOWNLOAD_WAIT;
}
[[nodiscard]] auto constexpr isSeeding() const noexcept
{
return getActivity() == TR_STATUS_SEED;
}
[[nodiscard]] auto constexpr isWaitingToSeed() const noexcept
{
return getActivity() == TR_STATUS_SEED_WAIT;
}
[[nodiscard]] auto constexpr isReadyToTransfer() const noexcept
{
return getActivity() == TR_STATUS_DOWNLOAD || getActivity() == TR_STATUS_SEED;
}
[[nodiscard]] auto constexpr isQueued() const noexcept
{
return isWaitingToDownload() || isWaitingToSeed();
}
[[nodiscard]] auto constexpr canManualAnnounceAt(time_t t) const noexcept
{
return isReadyToTransfer() && (manualAnnounceTime() <= t);
}
QIcon getMimeTypeIcon() const;
enum Field
{
ACTIVITY_DATE,
ADDED_DATE,
BANDWIDTH_PRIORITY,
COMMENT,
CREATOR,
DATE_CREATED,
DESIRED_AVAILABLE,
DOWNLOADED_EVER,
DOWNLOAD_DIR,
DOWNLOAD_LIMIT,
DOWNLOAD_LIMITED,
DOWNLOAD_SPEED,
EDIT_DATE,
ERROR,
ERROR_STRING,
ETA,
FAILED_EVER,
FILE_COUNT,
FILES,
HASH,
HAVE_UNCHECKED,
HAVE_VERIFIED,
HONORS_SESSION_LIMITS,
ICON,
IS_FINISHED,
IS_PRIVATE,
IS_STALLED,
LEFT_UNTIL_DONE,
MANUAL_ANNOUNCE_TIME,
METADATA_PERCENT_COMPLETE,
NAME,
PEERS,
PEERS_CONNECTED,
PEERS_GETTING_FROM_US,
PEERS_SENDING_TO_US,
PEER_LIMIT,
PERCENT_DONE,
PIECE_COUNT,
PIECE_SIZE,
PRIMARY_MIME_TYPE,
QUEUE_POSITION,
RECHECK_PROGRESS,
SEED_IDLE_LIMIT,
SEED_IDLE_MODE,
SEED_RATIO_LIMIT,
SEED_RATIO_MODE,
SIZE_WHEN_DONE,
START_DATE,
STATUS,
TOTAL_SIZE,
TRACKER_STATS,
TRACKER_LIST,
UPLOADED_EVER,
UPLOAD_LIMIT,
UPLOAD_LIMITED,
UPLOAD_SPEED,
WEBSEEDS_SENDING_TO_US,
N_FIELDS
};
using fields_t = std::bitset<N_FIELDS>;
fields_t update(tr_quark const* keys, tr_variant const* const* values, size_t n);
private:
tr_torrent_id_t const id_;
bool download_limited_ = {};
bool honors_session_limits_ = {};
bool is_finished_ = {};
bool is_private_ = {};
bool is_stalled_ = {};
bool upload_limited_ = {};
time_t activity_date_ = {};
time_t added_date_ = {};
time_t date_created_ = {};
time_t edit_date_ = {};
time_t manual_announce_time_ = {};
time_t start_date_ = {};
int bandwidth_priority_ = {};
int download_limit_ = {};
int error_ = {};
int eta_ = {};
int peer_limit_ = {};
int peers_connected_ = {};
int peers_getting_from_us_ = {};
int peers_sending_to_us_ = {};
int piece_count_ = {};
int queue_position_ = {};
int seed_idle_limit_ = {};
int seed_idle_mode_ = {};
int seed_ratio_mode_ = {};
int status_ = {};
int upload_limit_ = {};
int webseeds_sending_to_us_ = {};
uint64_t desired_available_ = {};
uint64_t downloaded_ever_ = {};
uint64_t failed_ever_ = {};
uint64_t file_count_ = {};
uint64_t have_unchecked_ = {};
uint64_t have_verified_ = {};
uint64_t left_until_done_ = {};
uint64_t piece_size_ = {};
uint64_t size_when_done_ = {};
uint64_t total_size_ = {};
uint64_t uploaded_ever_ = {};
double metadata_percent_complete_ = {};
double percent_done_ = {};
double recheck_progress_ = {};
double seed_ratio_limit_ = {};
QString comment_;
QString creator_;
QString download_dir_;
QString error_string_;
QString name_;
QString primary_mime_type_;
QString tracker_list_;
// mutable because it's a lazy lookup
mutable QIcon icon_ = IconCache::get().fileIcon();
PeerList peers_;
FileList files_;
std::vector<QString> sitenames_;
TrackerStatsList tracker_stats_;
Speed upload_speed_;
Speed download_speed_;
Prefs const& prefs_;
TorrentHash hash_;
};
Q_DECLARE_METATYPE(Torrent const*)