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

Code before:
    # - https://accounts.google.com/.well-known/openid-configuration
    # - https://www.rfc-editor.org/rfc/rfc8628
    # - https://www.rfc-editor.org/rfc/rfc6749
    #
    # Note: The official client appears to use a proxied version of the oauth2 endpoints on youtube.com/o/oauth2,
    # which applies some modifications to the response (such as returning errors as 200 OK).
    # Since the client works with the standard API, we will use that as it is well-documented.

    _OAUTH_PROFILE = None
    _OAUTH_ACCESS_TOKEN_CACHE = {}
    _OAUTH_DISPLAY_ID = 'oauth'

    # YouTube TV (TVHTML5) client. You can find these at youtube.com/tv.
    _OAUTH_CLIENT_ID = '861556708454-d6dlm3lh05idd8npek18k6be8ba3oc68.apps.googleusercontent.com'
    _OAUTH_CLIENT_SECRET = 'SboVhoG9s0rNafixCSGGKXAT'
    _OAUTH_SCOPE = 'http://gdata.youtube.com https://www.googleapis.com/auth/youtube-paid-content'

    # From https://accounts.google.com/.well-known/openid-configuration
    # Technically, these should be fetched dynamically and not hard-coded.
    # However, as these endpoints rarely change, we can risk saving an extra request for every invocation.
    _OAUTH_DEVICE_AUTHORIZATION_ENDPOINT = 'https://oauth2.googleapis.com/device/code'  # device_authorization_endpoint
    _OAUTH_TOKEN_ENDPOINT = 'https://oauth2.googleapis.com/token'                       # token_endpoint

    @staticmethod
    def _get_oauth_cache_key(profile):
        return f'oauth_refresh_token_{profile}'

    def _set_oauth_info(self, token_response, profile):
        YoutubeBaseInfoExtractor._OAUTH_ACCESS_TOKEN_CACHE.setdefault(profile, {}).update({
            'access_token': token_response['access_token'],
            'token_type': token_response['token_type'],
            'expiry': time_seconds(
                seconds=traverse_obj(token_response, ('expires_in', {float_or_none}), default=300) - 10),
        })
        refresh_token = traverse_obj(token_response, 'refresh_token', {str})
        if refresh_token:
            self.cache.store(self._NETRC_MACHINE, self._get_oauth_cache_key(profile), refresh_token)
            YoutubeBaseInfoExtractor._OAUTH_ACCESS_TOKEN_CACHE[profile]['refresh_token'] = refresh_token

    def _initialize_oauth(self, user, refresh_token):
        self._OAUTH_PROFILE = user or 'default'

        if self._OAUTH_PROFILE in YoutubeBaseInfoExtractor._OAUTH_ACCESS_TOKEN_CACHE:
            self.write_debug(f'{self._OAUTH_DISPLAY_ID}: Using cached access token for profile "{self._OAUTH_PROFILE}"')
            return

        YoutubeBaseInfoExtractor._OAUTH_ACCESS_TOKEN_CACHE[self._OAUTH_PROFILE] = {}

        if refresh_token == "''":
            refresh_token = None


Review comment (security):
maybe this would be nicer? `self._oauth_cache_key` vs `self._get_oauth_cache_key(profile)`
```suggestion
    @property
    def _oauth_cache_key(self):
        return f'oauth_refresh_token_{YoutubeBaseInfoExtractor._OAUTH_PROFILE}'
```

Code after:
    - https://www.rfc-editor.org/rfc/rfc6749

    Note: The official client appears to use a proxied version of the oauth2 endpoints on youtube.com/o/oauth2,
    which applies some modifications to the response (such as returning errors as 200 OK).
    Since the client works with the standard API, we will use that as it is well-documented.
    '''

    _OAUTH_PROFILE = None
    _OAUTH_ACCESS_TOKEN_CACHE = {}
    _OAUTH_DISPLAY_ID = 'oauth'

    # YouTube TV (TVHTML5) client. You can find these at youtube.com/tv
    _OAUTH_CLIENT_ID = '861556708454-d6dlm3lh05idd8npek18k6be8ba3oc68.apps.googleusercontent.com'
    _OAUTH_CLIENT_SECRET = 'SboVhoG9s0rNafixCSGGKXAT'
    _OAUTH_SCOPE = 'http://gdata.youtube.com https://www.googleapis.com/auth/youtube-paid-content'

    # From https://accounts.google.com/.well-known/openid-configuration
    # Technically, these should be fetched dynamically and not hard-coded.
    # However, as these endpoints rarely change, we can risk saving an extra request for every invocation.
    _OAUTH_DEVICE_AUTHORIZATION_ENDPOINT = 'https://oauth2.googleapis.com/device/code'
    _OAUTH_TOKEN_ENDPOINT = 'https://oauth2.googleapis.com/token'

    @property
    def _oauth_cache_key(self):
        return f'oauth_refresh_token_{self._OAUTH_PROFILE}'

    def _read_oauth_error_response(self, response):
        return traverse_obj(
            self._webpage_read_content(response, self._OAUTH_TOKEN_ENDPOINT, self._OAUTH_DISPLAY_ID, fatal=False),
            ({json.loads}, 'error', {str}))

    def _set_oauth_info(self, token_response):
        YoutubeBaseInfoExtractor._OAUTH_ACCESS_TOKEN_CACHE.setdefault(self._OAUTH_PROFILE, {}).update({
            'access_token': token_response['access_token'],
            'token_type': token_response['token_type'],
            'expiry': time_seconds(
                seconds=traverse_obj(token_response, ('expires_in', {float_or_none}), default=300) - 10),
        })
        refresh_token = traverse_obj(token_response, ('refresh_token', {str}))
        if refresh_token:
            self.cache.store(self._NETRC_MACHINE, self._oauth_cache_key, refresh_token)
            YoutubeBaseInfoExtractor._OAUTH_ACCESS_TOKEN_CACHE[self._OAUTH_PROFILE]['refresh_token'] = refresh_token

    def _initialize_oauth(self, user, refresh_token):
        self._OAUTH_PROFILE = user or 'default'

        if self._OAUTH_PROFILE in YoutubeBaseInfoExtractor._OAUTH_ACCESS_TOKEN_CACHE:
            self.write_debug(f'{self._OAUTH_DISPLAY_ID}: Using cached access token for profile "{self._OAUTH_PROFILE}"')
            return

        YoutubeBaseInfoExtractor._OAUTH_ACCESS_TOKEN_CACHE[self._OAUTH_PROFILE] = {}