mirror of
https://github.com/transmission/transmission
synced 2024-12-27 18:18:10 +00:00
689 lines
13 KiB
C++
689 lines
13 KiB
C++
// 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 <bitset>
|
|
#include <ctime> // time_t
|
|
#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;
|
|
FaviconCache::Key favicon_key;
|
|
QString announce;
|
|
QString last_announce_result;
|
|
QString last_scrape_result;
|
|
};
|
|
|
|
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()
|
|
{
|
|
}
|
|
|
|
explicit TorrentHash(tr_sha1_digest_t const& data)
|
|
: data_{ data }
|
|
{
|
|
}
|
|
|
|
explicit TorrentHash(char const* str)
|
|
{
|
|
data_ = tr_sha1_from_string(str != nullptr ? str : "");
|
|
}
|
|
|
|
explicit TorrentHash(QString const& str)
|
|
{
|
|
data_ = tr_sha1_from_string(str.toStdString());
|
|
}
|
|
|
|
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_;
|
|
}
|
|
|
|
bool getSeedRatio(double& setmeRatio) 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<double>(totalSize()) : 0;
|
|
}
|
|
|
|
double percentDone() const
|
|
{
|
|
auto const l = leftUntilDone();
|
|
auto const s = sizeWhenDone();
|
|
return s ? static_cast<double>(s - l) / static_cast<double>(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(FaviconCache::Key const& key) const;
|
|
|
|
FaviconCache::Keys const& trackerKeys() const
|
|
{
|
|
return tracker_keys_;
|
|
}
|
|
|
|
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<tr_ratiolimit>(seed_ratio_mode_);
|
|
}
|
|
|
|
int seedIdleLimit() const
|
|
{
|
|
return seed_idle_limit_;
|
|
}
|
|
|
|
tr_idlelimit seedIdleMode() const
|
|
{
|
|
return static_cast<tr_idlelimit>(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<tr_torrent_activity>(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<N_FIELDS>;
|
|
|
|
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_;
|
|
|
|
FaviconCache::Keys tracker_keys_;
|
|
TrackerStatsList tracker_stats_;
|
|
|
|
Speed upload_speed_;
|
|
Speed download_speed_;
|
|
|
|
Prefs const& prefs_;
|
|
|
|
TorrentHash hash_;
|
|
};
|
|
|
|
Q_DECLARE_METATYPE(Torrent const*)
|