Expose peer and piece state to web UI
This commit is contained in:
parent
f4f4e86be4
commit
6ae72907b0
6 changed files with 213 additions and 11 deletions
|
|
@ -213,6 +213,16 @@ static json_t *torrent_json(torrent_task *task) {
|
||||||
json_integer(task->stats.pieces_done));
|
json_integer(task->stats.pieces_done));
|
||||||
json_object_set_new(result, "total_pieces",
|
json_object_set_new(result, "total_pieces",
|
||||||
json_integer(task->stats.total_pieces));
|
json_integer(task->stats.total_pieces));
|
||||||
|
json_t *piece_states = json_array();
|
||||||
|
if (piece_states) {
|
||||||
|
uint32_t state_count = task->stats.piece_state_count;
|
||||||
|
if (state_count > NAUT_SWARM_MAX_PIECE_STATS)
|
||||||
|
state_count = NAUT_SWARM_MAX_PIECE_STATS;
|
||||||
|
for (uint32_t i = 0; i < state_count; i++)
|
||||||
|
json_array_append_new(piece_states,
|
||||||
|
json_integer(task->stats.piece_states[i]));
|
||||||
|
json_object_set_new(result, "piece_states", piece_states);
|
||||||
|
}
|
||||||
json_object_set_new(result, "peers",
|
json_object_set_new(result, "peers",
|
||||||
json_integer(task->stats.peers_active));
|
json_integer(task->stats.peers_active));
|
||||||
json_object_set_new(result, "peers_discovered",
|
json_object_set_new(result, "peers_discovered",
|
||||||
|
|
@ -221,6 +231,31 @@ static json_t *torrent_json(torrent_task *task) {
|
||||||
json_integer(task->stats.peers_connecting));
|
json_integer(task->stats.peers_connecting));
|
||||||
json_object_set_new(result, "peers_failed",
|
json_object_set_new(result, "peers_failed",
|
||||||
json_integer(task->stats.peers_failed));
|
json_integer(task->stats.peers_failed));
|
||||||
|
json_t *peer_list = json_array();
|
||||||
|
if (peer_list) {
|
||||||
|
uint32_t peer_count = task->stats.peer_count;
|
||||||
|
if (peer_count > NAUT_SWARM_MAX_PEER_STATS)
|
||||||
|
peer_count = NAUT_SWARM_MAX_PEER_STATS;
|
||||||
|
for (uint32_t i = 0; i < peer_count; i++) {
|
||||||
|
const naut_swarm_peer_stats *peer =
|
||||||
|
&task->stats.peer_stats[i];
|
||||||
|
json_t *item = json_pack(
|
||||||
|
"{s:s,s:i,s:s,s:s,s:s,s:f,s:f,s:I,s:I,s:I,s:I}",
|
||||||
|
"ip", peer->ip,
|
||||||
|
"port", (int)peer->port,
|
||||||
|
"client", peer->client,
|
||||||
|
"connection", peer->connection,
|
||||||
|
"flags", peer->flags,
|
||||||
|
"progress", peer->progress,
|
||||||
|
"relevance", peer->relevance,
|
||||||
|
"downloaded", (json_int_t)peer->downloaded,
|
||||||
|
"uploaded", (json_int_t)peer->uploaded,
|
||||||
|
"dlspeed", (json_int_t)peer->dlspeed,
|
||||||
|
"upspeed", (json_int_t)peer->upspeed);
|
||||||
|
if (item) json_array_append_new(peer_list, item);
|
||||||
|
}
|
||||||
|
json_object_set_new(result, "peer_list", peer_list);
|
||||||
|
}
|
||||||
json_object_set_new(result, "elapsed_seconds",
|
json_object_set_new(result, "elapsed_seconds",
|
||||||
json_real(task->stats.elapsed_seconds));
|
json_real(task->stats.elapsed_seconds));
|
||||||
json_object_set_new(result, "pending_moves",
|
json_object_set_new(result, "pending_moves",
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
@ -51,12 +52,16 @@ typedef struct {
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int fd;
|
int fd;
|
||||||
|
naut_peer_addr addr;
|
||||||
char name[40];
|
char name[40];
|
||||||
|
uint8_t peer_id[20];
|
||||||
|
bool have_peer_id;
|
||||||
uint8_t *rbuf; size_t rcap, rlen;
|
uint8_t *rbuf; size_t rcap, rlen;
|
||||||
bool hs_done, peer_choking;
|
bool hs_done, peer_choking;
|
||||||
naut_bitfield have;
|
naut_bitfield have;
|
||||||
req_t *inflight; size_t nflight, cflight;
|
req_t *inflight; size_t nflight, cflight;
|
||||||
uint64_t blocks_received;
|
uint64_t blocks_received;
|
||||||
|
uint64_t bytes_received;
|
||||||
naut_ext_handshake extensions;
|
naut_ext_handshake extensions;
|
||||||
uint64_t pex_received;
|
uint64_t pex_received;
|
||||||
naut_pipeline pipeline;
|
naut_pipeline pipeline;
|
||||||
|
|
@ -121,11 +126,77 @@ static void on_piece_complete(void *opaque, uint32_t index) {
|
||||||
emit_event(config, NAUT_EVENT_PIECE_COMPLETE, index, NULL, NULL);
|
emit_event(config, NAUT_EVENT_PIECE_COMPLETE, index, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void peer_client_label(const peer_t *peer, char out[64]) {
|
||||||
|
if (!peer || !peer->have_peer_id) {
|
||||||
|
snprintf(out, 64, "Unknown");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (peer->peer_id[0] == '-' && peer->peer_id[7] == '-') {
|
||||||
|
char code[3] = {
|
||||||
|
(char)peer->peer_id[1],
|
||||||
|
(char)peer->peer_id[2],
|
||||||
|
0,
|
||||||
|
};
|
||||||
|
char version[5];
|
||||||
|
memcpy(version, peer->peer_id + 3, 4);
|
||||||
|
version[4] = 0;
|
||||||
|
for (size_t i = 0; i < sizeof version - 1; i++)
|
||||||
|
if (!isprint((unsigned char)version[i])) version[i] = '?';
|
||||||
|
snprintf(out, 64, "%s %s", code, version);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
char id[21];
|
||||||
|
memcpy(id, peer->peer_id, sizeof peer->peer_id);
|
||||||
|
id[20] = 0;
|
||||||
|
for (size_t i = 0; i < sizeof id - 1; i++)
|
||||||
|
if (!isprint((unsigned char)id[i])) id[i] = '.';
|
||||||
|
snprintf(out, 64, "%s", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void peer_flags(const peer_t *peer, char out[16]) {
|
||||||
|
size_t n = 0;
|
||||||
|
if (peer && !peer->peer_choking && n + 1 < 16) out[n++] = 'D';
|
||||||
|
if (peer && (peer->extensions.ut_pex || peer->pex_received) && n + 1 < 16)
|
||||||
|
out[n++] = 'X';
|
||||||
|
out[n] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void snapshot_peer_stats(naut_swarm_stats *stats,
|
||||||
|
const peer_t *peers, int npeers,
|
||||||
|
uint32_t total_pieces) {
|
||||||
|
if (!stats || !peers || npeers <= 0) return;
|
||||||
|
for (int i = 0; i < npeers &&
|
||||||
|
stats->peer_count < NAUT_SWARM_MAX_PEER_STATS; i++) {
|
||||||
|
const peer_t *peer = &peers[i];
|
||||||
|
if (peer->dead || !peer->hs_done) continue;
|
||||||
|
naut_swarm_peer_stats *out =
|
||||||
|
&stats->peer_stats[stats->peer_count++];
|
||||||
|
snprintf(out->ip, sizeof out->ip, "%u.%u.%u.%u",
|
||||||
|
peer->addr.ip[0], peer->addr.ip[1],
|
||||||
|
peer->addr.ip[2], peer->addr.ip[3]);
|
||||||
|
out->port = peer->addr.port;
|
||||||
|
peer_client_label(peer, out->client);
|
||||||
|
snprintf(out->connection, sizeof out->connection, "TCP");
|
||||||
|
peer_flags(peer, out->flags);
|
||||||
|
size_t have = peer->have.words ? naut_bitfield_count(&peer->have) : 0;
|
||||||
|
double ratio = total_pieces
|
||||||
|
? (double)have / (double)total_pieces : 0.0;
|
||||||
|
if (ratio > 1.0) ratio = 1.0;
|
||||||
|
out->progress = ratio;
|
||||||
|
out->relevance = ratio;
|
||||||
|
out->dlspeed = peer->pipeline.bytes_per_second;
|
||||||
|
out->upspeed = 0.0;
|
||||||
|
out->downloaded = peer->bytes_received;
|
||||||
|
out->uploaded = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void report_progress(const naut_swarm_config *config,
|
static void report_progress(const naut_swarm_config *config,
|
||||||
const naut_download *download,
|
const naut_download *download,
|
||||||
const naut_metainfo *metainfo,
|
const naut_metainfo *metainfo,
|
||||||
uint32_t peers_total, uint32_t peers_connecting,
|
uint32_t peers_total, uint32_t peers_connecting,
|
||||||
uint32_t peers_active, uint32_t peers_failed,
|
uint32_t peers_active, uint32_t peers_failed,
|
||||||
|
const peer_t *peers, int npeers,
|
||||||
double started_at) {
|
double started_at) {
|
||||||
if (!config->on_progress) return;
|
if (!config->on_progress) return;
|
||||||
naut_swarm_stats stats = {
|
naut_swarm_stats stats = {
|
||||||
|
|
@ -139,6 +210,11 @@ static void report_progress(const naut_swarm_config *config,
|
||||||
.peers_failed = peers_failed,
|
.peers_failed = peers_failed,
|
||||||
.elapsed_seconds = now() - started_at,
|
.elapsed_seconds = now() - started_at,
|
||||||
};
|
};
|
||||||
|
snapshot_peer_stats(&stats, peers, npeers, metainfo->num_pieces);
|
||||||
|
if (download) {
|
||||||
|
stats.piece_state_count = (uint32_t)naut_download_piece_states(
|
||||||
|
download, stats.piece_states, NAUT_SWARM_MAX_PIECE_STATS);
|
||||||
|
}
|
||||||
config->on_progress(config->context, &stats);
|
config->on_progress(config->context, &stats);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -476,6 +552,7 @@ static bool peer_start(naut_download *download, const naut_metainfo *metainfo,
|
||||||
const uint8_t peerid[20], const endpoint_t *endpoint,
|
const uint8_t peerid[20], const endpoint_t *endpoint,
|
||||||
peer_t *peer, int fd) {
|
peer_t *peer, int fd) {
|
||||||
peer->fd = fd;
|
peer->fd = fd;
|
||||||
|
peer->addr = endpoint->addr;
|
||||||
snprintf(peer->name, sizeof peer->name, "%s", endpoint->name);
|
snprintf(peer->name, sizeof peer->name, "%s", endpoint->name);
|
||||||
peer->peer_choking = true;
|
peer->peer_choking = true;
|
||||||
naut_pipeline_init(&peer->pipeline, NAUT_BLOCK, 4, 1024, 32);
|
naut_pipeline_init(&peer->pipeline, NAUT_BLOCK, 4, 1024, 32);
|
||||||
|
|
@ -580,6 +657,8 @@ static naut_err peer_process(naut_download *d, const naut_metainfo *mi,
|
||||||
p->dead = true;
|
p->dead = true;
|
||||||
return NAUT_OK;
|
return NAUT_OK;
|
||||||
}
|
}
|
||||||
|
memcpy(p->peer_id, pid, sizeof p->peer_id);
|
||||||
|
p->have_peer_id = true;
|
||||||
pos = NAUT_HANDSHAKE_LEN;
|
pos = NAUT_HANDSHAKE_LEN;
|
||||||
p->hs_done = true;
|
p->hs_done = true;
|
||||||
}
|
}
|
||||||
|
|
@ -642,6 +721,7 @@ static naut_err peer_process(naut_download *d, const naut_metainfo *mi,
|
||||||
(uint32_t)m.payload_len, &pdone);
|
(uint32_t)m.payload_len, &pdone);
|
||||||
if (e == NAUT_OK) {
|
if (e == NAUT_OK) {
|
||||||
p->blocks_received++;
|
p->blocks_received++;
|
||||||
|
p->bytes_received += m.payload_len;
|
||||||
cancel_block(d, peers, npeers, p, m.index, m.begin);
|
cancel_block(d, peers, npeers, p, m.index, m.begin);
|
||||||
} else if (e == NAUT_ERR_PROTO) {
|
} else if (e == NAUT_ERR_PROTO) {
|
||||||
if (expected) cancel_piece(d, peers, npeers, m.index);
|
if (expected) cancel_piece(d, peers, npeers, m.index);
|
||||||
|
|
@ -837,7 +917,8 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
||||||
uint32_t failed = 0;
|
uint32_t failed = 0;
|
||||||
struct pollfd *connect_fds = pfd;
|
struct pollfd *connect_fds = pfd;
|
||||||
for (int i = 0; i < npeers; i++) connect_fds[i].fd = -1;
|
for (int i = 0; i < npeers; i++) connect_fds[i].fd = -1;
|
||||||
report_progress(config, d, &mi, (uint32_t)npeers, 0, 0, 0, t0);
|
report_progress(config, d, &mi, (uint32_t)npeers, 0, 0, 0,
|
||||||
|
peers, npeers, t0);
|
||||||
for (int i = 0; i < npeers; i++) {
|
for (int i = 0; i < npeers; i++) {
|
||||||
bool connected = false;
|
bool connected = false;
|
||||||
int fd = connect_start(&endpoints[i], &connected);
|
int fd = connect_start(&endpoints[i], &connected);
|
||||||
|
|
@ -871,7 +952,7 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
||||||
while (connecting > 0 && now() < connect_deadline &&
|
while (connecting > 0 && now() < connect_deadline &&
|
||||||
!stop_requested(config)) {
|
!stop_requested(config)) {
|
||||||
report_progress(config, d, &mi, (uint32_t)npeers, connecting,
|
report_progress(config, d, &mi, (uint32_t)npeers, connecting,
|
||||||
active, failed, t0);
|
active, failed, peers, npeers, t0);
|
||||||
int ready = poll(connect_fds, (nfds_t)neps, 100);
|
int ready = poll(connect_fds, (nfds_t)neps, 100);
|
||||||
if (ready < 0) {
|
if (ready < 0) {
|
||||||
if (errno == EINTR) continue;
|
if (errno == EINTR) continue;
|
||||||
|
|
@ -921,7 +1002,8 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
||||||
active, npeers, mi.num_pieces, (long long)mi.total_length);
|
active, npeers, mi.num_pieces, (long long)mi.total_length);
|
||||||
|
|
||||||
emit_event(config, NAUT_EVENT_TORRENT_ADDED, 0, NULL, NULL);
|
emit_event(config, NAUT_EVENT_TORRENT_ADDED, 0, NULL, NULL);
|
||||||
report_progress(config, d, &mi, (uint32_t)npeers, 0, active, failed, t0);
|
report_progress(config, d, &mi, (uint32_t)npeers, 0, active, failed,
|
||||||
|
peers, npeers, t0);
|
||||||
while (!naut_download_complete(d) && run_error == NAUT_OK) {
|
while (!naut_download_complete(d) && run_error == NAUT_OK) {
|
||||||
service_control(config, st);
|
service_control(config, st);
|
||||||
if (stop_requested(config)) {
|
if (stop_requested(config)) {
|
||||||
|
|
@ -951,7 +1033,8 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
||||||
}
|
}
|
||||||
report_progress(config, d, &mi, (uint32_t)npeers, 0,
|
report_progress(config, d, &mi, (uint32_t)npeers, 0,
|
||||||
(uint32_t)live_peers,
|
(uint32_t)live_peers,
|
||||||
(uint32_t)npeers - (uint32_t)live_peers, t0);
|
(uint32_t)npeers - (uint32_t)live_peers,
|
||||||
|
peers, npeers, t0);
|
||||||
int r = poll(pfd, nf, 200);
|
int r = poll(pfd, nf, 200);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
if (errno == EINTR) continue;
|
if (errno == EINTR) continue;
|
||||||
|
|
@ -1007,7 +1090,7 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
||||||
naut_download_pieces_done(d), mi.num_pieces, dt, mb/dt,
|
naut_download_pieces_done(d), mi.num_pieces, dt, mb/dt,
|
||||||
naut_download_in_endgame(d) ? " (passed through endgame)" : "");
|
naut_download_in_endgame(d) ? " (passed through endgame)" : "");
|
||||||
report_progress(config, d, &mi, (uint32_t)npeers, 0, 0,
|
report_progress(config, d, &mi, (uint32_t)npeers, 0, 0,
|
||||||
(uint32_t)npeers, t0);
|
(uint32_t)npeers, peers, npeers, t0);
|
||||||
emit_event(config, NAUT_EVENT_TORRENT_FINISHED, 0, NULL, NULL);
|
emit_event(config, NAUT_EVENT_TORRENT_FINISHED, 0, NULL, NULL);
|
||||||
while (config->keep_alive && !stop_requested(config)) {
|
while (config->keep_alive && !stop_requested(config)) {
|
||||||
service_control(config, st);
|
service_control(config, st);
|
||||||
|
|
|
||||||
|
|
@ -87,5 +87,7 @@ bool naut_download_complete(const naut_download *d);
|
||||||
uint32_t naut_download_num_pieces(const naut_download *d);
|
uint32_t naut_download_num_pieces(const naut_download *d);
|
||||||
uint32_t naut_download_pieces_done(const naut_download *d);
|
uint32_t naut_download_pieces_done(const naut_download *d);
|
||||||
uint64_t naut_download_bytes_done(const naut_download *d);
|
uint64_t naut_download_bytes_done(const naut_download *d);
|
||||||
|
size_t naut_download_piece_states(const naut_download *d, uint8_t *out,
|
||||||
|
size_t capacity);
|
||||||
|
|
||||||
#endif /* NAUT_PIECE_H */
|
#endif /* NAUT_PIECE_H */
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,23 @@
|
||||||
#include "naut/event.h"
|
#include "naut/event.h"
|
||||||
#include "naut/storage.h"
|
#include "naut/storage.h"
|
||||||
|
|
||||||
|
#define NAUT_SWARM_MAX_PEER_STATS 64
|
||||||
|
#define NAUT_SWARM_MAX_PIECE_STATS 4000
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char ip[46];
|
||||||
|
uint16_t port;
|
||||||
|
char client[64];
|
||||||
|
char connection[16];
|
||||||
|
char flags[16];
|
||||||
|
double progress;
|
||||||
|
double relevance;
|
||||||
|
double dlspeed;
|
||||||
|
double upspeed;
|
||||||
|
uint64_t downloaded;
|
||||||
|
uint64_t uploaded;
|
||||||
|
} naut_swarm_peer_stats;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint64_t total_bytes;
|
uint64_t total_bytes;
|
||||||
uint64_t bytes_done;
|
uint64_t bytes_done;
|
||||||
|
|
@ -21,6 +38,10 @@ typedef struct {
|
||||||
uint32_t peers_active;
|
uint32_t peers_active;
|
||||||
uint32_t peers_failed;
|
uint32_t peers_failed;
|
||||||
double elapsed_seconds;
|
double elapsed_seconds;
|
||||||
|
uint32_t peer_count;
|
||||||
|
naut_swarm_peer_stats peer_stats[NAUT_SWARM_MAX_PEER_STATS];
|
||||||
|
uint32_t piece_state_count;
|
||||||
|
uint8_t piece_states[NAUT_SWARM_MAX_PIECE_STATS];
|
||||||
} naut_swarm_stats;
|
} naut_swarm_stats;
|
||||||
|
|
||||||
typedef void (*naut_swarm_progress_cb)(void *context,
|
typedef void (*naut_swarm_progress_cb)(void *context,
|
||||||
|
|
|
||||||
|
|
@ -442,6 +442,12 @@ static uint64_t json_u64(const json_t *obj, const char *key) {
|
||||||
? (uint64_t)json_integer_value(value) : 0;
|
? (uint64_t)json_integer_value(value) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static double json_number_or(const json_t *obj, const char *key,
|
||||||
|
double fallback) {
|
||||||
|
json_t *value = json_object_get(obj, key);
|
||||||
|
return json_is_number(value) ? json_number_value(value) : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
static const char *base_name(const char *path) {
|
static const char *base_name(const char *path) {
|
||||||
if (!path || !*path) return "torrent";
|
if (!path || !*path) return "torrent";
|
||||||
const char *slash = strrchr(path, '/');
|
const char *slash = strrchr(path, '/');
|
||||||
|
|
@ -568,6 +574,33 @@ static json_t *tracker_hosts(json_t *trackers) {
|
||||||
return hosts;
|
return hosts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static json_t *map_peer_list(json_t *torrent) {
|
||||||
|
json_t *out = json_array();
|
||||||
|
json_t *peers = json_object_get(torrent, "peer_list");
|
||||||
|
if (!out || !json_is_array(peers)) return out;
|
||||||
|
size_t index;
|
||||||
|
json_t *peer;
|
||||||
|
json_array_foreach(peers, index, peer) {
|
||||||
|
if (!json_is_object(peer)) continue;
|
||||||
|
json_t *item = json_pack(
|
||||||
|
"{s:s,s:s,s:i,s:s,s:s,s:s,s:f,s:i,s:i,s:I,s:I,s:f}",
|
||||||
|
"country", "",
|
||||||
|
"ip", json_string_or(peer, "ip", ""),
|
||||||
|
"port", (int)json_u64(peer, "port"),
|
||||||
|
"client", json_string_or(peer, "client", "Unknown"),
|
||||||
|
"connection", json_string_or(peer, "connection", "TCP"),
|
||||||
|
"flags", json_string_or(peer, "flags", ""),
|
||||||
|
"progress", json_number_or(peer, "progress", 0.0),
|
||||||
|
"dlspeed", (int)json_u64(peer, "dlspeed"),
|
||||||
|
"upspeed", (int)json_u64(peer, "upspeed"),
|
||||||
|
"downloaded", (json_int_t)json_u64(peer, "downloaded"),
|
||||||
|
"uploaded", (json_int_t)json_u64(peer, "uploaded"),
|
||||||
|
"relevance", json_number_or(peer, "relevance", 0.0));
|
||||||
|
if (item) json_array_append_new(out, item);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
static double torrent_progress_ratio(json_t *torrent, uint64_t *done_out,
|
static double torrent_progress_ratio(json_t *torrent, uint64_t *done_out,
|
||||||
uint64_t *total_out) {
|
uint64_t *total_out) {
|
||||||
uint64_t done = json_u64(torrent, "bytes_done");
|
uint64_t done = json_u64(torrent, "bytes_done");
|
||||||
|
|
@ -781,7 +814,7 @@ static json_t *map_torrent(json_t *torrent, bool detail, double dlspeed) {
|
||||||
"{s:s,s:I,s:f,s:i,s:f}", "name", name,
|
"{s:s,s:I,s:f,s:i,s:f}", "name", name,
|
||||||
"size", (json_int_t)total, "progress", progress,
|
"size", (json_int_t)total, "progress", progress,
|
||||||
"priority", 1, "availability", 1.0));
|
"priority", 1, "availability", 1.0));
|
||||||
peers_list = json_array();
|
peers_list = map_peer_list(torrent);
|
||||||
hosts = tracker_hosts(trackers);
|
hosts = tracker_hosts(trackers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -844,6 +877,11 @@ static json_t *map_torrent(json_t *torrent, bool detail, double dlspeed) {
|
||||||
json_string(json_string_or(torrent, "source", "")));
|
json_string(json_string_or(torrent, "source", "")));
|
||||||
json_object_set_new(out, "pieceCount", json_integer((json_int_t)pieces));
|
json_object_set_new(out, "pieceCount", json_integer((json_int_t)pieces));
|
||||||
json_object_set_new(out, "piecesDone", json_integer((json_int_t)pieces_done));
|
json_object_set_new(out, "piecesDone", json_integer((json_int_t)pieces_done));
|
||||||
|
json_t *piece_states = json_object_get(torrent, "piece_states");
|
||||||
|
json_object_set_new(out, "pieceStates",
|
||||||
|
json_is_array(piece_states)
|
||||||
|
? json_deep_copy(piece_states)
|
||||||
|
: json_array());
|
||||||
json_object_set_new(out, "trackers", trackers ? trackers : json_array());
|
json_object_set_new(out, "trackers", trackers ? trackers : json_array());
|
||||||
json_object_set_new(out, "peersList", peers_list ? peers_list : json_array());
|
json_object_set_new(out, "peersList", peers_list ? peers_list : json_array());
|
||||||
json_object_set_new(out, "files", files ? files : json_array());
|
json_object_set_new(out, "files", files ? files : json_array());
|
||||||
|
|
@ -1056,9 +1094,12 @@ static void api_torrent_detail(int fd, const char *tail) {
|
||||||
} else if (strcmp(tab, "pieces") == 0) {
|
} else if (strcmp(tab, "pieces") == 0) {
|
||||||
uint64_t count = json_u64(torrent, "pieceCount");
|
uint64_t count = json_u64(torrent, "pieceCount");
|
||||||
uint64_t done = json_u64(torrent, "piecesDone");
|
uint64_t done = json_u64(torrent, "piecesDone");
|
||||||
json_t *pieces = json_array();
|
json_t *states = json_object_get(torrent, "pieceStates");
|
||||||
for (uint64_t i = 0; pieces && i < count && i < 4000; i++)
|
json_t *pieces = json_is_array(states) ? json_deep_copy(states)
|
||||||
json_array_append_new(pieces, json_integer(i < done ? 2 : 0));
|
: json_array();
|
||||||
|
if (pieces && json_array_size(pieces) == 0)
|
||||||
|
for (uint64_t i = 0; i < count && i < 4000; i++)
|
||||||
|
json_array_append_new(pieces, json_integer(i < done ? 2 : 0));
|
||||||
json_t *value = json_pack("{s:I,s:I,s:o}",
|
json_t *value = json_pack("{s:I,s:I,s:o}",
|
||||||
"pieceSize", json_u64(torrent, "pieceSize"),
|
"pieceSize", json_u64(torrent, "pieceSize"),
|
||||||
"pieceCount", count, "pieces", pieces);
|
"pieceCount", count, "pieces", pieces);
|
||||||
|
|
@ -1085,9 +1126,15 @@ static void api_add(int fd, const char *body, size_t len) {
|
||||||
if (!save_path || !*save_path) save_path = ".";
|
if (!save_path || !*save_path) save_path = ".";
|
||||||
if (!source) source = magnet;
|
if (!source) source = magnet;
|
||||||
if ((!source || !*source) && (!data || !*data)) {
|
if ((!source || !*source) && (!data || !*data)) {
|
||||||
|
/* A request carrying only parsed metadata (name/files) but no bytes is
|
||||||
|
* the tell-tale of a stale UI that predates base64 upload support. */
|
||||||
|
bool looks_stale = json_object_get(req, "name") ||
|
||||||
|
json_object_get(req, "files");
|
||||||
json_decref(req);
|
json_decref(req);
|
||||||
http_text(fd, 400, "Bad Request",
|
http_text(fd, 400, "Bad Request", looks_stale
|
||||||
"torrent-ui must send a magnet, source, or torrent data");
|
? "no torrent bytes in request: the page is running an old UI. "
|
||||||
|
"Hard-reload the browser (Ctrl+Shift+R) and add the file again."
|
||||||
|
: "send a magnet, a source path, or uploaded torrent bytes");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
json_t *params = json_object();
|
json_t *params = json_object();
|
||||||
|
|
|
||||||
|
|
@ -391,3 +391,17 @@ bool naut_download_in_endgame(const naut_download *d) { return d->endgame; }
|
||||||
uint32_t naut_download_num_pieces(const naut_download *d) { return d->num_pieces; }
|
uint32_t naut_download_num_pieces(const naut_download *d) { return d->num_pieces; }
|
||||||
uint32_t naut_download_pieces_done(const naut_download *d) { return d->pieces_done; }
|
uint32_t naut_download_pieces_done(const naut_download *d) { return d->pieces_done; }
|
||||||
uint64_t naut_download_bytes_done(const naut_download *d) { return d->bytes_done; }
|
uint64_t naut_download_bytes_done(const naut_download *d) { return d->bytes_done; }
|
||||||
|
size_t naut_download_piece_states(const naut_download *d, uint8_t *out,
|
||||||
|
size_t capacity) {
|
||||||
|
if (!d || !out || capacity == 0) return 0;
|
||||||
|
size_t count = NAUT_MIN((size_t)d->num_pieces, capacity);
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
if (naut_bitfield_test(&d->have, i))
|
||||||
|
out[i] = 2;
|
||||||
|
else if (d->ps[i])
|
||||||
|
out[i] = 1;
|
||||||
|
else
|
||||||
|
out[i] = 0;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue