// This file Copyright © 2009-2022 Mnemosyne LLC. // It may be used under GPLv2 (SPDX: GPL-2.0), GPLv3 (SPDX: GPL-3.0), // or any future license endorsed by Mnemosyne LLC. // License text can be found in the licenses/ folder. #pragma once #include #include // time_t #include #include #include #include #include #include #include #include #include #include #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; 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; 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; class TorrentHash { private: tr_sha1_digest_t data_ = {}; public: TorrentHash() { } 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; } } bool operator==(TorrentHash const& that) const { return data_ == that.data_; } bool operator!=(TorrentHash const& that) const { return data_ != that.data_; } bool 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); int getBandwidthPriority() const { return bandwidth_priority_; } int id() const { return id_; } QString const& name() const { return name_; } bool hasName() const { return !name_.isEmpty(); } QString const& creator() const { return creator_; } QString const& comment() const { return comment_; } QString const& getPath() const { return download_dir_; } QString getError() const; TorrentHash const& hash() const { return hash_; } bool hasError() const { return error_ != TR_STAT_OK; } bool isDone() const { return leftUntilDone() == 0; } bool isSeed() const { return haveVerified() >= totalSize(); } bool isPrivate() const { return is_private_; } std::optional getSeedRatioLimit() const; uint64_t haveVerified() const { return have_verified_; } uint64_t haveUnverified() const { return have_unchecked_; } uint64_t desiredAvailable() const { return desired_available_; } uint64_t haveTotal() const { return haveVerified() + haveUnverified(); } uint64_t totalSize() const { return total_size_; } uint64_t sizeWhenDone() const { return size_when_done_; } uint64_t leftUntilDone() const { return left_until_done_; } uint64_t pieceSize() const { return piece_size_; } bool hasMetadata() const { return metadataPercentDone() >= 1.0; } int pieceCount() const { return piece_count_; } double ratio() const { auto const u = uploadedEver(); auto const d = downloadedEver(); auto const t = totalSize(); return double(u) / (d ? d : t); } double percentComplete() const { return totalSize() != 0 ? haveTotal() / static_cast(totalSize()) : 0; } double percentDone() const { auto const l = leftUntilDone(); auto const s = sizeWhenDone(); return s ? static_cast(s - l) / static_cast(s) : 0.0; } double metadataPercentDone() const { return metadata_percent_complete_; } uint64_t downloadedEver() const { return downloaded_ever_; } uint64_t uploadedEver() const { return uploaded_ever_; } uint64_t failedEver() const { return failed_ever_; } int compareSeedRatio(Torrent const&) const; int compareRatio(Torrent const&) const; int compareETA(Torrent const&) const; bool hasETA() const { return getETA() >= 0; } int getETA() const { return eta_; } time_t lastActivity() const { return activity_date_; } time_t lastStarted() const { return start_date_; } time_t dateAdded() const { return added_date_; } time_t dateCreated() const { return date_created_; } time_t dateEdited() const { return edit_date_; } time_t manualAnnounceTime() const { return manual_announce_time_; } bool canManualAnnounceAt(time_t t) const { return isReadyToTransfer() && (manualAnnounceTime() <= t); } int peersWeAreDownloadingFrom() const { return peers_sending_to_us_; } int webseedsWeAreDownloadingFrom() const { return webseeds_sending_to_us_; } int peersWeAreUploadingTo() const { return peers_getting_from_us_; } bool isUploading() const { return peersWeAreUploadingTo() > 0; } int connectedPeers() const { return peers_connected_; } int connectedPeersAndWebseeds() const { return connectedPeers() + webseedsWeAreDownloadingFrom(); } Speed const& downloadSpeed() const { return download_speed_; } Speed const& uploadSpeed() const { return upload_speed_; } double getVerifyProgress() const { return recheck_progress_; } bool includesTracker(QString const& sitename) const; std::vector const& sitenames() const { return sitenames_; } Speed uploadLimit() const { return Speed::fromKBps(upload_limit_); } Speed downloadLimit() const { return Speed::fromKBps(download_limit_); } bool uploadIsLimited() const { return upload_limited_; } bool downloadIsLimited() const { return download_limited_; } bool honorsSessionLimits() const { return honors_session_limits_; } int peerLimit() const { return peer_limit_; } double seedRatioLimit() const { return seed_ratio_limit_; } tr_ratiolimit seedRatioMode() const { return static_cast(seed_ratio_mode_); } int seedIdleLimit() const { return seed_idle_limit_; } tr_idlelimit seedIdleMode() const { return static_cast(seed_idle_mode_); } TrackerStatsList const& trackerStats() const { return tracker_stats_; } PeerList const& peers() const { return peers_; } FileList const& files() const { return files_; } int queuePosition() const { return queue_position_; } bool isStalled() const { return is_stalled_; } QString activityString() const; tr_torrent_activity getActivity() const { return static_cast(status_); } bool isFinished() const { return is_finished_; } bool isPaused() const { return getActivity() == TR_STATUS_STOPPED; } bool isWaitingToVerify() const { return getActivity() == TR_STATUS_CHECK_WAIT; } bool isVerifying() const { return getActivity() == TR_STATUS_CHECK; } bool isDownloading() const { return getActivity() == TR_STATUS_DOWNLOAD; } bool isWaitingToDownload() const { return getActivity() == TR_STATUS_DOWNLOAD_WAIT; } bool isSeeding() const { return getActivity() == TR_STATUS_SEED; } bool isWaitingToSeed() const { return getActivity() == TR_STATUS_SEED_WAIT; } bool isReadyToTransfer() const { return getActivity() == TR_STATUS_DOWNLOAD || getActivity() == TR_STATUS_SEED; } bool isQueued() const { return isWaitingToDownload() || isWaitingToSeed(); } 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, UPLOADED_EVER, UPLOAD_LIMIT, UPLOAD_LIMITED, UPLOAD_SPEED, WEBSEEDS_SENDING_TO_US, N_FIELDS }; using fields_t = std::bitset; fields_t update(tr_quark const* keys, tr_variant const* const* values, size_t n); private: int 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 primary_mime_type_; QString comment_; QString creator_; QString download_dir_; QString error_string_; QString name_; // mutable because it's a lazy lookup mutable QIcon icon_ = IconCache::get().fileIcon(); PeerList peers_; FileList files_; std::vector sitenames_; TrackerStatsList tracker_stats_; Speed upload_speed_; Speed download_speed_; Prefs const& prefs_; TorrentHash hash_; }; Q_DECLARE_METATYPE(Torrent const*)