Quantcast
Channel: Active questions tagged youtube-api - Stack Overflow
Viewing all articles
Browse latest Browse all 3831

YouTube upload always fails with "youtubeSignupRequired"

$
0
0

I've read through other posts with similar issues but I've hit a brick wall with Google APIs. Here's the situation:

  • I have a YouTube channel since 5 years ago, created from my personal Google account. It's for an audio podcast I'm recording. The YouTube channel is now a "brand account" apparently.
  • Now I'm trying to automatically upload the backlog of episodes, converted to videos, and automate future uploads.
  • After days of fiddling around with setting up a GCP project to allow this, the suggestion from some Google support bot was to create an organisation to avoid making the project public. I need a long-lived OAuth2 token here to make the automatisation worthwhile.
  • I created an organisation, which looks very much like a Google Workspace but is somehow different, and have 1 account there. I've added this account as an Owner to the YouTube channel. The new account can successfully manage the YouTube channel (30 days have passed since its creation - that's also a weird requirement).
  • I can successfully log in with this account in the backend Python script. Permitted domains, Oauth2 callbacks, etc. looks to be set up ok. Scopes are ok ("https://www.googleapis.com/auth/youtube.upload", "https://www.googleapis.com/auth/youtube.force-ssl")
  • Still, when I try to upload a video, Google bombs out with:
b'{\n  "error": {\n    "code": 401,\n    "message": "Unauthorized",\n    "errors": [\n      {\n        "message": "Unauthorized",\n        "domain": "youtube.header",\n        "reason": "youtubeSignupRequired",\n        "location": "Authorization",\n        "locationType": "header"\n}\n    ]\n  }\n}\n'

What frustrates any debugging is that even though it errors out in this way, apparently in the Authorization stage, the call is still counted against the quota! I can only do 2-3 of those calls a day!

Is there way to make YouTube uploads work from a script?

EDIT: per commentator's request, here's the login script, it writes the client_credentials.json file after login:

import argparseimport httplib2import jsonimport os, os.pathimport pprintimport randomimport timeimport google.oauth2.credentialsimport google_auth_oauthlib.flowfrom googleapiclient.discovery import buildfrom googleapiclient.errors import HttpErrorfrom googleapiclient.http import MediaFileUploadfrom google_auth_oauthlib.flow import InstalledAppFlowfrom google.auth import external_account_authorized_user# Explicitly tell the underlying HTTP transport library not to retry, since# we are handling retry logic ourselves.httplib2.RETRIES = 1# Maximum number of times to retry before giving up.MAX_RETRIES = 10# Always retry when these exceptions are raised.RETRIABLE_EXCEPTIONS = (    httplib2.HttpLib2Error,    IOError,)# Always retry when an apiclient.errors.HttpError with one of these status# codes is raised.RETRIABLE_STATUS_CODES = [500, 502, 503, 504]CLIENT_SECRETS_FILE = "client_secrets.json"CLIENT_CREDENTIALS_FILE = "client_credentials.json"SCOPES = ["https://www.googleapis.com/auth/youtube.upload", "https://www.googleapis.com/auth/youtube.force-ssl"]API_SERVICE_NAME = "youtube"API_VERSION = "v3"# Authorize the request and store authorization credentials.def get_authenticated_service():    if not os.path.exists(CLIENT_CREDENTIALS_FILE):        flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRETS_FILE, SCOPES)        credentials = flow.run_local_server(open_browser=False, port=8000, host='surovestrasti.com')        credentials_json = credentials.to_json().replace('token_uri', 'token_url')        #print(credentials_json)        open(CLIENT_CREDENTIALS_FILE, 'wt').write(credentials_json)    else:        raise Exception("Please delete the client credentials file %s" % CLIENT_CREDENTIALS_FILE)    return build(API_SERVICE_NAME, API_VERSION, credentials=credentials)if __name__ == "__main__":    youtube = get_authenticated_service()    print("Logged in and stored credentials into %s" % CLIENT_CREDENTIALS_FILE)

And here's the YouTube upload script:

