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

Code before:
            '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(metadata_available=False)
        else:
            raise ExtractorError(f'Unknown error: {error}')

    def _get_token(self, video_id):
        return self._search_regex(r'\"([^"]+)\"', self._download_webpage(
            'https://asobistage-api.asobistore.jp/api/v1/vspf/token', video_id,
            note='Getting token', errnote='Unable to get token'),
            name="token")

    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'),
        ]:
            for id in traverse_obj(self._download_json(
                    url, video_id, note=f'Downloading {name}', errnote=f'Unable to download {name}'),
                    ('payload', 'value', ..., 'digital_product_id', {str})):
                yield id

    def _real_extract(self, url):
        video_id = self._match_id(url)
        video_type_name = self._match_valid_url(url).group('type')
        webpage = self._download_webpage(url, video_id)

        self._check_login(video_id)

        video_type_id, live_status = {
            'archive': ['archives', 'was_live'],
            'player': ['broadcasts', 'is_live'],
        }.get(video_type_name) or [None, None]
        if not video_type_id:
            raise ExtractorError('Unknown video type')

        event_data = self._search_nextjs_data(webpage, video_id)
        event_id = traverse_obj(event_data, ('query', 'event', {str}))
        event_slug = traverse_obj(event_data, ('query', 'player_slug', {str}))
        event_title = traverse_obj(event_data, ('props', 'pageProps', 'eventCMSData', 'event_name', {str}))
        if not all((event_id, event_slug, event_title)):
            raise ExtractorError('Unable to get required event data')

        channel_list_url = functools.reduce(urljoin, [
            self._search_regex(
                r'"(?P<url>https://asobistage\.asobistore\.jp/cdn/[^/]+/)', webpage,

Review comment (suggestion):
I think this should also work:
```suggestion
            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}))
```

Code after:
            '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(
                traverse_obj(response, ('payload', 'value', ..., 'digital_product_id', {str_or_none})))

        return owned_tickets

    def _get_available_channel_id(self, channel):
        channel_id = traverse_obj(channel, ('chennel_vspf_id', {str}))
        if not channel_id:
            return None
        # if rights_type_id == 6, then 'No conditions (no login required - non-members are OK)'
        if traverse_obj(channel, ('viewrights', lambda _, v: v['rights_type_id'] == 6)):
            return channel_id
        available_tickets = traverse_obj(channel, (
            'viewrights', ..., ('tickets', 'serialcodes'), ..., 'digital_product_id', {str_or_none}))
        if not self._owned_tickets.intersection(available_tickets):
            self.report_warning(
                f'You are not a ticketholder for "{channel.get("channel_name") or channel_id}"')
            return None
        return channel_id

    def _real_initialize(self):
        if self._get_cookies(self._API_HOST):