Compare commits

...
5 Commits
Author SHA1 Message Date
bashonlyandGitHub 5d6b8c8cd1 [ie/youtube] Extract channel_follower_count for collaborators (#17363)
To clarify the somewhat misleading subject line: this patch
only extracts the 1st collaborator's channel_follower_count
for videos with multiple collaborators.

Closes #17360
Authored by: bashonly
2026-08-04 22:58:34 +00:00
dlp-botandGitHub 15941bd48f [utils] random_user_agent: Bump version range 144-150 => 145-151 (#17344)
Authored by: dlp-bot
2026-08-04 14:28:47 +00:00
bashonlyandGitHub d798b9b8fb [ci] Fix broken Windows PyPy tests (#17347)
Workaround for https://github.com/actions/setup-python/issues/1348

Authored by: bashonly
2026-08-04 05:01:09 +00:00
bashonlyandGitHub fdcc954df4 [ie/vimeo] Add macos_basic client (#17292)
Authored by: bashonly
2026-07-23 16:54:48 +00:00
bashonlyandGitHub 07591f601e [ie/vimeo] Remove ios and macos clients (#17290)
Closes #17271, Closes #17286
Authored by: bashonly
2026-07-23 10:59:57 +00:00
7 changed files with 67 additions and 49 deletions
+25 -9
View File
@@ -43,45 +43,61 @@ jobs:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
- name: Install Deno
uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2.0.4
with:
deno-version: '2.3.0' # minimum supported version
- name: Install Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: '1.2.11' # minimum supported version
no-cache: true
- name: Install Node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '22.0' # minimum supported version
- name: Install QuickJS (Linux)
if: matrix.os == 'ubuntu-latest'
shell: bash
run: |
wget "https://bellard.org/quickjs/binary_releases/quickjs-linux-x86_64-${QJS_VERSION}.zip" -O quickjs.zip
unzip quickjs.zip qjs
sudo install qjs /usr/local/bin/qjs
wget "https://bellard.org/quickjs/binary_releases/quickjs-linux-x86_64-${QJS_VERSION}.zip" -O quickjs.zip
unzip quickjs.zip qjs
sudo install qjs /usr/local/bin/qjs
- name: Install QuickJS (Windows)
if: matrix.os == 'windows-latest'
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$PSNativeCommandUseErrorActionPreference = $true
Invoke-WebRequest "https://bellard.org/quickjs/binary_releases/quickjs-win-x86_64-${Env:QJS_VERSION}.zip" -OutFile quickjs.zip
unzip quickjs.zip
- name: Install test requirements
shell: bash
$ErrorActionPreference = "Stop"
$PSNativeCommandUseErrorActionPreference = $true
Invoke-WebRequest "https://bellard.org/quickjs/binary_releases/quickjs-win-x86_64-${Env:QJS_VERSION}.zip" -OutFile quickjs.zip
unzip quickjs.zip
- name: Install test requirements (cpython)
if: ${{ !startsWith(matrix.python-version, 'pypy') }}
run: |
python -m pip install -U --require-hashes -r "bundle/requirements/pip.txt"
python -m pip install -U --require-hashes -r "bundle/requirements/test.txt"
python -m pip install -U --require-hashes -r "bundle/requirements/default.txt"
- name: Install test requirements (PyPy)
if: ${{ startsWith(matrix.python-version, 'pypy') }}
run: |
# Upgrading/downgrading pip on Windows with actions/setup-python's PyPy can cause breakage
# See https://github.com/actions/setup-python/issues/1348
python -m pip install -U --require-hashes -r "bundle/requirements/test.txt"
python -m pip install -U --require-hashes -r "bundle/requirements/default.txt"
- name: Run tests
timeout-minutes: 15
shell: bash
+2 -1
View File
@@ -85,7 +85,8 @@ jobs:
- name: Install test requirements (PyPy)
if: ${{ startsWith(matrix.python-version, 'pypy') }}
run: |
python -m pip install -U --require-hashes -r "bundle/requirements/pip.txt"
# Upgrading/downgrading pip on Windows with actions/setup-python's PyPy can cause breakage
# See https://github.com/actions/setup-python/issues/1348
python -m pip install -U --require-hashes -r "bundle/requirements/test.txt"
python -m pip install -U --require-hashes -r "bundle/requirements/default.txt"
+1 -1
View File
@@ -1975,7 +1975,7 @@ The following extractors use this feature:
* `backend`: Backend API to use for extraction - one of `streaks` (default) or `brightcove` (deprecated)
#### vimeo
* `client`: Client to extract video data from. The currently available clients are `android`, `ios`, `macos` and `web`. Only one client can be used. The `macos` client is used by default, but the `web` client is used when logged-in. The `web` client only works with account cookies or login credentials. The `android` and `ios` clients only work with previously cached OAuth tokens
* `client`: Client to extract video data from. The currently available clients are `android`, `macos_basic`, and `web`. Only one client can be used. The `macos_basic` client is used by default, but the `web` client is used when logged-in. The `web` client only works with account cookies or login credentials. The `android` client only works with previously cached OAuth tokens
* `original_format_policy`: Policy for when to try extracting original formats. One of `always`, `never`, or `auto`. The default `auto` policy tries to avoid exceeding the web client's API rate-limit by only making an extra request when Vimeo publicizes the video's downloadability
#### zan
+4
View File
@@ -382,5 +382,9 @@
"action": "add",
"when": "b6590aaa1e3808155d69c9a79a797ae484163789",
"short": "[priority] Security: [[CVE-2026-55404](https://nvd.nist.gov/vuln/detail/CVE-2026-55404)] [Downstream command injection via improper sanitization of --write-link output](https://github.com/yt-dlp/yt-dlp/security/advisories/GHSA-6v4j-43gg-vj32)\n - Shortcut file data is now properly validated and sanitized when the `--write-link` options are used"
},
{
"action": "remove",
"when": "a8be438aac1b90c3888e974056d967b8be90fa7e"
}
]
+18 -27
View File
@@ -49,7 +49,7 @@ class VimeoBaseInfoExtractor(InfoExtractor):
'Cannot download embed-only video without embedding URL. Please call yt-dlp '
'with the URL of the page that embeds this video.')
_DEFAULT_CLIENT = 'web'
_DEFAULT_CLIENT = 'macos_basic'
_DEFAULT_AUTHED_CLIENT = 'web'
_CLIENT_HEADERS = {
'Accept': 'application/vnd.vimeo.*+json; version=3.4.10',
@@ -59,7 +59,6 @@ class VimeoBaseInfoExtractor(InfoExtractor):
'android': {
'CACHE_KEY': 'oauth-token-android',
'CACHE_ONLY': True,
'VIEWER_JWT': False,
'REQUIRES_AUTH': False,
'AUTH': 'NzRmYTg5YjgxMWExY2JiNzUwZDg1MjhkMTYzZjQ4YWYyOGEyZGJlMTp4OGx2NFd3QnNvY1lkamI2UVZsdjdDYlNwSDUrdm50YzdNNThvWDcwN1JrenJGZC9tR1lReUNlRjRSVklZeWhYZVpRS0tBcU9YYzRoTGY2Z1dlVkJFYkdJc0dMRHpoZWFZbU0reDRqZ1dkZ1diZmdIdGUrNUM5RVBySlM0VG1qcw==',
'USER_AGENT': 'com.vimeo.android.videoapp (OnePlus, ONEPLUS A6003, OnePlus, Android 14/34 Version 11.8.1) Kotlin VimeoNetworking/3.12.0',
@@ -71,26 +70,8 @@ class VimeoBaseInfoExtractor(InfoExtractor):
'resource_key', 'badge', 'upload', 'transcode', 'is_playable', 'has_audio',
),
},
'ios': {
'CACHE_KEY': 'oauth-token-ios',
'CACHE_ONLY': True,
'VIEWER_JWT': False,
'REQUIRES_AUTH': False,
'AUTH': 'MTMxNzViY2Y0NDE0YTQ5YzhjZTc0YmU0NjVjNDQxYzNkYWVjOWRlOTpHKzRvMmgzVUh4UkxjdU5FRW80cDNDbDhDWGR5dVJLNUJZZ055dHBHTTB4V1VzaG41bEx1a2hiN0NWYWNUcldSSW53dzRUdFRYZlJEZmFoTTArOTBUZkJHS3R4V2llYU04Qnl1bERSWWxUdXRidjNqR2J4SHFpVmtFSUcyRktuQw==',
'USER_AGENT': 'Vimeo/11.10.0 (com.vimeo; build:250424.164813.0; iOS 18.4.1) Alamofire/5.9.0 VimeoNetworking/5.0.0',
'VIDEOS_FIELDS': (
'uri', 'name', 'description', 'type', 'link', 'player_embed_url', 'duration',
'width', 'language', 'height', 'embed', 'created_time', 'modified_time', 'release_time',
'content_rating', 'content_rating_class', 'rating_mod_locked', 'license', 'config_url',
'embed_player_config_url', 'privacy', 'pictures', 'tags', 'stats', 'categories', 'uploader',
'metadata', 'user', 'files', 'download', 'app', 'play', 'status', 'resource_key', 'badge',
'upload', 'transcode', 'is_playable', 'has_audio',
),
},
'macos': {
'CACHE_KEY': 'oauth-token-macos',
'CACHE_ONLY': True,
'VIEWER_JWT': False,
'macos_basic': {
'CACHE_ONLY': False,
'REQUIRES_AUTH': False,
'AUTH': 'NDc1N2JlN2Y5ZjZmMjU3NzE3NTRkZTg1NmY2YzU2MTI0OTFlNjJiYjpwVUNDWUlBZmZqSHhQcndBYWxGMzgyYys2NkN5d1JrREJZZXdPcEdsU05tdjFlVVo2aE1lYk9GcWE3ZW9KVldlYnFlOWh5Vno5UWtpUGJ5empYZFBpYkFwV0FFTnB5VWV4ZEh3aHZnRUNEL0VySnBzTmFraDdNbS9nMXhWanhIcw==',
'USER_AGENT': 'Vimeo/1.6.3 (com.vimeo.mac; build:251121.142637.0; macOS 13.7.8) Alamofire/5.9.0 VimeoNetworking/5.0.0',
@@ -104,7 +85,6 @@ class VimeoBaseInfoExtractor(InfoExtractor):
},
'web': {
'CACHE_ONLY': False,
'VIEWER_JWT': True,
'REQUIRES_AUTH': True,
'USER_AGENT': None,
'VIDEOS_FIELDS': (
@@ -182,7 +162,8 @@ class VimeoBaseInfoExtractor(InfoExtractor):
if self._LOGIN_REQUIRED:
self.raise_login_required()
if self._DEFAULT_CLIENT != 'web':
# Don't auto-load token from cache if the user has specified a client
if self._configuration_arg('client', [None], ie_key=VimeoIE)[0]:
return
for client_name, client_config in self._CLIENT_CONFIGS.items():
@@ -367,11 +348,14 @@ class VimeoBaseInfoExtractor(InfoExtractor):
}
def _fetch_oauth_token(self, client):
client_config = self._CLIENT_CONFIGS[client]
if client_config['VIEWER_JWT']:
base_client, _, variant = client.partition('_')
if base_client == 'web':
return f'jwt {self._fetch_viewer_info()["jwt"]}'
client_config = self._CLIENT_CONFIGS[client]
if variant == 'basic':
return f'Basic {client_config["AUTH"]}'
cache_key = client_config['CACHE_KEY']
if not self._oauth_tokens.get(cache_key):
@@ -1213,6 +1197,9 @@ class VimeoIE(VimeoBaseInfoExtractor):
'If your IP address is located in Europe you could try using a VPN/proxy,',
f'or else u{self._login_hint()[1:]}',
delim=' '), method=None)
# XXX: Temporary while macos_basic is the default client
elif e.cause.status == 401 and self._get_requested_client() == 'macos_basic':
self.raise_login_required('The Vimeo extractor only works when logged-in')
else:
raise
@@ -1221,6 +1208,10 @@ class VimeoIE(VimeoBaseInfoExtractor):
else:
info = self._parse_api_response(video, video_id, unlisted_hash)
# XXX: Temporary while macos_basic is the default client
if not info.get('formats') and self._get_requested_client() == 'macos_basic':
self.raise_login_required('The Vimeo extractor only works when logged-in')
source_format = self._extract_original_format(
f'https://vimeo.com/{video_id}', video_id, unlisted_hash)
if source_format:
+16 -10
View File
@@ -1600,13 +1600,13 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
'info_dict': {
'id': 'brhfDfLdDZ8',
'ext': 'mp4',
'title': 'This is the WORST Movie Science We\'ve Ever Seen',
'title': 'Scientists React to Terrible Movie Science | Moonfall (2021)',
'description': 'md5:8afd0a3cd69ec63438fc573580436f92',
'media_type': 'video',
'uploader': 'Open Sauce',
'uploader_id': '@opensaucelive',
'uploader_url': 'https://www.youtube.com/@opensaucelive',
'channel': 'Open Sauce',
'uploader': 'Sauce +',
'uploader_id': '@sauceplusofficial',
'uploader_url': 'https://www.youtube.com/@sauceplusofficial',
'channel': 'Sauce +',
'channel_id': 'UC2EiGVmCeD79l_vZ204DUSw',
'channel_url': 'https://www.youtube.com/channel/UC2EiGVmCeD79l_vZ204DUSw',
'comment_count': int,
@@ -1614,15 +1614,17 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
'like_count': int,
'age_limit': 0,
'duration': 1664,
'thumbnail': 'https://i.ytimg.com/vi/brhfDfLdDZ8/hqdefault.jpg',
'thumbnail': 'https://i.ytimg.com/vi/brhfDfLdDZ8/sddefault.jpg',
'categories': ['Entertainment'],
'tags': ['Moonfall', 'Bad Science', 'Open Sauce', 'Sauce+', 'The Backyard Scientist', 'William Osman', 'Allen Pan'],
'creators': ['Open Sauce', 'William Osman 2'],
'creators': ['Sauce +', 'William Osman 2'],
'timestamp': 1759452918,
'upload_date': '20251003',
'playable_in_embed': True,
'availability': 'public',
'live_status': 'not_live',
'channel_follower_count': int,
'heatmap': 'count:100',
},
'params': {'skip_download': True},
}, {
@@ -1656,6 +1658,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
'playable_in_embed': True,
'availability': 'public',
'live_status': 'not_live',
'channel_follower_count': int,
},
'params': {'skip_download': True},
}, {
@@ -4468,13 +4471,16 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
vsir = get_first(contents, 'videoSecondaryInfoRenderer')
if vsir:
vor = traverse_obj(vsir, ('owner', 'videoOwnerRenderer'))
collaborators = traverse_obj(vor, (
collab_view_models = traverse_obj(vor, (
'attributedTitle', 'commandRuns', ..., 'onTap', 'innertubeCommand', 'showDialogCommand',
'panelLoadingStrategy', 'inlineContent', 'dialogViewModel', 'customContent', 'listViewModel',
'listItems', ..., 'listItemViewModel', 'title', 'content', {str}))
'listItems', ..., 'listItemViewModel', {dict}))
collaborators = traverse_obj(collab_view_models, (..., 'title', 'content', {str}))
info.update({
'channel': self._get_text(vor, 'title') or (collaborators[0] if collaborators else None),
'channel_follower_count': self._get_count(vor, 'subscriberCountText'),
'channel_follower_count': (
self._get_count(vor, 'subscriberCountText')
or traverse_obj(collab_view_models, (0, 'rendererContext', 'accessibilityContext', 'label', {parse_count}))),
'creators': collaborators if collaborators else None,
})
+1 -1
View File
@@ -17,7 +17,7 @@ from .traversal import traverse_obj
def random_user_agent():
USER_AGENT_TMPL = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{}.0.0.0 Safari/537.36'
# Target versions released within the last ~6 months
CHROME_MAJOR_VERSION_RANGE = (144, 150)
CHROME_MAJOR_VERSION_RANGE = (145, 151)
return USER_AGENT_TMPL.format(random.randint(*CHROME_MAJOR_VERSION_RANGE))