mirror of
https://github.com/transmission/transmission.git
synced 2026-02-14 23:19:34 +00:00
feat: torrent_get.wanted is now boolean (#7997)
This commit is contained in:
@@ -336,7 +336,7 @@ Files are returned in the order they are laid out in the torrent. References to
|
||||
| Key | Value Type | transmission.h source
|
||||
|:--|:--|:--
|
||||
| `bytes_completed` | number | tr_file_view
|
||||
| `wanted` | boolean | tr_file_view (**Note:** Not to be confused with `torrent_get.wanted`, which is an array of 0/1 instead of boolean)
|
||||
| `wanted` | boolean | tr_file_view
|
||||
| `priority` | number | tr_file_view
|
||||
|
||||
`peers`: an array of objects, each containing:
|
||||
@@ -437,10 +437,9 @@ Files are returned in the order they are laid out in the torrent. References to
|
||||
| `tier` | number | tr_tracker_view
|
||||
|
||||
|
||||
`wanted`: An array of `tr_torrentFileCount()` 0/1, 1 (true) if the corresponding file is to be downloaded. (Source: `tr_file_view`)
|
||||
`wanted`: An array of `tr_torrentFileCount()` booleans, true if the corresponding file is to be downloaded. (Source: `tr_file_view`)
|
||||
|
||||
**Note:** For backwards compatibility, in `4.x.x`, `wanted` is serialized as an array of `0` or `1` that should be treated as booleans.
|
||||
This will be fixed in `5.0.0` to return an array of booleans.
|
||||
**Note:** For backwards compatibility, in the old bespoke API, `wanted` is serialized as an array of `0` or `1` that should be treated as booleans.
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
@@ -670,9 +670,96 @@ void convert_keys(tr_variant& var, State& state)
|
||||
});
|
||||
}
|
||||
|
||||
namespace convert_jsonrpc_helpers
|
||||
{
|
||||
void convert_files_wanted(tr_variant::Vector& wanted, State const& state)
|
||||
{
|
||||
auto ret = tr_variant::Vector{};
|
||||
ret.reserve(std::size(wanted));
|
||||
for (auto const& var : wanted)
|
||||
{
|
||||
if (state.style == Style::Tr5)
|
||||
{
|
||||
if (auto const val = var.value_if<bool>())
|
||||
{
|
||||
ret.emplace_back(*val);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto const val = var.value_if<int64_t>(); val == 0 || val == 1)
|
||||
{
|
||||
ret.emplace_back(*val);
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wanted = std::move(ret);
|
||||
}
|
||||
|
||||
void convert_files_wanted_response(tr_variant::Map& top, State const& state)
|
||||
{
|
||||
if (auto* const args = top.find_if<tr_variant::Map>(state.style == Style::Tr5 ? TR_KEY_result : TR_KEY_arguments))
|
||||
{
|
||||
if (auto* const torrents = args->find_if<tr_variant::Vector>(TR_KEY_torrents);
|
||||
torrents != nullptr && !std::empty(*torrents))
|
||||
{
|
||||
// TrFormat::Table
|
||||
if (auto* const first_vec = torrents->front().get_if<tr_variant::Vector>();
|
||||
first_vec != nullptr && !std::empty(*first_vec))
|
||||
{
|
||||
if (auto const wanted_iter = std::find_if(
|
||||
std::begin(*first_vec),
|
||||
std::end(*first_vec),
|
||||
[](tr_variant const& v)
|
||||
{ return v.value_if<std::string_view>() == tr_quark_get_string_view(TR_KEY_wanted); });
|
||||
wanted_iter != std::end(*first_vec))
|
||||
{
|
||||
auto const wanted_idx = static_cast<size_t>(wanted_iter - std::begin(*first_vec));
|
||||
for (auto it = std::next(std::begin(*torrents)); it != std::end(*torrents); ++it)
|
||||
{
|
||||
if (auto* const row = it->get_if<tr_variant::Vector>(); row != nullptr && wanted_idx < std::size(*row))
|
||||
{
|
||||
if (auto* const wanted = (*row)[wanted_idx].get_if<tr_variant::Vector>())
|
||||
{
|
||||
convert_files_wanted(*wanted, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// TrFormat::Object
|
||||
else if (torrents->front().index() == tr_variant::MapIndex)
|
||||
{
|
||||
for (auto& var : *torrents)
|
||||
{
|
||||
if (auto* const map = var.get_if<tr_variant::Map>())
|
||||
{
|
||||
if (auto* const wanted = map->find_if<tr_variant::Vector>(TR_KEY_wanted))
|
||||
{
|
||||
convert_files_wanted(*wanted, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace convert_jsonrpc_helpers
|
||||
|
||||
// jsonrpc <-> legacy rpc conversion
|
||||
void convert_jsonrpc(tr_variant::Map& top, State const& state)
|
||||
{
|
||||
using namespace convert_jsonrpc_helpers;
|
||||
|
||||
if (!state.is_rpc)
|
||||
{
|
||||
return;
|
||||
@@ -705,6 +792,8 @@ void convert_jsonrpc(tr_variant::Map& top, State const& state)
|
||||
// - add `result: "success"`
|
||||
top.replace_key(TR_KEY_result, TR_KEY_arguments);
|
||||
top.try_emplace(TR_KEY_result, tr_variant::unmanaged_string("success"));
|
||||
|
||||
convert_files_wanted_response(top, state);
|
||||
}
|
||||
|
||||
if (state.is_response && is_legacy && !state.is_success)
|
||||
@@ -751,6 +840,8 @@ void convert_jsonrpc(tr_variant::Map& top, State const& state)
|
||||
{
|
||||
top.erase(TR_KEY_result);
|
||||
top.replace_key(TR_KEY_arguments, TR_KEY_result);
|
||||
|
||||
convert_files_wanted_response(top, state);
|
||||
}
|
||||
|
||||
if (state.is_response && is_jsonrpc && !state.is_success && state.was_legacy)
|
||||
|
||||
@@ -458,7 +458,7 @@ namespace make_torrent_field_helpers
|
||||
vec.reserve(n_files);
|
||||
for (tr_file_index_t idx = 0U; idx != n_files; ++idx)
|
||||
{
|
||||
vec.emplace_back(tr_torrentFile(&tor, idx).wanted ? 1 : 0);
|
||||
vec.emplace_back(tr_torrentFile(&tor, idx).wanted);
|
||||
}
|
||||
return tr_variant{ std::move(vec) };
|
||||
}
|
||||
|
||||
@@ -300,6 +300,128 @@ constexpr std::string_view CurrentTorrentGetJson = R"json({
|
||||
}
|
||||
})json";
|
||||
|
||||
constexpr std::string_view CurrentFilesWantedResponseObjectJson = R"json({
|
||||
"id": 6,
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"torrents": [
|
||||
{
|
||||
"wanted": [
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"wanted": [
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
})json";
|
||||
|
||||
constexpr std::string_view LegacyFilesWantedResponseObjectJson = R"json({
|
||||
"arguments": {
|
||||
"torrents": [
|
||||
{
|
||||
"wanted": [
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"wanted": [
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"result": "success",
|
||||
"tag": 6
|
||||
})json";
|
||||
|
||||
constexpr std::string_view CurrentFilesWantedResponseArrayJson = R"json({
|
||||
"id": 6,
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"torrents": [
|
||||
[
|
||||
"comment",
|
||||
"wanted",
|
||||
"id"
|
||||
],
|
||||
[
|
||||
"id 1",
|
||||
[
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false
|
||||
],
|
||||
1
|
||||
],
|
||||
[
|
||||
"id 2",
|
||||
[
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
],
|
||||
2
|
||||
]
|
||||
]
|
||||
}
|
||||
})json";
|
||||
|
||||
constexpr std::string_view LegacyFilesWantedResponseArrayJson = R"json({
|
||||
"arguments": {
|
||||
"torrents": [
|
||||
[
|
||||
"comment",
|
||||
"wanted",
|
||||
"id"
|
||||
],
|
||||
[
|
||||
"id 1",
|
||||
[
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
0
|
||||
],
|
||||
1
|
||||
],
|
||||
[
|
||||
"id 2",
|
||||
[
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
1
|
||||
],
|
||||
2
|
||||
]
|
||||
]
|
||||
},
|
||||
"result": "success",
|
||||
"tag": 6
|
||||
})json";
|
||||
|
||||
constexpr std::string_view CurrentPortTestErrorResponse = R"json({
|
||||
"error": {
|
||||
"code": 8,
|
||||
@@ -963,7 +1085,7 @@ TEST_F(ApiCompatTest, canConvertRpc)
|
||||
using TestCase = std::tuple<std::string_view, std::string_view, Style, std::string_view>;
|
||||
|
||||
// clang-format off
|
||||
static auto constexpr TestCases = std::array<TestCase, 42U>{ {
|
||||
static auto constexpr TestCases = std::array<TestCase, 50U>{ {
|
||||
{ "free_space tr5 -> tr5", BadFreeSpaceRequest, Style::Tr5, BadFreeSpaceRequest },
|
||||
{ "free_space tr5 -> tr4", BadFreeSpaceRequest, Style::Tr4, BadFreeSpaceRequestLegacy },
|
||||
{ "free_space tr4 -> tr5", BadFreeSpaceRequestLegacy, Style::Tr5, BadFreeSpaceRequest },
|
||||
@@ -1006,6 +1128,14 @@ TEST_F(ApiCompatTest, canConvertRpc)
|
||||
{ "unrecognised info tr4 -> tr4", UnrecognisedInfoLegacyResponse, Style::Tr4, UnrecognisedInfoLegacyResponse},
|
||||
{ "non-int tag tr4 -> tr5", LegacyNonIntTagRequest, Style::Tr5, LegacyNonIntTagRequestResult },
|
||||
{ "non-int tag tr4 -> tr4", LegacyNonIntTagRequest, Style::Tr4, LegacyNonIntTagRequest },
|
||||
{ "files wanted response object tr5 -> tr5", CurrentFilesWantedResponseObjectJson, Style::Tr5, CurrentFilesWantedResponseObjectJson },
|
||||
{ "files wanted response object tr5 -> tr4", CurrentFilesWantedResponseObjectJson, Style::Tr4, LegacyFilesWantedResponseObjectJson },
|
||||
{ "files wanted response object tr4 -> tr5", LegacyFilesWantedResponseObjectJson, Style::Tr5, CurrentFilesWantedResponseObjectJson },
|
||||
{ "files wanted response object tr5 -> tr4", LegacyFilesWantedResponseObjectJson, Style::Tr4, LegacyFilesWantedResponseObjectJson },
|
||||
{ "files wanted response array tr5 -> tr5", CurrentFilesWantedResponseArrayJson, Style::Tr5, CurrentFilesWantedResponseArrayJson },
|
||||
{ "files wanted response array tr5 -> tr4", CurrentFilesWantedResponseArrayJson, Style::Tr4, LegacyFilesWantedResponseArrayJson },
|
||||
{ "files wanted response array tr4 -> tr5", LegacyFilesWantedResponseArrayJson, Style::Tr5, CurrentFilesWantedResponseArrayJson },
|
||||
{ "files wanted response array tr5 -> tr4", LegacyFilesWantedResponseArrayJson, Style::Tr4, LegacyFilesWantedResponseArrayJson },
|
||||
|
||||
// TODO(ckerr): torrent-get with 'table'
|
||||
} };
|
||||
|
||||
Reference in New Issue
Block a user