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

Code before:
            'like_count': int,
            'is_mature': False,
            'duration': 49.0,
            'thumbnail': 'https://clips.kick.com/clips/j3/clip_01H9SKET879NE7N9RJRRDS98J3/thumbnail.png',
            'uploader_id': 2027722,
            'channel': 'Destiny',
            'channel_id': 1772249,
            'category_id': 15,
            'uploader': 'punished_furry',
            'category': 'Just Chatting',
            'livestream_id': 14549749,
            'upload_date': '20230908',
        },
        'params': {
            'skip_download': 'm3u8',
        },
        'expected_warnings': [r'impersonation'],

    }]

    def _real_extract(self, url):

        channel_slug, clip_id = self._match_valid_url(url).groups()
        response = self._call_api(f'v2/clips/{clip_id}/play', clip_id, note='Getting clip information')
        clip = traverse_obj(response, 'clip', expected_type=dict)
        clip_url = traverse_obj(clip, 'clip_url')

        if determine_ext(clip_url) == 'm3u8':
            formats = self._extract_m3u8_formats(clip_url, clip_id, 'mp4')
        else:
            formats = [{'url': clip_url}]

        return {
            'id': clip_id,
            'formats': formats,
            'ext': 'mp4',
            'title': traverse_obj(clip, 'title', default=''),
            'livestream_id': int_or_none(traverse_obj(clip, 'livestream_id')),
            'category_id': int_or_none(traverse_obj(clip, 'category_id')),
            'channel': traverse_obj(clip, ('channel', 'username')),
            'channel_id': int_or_none(traverse_obj(clip, ('channel', 'id'))),
            'uploader': traverse_obj(clip, ('creator', 'username')),
            'uploader_id': int_or_none(traverse_obj(clip, ('creator', 'id'))),
            'thumbnail': url_or_none(traverse_obj(clip, 'thumbnail_url')),
            'duration': float_or_none(traverse_obj(clip, 'duration')),
            'category': traverse_obj(clip, ('category', 'name'), default=''),
            'upload_date': unified_strdate(traverse_obj(clip, 'created_at')),
            'view_count': int_or_none(traverse_obj(clip, 'likes')),
            'like_count': int_or_none(traverse_obj(clip, 'views')),
            'is_mature': traverse_obj(clip, 'is_mature'),
        }

Review comment (suggestion):
same here, it's ok for `clip_url` to be fatal
```suggestion
        clip_url = clip['clip_url']
```

Code after:
    }]

    def _real_extract(self, url):
        clip_id = self._match_id(url)
        clip = self._call_api(f'v2/clips/{clip_id}/play', clip_id)['clip']
        clip_url = clip['clip_url']

        if determine_ext(clip_url) == 'm3u8':
            formats = self._extract_m3u8_formats(clip_url, clip_id, 'mp4')
        else:
            formats = [{'url': clip_url}]

        return {
            'id': clip_id,
            'formats': formats,
            **traverse_obj(clip, {
                'title': ('title', {str}),
                'channel': ('channel', 'slug', {str}),
                'channel_id': ('channel', 'id', {int}, {str_or_none}),
                'uploader': ('creator', 'username', {str}),
                'uploader_id': ('creator', 'id', {int}, {str_or_none}),
                'thumbnail': ('thumbnail_url', {url_or_none}),
                'duration': ('duration', {float_or_none}),
                'categories': ('category', 'name', {str}, all),
                'timestamp': ('created_at', {parse_iso8601}),
                'view_count': ('views', {int_or_none}),
                'like_count': ('likes', {int_or_none}),
                'age_limit': ('is_mature', {bool}, {lambda x: 18 if x else 0}),
            }),
        }