Search code examples
pythongoogle-oauthyoutube-data-api

YouTube Data API with multiple accounts using Python


I am developing an application that is supposed to help a friend of mine better organize his YouTube channels. He has multiple channels on different Google accounts. I'm developing this in Python and I currently don't have too much experience with the YouTube Data API, which I'm planning on using, since it seems like the only option.

The application itself isn't very complicated. The only things it needs to be able to do is upload videos, with a specified title, description and other properties and it should also be possible to write comments on videos. I started a simple application in the Google Developers Console, enabled the YouTube Data API and created an API key and an OAUTH-Client ID.

So far I've managed to post comments on videos, but it seems like every time I run the Python script (currently its just a simple script that posts a single comment) Google wants me to explicitly choose which account I want to use and I have to give permission to the script every time I run it.

Is there a way I can just run the script once and tell Google which account I want to use to post the comment, give all the permissions and Google then remembers that so I don't have to explicitly give permissions every time?

Also how would I be able to then switch accounts and make uploads with that one, because currently I always need to choose one, when the Google client pops up, when running the script.

I've heard you can get an application authorized by Google, would that help with this or is it fine if I just keep my app in test and not in production?


Solution

  • If you have N accounts and want to upload videos on each of them, then you'll have to run to successful completion N OAuth 2 authorization/authentication flows.

    For each of these N OAuth flows, upon completing each one successfully, you'll have to make persistent the obtained credentials data to a separate file within your computer local storage.

    This could well be conceived as an initialization step of your app (although, at any later stage, you may well repeat it for any additional channel that you need your app be aware of). Your code would look like:

    # run an OAuth flow; then obtain credentials data
    # relative to the channel the app's user had chosen
    # during that OAuth flow
    from google_auth_oauthlib.flow import InstalledAppFlow
    scopes = ['https://www.googleapis.com/auth/youtube']
    flow = InstalledAppFlow.from_client_secrets_file(
               client_secret_file, scopes)
    cred = flow.run_console()
    
    # build an YouTube service object such that to
    # be able to retrieve the ID of the channel that
    # the app's user had chosen during the OAuth flow
    from googleapiclient.discovery import build
    youtube = build('youtube', 'v3', credentials = cred)
    response = youtube.channels().list(
        part = 'id',
        mine = True
    ).execute()
    channel_id = response['items'][0]['id']
    
    # save the credentials data to a JSON text file
    cred_file = f"/path/to/credentials/data/dir/{channel_id}.json"
    with open(cred_file, 'w', encoding = 'UTF-8') as json_file:
        json_file.write(cred.to_json())
    

    Above, client_secret_file is the full path to the file containing your app's client secret JSON file that you've obtained from Google Developers Console.

    Subsequently, each time you'll want to upload a video, you'll have to choose from within the app to which channel to upload that video. From the perspective of the logic of your program that would imply the following thing -- say you've chosen the channel of which ID is channel_id: do read in the credentials data file associated to channel_id for to pass its content to your YouTube service object youtube constructed as shown below:

    # read in the credentials data associated to
    # the channel identified by its ID 'channel_id'
    from google.oauth2.credentials import Credentials
    cred_file = f"/path/to/credentials/data/dir/{channel_id}.json"
    cred = Credentials.from_authorized_user_file(cred_file)
    
    # the access token need be refreshed when
    # the previously saved one expired already
    from google.auth.transport.requests import Request
    assert cred and cred.valid and cred.refresh_token
    if cred.expired:
        cred.refresh(Request())
        # save credentials data upon it got refreshed
        with open(cred_file, 'w', encoding = 'UTF-8') as json_file:
            json_file.write(cred.to_json())
    
    # construct an YouTube service object through
    # which any API invocations are authorized on
    # behalf of the channel with ID 'channel_id'
    from googleapiclient.discovery import build
    youtube = build('youtube', 'v3', credentials = cred)
    

    Upon running this code, the YouTube service object youtube will be initialized such a way that each an every API endpoint call that is issued through this object will accomplish an authorized request on behalf of the channel identified by channel_id.

    An important note: you need to have installed the package Google Authentication Library for Python, google-auth, version >= 1.21.3 (google-auth v1.3.0 introduced Credentials.from_authorized_user_file, v1.8.0 introduced Credentials.to_json and v1.21.3 fixed this latter function w.r.t. its class' expiry member), for the credentials object cred to be saved to and loaded from JSON text files.

    Also an important note: the code above is simplified as much as possible. Error conditions are not handled at all. For example, the code above does not handle the error situation when cred_file already exists at the time of writing out a new credentials data file or when cred_file does not exist at the time of reading in credentials data that's supposed to already exist.