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

Code before:
        urlh = self._request_webpage(
            urlh.geturl(), None, fatal=False, headers={'referer': urlh.geturl()},
            note='logging in', errnote='unable to log in', data=urlencode_postdata({
                'lang': 'de',
                '_eventId_proceed': '',
                'j_username': username,
                'j_password': password
            }))

        if urlh and urlh.geturl() != 'https://tube.tugraz.at/paella/ui/index.html':
            self.report_warning('unable to login: incorrect password')

    def _extract_episode(self, episode_info):
        id = episode_info.get('id')
        formats = list(self._extract_formats(
            traverse_obj(episode_info, ('mediapackage', 'media', 'track')), id))

        title = traverse_obj(episode_info, ('mediapackage', 'title'), 'dcTitle')
        series_title = traverse_obj(episode_info, ('mediapackage', 'seriestitle'))
        creator = ', '.join(variadic(traverse_obj(
            episode_info, ('mediapackage', 'creators', 'creator'), 'dcCreator', default='')))
        return {
            'id': id,
            'title': title,
            'creator': creator or None,
            'duration': traverse_obj(episode_info, ('mediapackage', 'duration'), 'dcExtent'),
            'series': series_title,
            'series_id': traverse_obj(episode_info, ('mediapackage', 'series'), 'dcIsPartOf'),
            'episode': series_title and title,
            'formats': formats
        }

    def _set_format_type(self, formats, type):
        for f in formats:
            f['format_note'] = type
            if not type.startswith(self._FORMAT_TYPES[0]):
                f['preference'] = -2
        return formats

    def _extract_formats(self, format_list, id):
        has_hls, has_dash = False, False

        for format_info in format_list or []:
            url = traverse_obj(format_info, ('tags', 'url'), 'url')
            if url is None:
                continue

            type = format_info.get('type') or 'unknown'
            transport = (format_info.get('transport') or 'https').lower()

            if transport == 'https':

Review comment (suggestion):
```suggestion
        content, urlh = self._download_webpage_handle(
            urlh.geturl(), None, fatal=False, headers={'referer': urlh.geturl()},
            note='logging in with TFA', errnote='unable to log in with TFA',
            data=urlencode_postdata({
                'lang': 'de',
                '_eventId_proceed': '',
                'j_tokenNumber': self._get_tfa_info(),
            }))
```

Code after:
        content, urlh = self._download_webpage_handle(
            urlh.geturl(), None, fatal=False, headers={'referer': urlh.geturl()},
            note='logging in', errnote='unable to log in',
            data=urlencode_postdata({
                'lang': 'de',
                '_eventId_proceed': '',
                'j_username': username,
                'j_password': password
            }))
        if not urlh or urlh.geturl() == 'https://tube.tugraz.at/paella/ui/index.html':
            return

        if not self._html_search_regex(
                r'<p\b[^>]*>(Bitte geben Sie einen OTP-Wert ein:)</p>',
                content, 'TFA prompt', default=None):
            self.report_warning('unable to login: incorrect password')
            return

        content, urlh = self._download_webpage_handle(
            urlh.geturl(), None, fatal=False, headers={'referer': urlh.geturl()},
            note='logging in with TFA', errnote='unable to log in with TFA',
            data=urlencode_postdata({
                'lang': 'de',
                '_eventId_proceed': '',
                'j_tokenNumber': self._get_tfa_info(),
            }))
        if not urlh or urlh.geturl() == 'https://tube.tugraz.at/paella/ui/index.html':
            return

        self.report_warning('unable to login: incorrect TFA code')

    def _extract_episode(self, episode_info):
        id = episode_info.get('id')
        formats = list(self._extract_formats(
            traverse_obj(episode_info, ('mediapackage', 'media', 'track')), id))

        title = traverse_obj(episode_info, ('mediapackage', 'title'), 'dcTitle')
        series_title = traverse_obj(episode_info, ('mediapackage', 'seriestitle'))
        creator = ', '.join(variadic(traverse_obj(
            episode_info, ('mediapackage', 'creators', 'creator'), 'dcCreator', default='')))
        return {
            'id': id,
            'title': title,
            'creator': creator or None,
            'duration': traverse_obj(episode_info, ('mediapackage', 'duration'), 'dcExtent'),
            'series': series_title,
            'series_id': traverse_obj(episode_info, ('mediapackage', 'series'), 'dcIsPartOf'),
            'episode': series_title and title,
            'formats': formats
        }