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

Code before:
        'only_matching': True,
    }, ]

    def _real_extract(self, url):
        course_id, hostname, year, term, slug = self._match_valid_url(url).group('id', 'hostname', 'year', 'term', 'slug')

        json = f'{hostname}/api/courses/{slug}/?year={year}&term={term}'
        try:
            meta = self._download_json(json, course_id)

            lecture_series_title = meta.get('Name')
            streams = meta.get('Streams') or []

            lecture_urls = [self.url_result(f'{hostname}/w/{slug}/{stream["ID"]}', ie=RbgTumIE) for stream in streams if stream.get('ID')]

            return self.playlist_result(lecture_urls, course_id, lecture_series_title)
        except ExtractorError as e:
            self.report_warning(f'Failed to download JSON: {e.cause}, falling back to HTML parsing')
            webpage = self._download_webpage(url, course_id)

            lecture_series_title = self._html_search_regex(r'<title>(?:TUM-Live \| )(.*)</title>', webpage, 'title')

            lecture_urls = []
            for lecture_url in re.findall(r'href="/w/([^/]+/[^/"]+)"', webpage):
                lecture_urls.append(self.url_result(f'{hostname}/w/{lecture_url}', ie=RbgTumIE))

            return self.playlist_result(lecture_urls, course_id, lecture_series_title)


class RbgTumNewCourseIE(InfoExtractor):
    _VALID_URL = r'(?P<hostname>https://(live\.rbg\.tum\.de|tum\.live))/\?'
    _TESTS = [{
        'url': 'https://live.rbg.tum.de/?year=2022&term=S&slug=fpv&view=3',
        'info_dict': {
            'title': 'Funktionale Programmierung und Verifikation (IN0003)',
            'id': '2022/S/fpv',
        },
        'params': {
            'noplaylist': False,
        },
        'playlist_count': 13,
    }, {
        'url': 'https://live.rbg.tum.de/?year=2022&term=W&slug=set&view=3',
        'info_dict': {
            'title': 'SET FSMPIC',
            'id': '2022/W/set',
        },
        'params': {
            'noplaylist': False,
        },
        'playlist_count': 6,

Review comment (suggestion):
```suggestion
        meta = self._download_json(
            f'https://{hostname}/api/courses/{slug}/', course_id, fatal=False,
            query={'year': year, 'term': term}) or {}
        lecture_series_title = meta.get('Name')
        lectures = [self.url_result(f'https://{hostname}/w/{slug}/{stream_id}', RbgTumIE)
                    for stream_id in traverse_obj(meta, ('streams', ..., 'id'))]
            
        if not lectures:
            webpage = self._download_webpage(url, course_id)
            lecture_series_title = self._html_search_regex(
                r'<title>(?:TUM-Live \| )([^:<]+):?[^<]*</title>', webpage, 'title')
            lectures = [self.url_result(f'https://{hostname}/{lecture_path}', RbgTumIE)
                        for lecture_path in re.findall(r'href="(/w/[^"]+)"', webpage)]

        return self.playlist_result(lectures, course_id, lecture_series_title)
```

Code after:
    }, ]

    def _real_extract(self, url):
        course_id, hostname, year, term, slug = self._match_valid_url(url).group('id', 'hostname', 'year', 'term', 'slug')
        meta = self._download_json(
            f'https://{hostname}/api/courses/{slug}/', course_id, fatal=False,
            query={'year': year, 'term': term}) or {}
        lecture_series_title = meta.get('Name')
        lectures = [self.url_result(f'https://{hostname}/w/{slug}/{stream_id}', RbgTumIE)
                    for stream_id in traverse_obj(meta, ('Streams', ..., 'ID'))]

        if not lectures:
            webpage = self._download_webpage(url, course_id)
            lecture_series_title = remove_start(self._html_extract_title(webpage), 'TUM-Live | ')
            lectures = [self.url_result(f'https://{hostname}{lecture_path}', RbgTumIE)
                        for lecture_path in re.findall(r'href="(/w/[^/"]+/[^/"]+)"', webpage)]

        return self.playlist_result(lectures, course_id, lecture_series_title)


class RbgTumNewCourseIE(InfoExtractor):
    _VALID_URL = r'https://(?P<hostname>(?:live\.rbg\.tum\.de|tum\.live))/\?'
    _TESTS = [{
        'url': 'https://live.rbg.tum.de/?year=2022&term=S&slug=fpv&view=3',
        'info_dict': {
            'title': 'Funktionale Programmierung und Verifikation (IN0003)',
            'id': '2022/S/fpv',
        },
        'params': {
            'noplaylist': False,
        },
        'playlist_count': 13,
    }, {
        'url': 'https://live.rbg.tum.de/?year=2022&term=W&slug=set&view=3',
        'info_dict': {
            'title': 'SET FSMPIC',
            'id': '2022/W/set',
        },
        'params': {
            'noplaylist': False,
        },
        'playlist_count': 6,
    }, {
        'url': 'https://tum.live/?year=2023&term=S&slug=linalginfo&view=3',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        query = parse_qs(url)
        errors = [key for key in ('year', 'term', 'slug') if not query.get(key)]
        if errors: