Repository: yt-dlp/yt-dlp
File: yt_dlp/extractor/vimeo.py (Python)

Code before:
        for tt in (request.get('text_tracks') or []):
            subtitles.setdefault(tt['lang'], []).append({
                'ext': 'vtt',
                'url': urljoin('https://vimeo.com', tt['url']),
            })

        thumbnails = []
        if not is_live:
            for key, thumb in (video_data.get('thumbs') or {}).items():
                thumbnails.append({
                    'id': key,
                    'width': int_or_none(key),
                    'url': thumb,
                })
            thumbnail = video_data.get('thumbnail')
            if thumbnail:
                thumbnails.append({
                    'url': thumbnail,
                })

            thumbnail = video_data.get('thumbnail_url')
            if thumbnail:
                thumbnails.append({
                    'url': thumbnail,
                })

        owner = video_data.get('owner') or {}
        video_uploader_url = owner.get('url')

        return {
            'id': str_or_none(video_data.get('id')) or video_id,
            'title': video_title,
            'uploader': owner.get('name'),
            'uploader_id': video_uploader_url.split('/')[-1] if video_uploader_url else None,
            'uploader_url': video_uploader_url,
            'thumbnails': thumbnails,
            'duration': int_or_none(video_data.get('duration')),
            'chapters': sorted(traverse_obj(config, (
                'embed', 'chapters', lambda _, v: int(v['timecode']) is not None, {
                    'title': ('title', {str}),
                    'start_time': ('timecode', {int_or_none}),
                })), key=lambda c: c['start_time']) or None,
            'formats': formats,
            'subtitles': subtitles,
            'live_status': live_status,
            'release_timestamp': traverse_obj(live_event, ('ingest', 'scheduled_start_time', {parse_iso8601})),
            # Note: Bitrates are completely broken. Single m3u8 may contain entries in kbps and bps
            # at the same time without actual units specified.
            '_format_sort_fields': ('quality', 'res', 'fps', 'hdr:12', 'source'),
        }


Review comment (suggestion):
I'd replace this block and the `video_data.get('thumbnail')` block above with the single line below:
```py
           thumbnails.extend(traverse_obj(video_data, (('thumbnail', 'thumbnail_url'), {'url': {url_or_none}})))
```

Code after:

        for tt in (request.get('text_tracks') or []):
            subtitles.setdefault(tt['lang'], []).append({
                'ext': 'vtt',
                'url': urljoin('https://vimeo.com', tt['url']),
            })

        thumbnails = []
        if not is_live:
            for key, thumb in (video_data.get('thumbs') or {}).items():
                thumbnails.append({
                    'id': key,
                    'width': int_or_none(key),
                    'url': thumb,
                })
            thumbnails.extend(traverse_obj(video_data, (('thumbnail', 'thumbnail_url'), {'url': {url_or_none}})))

        owner = video_data.get('owner') or {}
        video_uploader_url = owner.get('url')

        return {
            'id': str_or_none(video_data.get('id')) or video_id,
            'title': video_title,
            'uploader': owner.get('name'),
            'uploader_id': video_uploader_url.split('/')[-1] if video_uploader_url else None,
            'uploader_url': video_uploader_url,
            'thumbnails': thumbnails,
            'duration': int_or_none(video_data.get('duration')),
            'chapters': sorted(traverse_obj(config, (
                'embed', 'chapters', lambda _, v: int(v['timecode']) is not None, {
                    'title': ('title', {str}),
                    'start_time': ('timecode', {int_or_none}),
                })), key=lambda c: c['start_time']) or None,
            'formats': formats,
            'subtitles': subtitles,
            'live_status': live_status,
            'release_timestamp': traverse_obj(live_event, ('ingest', 'scheduled_start_time', {parse_iso8601})),
            # Note: Bitrates are completely broken. Single m3u8 may contain entries in kbps and bps
            # at the same time without actual units specified.
            '_format_sort_fields': ('quality', 'res', 'fps', 'hdr:12', 'source'),
        }

    def _call_videos_api(self, video_id, jwt_token, unlisted_hash=None, **kwargs):
        return self._download_json(
            join_nonempty(f'https://api.vimeo.com/videos/{video_id}', unlisted_hash, delim=':'),
            video_id, 'Downloading API JSON', headers={
                'Authorization': f'jwt {jwt_token}',
                'Accept': 'application/json',
            }, query={
                'fields': ','.join((
                    'config_url', 'created_time', 'description', 'download', 'license',