Spaces:
Runtime error
Runtime error
Update bot/core/progress.py
Browse files- bot/core/progress.py +84 -30
bot/core/progress.py
CHANGED
|
@@ -1,38 +1,92 @@
|
|
| 1 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
import time
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
class SpeedETA:
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
def
|
| 12 |
-
|
| 13 |
-
self.
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
|
| 23 |
def human_bytes(n: float) -> str:
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
m, s = divmod(s, 60)
|
| 35 |
h, m = divmod(m, 60)
|
| 36 |
-
if h:
|
| 37 |
-
|
|
|
|
|
|
|
| 38 |
return f"{s}s"
|
|
|
|
| 1 |
+
# FILE: bot/core/progress.py
|
| 2 |
+
# NOTE: Updated so SpeedETA matches handlers.py (returns float speed, has eta_seconds)
|
| 3 |
+
|
| 4 |
+
from __future__ import annotations
|
| 5 |
+
|
| 6 |
import time
|
| 7 |
+
from collections import deque
|
| 8 |
+
from typing import Deque, Optional, Tuple
|
| 9 |
+
|
| 10 |
|
| 11 |
class SpeedETA:
|
| 12 |
+
"""Tracks moving-average speed and ETA.
|
| 13 |
+
|
| 14 |
+
- update(sent, total) returns speed in bytes/sec (float)
|
| 15 |
+
- eta_seconds is an int or None
|
| 16 |
+
"""
|
| 17 |
+
|
| 18 |
+
def __init__(self, window_seconds: float = 12.0) -> None:
|
| 19 |
+
self.window_seconds = float(window_seconds)
|
| 20 |
+
self.samples: Deque[Tuple[float, int]] = deque()
|
| 21 |
+
self.speed_bps: float = 0.0
|
| 22 |
+
self.eta_seconds: Optional[int] = None
|
| 23 |
+
|
| 24 |
+
def update(self, sent: int, total: int) -> float:
|
| 25 |
+
# Be defensive: sent/total can be weird types in callbacks
|
| 26 |
+
try:
|
| 27 |
+
s = int(sent)
|
| 28 |
+
except Exception:
|
| 29 |
+
s = 0
|
| 30 |
+
try:
|
| 31 |
+
t = int(total) if total else 0
|
| 32 |
+
except Exception:
|
| 33 |
+
t = 0
|
| 34 |
+
|
| 35 |
+
now = time.monotonic()
|
| 36 |
+
self.samples.append((now, s))
|
| 37 |
+
|
| 38 |
+
# keep only recent samples
|
| 39 |
+
while self.samples and (now - self.samples[0][0]) > self.window_seconds:
|
| 40 |
+
self.samples.popleft()
|
| 41 |
+
|
| 42 |
+
if len(self.samples) >= 2:
|
| 43 |
+
t0, b0 = self.samples[0]
|
| 44 |
+
t1, b1 = self.samples[-1]
|
| 45 |
+
dt = t1 - t0
|
| 46 |
+
db = b1 - b0
|
| 47 |
+
if dt > 0 and db >= 0:
|
| 48 |
+
self.speed_bps = db / dt
|
| 49 |
+
else:
|
| 50 |
+
self.speed_bps = 0.0
|
| 51 |
+
else:
|
| 52 |
+
self.speed_bps = 0.0
|
| 53 |
+
|
| 54 |
+
if t > 0 and self.speed_bps > 0:
|
| 55 |
+
remaining = max(0, t - s)
|
| 56 |
+
self.eta_seconds = int(remaining / self.speed_bps)
|
| 57 |
+
else:
|
| 58 |
+
self.eta_seconds = None
|
| 59 |
+
|
| 60 |
+
return self.speed_bps
|
| 61 |
+
|
| 62 |
|
| 63 |
def human_bytes(n: float) -> str:
|
| 64 |
+
try:
|
| 65 |
+
n = float(n)
|
| 66 |
+
except Exception:
|
| 67 |
+
n = 0.0
|
| 68 |
+
|
| 69 |
+
units = ["B", "KB", "MB", "GB", "TB"]
|
| 70 |
+
i = 0
|
| 71 |
+
while n >= 1024 and i < len(units) - 1:
|
| 72 |
+
n /= 1024
|
| 73 |
+
i += 1
|
| 74 |
+
return f"{n:.2f}{units[i]}"
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def human_eta(seconds: Optional[float]) -> str:
|
| 78 |
+
if seconds is None:
|
| 79 |
+
return "—"
|
| 80 |
+
try:
|
| 81 |
+
s = int(seconds)
|
| 82 |
+
except Exception:
|
| 83 |
+
return "—"
|
| 84 |
+
if s < 0:
|
| 85 |
+
s = 0
|
| 86 |
m, s = divmod(s, 60)
|
| 87 |
h, m = divmod(m, 60)
|
| 88 |
+
if h:
|
| 89 |
+
return f"{h}h {m}m"
|
| 90 |
+
if m:
|
| 91 |
+
return f"{m}m {s}s"
|
| 92 |
return f"{s}s"
|