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

Code before:

        chapter_marks = t.get('streamAnchorTag') or []
        chapter_marks.append({'anchorOffset': int_or_none(t.get('duration'))})
        chapters = [{
            'start_time': chap.get('anchorOffset'),
            'end_time': next_chap.get('anchorOffset'),
            'title': chap.get('anchorLabel'),
        } for chap, next_chap in zip(chapter_marks, chapter_marks[1:])]

        return merge_dicts(info, {
            'title': title,
            'description': content.get('leadParagraph') or content.get('teasertext'),
            'duration': int_or_none(t.get('duration')),
            'timestamp': unified_timestamp(content.get('editorialDate')),
            'thumbnails': thumbnails,
            'chapters': chapters or None,
            'episode': title,
            **traverse_obj(content, ('programmeItem', 0, 'http://zdf.de/rels/target', {
                'series_id': ('http://zdf.de/rels/cmdm/series', 'seriesUuid', {str}),
                'series': ('http://zdf.de/rels/cmdm/series', 'seriesTitle', {str}),
                'season': ('http://zdf.de/rels/cmdm/season', 'seasonTitle', {str}),
                'season_number': ('http://zdf.de/rels/cmdm/season', 'seasonNumber', {int_or_none}),
                'season_id': ('http://zdf.de/rels/cmdm/season', 'seasonUuid', {str}),
                'episode_number': ('episodeNumber', {int_or_none}),
                'episode_id': ('contentId', {str}),
            })),
        })

    def _extract_regular(self, url, player, video_id):
        player_url = player['content']

        for query in ({'profile': 'player-3'}, None):
            try:
                content = self._call_api(
                    update_url_query(player_url, query),
                    video_id, 'content', player['apiToken'], url)
            except ExtractorError as e:
                if query is None:
                    raise
                self.report_warning(f'{video_id}: {e.orig_msg}; retrying with v2 profile')

        return self._extract_entry(player_url, player, content, video_id)

    def _extract_mobile(self, video_id):
        video = self._download_v2_doc(video_id)

        formats = []
        formitaeten = try_get(video, lambda x: x['document']['formitaeten'], list)
        document = formitaeten and video['document']
        if formitaeten:
            title = document['titel']

Review comment (suggestion):
~~Currently, the usage of `merge_dicts` will overwrite these metadata fields if they already exist in the `info` dict. Do we want that? or do we want to priotize the new data? Or doesn't it matter?~~

Code after:

        chapter_marks = t.get('streamAnchorTag') or []
        chapter_marks.append({'anchorOffset': int_or_none(t.get('duration'))})
        chapters = [{
            'start_time': chap.get('anchorOffset'),
            'end_time': next_chap.get('anchorOffset'),
            'title': chap.get('anchorLabel'),
        } for chap, next_chap in zip(chapter_marks, chapter_marks[1:])]

        return merge_dicts(info, {
            'title': title,
            'description': content.get('leadParagraph') or content.get('teasertext'),
            'duration': int_or_none(t.get('duration')),
            'timestamp': unified_timestamp(content.get('editorialDate')),
            'thumbnails': thumbnails,
            'chapters': chapters or None,
            'episode': title,
            **traverse_obj(content, ('programmeItem', 0, 'http://zdf.de/rels/target', {
                'series_id': ('http://zdf.de/rels/cmdm/series', 'seriesUuid', {str}),
                'series': ('http://zdf.de/rels/cmdm/series', 'seriesTitle', {str}),
                'season': ('http://zdf.de/rels/cmdm/season', 'seasonTitle', {str}),
                'season_number': ('http://zdf.de/rels/cmdm/season', 'seasonNumber', {int_or_none}),
                'season_id': ('http://zdf.de/rels/cmdm/season', 'seasonUuid', {str}),
                'episode_number': ('episodeNumber', {int_or_none}),
                'episode_id': ('contentId', {str}),
            })),
        })

    def _extract_regular(self, url, player, video_id):
        player_url = player['content']

        try:
            content = self._call_api(
                update_url_query(player_url, {'profile': 'player-3'}),
                video_id, 'content', player['apiToken'], url)
        except ExtractorError as e:
            self.report_warning(f'{video_id}: {e.orig_msg}; retrying with v2 profile')
            content = self._call_api(
                player_url, video_id, 'content', player['apiToken'], url)

        return self._extract_entry(player_url, player, content, video_id)

    def _extract_mobile(self, video_id):
        video = self._download_v2_doc(video_id)

        formats = []
        formitaeten = try_get(video, lambda x: x['document']['formitaeten'], list)
        document = formitaeten and video['document']
        if formitaeten:
            title = document['titel']
            content_id = document['basename']