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

Code before:
            return

        if not CrunchyrollBaseIE._BASIC_AUTH:
            cx_api_param = self._CLIENT_ID[self.is_logged_in]
            self.write_debug(f'Using cxApiParam={cx_api_param}')
            CrunchyrollBaseIE._BASIC_AUTH = 'Basic ' + base64.b64encode(f'{cx_api_param}:'.encode()).decode()

        auth_headers = {'Authorization': CrunchyrollBaseIE._BASIC_AUTH}
        if self.is_logged_in:
            grant_type = 'etp_rt_cookie'
        else:
            grant_type = 'client_id'
            auth_headers['ETP-Anonymous-ID'] = uuid.uuid4()
        try:
            auth_response = self._download_json(
                f'{self._BASE_URL}/auth/v1/token', None, note=f'Authenticating with grant_type={grant_type}',
                headers=auth_headers, data=f'grant_type={grant_type}'.encode())
        except ExtractorError as error:
            if isinstance(error.cause, HTTPError) and error.cause.status == 403:
                raise ExtractorError(
                    'Request blocked by Cloudflare; navigate to Crunchyroll in your browser, '
                    'then pass the fresh cookies (with --cookies-from-browser or --cookies) '
                    'and your browser\'s User-Agent (with --user-agent)', expected=True)
            raise

        CrunchyrollBaseIE._IS_PREMIUM = 'cr_premium' in jwt_decode_hs256(auth_response['access_token']).get('benefits', [])
        CrunchyrollBaseIE._AUTH_HEADERS = {'Authorization': auth_response['token_type'] + ' ' + auth_response['access_token']}
        CrunchyrollBaseIE._AUTH_REFRESH = time_seconds(seconds=traverse_obj(auth_response, ('expires_in', {float_or_none}), default=300) - 10)

    def _locale_from_language(self, language):
        config_locale = self._configuration_arg('metadata', ie_key=CrunchyrollBetaIE, casesense=True)
        return config_locale[0] if config_locale else self._LOCALE_LOOKUP.get(language)

    def _call_base_api(self, endpoint, internal_id, lang, note=None, query={}):
        self._update_auth()

        if not endpoint.startswith('/'):
            endpoint = f'/{endpoint}'

        query = query.copy()
        locale = self._locale_from_language(lang)
        if locale:
            query['locale'] = locale

        return self._download_json(
            f'{self._BASE_URL}{endpoint}', internal_id, note or f'Calling API: {endpoint}',
            headers=CrunchyrollBaseIE._AUTH_HEADERS, query=query)

    def _call_api(self, path, internal_id, lang, note='api', query={}):
        if not path.startswith(f'/content/v2/{self._API_ENDPOINT}/'):
            path = f'/content/v2/{self._API_ENDPOINT}/{path}'

Review comment (security):
assuming `benefits` is a list/array, I would do this
```suggestion
        CrunchyrollBaseIE._IS_PREMIUM = 'cr_premium' in traverse_obj(auth_response, ('access_token', {jwt_decode_hs256}, 'benefits', ...))
        ```

Code after:
            return

        if not CrunchyrollBaseIE._BASIC_AUTH:
            cx_api_param = self._CLIENT_ID[self.is_logged_in]
            self.write_debug(f'Using cxApiParam={cx_api_param}')
            CrunchyrollBaseIE._BASIC_AUTH = 'Basic ' + base64.b64encode(f'{cx_api_param}:'.encode()).decode()

        auth_headers = {'Authorization': CrunchyrollBaseIE._BASIC_AUTH}
        if self.is_logged_in:
            grant_type = 'etp_rt_cookie'
        else:
            grant_type = 'client_id'
            auth_headers['ETP-Anonymous-ID'] = uuid.uuid4()
        try:
            auth_response = self._download_json(
                f'{self._BASE_URL}/auth/v1/token', None, note=f'Authenticating with grant_type={grant_type}',
                headers=auth_headers, data=f'grant_type={grant_type}'.encode())
        except ExtractorError as error:
            if isinstance(error.cause, HTTPError) and error.cause.status == 403:
                raise ExtractorError(
                    'Request blocked by Cloudflare; navigate to Crunchyroll in your browser, '
                    'then pass the fresh cookies (with --cookies-from-browser or --cookies) '
                    'and your browser\'s User-Agent (with --user-agent)', expected=True)
            raise

        CrunchyrollBaseIE._IS_PREMIUM = 'cr_premium' in traverse_obj(auth_response, ('access_token', {jwt_decode_hs256}, 'benefits', ...))
        CrunchyrollBaseIE._AUTH_HEADERS = {'Authorization': auth_response['token_type'] + ' ' + auth_response['access_token']}
        CrunchyrollBaseIE._AUTH_REFRESH = time_seconds(seconds=traverse_obj(auth_response, ('expires_in', {float_or_none}), default=300) - 10)

    def _locale_from_language(self, language):
        config_locale = self._configuration_arg('metadata', ie_key=CrunchyrollBetaIE, casesense=True)
        return config_locale[0] if config_locale else self._LOCALE_LOOKUP.get(language)

    def _call_base_api(self, endpoint, internal_id, lang, note=None, query={}):
        self._update_auth()

        if not endpoint.startswith('/'):
            endpoint = f'/{endpoint}'

        query = query.copy()
        locale = self._locale_from_language(lang)
        if locale:
            query['locale'] = locale

        return self._download_json(
            f'{self._BASE_URL}{endpoint}', internal_id, note or f'Calling API: {endpoint}',
            headers=CrunchyrollBaseIE._AUTH_HEADERS, query=query)

    def _call_api(self, path, internal_id, lang, note='api', query={}):
        if not path.startswith(f'/content/v2/{self._API_ENDPOINT}/'):
            path = f'/content/v2/{self._API_ENDPOINT}/{path}'