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

Code before:
        'playlist_mincount': 50,
    }, {
        'url': 'https://www.zdf.de/gesellschaft/aktenzeichen-xy-ungeloest',
        'info_dict': {
            'id': 'aktenzeichen-xy-ungeloest',
            'title': 'Aktenzeichen XY... Ungelöst',
            'description': 'md5:623ede5819c400c6d04943fa8100e6e7',
        },
        'playlist_mincount': 2,
    }, {
        'url': 'https://www.zdf.de/serien/taunuskrimi/',
        'only_matching': True,
    }]

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

    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 _extract_document_id(self, webpage):
        matches = re.search(r'docId\s*:\s*[\'"](?P<docid>[^\'"]+)[\'"]', webpage)
        return matches and matches.group('docid')

    def _get_playlist_description(self, page_data):
        headline = traverse_obj(page_data, ('shortText', 'headline'))
        text = traverse_obj(page_data, ('shortText', 'text'))
        if headline is not None and text is not None:
            return f'{headline}\n\n{text}'
        return headline or text

    def _convert_thumbnails(self, thumbnails):
        return [{
            'id': key,
            'url': thumbnail_info['url'],
            'width': int_or_none(thumbnail_info.get('width')),
            'height': int_or_none(thumbnail_info.get('height')),
        } for key, thumbnail_info in thumbnails.items() if url_or_none(thumbnail_info.get('url'))]

    def _teaser_to_url_result(self, teaser):
        return self.url_result(
            teaser['sharingUrl'], ie=ZDFIE.ie_key(),
            id=teaser.get('id'), title=teaser.get('titel', ''),
            thumbnails=self._convert_thumbnails(teaser.get('teaserBild', {})),
            description=teaser.get('beschreibung'),
            duration=float_or_none(teaser.get('length')),
            media_type=teaser.get('currentVideoType') or teaser.get('contentType'),
            season_number=int_or_none(teaser.get('seasonNumber')),
            episode_number=int_or_none(teaser.get('episodeNumber')))

Review comment (suggestion):
try to avoid methods only used once. and we would want to use `_search_regex` instead
```suggestion
```

Code after:
        'playlist_mincount': 50,
    }, {
        'url': 'https://www.zdf.de/gesellschaft/aktenzeichen-xy-ungeloest',
        'info_dict': {
            'id': 'aktenzeichen-xy-ungeloest',
            'title': 'Aktenzeichen XY... Ungelöst',
            'description': 'md5:623ede5819c400c6d04943fa8100e6e7',
        },
        'playlist_mincount': 2,
    }, {
        'url': 'https://www.zdf.de/serien/taunuskrimi/',
        'only_matching': True,
    }]

    @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(