import argparseimport httplib2import jsonimport os, os.pathimport pprintimport randomimport timeimport google.oauth2.credentialsimport google_auth_oauthlib.flowfrom googleapiclient.discovery import buildfrom googleapiclient.errors import HttpErrorfrom googleapiclient.http import MediaFileUploadfrom google_auth_oauthlib.flow import InstalledAppFlowfrom google.auth import external_account_authorized_user# Explicitly tell the underlying HTTP transport library not to retry, since# we are handling retry logic ourselves.httplib2.RETRIES = 1# Maximum number of times to retry before giving up.MAX_RETRIES = 10# Always retry when these exceptions are raised.RETRIABLE_EXCEPTIONS = (    httplib2.HttpLib2Error,    IOError,)RETRIABLE_STATUS_CODES = [500, 502, 503, 504]CLIENT_SECRETS_FILE = "client_secrets.json"CLIENT_CREDENTIALS_FILE = "client_credentials.json"# This OAuth 2.0 access scope allows an application to upload files to the# authenticated user's YouTube channel, but doesn't allow other types of access.SCOPES = ["https://www.googleapis.com/auth/youtube.upload", "https://www.googleapis.com/auth/youtube.force-ssl"]API_SERVICE_NAME = "youtube"API_VERSION = "v3"VALID_PRIVACY_STATUSES = ("public", "private", "unlisted")# Authorize the request and store authorization credentials.def get_authenticated_service():    if not os.path.exists(CLIENT_CREDENTIALS_FILE):        flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRETS_FILE, SCOPES)        credentials = flow.run_local_server(open_browser=False)        credentials_json = credentials.to_json().replace('token_uri', 'token_url')        #print(credentials_json)        open(CLIENT_CREDENTIALS_FILE, 'wt').write(credentials_json)    else:        credentials = external_account_authorized_user.Credentials.from_file(CLIENT_CREDENTIALS_FILE)    return build(API_SERVICE_NAME, API_VERSION, credentials=credentials)def initialize_upload(youtube, options):    tags = None    if options.keywords:        tags = options.keywords.split(",")    body = dict(        snippet=dict(            title=options.title,            description=options.description,            tags=tags,            categoryId=options.category,        ),        status=dict(privacyStatus=options.privacyStatus),    )    insert_request = youtube.videos().insert(        part=",".join(body.keys()),        body=body,        media_body=MediaFileUpload(options.file, chunksize=-1, resumable=True),    )    return resumable_upload(insert_request)# This method implements an exponential backoff strategy to resume a# failed upload.def resumable_upload(request):    response = None    error = None    retry = 0    while response is None:        try:            print("Uploading file...")            status, response = request.next_chunk()            if response is not None:                if "id" in response:                    print('Video id "%s" was successfully uploaded.' % response["id"])                    return response["id"]                else:                    exit("The upload failed with an unexpected response: %s" % response)        except HttpError as e:            if e.resp.status in RETRIABLE_STATUS_CODES:                error = "A retriable HTTP error %d occurred:\n%s" % (                    e.resp.status,                    e.content,                )            else:                raise        except RETRIABLE_EXCEPTIONS as e:            error = "A retriable error occurred: %s" % e        if error is not None:            print(error)            retry += 1            if retry > MAX_RETRIES:                exit("No longer attempting to retry.")            max_sleep = 2**retry            sleep_seconds = random.random() * max_sleep            print("Sleeping %f seconds and then retrying..." % sleep_seconds)            time.sleep(sleep_seconds)if __name__ == "__main__":    parser = argparse.ArgumentParser()    parser.add_argument("--file", required=True, help="Video file to upload")    parser.add_argument("--title", help="Video title", default="Test Title")    parser.add_argument("--description", help="Video description", default="Test Description"    )    parser.add_argument("--category",        default="22",        help="Numeric video category. "+"See https://developers.google.com/youtube/v3/docs/videoCategories/list",    )    parser.add_argument("--keywords", help="Video keywords, comma separated", default=""    )    parser.add_argument("--privacyStatus",        choices=VALID_PRIVACY_STATUSES,        default="private",        help="Video privacy status.",    )    args = parser.parse_args()    youtube = get_authenticated_service()    try:        video_id = initialize_upload(youtube, args)    except HttpError as e:        print("An HTTP error %d occurred:\n%s" % (e.resp.status, e.content))        pprint.pp(json.loads(e.content))        video_id = None    thumbnail_image = f"{args.file}.png"    if os.path.exists(thumbnail_image) and video_id:        print("Uploading thumbnail...")        req = youtube.thumbnails().set(videoId=video_id, media_body=MediaFileUpload(thumbnail_image))        resp = req.execute()        if resp['kind'] != 'youtube#thumbnailSetResponse':            print("ERROR: thumbnail image response:", resp)

Incredibly, Google's examples I've found are for Python 2, so the bulk of the scripts is Google's code adapted for Python 3.


Viewing all articles
Browse latest Browse all 3831

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>