upload_video.py (7068B)
1 #!/usr/bin/python 2 3 import httplib2 4 import os 5 import random 6 import sys 7 import time 8 9 from apiclient.discovery import build 10 from apiclient.errors import HttpError 11 from apiclient.http import MediaFileUpload 12 from oauth2client.client import flow_from_clientsecrets 13 from oauth2client.file import Storage 14 from oauth2client.tools import argparser, run_flow 15 16 17 # Explicitly tell the underlying HTTP transport library not to retry, since 18 # we are handling retry logic ourselves. 19 httplib2.RETRIES = 1 20 21 # Maximum number of times to retry before giving up. 22 MAX_RETRIES = 10 23 24 # Always retry when these exceptions are raised. 25 RETRIABLE_EXCEPTIONS = (httplib2.HttpLib2Error, IOError) 26 27 # Always retry when an apiclient.errors.HttpError with one of these status 28 # codes is raised. 29 RETRIABLE_STATUS_CODES = [500, 502, 503, 504] 30 31 # The CLIENT_SECRETS_FILE variable specifies the name of a file that contains 32 # the OAuth 2.0 information for this application, including its client_id and 33 # client_secret. You can acquire an OAuth 2.0 client ID and client secret from 34 # the Google API Console at 35 # https://console.cloud.google.com/. 36 # Please ensure that you have enabled the YouTube Data API for your project. 37 # For more information about using OAuth2 to access the YouTube Data API, see: 38 # https://developers.google.com/youtube/v3/guides/authentication 39 # For more information about the client_secrets.json file format, see: 40 # https://developers.google.com/api-client-library/python/guide/aaa_client_secrets 41 CLIENT_SECRETS_FILE = "client_secrets.json" 42 43 # This OAuth 2.0 access scope allows an application to upload files to the 44 # authenticated user's YouTube channel, but doesn't allow other types of access. 45 YOUTUBE_UPLOAD_SCOPE = "https://www.googleapis.com/auth/youtube.upload" 46 YOUTUBE_API_SERVICE_NAME = "youtube" 47 YOUTUBE_API_VERSION = "v3" 48 49 # This variable defines a message to display if the CLIENT_SECRETS_FILE is 50 # missing. 51 MISSING_CLIENT_SECRETS_MESSAGE = """ 52 WARNING: Please configure OAuth 2.0 53 54 To make this sample run you will need to populate the client_secrets.json file 55 found at: 56 57 %s 58 59 with information from the API Console 60 https://console.cloud.google.com/ 61 62 For more information about the client_secrets.json file format, please visit: 63 https://developers.google.com/api-client-library/python/guide/aaa_client_secrets 64 """ % os.path.abspath(os.path.join(os.path.dirname(__file__), 65 CLIENT_SECRETS_FILE)) 66 67 VALID_PRIVACY_STATUSES = ("public", "private", "unlisted") 68 69 70 def get_authenticated_service(args): 71 flow = flow_from_clientsecrets(CLIENT_SECRETS_FILE, 72 scope=YOUTUBE_UPLOAD_SCOPE, 73 message=MISSING_CLIENT_SECRETS_MESSAGE) 74 75 storage = Storage("%s-oauth2.json" % sys.argv[0]) 76 credentials = storage.get() 77 78 if credentials is None or credentials.invalid: 79 credentials = run_flow(flow, storage, args) 80 81 return build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, 82 http=credentials.authorize(httplib2.Http())) 83 84 85 def initialize_upload(youtube, options): 86 tags = None 87 if options.keywords: 88 tags = options.keywords.split(",") 89 90 body = dict( 91 snippet=dict( 92 title=options.title, 93 description=options.description, 94 tags=tags, 95 categoryId=options.category 96 ), 97 status=dict( 98 privacyStatus=options.privacyStatus 99 ) 100 ) 101 102 # Call the API's videos.insert method to create and upload the video. 103 insert_request = youtube.videos().insert( 104 part=",".join(body.keys()), 105 body=body, 106 # The chunksize parameter specifies the size of each chunk of data, in 107 # bytes, that will be uploaded at a time. Set a higher value for 108 # reliable connections as fewer chunks lead to faster uploads. Set a lower 109 # value for better recovery on less reliable connections. 110 # 111 # Setting "chunksize" equal to -1 in the code below means that the entire 112 # file will be uploaded in a single HTTP request. (If the upload fails, 113 # it will still be retried where it left off.) This is usually a best 114 # practice, but if you're using Python older than 2.6 or if you're 115 # running on App Engine, you should set the chunksize to something like 116 # 1024 * 1024 (1 megabyte). 117 media_body=MediaFileUpload(options.file, chunksize=-1, resumable=True) 118 ) 119 120 resumable_upload(insert_request) 121 122 # This method implements an exponential backoff strategy to resume a 123 # failed upload. 124 125 126 def resumable_upload(insert_request): 127 response = None 128 error = None 129 retry = 0 130 while response is None: 131 try: 132 print("Uploading file...") 133 status, response = insert_request.next_chunk() 134 if response is not None: 135 if 'id' in response: 136 print("Video id '%s' was successfully uploaded." % 137 response['id']) 138 else: 139 exit("The upload failed with an unexpected response: %s" % response) 140 except HttpError as e: 141 if e.resp.status in RETRIABLE_STATUS_CODES: 142 error = "A retriable HTTP error %d occurred:\n%s" % (e.resp.status, 143 e.content) 144 else: 145 raise 146 except RETRIABLE_EXCEPTIONS as e: 147 error = "A retriable error occurred: %s" % e 148 149 if error is not None: 150 print(error) 151 retry += 1 152 if retry > MAX_RETRIES: 153 exit("No longer attempting to retry.") 154 155 max_sleep = 2 ** retry 156 sleep_seconds = random.random() * max_sleep 157 print("Sleeping %f seconds and then retrying..." % sleep_seconds) 158 time.sleep(sleep_seconds) 159 160 161 if __name__ == '__main__': 162 argparser.add_argument("--file", required=True, 163 help="Video file to upload") 164 argparser.add_argument("--title", help="Video title", default="Test Title") 165 argparser.add_argument("--description", help="Video description", 166 default="Test Description") 167 argparser.add_argument("--category", default="22", 168 help="Numeric video category. " + 169 "See https://developers.google.com/youtube/v3/docs/videoCategories/list") 170 argparser.add_argument("--keywords", help="Video keywords, comma separated", 171 default="") 172 argparser.add_argument("--privacyStatus", choices=VALID_PRIVACY_STATUSES, 173 default=VALID_PRIVACY_STATUSES[0], help="Video privacy status.") 174 args = argparser.parse_args() 175 176 if not os.path.exists(args.file): 177 exit("Please specify a valid file using the --file= parameter.") 178 179 youtube = get_authenticated_service(args) 180 try: 181 initialize_upload(youtube, args) 182 except HttpError as e: 183 print("An HTTP error %d occurred:\n%s" % (e.resp.status, e.content))