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

Code before:
        return fmts

    def _parse_mpd_formats_and_subtitles(self, *args, **kwargs):
        periods = self._parse_mpd_periods(*args, **kwargs)
        return self._merge_mpd_periods(periods)

    def _merge_mpd_periods(self, periods):
        """
        Combine all formats and subtitles from an MPD manifest into a single list,
        by concatenate streams with similar formats.
        """
        formats, subtitles = [], {}

        # merge periods by concatenating fragments
        # require almost all parameters to be the same
        shared_keys = [k for k in periods[0]['formats'][0].keys()
                       if k not in ('format_id', 'fragments', 'manifest_stream_number',
                                    'dash_period_id', 'dash_period_idx')]

        # create merged formats
        stream_numbers = collections.defaultdict(int)
        groups = {}
        formats = []
        for period in periods:
            for f in period['formats']:
                format_key = tuple(f[k] for k in shared_keys)
                if 'fragments' not in f or format_key not in groups:
                    # unmergeable format, or first period of this format
                    mf = {}
                    mf.update(f)
                    mf['manifest_stream_number'] = stream_numbers[mf['url']]
                    formats.append(mf)
                    # keep track of mergeable formats
                    if 'fragments' in f:
                        mf['is_dash_periods'] = True
                        mf['fragments'] = f['fragments'].copy()
                        groups[format_key] = mf
                else:
                    # follow-up period of a mergeable format
                    groups[format_key]['fragments'] += f['fragments']

        for period in periods:
            subtitles.update(period['subtitles'])

        if len(subtitles) > 0 and len(periods) > 1:
            self.report_warning(bug_reports_message(
                'Ignoring multiple periods found in the DASH manifest; '
                'if part of the subtitles are missing,'
            ), only_once=True)

        return formats, subtitles

Review comment (suggestion):
```suggestion
                format_key = tuple(v for k, v in f.values() if k not in (
                    'format_id', 'fragments', 'manifest_stream_number'))
```

Code after:

    def _parse_mpd_formats_and_subtitles(self, *args, **kwargs):
        periods = self._parse_mpd_periods(*args, **kwargs)
        return self._merge_mpd_periods(periods)

    def _merge_mpd_periods(self, periods):
        """
        Combine all formats and subtitles from an MPD manifest into a single list,
        by concatenate streams with similar formats.
        """
        formats, subtitles = {}, {}
        for period in periods:
            for f in period['formats']:
                assert 'is_dash_periods' not in f, 'format already processed'
                f['is_dash_periods'] = True
                format_key = tuple(v for k, v in f.items() if k not in (
                    ('format_id', 'fragments', 'manifest_stream_number')))
                if format_key not in formats:
                    formats[format_key] = f
                elif 'fragments' in f:
                    formats[format_key].setdefault('fragments', []).extend(f['fragments'])

            if subtitles and period['subtitles']:
                self.report_warning(bug_reports_message(
                    'Found subtitles in multiple periods in the DASH manifest; '
                    'if part of the subtitles are missing,'
                ), only_once=True)

            for sub_lang, sub_info in period['subtitles'].items():
                subtitles.setdefault(sub_lang, []).extend(sub_info)

        return list(formats.values()), subtitles

    def _parse_mpd_periods(self, mpd_doc, mpd_id=None, mpd_base_url='', mpd_url=None):
        """
        Parse formats from MPD manifest.
        References:
         1. MPEG-DASH Standard, ISO/IEC 23009-1:2014(E),
            http://standards.iso.org/ittf/PubliclyAvailableStandards/c065274_ISO_IEC_23009-1_2014.zip
         2. https://en.wikipedia.org/wiki/Dynamic_Adaptive_Streaming_over_HTTP
        """
        if not self.get_param('dynamic_mpd', True):
            if mpd_doc.get('type') == 'dynamic':
                return [], {}

        namespace = self._search_regex(r'(?i)^{([^}]+)?}MPD$', mpd_doc.tag, 'namespace', default=None)

        def _add_ns(path):
            return self._xpath_ns(path, namespace)

        def is_drm_protected(element):