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

Code before:
            'ext': 'mp4',
            'uploader': 'ミス東大No.5 松藤百香🍑💫',
            'uploader_id': 12299174,
            'channel_follower_count': int,
            'view_count': int,
            'like_count': int,
            'comment_count': int,
            'uploader_url': 'https://mixch.tv/u/12299174',
            'live_status': 'not_live',
        },
    }, {
        'url': 'https://mixch.tv/m/61DzpIKE',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        data = self._download_json(
            f'https://mixch.tv/api-web/movies/{video_id}', video_id)
        return {
            'id': video_id,
            'formats': [{'format_id': 'mp4',
                         'url': traverse_obj(data, ('movie', 'file'), {url_or_none}),
                         'ext': 'mp4'}],
            **traverse_obj(data, {
                'title': ('movie', 'title', {str_or_none}),
                'thumbnail': ('movie', 'thumbnailURL', {url_or_none}),
                'uploader': ('ownerInfo', 'name', {str_or_none}),
                'uploader_id': ('ownerInfo', 'id', {int_or_none}),
                'channel_follower_count': ('ownerInfo', 'fan', {int_or_none}),
                'view_count': ('ownerInfo', 'view', {int_or_none}),
                'like_count': ('movie', 'favCount', {int_or_none}),
                'comment_count': ('movie', 'commentCount', {int_or_none}),
            }),
            'uploader_url': 'https://mixch.tv/u/' + traverse_obj(data, ('ownerInfo', 'id', {str_or_none})),
            'live_status': 'not_live',
            '__post_extractor': self.extract_comments(video_id),
        }

    def _get_comments(self, video_id):
        data = self._download_json(f'https://mixch.tv/api-web/movies/{video_id}/comments?', video_id,
                                   note='Downloading comments', errnote='Failed to download comments')
        comment_dl_times = 1
        MAX_DL_TIMES = 10
        has_next = True
        next_cursor = ''

        # Comments are organized in a json chain, connected with 'nextCursor' property.
        # There are up to 20 comments in one json file.
        while has_next:
            yield from traverse_obj(data,

Review comment (suggestion):
Instead of force converting to string, I'd rather check the type. Imagine if they turned the title in to a dict with `short` and `normal` as keys. `{str}` will just return no title, `{str_or_none}` will stringify that returning bad data.
```suggestion
                'title': ('movie', 'title', {str}),
```

Code after:
            'uploader': 'ミス東大No.5 松藤百香🍑💫',
            'uploader_id': '12299174',
            'channel_follower_count': int,
            'view_count': int,
            'like_count': int,
            'comment_count': int,
            'timestamp': 1724070828,
            'uploader_url': 'https://mixch.tv/u/12299174',
            'live_status': 'not_live',
            'upload_date': '20240819',
        },
    }, {
        'url': 'https://mixch.tv/m/61DzpIKE',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        data = self._download_json(
            f'https://mixch.tv/api-web/movies/{video_id}', video_id)
        return {
            'id': video_id,
            'formats': [{
                'format_id': 'mp4',
                'url': data['movie']['file'],
                'ext': 'mp4',
            }],
            **traverse_obj(data, {
                'title': ('movie', 'title', {str}),
                'thumbnail': ('movie', 'thumbnailURL', {url_or_none}),
                'uploader': ('ownerInfo', 'name', {str}),
                'uploader_id': ('ownerInfo', 'id', {int}, {str_or_none}),
                'channel_follower_count': ('ownerInfo', 'fan', {int_or_none}),
                'view_count': ('ownerInfo', 'view', {int_or_none}),
                'like_count': ('movie', 'favCount', {int_or_none}),
                'comment_count': ('movie', 'commentCount', {int_or_none}),
                'timestamp': ('movie', 'published', {int_or_none}),
                'uploader_url': ('ownerInfo', 'id', {lambda x: x and f'https://mixch.tv/u/{x}'}, filter),
            }),
            'live_status': 'not_live',
        }