mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2024-11-30 18:34:41 +01:00
[ie/youtube] Player client maintenance (#10573)
- Add clients: android_producer, android_testsuite, android_vr, tv, web_safari - Remove obsolete clients: android_embedded, ios_embedded, *_embedscreen Authored by: bashonly
This commit is contained in:
parent
fe15d3178e
commit
0e539617a4
@ -1758,7 +1758,7 @@ # Replace all spaces and "_" in title and uploader with a `-`
|
|||||||
|
|
||||||
# EXTRACTOR ARGUMENTS
|
# EXTRACTOR ARGUMENTS
|
||||||
|
|
||||||
Some extractors accept additional arguments which can be passed using `--extractor-args KEY:ARGS`. `ARGS` is a `;` (semicolon) separated string of `ARG=VAL1,VAL2`. E.g. `--extractor-args "youtube:player-client=android_embedded,web;formats=incomplete" --extractor-args "funimation:version=uncut"`
|
Some extractors accept additional arguments which can be passed using `--extractor-args KEY:ARGS`. `ARGS` is a `;` (semicolon) separated string of `ARG=VAL1,VAL2`. E.g. `--extractor-args "youtube:player-client=mediaconnect,web;formats=incomplete" --extractor-args "funimation:version=uncut"`
|
||||||
|
|
||||||
Note: In CLI, `ARG` can use `-` instead of `_`; e.g. `youtube:player-client"` becomes `youtube:player_client"`
|
Note: In CLI, `ARG` can use `-` instead of `_`; e.g. `youtube:player-client"` becomes `youtube:player_client"`
|
||||||
|
|
||||||
@ -1767,7 +1767,7 @@ # EXTRACTOR ARGUMENTS
|
|||||||
#### youtube
|
#### youtube
|
||||||
* `lang`: Prefer translated metadata (`title`, `description` etc) of this language code (case-sensitive). By default, the video primary language metadata is preferred, with a fallback to `en` translated. See [youtube.py](https://github.com/yt-dlp/yt-dlp/blob/c26f9b991a0681fd3ea548d535919cec1fbbd430/yt_dlp/extractor/youtube.py#L381-L390) for list of supported content language codes
|
* `lang`: Prefer translated metadata (`title`, `description` etc) of this language code (case-sensitive). By default, the video primary language metadata is preferred, with a fallback to `en` translated. See [youtube.py](https://github.com/yt-dlp/yt-dlp/blob/c26f9b991a0681fd3ea548d535919cec1fbbd430/yt_dlp/extractor/youtube.py#L381-L390) for list of supported content language codes
|
||||||
* `skip`: One or more of `hls`, `dash` or `translated_subs` to skip extraction of the m3u8 manifests, dash manifests and [auto-translated subtitles](https://github.com/yt-dlp/yt-dlp/issues/4090#issuecomment-1158102032) respectively
|
* `skip`: One or more of `hls`, `dash` or `translated_subs` to skip extraction of the m3u8 manifests, dash manifests and [auto-translated subtitles](https://github.com/yt-dlp/yt-dlp/issues/4090#issuecomment-1158102032) respectively
|
||||||
* `player_client`: Clients to extract video data from. The main clients are `web`, `ios` and `android`, with variants `_music`, `_embedded`, `_embedscreen`, `_creator` (e.g. `web_embedded`); and `mediaconnect`, `mweb`, `mweb_embedscreen` and `tv_embedded` (agegate bypass) with no variants. By default, `ios,web` is used, but `tv_embedded` and `creator` variants are added as required for age-gated videos. Similarly, the music variants are added for `music.youtube.com` urls. The `android` clients will always be given lowest priority since their formats are broken. You can use `all` to use all the clients, and `default` for the default clients.
|
* `player_client`: Clients to extract video data from. The main clients are `web`, `ios` and `android`, with variants `_music` and `_creator` (e.g. `web_creator`); and `mediaconnect`, `mweb`, `android_producer`, `android_testsuite`, `android_vr`, `web_safari`, `web_embedded`, `tv` and `tv_embedded` with no variants. By default, `ios,web` is used, but `tv_embedded` and `_creator` variants are added as required for age-gated videos. Similarly, the music variants are added for `music.youtube.com` urls. Most `android` clients will be given lowest priority since their formats are broken. You can use `all` to use all the clients, and `default` for the default clients.
|
||||||
* `player_skip`: Skip some network requests that are generally needed for robust extraction. One or more of `configs` (skip client configs), `webpage` (skip initial webpage), `js` (skip js player). While these options can help reduce the number of requests needed or avoid some rate-limiting, they could cause some issues. See [#860](https://github.com/yt-dlp/yt-dlp/pull/860) for more details
|
* `player_skip`: Skip some network requests that are generally needed for robust extraction. One or more of `configs` (skip client configs), `webpage` (skip initial webpage), `js` (skip js player). While these options can help reduce the number of requests needed or avoid some rate-limiting, they could cause some issues. See [#860](https://github.com/yt-dlp/yt-dlp/pull/860) for more details
|
||||||
* `player_params`: YouTube player parameters to use for player requests. Will overwrite any default ones set by yt-dlp.
|
* `player_params`: YouTube player parameters to use for player requests. Will overwrite any default ones set by yt-dlp.
|
||||||
* `comment_sort`: `top` or `new` (default) - choose comment sorting mode (on YouTube's side)
|
* `comment_sort`: `top` or `new` (default) - choose comment sorting mode (on YouTube's side)
|
||||||
@ -1775,7 +1775,7 @@ #### youtube
|
|||||||
* E.g. `all,all,1000,10` will get a maximum of 1000 replies total, with up to 10 replies per thread. `1000,all,100` will get a maximum of 1000 comments, with a maximum of 100 replies total
|
* E.g. `all,all,1000,10` will get a maximum of 1000 replies total, with up to 10 replies per thread. `1000,all,100` will get a maximum of 1000 comments, with a maximum of 100 replies total
|
||||||
* `formats`: Change the types of formats to return. `dashy` (convert HTTP to DASH), `duplicate` (identical content but different URLs or protocol; includes `dashy`), `incomplete` (cannot be downloaded completely - live dash and post-live m3u8)
|
* `formats`: Change the types of formats to return. `dashy` (convert HTTP to DASH), `duplicate` (identical content but different URLs or protocol; includes `dashy`), `incomplete` (cannot be downloaded completely - live dash and post-live m3u8)
|
||||||
* `innertube_host`: Innertube API host to use for all API requests; e.g. `studio.youtube.com`, `youtubei.googleapis.com`. Note that cookies exported from one subdomain will not work on others
|
* `innertube_host`: Innertube API host to use for all API requests; e.g. `studio.youtube.com`, `youtubei.googleapis.com`. Note that cookies exported from one subdomain will not work on others
|
||||||
* `innertube_key`: Innertube API key to use for all API requests
|
* `innertube_key`: Innertube API key to use for all API requests. By default, no API key is used
|
||||||
* `raise_incomplete_data`: `Incomplete Data Received` raises an error instead of reporting a warning
|
* `raise_incomplete_data`: `Incomplete Data Received` raises an error instead of reporting a warning
|
||||||
|
|
||||||
#### youtubetab (YouTube playlists, channels, feeds, etc.)
|
#### youtubetab (YouTube playlists, channels, feeds, etc.)
|
||||||
|
@ -72,133 +72,169 @@
|
|||||||
# any clients starting with _ cannot be explicitly requested by the user
|
# any clients starting with _ cannot be explicitly requested by the user
|
||||||
INNERTUBE_CLIENTS = {
|
INNERTUBE_CLIENTS = {
|
||||||
'web': {
|
'web': {
|
||||||
'INNERTUBE_API_KEY': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8',
|
|
||||||
'INNERTUBE_CONTEXT': {
|
'INNERTUBE_CONTEXT': {
|
||||||
'client': {
|
'client': {
|
||||||
'clientName': 'WEB',
|
'clientName': 'WEB',
|
||||||
'clientVersion': '2.20220801.00.00',
|
'clientVersion': '2.20240726.00.00',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 1,
|
||||||
|
},
|
||||||
|
# Safari UA returns pre-merged video+audio 144p/240p/360p/720p/1080p HLS formats
|
||||||
|
'web_safari': {
|
||||||
|
'INNERTUBE_CONTEXT': {
|
||||||
|
'client': {
|
||||||
|
'clientName': 'WEB',
|
||||||
|
'clientVersion': '2.20240726.00.00',
|
||||||
|
'userAgent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.5 Safari/605.1.15,gzip(gfe)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'INNERTUBE_CONTEXT_CLIENT_NAME': 1,
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 1,
|
||||||
},
|
},
|
||||||
'web_embedded': {
|
'web_embedded': {
|
||||||
'INNERTUBE_API_KEY': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8',
|
|
||||||
'INNERTUBE_CONTEXT': {
|
'INNERTUBE_CONTEXT': {
|
||||||
'client': {
|
'client': {
|
||||||
'clientName': 'WEB_EMBEDDED_PLAYER',
|
'clientName': 'WEB_EMBEDDED_PLAYER',
|
||||||
'clientVersion': '1.20220731.00.00',
|
'clientVersion': '1.20240723.01.00',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'INNERTUBE_CONTEXT_CLIENT_NAME': 56,
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 56,
|
||||||
},
|
},
|
||||||
'web_music': {
|
'web_music': {
|
||||||
'INNERTUBE_API_KEY': 'AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30',
|
|
||||||
'INNERTUBE_HOST': 'music.youtube.com',
|
'INNERTUBE_HOST': 'music.youtube.com',
|
||||||
'INNERTUBE_CONTEXT': {
|
'INNERTUBE_CONTEXT': {
|
||||||
'client': {
|
'client': {
|
||||||
'clientName': 'WEB_REMIX',
|
'clientName': 'WEB_REMIX',
|
||||||
'clientVersion': '1.20220727.01.00',
|
'clientVersion': '1.20240724.00.00',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'INNERTUBE_CONTEXT_CLIENT_NAME': 67,
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 67,
|
||||||
},
|
},
|
||||||
'web_creator': {
|
'web_creator': {
|
||||||
'INNERTUBE_API_KEY': 'AIzaSyBUPetSUmoZL-OhlxA7wSac5XinrygCqMo',
|
|
||||||
'INNERTUBE_CONTEXT': {
|
'INNERTUBE_CONTEXT': {
|
||||||
'client': {
|
'client': {
|
||||||
'clientName': 'WEB_CREATOR',
|
'clientName': 'WEB_CREATOR',
|
||||||
'clientVersion': '1.20220726.00.00',
|
'clientVersion': '1.20240723.03.00',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'INNERTUBE_CONTEXT_CLIENT_NAME': 62,
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 62,
|
||||||
},
|
},
|
||||||
'android': {
|
'android': {
|
||||||
'INNERTUBE_API_KEY': 'AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w',
|
|
||||||
'INNERTUBE_CONTEXT': {
|
'INNERTUBE_CONTEXT': {
|
||||||
'client': {
|
'client': {
|
||||||
'clientName': 'ANDROID',
|
'clientName': 'ANDROID',
|
||||||
'clientVersion': '19.09.37',
|
'clientVersion': '19.29.37',
|
||||||
'androidSdkVersion': 30,
|
'androidSdkVersion': 30,
|
||||||
'userAgent': 'com.google.android.youtube/19.09.37 (Linux; U; Android 11) gzip',
|
'userAgent': 'com.google.android.youtube/19.29.37 (Linux; U; Android 11) gzip',
|
||||||
|
'osName': 'Android',
|
||||||
|
'osVersion': '11',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'INNERTUBE_CONTEXT_CLIENT_NAME': 3,
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 3,
|
||||||
'REQUIRE_JS_PLAYER': False,
|
'REQUIRE_JS_PLAYER': False,
|
||||||
},
|
},
|
||||||
'android_embedded': {
|
|
||||||
'INNERTUBE_API_KEY': 'AIzaSyCjc_pVEDi4qsv5MtC2dMXzpIaDoRFLsxw',
|
|
||||||
'INNERTUBE_CONTEXT': {
|
|
||||||
'client': {
|
|
||||||
'clientName': 'ANDROID_EMBEDDED_PLAYER',
|
|
||||||
'clientVersion': '19.09.37',
|
|
||||||
'androidSdkVersion': 30,
|
|
||||||
'userAgent': 'com.google.android.youtube/19.09.37 (Linux; U; Android 11) gzip',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'INNERTUBE_CONTEXT_CLIENT_NAME': 55,
|
|
||||||
'REQUIRE_JS_PLAYER': False,
|
|
||||||
},
|
|
||||||
'android_music': {
|
'android_music': {
|
||||||
'INNERTUBE_API_KEY': 'AIzaSyAOghZGza2MQSZkY_zfZ370N-PUdXEo8AI',
|
|
||||||
'INNERTUBE_CONTEXT': {
|
'INNERTUBE_CONTEXT': {
|
||||||
'client': {
|
'client': {
|
||||||
'clientName': 'ANDROID_MUSIC',
|
'clientName': 'ANDROID_MUSIC',
|
||||||
'clientVersion': '6.42.52',
|
'clientVersion': '7.11.50',
|
||||||
'androidSdkVersion': 30,
|
'androidSdkVersion': 30,
|
||||||
'userAgent': 'com.google.android.apps.youtube.music/6.42.52 (Linux; U; Android 11) gzip',
|
'userAgent': 'com.google.android.apps.youtube.music/7.11.50 (Linux; U; Android 11) gzip',
|
||||||
|
'osName': 'Android',
|
||||||
|
'osVersion': '11',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'INNERTUBE_CONTEXT_CLIENT_NAME': 21,
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 21,
|
||||||
'REQUIRE_JS_PLAYER': False,
|
'REQUIRE_JS_PLAYER': False,
|
||||||
},
|
},
|
||||||
'android_creator': {
|
'android_creator': {
|
||||||
'INNERTUBE_API_KEY': 'AIzaSyD_qjV8zaaUMehtLkrKFgVeSX_Iqbtyws8',
|
|
||||||
'INNERTUBE_CONTEXT': {
|
'INNERTUBE_CONTEXT': {
|
||||||
'client': {
|
'client': {
|
||||||
'clientName': 'ANDROID_CREATOR',
|
'clientName': 'ANDROID_CREATOR',
|
||||||
'clientVersion': '22.30.100',
|
'clientVersion': '24.30.100',
|
||||||
'androidSdkVersion': 30,
|
'androidSdkVersion': 30,
|
||||||
'userAgent': 'com.google.android.apps.youtube.creator/22.30.100 (Linux; U; Android 11) gzip',
|
'userAgent': 'com.google.android.apps.youtube.creator/24.30.100 (Linux; U; Android 11) gzip',
|
||||||
|
'osName': 'Android',
|
||||||
|
'osVersion': '11',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'INNERTUBE_CONTEXT_CLIENT_NAME': 14,
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 14,
|
||||||
'REQUIRE_JS_PLAYER': False,
|
'REQUIRE_JS_PLAYER': False,
|
||||||
},
|
},
|
||||||
|
# YouTube Kids videos aren't returned on this client for some reason
|
||||||
|
'android_vr': {
|
||||||
|
'INNERTUBE_CONTEXT': {
|
||||||
|
'client': {
|
||||||
|
'clientName': 'ANDROID_VR',
|
||||||
|
'clientVersion': '1.57.29',
|
||||||
|
'deviceMake': 'Oculus',
|
||||||
|
'deviceModel': 'Quest 3',
|
||||||
|
'androidSdkVersion': 32,
|
||||||
|
'userAgent': 'com.google.android.apps.youtube.vr.oculus/1.57.29 (Linux; U; Android 12L; eureka-user Build/SQ3A.220605.009.A1) gzip',
|
||||||
|
'osName': 'Android',
|
||||||
|
'osVersion': '12L',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 28,
|
||||||
|
'REQUIRE_JS_PLAYER': False,
|
||||||
|
},
|
||||||
|
'android_testsuite': {
|
||||||
|
'INNERTUBE_CONTEXT': {
|
||||||
|
'client': {
|
||||||
|
'clientName': 'ANDROID_TESTSUITE',
|
||||||
|
'clientVersion': '1.9',
|
||||||
|
'androidSdkVersion': 30,
|
||||||
|
'userAgent': 'com.google.android.youtube/1.9 (Linux; U; Android 11) gzip',
|
||||||
|
'osName': 'Android',
|
||||||
|
'osVersion': '11',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 30,
|
||||||
|
'REQUIRE_JS_PLAYER': False,
|
||||||
|
'PLAYER_PARAMS': '2AMB',
|
||||||
|
},
|
||||||
|
# This client only has legacy formats and storyboards
|
||||||
|
'android_producer': {
|
||||||
|
'INNERTUBE_CONTEXT': {
|
||||||
|
'client': {
|
||||||
|
'clientName': 'ANDROID_PRODUCER',
|
||||||
|
'clientVersion': '0.111.1',
|
||||||
|
'androidSdkVersion': 30,
|
||||||
|
'userAgent': 'com.google.android.apps.youtube.producer/0.111.1 (Linux; U; Android 11) gzip',
|
||||||
|
'osName': 'Android',
|
||||||
|
'osVersion': '11',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 91,
|
||||||
|
'REQUIRE_JS_PLAYER': False,
|
||||||
|
},
|
||||||
# iOS clients have HLS live streams. Setting device model to get 60fps formats.
|
# iOS clients have HLS live streams. Setting device model to get 60fps formats.
|
||||||
# See: https://github.com/TeamNewPipe/NewPipeExtractor/issues/680#issuecomment-1002724558
|
# See: https://github.com/TeamNewPipe/NewPipeExtractor/issues/680#issuecomment-1002724558
|
||||||
'ios': {
|
'ios': {
|
||||||
'INNERTUBE_API_KEY': 'AIzaSyB-63vPrdThhKuerbB2N_l7Kwwcxj6yUAc',
|
|
||||||
'INNERTUBE_CONTEXT': {
|
'INNERTUBE_CONTEXT': {
|
||||||
'client': {
|
'client': {
|
||||||
'clientName': 'IOS',
|
'clientName': 'IOS',
|
||||||
'clientVersion': '19.09.3',
|
'clientVersion': '19.29.1',
|
||||||
'deviceModel': 'iPhone14,3',
|
'deviceMake': 'Apple',
|
||||||
'userAgent': 'com.google.ios.youtube/19.09.3 (iPhone14,3; U; CPU iOS 15_6 like Mac OS X)',
|
'deviceModel': 'iPhone16,2',
|
||||||
|
'userAgent': 'com.google.ios.youtube/19.29.1 (iPhone16,2; U; CPU iOS 17_5_1 like Mac OS X;)',
|
||||||
|
'osName': 'iPhone',
|
||||||
|
'osVersion': '17.5.1.21F90',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'INNERTUBE_CONTEXT_CLIENT_NAME': 5,
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 5,
|
||||||
'REQUIRE_JS_PLAYER': False,
|
'REQUIRE_JS_PLAYER': False,
|
||||||
},
|
},
|
||||||
'ios_embedded': {
|
|
||||||
'INNERTUBE_CONTEXT': {
|
|
||||||
'client': {
|
|
||||||
'clientName': 'IOS_MESSAGES_EXTENSION',
|
|
||||||
'clientVersion': '19.09.3',
|
|
||||||
'deviceModel': 'iPhone14,3',
|
|
||||||
'userAgent': 'com.google.ios.youtube/19.09.3 (iPhone14,3; U; CPU iOS 15_6 like Mac OS X)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
'INNERTUBE_CONTEXT_CLIENT_NAME': 66,
|
|
||||||
'REQUIRE_JS_PLAYER': False,
|
|
||||||
},
|
|
||||||
'ios_music': {
|
'ios_music': {
|
||||||
'INNERTUBE_API_KEY': 'AIzaSyBAETezhkwP0ZWA02RsqT1zu78Fpt0bC_s',
|
|
||||||
'INNERTUBE_CONTEXT': {
|
'INNERTUBE_CONTEXT': {
|
||||||
'client': {
|
'client': {
|
||||||
'clientName': 'IOS_MUSIC',
|
'clientName': 'IOS_MUSIC',
|
||||||
'clientVersion': '6.33.3',
|
'clientVersion': '7.08.2',
|
||||||
'deviceModel': 'iPhone14,3',
|
'deviceMake': 'Apple',
|
||||||
'userAgent': 'com.google.ios.youtubemusic/6.33.3 (iPhone14,3; U; CPU iOS 15_6 like Mac OS X)',
|
'deviceModel': 'iPhone16,2',
|
||||||
|
'userAgent': 'com.google.ios.youtubemusic/7.08.2 (iPhone16,2; U; CPU iOS 17_5_1 like Mac OS X;)',
|
||||||
|
'osName': 'iPhone',
|
||||||
|
'osVersion': '17.5.1.21F90',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'INNERTUBE_CONTEXT_CLIENT_NAME': 26,
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 26,
|
||||||
@ -208,9 +244,12 @@
|
|||||||
'INNERTUBE_CONTEXT': {
|
'INNERTUBE_CONTEXT': {
|
||||||
'client': {
|
'client': {
|
||||||
'clientName': 'IOS_CREATOR',
|
'clientName': 'IOS_CREATOR',
|
||||||
'clientVersion': '22.33.101',
|
'clientVersion': '24.30.100',
|
||||||
'deviceModel': 'iPhone14,3',
|
'deviceMake': 'Apple',
|
||||||
'userAgent': 'com.google.ios.ytcreator/22.33.101 (iPhone14,3; U; CPU iOS 15_6 like Mac OS X)',
|
'deviceModel': 'iPhone16,2',
|
||||||
|
'userAgent': 'com.google.ios.ytcreator/24.30.100 (iPhone16,2; U; CPU iOS 17_5_1 like Mac OS X;)',
|
||||||
|
'osName': 'iPhone',
|
||||||
|
'osVersion': '17.5.1.21F90',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'INNERTUBE_CONTEXT_CLIENT_NAME': 15,
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 15,
|
||||||
@ -219,19 +258,26 @@
|
|||||||
# mweb has 'ultralow' formats
|
# mweb has 'ultralow' formats
|
||||||
# See: https://github.com/yt-dlp/yt-dlp/pull/557
|
# See: https://github.com/yt-dlp/yt-dlp/pull/557
|
||||||
'mweb': {
|
'mweb': {
|
||||||
'INNERTUBE_API_KEY': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8',
|
|
||||||
'INNERTUBE_CONTEXT': {
|
'INNERTUBE_CONTEXT': {
|
||||||
'client': {
|
'client': {
|
||||||
'clientName': 'MWEB',
|
'clientName': 'MWEB',
|
||||||
'clientVersion': '2.20220801.00.00',
|
'clientVersion': '2.20240726.01.00',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
'INNERTUBE_CONTEXT_CLIENT_NAME': 2,
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 2,
|
||||||
},
|
},
|
||||||
|
'tv': {
|
||||||
|
'INNERTUBE_CONTEXT': {
|
||||||
|
'client': {
|
||||||
|
'clientName': 'TVHTML5',
|
||||||
|
'clientVersion': '7.20240724.13.00',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 7,
|
||||||
|
},
|
||||||
# This client can access age restricted videos (unless the uploader has disabled the 'allow embedding' option)
|
# This client can access age restricted videos (unless the uploader has disabled the 'allow embedding' option)
|
||||||
# See: https://github.com/zerodytrash/YouTube-Internal-Clients
|
# See: https://github.com/zerodytrash/YouTube-Internal-Clients
|
||||||
'tv_embedded': {
|
'tv_embedded': {
|
||||||
'INNERTUBE_API_KEY': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8',
|
|
||||||
'INNERTUBE_CONTEXT': {
|
'INNERTUBE_CONTEXT': {
|
||||||
'client': {
|
'client': {
|
||||||
'clientName': 'TVHTML5_SIMPLY_EMBEDDED_PLAYER',
|
'clientName': 'TVHTML5_SIMPLY_EMBEDDED_PLAYER',
|
||||||
@ -249,6 +295,7 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
'INNERTUBE_CONTEXT_CLIENT_NAME': 95,
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 95,
|
||||||
|
'REQUIRE_JS_PLAYER': False,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,7 +309,7 @@ def _split_innertube_client(client_name):
|
|||||||
|
|
||||||
|
|
||||||
def short_client_name(client_name):
|
def short_client_name(client_name):
|
||||||
main, *parts = _split_innertube_client(client_name)[0].replace('embedscreen', 'e_s').split('_')
|
main, *parts = _split_innertube_client(client_name)[0].split('_')
|
||||||
return join_nonempty(main[:4], ''.join(x[0] for x in parts)).upper()
|
return join_nonempty(main[:4], ''.join(x[0] for x in parts)).upper()
|
||||||
|
|
||||||
|
|
||||||
@ -274,23 +321,18 @@ def build_innertube_clients():
|
|||||||
priority = qualities(BASE_CLIENTS[::-1])
|
priority = qualities(BASE_CLIENTS[::-1])
|
||||||
|
|
||||||
for client, ytcfg in tuple(INNERTUBE_CLIENTS.items()):
|
for client, ytcfg in tuple(INNERTUBE_CLIENTS.items()):
|
||||||
ytcfg.setdefault('INNERTUBE_API_KEY', 'AIzaSyDCU8hByM-4DrUqRUYnGn-3llEO78bcxq8')
|
|
||||||
ytcfg.setdefault('INNERTUBE_HOST', 'www.youtube.com')
|
ytcfg.setdefault('INNERTUBE_HOST', 'www.youtube.com')
|
||||||
ytcfg.setdefault('REQUIRE_JS_PLAYER', True)
|
ytcfg.setdefault('REQUIRE_JS_PLAYER', True)
|
||||||
|
ytcfg.setdefault('PLAYER_PARAMS', None)
|
||||||
ytcfg['INNERTUBE_CONTEXT']['client'].setdefault('hl', 'en')
|
ytcfg['INNERTUBE_CONTEXT']['client'].setdefault('hl', 'en')
|
||||||
|
|
||||||
_, base_client, variant = _split_innertube_client(client)
|
_, base_client, variant = _split_innertube_client(client)
|
||||||
ytcfg['priority'] = 10 * priority(base_client)
|
ytcfg['priority'] = 10 * priority(base_client)
|
||||||
|
|
||||||
if not variant:
|
if variant == 'embedded':
|
||||||
INNERTUBE_CLIENTS[f'{client}_embedscreen'] = embedscreen = copy.deepcopy(ytcfg)
|
|
||||||
embedscreen['INNERTUBE_CONTEXT']['client']['clientScreen'] = 'EMBED'
|
|
||||||
embedscreen['INNERTUBE_CONTEXT']['thirdParty'] = THIRD_PARTY
|
|
||||||
embedscreen['priority'] -= 3
|
|
||||||
elif variant == 'embedded':
|
|
||||||
ytcfg['INNERTUBE_CONTEXT']['thirdParty'] = THIRD_PARTY
|
ytcfg['INNERTUBE_CONTEXT']['thirdParty'] = THIRD_PARTY
|
||||||
ytcfg['priority'] -= 2
|
ytcfg['priority'] -= 2
|
||||||
else:
|
elif variant:
|
||||||
ytcfg['priority'] -= 3
|
ytcfg['priority'] -= 3
|
||||||
|
|
||||||
|
|
||||||
@ -566,9 +608,6 @@ def _select_api_hostname(self, req_api_hostname, default_client=None):
|
|||||||
return (self._configuration_arg('innertube_host', [''], ie_key=YoutubeIE.ie_key())[0]
|
return (self._configuration_arg('innertube_host', [''], ie_key=YoutubeIE.ie_key())[0]
|
||||||
or req_api_hostname or self._get_innertube_host(default_client or 'web'))
|
or req_api_hostname or self._get_innertube_host(default_client or 'web'))
|
||||||
|
|
||||||
def _extract_api_key(self, ytcfg=None, default_client='web'):
|
|
||||||
return self._ytcfg_get_safe(ytcfg, lambda x: x['INNERTUBE_API_KEY'], str, default_client)
|
|
||||||
|
|
||||||
def _extract_context(self, ytcfg=None, default_client='web'):
|
def _extract_context(self, ytcfg=None, default_client='web'):
|
||||||
context = get_first(
|
context = get_first(
|
||||||
(ytcfg, self._get_default_ytcfg(default_client)), 'INNERTUBE_CONTEXT', expected_type=dict)
|
(ytcfg, self._get_default_ytcfg(default_client)), 'INNERTUBE_CONTEXT', expected_type=dict)
|
||||||
@ -614,13 +653,15 @@ def _call_api(self, ep, query, video_id, fatal=True, headers=None,
|
|||||||
real_headers.update({'content-type': 'application/json'})
|
real_headers.update({'content-type': 'application/json'})
|
||||||
if headers:
|
if headers:
|
||||||
real_headers.update(headers)
|
real_headers.update(headers)
|
||||||
api_key = (self._configuration_arg('innertube_key', [''], ie_key=YoutubeIE.ie_key(), casesense=True)[0]
|
|
||||||
or api_key or self._extract_api_key(default_client=default_client))
|
|
||||||
return self._download_json(
|
return self._download_json(
|
||||||
f'https://{self._select_api_hostname(api_hostname, default_client)}/youtubei/v1/{ep}',
|
f'https://{self._select_api_hostname(api_hostname, default_client)}/youtubei/v1/{ep}',
|
||||||
video_id=video_id, fatal=fatal, note=note, errnote=errnote,
|
video_id=video_id, fatal=fatal, note=note, errnote=errnote,
|
||||||
data=json.dumps(data).encode('utf8'), headers=real_headers,
|
data=json.dumps(data).encode('utf8'), headers=real_headers,
|
||||||
query={'key': api_key, 'prettyPrint': 'false'})
|
query=filter_dict({
|
||||||
|
'key': self._configuration_arg(
|
||||||
|
'innertube_key', [api_key], ie_key=YoutubeIE.ie_key(), casesense=True)[0],
|
||||||
|
'prettyPrint': 'false',
|
||||||
|
}, cndn=lambda _, v: v))
|
||||||
|
|
||||||
def extract_yt_initial_data(self, item_id, webpage, fatal=True):
|
def extract_yt_initial_data(self, item_id, webpage, fatal=True):
|
||||||
return self._search_json(self._YT_INITIAL_DATA_RE, webpage, 'yt initial data', item_id, fatal=fatal)
|
return self._search_json(self._YT_INITIAL_DATA_RE, webpage, 'yt initial data', item_id, fatal=fatal)
|
||||||
@ -972,7 +1013,6 @@ def _extract_response(self, item_id, query, note='Downloading API JSON', headers
|
|||||||
ep=ep, fatal=True, headers=headers,
|
ep=ep, fatal=True, headers=headers,
|
||||||
video_id=item_id, query=query, note=note,
|
video_id=item_id, query=query, note=note,
|
||||||
context=self._extract_context(ytcfg, default_client),
|
context=self._extract_context(ytcfg, default_client),
|
||||||
api_key=self._extract_api_key(ytcfg, default_client),
|
|
||||||
api_hostname=api_hostname, default_client=default_client)
|
api_hostname=api_hostname, default_client=default_client)
|
||||||
except ExtractorError as e:
|
except ExtractorError as e:
|
||||||
if not isinstance(e.cause, network_exceptions):
|
if not isinstance(e.cause, network_exceptions):
|
||||||
@ -1295,6 +1335,10 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|||||||
}
|
}
|
||||||
_SUBTITLE_FORMATS = ('json3', 'srv1', 'srv2', 'srv3', 'ttml', 'vtt')
|
_SUBTITLE_FORMATS = ('json3', 'srv1', 'srv2', 'srv3', 'ttml', 'vtt')
|
||||||
_POTOKEN_EXPERIMENTS = ('51217476', '51217102')
|
_POTOKEN_EXPERIMENTS = ('51217476', '51217102')
|
||||||
|
_BROKEN_CLIENTS = {
|
||||||
|
short_client_name(client): client
|
||||||
|
for client in ('android', 'android_creator', 'android_music')
|
||||||
|
}
|
||||||
|
|
||||||
_GEO_BYPASS = False
|
_GEO_BYPASS = False
|
||||||
|
|
||||||
@ -3661,9 +3705,10 @@ def _extract_player_response(self, client, video_id, master_ytcfg, player_ytcfg,
|
|||||||
'videoId': video_id,
|
'videoId': video_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
pp_arg = self._configuration_arg('player_params', [None], casesense=True)[0]
|
default_pp = traverse_obj(
|
||||||
if pp_arg:
|
INNERTUBE_CLIENTS, (_split_innertube_client(client)[0], 'PLAYER_PARAMS', {str}))
|
||||||
yt_query['params'] = pp_arg
|
if player_params := self._configuration_arg('player_params', [default_pp], casesense=True)[0]:
|
||||||
|
yt_query['params'] = player_params
|
||||||
|
|
||||||
yt_query.update(self._generate_player_context(sts))
|
yt_query.update(self._generate_player_context(sts))
|
||||||
return self._extract_response(
|
return self._extract_response(
|
||||||
@ -3675,7 +3720,7 @@ def _extract_player_response(self, client, video_id, master_ytcfg, player_ytcfg,
|
|||||||
|
|
||||||
def _get_requested_clients(self, url, smuggled_data):
|
def _get_requested_clients(self, url, smuggled_data):
|
||||||
requested_clients = []
|
requested_clients = []
|
||||||
android_clients = []
|
broken_clients = []
|
||||||
default = ['ios', 'web']
|
default = ['ios', 'web']
|
||||||
allowed_clients = sorted(
|
allowed_clients = sorted(
|
||||||
(client for client in INNERTUBE_CLIENTS if client[:1] != '_'),
|
(client for client in INNERTUBE_CLIENTS if client[:1] != '_'),
|
||||||
@ -3687,18 +3732,21 @@ def _get_requested_clients(self, url, smuggled_data):
|
|||||||
requested_clients.extend(allowed_clients)
|
requested_clients.extend(allowed_clients)
|
||||||
elif client not in allowed_clients:
|
elif client not in allowed_clients:
|
||||||
self.report_warning(f'Skipping unsupported client {client}')
|
self.report_warning(f'Skipping unsupported client {client}')
|
||||||
elif client.startswith('android'):
|
elif client in self._BROKEN_CLIENTS.values():
|
||||||
android_clients.append(client)
|
broken_clients.append(client)
|
||||||
else:
|
else:
|
||||||
requested_clients.append(client)
|
requested_clients.append(client)
|
||||||
# Force deprioritization of broken Android clients for format de-duplication
|
# Force deprioritization of _BROKEN_CLIENTS for format de-duplication
|
||||||
requested_clients.extend(android_clients)
|
requested_clients.extend(broken_clients)
|
||||||
if not requested_clients:
|
if not requested_clients:
|
||||||
requested_clients = default
|
requested_clients = default
|
||||||
|
|
||||||
if smuggled_data.get('is_music_url') or self.is_music_url(url):
|
if smuggled_data.get('is_music_url') or self.is_music_url(url):
|
||||||
requested_clients.extend(
|
for requested_client in requested_clients:
|
||||||
f'{client}_music' for client in requested_clients if f'{client}_music' in INNERTUBE_CLIENTS)
|
_, base_client, variant = _split_innertube_client(requested_client)
|
||||||
|
music_client = f'{base_client}_music'
|
||||||
|
if variant != 'music' and music_client in INNERTUBE_CLIENTS:
|
||||||
|
requested_clients.append(music_client)
|
||||||
|
|
||||||
return orderedSet(requested_clients)
|
return orderedSet(requested_clients)
|
||||||
|
|
||||||
@ -3793,13 +3841,12 @@ def append_client(*client_names):
|
|||||||
prs.append(pr)
|
prs.append(pr)
|
||||||
|
|
||||||
# creator clients can bypass AGE_VERIFICATION_REQUIRED if logged in
|
# creator clients can bypass AGE_VERIFICATION_REQUIRED if logged in
|
||||||
if variant == 'embedded' and self._is_unplayable(pr) and self.is_authenticated:
|
if variant == 'tv_embedded' and self._is_unplayable(pr) and self.is_authenticated:
|
||||||
append_client(f'{base_client}_creator')
|
append_client(f'{base_client}_creator')
|
||||||
elif self._is_agegated(pr):
|
elif variant != 'tv_embedded' and self._is_agegated(pr):
|
||||||
if variant == 'tv_embedded':
|
if self.is_authenticated:
|
||||||
append_client(f'{base_client}_embedded')
|
append_client(f'{base_client}_creator')
|
||||||
elif not variant:
|
append_client(f'tv_embedded.{base_client}')
|
||||||
append_client(f'tv_embedded.{base_client}', f'{base_client}_embedded')
|
|
||||||
|
|
||||||
if skipped_clients:
|
if skipped_clients:
|
||||||
self.report_warning(
|
self.report_warning(
|
||||||
@ -3935,13 +3982,13 @@ def build_fragments(f):
|
|||||||
f'{video_id}: Some formats are possibly damaged. They will be deprioritized', only_once=True)
|
f'{video_id}: Some formats are possibly damaged. They will be deprioritized', only_once=True)
|
||||||
|
|
||||||
client_name = fmt.get(STREAMING_DATA_CLIENT_NAME)
|
client_name = fmt.get(STREAMING_DATA_CLIENT_NAME)
|
||||||
# Android client formats are broken due to integrity check enforcement
|
# _BROKEN_CLIENTS return videoplayback URLs that expire after 30 seconds
|
||||||
# Ref: https://github.com/yt-dlp/yt-dlp/issues/9554
|
# Ref: https://github.com/yt-dlp/yt-dlp/issues/9554
|
||||||
is_broken = client_name and client_name.startswith(short_client_name('android'))
|
is_broken = client_name in self._BROKEN_CLIENTS
|
||||||
if is_broken:
|
if is_broken:
|
||||||
self.report_warning(
|
self.report_warning(
|
||||||
f'{video_id}: Android client formats are broken and may yield HTTP Error 403. '
|
f'{video_id}: {self._BROKEN_CLIENTS[client_name]} client formats are broken '
|
||||||
'They will be deprioritized', only_once=True)
|
'and may yield HTTP Error 403. They will be deprioritized', only_once=True)
|
||||||
|
|
||||||
name = fmt.get('qualityLabel') or quality.replace('audio_quality_', '') or ''
|
name = fmt.get('qualityLabel') or quality.replace('audio_quality_', '') or ''
|
||||||
fps = int_or_none(fmt.get('fps')) or 0
|
fps = int_or_none(fmt.get('fps')) or 0
|
||||||
|
Loading…
Reference in New Issue
Block a user