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

Code before:
            'view_count': get_count('watch'),
            'like_count': get_count('like'),
            'comment_count': get_count('comment'),
            'comments': comments,
            'categories': [category] if category else None,
        }
        info.update(self._extract_streamer_info(vod_detail_info))
        return info


class TrovoBatchIE(InfoExtractor):
    def _entries(self, uid):
        for page in itertools.count(1):
            vod_json = self._get_vod_json(page, uid)
            vods = vod_json.get('vodInfos', [])
            for vod in vods:
                yield self.url_result(
                    'https://trovo.live/video/%s' % vod.get('vid'),
                    ie=TrovoVodIE.ie_key())
            has_more = vod_json['hasMore']
            if not has_more:
                break

    def _real_extract(self, url):
        id = self._match_id(url)
        uid = str(self._download_json('https://gql.trovo.live/', id, query={
            'query': '{getLiveInfo(params:{userName:"%s"}){streamerInfo{uid}}}' % id
        })['data']['getLiveInfo']['streamerInfo']['uid'])
        return self.playlist_result(self._entries(uid), playlist_id=uid)


class TrovoBatchVodIE(TrovoBatchIE):
    _VALID_URL = r'trovovod:(?P<id>[^\s]+)'
    IE_DESC = 'All VODs of a trovo.live channel, "trovovod" keyword'

    _TESTS = [{
        'url': 'trovovod:OneTappedYou',
        'playlist_mincount': 24,
        'info_dict': {
            'id': '100719456',
        },
    }]

    _QUERY = '{getChannelLtvVideoInfos(params:{pageSize:99,currPage:%d,channelID:%s}){hasMore,vodInfos{vid}}}'

    def _get_vod_json(self, page, uid):
        return self._download_json('https://gql.trovo.live/', uid, query={
            'query': self._QUERY % (page, uid)
        })['data']['getChannelLtvVideoInfos']



Review comment (suggestion):
```suggestion

    def _get_vod_json(self, page, uid):
        raise NotImplementedError('This method must be implemented by subclasses')

```

for self-documentation

Code after:
            'view_count': get_count('watch'),
            'like_count': get_count('like'),
            'comment_count': get_count('comment'),
            'comments': comments,
            'categories': [category] if category else None,
        }
        info.update(self._extract_streamer_info(vod_detail_info))
        return info


class TrovoChannelBaseIE(InfoExtractor):
    def _get_vod_json(self, page, uid):
        raise NotImplementedError('This method must be implemented by subclasses')

    def _entries(self, uid):
        for page in itertools.count(1):
            vod_json = self._get_vod_json(page, uid)
            vods = vod_json.get('vodInfos', [])
            for vod in vods:
                yield self.url_result(
                    'https://trovo.live/%s/%s' % (self._TYPE, vod.get('vid')),
                    ie=TrovoVodIE.ie_key())
            has_more = vod_json['hasMore']
            if not has_more:
                break

    def _real_extract(self, url):
        id = self._match_id(url)
        uid = str(self._download_json('https://gql.trovo.live/', id, query={
            'query': '{getLiveInfo(params:{userName:"%s"}){streamerInfo{uid}}}' % id
        })['data']['getLiveInfo']['streamerInfo']['uid'])
        return self.playlist_result(self._entries(uid), playlist_id=uid)


class TrovoChannelVodIE(TrovoChannelBaseIE):
    _VALID_URL = r'trovovod:(?P<id>[^\s]+)'
    IE_DESC = 'All VODs of a trovo.live channel, "trovovod" keyword'

    _TESTS = [{
        'url': 'trovovod:OneTappedYou',
        'playlist_mincount': 24,
        'info_dict': {
            'id': '100719456',
        },
    }]

    _QUERY = '{getChannelLtvVideoInfos(params:{pageSize:99,currPage:%d,channelID:%s}){hasMore,vodInfos{vid}}}'
    _TYPE = 'video'

    def _get_vod_json(self, page, uid):
        return self._download_json('https://gql.trovo.live/', uid, query={