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

Code before:
        if video_info := self._download_json(f'{base_url}/data/secure', video_id, 'Trying auth token',
                                             headers=headers, fatal=False):
            video_extra = self._download_json(f'{base_url}', video_id, 'Retrieving extra attributes',
                                              headers=headers, fatal=False)

        # Blackboard will allow redownloading from the same IP without authentication for a while, so if previous method fails, try this
        else:
            video_info = self._download_json(f'{base_url}/data', video_id, 'Trying fallback')
            video_extra = 0

        # Get metadata
        duration = video_info.get('duration') / 1000
        title = video_info.get('name')
        upload_date = video_info.get('created')

        # Get streams
        stream_formats = []
        streams = video_info.get('extStreams')  # Can also use video_info.get('streams') but I don't know its structure

        for current_stream in streams:
            stream_formats.append({
                'url': current_stream['streamUrl'],
                'container': mimetype2ext(current_stream.get('contentType')),
                'filesize': video_extra.get('storageSize', None),
                'aspect_ratio': video_info.get('aspectRatio', ''),
            })

        # Get subtitles
        subtitles = {}
        subs = video_info.get('subtitles')
        for current_subs in subs:
            lang_code = current_subs.get('lang')
            subtitles.setdefault(lang_code, []).append({
                'name': current_subs.get('label'),
                'url': current_subs['url'],
            })

        # Get chat
        chats = video_info.get('chats')
        for current_chat in chats:
            subtitles.setdefault('live_chat', []).append({'url': current_chat['url']})

        return {
            'duration': duration,
            'formats': stream_formats,
            'id': video_id,
            'timestamp': parse_iso8601(upload_date),
            'subtitles': subtitles,
            'title': title,
        }


Review comment (suggestion):
Try to use more `traverse_obj()` and other util functions. For example:

```diff
-duration = video_info.get('duration') / 1000
+duration = int_or_none(video_info.get('duration'), 1000)

```

.

```diff
-stream_formats = []
-...
-for current_stream in streams:
+formats = traverse_obj(video_info, ('extStreams', ..., {
+    'url': ('streamUrl', {url_or_none}),
+    'container': ('contentType', {mimetype2ext}),
+    ...
+}))

```

.

Code after:
        token = parse_qs(url).get('authToken', [None])[-1]

        video_info = self._call_api(region, video_id, path='data/secure', token=token, note='Trying auth token')
        if video_info:
            video_extra = self._call_api(region, video_id, token=token, note='Retrieving extra attributes')
        else:
            video_info = self._call_api(region, video_id, path='data', note='Trying fallback', fatal=True)
            video_extra = {}

        formats = traverse_obj(video_info, ('extStreams', lambda _, v: url_or_none(v['streamUrl']), {
            'url': 'streamUrl',
            'ext': ('contentType', {mimetype2ext}),
            'aspect_ratio': ('aspectRatio', {float_or_none}),
        }))

        if filesize := traverse_obj(video_extra, ('storageSize', {int_or_none})):
            for fmt in formats:
                fmt['filesize'] = filesize

        subtitles = {}
        for subs in traverse_obj(video_info, ('subtitles', lambda _, v: url_or_none(v['url']))):
            subtitles.setdefault(subs.get('lang') or 'und', []).append({
                'name': traverse_obj(subs, ('label', {str})),
                'url': subs['url'],
            })

        for live_chat_url in traverse_obj(video_info, ('chats', ..., 'url', {url_or_none})):
            subtitles.setdefault('live_chat', []).append({'url': live_chat_url})

        return {
            **traverse_obj(video_info, {
                'title': ('name', {str}),
                'timestamp': ('created', {parse_iso8601}),
                'duration': ('duration', {int_or_none(scale=1000)}),
            }),
            'formats': formats,
            'id': video_id,
            'subtitles': subtitles,
        }


class BlackboardCollaborateLaunchIE(InfoExtractor):
    _VALID_URL = r'https?://[a-z]+\.bbcollab\.com/launch/(?P<id>[^/?#]+)'

    _TESTS = [
        {
            'url': 'https://au.bbcollab.com/launch/eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJiYkNvbGxhYkFwaSIsInN1YiI6ImJiQ29sbGFiQXBpIiwiZXhwIjoxNzQwNDE2NDgzLCJpYXQiOjE3NDA0MTYxODMsInJlc291cmNlQWNjZXNzVGlja2V0Ijp7InJlc291cmNlSWQiOiI3MzI4YzRjZTNmM2U0ZTcwYmY3MTY3N2RkZTgzMzk2NSIsImNvbnN1bWVySWQiOiJhM2Q3NGM0Y2QyZGU0MGJmODFkMjFlODNlMmEzNzM5MCIsInR5cGUiOiJSRUNPUkRJTkciLCJyZXN0cmljdGlvbiI6eyJ0eXBlIjoiVElNRSIsImV4cGlyYXRpb25Ib3VycyI6MCwiZXhwaXJhdGlvbk1pbnV0ZXMiOjUsIm1heFJlcXVlc3RzIjotMX0sImRpc3Bvc2l0aW9uIjoiTEFVTkNIIiwibGF1bmNoVHlwZSI6bnVsbCwibGF1bmNoQ29tcG9uZW50IjpudWxsLCJsYXVuY2hQYXJhbUtleSI6bnVsbH19.xuELw4EafEwUMoYcCHidGn4Tw9O1QCbYHzYGJUl0kKk',
            'only_matching': True,
        },
        {
            'url': 'https://us.bbcollab.com/launch/eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJiYkNvbGxhYkFwaSIsInN1YiI6ImJiQ29sbGFiQXBpIiwiZXhwIjoxNjk0NDgxOTc3LCJpYXQiOjE2OTQ0ODE2NzcsInJlc291cmNlQWNjZXNzVGlja2V0Ijp7InJlc291cmNlSWQiOiI3YWU0MTFhNTU3NjU0OWFiOTZlYjVmMTM1YmY3MWU5MCIsImNvbnN1bWVySWQiOiJBRUU2MEI4MDI2QzM3ODU2RjMwMzNEN0ZEOTQzMTFFNSIsInR5cGUiOiJSRUNPUkRJTkciLCJyZXN0cmljdGlvbiI6eyJ0eXBlIjoiVElNRSIsImV4cGlyYXRpb25Ib3VycyI6MCwiZXhwaXJhdGlvbk1pbnV0ZXMiOjUsIm1heFJlcXVlc3RzIjotMX0sImRpc3Bvc2l0aW9uIjoiTEFVTkNIIiwibGF1bmNoVHlwZSI6bnVsbCwibGF1bmNoQ29tcG9uZW50IjpudWxsLCJsYXVuY2hQYXJhbUtleSI6bnVsbH19.yOhRZNaIjXYoMYMpcTzgjZJCnIFaYf2cAzbco8OAxlY',