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

Code before:
            raise ExtractorError(f'Failed to refresh access token: {error}', expected=True)

        return token_response

    def _oauth_authorize(self):
        code_response = self._download_json(
            'https://www.youtube.com/o/oauth2/device/code',
            video_id='oauth',
            note='Initializing oauth authorization flow',
            data=json.dumps({
                'client_id': self._OAUTH_CLIENT_ID,
                'scope': self._OAUTH_SCOPE,
                'device_id': uuid.uuid4().hex,
                'device_model': 'ytlr::',
            }).encode(),
            headers={'Content-Type': 'application/json'})

        verification_url = traverse_obj(code_response, 'verification_url', {str})
        user_code = traverse_obj(code_response, 'user_code', {str})
        if not verification_url or not user_code:
            raise ExtractorError('Failed to initialize oauth authorization flow')

        self.to_screen(f'To give yt-dlp access to your account, go to  {verification_url}  and enter code  {user_code}')

        while True:
            # TODO: add a retry manager to retry 3 times if there is some sort of network/http error, and then give up.
            token_response = self._download_json(
                'https://www.youtube.com/o/oauth2/token',
                video_id='oauth',
                note=False,
                errnote='Failed to authorize oauth',
                data=json.dumps({
                    'client_id': self._OAUTH_CLIENT_ID,
                    'client_secret': self._OAUTH_CLIENT_SECRET,
                    'code': code_response['device_code'],
                    'grant_type': 'http://oauth.net/grant_type/device/1.0',
                }).encode(),
                headers={'Content-Type': 'application/json'})

            error = traverse_obj(token_response, 'error', {str})
            if error:
                if error == 'authorization_pending':
                    time.sleep(code_response['interval'])
                    continue
                elif error == 'expired_token':
                    raise ExtractorError('oauth authorization flow timed out', expected=True)
                else:
                    raise ExtractorError(f'Unknown error occurred during oauth authorization flow: {error}')

            return token_response


Review comment (refactor):
If you wanted to do that:
```py
            for retry in self.RetryManager():
                while True:
```
and then later
```py
                    try:
                        token_response = self._download_json(...)
                    except ExtractorError as e:
                        if isinstance(e.cause, HTTPError):
                            retry.error = err
                            break
                        raise
```

Code after:
            except ExtractorError as e:
                error_msg = str(e.orig_msg).replace('Failed to refresh access token: ', '')
                self.report_warning(f'{self._OAUTH_DISPLAY_ID}: Failed to refresh access token: {error_msg}')
                token_response = self._oauth_authorize
        else:
            token_response = self._oauth_authorize

        self._set_oauth_info(token_response)
        self.write_debug(f'{self._OAUTH_DISPLAY_ID}: Logged in using profile "{self._OAUTH_PROFILE}"')

    def _refresh_token(self, refresh_token):
        try:
            token_response = self._download_json(
                self._OAUTH_TOKEN_ENDPOINT,
                video_id=self._OAUTH_DISPLAY_ID,
                note='Refreshing access token',
                data=json.dumps({
                    'client_id': self._OAUTH_CLIENT_ID,
                    'client_secret': self._OAUTH_CLIENT_SECRET,
                    'refresh_token': refresh_token,
                    'grant_type': 'refresh_token',
                }).encode(),
                headers={'Content-Type': 'application/json'})
        except ExtractorError as e:
            if isinstance(e.cause, HTTPError):
                error = self._read_oauth_error_response(e.cause.response)
                if error == 'invalid_grant':
                    # RFC6749 § 5.2
                    raise ExtractorError(
                        'Failed to refresh access token: Refresh token is invalid, revoked, or expired (invalid_grant)',
                        expected=True, video_id=self._OAUTH_DISPLAY_ID)
                raise ExtractorError(
                    f'Failed to refresh access token: Authorization server returned error {error}',
                    video_id=self._OAUTH_DISPLAY_ID)
            raise
        return token_response

    @property
    def _oauth_authorize(self):
        code_response = self._download_json(
            self._OAUTH_DEVICE_AUTHORIZATION_ENDPOINT,
            video_id=self._OAUTH_DISPLAY_ID,
            note='Initializing authorization flow',
            data=json.dumps({
                'client_id': self._OAUTH_CLIENT_ID,
                'scope': self._OAUTH_SCOPE,
            }).encode(),
            headers={'Content-Type': 'application/json'})

        verification_url = traverse_obj(code_response, ('verification_url', {str}))
        user_code = traverse_obj(code_response, ('user_code', {str}))