3

I am working on an application to pull the YouTube metrics using the YouTube analytics API. This is the first time I am working with google auth flow to authenticate my app so that it can silently pull the report.

I am following the below Google published article to work out the same: https://developers.google.com/youtube/reporting/guides/authorization/server-side-web-apps

While following this article I am unable to figure out how to redirect the user to the auth_uri and obtain the auth_code.

Below is the code I have written for the auth flow so far:

API_SERVICE_NAME = 'youtubeAnalytics'
API_VERSION = 'v2'
CLIENT_SECRETS_FILE = 'C:/Users/Tushar/Documents/Serato_Video_Intelligence/client_secret_youtube.json'
def get_service():
  global auth_code
  global auth_uri
  flow = client.flow_from_clientsecrets(
  CLIENT_SECRETS_FILE, 
  scope='https://www.googleapis.com/auth/yt-analytics.readonly',
  redirect_uri = "http://localhost:8080")
  flow.params['access_type'] = 'offline'
  flow.params['include_granted_scopes'] = True
  auth_uri = flow.step1_get_authorize_url()
  credentials = flow.step2_exchange(auth_code)
  http_auth = credentials.authorize(httplib2.Http())
  return build(API_SERVICE_NAME, API_VERSION, http=http_auth)
  
def execute_api_request(client_library_function, **kwargs):
  response = client_library_function(
    **kwargs
  ).execute()
        

if __name__ == '__main__':
  # Disable OAuthlib's HTTPs verification when running locally.
  # *DO NOT* leave this option enabled when running in production.
  #os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'

  youtubeAnalytics = get_service()
  execute_api_request(
      youtubeAnalytics.reports().query,
      ids='channel==XXXXXXXXXXXXXX',
      startDate='2019-04-12',
      endDate='2019-08-13',
      filters= 'video==XXXXXXXXXXXXXX',
      metrics='audienceWatchRatio,relativeRetentionPerformance',
      dimensions='elapsedVideoTimeRatio'
      
  )

When I run this code I get an error

  File "C:\Users\Tushar\Documents\Serato_Video_Intelligence\youtube_auth_testing.py", line 65, in <module>
    youtubeAnalytics = get_service()
  File "C:\Users\Tushar\Documents\Serato_Video_Intelligence\youtube_auth_testing.py", line 40, in get_service
    credentials = flow.step2_exchange(auth_code)
NameError: name 'auth_code' is not defined

I have gone through the articles on Stack Overflow and Google but have been unable to figure out what to do. I certainly know there are couple of steps that I am missing but I am unable to resolve it.

halfer
  • 19,824
  • 17
  • 99
  • 186
Analytics_TM
  • 493
  • 6
  • 28

1 Answers1

4
  • You want to retrieve the access token with the authorization process of OAuth2.
  • You want to achieve this using oauth2client with Python.

If my understanding is correct, how about this modification?

Authorization flow:

When the access token is retrieved by the authorization process of OAuth2, at first, it is required to authorize the scopes by own browser. When the scopes are authorize, the authorization code can be retrieved. Using this code, the refresh token and access token can be retrieved.

Patten 1:

When your current script is modified, it becomes as follows. In this modification, please modify get_service().

Modified script:

import httplib2
from apiclient.discovery import build
from oauth2client import client

def get_service():
    flow = client.flow_from_clientsecrets(
        CLIENT_SECRETS_FILE,
        scope='https://www.googleapis.com/auth/yt-analytics.readonly',
        redirect_uri='urn:ietf:wg:oauth:2.0:oob')
    flow.params['access_type'] = 'offline'
    auth_uri = flow.step1_get_authorize_url()
    print('Please go to this URL: {}'.format(auth_uri))
    auth_code = input('Enter the authorization code: ')
    credentials = flow.step2_exchange(auth_code)
    http_auth = credentials.authorize(httplib2.Http())
    return build(API_SERVICE_NAME, API_VERSION, http=http_auth)

Or (This is from the sample script of Reports: Query.)

