unlimited-storage

YouTube filesystem tool for uploading arbitrary data to the service
git clone git://git.laack.co/unlimited-storage.git
Log | Files | Refs | README

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))