Display upload speed in Web UI

This commit is contained in:
Igor Katson 2023-12-05 20:52:30 +00:00
parent 4784f3f14b
commit 80df2c1001
No known key found for this signature in database
GPG key ID: B4EC22B66D61A3F5
8 changed files with 89 additions and 55 deletions

View file

@ -188,7 +188,8 @@ pub struct TorrentStateLive {
cancel_tx: tokio::sync::watch::Sender<()>,
cancel_rx: tokio::sync::watch::Receiver<()>,
speed_estimator: SpeedEstimator,
down_speed_estimator: SpeedEstimator,
up_speed_estimator: SpeedEstimator,
}
impl TorrentStateLive {
@ -198,7 +199,8 @@ impl TorrentStateLive {
) -> Arc<Self> {
let (peer_queue_tx, peer_queue_rx) = unbounded_channel();
let speed_estimator = SpeedEstimator::new(5);
let down_speed_estimator = SpeedEstimator::new(5);
let up_speed_estimator = SpeedEstimator::new(5);
let have_bytes = paused.have_bytes;
let needed_bytes = paused.info.lengths.total_length() - have_bytes;
@ -225,7 +227,8 @@ impl TorrentStateLive {
peer_semaphore: Semaphore::new(128),
peer_queue_tx,
finished_notify: Notify::new(),
speed_estimator,
down_speed_estimator,
up_speed_estimator,
cancel_rx,
cancel_tx,
});
@ -249,6 +252,7 @@ impl TorrentStateLive {
Some(state) => state,
None => return Ok(()),
};
let now = Instant::now();
let stats = state.stats_snapshot();
let fetched = stats.fetched_bytes;
let needed = state.initially_needed();
@ -257,8 +261,11 @@ impl TorrentStateLive {
.wrapping_sub(fetched)
.min(needed - stats.downloaded_and_checked_bytes);
state
.speed_estimator
.add_snapshot(fetched, remaining, Instant::now());
.down_speed_estimator
.add_snapshot(fetched, Some(remaining), now);
state
.up_speed_estimator
.add_snapshot(stats.uploaded_bytes, None, now);
tokio::time::sleep(Duration::from_secs(1)).await;
}
}
@ -291,8 +298,12 @@ impl TorrentStateLive {
});
}
pub fn speed_estimator(&self) -> &SpeedEstimator {
&self.speed_estimator
pub fn down_speed_estimator(&self) -> &SpeedEstimator {
&self.down_speed_estimator
}
pub fn up_speed_estimator(&self) -> &SpeedEstimator {
&self.up_speed_estimator
}
async fn tracker_one_request(&self, tracker_url: Url) -> anyhow::Result<u64> {

View file

@ -10,6 +10,7 @@ pub struct LiveStats {
pub snapshot: StatsSnapshot,
pub average_piece_download_time: Option<Duration>,
pub download_speed: Speed,
pub upload_speed: Speed,
pub time_remaining: Option<DurationWithHumanReadable>,
}
@ -17,8 +18,9 @@ impl std::fmt::Display for LiveStats {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "down speed: {}", self.download_speed)?;
if let Some(time_remaining) = &self.time_remaining {
write!(f, " eta: {time_remaining}")?;
write!(f, ", eta: {time_remaining}")?;
}
write!(f, ", up speed: {}", self.upload_speed)?;
Ok(())
}
}
@ -26,13 +28,17 @@ impl std::fmt::Display for LiveStats {
impl From<&TorrentStateLive> for LiveStats {
fn from(live: &TorrentStateLive) -> Self {
let snapshot = live.stats_snapshot();
let estimator = live.speed_estimator();
let down_estimator = live.down_speed_estimator();
let up_estimator = live.up_speed_estimator();
Self {
average_piece_download_time: snapshot.average_piece_download_time(),
snapshot,
download_speed: estimator.download_mbps().into(),
time_remaining: estimator.time_remaining().map(DurationWithHumanReadable),
download_speed: down_estimator.mbps().into(),
upload_speed: up_estimator.mbps().into(),
time_remaining: down_estimator
.time_remaining()
.map(DurationWithHumanReadable),
}
}
}

File diff suppressed because one or more lines are too long

View file

@ -4,7 +4,7 @@
"src": "assets/logo.svg"
},
"index.html": {
"file": "assets/index-6d4556f3.js",
"file": "assets/index-713a95fc.js",
"isEntry": true,
"src": "index.html"
}

View file

@ -27,6 +27,11 @@ export interface ListTorrentsResponse {
torrents: Array<TorrentId>;
}
export interface Speed {
mbps: number;
human_readable: string;
}
// Interface for the Torrent Stats API response
export interface LiveTorrentStats {
snapshot: {
@ -52,10 +57,8 @@ export interface LiveTorrentStats {
secs: number;
nanos: number;
};
download_speed: {
mbps: number;
human_readable: string;
};
download_speed: Speed;
upload_speed: Speed;
all_time_download_speed: {
mbps: number;
human_readable: string;

View file

@ -185,6 +185,27 @@ const TorrentActions: React.FC<{
</Row>
}
const Speed: React.FC<{ statsResponse: TorrentStats }> = ({ statsResponse }) => {
switch (statsResponse.state) {
case STATE_PAUSED: return 'Paused';
case STATE_INITIALIZING: return 'Checking files';
case STATE_ERROR: return 'Error';
}
// Unknown state
if (statsResponse.state != 'live' || statsResponse.live === null) {
return statsResponse.state;
}
return <>
{!statsResponse.finished && <p> {statsResponse.live.download_speed.human_readable}</p>}
<p> {statsResponse.live.upload_speed.human_readable}</p>
</>
if (statsResponse.finished) {
return <span>Completed</span>;
}
}
const TorrentRow: React.FC<{
id: number,
detailsResponse: TorrentDetails | null,
@ -208,19 +229,6 @@ const TorrentRow: React.FC<{
return `${peer_stats.live} / ${peer_stats.seen}`;
}
const formatDownloadSpeed = () => {
if (finished) {
return 'Completed';
}
switch (state) {
case STATE_PAUSED: return 'Paused';
case STATE_INITIALIZING: return 'Checking files';
case STATE_ERROR: return 'Error';
}
return statsResponse?.live?.download_speed.human_readable ?? "N/A";
}
let classNames = [];
if (error) {
@ -253,7 +261,9 @@ const TorrentRow: React.FC<{
animated={isAnimated}
variant={progressBarVariant} />
</Column>
<Column size={2} label="Down Speed">{formatDownloadSpeed()}</Column>
<Column size={2} label="Speed">
<Speed statsResponse={statsResponse} />
</Column>
<Column label="ETA">{getCompletionETA(statsResponse)}</Column>
<Column size={2} label="Peers">{formatPeersString()}</Column >
<Column label="Actions">