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

Code before:
    def _og_search_title(self, webpage, fatal=False):
        title = super()._og_search_title(webpage, fatal=fatal)
        return re.split(r'\s+[-|]\s+ZDF(?:mediathek)?$', title or '')[0] or None

    def _real_extract(self, url):
        channel_id = self._match_id(url)

        webpage = self._download_webpage(url, channel_id)

        matches = re.finditer(
            fr'''<div\b[^>]*?\sdata-plusbar-id\s*=\s*(["'])(?P<p_id>[\w-]+)\1[^>]*?\sdata-plusbar-url=\1(?P<url>{ZDFIE._VALID_URL})\1''',
            webpage)

        if self._downloader.params.get('noplaylist', False):
            entry = next(
                (self.url_result(m.group('url'), ie=ZDFIE.ie_key()) for m in matches),
                None)
            self.to_screen('Downloading just the main video because of --no-playlist')
            if entry:
                return entry
        else:
            self.to_screen(f'Downloading playlist {channel_id} - add --no-playlist to download just the main video')

        def check_video(m):
            v_ref = self._search_regex(
                fr'''(<a\b[^>]*?\shref\s*=[^>]+?\sdata-target-id\s*=\s*(["']){m.group('p_id')}\2[^>]*>)''',
                webpage, 'check id', default='')
            v_ref = extract_attributes(v_ref)
            return v_ref.get('data-target-video-type') != 'novideo'

        return self.playlist_from_matches(
            (m.group('url') for m in matches if check_video(m)),
            channel_id, self._og_search_title(webpage, fatal=False))


Review comment (suggestion):
```suggestion
                rf'''(<a\b[^>]*?\shref\s*=[^>]+?\sdata-target-id\s*=\s*(["']){m.group("p_id")}\2[^>]*>)''',
```

Code after:
    }]

    @classmethod
    def suitable(cls, url):
        return False if ZDFIE.suitable(url) else super().suitable(url)

    def _extract_entry(self, entry):
        return self.url_result(
            entry['sharingUrl'], ZDFIE, **traverse_obj(entry, {
                'id': ('basename', {str}),
                'title': ('titel', {str}),
                'description': ('beschreibung', {str}),
                'duration': ('length', {float_or_none}),
                # TODO: seasonNumber and episodeNumber can be extracted but need to also be in ZDFIE
            }))

    def _entries(self, data, document_id):
        for entry in traverse_obj(data, (
            'cluster', lambda _, v: v['type'] == 'teaser',
            # If 'brandId' differs, it is a 'You might also like' video. Filter these out
            'teaser', lambda _, v: v['type'] == 'video' and v['brandId'] == document_id and v['sharingUrl'],
        )):
            yield self._extract_entry(entry)

    def _real_extract(self, url):
        channel_id = self._match_id(url)
        webpage = self._download_webpage(url, channel_id)
        document_id = self._search_regex(
            r'docId\s*:\s*(["\'])(?P<doc_id>(?:(?!\1).)+)\1', webpage, 'document id', group='doc_id')
        data = self._download_v2_doc(document_id)

        main_video = traverse_obj(data, (
            'cluster', lambda _, v: v['type'] == 'teaserContent',
            'teaser', lambda _, v: v['type'] == 'video' and v['basename'] and v['sharingUrl'], any)) or {}

        if not self._yes_playlist(channel_id, main_video.get('basename')):
            return self._extract_entry(main_video)

        return self.playlist_result(
            self._entries(data, document_id), channel_id,
            re.split(r'\s+[-|]\s+ZDF(?:mediathek)?$', self._og_search_title(webpage) or '')[0] or None,
            join_nonempty(
                'headline', 'text', delim='\n\n',
                from_dict=traverse_obj(data, ('shortText', {dict}), default={})) or None)