Google Oauth for YouTube API in Streamlit

I am using the YouTube Data API to gather channel and video data. I have my app deployed to Streamlit but any request that requires oauth instead of a api key gives me the error below. I can run the oauth required requests all day in a local Streamlit server. The function to authenticate that works locally is below along with the error.

I’m running python 3.11 and Streamlit 1.30.

def authenticate_youtube_api(api_version="v3"):
    SCOPES = ["https://www.googleapis.com/auth/youtube.force-ssl"]

    flow = InstalledAppFlow.from_client_secrets_file(
        "client_secrets.json", SCOPES
    )
    credentials = flow.run_local_server(port=9000, prompt='consent',
                                                authorization_prompt_message='')

    return build("youtube", api_version, credentials=credentials)
Traceback (most recent call last):

  File "/home/adminuser/venv/lib/python3.9/site-packages/streamlit/runtime/scriptrunner/script_runner.py", line 535, in _run_script

    exec(code, module.__dict__)

  File "/mount/src/concord/app_scripts/app.py", line 67, in <module>

    create_playlists_from_data()

  File "/mount/src/concord/app_scripts/app_create_playlist_add_videos_from_existing_playlist_data.py", line 161, in create_playlists_from_data

    youtube = authenticate_youtube_api()

  File "/mount/src/concord/app_scripts/app_create_playlist_add_videos_from_existing_playlist_data.py", line 43, in authenticate_youtube_api

    credentials = flow.run_local_server(port=9000)

  File "/home/adminuser/venv/lib/python3.9/site-packages/google_auth_oauthlib/flow.py", line 444, in run_local_server

    webbrowser.get(browser).open(auth_url, new=1, autoraise=True)

  File "/usr/local/lib/python3.9/webbrowser.py", line 65, in get

    raise Error("could not locate runnable browser")

Could you post a minimal reproducible code?

I have managed to find a flow that does allow me to use a link to perform the oauth. But I never go back to where I was and it never finishes processing my original API request. Plus I get a 403 error saying the YouTube Data API is not enabled in my project. The project number given is not the same as my actual project number. My client secrets file is up to date.

Here is one of the scripts for an API request

import streamlit as st
import googleapiclient.discovery
import pandas as pd
from google.oauth2 import service_account
from google_auth_oauthlib.flow import Flow
from extract_channel_info import extract_info

SCOPES = ['https://www.googleapis.com/auth/youtube.readonly']

def authorize():
    # Create a flow instance using the client ID and client secret obtained from the Google Developers Console
    flow = Flow.from_client_secrets_file(
        'client_secrets.json',  # Replace this with the path to your client secret JSON file
        scopes=SCOPES,
        redirect_uri='https://concordmusic.streamlit.app'
    )

    # Construct the authorization URL
    auth_url, _ = flow.authorization_url(prompt='consent')

    # Print the authorization URL and prompt the user to visit it
    st.write("Please visit this URL to authorize access:", auth_url)
    
    # Ask the user to enter the authorization code obtained after authorizing access
    authorization_code = st.text_input("Enter the authorization code: ")
    # Check if the authorization code has been provided
    if authorization_code:
        # Fetch the access token using the authorization code
        flow.fetch_token(code=authorization_code)
        # Get the credentials
        credentials = flow.credentials
        return credentials
    else:
        st.write("Authorization code is required.")

def get_authenticated_service():
    # Authorize and get the credentials
    credentials = authorize()

    # Build the YouTube Data API service
    service = googleapiclient.discovery.build('youtube', 'v3', credentials=credentials)

    return service

def get_single_channel_info():
    channel_id = st.text_input("What is the Channel ID?")
    filename = st.text_input("Output File Name:")

    if st.button("Perform Call"):
        # Check if either input field is blank
        if not channel_id or not filename:
            st.warning("Both Channel ID and Output File Name are required.")
            return  # Stop further execution

        st.write("Running your request now...")

        # Authenticate and get the service
        service = get_authenticated_service()

        channel_info_list = []

        request = service.channels().list(
            part="brandingSettings,contentDetails,contentOwnerDetails,id,localizations,snippet,statistics,status,topicDetails",
            id=channel_id
        )
        response = request.execute()

        if "items" in response:
            for item in response["items"]:
                channel_info = extract_info(item)
                channel_info_list.append(channel_info)

        # Create a DataFrame from the video information
        df = pd.DataFrame(channel_info_list)

        if filename:
            # Ensure file name ends with ".xlsx"
            if not filename.endswith(".xlsx"):
                filename += ".xlsx"
        # Save the DataFrame to an Excel file
        df.to_excel(filename, index=False, engine="openpyxl")
        st.write("Results have been saved to", filename)
        # Provide a download button for the output file
        with open(filename, 'rb') as file:
            file_content = file.read()
        st.download_button(label="Download Excel File", data=file_content, key='download_excel', file_name=filename)

        print(f"Channel info saved to {filename}")

if __name__ == "__main__":
    get_single_channel_info()