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

Code before:
    _TOKEN = None

    def _perform_login(self, username, password):
        response = self._download_json(
            f'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={self._API_KEY}', None, note='Logging in',
            data=urlencode_postdata({'email': username, 'password': password, 'returnSecureToken': True}))

        if response:
            self._TOKEN = response['idToken']

    def _call_api(self, endpoint, item_id):
        return self._download_json(
            self._ENDPOINTS[endpoint].format(item_id), item_id,
            note=f'Downloading {endpoint} JSON metadata',
            headers={'Authorization': f'Bearer {self._TOKEN}'})

    def _get_vimeo_id(self, activity_id):
        # Launch API is completely different depending on how old/new the course is.
        launch_api = self._call_api('launch', activity_id)

        try:
            vimeo_id = self._search_regex(r'https?://player.vimeo.com/video/(?P<vimeo_id>[0-9]+)', launch_api['url'], 'vimeo_id')
        except KeyError:
            vimeo_id = next((x for x in traverse_obj(launch_api, ('vendor_data', 'content', ..., 'videoId'))), None)

        return vimeo_id


class CybraryIE(CybraryBaseIE):
    _VALID_URL = r'https?://app.cybrary.it/immersive/(?P<enrollment>[0-9]+)/activity/(?P<id>[0-9]+)'
    _TESTS = [{
        'url': 'https://app.cybrary.it/immersive/12487950/activity/63102',
        'md5': '9ae12d37e555cb2ed554223a71a701d0',
        'info_dict': {
            'id': '646609770',
            'ext': 'mp4',
            'title': 'Getting Started',
            'thumbnail': 'https://i.vimeocdn.com/video/1301817996-76a268f0c56cff18a5cecbbdc44131eb9dda0c80eb0b3a036_1280',
            'series_id': '63111',
            'uploader_url': 'https://vimeo.com/user30867300',
            'duration': 88,
            'uploader_id': 'user30867300',
            'series': 'Cybrary Orientation',
            'uploader': 'Cybrary',
            'chapter': 'Cybrary Orientation Series',
            'chapter_id': '63110'
        },
        'expected_warnings': ['No authenticators for vimeo']
    }, {
        'url': 'https://app.cybrary.it/immersive/12747143/activity/52686',
        'md5': '62f26547dccc59c44363e2a13d4ad08d',

Review comment (suggestion):
```suggestion
        if launch_api.get('url'):
            return self._search_regex(r'https?://player\.vimeo\.com/video/(?P<vimeo_id>[0-9]+)', launch_api['url'], 'vimeo_id')
        return traverse_obj(launch_api, ('vendor_data', 'content', ..., 'videoId'), get_all=False)
```

Code after:
    _TOKEN = None

    def _perform_login(self, username, password):
        CybraryBaseIE._TOKEN = self._download_json(
            f'https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={self._API_KEY}',
            None, data=urlencode_postdata({'email': username, 'password': password, 'returnSecureToken': True}),
            note='Logging in')['idToken']

    def _real_initialize(self):
        if not self._TOKEN:
            self.raise_login_required(method='password')

    def _call_api(self, endpoint, item_id):
        return self._download_json(
            self._ENDPOINTS[endpoint].format(item_id), item_id,
            note=f'Downloading {endpoint} JSON metadata',
            headers={'Authorization': f'Bearer {self._TOKEN}'})

    def _get_vimeo_id(self, activity_id):
        launch_api = self._call_api('launch', activity_id)

        if launch_api.get('url'):
            return self._search_regex(r'https?://player\.vimeo\.com/video/(?P<vimeo_id>[0-9]+)', launch_api['url'], 'vimeo_id')
        return traverse_obj(launch_api, ('vendor_data', 'content', ..., 'videoId'), get_all=False)


class CybraryIE(CybraryBaseIE):
    _VALID_URL = r'https?://app.cybrary.it/immersive/(?P<enrollment>[0-9]+)/activity/(?P<id>[0-9]+)'
    _TESTS = [{
        'url': 'https://app.cybrary.it/immersive/12487950/activity/63102',
        'md5': '9ae12d37e555cb2ed554223a71a701d0',
        'info_dict': {
            'id': '646609770',
            'ext': 'mp4',
            'title': 'Getting Started',
            'thumbnail': 'https://i.vimeocdn.com/video/1301817996-76a268f0c56cff18a5cecbbdc44131eb9dda0c80eb0b3a036_1280',
            'series_id': '63111',
            'uploader_url': 'https://vimeo.com/user30867300',
            'duration': 88,
            'uploader_id': 'user30867300',
            'series': 'Cybrary Orientation',
            'uploader': 'Cybrary',
            'chapter': 'Cybrary Orientation Series',
            'chapter_id': '63110'
        },
        'expected_warnings': ['No authenticators for vimeo']
    }, {
        'url': 'https://app.cybrary.it/immersive/12747143/activity/52686',
        'md5': '62f26547dccc59c44363e2a13d4ad08d',
        'info_dict': {
            'id': '445638073',