def get_service():
    flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRETS_FILE, SCOPES)
    credentials = flow.run_console()
    return build(API_SERVICE_NAME, API_VERSION, credentials = credentials)
  • When you run the script, Please go to this URL: ### is shown in the terminal. Please copy and paste it to your browser, and please authorize the scopes. Then, please retrieve the authorization code, and please paste it to the terminal. By this flow, the access token can be retrieved.
  • In above script, when the script is run, the authorization process is required to be done every time. So if you don't want to do this, how about the following pattern 2?

Patten 2:

In this pattern, when the script is run, the browser is automatically opened, and when the scopes are manually authorized, the authorization code is automatically retrieved.

Modified script:

import httplib2
import os
from apiclient.discovery import build
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage

def get_service():
    SCOPES = 'https://www.googleapis.com/auth/yt-analytics.readonly'
    credential_path = os.path.join("./", 'tokens.json')
    store = Storage(credential_path)
    credentials = store.get()
    if not credentials or credentials.invalid:
        flow = client.flow_from_clientsecrets(CLIENT_SECRETS_FILE, SCOPES)
        credentials = tools.run_flow(flow, store)
    http = credentials.authorize(httplib2.Http())
    return build(API_SERVICE_NAME, API_VERSION, http=http_auth)
  • In above script, when the script is run, when tokens.json is existing, the access token is retrieved using the refresh token in the file. By this, the authorization process using own browser is not required after 2nd run.

Patten 3:

Recently, google_auth_oauthlib is used for the authorization process. For example, you can see it at here. When this is reflected to your script, get_service() becomes as follows. Also you can see this flow at here.

Modified script:

import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

def get_service():
    SCOPES = ['https://www.googleapis.com/auth/yt-analytics.readonly']
    TOKEN_FILE = 'token.pickle'
    creds = None
    if os.path.exists(TOKEN_FILE):
        with open(TOKEN_FILE, 'rb') as token:
            creds = pickle.load(token)
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                CLIENT_SECRETS_FILE, SCOPES)
            creds = flow.run_local_server()
        with open(TOKEN_FILE, 'wb') as token:
            pickle.dump(creds, token)
    return build(API_SERVICE_NAME, API_VERSION, credentials=creds)
  • When the script is run, the browser is automatically opened, and when the scopes are manually authorized, the authorization code is automatically retrieved.
  • In above script, when the script is run, when token.pickle is existing, the access token is retrieved using the refresh token in the file. By this, the authorization process using own browser is not required after 2nd run.

Note:

  • In my environment, I could confirmed that when youtubeAnalytics of youtubeAnalytics = get_service() is used, execute_api_request() worked fine. If in your situation, an error occurs, please confirm whether API is enabled.

References:

If I misunderstood your question and this was not the direction you want, I apologize.

Tanaike
  • 181,128
  • 11
  • 97
  • 165
  • Thanks a lot for taking out time and providing such a descriptive explanation. This is indeed I was looking for and this has helped me to resolve my issue. And thank you for sharing all the three methods. I am using the third one now and it works like magic, but the other two would definitely be useful in future implementation. Once again thanks a lot for all the help, its such a big relief – Analytics_TM Aug 15 '19 at 03:31
  • @AnalyticsTeam Thank you for replying. I'm glad your issue was resolved. Thank you, too. – Tanaike Aug 15 '19 at 04:37
  • @Tanaike I wanted to ask that when I am adding `flow.run_local_server()` its not opening new tab for login and my website return time out. – anonymous Dec 09 '21 at 05:39
  • @anonymous I would like to support you. But this is not your question. So can you post it by including more information? By this, it will help a lot of users including me think of the solution. If you can cooperate to resolve your issue, I'm glad. Can you cooperate to do it? – Tanaike Dec 09 '21 at 05:45
  • @Tanaike I have already posted. Please see here and thank you. https://stackoverflow.com/questions/70270177/how-to-fix-504-gateway-time-out-while-connecting-to-google-calendar-api – anonymous Dec 09 '21 at 05:56
  • @anonymous Thank you for replying. I couldn't notice you had already posted it as a question. This is due to my poor skill. I deeply apologize for this. – Tanaike Dec 09 '21 at 05:58