From 51eeb4a9502eb77b8e68761fc7891c132880a16b Mon Sep 17 00:00:00 2001 From: Jules-A <1760158+Jules-A@users.noreply.github.com> Date: Sat, 30 Sep 2023 19:18:26 +0800 Subject: [PATCH] Add files via upload --- yt_dlp/utils/_utils.py | 36 +++++++++++++++ yt_dlp/utils/progress.py | 99 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 yt_dlp/utils/progress.py diff --git a/yt_dlp/utils/_utils.py b/yt_dlp/utils/_utils.py index ba6242380638..e183237cf303 100644 --- a/yt_dlp/utils/_utils.py +++ b/yt_dlp/utils/_utils.py @@ -4812,6 +4812,41 @@ def determine_file_encoding(data): return mobj.group(1).decode() if mobj else None, 0 +class TimeKeeper: + def __init__(self): + self._last_ts = time.monotonic() + + def elapsed(self, seconds: float) -> bool: + time_now = time.monotonic() + dif = time_now - self._last_ts + if dif > seconds: + self._last_ts = time_now + return True + return False + + +class TsTracker: + def __init__(self, updates, initial_bytes=0): + self._ts_data = collections.deque([[0] * 2] * updates, maxlen=updates) + self._initial_bytes = initial_bytes + self._started = time.monotonic() + + def update(self, bytes_down): + time_now = time.monotonic() + session_down = float(bytes_down - self._initial_bytes) + self._ts_data.append((time_now, bytes_down)) + time_dif = time_now - self._ts_data[0][0] + + if self._ts_data[0][1] > 0 and time_dif > 0: + speed = float(bytes_down - self._ts_data[0][1]) / time_dif + elif session_down > 0: + speed = session_down / (time_now - self._started) + else: + speed = None + + return speed + + class Config: own_args = None parsed_args = None @@ -5173,6 +5208,7 @@ def orderedSet_from_options(options, alias_dict, *, use_regex=False, start=None) return orderedSet(requested) + # TODO: Rewrite class FormatSorter: regex = r' *((?P\+)?(?P[a-zA-Z0-9_]+)((?P[~:])(?P.*?))?)? *$' diff --git a/yt_dlp/utils/progress.py b/yt_dlp/utils/progress.py new file mode 100644 index 000000000000..60646249538c --- /dev/null +++ b/yt_dlp/utils/progress.py @@ -0,0 +1,99 @@ +from __future__ import annotations + +import bisect +# import operator +import statistics +import threading +import time +from dataclasses import dataclass + + +@dataclass +class ThreadInfo: + speed: float + last_update: float + last_size: int + times: list[float] + speeds: list[float] + + +class ProgressCalculator: + # Time to calculate the average over (in seconds) + WINDOW_SIZE = 2.0 + # Minimum time before we add another datapoint (in seconds) + # This is *NOT* the same as the time between a progress change + UPDATE_TIME = 0.1 + + def __init__(self, initial): + self.downloaded = initial if initial else 0 + + self.elapsed = 0 + self.speed = None + self.eta = None + self._total = None + + self._start_time = time.monotonic() + self._thread_infos: dict[int, ThreadInfo] = {} + self._lock = threading.Lock() + + @property + def total(self): + return self._total + + @total.setter + def total(self, value): + with self._lock: + if not value or value <= 0.01: + value = None + elif value < self.downloaded: + value = self.downloaded + + self._total = value + + def update(self, size): + with self._lock: + return self._update(size) + + def _update(self, size): + current_thread = threading.get_ident() + thread_info = self._thread_infos.get(current_thread) + if not thread_info: + thread_info = ThreadInfo(self._start_time, 0, 0, [], []) + self._thread_infos[current_thread] = thread_info + + last_size = thread_info.last_size + if size < last_size: + chunk = size + elif size > last_size: + chunk = size - last_size + else: + return + self.downloaded += chunk + if self.total and self.downloaded > self.total: + self._total = self.downloaded + thread_info.last_size = size + + current_time = time.monotonic() + self.elapsed = current_time - self._start_time + + last_update = thread_info.last_update + if current_time - self.UPDATE_TIME <= last_update: + return + thread_info.last_update = current_time + + offset = bisect.bisect_left(thread_info.times, current_time - self.WINDOW_SIZE) + del thread_info.times[:offset] + del thread_info.speeds[:offset] + thread_info.times.append(current_time) + thread_info.speeds.append(size / (current_time - last_update)) + + # weights = tuple(1 + (point - current_time) / self.WINDOW_SIZE for point in thread_info.times) + # Same as `statistics.fmean(self.data_points, weights)`, but weights is >=3.11 + # thread_info.speed = sum(map(operator.mul, thread_info.speeds, weights)) / sum(weights) + thread_info.speed = statistics.fmean(thread_info.speeds) + self.speed = sum(info.speed for info in self._thread_infos.values()) + + if not self.total: + self.eta = None + else: + self.eta = (self.total - self.downloaded) / self.speed