mirror of
https://github.com/transmission/transmission.git
synced 2026-02-14 23:19:34 +00:00
feat(web): add webseeds list (#8421)
* Add webseeds list to web ui * Better webseed url column title * Apply suggestions * Follow existing table row creation style * Fix webseed table speed-down column * Fix empty class error * Add webseeds_ex to rpc changelogs * Apply suggestions from CI * Fix webseeds_ex rpc spec
This commit is contained in:
@@ -313,6 +313,7 @@ The 'source' column here corresponds to the data structure there.
|
||||
| `upload_ratio`| double| tr_stat
|
||||
| `wanted`| array (see below)| n/a
|
||||
| `webseeds`| array of strings | tr_tracker_view
|
||||
| `webseeds_ex`| array (see below)| n/a
|
||||
| `webseeds_sending_to_us`| number| tr_stat
|
||||
|
||||
`availability`: An array of `piece_count` numbers representing the number of connected peers that have each piece, or -1 if we already have the piece ourselves.
|
||||
@@ -437,6 +438,14 @@ Files are returned in the order they are laid out in the torrent. References to
|
||||
| `tier` | number | tr_tracker_view
|
||||
|
||||
|
||||
`webseeds_ex`: array of objects, each containing:
|
||||
|
||||
| Key | Value Type | transmission.h source
|
||||
|:--|:--|:--
|
||||
| `url` | string | tr_webseed_view
|
||||
| `is_downloading` | boolean | tr_webseed_view
|
||||
| `download_bytes_per_second` | number | tr_webseed_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 the old bespoke API, `wanted` is serialized as an array of `0` or `1` that should be treated as booleans.
|
||||
@@ -1109,3 +1118,4 @@ Transmission 4.2.0 (`rpc_version_semver` 6.1.0, `rpc_version`: ?)
|
||||
|
||||
| Method | Description
|
||||
|:---|:---
|
||||
| `torrent_get` | new arg `webseeds_ex`
|
||||
|
||||
@@ -142,6 +142,7 @@ auto constexpr MyStatic = std::array<std::string_view, TR_N_KEYS>{
|
||||
"downloadLimit"sv, // rpc
|
||||
"downloadLimited"sv, // rpc
|
||||
"downloadSpeed"sv, // rpc
|
||||
"download_bytes_per_second"sv, // rpc
|
||||
"download_count"sv, // rpc
|
||||
"download_dir"sv, // daemon, gtk app, rpc, tr_session::Settings
|
||||
"download_dir_free_space"sv, // rpc
|
||||
@@ -263,6 +264,7 @@ auto constexpr MyStatic = std::array<std::string_view, TR_N_KEYS>{
|
||||
"isUTP"sv, // rpc
|
||||
"isUploadingTo"sv, // rpc
|
||||
"is_backup"sv, // rpc
|
||||
"is_downloading"sv, // rpc
|
||||
"is_downloading_from"sv, // rpc
|
||||
"is_encrypted"sv, // rpc
|
||||
"is_finished"sv, // rpc
|
||||
@@ -708,6 +710,7 @@ auto constexpr MyStatic = std::array<std::string_view, TR_N_KEYS>{
|
||||
"uploadedEver"sv, // rpc
|
||||
"uploaded_bytes"sv, // rpc, stats.json
|
||||
"uploaded_ever"sv, // rpc
|
||||
"url"sv, // rpc
|
||||
"url-list"sv, // .torrent
|
||||
"use-global-speed-limit"sv, // .resume
|
||||
"use-speed-limit"sv, // .resume
|
||||
@@ -729,6 +732,7 @@ auto constexpr MyStatic = std::array<std::string_view, TR_N_KEYS>{
|
||||
"watch_dir_force_generic"sv, // daemon
|
||||
"webseeds"sv, // rpc
|
||||
"webseedsSendingToUs"sv, // rpc
|
||||
"webseeds_ex"sv, // rpc
|
||||
"webseeds_sending_to_us"sv, // rpc
|
||||
"yourip"sv, // BEP0010; BT protocol
|
||||
};
|
||||
|
||||
@@ -155,6 +155,7 @@ enum // NOLINT(performance-enum-size)
|
||||
TR_KEY_download_limit_camel_APICOMPAT,
|
||||
TR_KEY_download_limited_camel_APICOMPAT,
|
||||
TR_KEY_download_speed_camel_APICOMPAT,
|
||||
TR_KEY_download_bytes_per_second,
|
||||
TR_KEY_download_count,
|
||||
TR_KEY_download_dir,
|
||||
TR_KEY_download_dir_free_space,
|
||||
@@ -276,6 +277,7 @@ enum // NOLINT(performance-enum-size)
|
||||
TR_KEY_is_utp_camel_APICOMPAT,
|
||||
TR_KEY_is_uploading_to_camel_APICOMPAT,
|
||||
TR_KEY_is_backup,
|
||||
TR_KEY_is_downloading,
|
||||
TR_KEY_is_downloading_from,
|
||||
TR_KEY_is_encrypted,
|
||||
TR_KEY_is_finished,
|
||||
@@ -721,6 +723,7 @@ enum // NOLINT(performance-enum-size)
|
||||
TR_KEY_uploaded_ever_camel_APICOMPAT,
|
||||
TR_KEY_uploaded_bytes,
|
||||
TR_KEY_uploaded_ever,
|
||||
TR_KEY_url,
|
||||
TR_KEY_url_list,
|
||||
TR_KEY_use_global_speed_limit_kebab_APICOMPAT,
|
||||
TR_KEY_use_speed_limit_kebab_APICOMPAT,
|
||||
@@ -742,6 +745,7 @@ enum // NOLINT(performance-enum-size)
|
||||
TR_KEY_watch_dir_force_generic,
|
||||
TR_KEY_webseeds,
|
||||
TR_KEY_webseeds_sending_to_us_camel_APICOMPAT,
|
||||
TR_KEY_webseeds_ex,
|
||||
TR_KEY_webseeds_sending_to_us,
|
||||
TR_KEY_yourip,
|
||||
TR_N_KEYS
|
||||
|
||||
@@ -554,6 +554,23 @@ namespace make_torrent_field_helpers
|
||||
return tr_variant{ std::move(vec) };
|
||||
}
|
||||
|
||||
[[nodiscard]] auto make_webseed_ex_vec(tr_torrent const& tor)
|
||||
{
|
||||
auto const n_webseeds = tor.webseed_count();
|
||||
auto vec = tr_variant::Vector{};
|
||||
vec.reserve(n_webseeds);
|
||||
for (size_t idx = 0U; idx != n_webseeds; ++idx)
|
||||
{
|
||||
auto const webseed = tr_torrentWebseed(&tor, idx);
|
||||
auto webseed_map = tr_variant::Map{ 3U };
|
||||
webseed_map.try_emplace(TR_KEY_url, webseed.url);
|
||||
webseed_map.try_emplace(TR_KEY_is_downloading, webseed.is_downloading);
|
||||
webseed_map.try_emplace(TR_KEY_download_bytes_per_second, webseed.download_bytes_per_second);
|
||||
vec.emplace_back(std::move(webseed_map));
|
||||
}
|
||||
return tr_variant{ std::move(vec) };
|
||||
}
|
||||
|
||||
[[nodiscard]] auto make_tracker_vec(tr_torrent const& tor)
|
||||
{
|
||||
auto const& trackers = tor.announce_list();
|
||||
@@ -768,6 +785,7 @@ namespace make_torrent_field_helpers
|
||||
case TR_KEY_uploaded_ever:
|
||||
case TR_KEY_wanted:
|
||||
case TR_KEY_webseeds:
|
||||
case TR_KEY_webseeds_ex:
|
||||
case TR_KEY_webseeds_sending_to_us:
|
||||
return true;
|
||||
|
||||
@@ -942,6 +960,8 @@ namespace make_torrent_field_helpers
|
||||
return make_file_wanted_vec(tor);
|
||||
case TR_KEY_webseeds:
|
||||
return make_webseed_vec(tor);
|
||||
case TR_KEY_webseeds_ex:
|
||||
return make_webseed_ex_vec(tor);
|
||||
case TR_KEY_webseeds_sending_to_us:
|
||||
return st.webseeds_sending_to_us;
|
||||
default:
|
||||
|
||||
@@ -1185,6 +1185,7 @@ a {
|
||||
table-layout: fixed;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
td,
|
||||
th {
|
||||
@@ -1252,6 +1253,12 @@ a {
|
||||
cursor: pointer;
|
||||
width: 10%;
|
||||
}
|
||||
|
||||
.webseed-url {
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
/// FILE PRIORITY BUTTONS
|
||||
|
||||
@@ -18,6 +18,8 @@ const peer_column_classes = [
|
||||
'peer-app-name',
|
||||
];
|
||||
|
||||
const webseed_column_classes = ['url', 'speed-down'];
|
||||
|
||||
export class Inspector extends EventTarget {
|
||||
constructor(controller) {
|
||||
super();
|
||||
@@ -138,6 +140,26 @@ export class Inspector extends EventTarget {
|
||||
}
|
||||
|
||||
static _createPeersPage() {
|
||||
const container = document.createElement('div');
|
||||
|
||||
// Webseeds table
|
||||
const webseedsTable = document.createElement('table');
|
||||
webseedsTable.classList.add('peer-list');
|
||||
const webseedsThead = document.createElement('thead');
|
||||
const webseedsTr = document.createElement('tr');
|
||||
const webseedsHeaders = ['Web Seeds', 'Down'];
|
||||
for (const [index, name] of webseedsHeaders.entries()) {
|
||||
const th = document.createElement('th');
|
||||
th.classList.add(webseed_column_classes[index]);
|
||||
setTextContent(th, name);
|
||||
webseedsTr.append(th);
|
||||
}
|
||||
const webseedsTbody = document.createElement('tbody');
|
||||
webseedsThead.append(webseedsTr);
|
||||
webseedsTable.append(webseedsThead);
|
||||
webseedsTable.append(webseedsTbody);
|
||||
|
||||
// Peers table
|
||||
const table = document.createElement('table');
|
||||
table.classList.add('peer-list');
|
||||
const thead = document.createElement('thead');
|
||||
@@ -157,9 +179,16 @@ export class Inspector extends EventTarget {
|
||||
thead.append(tr);
|
||||
table.append(thead);
|
||||
table.append(tbody);
|
||||
|
||||
container.append(webseedsTable);
|
||||
container.append(table);
|
||||
|
||||
return {
|
||||
root: table,
|
||||
peersTable: table,
|
||||
root: container,
|
||||
tbody,
|
||||
webseedsTable,
|
||||
webseedsTbody,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -601,7 +630,7 @@ export class Inspector extends EventTarget {
|
||||
_updatePeers() {
|
||||
const fmt = Formatter;
|
||||
const { elements, torrents } = this;
|
||||
const { tbody } = elements.peers;
|
||||
const { tbody, webseedsTbody, webseedsTable } = elements.peers;
|
||||
|
||||
const cell_setters = [
|
||||
(peer, td) => {
|
||||
@@ -632,36 +661,86 @@ export class Inspector extends EventTarget {
|
||||
},
|
||||
];
|
||||
|
||||
const rows = [];
|
||||
const webseed_cell_setters = [
|
||||
(webseed, td) => {
|
||||
setTextContent(td, webseed.url);
|
||||
td.setAttribute('title', webseed.url);
|
||||
},
|
||||
(webseed, td) => {
|
||||
setTextContent(
|
||||
td,
|
||||
webseed.download_bytes_per_second
|
||||
? fmt.speedBps(webseed.download_bytes_per_second)
|
||||
: '',
|
||||
);
|
||||
},
|
||||
];
|
||||
|
||||
const webseedsRows = [];
|
||||
const peersRows = [];
|
||||
let hasWebseeds = false;
|
||||
|
||||
for (const tor of torrents) {
|
||||
// torrent name
|
||||
const tortr = document.createElement('tr');
|
||||
tortr.classList.add('torrent-row');
|
||||
const tortd = document.createElement('td');
|
||||
tortd.setAttribute('colspan', cell_setters.length);
|
||||
setTextContent(tortd, tor.getName());
|
||||
tortr.append(tortd);
|
||||
rows.push(tortr);
|
||||
// create base torrent row
|
||||
const baseTortr = document.createElement('tr');
|
||||
baseTortr.classList.add('torrent-row');
|
||||
const baseTortd = document.createElement('td');
|
||||
setTextContent(baseTortd, tor.getName());
|
||||
baseTortr.append(baseTortd);
|
||||
|
||||
// webseeds
|
||||
const webseeds = tor.getWebseedsEx();
|
||||
if (webseeds.length > 0) {
|
||||
hasWebseeds = true;
|
||||
const tortr = baseTortr.cloneNode(true);
|
||||
tortr.firstChild.setAttribute('colspan', webseed_cell_setters.length);
|
||||
webseedsRows.push(tortr);
|
||||
|
||||
// webseed rows
|
||||
for (const webseed of webseeds) {
|
||||
const tr = document.createElement('tr');
|
||||
tr.classList.add('webseed-row');
|
||||
for (const [index, setter] of webseed_cell_setters.entries()) {
|
||||
const td = document.createElement('td');
|
||||
td.classList.add(webseed_column_classes[index]);
|
||||
setter(webseed, td);
|
||||
tr.append(td);
|
||||
}
|
||||
webseedsRows.push(tr);
|
||||
}
|
||||
}
|
||||
|
||||
// peers
|
||||
const tortr = baseTortr.cloneNode(true);
|
||||
tortr.firstChild.setAttribute('colspan', cell_setters.length);
|
||||
peersRows.push(tortr);
|
||||
|
||||
// peers
|
||||
for (const peer of tor.getPeers()) {
|
||||
const tr = document.createElement('tr');
|
||||
tr.classList.add('peer-row');
|
||||
for (const [index, setter] of cell_setters.entries()) {
|
||||
const td = document.createElement('td');
|
||||
td.classList.add(peer_column_classes[index]);
|
||||
setter(peer, td);
|
||||
tr.append(td);
|
||||
}
|
||||
rows.push(tr);
|
||||
peersRows.push(tr);
|
||||
}
|
||||
|
||||
// TODO: modify instead of rebuilding wholesale?
|
||||
while (tbody.firstChild) {
|
||||
tbody.firstChild.remove();
|
||||
}
|
||||
tbody.append(...rows);
|
||||
}
|
||||
|
||||
// TODO: modify instead of rebuilding wholesale?
|
||||
webseedsTable.style.display = hasWebseeds ? '' : 'none';
|
||||
while (webseedsTbody.firstChild) {
|
||||
webseedsTbody.firstChild.remove();
|
||||
}
|
||||
if (hasWebseeds) {
|
||||
webseedsTbody.append(...webseedsRows);
|
||||
}
|
||||
|
||||
while (tbody.firstChild) {
|
||||
tbody.firstChild.remove();
|
||||
}
|
||||
tbody.append(...peersRows);
|
||||
}
|
||||
|
||||
/// TRACKERS PAGE
|
||||
|
||||
@@ -189,6 +189,9 @@ export class Torrent extends EventTarget {
|
||||
getPeers() {
|
||||
return this.fields.peers || [];
|
||||
}
|
||||
getWebseedsEx() {
|
||||
return this.fields.webseeds_ex || [];
|
||||
}
|
||||
getPeersConnected() {
|
||||
return this.fields.peers_connected;
|
||||
}
|
||||
@@ -653,4 +656,5 @@ Torrent.Fields.StatsExtra = [
|
||||
'peers',
|
||||
'start_date',
|
||||
'tracker_stats',
|
||||
'webseeds_ex',
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user