feat: add "table" format in torrent-get RPC (#1049)

* feat: add optional "format" arg to torrent-get RPC

If the "format" request was "objects" (default), "torrents" will be an
array of objects, each of which contains the key/value pairs matching
the request's "fields" arg. This is unchanged from previous versions.

If the format was "table", then "torrents" will be an array of arrays.
The first row holds the keys and each remaining row holds a torrent's
values for those keys. This format is more efficient in terms of JSON
generation and JSON parsing.
This commit is contained in:
Charles Kerr 2019-11-09 17:02:23 -06:00 committed by GitHub
parent d857a5821a
commit 96f76999aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 291 additions and 162 deletions

View File

@ -148,11 +148,25 @@
(1) An optional "ids" array as described in 3.1.
(2) A required "fields" array of keys. (see list below)
(3) An optional "format" string specifying how to format the
"torrents" response field. Allowed values are "objects" (default)
and "table". (see "Response arguments" below)
Response arguments:
(1) A "torrents" array of objects, each of which contains
the key/value pairs matching the request's "fields" argument.
(1) A "torrents" array.
If the "format" request was "objects" (default), "torrents" will
be an array of objects, each of which contains the key/value
pairs matching the request's "fields" arg. This was the only
format before Transmission 3 and has some obvious programmer
conveniences, such as parsing directly into Javascript objects.
If the format was "table", then "torrents" will be an array of
arrays. The first row holds the keys and each remaining row holds
a torrent's values for those keys. This format is more efficient
in terms of JSON generation and JSON parsing.
(2) If the request's "ids" field was "recently-active",
a "removed" array of torrent-id numbers of recently-removed
torrents.
@ -792,6 +806,7 @@
| | yes | session-get | new arg "session-id"
| | yes | torrent-get | new arg "labels"
| | yes | torrent-set | new arg "labels"
| | yes | torrent-get | new arg "format"
5.1. Upcoming Breakage

View File

@ -125,6 +125,7 @@ static struct tr_key_struct const my_static[] =
Q("filter-trackers"),
Q("flagStr"),
Q("flags"),
Q("format"),
Q("fromCache"),
Q("fromDht"),
Q("fromIncoming"),

View File

@ -125,6 +125,7 @@ enum
TR_KEY_filter_trackers,
TR_KEY_flagStr,
TR_KEY_flags,
TR_KEY_format,
TR_KEY_fromCache,
TR_KEY_fromDht,
TR_KEY_fromIncoming,

View File

@ -29,6 +29,7 @@
#include "stats.h"
#include "torrent.h"
#include "tr-assert.h"
#include "tr-macros.h"
#include "utils.h"
#include "variant.h"
#include "version.h"
@ -45,6 +46,13 @@
#define dbgmsg(...) tr_logAddDeepNamed("RPC", __VA_ARGS__)
#endif
typedef enum
{
TR_FORMAT_OBJECT = 0,
TR_FORMAT_TABLE
}
tr_format;
/***
****
***/
@ -532,183 +540,185 @@ static void addPeers(tr_torrent* tor, tr_variant* list)
tr_torrentPeersFree(peers, peerCount);
}
static void addField(tr_torrent* const tor, tr_info const* const inf, tr_stat const* const st, tr_variant* const d,
tr_quark const key)
static void initField(tr_torrent* const tor, tr_info const* const inf, tr_stat const* const st, tr_variant* const initme,
tr_quark key)
{
char* str;
switch (key)
{
case TR_KEY_activityDate:
tr_variantDictAddInt(d, key, st->activityDate);
tr_variantInitInt(initme, st->activityDate);
break;
case TR_KEY_addedDate:
tr_variantDictAddInt(d, key, st->addedDate);
tr_variantInitInt(initme, st->addedDate);
break;
case TR_KEY_bandwidthPriority:
tr_variantDictAddInt(d, key, tr_torrentGetPriority(tor));
tr_variantInitInt(initme, tr_torrentGetPriority(tor));
break;
case TR_KEY_comment:
tr_variantDictAddStr(d, key, inf->comment != NULL ? inf->comment : "");
tr_variantInitStr(initme, inf->comment != NULL ? inf->comment : "", TR_BAD_SIZE);
break;
case TR_KEY_corruptEver:
tr_variantDictAddInt(d, key, st->corruptEver);
tr_variantInitInt(initme, st->corruptEver);
break;
case TR_KEY_creator:
tr_variantDictAddStr(d, key, inf->creator != NULL ? inf->creator : "");
tr_variantInitStr(initme, inf->creator != NULL ? inf->creator : "", TR_BAD_SIZE);
break;
case TR_KEY_dateCreated:
tr_variantDictAddInt(d, key, inf->dateCreated);
tr_variantInitInt(initme, inf->dateCreated);
break;
case TR_KEY_desiredAvailable:
tr_variantDictAddInt(d, key, st->desiredAvailable);
tr_variantInitInt(initme, st->desiredAvailable);
break;
case TR_KEY_doneDate:
tr_variantDictAddInt(d, key, st->doneDate);
tr_variantInitInt(initme, st->doneDate);
break;
case TR_KEY_downloadDir:
tr_variantDictAddStr(d, key, tr_torrentGetDownloadDir(tor));
tr_variantInitStr(initme, tr_torrentGetDownloadDir(tor), TR_BAD_SIZE);
break;
case TR_KEY_downloadedEver:
tr_variantDictAddInt(d, key, st->downloadedEver);
tr_variantInitInt(initme, st->downloadedEver);
break;
case TR_KEY_downloadLimit:
tr_variantDictAddInt(d, key, tr_torrentGetSpeedLimit_KBps(tor, TR_DOWN));
tr_variantInitInt(initme, tr_torrentGetSpeedLimit_KBps(tor, TR_DOWN));
break;
case TR_KEY_downloadLimited:
tr_variantDictAddBool(d, key, tr_torrentUsesSpeedLimit(tor, TR_DOWN));
tr_variantInitBool(initme, tr_torrentUsesSpeedLimit(tor, TR_DOWN));
break;
case TR_KEY_error:
tr_variantDictAddInt(d, key, st->error);
tr_variantInitInt(initme, st->error);
break;
case TR_KEY_errorString:
tr_variantDictAddStr(d, key, st->errorString);
tr_variantInitStr(initme, st->errorString, TR_BAD_SIZE);
break;
case TR_KEY_eta:
tr_variantDictAddInt(d, key, st->eta);
tr_variantInitInt(initme, st->eta);
break;
case TR_KEY_files:
addFiles(tor, tr_variantDictAddList(d, key, inf->fileCount));
tr_variantInitList(initme, inf->fileCount);
addFiles(tor, initme);
break;
case TR_KEY_fileStats:
addFileStats(tor, tr_variantDictAddList(d, key, inf->fileCount));
tr_variantInitList(initme, inf->fileCount);
addFileStats(tor, initme);
break;
case TR_KEY_hashString:
tr_variantDictAddStr(d, key, tor->info.hashString);
tr_variantInitStr(initme, tor->info.hashString, TR_BAD_SIZE);
break;
case TR_KEY_haveUnchecked:
tr_variantDictAddInt(d, key, st->haveUnchecked);
tr_variantInitInt(initme, st->haveUnchecked);
break;
case TR_KEY_haveValid:
tr_variantDictAddInt(d, key, st->haveValid);
tr_variantInitInt(initme, st->haveValid);
break;
case TR_KEY_honorsSessionLimits:
tr_variantDictAddBool(d, key, tr_torrentUsesSessionLimits(tor));
tr_variantInitBool(initme, tr_torrentUsesSessionLimits(tor));
break;
case TR_KEY_id:
tr_variantDictAddInt(d, key, st->id);
tr_variantInitInt(initme, st->id);
break;
case TR_KEY_isFinished:
tr_variantDictAddBool(d, key, st->finished);
tr_variantInitBool(initme, st->finished);
break;
case TR_KEY_isPrivate:
tr_variantDictAddBool(d, key, tr_torrentIsPrivate(tor));
tr_variantInitBool(initme, tr_torrentIsPrivate(tor));
break;
case TR_KEY_isStalled:
tr_variantDictAddBool(d, key, st->isStalled);
tr_variantInitBool(initme, st->isStalled);
break;
case TR_KEY_labels:
addLabels(tor, tr_variantDictAdd(d, key));
addLabels(tor, initme);
break;
case TR_KEY_leftUntilDone:
tr_variantDictAddInt(d, key, st->leftUntilDone);
tr_variantInitInt(initme, st->leftUntilDone);
break;
case TR_KEY_manualAnnounceTime:
tr_variantDictAddInt(d, key, st->manualAnnounceTime);
tr_variantInitInt(initme, st->manualAnnounceTime);
break;
case TR_KEY_maxConnectedPeers:
tr_variantDictAddInt(d, key, tr_torrentGetPeerLimit(tor));
tr_variantInitInt(initme, tr_torrentGetPeerLimit(tor));
break;
case TR_KEY_magnetLink:
str = tr_torrentGetMagnetLink(tor);
tr_variantDictAddStr(d, key, str);
tr_variantInitStr(initme, str, TR_BAD_SIZE);
tr_free(str);
break;
case TR_KEY_metadataPercentComplete:
tr_variantDictAddReal(d, key, st->metadataPercentComplete);
tr_variantInitReal(initme, st->metadataPercentComplete);
break;
case TR_KEY_name:
tr_variantDictAddStr(d, key, tr_torrentName(tor));
tr_variantInitStr(initme, tr_torrentName(tor), TR_BAD_SIZE);
break;
case TR_KEY_percentDone:
tr_variantDictAddReal(d, key, st->percentDone);
tr_variantInitReal(initme, st->percentDone);
break;
case TR_KEY_peer_limit:
tr_variantDictAddInt(d, key, tr_torrentGetPeerLimit(tor));
tr_variantInitInt(initme, tr_torrentGetPeerLimit(tor));
break;
case TR_KEY_peers:
addPeers(tor, tr_variantDictAdd(d, key));
addPeers(tor, initme);
break;
case TR_KEY_peersConnected:
tr_variantDictAddInt(d, key, st->peersConnected);
tr_variantInitInt(initme, st->peersConnected);
break;
case TR_KEY_peersFrom:
{
tr_variant* tmp = tr_variantDictAddDict(d, key, 7);
tr_variantInitDict(initme, 7);
int const* f = st->peersFrom;
tr_variantDictAddInt(tmp, TR_KEY_fromCache, f[TR_PEER_FROM_RESUME]);
tr_variantDictAddInt(tmp, TR_KEY_fromDht, f[TR_PEER_FROM_DHT]);
tr_variantDictAddInt(tmp, TR_KEY_fromIncoming, f[TR_PEER_FROM_INCOMING]);
tr_variantDictAddInt(tmp, TR_KEY_fromLpd, f[TR_PEER_FROM_LPD]);
tr_variantDictAddInt(tmp, TR_KEY_fromLtep, f[TR_PEER_FROM_LTEP]);
tr_variantDictAddInt(tmp, TR_KEY_fromPex, f[TR_PEER_FROM_PEX]);
tr_variantDictAddInt(tmp, TR_KEY_fromTracker, f[TR_PEER_FROM_TRACKER]);
tr_variantDictAddInt(initme, TR_KEY_fromCache, f[TR_PEER_FROM_RESUME]);
tr_variantDictAddInt(initme, TR_KEY_fromDht, f[TR_PEER_FROM_DHT]);
tr_variantDictAddInt(initme, TR_KEY_fromIncoming, f[TR_PEER_FROM_INCOMING]);
tr_variantDictAddInt(initme, TR_KEY_fromLpd, f[TR_PEER_FROM_LPD]);
tr_variantDictAddInt(initme, TR_KEY_fromLtep, f[TR_PEER_FROM_LTEP]);
tr_variantDictAddInt(initme, TR_KEY_fromPex, f[TR_PEER_FROM_PEX]);
tr_variantDictAddInt(initme, TR_KEY_fromTracker, f[TR_PEER_FROM_TRACKER]);
break;
}
case TR_KEY_peersGettingFromUs:
tr_variantDictAddInt(d, key, st->peersGettingFromUs);
tr_variantInitInt(initme, st->peersGettingFromUs);
break;
case TR_KEY_peersSendingToUs:
tr_variantDictAddInt(d, key, st->peersSendingToUs);
tr_variantInitInt(initme, st->peersSendingToUs);
break;
case TR_KEY_pieces:
@ -717,148 +727,146 @@ static void addField(tr_torrent* const tor, tr_info const* const inf, tr_stat co
size_t byte_count = 0;
void* bytes = tr_torrentCreatePieceBitfield(tor, &byte_count);
char* str = tr_base64_encode(bytes, byte_count, NULL);
tr_variantDictAddStr(d, key, str != NULL ? str : "");
tr_variantInitStr(initme, str != NULL ? str : "", TR_BAD_SIZE);
tr_free(str);
tr_free(bytes);
}
else
{
tr_variantDictAddStr(d, key, "");
tr_variantInitStr(initme, "", 0);
}
break;
case TR_KEY_pieceCount:
tr_variantDictAddInt(d, key, inf->pieceCount);
tr_variantInitInt(initme, inf->pieceCount);
break;
case TR_KEY_pieceSize:
tr_variantDictAddInt(d, key, inf->pieceSize);
tr_variantInitInt(initme, inf->pieceSize);
break;
case TR_KEY_priorities:
tr_variantInitList(initme, inf->fileCount);
for (tr_file_index_t i = 0; i < inf->fileCount; ++i)
{
tr_variant* p = tr_variantDictAddList(d, key, inf->fileCount);
for (tr_file_index_t i = 0; i < inf->fileCount; ++i)
{
tr_variantListAddInt(p, inf->files[i].priority);
}
break;
tr_variantListAddInt(initme, inf->files[i].priority);
}
break;
case TR_KEY_queuePosition:
tr_variantDictAddInt(d, key, st->queuePosition);
tr_variantInitInt(initme, st->queuePosition);
break;
case TR_KEY_etaIdle:
tr_variantDictAddInt(d, key, st->etaIdle);
tr_variantInitInt(initme, st->etaIdle);
break;
case TR_KEY_rateDownload:
tr_variantDictAddInt(d, key, toSpeedBytes(st->pieceDownloadSpeed_KBps));
tr_variantInitInt(initme, toSpeedBytes(st->pieceDownloadSpeed_KBps));
break;
case TR_KEY_rateUpload:
tr_variantDictAddInt(d, key, toSpeedBytes(st->pieceUploadSpeed_KBps));
tr_variantInitInt(initme, toSpeedBytes(st->pieceUploadSpeed_KBps));
break;
case TR_KEY_recheckProgress:
tr_variantDictAddReal(d, key, st->recheckProgress);
tr_variantInitReal(initme, st->recheckProgress);
break;
case TR_KEY_seedIdleLimit:
tr_variantDictAddInt(d, key, tr_torrentGetIdleLimit(tor));
tr_variantInitInt(initme, tr_torrentGetIdleLimit(tor));
break;
case TR_KEY_seedIdleMode:
tr_variantDictAddInt(d, key, tr_torrentGetIdleMode(tor));
tr_variantInitInt(initme, tr_torrentGetIdleMode(tor));
break;
case TR_KEY_seedRatioLimit:
tr_variantDictAddReal(d, key, tr_torrentGetRatioLimit(tor));
tr_variantInitReal(initme, tr_torrentGetRatioLimit(tor));
break;
case TR_KEY_seedRatioMode:
tr_variantDictAddInt(d, key, tr_torrentGetRatioMode(tor));
tr_variantInitInt(initme, tr_torrentGetRatioMode(tor));
break;
case TR_KEY_sizeWhenDone:
tr_variantDictAddInt(d, key, st->sizeWhenDone);
tr_variantInitInt(initme, st->sizeWhenDone);
break;
case TR_KEY_startDate:
tr_variantDictAddInt(d, key, st->startDate);
tr_variantInitInt(initme, st->startDate);
break;
case TR_KEY_status:
tr_variantDictAddInt(d, key, st->activity);
tr_variantInitInt(initme, st->activity);
break;
case TR_KEY_secondsDownloading:
tr_variantDictAddInt(d, key, st->secondsDownloading);
tr_variantInitInt(initme, st->secondsDownloading);
break;
case TR_KEY_secondsSeeding:
tr_variantDictAddInt(d, key, st->secondsSeeding);
tr_variantInitInt(initme, st->secondsSeeding);
break;
case TR_KEY_trackers:
addTrackers(inf, tr_variantDictAddList(d, key, inf->trackerCount));
tr_variantInitList(initme, inf->trackerCount);
addTrackers(inf, initme);
break;
case TR_KEY_trackerStats:
{
int n;
tr_tracker_stat* s = tr_torrentTrackers(tor, &n);
addTrackerStats(s, n, tr_variantDictAddList(d, key, n));
tr_variantInitList(initme, n);
addTrackerStats(s, n, initme);
tr_torrentTrackersFree(s, n);
break;
}
case TR_KEY_torrentFile:
tr_variantDictAddStr(d, key, inf->torrent);
tr_variantInitStr(initme, inf->torrent, TR_BAD_SIZE);
break;
case TR_KEY_totalSize:
tr_variantDictAddInt(d, key, inf->totalSize);
tr_variantInitInt(initme, inf->totalSize);
break;
case TR_KEY_uploadedEver:
tr_variantDictAddInt(d, key, st->uploadedEver);
tr_variantInitInt(initme, st->uploadedEver);
break;
case TR_KEY_uploadLimit:
tr_variantDictAddInt(d, key, tr_torrentGetSpeedLimit_KBps(tor, TR_UP));
tr_variantInitInt(initme, tr_torrentGetSpeedLimit_KBps(tor, TR_UP));
break;
case TR_KEY_uploadLimited:
tr_variantDictAddBool(d, key, tr_torrentUsesSpeedLimit(tor, TR_UP));
tr_variantInitBool(initme, tr_torrentUsesSpeedLimit(tor, TR_UP));
break;
case TR_KEY_uploadRatio:
tr_variantDictAddReal(d, key, st->ratio);
tr_variantInitReal(initme, st->ratio);
break;
case TR_KEY_wanted:
tr_variantInitList(initme, inf->fileCount);
for (tr_file_index_t i = 0; i < inf->fileCount; ++i)
{
tr_variant* w = tr_variantDictAddList(d, key, inf->fileCount);
for (tr_file_index_t i = 0; i < inf->fileCount; ++i)
{
tr_variantListAddInt(w, inf->files[i].dnd ? 0 : 1);
}
break;
tr_variantListAddInt(initme, inf->files[i].dnd ? 0 : 1);
}
break;
case TR_KEY_webseeds:
addWebseeds(inf, tr_variantDictAddList(d, key, inf->webseedCount));
tr_variantInitList(initme, inf->webseedCount);
addWebseeds(inf, initme);
break;
case TR_KEY_webseedsSendingToUs:
tr_variantDictAddInt(d, key, st->webseedsSendingToUs);
tr_variantInitInt(initme, st->webseedsSendingToUs);
break;
default:
@ -866,26 +874,29 @@ static void addField(tr_torrent* const tor, tr_info const* const inf, tr_stat co
}
}
static void addInfo(tr_torrent* tor, tr_variant* d, tr_variant* fields)
static void addTorrentInfo(tr_torrent* tor, tr_format format, tr_variant* entry, tr_quark const* fields, size_t fieldCount)
{
int const n = tr_variantListSize(fields);
if (format == TR_FORMAT_TABLE)
{
tr_variantInitList(entry, fieldCount);
}
else
{
tr_variantInitDict(entry, fieldCount);
}
tr_variantInitDict(d, n);
if (n > 0)
if (fieldCount > 0)
{
tr_info const* const inf = tr_torrentInfo(tor);
tr_stat const* const st = tr_torrentStat((tr_torrent*)tor);
for (int i = 0; i < n; ++i)
for (size_t i = 0; i < fieldCount; ++i)
{
size_t len;
char const* str;
tr_variant* child = format == TR_FORMAT_TABLE ?
tr_variantListAdd(entry) :
tr_variantDictAdd(entry, fields[i]);
if (tr_variantGetStr(tr_variantListChild(fields, i), &str, &len))
{
addField(tor, inf, st, d, tr_quark_new(str, len));
}
initField(tor, inf, st, child, fields[i]);
}
}
}
@ -897,10 +908,20 @@ static char const* torrentGet(tr_session* session, tr_variant* args_in, tr_varia
int torrentCount;
tr_torrent** torrents = getTorrents(session, args_in, &torrentCount);
tr_variant* list = tr_variantDictAddList(args_out, TR_KEY_torrents, torrentCount);
tr_variant* list = tr_variantDictAddList(args_out, TR_KEY_torrents, torrentCount + 1);
tr_variant* fields;
char const* strVal;
char const* errmsg = NULL;
tr_format format;
if (tr_variantDictFindStr(args_in, TR_KEY_format, &strVal, NULL) && strcmp(strVal, "table") == 0)
{
format = TR_FORMAT_TABLE;
}
else /* default value */
{
format = TR_FORMAT_OBJECT;
}
if (tr_variantDictFindStr(args_in, TR_KEY_ids, &strVal, NULL) && strcmp(strVal, "recently-active") == 0)
{
@ -932,10 +953,35 @@ static char const* torrentGet(tr_session* session, tr_variant* args_in, tr_varia
}
else
{
/* make an array of property name quarks */
size_t keyCount = 0;
size_t const n = tr_variantListSize(fields);
tr_quark* keys = tr_new(tr_quark, n);
for (size_t i = 0; i < n; ++i)
{
size_t len;
if (tr_variantGetStr(tr_variantListChild(fields, i), &strVal, &len))
{
keys[keyCount++] = tr_quark_new(strVal, len);
}
}
if (format == TR_FORMAT_TABLE)
{
/* first entry is an array of property names */
tr_variant* names = tr_variantListAddList(list, keyCount);
for (size_t i = 0; i < keyCount; ++i)
{
tr_variantListAddQuark(names, keys[i]);
}
}
for (int i = 0; i < torrentCount; ++i)
{
addInfo(torrents[i], tr_variantListAdd(list), fields);
addTorrentInfo(torrents[i], format, tr_variantListAdd(list), keys, keyCount);
}
tr_free(keys);
}
tr_free(torrents);
@ -1704,19 +1750,20 @@ static void addTorrentImpl(struct tr_rpc_idle_data* data, tr_ctor* ctor)
if (tor != NULL && key != 0)
{
tr_variant fields;
tr_variantInitList(&fields, 3);
tr_variantListAddStr(&fields, "id");
tr_variantListAddStr(&fields, "name");
tr_variantListAddStr(&fields, "hashString");
addInfo(tor, tr_variantDictAdd(data->args_out, key), &fields);
tr_quark const fields[] =
{
TR_KEY_id,
TR_KEY_name,
TR_KEY_hashString
};
addTorrentInfo(tor, TR_FORMAT_OBJECT, tr_variantDictAdd(data->args_out, key), fields, TR_N_ELEMENTS(fields));
if (result == NULL)
{
notify(data->session, TR_RPC_TORRENT_ADDED, tor);
}
tr_variantFree(&fields);
result = NULL;
}

View File

@ -50,8 +50,8 @@ private slots:
private:
void maybeUpdateBlocklist();
void loadTranslations();
void quitLater();
QStringList getNames(QSet<int> const& ids) const;
void quitLater();
private:
Prefs* myPrefs;

View File

@ -552,7 +552,8 @@ void Session::torrentRenamePath(QSet<int> const& ids, QString const& oldpath, QS
void Session::refreshTorrents(QSet<int> const& ids, KeyList const& keys)
{
tr_variant args;
tr_variantInitDict(&args, 2);
tr_variantInitDict(&args, 3);
tr_variantDictAddStr(&args, TR_KEY_format, "table");
addList(tr_variantDictAddList(&args, TR_KEY_fields, 0), keys);
addOptionalIds(&args, ids);

View File

@ -541,7 +541,7 @@ void Torrent::updateMimeIcon()
****
***/
bool Torrent::update(tr_variant* d)
bool Torrent::update(tr_quark const* keys, tr_variant** values, size_t n)
{
static bool lookup_initialized = false;
static int key_to_property_index[TR_N_KEYS];
@ -562,12 +562,11 @@ bool Torrent::update(tr_variant* d)
}
}
tr_quark key;
tr_variant* child;
size_t pos = 0;
while (tr_variantDictChild(d, pos++, &key, &child))
for (size_t pos = 0; pos < n; ++pos)
{
tr_quark key = keys[pos];
tr_variant* child = values[pos];
int const property_index = key_to_property_index[key];
if (property_index == -1) // we're not interested in this one
@ -669,10 +668,10 @@ bool Torrent::update(tr_variant* d)
}
}
tr_variant* files;
if (tr_variantDictFindList(d, TR_KEY_files, &files))
auto it = std::find(keys, keys + n, TR_KEY_files);
if (it != keys + n)
{
tr_variant* files = values[std::distance(keys, it)];
char const* str;
int64_t intVal;
int i = 0;
@ -704,8 +703,10 @@ bool Torrent::update(tr_variant* d)
changed = true;
}
if (tr_variantDictFindList(d, TR_KEY_fileStats, &files))
it = std::find(keys, keys + n, TR_KEY_fileStats);
if (it != keys + n)
{
tr_variant* files = values[std::distance(keys, it)];
int const n = tr_variantListSize(files);
for (int i = 0; i < n && i < myFiles.size(); ++i)
@ -734,51 +735,50 @@ bool Torrent::update(tr_variant* d)
changed = true;
}
tr_variant* trackers;
if (tr_variantDictFindList(d, TR_KEY_trackers, &trackers))
it = std::find(keys, keys + n, TR_KEY_trackers);
if (it != keys + n)
{
size_t len;
char const* str;
int i = 0;
QStringList list;
tr_variant* child;
tr_variant* v = values[std::distance(keys, it)];
while ((child = tr_variantListChild(trackers, i++)) != nullptr)
// build the new tracker list
QStringList trackers;
trackers.reserve(tr_variantListSize(v));
tr_variant* child;
int i = 0;
while ((child = tr_variantListChild(v, i++)) != nullptr)
{
char const* str;
size_t len;
if (tr_variantDictFindStr(child, TR_KEY_announce, &str, &len))
{
qApp->faviconCache().add(QUrl(QString::fromUtf8(str)));
list.append(QString::fromUtf8(str, len));
trackers.append(QString::fromUtf8(str, len));
}
}
if (myValues[TRACKERS] != list)
// update the trackers
if (myValues[TRACKERS] != trackers)
{
QStringList hosts;
for (QString const& tracker : list)
for (auto const& tracker : trackers)
{
QString const host = FaviconCache::getHost(QUrl(tracker));
if (!host.isEmpty())
{
hosts.append(host);
}
auto const url = QUrl(tracker);
qApp->faviconCache().add(url);
hosts.append(FaviconCache::getHost(url));
}
hosts.removeDuplicates();
hosts.removeOne(QString());
myValues[TRACKERS].setValue(list);
myValues[TRACKERS].setValue(trackers);
myValues[HOSTS].setValue(hosts);
changed = true;
}
}
tr_variant* trackerStats;
if (tr_variantDictFindList(d, TR_KEY_trackerStats, &trackerStats))
it = std::find(keys, keys + n, TR_KEY_trackerStats);
if (it != keys + n)
{
tr_variant* trackerStats = values[std::distance(keys, it)];
tr_variant* child;
TrackerStatsList trackerStatsList;
int childNum = 0;
@ -924,10 +924,10 @@ bool Torrent::update(tr_variant* d)
changed = true;
}
tr_variant* peers;
if (tr_variantDictFindList(d, TR_KEY_peers, &peers))
it = std::find(keys, keys + n, TR_KEY_peers);
if (it != keys + n)
{
tr_variant* peers = values[std::distance(keys, it)];
tr_variant* child;
PeerList peerList;
int childNum = 0;

View File

@ -580,7 +580,7 @@ public:
return isWaitingToDownload() || isWaitingToSeed();
}
bool update(tr_variant* dict);
bool update(tr_quark const* keys, tr_variant** values, size_t n);
QIcon getMimeTypeIcon() const
{

View File

@ -168,13 +168,77 @@ void TorrentModel::updateTorrents(tr_variant* torrents, bool isCompleteList)
return (date != 0) && (difftime(now, date) < max_age);
};
size_t i = 0;
tr_variant* child;
while ((child = tr_variantListChild(torrents, i++)) != nullptr)
// build a list of the property keys
tr_variant* const firstChild = tr_variantListChild(torrents, 0);
bool const table = tr_variantIsList(firstChild);
std::vector<tr_quark> keys;
if (table)
{
int64_t id;
// In 'table' format, the first entry in 'torrents' is an array of keys.
// All the other entries are an array of the values for one torrent.
char const* str;
size_t len;
size_t i = 0;
while (tr_variantGetStr(tr_variantListChild(firstChild, i++), &str, &len))
{
keys.push_back(tr_quark_new(str, len));
}
}
else
{
// In 'object' format, every entry is an object with the same set of properties
size_t i = 0;
tr_quark key;
tr_variant* value;
while (firstChild && tr_variantDictChild(firstChild, i++, &key, &value))
{
keys.push_back(key);
}
}
if (!tr_variantDictFindInt(child, TR_KEY_id, &id))
// Find the position of TR_KEY_id so we can do torrent lookup
auto const id_it = std::find(std::begin(keys), std::end(keys), TR_KEY_id);
if (id_it == std::end(keys)) // no ids provided; we can't proceed
{
return;
}
auto const id_pos = std::distance(std::begin(keys), id_it);
// Loop through the torrent records...
std::vector<tr_variant*> values;
values.reserve(keys.size());
size_t tor_index = table ? 1 : 0;
tr_variant* v;
while ((v = tr_variantListChild(torrents, tor_index++)))
{
// Build an array of values
values.clear();
if (table)
{
// In table mode, v is already a list of values
size_t i = 0;
tr_variant* val;
while ((val = tr_variantListChild(v, i++)))
{
values.push_back(val);
}
}
else
{
// In object mode, v is an object of torrent property key/vals
size_t i = 0;
tr_quark key;
tr_variant* value;
while (tr_variantDictChild(v, i++, &key, &value))
{
values.push_back(value);
}
}
// Find the torrent id
int64_t id;
if (!tr_variantGetInt(values[id_pos], &id))
{
continue;
}
@ -192,7 +256,7 @@ void TorrentModel::updateTorrents(tr_variant* torrents, bool isCompleteList)
leftUntilDone = tor->leftUntilDone();
}
if (tor->update(child))
if (tor->update(keys.data(), values.data(), keys.size()))
{
changed.insert(id);
}