summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS2
-rw-r--r--Changelog.md15
-rw-r--r--README.md2
-rw-r--r--test/test_jsinterp.py28
-rw-r--r--test/test_youtube_signature.py4
-rw-r--r--yt_dlp/extractor/cbc.py89
-rw-r--r--yt_dlp/extractor/dplay.py8
-rw-r--r--yt_dlp/extractor/niconico.py22
-rw-r--r--yt_dlp/extractor/olympics.py23
-rw-r--r--yt_dlp/extractor/youku.py2
-rw-r--r--yt_dlp/extractor/youtube.py11
-rw-r--r--yt_dlp/jsinterp.py6
-rw-r--r--yt_dlp/version.py6
13 files changed, 161 insertions, 57 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 2180ecf..489ab7d 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -655,3 +655,5 @@ iancmy
mokrueger
luvyana
szantnerb
+hugepower
+scribblemaniac
diff --git a/Changelog.md b/Changelog.md
index 73bf828..0b96ab2 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -4,6 +4,21 @@
# To create a release, dispatch the https://github.com/yt-dlp/yt-dlp/actions/workflows/release.yml workflow on master
-->
+### 2024.08.06
+
+#### Core changes
+- **jsinterp**: [Improve `slice` implementation](https://github.com/yt-dlp/yt-dlp/commit/bb8bf1db993f59752d20b73b861bd55e40cf0e31) ([#10664](https://github.com/yt-dlp/yt-dlp/issues/10664)) by [seproDev](https://github.com/seproDev)
+
+#### Extractor changes
+- **discoveryplusitaly**: [Support sport and olympics URLs](https://github.com/yt-dlp/yt-dlp/commit/e7d73bc4531ee3f91a46b15e218dcc1fbeb6226c) ([#10655](https://github.com/yt-dlp/yt-dlp/issues/10655)) by [bashonly](https://github.com/bashonly)
+- **gem.cbc.ca**: live: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/fc5eecfa31c9571b6031cc3968aaa0394be55d7a) ([#10565](https://github.com/yt-dlp/yt-dlp/issues/10565)) by [bashonly](https://github.com/bashonly), [scribblemaniac](https://github.com/scribblemaniac)
+- **niconico**: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/4d9231208332d4c32364b8cd814bff8b20232cae) ([#10677](https://github.com/yt-dlp/yt-dlp/issues/10677)) by [bashonly](https://github.com/bashonly)
+- **olympics**: [Fix extraction](https://github.com/yt-dlp/yt-dlp/commit/919540a9644e55deb78cdd6751757ec8fdaf76f4) ([#10625](https://github.com/yt-dlp/yt-dlp/issues/10625)) by [bashonly](https://github.com/bashonly)
+- **youku**: [Fix extractor](https://github.com/yt-dlp/yt-dlp/commit/0088c6de23d832b117061a33e984dc452d992e9c) ([#10626](https://github.com/yt-dlp/yt-dlp/issues/10626)) by [hugepower](https://github.com/hugepower)
+- **youtube**
+ - [Change default player clients to `ios,web_creator`](https://github.com/yt-dlp/yt-dlp/commit/406f4c2e47502fffc1b0c210b4ee6487c89a44cb) ([#10674](https://github.com/yt-dlp/yt-dlp/issues/10674)) by [bashonly](https://github.com/bashonly)
+ - [Fix `n` function name extraction for player `b12cc44b`](https://github.com/yt-dlp/yt-dlp/commit/c86891eb9434b4d7eec426d38c0c625b5e13cb2f) ([#10668](https://github.com/yt-dlp/yt-dlp/issues/10668)) by [seproDev](https://github.com/seproDev)
+
### 2024.08.01
#### Core changes
diff --git a/README.md b/README.md
index dd78012..ca32e09 100644
--- a/README.md
+++ b/README.md
@@ -1767,7 +1767,7 @@ The following extractors use this feature:
#### 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
* `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` and `_creator` (e.g. `ios_creator`); and `mediaconnect`, `mweb`, `android_producer`, `android_testsuite`, `android_vr`, `web_safari`, `web_embedded`, `tv` and `tv_embedded` with no variants. By default, `ios,tv` is used, but `tv_embedded`, `web_creator` and `mediaconnect` 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_client`: Clients to extract video data from. The main clients are `web`, `ios` and `android`, with variants `_music` and `_creator` (e.g. `ios_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_creator` is used, and `tv_embedded`, `web_creator` and `mediaconnect` 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_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)
diff --git a/test/test_jsinterp.py b/test/test_jsinterp.py
index df92c83..06840ed 100644
--- a/test/test_jsinterp.py
+++ b/test/test_jsinterp.py
@@ -403,6 +403,34 @@ class TestJSInterpreter(unittest.TestCase):
self._test(jsi, [''], args=['', '-'])
self._test(jsi, [], args=['', ''])
+ def test_slice(self):
+ self._test('function f(){return [0, 1, 2, 3, 4, 5, 6, 7, 8].slice()}', [0, 1, 2, 3, 4, 5, 6, 7, 8])
+ self._test('function f(){return [0, 1, 2, 3, 4, 5, 6, 7, 8].slice(0)}', [0, 1, 2, 3, 4, 5, 6, 7, 8])
+ self._test('function f(){return [0, 1, 2, 3, 4, 5, 6, 7, 8].slice(5)}', [5, 6, 7, 8])
+ self._test('function f(){return [0, 1, 2, 3, 4, 5, 6, 7, 8].slice(99)}', [])
+ self._test('function f(){return [0, 1, 2, 3, 4, 5, 6, 7, 8].slice(-2)}', [7, 8])
+ self._test('function f(){return [0, 1, 2, 3, 4, 5, 6, 7, 8].slice(-99)}', [0, 1, 2, 3, 4, 5, 6, 7, 8])
+ self._test('function f(){return [0, 1, 2, 3, 4, 5, 6, 7, 8].slice(0, 0)}', [])
+ self._test('function f(){return [0, 1, 2, 3, 4, 5, 6, 7, 8].slice(1, 0)}', [])
+ self._test('function f(){return [0, 1, 2, 3, 4, 5, 6, 7, 8].slice(0, 1)}', [0])
+ self._test('function f(){return [0, 1, 2, 3, 4, 5, 6, 7, 8].slice(3, 6)}', [3, 4, 5])
+ self._test('function f(){return [0, 1, 2, 3, 4, 5, 6, 7, 8].slice(1, -1)}', [1, 2, 3, 4, 5, 6, 7])
+ self._test('function f(){return [0, 1, 2, 3, 4, 5, 6, 7, 8].slice(-1, 1)}', [])
+ self._test('function f(){return [0, 1, 2, 3, 4, 5, 6, 7, 8].slice(-3, -1)}', [6, 7])
+ self._test('function f(){return "012345678".slice()}', '012345678')
+ self._test('function f(){return "012345678".slice(0)}', '012345678')
+ self._test('function f(){return "012345678".slice(5)}', '5678')
+ self._test('function f(){return "012345678".slice(99)}', '')
+ self._test('function f(){return "012345678".slice(-2)}', '78')
+ self._test('function f(){return "012345678".slice(-99)}', '012345678')
+ self._test('function f(){return "012345678".slice(0, 0)}', '')
+ self._test('function f(){return "012345678".slice(1, 0)}', '')
+ self._test('function f(){return "012345678".slice(0, 1)}', '0')
+ self._test('function f(){return "012345678".slice(3, 6)}', '345')
+ self._test('function f(){return "012345678".slice(1, -1)}', '1234567')
+ self._test('function f(){return "012345678".slice(-1, 1)}', '')
+ self._test('function f(){return "012345678".slice(-3, -1)}', '67')
+
if __name__ == '__main__':
unittest.main()
diff --git a/test/test_youtube_signature.py b/test/test_youtube_signature.py
index d37df7a..0f7ae34 100644
--- a/test/test_youtube_signature.py
+++ b/test/test_youtube_signature.py
@@ -179,6 +179,10 @@ _NSIG_TESTS = [
'https://www.youtube.com/s/player/20dfca59/player_ias.vflset/en_US/base.js',
'-fLCxedkAk4LUTK2', 'O8kfRq1y1eyHGw',
),
+ (
+ 'https://www.youtube.com/s/player/b12cc44b/player_ias.vflset/en_US/base.js',
+ 'keLa5R2U00sR9SQK', 'N1OGyujjEwMnLw',
+ ),
]
diff --git a/yt_dlp/extractor/cbc.py b/yt_dlp/extractor/cbc.py
index 373c9d2..40224f6 100644
--- a/yt_dlp/extractor/cbc.py
+++ b/yt_dlp/extractor/cbc.py
@@ -806,11 +806,11 @@ class CBCGemLiveIE(InfoExtractor):
'title': 'Ottawa',
'description': 'The live TV channel and local programming from Ottawa',
'thumbnail': 'https://thumbnails.cbc.ca/maven_legacy/thumbnails/CBC_OTT_VMS/Live_Channel_Static_Images/Ottawa_2880x1620.jpg',
- 'is_live': True,
+ 'live_status': 'is_live',
'id': 'AyqZwxRqh8EH',
'ext': 'mp4',
- 'timestamp': 1492106160,
- 'upload_date': '20170413',
+ 'release_timestamp': 1492106160,
+ 'release_date': '20170413',
'uploader': 'CBCC-NEW',
},
'skip': 'Live might have ended',
@@ -839,49 +839,84 @@ class CBCGemLiveIE(InfoExtractor):
'description': 'March 24, 2023 | President Biden’s Ottawa visit ends with big pledges from both countries. Plus, Gwyneth Paltrow testifies in her ski collision trial.',
'live_status': 'is_live',
'thumbnail': r're:https://images.gem.cbc.ca/v1/cbc-gem/live/.*',
- 'timestamp': 1679706000,
- 'upload_date': '20230325',
+ 'release_timestamp': 1679706000,
+ 'release_date': '20230325',
},
'params': {'skip_download': True},
'skip': 'Live might have ended',
},
+ { # event replay (medianetlive)
+ 'url': 'https://gem.cbc.ca/live-event/42314',
+ 'md5': '297a9600f554f2258aed01514226a697',
+ 'info_dict': {
+ 'id': '42314',
+ 'ext': 'mp4',
+ 'live_status': 'was_live',
+ 'title': 'Women\'s Soccer - Canada vs New Zealand',
+ 'description': 'md5:36200e5f1a70982277b5a6ecea86155d',
+ 'thumbnail': r're:https://.+default\.jpg',
+ 'release_timestamp': 1721917200,
+ 'release_date': '20240725',
+ },
+ 'params': {'skip_download': True},
+ 'skip': 'Replay might no longer be available',
+ },
+ { # event replay (medianetlive)
+ 'url': 'https://gem.cbc.ca/live-event/43273',
+ 'only_matching': True,
+ },
]
+ _GEO_COUNTRIES = ['CA']
def _real_extract(self, url):
video_id = self._match_id(url)
webpage = self._download_webpage(url, video_id)
video_info = self._search_nextjs_data(webpage, video_id)['props']['pageProps']['data']
- # Two types of metadata JSON
+ # Three types of video_info JSON: info in root, freeTv stream/item, event replay
if not video_info.get('formattedIdMedia'):
- video_info = traverse_obj(
- video_info, (('freeTv', ('streams', ...)), 'items', lambda _, v: v['key'] == video_id, {dict}),
- get_all=False, default={})
+ if traverse_obj(video_info, ('event', 'key')) == video_id:
+ video_info = video_info['event']
+ else:
+ video_info = traverse_obj(video_info, (
+ ('freeTv', ('streams', ...)), 'items',
+ lambda _, v: v['key'].partition('-')[0] == video_id, any)) or {}
video_stream_id = video_info.get('formattedIdMedia')
if not video_stream_id:
- raise ExtractorError('Couldn\'t find video metadata, maybe this livestream is now offline', expected=True)
-
- stream_data = self._download_json(
- 'https://services.radio-canada.ca/media/validation/v2/', video_id, query={
- 'appCode': 'mpx',
- 'connectionType': 'hd',
- 'deviceType': 'ipad',
- 'idMedia': video_stream_id,
- 'multibitrate': 'true',
- 'output': 'json',
- 'tech': 'hls',
- 'manifestType': 'desktop',
- })
+ raise ExtractorError(
+ 'Couldn\'t find video metadata, maybe this livestream is now offline', expected=True)
+
+ live_status = 'was_live' if video_info.get('isVodEnabled') else 'is_live'
+ release_timestamp = traverse_obj(video_info, ('airDate', {parse_iso8601}))
+
+ if live_status == 'is_live' and release_timestamp and release_timestamp > time.time():
+ formats = []
+ live_status = 'is_upcoming'
+ self.raise_no_formats('This livestream has not yet started', expected=True)
+ else:
+ stream_data = self._download_json(
+ 'https://services.radio-canada.ca/media/validation/v2/', video_id, query={
+ 'appCode': 'medianetlive',
+ 'connectionType': 'hd',
+ 'deviceType': 'ipad',
+ 'idMedia': video_stream_id,
+ 'multibitrate': 'true',
+ 'output': 'json',
+ 'tech': 'hls',
+ 'manifestType': 'desktop',
+ })
+ formats = self._extract_m3u8_formats(
+ stream_data['url'], video_id, 'mp4', live=live_status == 'is_live')
return {
'id': video_id,
- 'formats': self._extract_m3u8_formats(stream_data['url'], video_id, 'mp4', live=True),
- 'is_live': True,
+ 'formats': formats,
+ 'live_status': live_status,
+ 'release_timestamp': release_timestamp,
**traverse_obj(video_info, {
- 'title': 'title',
- 'description': 'description',
+ 'title': ('title', {str}),
+ 'description': ('description', {str}),
'thumbnail': ('images', 'card', 'url'),
- 'timestamp': ('airDate', {parse_iso8601}),
}),
}
diff --git a/yt_dlp/extractor/dplay.py b/yt_dlp/extractor/dplay.py
index cdf84c5..8d77072 100644
--- a/yt_dlp/extractor/dplay.py
+++ b/yt_dlp/extractor/dplay.py
@@ -1147,13 +1147,19 @@ class DiscoveryPlusShowBaseIE(DPlayBaseIE):
class DiscoveryPlusItalyIE(DiscoveryPlusBaseIE):
- _VALID_URL = r'https?://(?:www\.)?discoveryplus\.com/it/video' + DPlayBaseIE._PATH_REGEX
+ _VALID_URL = r'https?://(?:www\.)?discoveryplus\.com/it/video(?:/sport|/olympics)?' + DPlayBaseIE._PATH_REGEX
_TESTS = [{
'url': 'https://www.discoveryplus.com/it/video/i-signori-della-neve/stagione-2-episodio-1-i-preparativi',
'only_matching': True,
}, {
'url': 'https://www.discoveryplus.com/it/video/super-benny/trailer',
'only_matching': True,
+ }, {
+ 'url': 'https://www.discoveryplus.com/it/video/olympics/dplus-sport-dplus-sport-sport/water-polo-greece-italy',
+ 'only_matching': True,
+ }, {
+ 'url': 'https://www.discoveryplus.com/it/video/sport/dplus-sport-dplus-sport-sport/lisa-vittozzi-allinferno-e-ritorno',
+ 'only_matching': True,
}]
_PRODUCT = 'dplus_it'
diff --git a/yt_dlp/extractor/niconico.py b/yt_dlp/extractor/niconico.py
index 9d7b010..179e7a9 100644
--- a/yt_dlp/extractor/niconico.py
+++ b/yt_dlp/extractor/niconico.py
@@ -40,7 +40,6 @@ class NiconicoIE(InfoExtractor):
_TESTS = [{
'url': 'http://www.nicovideo.jp/watch/sm22312215',
- 'md5': 'd1a75c0823e2f629128c43e1212760f9',
'info_dict': {
'id': 'sm22312215',
'ext': 'mp4',
@@ -56,8 +55,8 @@ class NiconicoIE(InfoExtractor):
'comment_count': int,
'genres': ['未設定'],
'tags': [],
- 'expected_protocol': str,
},
+ 'params': {'skip_download': 'm3u8'},
}, {
# File downloaded with and without credentials are different, so omit
# the md5 field
@@ -77,8 +76,8 @@ class NiconicoIE(InfoExtractor):
'view_count': int,
'genres': ['音楽・サウンド'],
'tags': ['Translation_Request', 'Kagamine_Rin', 'Rin_Original'],
- 'expected_protocol': str,
},
+ 'params': {'skip_download': 'm3u8'},
}, {
# 'video exists but is marked as "deleted"
# md5 is unstable
@@ -112,7 +111,6 @@ class NiconicoIE(InfoExtractor):
}, {
# video not available via `getflv`; "old" HTML5 video
'url': 'http://www.nicovideo.jp/watch/sm1151009',
- 'md5': 'f95a3d259172667b293530cc2e41ebda',
'info_dict': {
'id': 'sm1151009',
'ext': 'mp4',
@@ -128,11 +126,10 @@ class NiconicoIE(InfoExtractor):
'comment_count': int,
'genres': ['ゲーム'],
'tags': [],
- 'expected_protocol': str,
},
+ 'params': {'skip_download': 'm3u8'},
}, {
# "New" HTML5 video
- # md5 is unstable
'url': 'http://www.nicovideo.jp/watch/sm31464864',
'info_dict': {
'id': 'sm31464864',
@@ -149,12 +146,11 @@ class NiconicoIE(InfoExtractor):
'comment_count': int,
'genres': ['アニメ'],
'tags': [],
- 'expected_protocol': str,
},
+ 'params': {'skip_download': 'm3u8'},
}, {
# Video without owner
'url': 'http://www.nicovideo.jp/watch/sm18238488',
- 'md5': 'd265680a1f92bdcbbd2a507fc9e78a9e',
'info_dict': {
'id': 'sm18238488',
'ext': 'mp4',
@@ -168,8 +164,8 @@ class NiconicoIE(InfoExtractor):
'comment_count': int,
'genres': ['エンターテイメント'],
'tags': [],
- 'expected_protocol': str,
},
+ 'params': {'skip_download': 'm3u8'},
}, {
'url': 'http://sp.nicovideo.jp/watch/sm28964488?ss_pos=1&cp_in=wt_tg',
'only_matching': True,
@@ -458,9 +454,11 @@ class NiconicoIE(InfoExtractor):
if video_id.startswith('so'):
video_id = self._match_id(handle.url)
- api_data = self._parse_json(self._html_search_regex(
- 'data-api-data="([^"]+)"', webpage,
- 'API data', default='{}'), video_id)
+ api_data = traverse_obj(
+ self._parse_json(self._html_search_meta('server-response', webpage) or '', video_id),
+ ('data', 'response', {dict}))
+ if not api_data:
+ raise ExtractorError('Server response data not found')
except ExtractorError as e:
try:
api_data = self._download_json(
diff --git a/yt_dlp/extractor/olympics.py b/yt_dlp/extractor/olympics.py
index a50c510..bbf83e5 100644
--- a/yt_dlp/extractor/olympics.py
+++ b/yt_dlp/extractor/olympics.py
@@ -4,7 +4,9 @@ from ..utils import (
ExtractorError,
int_or_none,
parse_iso8601,
+ parse_qs,
try_get,
+ update_url,
url_or_none,
)
from ..utils.traversal import traverse_obj
@@ -24,9 +26,6 @@ class OlympicsReplayIE(InfoExtractor):
'thumbnail': 'https://img.olympics.com/images/image/private/t_1-1_1280/primary/nua4o7zwyaznoaejpbk2',
'duration': 7017.0,
},
- 'params': {
- 'skip_download': True,
- },
}, {
'url': 'https://olympics.com/en/original-series/episode/b-boys-and-b-girls-take-the-spotlight-breaking-life-road-to-paris-2024',
'info_dict': {
@@ -74,7 +73,7 @@ class OlympicsReplayIE(InfoExtractor):
is_live = traverse_obj(data, ('streamingStatus', {str})) == 'LIVE'
m3u8_url = traverse_obj(data, ('videoUrl', {url_or_none})) or data['streamUrl']
- tokenized_url = m3u8_url if is_live else self._tokenize_url(m3u8_url, video_id)
+ tokenized_url = self._tokenize_url(m3u8_url, data['jwtToken'], is_live, video_id)
try:
formats, subtitles = self._extract_m3u8_formats_and_subtitles(
@@ -95,10 +94,20 @@ class OlympicsReplayIE(InfoExtractor):
}),
}
- def _tokenize_url(self, url, video_id):
+ def _tokenize_url(self, url, token, is_live, video_id):
+ return self._download_json(
+ 'https://metering.olympics.com/tokengenerator', video_id,
+ 'Downloading tokenized m3u8 url', query={
+ **parse_qs(url),
+ 'url': update_url(url, query=None),
+ 'service-id': 'live' if is_live else 'vod',
+ 'user-auth': token,
+ })['data']['url']
+
+ def _legacy_tokenize_url(self, url, video_id):
return self._download_json(
'https://olympics.com/tokenGenerator', video_id,
- 'Downloading tokenized m3u8 url', query={'url': url})
+ 'Downloading legacy tokenized m3u8 url', query={'url': url})
def _real_extract(self, url):
video_id = self._match_id(url)
@@ -130,7 +139,7 @@ class OlympicsReplayIE(InfoExtractor):
})
formats, subtitles = self._extract_m3u8_formats_and_subtitles(
- self._tokenize_url(m3u8_url, video_uuid), video_uuid, 'mp4', m3u8_id='hls')
+ self._legacy_tokenize_url(m3u8_url, video_uuid), video_uuid, 'mp4', m3u8_id='hls')
return {
'id': video_uuid,
diff --git a/yt_dlp/extractor/youku.py b/yt_dlp/extractor/youku.py
index fa6b053..3bdfa6c 100644
--- a/yt_dlp/extractor/youku.py
+++ b/yt_dlp/extractor/youku.py
@@ -136,7 +136,7 @@ class YoukuIE(InfoExtractor):
# request basic data
basic_data_params = {
'vid': video_id,
- 'ccode': '0524',
+ 'ccode': '0564',
'client_ip': '192.168.1.1',
'utid': cna,
'client_ts': time.time() / 1000,
diff --git a/yt_dlp/extractor/youtube.py b/yt_dlp/extractor/youtube.py
index 88e1a28..224c9b9 100644
--- a/yt_dlp/extractor/youtube.py
+++ b/yt_dlp/extractor/youtube.py
@@ -3180,6 +3180,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
# * b=String.fromCharCode(110),c=a.get(b))&&c=narray[idx](c)
# * a.D&&(b="nn"[+a.D],c=a.get(b))&&(c=narray[idx](c),a.set(b,c),narray.length||nfunc("")
# * a.D&&(PL(a),b=a.j.n||null)&&(b=narray[0](b),a.set("n",b),narray.length||nfunc("")
+ # * a.D&&(b="nn"[+a.D],vL(a),c=a.j[b]||null)&&(c=narray[idx](c),a.set(b,c),narray.length||nfunc("")
funcname, idx = self._search_regex(
r'''(?x)
(?:
@@ -3187,7 +3188,13 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
(?:
b=String\.fromCharCode\(110\)|
(?P<str_idx>[a-zA-Z0-9_$.]+)&&\(b="nn"\[\+(?P=str_idx)\]
- ),c=a\.get\(b\)\)&&\(c=|
+ )
+ (?:
+ ,[a-zA-Z0-9_$]+\(a\))?,c=a\.
+ (?:
+ get\(b\)|
+ [a-zA-Z0-9_$]+\[b\]\|\|null
+ )\)&&\(c=|
\b(?P<var>[a-zA-Z0-9_$]+)=
)(?P<nfunc>[a-zA-Z0-9_$]+)(?:\[(?P<idx>\d+)\])?\([a-zA-Z]\)
(?(var),[a-zA-Z0-9_$]+\.set\("n"\,(?P=var)\),(?P=nfunc)\.length)''',
@@ -3737,7 +3744,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
def _get_requested_clients(self, url, smuggled_data):
requested_clients = []
broken_clients = []
- default = ['ios', 'tv']
+ default = ['ios', 'web_creator']
allowed_clients = sorted(
(client for client in INNERTUBE_CLIENTS if client[:1] != '_'),
key=lambda client: INNERTUBE_CLIENTS[client]['priority'], reverse=True)
diff --git a/yt_dlp/jsinterp.py b/yt_dlp/jsinterp.py
index 851d4dc..ba059ba 100644
--- a/yt_dlp/jsinterp.py
+++ b/yt_dlp/jsinterp.py
@@ -709,9 +709,9 @@ class JSInterpreter:
obj.reverse()
return obj
elif member == 'slice':
- assertion(isinstance(obj, list), 'must be applied on a list')
- assertion(len(argvals) == 1, 'takes exactly one argument')
- return obj[argvals[0]:]
+ assertion(isinstance(obj, (list, str)), 'must be applied on a list or string')
+ assertion(len(argvals) <= 2, 'takes between 0 and 2 arguments')
+ return obj[slice(*argvals, None)]
elif member == 'splice':
assertion(isinstance(obj, list), 'must be applied on a list')
assertion(argvals, 'takes one or more arguments')
diff --git a/yt_dlp/version.py b/yt_dlp/version.py
index 81d1c2c..6633a11 100644
--- a/yt_dlp/version.py
+++ b/yt_dlp/version.py
@@ -1,8 +1,8 @@
# Autogenerated by devscripts/update-version.py
-__version__ = '2024.08.01'
+__version__ = '2024.08.06'
-RELEASE_GIT_HEAD = 'ffd7781d6588926f820b44a34b9e6e3068fb9f97'
+RELEASE_GIT_HEAD = '4d9231208332d4c32364b8cd814bff8b20232cae'
VARIANT = None
@@ -12,4 +12,4 @@ CHANNEL = 'stable'
ORIGIN = 'yt-dlp/yt-dlp'
-_pkg_version = '2024.08.01'
+_pkg_version = '2024.08.06'