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

Code before:
from .common import InfoExtractor
from ..utils import (
    traverse_obj,
    ExtractorError,
    # GeoRestrictedError,
)


class NetverseBaseIE(InfoExtractor):
    def get_required_json(self, url, data=None):
        match = self._match_valid_url(url).groupdict()
        display_id, sites_type = match['display_id'], match['type']

        if sites_type == 'watch' or sites_type == 'video':
            media_api_url = f'https://api.netverse.id/medias/api/v2/watchvideo/{display_id}'
        elif sites_type == 'webseries':
            media_api_url = f'https://api.netverse.id/medias/api/v2/webseries/{display_id}'

        json_data = self._download_json(media_api_url, display_id, data=data)

        if json_data.get('error'):
            raise ExtractorError(json_data.get('message'))

        return json_data

    def get_access_id(self, dailymotion_url):
        return dailymotion_url.split('/')[-1]

    def _call_metadata_api_from_video_url(self, dailymotion_url):
        access_id = self.get_access_id(dailymotion_url)
        metadata_json = self._call_metadata_api(access_id)
        return access_id, metadata_json

    def _call_metadata_api(self, access_id, req_file_type='video', query=None):
        video_metadata_api_url = f'https://www.dailymotion.com/player/metadata/{req_file_type}/{access_id}'

        if query is None:
            required_query = {
                'embedder': 'https://www.netverse.id'
            }
        else:
            required_query = query

        return self._download_json(video_metadata_api_url, access_id, query=required_query)


class NetverseIE(NetverseBaseIE):

Review comment (refactor):
```suggestion
        if json_data.get('error'):
            raise ExtractorError(f'{self.IE_NAME} said: {json_data.get("message")}')
```

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
        'url': 'https://www.netverse.id/watch/jadoo-seorang-model',