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

Code before:
            'live_status': 'was_live',
            'thumbnail': r're:^https?://[\w.-]+/\w+/\w+',
        },
        'params': {
            'skip_download': True,
            'ignore_no_formats_error': True,
        },
    }, {
        'url': 'https://asobistage.asobistore.jp/event/ijigenfes_utagassen/player/day1',
        'only_matching': True,
    }]

    _TOKEN = ''

    def _check_login(self, video_id):
        check_login_json = self._download_json(
            'https://asobistage-api.asobistore.jp/api/v1/check_login', video_id, expected_status=(400),
            note='Checking login status', errnote='Unable to check login status')
        error = traverse_obj(check_login_json, ('payload', 'error_message'), ('error'), get_all=False)

        if not error:
            return
        elif error == 'notlogin':
            self.raise_login_required()
        else:
            raise ExtractorError(f'Unknown error: {error!r}')

    def _get_owned_tickets(self, video_id):
        for url, name in [
            ('https://asobistage-api.asobistore.jp/api/v1/purchase_history/list', 'ticket purchase history'),
            ('https://asobistage-api.asobistore.jp/api/v1/serialcode/list', 'redemption history'),
        ]:
            yield from traverse_obj(self._download_json(
                url, video_id, note=f'Downloading {name}', errnote=f'Unable to download {name}'),
                ('payload', 'value', ..., 'digital_product_id', {str}))

    def _real_initialize(self):
        self._TOKEN = self._download_json(
            'https://asobistage-api.asobistore.jp/api/v1/vspf/token', None,
            note='Getting token', errnote='Unable to get token')

    def _real_extract(self, url):
        video_id, video_type_name = self._match_valid_url(url).group('id', 'type')
        self._check_login(video_id)

        webpage = self._download_webpage(url, video_id)

        video_type_id = {
            'archive': 'archives',
            'player': 'broadcasts',
        }.get(video_type_name)

Review comment (refactor):
```suggestion
        raise ExtractorError(f'Unknown error: {error!r}')
```

Code after:
        }],
    }, {
        'url': 'https://asobistage.asobistore.jp/event/idolmaster_idolworld2023_goods/archive/live',
        'info_dict': {
            'id': 'idolmaster_idolworld2023_goods/archive/live',
            'title': 'md5:378510b6e830129d505885908bd6c576',
            'thumbnail': r're:^https?://[\w.-]+/\w+/\w+',
        },
        'playlist_count': 1,
        'playlist': [{
            'info_dict': {
                'id': '3aef7110',
                'ext': 'mp4',
                'title': 'asobistore_station_1020_serverREC',
                'thumbnail': r're:^https?://[\w.-]+/\w+/\w+',
            },
        }],
    }, {
        'url': 'https://asobistage.asobistore.jp/event/sidem_fclive_bpct/archive/premium_hc',
        'playlist_count': 4,
        'info_dict': {
            'id': 'sidem_fclive_bpct/archive/premium_hc',
            'title': '315 Production presents F＠NTASTIC COMBINATION LIVE ～BRAINPOWER!!～/～CONNECTIME!!!!～',
            'thumbnail': r're:^https?://[\w.-]+/\w+/\w+',
        },
    }, {
        'url': 'https://asobistage.asobistore.jp/event/ijigenfes_utagassen/player/day1',
        'only_matching': True,
    }]

    _API_HOST = 'https://asobistage-api.asobistore.jp'
    _HEADERS = {}
    _is_logged_in = False

    @functools.cached_property
    def _owned_tickets(self):
        owned_tickets = set()
        if not self._is_logged_in:
            return owned_tickets

        for path, name in [
            ('api/v1/purchase_history/list', 'ticket purchase history'),
            ('api/v1/serialcode/list', 'redemption history'),
        ]:
            response = self._download_json(
                f'{self._API_HOST}/{path}', None, f'Downloading {name}',
                f'Unable to download {name}', expected_status=400)
            if traverse_obj(response, ('payload', 'error_message'), 'error') == 'notlogin':
                self._is_logged_in = False
                break
            owned_tickets.update(