mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2024-09-20 01:11:23 +02:00
Compare commits
6 Commits
2065ccf2fc
...
8fc1d89d9e
Author | SHA1 | Date | |
---|---|---|---|
|
8fc1d89d9e | ||
|
8e9aa95c3b | ||
|
3e2ac33181 | ||
|
3227e7e610 | ||
|
b1bf57584d | ||
|
8c8420df32 |
|
@ -257,6 +257,7 @@ class FragmentFD(FileDownloader):
|
||||||
frag_total_bytes = s.get('total_bytes') or 0
|
frag_total_bytes = s.get('total_bytes') or 0
|
||||||
s['fragment_info_dict'] = s.pop('info_dict', {})
|
s['fragment_info_dict'] = s.pop('info_dict', {})
|
||||||
|
|
||||||
|
# XXX: Fragment resume is not accounted for here
|
||||||
if not ctx['live']:
|
if not ctx['live']:
|
||||||
estimated_size = (
|
estimated_size = (
|
||||||
(ctx['complete_frags_downloaded_bytes'] + frag_total_bytes)
|
(ctx['complete_frags_downloaded_bytes'] + frag_total_bytes)
|
||||||
|
@ -269,14 +270,12 @@ class FragmentFD(FileDownloader):
|
||||||
|
|
||||||
if s['status'] == 'finished':
|
if s['status'] == 'finished':
|
||||||
state['fragment_index'] += 1
|
state['fragment_index'] += 1
|
||||||
progress.thread_reset()
|
|
||||||
ctx['fragment_index'] = state['fragment_index']
|
ctx['fragment_index'] = state['fragment_index']
|
||||||
state['downloaded_bytes'] = ctx['complete_frags_downloaded_bytes'] = progress.downloaded
|
progress.thread_reset()
|
||||||
ctx['speed'] = state['speed'] = progress.speed
|
|
||||||
else:
|
state['downloaded_bytes'] = ctx['complete_frags_downloaded_bytes'] = progress.downloaded
|
||||||
state['downloaded_bytes'] = progress.downloaded
|
ctx['speed'] = state['speed'] = progress.smooth_speed
|
||||||
ctx['speed'] = state['speed'] = progress.speed
|
state['eta'] = progress.eta
|
||||||
state['eta'] = progress.eta
|
|
||||||
|
|
||||||
self._hook_progress(state, info_dict)
|
self._hook_progress(state, info_dict)
|
||||||
|
|
||||||
|
|
|
@ -4,44 +4,38 @@ import bisect
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
# FIXME: monotonic has terrible resolution on Windows
|
|
||||||
TIME_PROVIDER = time.perf_counter
|
|
||||||
|
|
||||||
|
|
||||||
# XXX: Fragment resume is not accounted for here
|
|
||||||
class ProgressCalculator:
|
class ProgressCalculator:
|
||||||
# Time to calculate the average over (in seconds)
|
# Time to calculate the speed over (in nanoseconds)
|
||||||
WINDOW_SIZE = 2.0
|
SAMPLING_WINDOW = 1_000_000_000
|
||||||
# Minimum time before we add another datapoint (in seconds)
|
# Factor for the exponential moving average (from 0 = prev to 1 = current)
|
||||||
# This is *NOT* the same as the time between a progress change
|
SMOOTHING_FACTOR = 0.3
|
||||||
UPDATE_TIME = 0.1
|
|
||||||
|
|
||||||
def __init__(self, initial):
|
def __init__(self, initial: int):
|
||||||
self.downloaded = initial if initial else 0
|
self.downloaded = initial or 0
|
||||||
|
|
||||||
|
self.elapsed: float = 0
|
||||||
|
self.speed: float = 0
|
||||||
|
self.smooth_speed: int = 0
|
||||||
|
self.eta: float | None = None
|
||||||
|
|
||||||
self.elapsed = 0
|
|
||||||
self.speed = None
|
|
||||||
self.eta = None
|
|
||||||
self._total = None
|
self._total = None
|
||||||
|
self._start_time = time.monotonic_ns()
|
||||||
|
|
||||||
self._lock = threading.Lock()
|
self._lock = threading.Lock()
|
||||||
self._start_time = TIME_PROVIDER()
|
|
||||||
self._last_update = self._start_time
|
|
||||||
|
|
||||||
self._times: list[float] = []
|
|
||||||
self._speeds: list[int] = []
|
|
||||||
|
|
||||||
self._thread_updates: dict[int, float] = {}
|
|
||||||
self._thread_sizes: dict[int, int] = {}
|
self._thread_sizes: dict[int, int] = {}
|
||||||
|
|
||||||
|
self._times = [self._start_time]
|
||||||
|
self._downloaded = [self.downloaded]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def total(self):
|
def total(self):
|
||||||
return self._total
|
return self._total
|
||||||
|
|
||||||
@total.setter
|
@total.setter
|
||||||
def total(self, value):
|
def total(self, value: int | None):
|
||||||
with self._lock:
|
with self._lock:
|
||||||
if not value or value <= 0.01:
|
if not value:
|
||||||
value = None
|
value = None
|
||||||
elif value < self.downloaded:
|
elif value < self.downloaded:
|
||||||
value = self.downloaded
|
value = self.downloaded
|
||||||
|
@ -52,7 +46,6 @@ class ProgressCalculator:
|
||||||
current_thread = threading.get_ident()
|
current_thread = threading.get_ident()
|
||||||
with self._lock:
|
with self._lock:
|
||||||
self._thread_sizes[current_thread] = 0
|
self._thread_sizes[current_thread] = 0
|
||||||
self._thread_updates[current_thread] = TIME_PROVIDER()
|
|
||||||
|
|
||||||
def update(self, size: int | None):
|
def update(self, size: int | None):
|
||||||
if not size:
|
if not size:
|
||||||
|
@ -61,39 +54,34 @@ class ProgressCalculator:
|
||||||
current_thread = threading.get_ident()
|
current_thread = threading.get_ident()
|
||||||
|
|
||||||
with self._lock:
|
with self._lock:
|
||||||
current_time = TIME_PROVIDER()
|
last_size = self._thread_sizes.get(current_thread, 0)
|
||||||
last_size = self._thread_sizes.get(current_thread) or 0
|
|
||||||
last_update = self._thread_updates.get(current_thread) or self._start_time
|
|
||||||
chunk = size - last_size
|
|
||||||
print(f' [{threading.get_ident()}] {last_update} -> {current_time} ({chunk}B)')
|
|
||||||
|
|
||||||
update_time = self._update(chunk, current_time, last_update)
|
|
||||||
if update_time:
|
|
||||||
self._thread_updates[current_thread] = current_time
|
|
||||||
self._thread_sizes[current_thread] = size
|
self._thread_sizes[current_thread] = size
|
||||||
|
self._update(size - last_size)
|
||||||
|
|
||||||
|
def _update(self, size: int):
|
||||||
|
current_time = time.monotonic_ns()
|
||||||
|
|
||||||
def _update(self, size, current_time, last_update):
|
|
||||||
self.downloaded += size
|
self.downloaded += size
|
||||||
self.elapsed = current_time - self._start_time
|
self.elapsed = (current_time - self._start_time) / 1_000_000_000
|
||||||
if self.total is not None and self.downloaded > self.total:
|
if self.total is not None and self.downloaded > self.total:
|
||||||
self._total = self.downloaded
|
self._total = self.downloaded
|
||||||
|
|
||||||
self._times.append(current_time)
|
self._times.append(current_time)
|
||||||
self._speeds.append(size / (current_time - last_update))
|
self._downloaded.append(self.downloaded)
|
||||||
|
|
||||||
if current_time - self.UPDATE_TIME <= last_update:
|
offset = bisect.bisect_left(self._times, current_time - self.SAMPLING_WINDOW)
|
||||||
return
|
|
||||||
self._last_update = current_time
|
|
||||||
|
|
||||||
offset = bisect.bisect_left(self._times, current_time - self.WINDOW_SIZE)
|
|
||||||
del self._times[:offset]
|
del self._times[:offset]
|
||||||
del self._speeds[:offset]
|
del self._downloaded[:offset]
|
||||||
|
|
||||||
weights = tuple(1 + (point - current_time) / self.WINDOW_SIZE for point in self._times)
|
download_time = current_time - self._times[0]
|
||||||
# Same as `statistics.fmean(self.data_points, weights)`, but weights is >=3.11
|
if not download_time:
|
||||||
self.speed = sum(map(float.__mul__, self._speeds, weights)) / sum(weights)
|
return
|
||||||
|
downloaded_bytes = self.downloaded - self._downloaded[0]
|
||||||
|
|
||||||
|
self.speed = downloaded_bytes * 1_000_000_000 / download_time
|
||||||
|
self.smooth_speed = int(self.SMOOTHING_FACTOR * self.speed + (1 - self.SMOOTHING_FACTOR) * self.smooth_speed)
|
||||||
|
|
||||||
if not self.total:
|
if not self.total:
|
||||||
self.eta = None
|
self.eta = None
|
||||||
else:
|
elif self.speed:
|
||||||
self.eta = (self.total - self.downloaded) / self.speed
|
self.eta = (self.total - self.downloaded) / self.speed
|
||||||
|
|
Loading…
Reference in New Issue
Block a user