diff --git a/README.md b/README.md index c98c69f418..48f8893411 100644 --- a/README.md +++ b/README.md @@ -1871,6 +1871,7 @@ The following extractors use this feature: * `pot_trace`: Enable debug logging for PO Token fetching. Either `true` or `false` (default) * `fetch_pot`: Policy to use for fetching a PO Token from providers. One of `always` (always try fetch a PO Token regardless if the client requires one for the given context), `never` (never fetch a PO Token), or `auto` (default; only fetch a PO Token if the client requires one for the given context) * `jsc_trace`: Enable debug logging for JS Challenge fetching. Either `true` or `false` (default) +* `use_ad_playback_context`: Skip preroll ads to eliminate the mandatory wait period before download. Do NOT use this when passing premium account cookies to yt-dlp, as it will result in a loss of premium formats. Only effective with the `web`, `web_safari`, `web_music` and `mweb` player clients. Either `true` or `false` (default) #### youtube-ejs * `jitless`: Run suported Javascript engines in JIT-less mode. Supported runtimes are `deno`, `node` and `bun`. Provides better security at the cost of performance/speed. Do note that `node` and `bun` are still considered unsecure. Either `true` or `false` (default) diff --git a/yt_dlp/extractor/youtube/_base.py b/yt_dlp/extractor/youtube/_base.py index 9ecce15553..114eee821b 100644 --- a/yt_dlp/extractor/youtube/_base.py +++ b/yt_dlp/extractor/youtube/_base.py @@ -104,6 +104,7 @@ INNERTUBE_CLIENTS = { }, 'INNERTUBE_CONTEXT_CLIENT_NAME': 1, 'SUPPORTS_COOKIES': True, + 'SUPPORTS_AD_PLAYBACK_CONTEXT': True, **WEB_PO_TOKEN_POLICIES, }, # Safari UA returns pre-merged video+audio 144p/240p/360p/720p/1080p HLS formats @@ -117,6 +118,7 @@ INNERTUBE_CLIENTS = { }, 'INNERTUBE_CONTEXT_CLIENT_NAME': 1, 'SUPPORTS_COOKIES': True, + 'SUPPORTS_AD_PLAYBACK_CONTEXT': True, **WEB_PO_TOKEN_POLICIES, }, 'web_embedded': { @@ -157,6 +159,7 @@ INNERTUBE_CLIENTS = { ), }, 'SUPPORTS_COOKIES': True, + 'SUPPORTS_AD_PLAYBACK_CONTEXT': True, }, # This client now requires sign-in for every video 'web_creator': { @@ -313,6 +316,7 @@ INNERTUBE_CLIENTS = { ), }, 'SUPPORTS_COOKIES': True, + 'SUPPORTS_AD_PLAYBACK_CONTEXT': True, }, 'tv': { 'INNERTUBE_CONTEXT': { @@ -412,6 +416,7 @@ def build_innertube_clients(): ytcfg.setdefault('SUBS_PO_TOKEN_POLICY', SubsPoTokenPolicy()) ytcfg.setdefault('REQUIRE_AUTH', False) ytcfg.setdefault('SUPPORTS_COOKIES', False) + ytcfg.setdefault('SUPPORTS_AD_PLAYBACK_CONTEXT', False) ytcfg.setdefault('PLAYER_PARAMS', None) ytcfg.setdefault('AUTHENTICATED_USER_AGENT', None) ytcfg['INNERTUBE_CONTEXT']['client'].setdefault('hl', 'en') diff --git a/yt_dlp/extractor/youtube/_video.py b/yt_dlp/extractor/youtube/_video.py index a792332046..24c4458d61 100644 --- a/yt_dlp/extractor/youtube/_video.py +++ b/yt_dlp/extractor/youtube/_video.py @@ -2629,16 +2629,23 @@ class YoutubeIE(YoutubeBaseInfoExtractor): return {'contentCheckOk': True, 'racyCheckOk': True} @classmethod - def _generate_player_context(cls, sts=None): + def _generate_player_context(cls, sts=None, use_ad_playback_context=False): context = { 'html5Preference': 'HTML5_PREF_WANTS', } if sts is not None: context['signatureTimestamp'] = sts + + playback_context = { + 'contentPlaybackContext': context, + } + if use_ad_playback_context: + playback_context['adPlaybackContext'] = { + 'pyv': True, + } + return { - 'playbackContext': { - 'contentPlaybackContext': context, - }, + 'playbackContext': playback_context, **cls._get_checkok_params(), } @@ -2866,7 +2873,13 @@ class YoutubeIE(YoutubeBaseInfoExtractor): yt_query['serviceIntegrityDimensions'] = {'poToken': po_token} sts = self._extract_signature_timestamp(video_id, player_url, webpage_ytcfg, fatal=False) if player_url else None - yt_query.update(self._generate_player_context(sts)) + + use_ad_playback_context = ( + self._configuration_arg('use_ad_playback_context', ['false'])[0] != 'false' + and traverse_obj(INNERTUBE_CLIENTS, (client, 'SUPPORTS_AD_PLAYBACK_CONTEXT', {bool}))) + + yt_query.update(self._generate_player_context(sts, use_ad_playback_context)) + return self._extract_response( item_id=video_id, ep='player', query=yt_query, ytcfg=player_ytcfg, headers=headers, fatal=True,