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

Code before:
            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')

        channels_json = self._download_json(
            f'https://asobistage.asobistore.jp/cdn/v101/events/{event_id}/{video_type_id}.json', video_id,
            fatal=False, note='Getting channel list', errnote='Unable to get channel list')
        available_channels = traverse_obj(channels_json, (
            video_type_id, lambda _, v: v['broadcast_slug'] == event_slug, 'channels',
            lambda _, v: v['chennel_vspf_id'] != '00000', {dict}))

        owned_tickets = set(self._get_owned_tickets(video_id))
        available_tickets = traverse_obj(
            available_channels, (..., 'viewrights', ..., 'tickets', ..., 'digital_product_id', {str_or_none}))
        if not owned_tickets.intersection(available_tickets):
            raise ExtractorError('No valid ticket for this event', expected=True)

        channel_ids = traverse_obj(available_channels, (..., 'chennel_vspf_id', {str}))

        entries = []
        for channel_id in channel_ids:
            channel_data = {}

            if video_type_name == 'archive':
                channel_json = self._download_json(
                    f'https://survapi.channel.or.jp/proxy/v1/contents/{channel_id}/get_by_cuid', f'{video_id}/{channel_id}',
                    note='Getting archive channel info', errnote='Unable to get archive channel info',
                    headers={'Authorization': f'Bearer {self._TOKEN}'})
                channel_data = traverse_obj(channel_json, ('ex_content', {
                    'm3u8_url': 'streaming_url',
                    'title': 'title',
                    'thumbnail': ('thumbnail', 'url'),
                }))
            elif video_type_name == 'player':
                channel_json = self._download_json(
                    f'https://survapi.channel.or.jp/ex/events/{channel_id}', f'{video_id}/{channel_id}',
                    note='Getting live channel info', errnote='Unable to get live channel info',
                    headers={'Authorization': f'Bearer {self._TOKEN}'}, query={'embed': 'channel'})
                channel_data = traverse_obj(channel_json, ('data', {
                    'm3u8_url': ('Channel', 'Custom_live_url'),
                    'title': 'Name',
                    'thumbnail': 'Poster_url',
                }))

            m3u8_url = channel_data.get('m3u8_url')
            if not m3u8_url:

Review comment (suggestion):
```suggestion
        entries = []
        for channel_id in traverse_obj(available_channels, (..., 'chennel_vspf_id', {str})):
```

Code after:

        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):
            self._is_logged_in = True
        token = self._download_json(
            f'{self._API_HOST}/api/v1/vspf/token', None, 'Getting token', 'Unable to get token')
        self._HEADERS['Authorization'] = f'Bearer {token}'

    def _real_extract(self, url):
        video_id, event, type_, slug = self._match_valid_url(url).group('id', 'event', 'type', 'slug')
        video_type = {'archive': 'archives', 'player': 'broadcasts'}[type_]
        webpage = self._download_webpage(url, video_id)
        event_data = traverse_obj(
            self._search_nextjs_data(webpage, video_id, default='{}'),
            ('props', 'pageProps', 'eventCMSData', {
                'title': ('event_name', {str}),
                'thumbnail': ('event_thumbnail_image', {url_or_none}),
            }))

        available_channels = traverse_obj(self._download_json(
            f'https://asobistage.asobistore.jp/cdn/v101/events/{event}/{video_type}.json',
            video_id, 'Getting channel list', 'Unable to get channel list'), (
            video_type, lambda _, v: v['broadcast_slug'] == slug,
            'channels', lambda _, v: v['chennel_vspf_id'] != '00000'))

        entries = []
        for channel_id in traverse_obj(available_channels, (..., {self._get_available_channel_id})):
            if video_type == 'archives':
                channel_json = self._download_json(
                    f'https://survapi.channel.or.jp/proxy/v1/contents/{channel_id}/get_by_cuid', channel_id,
                    'Getting archive channel info', 'Unable to get archive channel info', fatal=False,
                    headers=self._HEADERS)
                channel_data = traverse_obj(channel_json, ('ex_content', {
                    'm3u8_url': 'streaming_url',