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

Code before:
import functools

from .common import InfoExtractor
from .dailymotion import DailymotionIE
from ..utils import (
    InAdvancePagedList,
    smuggle_url,
    traverse_obj,
)


class NetverseBaseIE(InfoExtractor):
    _ENDPOINTS = {
        'watch': 'watchvideo',
        'video': 'watchvideo',
        'webseries': 'webseries',
        'season': 'webseason_videos',
    }

    def _call_api(self, input_data, query={}, season='',
                  custom_id=None, force_endpoint_type='auto', input_type='url'):

        slug = None
        if input_type == 'url':
            display_id, sites_type = self._match_valid_url(input_data).group('display_id', 'type')
        elif input_type == 'slug':
            slug = input_data
        display_id = display_id if slug is None else slug
        endpoint = self._ENDPOINTS[sites_type] if force_endpoint_type == 'auto' else self._ENDPOINTS[force_endpoint_type]

        json_data = self._download_json(
            f'https://api.netverse.id/medias/api/v2/{endpoint}/{display_id}/{season}',
            custom_id or display_id, query=query)
        return display_id, json_data


class NetverseIE(NetverseBaseIE):
    _VALID_URL = r'https?://(?:\w+\.)?netverse\.id/(?P<type>watch|video)/(?P<display_id>[^/?#&]+)'
    _TESTS = [{
        # Watch video
        'url': 'https://www.netverse.id/watch/waktu-indonesia-bercanda-edisi-spesial-lebaran-2016',
        'info_dict': {
            'id': 'k4yhqUwINAGtmHx3NkL',
            'title': 'Waktu Indonesia Bercanda - Edisi Spesial Lebaran 2016',
            'ext': 'mp4',
            'season': 'Season 2016',

Review comment (suggestion):
This doesn't really make sense. Try:
```suggestion
    def _call_api(self, url=None, slug=None, endpoint=None, query={}, season=''):
        assert (url is None and slug) or (slug is None and url)
        ...
```
I don't see the point of `custom_id`

Code after:
from .common import InfoExtractor
from .dailymotion import DailymotionIE
from ..utils import smuggle_url, traverse_obj


class NetverseBaseIE(InfoExtractor):
    _ENDPOINTS = {
        'watch': 'watchvideo',
        'video': 'watchvideo',
        'webseries': 'webseries',
        'season': 'webseason_videos',
    }

    def _call_api(self, slug, endpoint, query={}, season_id='', display_id=None):
        return self._download_json(
            f'https://api.netverse.id/medias/api/v2/{self._ENDPOINTS[endpoint]}/{slug}/{season_id}',
            display_id or slug, query=query)


class NetverseIE(NetverseBaseIE):
    _VALID_URL = r'https?://(?:\w+\.)?netverse\.id/(?P<type>watch|video)/(?P<display_id>[^/?#&]+)'
    _TESTS = [{
        # Watch video
        'url': 'https://www.netverse.id/watch/waktu-indonesia-bercanda-edisi-spesial-lebaran-2016',
        'info_dict': {
            'id': 'k4yhqUwINAGtmHx3NkL',
            'title': 'Waktu Indonesia Bercanda - Edisi Spesial Lebaran 2016',
            'ext': 'mp4',
            'season': 'Season 2016',
            'description': 'md5:d41d8cd98f00b204e9800998ecf8427e',
            'thumbnail': r're:https?://s\d+\.dmcdn\.net/v/T7aV31Y0eGRWBbwkK/x1080',
            'episode_number': 22,
            'episode': 'Episode 22',
            'uploader_id': 'x2ir3vq',
            'age_limit': 0,
            'tags': [],
            'view_count': int,
            'display_id': 'waktu-indonesia-bercanda-edisi-spesial-lebaran-2016',
            'duration': 2990,
            'upload_date': '20210722',
            'timestamp': 1626919804,
            'like_count': int,
            'uploader': 'Net Prime',
        }
    }, {
        # series