diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py index 14823a4c6..e72354bec 100644 --- a/yt_dlp/YoutubeDL.py +++ b/yt_dlp/YoutubeDL.py @@ -107,6 +107,7 @@ from .utils import ( iri_to_uri, join_nonempty, locked_file, + make_archive_id, make_dir, make_HTTPS_handler, merge_headers, @@ -1738,8 +1739,8 @@ class YoutubeDL: # Better to do this after potentially exhausting entries ie_result['playlist_count'] = all_entries.get_full_count() - ie_copy = collections.ChainMap( - ie_result, self._playlist_infodict(ie_result, n_entries=int_or_none(n_entries))) + extra = self._playlist_infodict(ie_result, n_entries=int_or_none(n_entries)) + ie_copy = collections.ChainMap(ie_result, extra) _infojson_written = False write_playlist_files = self.params.get('allow_playlist_files', True) @@ -1785,14 +1786,14 @@ class YoutubeDL: if not lazy and 'playlist-index' in self.params.get('compat_opts', []): playlist_index = ie_result['requested_entries'][i] - extra = { + entry_copy = collections.ChainMap(entry, { **common_info, 'n_entries': int_or_none(n_entries), 'playlist_index': playlist_index, 'playlist_autonumber': i + 1, - } + }) - if self._match_entry(collections.ChainMap(entry, extra), incomplete=True) is not None: + if self._match_entry(entry_copy, incomplete=True) is not None: continue self.to_screen('[download] Downloading video %s of %s' % ( @@ -3448,7 +3449,7 @@ class YoutubeDL: break else: return - return f'{extractor.lower()} {video_id}' + return make_archive_id(extractor, video_id) def in_download_archive(self, info_dict): fn = self.params.get('download_archive') diff --git a/yt_dlp/__init__.py b/yt_dlp/__init__.py index c106c0ae7..4024b6ba1 100644 --- a/yt_dlp/__init__.py +++ b/yt_dlp/__init__.py @@ -1,4 +1,8 @@ -f'You are using an unsupported version of Python. Only Python versions 3.7 and above are supported by yt-dlp' # noqa: F541 +try: + import contextvars # noqa: F401 +except Exception: + raise Exception( + f'You are using an unsupported version of Python. Only Python versions 3.7 and above are supported by yt-dlp') # noqa: F541 __license__ = 'Public Domain' diff --git a/yt_dlp/extractor/common.py b/yt_dlp/extractor/common.py index 0ae0f4301..bf3fc8258 100644 --- a/yt_dlp/extractor/common.py +++ b/yt_dlp/extractor/common.py @@ -316,7 +316,7 @@ class InfoExtractor: live stream that goes on instead of a fixed-length video. was_live: True, False, or None (=unknown). Whether this video was originally a live stream. - live_status: None (=unknown), 'is_live', 'is_upcoming', 'was_live', 'not_live' + live_status: None (=unknown), 'is_live', 'is_upcoming', 'was_live', 'not_live', or 'post_live' (was live, but VOD is not yet processed) If absent, automatically set from is_live, was_live start_time: Time in seconds where the reproduction should start, as diff --git a/yt_dlp/extractor/commonmistakes.py b/yt_dlp/extractor/commonmistakes.py index 62bd51fd7..1d3b61c73 100644 --- a/yt_dlp/extractor/commonmistakes.py +++ b/yt_dlp/extractor/commonmistakes.py @@ -4,9 +4,7 @@ from ..utils import ExtractorError class CommonMistakesIE(InfoExtractor): IE_DESC = False # Do not list - _VALID_URL = r'''(?x) - (?:url|URL)$ - ''' + _VALID_URL = r'(?:url|URL|yt-dlp)$' _TESTS = [{ 'url': 'url', diff --git a/yt_dlp/extractor/funimation.py b/yt_dlp/extractor/funimation.py index 5881f1687..c70cf50c7 100644 --- a/yt_dlp/extractor/funimation.py +++ b/yt_dlp/extractor/funimation.py @@ -5,17 +5,18 @@ import string from .common import InfoExtractor from ..compat import compat_HTTPError from ..utils import ( + ExtractorError, determine_ext, int_or_none, join_nonempty, js_to_json, + make_archive_id, orderedSet, qualities, str_or_none, traverse_obj, try_get, urlencode_postdata, - ExtractorError, ) @@ -250,7 +251,7 @@ class FunimationIE(FunimationBaseIE): return { 'id': episode_id, - '_old_archive_ids': [initial_experience_id], + '_old_archive_ids': [make_archive_id(self, initial_experience_id)], 'display_id': display_id, 'duration': duration, 'title': episode['episodeTitle'], diff --git a/yt_dlp/extractor/genericembeds.py b/yt_dlp/extractor/genericembeds.py index f3add4794..64bd20e3a 100644 --- a/yt_dlp/extractor/genericembeds.py +++ b/yt_dlp/extractor/genericembeds.py @@ -1,4 +1,5 @@ from .common import InfoExtractor +from ..utils import make_archive_id class HTML5MediaEmbedIE(InfoExtractor): @@ -23,7 +24,7 @@ class HTML5MediaEmbedIE(InfoExtractor): 'id': f'{video_id}-{num}', 'title': f'{title} ({num})', '_old_archive_ids': [ - f'Generic {f"{video_id}-{num}" if len(entries) > 1 else video_id}', + make_archive_id('generic', f'{video_id}-{num}' if len(entries) > 1 else video_id), ], }) self._sort_formats(entry['formats']) diff --git a/yt_dlp/extractor/twitch.py b/yt_dlp/extractor/twitch.py index 7a798b912..a667d6ec2 100644 --- a/yt_dlp/extractor/twitch.py +++ b/yt_dlp/extractor/twitch.py @@ -19,6 +19,7 @@ from ..utils import ( dict_get, float_or_none, int_or_none, + make_archive_id, parse_duration, parse_iso8601, parse_qs, @@ -1166,7 +1167,7 @@ class TwitchClipsIE(TwitchBaseIE): return { 'id': clip.get('id') or video_id, - '_old_archive_ids': [f'{self.ie_key()} {old_id}'] if old_id else None, + '_old_archive_ids': [make_archive_id(self, old_id)] if old_id else None, 'display_id': video_id, 'title': clip.get('title') or video_id, 'formats': formats, diff --git a/yt_dlp/utils.py b/yt_dlp/utils.py index a5c2d10ef..c0d9c6f79 100644 --- a/yt_dlp/utils.py +++ b/yt_dlp/utils.py @@ -5707,6 +5707,11 @@ class RetryManager: time.sleep(delay) +def make_archive_id(ie, video_id): + ie_key = ie if isinstance(ie, str) else ie.ie_key() + return f'{ie_key.lower()} {video_id}' + + # Deprecated has_certifi = bool(certifi) has_websockets = bool(websockets)