Repository: yt-dlp/yt-dlp
File: yt_dlp/downloader/hls.py (Python)

Code before:
            # r'#EXT-X-MAP:',  # media initialization [5]
            # 1. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.2.4
            # 2. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.2.2
            # 3. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.3.2
            # 4. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.3.5
            # 5. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.2.5
        ]
        if not allow_unplayable_formats:
            UNSUPPORTED_FEATURES += [
                r'#EXT-X-KEY:METHOD=(?!NONE|AES-128)',  # encrypted streams [1], but not necessarily DRM
            ]

        def check_results():
            yield not info_dict.get('is_live')
            for feature in UNSUPPORTED_FEATURES:
                yield not re.search(feature, manifest)
            if not allow_unplayable_formats:
                yield not cls._has_drm(manifest)
        return all(check_results())

    def real_download(self, filename, info_dict):
        man_url = info_dict['url']

        s = info_dict.get('manifest_data')
        if s:
            self.to_screen(f'[{self.FD_NAME}] Loading cached m3u8 manifest')
        else:
            self.to_screen(f'[{self.FD_NAME}] Downloading m3u8 manifest')
            urlh = self.ydl.urlopen(self._prepare_url(info_dict, man_url))
            man_url = urlh.url
            s = urlh.read().decode('utf-8', 'ignore')

        can_download, message = self.can_download(s, info_dict, self.params.get('allow_unplayable_formats')), None
        if can_download:
            has_ffmpeg = FFmpegFD.available()
            no_crypto = not Cryptodome.AES and '#EXT-X-KEY:METHOD=AES-128' in s
            if no_crypto and has_ffmpeg:
                can_download, message = False, 'The stream has AES-128 encryption and pycryptodomex is not available'
            elif no_crypto:
                message = ('The stream has AES-128 encryption and neither ffmpeg nor pycryptodomex are available; '
                           'Decryption will be performed natively, but will be extremely slow')
            elif info_dict.get('extractor_key') == 'Generic' and re.search(r'(?m)#EXT-X-MEDIA-SEQUENCE:(?!0$)', s):
                install_ffmpeg = '' if has_ffmpeg else 'install ffmpeg and '
                message = ('Live HLS streams are not supported by the native downloader. If this is a livestream, '
                           f'please {install_ffmpeg}add "--downloader ffmpeg --hls-use-mpegts" to your command')
        if not can_download:
            if self._has_drm(s) and not self.params.get('allow_unplayable_formats'):
                if info_dict.get('has_drm') and self.params.get('test'):
                    self.to_screen(f'[{self.FD_NAME}] This format is DRM protected', skip_eol=True)
                else:
                    self.report_error(

Review comment (suggestion):
? I wasn't sure of the best wording
```suggestion
            self.to_screen(f'[{self.FD_NAME}] Loading saved m3u8 manifest')
```

Code after:
            # r'#EXT-X-MAP:',  # media initialization [5]
            # 1. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.2.4
            # 2. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.2.2
            # 3. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.3.2
            # 4. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.3.5
            # 5. https://tools.ietf.org/html/draft-pantos-http-live-streaming-17#section-4.3.2.5
        ]
        if not allow_unplayable_formats:
            UNSUPPORTED_FEATURES += [
                r'#EXT-X-KEY:METHOD=(?!NONE|AES-128)',  # encrypted streams [1], but not necessarily DRM
            ]

        def check_results():
            yield not info_dict.get('is_live')
            for feature in UNSUPPORTED_FEATURES:
                yield not re.search(feature, manifest)
            if not allow_unplayable_formats:
                yield not cls._has_drm(manifest)
        return all(check_results())

    def real_download(self, filename, info_dict):
        man_url = info_dict['url']

        s = info_dict.get('hls_media_playlist_data')
        if s:
            self.to_screen(f'[{self.FD_NAME}] Using m3u8 manifest from extracted info')
        else:
            self.to_screen(f'[{self.FD_NAME}] Downloading m3u8 manifest')
            urlh = self.ydl.urlopen(self._prepare_url(info_dict, man_url))
            man_url = urlh.url
            s = urlh.read().decode('utf-8', 'ignore')

        can_download, message = self.can_download(s, info_dict, self.params.get('allow_unplayable_formats')), None
        if can_download:
            has_ffmpeg = FFmpegFD.available()
            no_crypto = not Cryptodome.AES and '#EXT-X-KEY:METHOD=AES-128' in s
            if no_crypto and has_ffmpeg:
                can_download, message = False, 'The stream has AES-128 encryption and pycryptodomex is not available'
            elif no_crypto:
                message = ('The stream has AES-128 encryption and neither ffmpeg nor pycryptodomex are available; '
                           'Decryption will be performed natively, but will be extremely slow')
            elif info_dict.get('extractor_key') == 'Generic' and re.search(r'(?m)#EXT-X-MEDIA-SEQUENCE:(?!0$)', s):
                install_ffmpeg = '' if has_ffmpeg else 'install ffmpeg and '
                message = ('Live HLS streams are not supported by the native downloader. If this is a livestream, '
                           f'please {install_ffmpeg}add "--downloader ffmpeg --hls-use-mpegts" to your command')
        if not can_download:
            if self._has_drm(s) and not self.params.get('allow_unplayable_formats'):
                if info_dict.get('has_drm') and self.params.get('test'):
                    self.to_screen(f'[{self.FD_NAME}] This format is DRM protected', skip_eol=True)
                else:
                    self.report_error(