1 from __future__
import annotations
8 class ProgressCalculator
:
9 # Time to calculate the speed over (seconds)
11 # Minimum timeframe before to sample next downloaded bytes (seconds)
13 # Time before showing eta (seconds)
16 def __init__(self
, initial
: int):
17 self
._initial
= initial
or 0
18 self
.downloaded
= self
._initial
20 self
.elapsed
: float = 0
21 self
.speed
= SmoothValue(0, smoothing
=0.7)
22 self
.eta
= SmoothValue(None, smoothing
=0.9)
25 self
._start
_time
= time
.monotonic()
26 self
._last
_update
= self
._start
_time
28 self
._lock
= threading
.Lock()
29 self
._thread
_sizes
: dict[int, int] = {}
31 self
._times
= [self
._start
_time
]
32 self
._downloaded
= [self
.downloaded
]
39 def total(self
, value
: int |
None):
41 if value
is not None and value
< self
.downloaded
:
42 value
= self
.downloaded
46 def thread_reset(self
):
47 current_thread
= threading
.get_ident()
49 self
._thread
_sizes
[current_thread
] = 0
51 def update(self
, size
: int |
None):
55 current_thread
= threading
.get_ident()
58 last_size
= self
._thread
_sizes
.get(current_thread
, 0)
59 self
._thread
_sizes
[current_thread
] = size
60 self
._update
(size
- last_size
)
62 def _update(self
, size
: int):
63 current_time
= time
.monotonic()
65 self
.downloaded
+= size
66 self
.elapsed
= current_time
- self
._start
_time
67 if self
.total
is not None and self
.downloaded
> self
.total
:
68 self
._total
= self
.downloaded
70 if self
._last
_update
+ self
.SAMPLING_RATE
> current_time
:
72 self
._last
_update
= current_time
74 self
._times
.append(current_time
)
75 self
._downloaded
.append(self
.downloaded
)
77 offset
= bisect
.bisect_left(self
._times
, current_time
- self
.SAMPLING_WINDOW
)
78 del self
._times
[:offset
]
79 del self
._downloaded
[:offset
]
80 if len(self
._times
) < 2:
85 download_time
= current_time
- self
._times
[0]
89 self
.speed
.set((self
.downloaded
- self
._downloaded
[0]) / download_time
)
90 if self
.total
and self
.speed
.value
and self
.elapsed
> self
.GRACE_PERIOD
:
91 self
.eta
.set((self
.total
- self
.downloaded
) / self
.speed
.value
)
97 def __init__(self
, initial
: float |
None, smoothing
: float):
98 self
.value
= self
.smooth
= self
._initial
= initial
99 self
._smoothing
= smoothing
101 def set(self
, value
: float):
103 if self
.smooth
is None:
104 self
.smooth
= self
.value
106 self
.smooth
= (1 - self
._smoothing
) * value
+ self
._smoothing
* self
.smooth
109 self
.value
= self
.smooth
= self
._initial