'Python using Google YouTube Data API (v3) gives an Oauth2 KeyError: 'client_secret'

I am trying to do a personal project related to gathering data from YouTube, and I am having a heck of a time using the API methods that require one to authenticate with OAuth2. The ones you can use an API key with, I'm all set on, and that part of my little app is working great. But downloading the transcript of a video requires OAuth2.

As a starting point to figuring OAuth2 out, I got example Python code generated from the Try It sidebar on the Youtube API Reference page for the Captions Download method. I modified it to include the caption ID for one of the videos I am interested in, the JSON secrets file for the project's OAuth 2.0 Client ID of type desktop that I downloaded from the Google Cloud Console, and the file path where I wanted it to put the transcript.

Unfortunately, it does not work! I've included below (with possibly-secret info replaced with placeholders) the code I was using, the contents of the secrets JSON file, and the contents of the console with the error message.

The code includes a call to flow.run_console() which is marked in PyCharm as deprecated. When I hover my mouse over it for a couple seconds, this pops up above the documentation about the function:

New clients will be unable to use `InstalledAppFlow.run_console` starting on Feb 28, 2022. All clients will be unable to use this method starting on Oct 3, 2022. Use `InstalledAppFlow.run_local_server` instead. For details on the OOB flow deprecation, see https://developers.googleblog.com/2022/02/making-oauth-flows-safer.html?m=1#disallowed-oob

At that site, I read about the security risks that made them deprecate this. Unfortunately, I could not figure out from that site how to replace it. Using credentials = flow.run_local_server() instead of credentials = flow.run_console() produced the exact same KeyError. It seems like the other options are all intended to be hosted on a website or used for mobile apps. I only ever want to use this app on my computer specifically.

It does seem to work as far as that line, or at least, it gives me a URL that lets me click, authorize my project to use my Youtube account, and paste the code back in. It's after I paste in the code that it fails. The error it gives me is actually about the credentials file not having a key with the name client_secret -- which, indeed, is missing from my credentials file. Maybe they stopped including that in general and never updated the example-code-generator on the API reference? When searching for other answers, I see a lot of references to creating an OAuth2 ClientID with the type of "Installed / other" but that is no longer an option in the type dropdown, as far as I can see.

Or, maybe it's related to the fact that my app is still listed with Google as in "testing" mode? I don't intend to release it to the world, I'm the only one who will ever use this app. It's staying on my computer, so I don't have a website to give Google so that they can check out my privacy policy or whatever other requirements they need for a released app.

Error message when running code:

C:\Users\myUserName\PycharmProjects\MyProjectName\venv\Scripts\python.exe C:/Users/acrai/PycharmProjects/MyProjectName/example_download_captions.py
flow_before
Please visit this URL to authorize this application: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=xxxxxxxxxxxxxxx.apps.googleusercontent.com&redirect_uri=urnxxxxxxoauth%3A2.0%3Aoob&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fyoutube.force-ssl&state=xxxxxxxxxx&prompt=consent&access_type=offline
Enter the authorization code: 4/1AX4blahblahblah-I clicked the link above, authorized with my Youtube account, and pasted in the code it gave me here!
Traceback (most recent call last):
  File "C:\Users\myUserName\PycharmProjects\MyProjectName\example_download_captions.py", line 54, in <module>
    main()
  File "C:\Users\myUserName\PycharmProjects\MyProjectName\example_download_captions.py", line 36, in main
    credentials = flow.run_console()
  File "C:\Users\myUserName\PycharmProjects\MyProjectName\venv\lib\site-packages\google_auth_oauthlib\flow.py", line 439, in run_console
    self.fetch_token(code=code)
  File "C:\Users\myUserName\PycharmProjects\MyProjectName\venv\lib\site-packages\google_auth_oauthlib\flow.py", line 298, in fetch_token
    kwargs.setdefault("client_secret", self.client_config["client_secret"])
KeyError: 'client_secret'

My code, very much based on the example code generated from the Try It sidebar on the Youtube API Reference page for the Captions Download method:

# https://developers.google.com/youtube/v3/docs/captions/download
# -*- coding: utf-8 -*-

# Sample Python code for youtube.captions.download
# NOTE: This sample code downloads a file and can't be executed via this
#       interface. To test this sample, you must run it locally using your
#       own API credentials.

# See instructions for running these code samples locally:
# https://developers.google.com/explorer-help/code-samples#python

import io
import os

import google_auth_oauthlib.flow
import googleapiclient.discovery
import googleapiclient.errors

from googleapiclient.http import MediaIoBaseDownload

scopes = ["https://www.googleapis.com/auth/youtube.force-ssl"]

def main():
    # Disable OAuthlib's HTTPS verification when running locally.
    # *DO NOT* leave this option enabled in production.
    os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"

    api_service_name = "youtube"
    api_version = "v3"
    client_secrets_file = "C:\\Users\\myUserName\\Documents\\MyProjectDirectoryName\\client_secret_xxxxxxxx.apps.googleusercontent.com.json"
    result_file = 'C:\\Users\\myUserName\\Documents\\MyProjectDirectoryName\\Video Transcripts\\vid_example_transcript.txt'
    test_caption_id = 'VxPQ0cBI7BNjG-6Kpo8Wi1H6VXAziegGD0Be0NlMJCg='

    # Get credentials and create an API client
    flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(
        client_secrets_file, scopes)
    print("flow_before")
    credentials = flow.run_console()
    print("flow_after")
    youtube = googleapiclient.discovery.build(
        api_service_name, api_version, credentials=credentials)

    request = youtube.captions().download(
        id=test_caption_id
    )

    fh = io.FileIO(result_file, "wb")

    download = MediaIoBaseDownload(fh, request)
    complete = False
    while not complete:
      status, complete = download.next_chunk()


if __name__ == "__main__":
    main()

What is in the JSON secrets file:

{"installed": {
        "client_id": "abcd-1234.apps.googleusercontent.com",
        "project_id": "MyProjectName",
        "auth_uri": "https://accounts.google.com/o/oauth2/auth",
        "token_uri": "https://oauth2.googleapis.com/token",
        "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
        "redirect_uris": [
            "http://localhost"
        ]
    }}

Here's how I obtained this file:

I went to https://console.cloud.google.com/apis/api/youtube.googleapis.com/credentials?project=MyProjectName Under "OAuth 2.0 Client IDs" on that page is the credential I made with the same name as the project name, the type is Desktop. At the far right of that line with the credential is an Actions column, I hit the Download icon, and then in the box that pops up, I hit the Download JSON button.

So, does anyone know how I can change my code to make it work?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source