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

Code before:
            raise

        if not result:
            raise ExtractorError(f'Unexpected response when downloading {note} JSON')
        return result

    def _extract_chapters(self, internal_id):
        # if no skip events are available, a 403 xml error is returned
        skip_events = self._download_json(
            f'https://static.crunchyroll.com/skip-events/production/{internal_id}.json',
            internal_id, note='Downloading chapter info', fatal=False, errnote=False) or {}

        chapters = []
        for event in ('recap', 'intro', 'credits', 'preview'):
            start = skip_events.get(event, {}).get('start')
            end = skip_events.get(event, {}).get('end')

            # some chapters have no start and/or ending time, they will just be ignored
            if start is None or end is None:
                continue

            chapters.append({'title': event.capitalize(), 'start_time': start, 'end_time': end})

        return chapters

    def _extract_formats(self, identifier, display_id=None):
        if not display_id:
            display_id = identifier
        stream_response = self._download_json(
            f'https://cr-play-service.prd.crunchyrollsvc.com/v1/{identifier}/console/switch/play',
            display_id, note='stream info', headers=CrunchyrollBaseIE._AUTH_HEADERS)

        available_formats = {'': ('', '', stream_response['url'])}
        for hardsub_lang, stream in traverse_obj(stream_response, ('hardSubs', {dict.items}, ...)):
            available_formats[hardsub_lang] = (f'hardsub-{hardsub_lang}', hardsub_lang, stream['url'])

        requested_hardsubs = [('' if val == 'none' else val) for val in (self._configuration_arg('hardsub') or ['none'])]
        if '' in available_formats and 'all' not in requested_hardsubs:
            full_format_langs = set(requested_hardsubs)
            self.to_screen(
                'To get all formats of a hardsub language, use '
                '"--extractor-args crunchyrollbeta:hardsub=<language_code or all>". '
                'See https://github.com/yt-dlp/yt-dlp#crunchyrollbeta-crunchyroll for more info',
                only_once=True)
        else:
            full_format_langs = set(map(str.lower, available_formats))

        audio_locale = stream_response.get('audioLocale')
        hardsub_preference = qualities(requested_hardsubs[::-1])
        formats = []
        for format_id, hardsub_lang, stream_url in available_formats.values():

Review comment (refactor):
I would name this either `_extract_stream()` as you originally had it and Grub suggested, or else rename it to `_extract_formats_and_subtitles()`

Code after:
            raise

        if not result:
            raise ExtractorError(f'Unexpected response when downloading {note} JSON')
        return result

    def _extract_chapters(self, internal_id):
        # if no skip events are available, a 403 xml error is returned
        skip_events = self._download_json(
            f'https://static.crunchyroll.com/skip-events/production/{internal_id}.json',
            internal_id, note='Downloading chapter info', fatal=False, errnote=False)
        if not skip_events:
            return None

        chapters = []
        for event in ('recap', 'intro', 'credits', 'preview'):
            start = traverse_obj(skip_events, (event, 'start', {float_or_none}))
            end = traverse_obj(skip_events, (event, 'end', {float_or_none}))
            # some chapters have no start and/or ending time, they will just be ignored
            if start is None or end is None:
                continue
            chapters.append({'title': event.capitalize(), 'start_time': start, 'end_time': end})

        return chapters

    def _extract_stream(self, identifier, display_id=None):
        if not display_id:
            display_id = identifier

        self._update_auth()
        stream_response = self._download_json(
            f'https://cr-play-service.prd.crunchyrollsvc.com/v1/{identifier}/console/switch/play',
            display_id, note='Downloading stream info', headers=CrunchyrollBaseIE._AUTH_HEADERS)

        available_formats = {'': ('', '', stream_response['url'])}
        for hardsub_lang, stream in traverse_obj(stream_response, ('hardSubs', {dict.items}, lambda _, v: v[1]['url'])):
            available_formats[hardsub_lang] = (f'hardsub-{hardsub_lang}', hardsub_lang, stream['url'])

        requested_hardsubs = [('' if val == 'none' else val) for val in (self._configuration_arg('hardsub') or ['none'])]
        hardsub_langs = [lang for lang in available_formats if lang]
        if hardsub_langs and 'all' not in requested_hardsubs:
            full_format_langs = set(requested_hardsubs)
            self.to_screen(f'Available hardsub languages: {", ".join(hardsub_langs)}')
            self.to_screen(
                'To extract formats of a hardsub language, use '
                '"--extractor-args crunchyrollbeta:hardsub=<language_code or all>". '
                'See https://github.com/yt-dlp/yt-dlp#crunchyrollbeta-crunchyroll for more info',
                only_once=True)
        else:
            full_format_langs = set(map(str.lower, available_formats))