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

Code before:
            'title': '@staderlabs x Hedera AMA w/ @defigirlxoxo, @bmgentile, & @thehbarbull',
            'description': 'Twitter Space partecipated by nobody yet.',
            'uploader': 'Hedera',
            'uploader_id': 'hedera',
            'live_status': 'is_upcoming',
            'timestamp': 1662394668924,
        },
        'params': {
            'ignore_no_formats_error': True,
            'skip_download': True,  # requires ffmpeg
        },
        'expected_warnings': ['Twitter Space not started yet', 'No video formats found', 'Requested format is not available'],
    }]

    def _parse_spaces_data(self, data, space_id):
        metadata = data.get('metadata')
        SPACE_SATUS = {
            'notstarted': 'is_upcoming',
            'ended': 'was_live',
            'running': 'is_live',
            'timedout': 'post_live',
        }
        live_status = SPACE_SATUS.get(metadata.get('state').lower())

        # participants
        participants = join_nonempty(*[p.get('display_name') for p in traverse_obj(data, ('participants', 'speakers'))], delim=', ') or 'nobody yet.'

        return {
            'id': space_id,
            'title': metadata.get('title'),
            'description': f'Twitter Space partecipated by {participants}',
            'media_key': metadata.get('media_key'),
            'uploader': traverse_obj(
                metadata, ('creator_results', 'result', 'legacy', 'name')),
            'uploader_id': traverse_obj(
                metadata, ('creator_results', 'result', 'legacy', 'screen_name')),
            'is_live': live_status == 'is_live',
            'live_status': live_status,
            'timestamp': metadata.get('created_at'),
        }

    def _build_graphql_query(self, space_id):
        return {
            'variables': {
                'id': space_id,
                'isMetatagsQuery': True,
                'withDownvotePerspective': False,
                'withReactionsMetadata': False,
                'withReactionsPerspective': False,
                'withReplays': True,
                'withSuperFollowsUserFields': True,

Review comment (suggestion):
```suggestion
        participants = join_nonempty(*traverse_obj(data, ('participants', 'speakers', ..., 'display_name')), delim=', ') or 'nobody yet.'
```
But I wonder if we even have to use `join_nonempty` then, as the `None` should be filtered by `traverse_obj` already?
```suggestion
        participants = ', '.join(traverse_obj(data, ('participants', 'speakers', ..., 'display_name'))) or 'nobody yet.'
```

Code after:
        broadcast_id = self._match_id(url)
        broadcast = self._call_api(
            'broadcasts/show.json', broadcast_id,
            {'ids': broadcast_id})['broadcasts'][broadcast_id]
        info = self._parse_broadcast_data(broadcast, broadcast_id)
        media_key = broadcast['media_key']
        source = self._call_api(
            f'live_video_stream/status/{media_key}', media_key)['source']
        m3u8_url = source.get('noRedirectPlaybackUrl') or source['location']
        if '/live_video_stream/geoblocked/' in m3u8_url:
            self.raise_geo_restricted()
        m3u8_id = compat_parse_qs(compat_urllib_parse_urlparse(
            m3u8_url).query).get('type', [None])[0]
        state, width, height = self._extract_common_format_info(broadcast)
        info['formats'] = self._extract_pscp_m3u8_formats(
            m3u8_url, broadcast_id, m3u8_id, state, width, height)
        return info


class TwitterSpacesIE(TwitterBaseIE):
    IE_NAME = 'twitter:spaces'
    _VALID_URL = TwitterBaseIE._BASE_REGEX + r'i/spaces/(?P<id>[0-9a-zA-Z]{13})'
    _TWITTER_GRAPHQL = 'https://twitter.com/i/api/graphql/HPEisOmj1epUNLCWTYhUWw/'

    _TESTS = [{
        'url': 'https://twitter.com/i/spaces/1RDxlgyvNXzJL',
        'info_dict': {
            'id': '1RDxlgyvNXzJL',
            'ext': 'm4a',
            'title': 'King Carlo e la mossa Kansas City per fare il Grande Centro',
            'description': 'Twitter Space participated by annarita digiorgio, Signor Ernesto, Raffaello Colosimo, Simone M. Sepe',
            'uploader': r're:Lucio Di Gaetano.*?',
            'uploader_id': 'luciodigaetano',
            'live_status': 'was_live',
            'timestamp': 1659877956397,
        },
        'params': {'skip_download': 'm3u8'},
    }]

    SPACE_STATUS = {
        'notstarted': 'is_upcoming',
        'ended': 'was_live',
        'running': 'is_live',
        'timedout': 'post_live',
    }

    def _build_graphql_query(self, space_id):
        return {
            'variables': {
                'id': space_id,
                'isMetatagsQuery': True,