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

Code before:
        return content or {}

    def _real_extract(self, url):
        video_id, host, display_id = self._match_valid_url(url).group('id', 'host', 'display_id')
        headers = {'Referer': url, 'X-Requested-With': 'XMLHttpRequest'}

        video_file = self._call_api(
            f'https://{host}/api/videofile.php?video_id={video_id}&lifetime=8640000',
            video_id, fatal=True, note='Downloading video file info', headers=headers)

        slug = f'{int(1E6 * (int(video_id) // 1E6))}/{1000 * (int(video_id) // 1000)}'
        video_info = self._call_api(
            f'https://{host}/api/json/video/86400/{slug}/{video_id}.json',
            video_id, note='Downloading video info', headers=headers)

        return {
            'id': video_id,
            'display_id': display_id,
            'title': traverse_obj(video_info, ('video', 'title')),
            'uploader': traverse_obj(video_info, ('video', 'user', 'username')),
            'duration': parse_duration(traverse_obj(video_info, ('video', 'duration'))),
            'view_count': int_or_none(traverse_obj(video_info, ('video', 'statistics', 'viewed'))),
            'like_count': int_or_none(traverse_obj(video_info, ('video', 'statistics', 'likes'))),
            'dislike_count': int_or_none(traverse_obj(video_info, ('video', 'statistics', 'dislikes'))),
            'age_limit': 18,
            'thumbnail': traverse_obj(video_info, ('video', 'thumbsrc')),
            'formats': get_formats(host, video_file),
        }


class PornTopIE(InfoExtractor):
    _VALID_URL = r'https?://(?P<host>(?:www\.)?porntop\.com)/video/(?P<id>\d+)(?:/(?P<display_id>[^/?]+))?'
    _TESTS = [{
        'url': 'https://porntop.com/video/101569/triple-threat-with-lia-lor-malena-morgan-and-dani-daniels/',
        'md5': '612ba7b3cb99455b382972948e200b08',
        'info_dict': {
            'id': '101569',
            'display_id': 'triple-threat-with-lia-lor-malena-morgan-and-dani-daniels',
            'ext': 'mp4',
            'title': 'Triple Threat With Lia Lor, Malena Morgan And Dani Daniels',
            'description': 'md5:285357d9d3a00ce5acb29f39f826dbf6',
            'uploader': 'PatrickBush',
            'duration': 480,
            'view_count': int,
            'like_count': int,
            'dislike_count': int,
            'age_limit': 18,
            'timestamp': 1609455029,
            'upload_date': '20201231',
            'thumbnail': 'https://tn.porntop.com/media/tn/sources/101569_1.jpg',
        }

Review comment (suggestion):
maybe we should use `url_or_none` just in case? Need to import it from `utils`
```suggestion
            'thumbnail': traverse_obj(video_info, ('video', 'thumbsrc', {url_or_none})),
```

Code after:
                f'Txxx said: {content["error"]}', expected=True), fatal=fatal)
        return content or {}

    def _real_extract(self, url):
        video_id, host, display_id = self._match_valid_url(url).group('id', 'host', 'display_id')
        headers = {'Referer': url, 'X-Requested-With': 'XMLHttpRequest'}

        video_file = self._call_api(
            f'https://{host}/api/videofile.php?video_id={video_id}&lifetime=8640000',
            video_id, fatal=True, note='Downloading video file info', headers=headers)

        slug = f'{int(1E6 * (int(video_id) // 1E6))}/{1000 * (int(video_id) // 1000)}'
        video_info = self._call_api(
            f'https://{host}/api/json/video/86400/{slug}/{video_id}.json',
            video_id, note='Downloading video info', headers=headers)

        return {
            'id': video_id,
            'display_id': display_id,
            'title': traverse_obj(video_info, ('video', 'title')),
            'uploader': traverse_obj(video_info, ('video', 'user', 'username')),
            'duration': parse_duration(traverse_obj(video_info, ('video', 'duration'))),
            'view_count': int_or_none(traverse_obj(video_info, ('video', 'statistics', 'viewed'))),
            'like_count': int_or_none(traverse_obj(video_info, ('video', 'statistics', 'likes'))),
            'dislike_count': int_or_none(traverse_obj(video_info, ('video', 'statistics', 'dislikes'))),
            'age_limit': 18,
            'thumbnail': traverse_obj(video_info, ('video', 'thumbsrc', {url_or_none})),
            'formats': get_formats(host, video_file),
        }


class PornTopIE(InfoExtractor):
    _VALID_URL = r'https?://(?P<host>(?:www\.)?porntop\.com)/video/(?P<id>\d+)(?:/(?P<display_id>[^/?]+))?'
    _TESTS = [{
        'url': 'https://porntop.com/video/101569/triple-threat-with-lia-lor-malena-morgan-and-dani-daniels/',
        'md5': '612ba7b3cb99455b382972948e200b08',
        'info_dict': {
            'id': '101569',
            'display_id': 'triple-threat-with-lia-lor-malena-morgan-and-dani-daniels',
            'ext': 'mp4',
            'title': 'Triple Threat With Lia Lor, Malena Morgan And Dani Daniels',
            'description': 'md5:285357d9d3a00ce5acb29f39f826dbf6',
            'uploader': 'PatrickBush',
            'duration': 480,
            'view_count': int,
            'like_count': int,
            'dislike_count': int,
            'age_limit': 18,
            'timestamp': 1609455029,
            'upload_date': '20201231',
            'thumbnail': 'https://tn.porntop.com/media/tn/sources/101569_1.jpg',