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

Code before:
        return {
            '_type': 'playlist',
            'id': show_id,
            'title': title,
            'entries': reversed(entries),
        }


class CrunchyrollBetaBaseIE(CrunchyrollBaseIE):
    params = None

    def _get_params(self, lang):
        if not CrunchyrollBetaBaseIE.params:
            initial_state, app_config = self._get_beta_embedded_json(self._download_webpage(
                f'https://beta.crunchyroll.com/{lang}', None, note='Retrieving main page'), None)
            api_domain = app_config['cxApiParams']['apiDomain']
            basic_token = str(base64.b64encode(('%s:' % app_config['cxApiParams']['accountAuthClientId']).encode('ascii')), 'ascii')
            auth_response = self._download_json(
                f'{api_domain}/auth/v1/token', None,
                note='Authenticating with cookie',
                headers={
                    'Authorization': 'Basic ' + basic_token
                }, data='grant_type=etp_rt_cookie'.encode('ascii'))
            policy_response = self._download_json(
                f'{api_domain}/index/v2', None,
                note='Retrieving signed policy',
                headers={
                    'Authorization': auth_response['token_type'] + ' ' + auth_response['access_token']
                })
            bucket = policy_response['cms']['bucket']
            params = {
                'Policy': policy_response['cms']['policy'],
                'Signature': policy_response['cms']['signature'],
                'Key-Pair-Id': policy_response['cms']['key_pair_id']
            }
            locale = traverse_obj(initial_state, ('localization', 'locale'))
            if locale:
                params['locale'] = locale
            CrunchyrollBetaBaseIE.params = (api_domain, bucket, params)
        return CrunchyrollBetaBaseIE.params


class CrunchyrollBetaIE(CrunchyrollBetaBaseIE):
    IE_NAME = 'crunchyroll:beta'
    _VALID_URL = r'https?://beta\.crunchyroll\.com/(?P<lang>(?:\w{1,2}/)?)watch/(?P<id>\w+)/(?P<display_id>[\w\-]*)/?(?:\?|$)'
    _TESTS = [{
        'url': 'https://beta.crunchyroll.com/watch/GY2P1Q98Y/to-the-future',
        'info_dict': {
            'id': '696363',
            'ext': 'mp4',
            'timestamp': 1459610100,

Review comment (suggestion):
```suggestion
                f'{api_domain}/index/v2', None, note='Retrieving signed policy',
```

Code after:
        }


class CrunchyrollBetaBaseIE(CrunchyrollBaseIE):
    params = None

    def _get_params(self, lang):
        if not CrunchyrollBetaBaseIE.params:
            initial_state, app_config = self._get_beta_embedded_json(self._download_webpage(
                f'https://beta.crunchyroll.com/{lang}', None, note='Retrieving main page'), None)
            api_domain = app_config['cxApiParams']['apiDomain']
            basic_token = str(base64.b64encode(('%s:' % app_config['cxApiParams']['accountAuthClientId']).encode('ascii')), 'ascii')
            auth_response = self._download_json(
                f'{api_domain}/auth/v1/token', None, note='Authenticating with cookie',
                headers={
                    'Authorization': 'Basic ' + basic_token
                }, data='grant_type=etp_rt_cookie'.encode('ascii'))
            policy_response = self._download_json(
                f'{api_domain}/index/v2', None, note='Retrieving signed policy',
                headers={
                    'Authorization': auth_response['token_type'] + ' ' + auth_response['access_token']
                })
            bucket = policy_response['cms']['bucket']
            params = {
                'Policy': policy_response['cms']['policy'],
                'Signature': policy_response['cms']['signature'],
                'Key-Pair-Id': policy_response['cms']['key_pair_id']
            }
            locale = traverse_obj(initial_state, ('localization', 'locale'))
            if locale:
                params['locale'] = locale
            CrunchyrollBetaBaseIE.params = (api_domain, bucket, params)
        return CrunchyrollBetaBaseIE.params

    def _redirect_from_beta(self, url, lang, internal_id, display_id, is_episode, iekey):
        initial_state, app_config = self._get_beta_embedded_json(self._download_webpage(url, display_id), display_id)
        content_data = initial_state['content']['byId'][internal_id]
        if is_episode:
            video_id = content_data['external_id'].split('.')[1]
            series_id = content_data['episode_metadata']['series_slug_title']
        else:
            series_id = content_data['slug_title']
        series_id = re.sub(r'-{2,}', '-', series_id)
        url = f'https://www.crunchyroll.com/{lang}{series_id}'
        if is_episode:
            url = url + f'/{display_id}-{video_id}'
        self.to_screen(f'{display_id}: Not logged in. Redirecting to non-beta site - {url}')
        return self.url_result(url, iekey, display_id)


class CrunchyrollBetaIE(CrunchyrollBetaBaseIE):