From 37e329da83ae02eadb8a221b0b478c3ad902bb92 Mon Sep 17 00:00:00 2001 From: bashonly Date: Sun, 10 Aug 2025 03:17:20 -0500 Subject: [PATCH] [fd] Support `available_at` format field Authored by: bashonly --- yt_dlp/YoutubeDL.py | 4 ++-- yt_dlp/downloader/common.py | 16 ++++++++++++++-- yt_dlp/extractor/common.py | 1 + 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py index 5985d2ec76..40b8cac4bf 100644 --- a/yt_dlp/YoutubeDL.py +++ b/yt_dlp/YoutubeDL.py @@ -599,7 +599,7 @@ class YoutubeDL: _NUMERIC_FIELDS = { 'width', 'height', 'asr', 'audio_channels', 'fps', 'tbr', 'abr', 'vbr', 'filesize', 'filesize_approx', - 'timestamp', 'release_timestamp', + 'timestamp', 'release_timestamp', 'available_at', 'duration', 'view_count', 'like_count', 'dislike_count', 'repost_count', 'average_rating', 'comment_count', 'age_limit', 'start_time', 'end_time', @@ -609,7 +609,7 @@ class YoutubeDL: _format_fields = { # NB: Keep in sync with the docstring of extractor/common.py - 'url', 'manifest_url', 'manifest_stream_number', 'ext', 'format', 'format_id', 'format_note', + 'url', 'manifest_url', 'manifest_stream_number', 'ext', 'format', 'format_id', 'format_note', 'available_at', 'width', 'height', 'aspect_ratio', 'resolution', 'dynamic_range', 'tbr', 'abr', 'acodec', 'asr', 'audio_channels', 'vbr', 'fps', 'vcodec', 'container', 'filesize', 'filesize_approx', 'rows', 'columns', 'hls_media_playlist_data', 'player_url', 'protocol', 'fragment_base_url', 'fragments', 'is_from_start', 'is_dash_periods', 'request_data', diff --git a/yt_dlp/downloader/common.py b/yt_dlp/downloader/common.py index 7bc70a51a2..3dd86a971d 100644 --- a/yt_dlp/downloader/common.py +++ b/yt_dlp/downloader/common.py @@ -455,14 +455,26 @@ def download(self, filename, info_dict, subtitle=False): self._finish_multiline_status() return True, False + sleep_note = '' if subtitle: sleep_interval = self.params.get('sleep_interval_subtitles') or 0 else: min_sleep_interval = self.params.get('sleep_interval') or 0 + max_sleep_interval = self.params.get('max_sleep_interval') or 0 + + if available_at := info_dict.get('available_at'): + forced_sleep_interval = available_at - int(time.time()) + if forced_sleep_interval > min_sleep_interval: + sleep_note = 'as required by the site' + min_sleep_interval = forced_sleep_interval + if forced_sleep_interval > max_sleep_interval: + max_sleep_interval = forced_sleep_interval + sleep_interval = random.uniform( - min_sleep_interval, self.params.get('max_sleep_interval') or min_sleep_interval) + min_sleep_interval, max_sleep_interval or min_sleep_interval) + if sleep_interval > 0: - self.to_screen(f'[download] Sleeping {sleep_interval:.2f} seconds ...') + self.to_screen(f'[download] Sleeping {sleep_interval:.2f} seconds {sleep_note}...') time.sleep(sleep_interval) ret = self.real_download(filename, info_dict) diff --git a/yt_dlp/extractor/common.py b/yt_dlp/extractor/common.py index 4a4b5416d0..28ab962a3f 100644 --- a/yt_dlp/extractor/common.py +++ b/yt_dlp/extractor/common.py @@ -263,6 +263,7 @@ class InfoExtractor: * a string in the format of CLIENT[:OS] * a list or a tuple of CLIENT[:OS] strings or ImpersonateTarget instances * a boolean value; True means any impersonate target is sufficient + * available_at Unix timestamp of when a format will be available to download * downloader_options A dictionary of downloader options (For internal use only) * http_chunk_size Chunk size for HTTP downloads