From f30f54e35a5d9b8a7b5160d2359321fa2ab6e106 Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Mon, 3 Mar 2025 15:52:20 +0100 Subject: [PATCH] Add live download status updates. --- src/ytplom/misc.py | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/ytplom/misc.py b/src/ytplom/misc.py index aade5de..26d1369 100644 --- a/src/ytplom/misc.py +++ b/src/ytplom/misc.py @@ -74,7 +74,8 @@ LEGAL_EXTENSIONS = {'webm', 'mp4', 'mkv'} FILE_FLAGS: dict[FlagName, FlagsInt] = { FlagName('do not sync'): FlagsInt(1 << 62) } -ONE_MILLION = 1000 * 1000 +MILLION = 1000 * 1000 +MEGA = 1024 * 1024 def ensure_expected_dirs(expected_dirs: list[Path]) -> None: @@ -359,7 +360,7 @@ class VideoFile(DbData): self.yt_id = yt_id self.duration_ms = ( duration_ms if duration_ms >= 0 - else int(ONE_MILLION * Decimal( + else int(MILLION * Decimal( ffprobe(self.full_path)['format']['duration']))) self.last_update = last_update if last_update else _now_string() self._hash_on_last_update = hash(self) @@ -457,14 +458,14 @@ class VideoFile(DbData): """If file at .full_path, return its megabytes size, else -1.""" if not self.full_path.is_file(): return -1 - return self.full_path.stat().st_size / (1024 * 1024) + return self.full_path.stat().st_size / MEGA def duration(self, short: bool = False) -> str: """Return human-friendly formatting of .duration_ms.""" if self.duration_ms < 0: return '?' - seconds_str = f'{_readable_seconds(self.duration_ms // ONE_MILLION)}' - milliseconds_str = f'{self.duration_ms // ONE_MILLION}'.rjust(6, '0') + seconds_str = f'{_readable_seconds(self.duration_ms // MILLION)}' + milliseconds_str = f'{self.duration_ms // MILLION}'.rjust(6, '0') return seconds_str if short else f'{seconds_str}.{milliseconds_str}' @property @@ -875,10 +876,29 @@ class DownloadsManager: def _download_next(self) -> None: if self._to_download: + downloaded_before: int = 0 + estimated_total: int = 0 + estimated_total_mb: float + + def hook(d) -> None: + nonlocal downloaded_before + if d['status'] in {'downloading', 'finished'}: + downloaded_i = d['downloaded_bytes'] + downloaded_mb = (downloaded_i + downloaded_before) / MEGA + msg = f'{int(100 * downloaded_mb/estimated_total_mb)}%: '\ + f'{downloaded_mb:5.1f}/{estimated_total_mb:.1f}' + self._update_status(video_id, f'downloading: {msg} MB') + if d['status'] == 'finished': + downloaded_before += downloaded_i + video_id = self._to_download.pop(0) - with YoutubeDL(YT_DL_PARAMS) as ydl: - self._update_status(video_id, 'downloading') - ydl.download([f'{YOUTUBE_URL_PREFIX}{video_id}']) + url = f'{YOUTUBE_URL_PREFIX}{video_id}' + with YoutubeDL(YT_DL_PARAMS | {'progress_hooks': [hook]}) as ydl: + info = ydl.sanitize_info(ydl.extract_info(url, download=False)) + for requested in info['requested_formats']: + estimated_total += requested['filesize_approx'] + estimated_total_mb = estimated_total / MEGA + ydl.download(url) self._sync_db() def start_thread(self) -> None: -- 2.30.2