|
|
@ -51,13 +51,14 @@ RETRIABLE_STATUS_CODES = [500, 502, 503, 504] |
|
|
|
|
|
|
|
CLIENT_SECRETS_FILE = 'youtube_secret.json' |
|
|
|
CREDENTIALS_PATH = ".youtube_credentials.json" |
|
|
|
SCOPES = ['https://www.googleapis.com/auth/youtube.upload'] |
|
|
|
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(): |
|
|
|
check_authenticated_scopes() |
|
|
|
flow = InstalledAppFlow.from_client_secrets_file( |
|
|
|
CLIENT_SECRETS_FILE, SCOPES) |
|
|
|
if exists(CREDENTIALS_PATH): |
|
|
@ -121,6 +122,17 @@ def initialize_upload(youtube, options): |
|
|
|
publishAt = tz.localize(publishAt).isoformat() |
|
|
|
body['status']['publishAt'] = str(publishAt) |
|
|
|
|
|
|
|
if options.get('--playlist'): |
|
|
|
playlist_id = get_playlist_by_name(youtube, options.get('--playlist')) |
|
|
|
if not playlist_id and options.get('--playlistCreate'): |
|
|
|
playlist_id = create_playlist(youtube, options.get('--playlist')) |
|
|
|
elif not playlist_id: |
|
|
|
logging.warning("Youtube: Playlist `" + options.get('--playlist') + "` is unknown.") |
|
|
|
logging.warning("If you want to create it, set the --playlistCreate option.") |
|
|
|
playlist_id = "" |
|
|
|
else: |
|
|
|
playlist_id = "" |
|
|
|
|
|
|
|
# Call the API's videos.insert method to create and upload the video. |
|
|
|
insert_request = youtube.videos().insert( |
|
|
|
part=','.join(body.keys()), |
|
|
@ -133,9 +145,77 @@ def initialize_upload(youtube, options): |
|
|
|
if video_id and options.get('--thumbnail'): |
|
|
|
set_thumbnail(youtube, options.get('--thumbnail'), videoId=video_id) |
|
|
|
|
|
|
|
# If we get a video_id, upload is successful and we are able to set playlist |
|
|
|
if video_id and options.get('--playlist'): |
|
|
|
set_playlist(youtube, playlist_id, video_id) |
|
|
|
|
|
|
|
|
|
|
|
def get_playlist_by_name(youtube, playlist_name): |
|
|
|
response = youtube.playlists().list( |
|
|
|
part='snippet,id', |
|
|
|
mine=True, |
|
|
|
maxResults=50 |
|
|
|
).execute() |
|
|
|
for playlist in response["items"]: |
|
|
|
if playlist["snippet"]['title'] == playlist_name: |
|
|
|
return playlist['id'] |
|
|
|
|
|
|
|
|
|
|
|
def create_playlist(youtube, playlist_name): |
|
|
|
template = ('Youtube: Playlist %s does not exist, creating it.') |
|
|
|
logging.info(template % (str(playlist_name))) |
|
|
|
resources = build_resource({'snippet.title': playlist_name, |
|
|
|
'snippet.description': '', |
|
|
|
'status.privacyStatus': 'public'}) |
|
|
|
response = youtube.playlists().insert( |
|
|
|
body=resources, |
|
|
|
part='status,snippet,id' |
|
|
|
).execute() |
|
|
|
return response["id"] |
|
|
|
|
|
|
|
|
|
|
|
def build_resource(properties): |
|
|
|
resource = {} |
|
|
|
for p in properties: |
|
|
|
# Given a key like "snippet.title", split into "snippet" and "title", where |
|
|
|
# "snippet" will be an object and "title" will be a property in that object. |
|
|
|
prop_array = p.split('.') |
|
|
|
ref = resource |
|
|
|
for pa in range(0, len(prop_array)): |
|
|
|
is_array = False |
|
|
|
key = prop_array[pa] |
|
|
|
|
|
|
|
# For properties that have array values, convert a name like |
|
|
|
# "snippet.tags[]" to snippet.tags, and set a flag to handle |
|
|
|
# the value as an array. |
|
|
|
if key[-2:] == '[]': |
|
|
|
key = key[0:len(key)-2:] |
|
|
|
is_array = True |
|
|
|
|
|
|
|
if pa == (len(prop_array) - 1): |
|
|
|
# Leave properties without values out of inserted resource. |
|
|
|
if properties[p]: |
|
|
|
if is_array: |
|
|
|
ref[key] = properties[p].split(',') |
|
|
|
else: |
|
|
|
ref[key] = properties[p] |
|
|
|
elif key not in ref: |
|
|
|
# For example, the property is "snippet.title", but the resource does |
|
|
|
# not yet have a "snippet" object. Create the snippet object here. |
|
|
|
# Setting "ref = ref[key]" means that in the next time through the |
|
|
|
# "for pa in range ..." loop, we will be setting a property in the |
|
|
|
# resource's "snippet" object. |
|
|
|
ref[key] = {} |
|
|
|
ref = ref[key] |
|
|
|
else: |
|
|
|
# For example, the property is "snippet.description", and the resource |
|
|
|
# already has a "snippet" object. |
|
|
|
ref = ref[key] |
|
|
|
return resource |
|
|
|
|
|
|
|
|
|
|
|
def set_thumbnail(youtube, media_file, **kwargs): |
|
|
|
kwargs = utils.remove_empty_kwargs(**kwargs) # See full sample for function |
|
|
|
kwargs = utils.remove_empty_kwargs(**kwargs) |
|
|
|
request = youtube.thumbnails().set( |
|
|
|
media_body=MediaFileUpload(media_file, chunksize=-1, |
|
|
|
resumable=True), |
|
|
@ -146,6 +226,26 @@ def set_thumbnail(youtube, media_file, **kwargs): |
|
|
|
return resumable_upload(request, 'thumbnail', 'set') |
|
|
|
|
|
|
|
|
|
|
|
def set_playlist(youtube, playlist_id, video_id): |
|
|
|
logging.info('Youtube: Configuring playlist...') |
|
|
|
resource = build_resource({'snippet.playlistId': playlist_id, |
|
|
|
'snippet.resourceId.kind': 'youtube#video', |
|
|
|
'snippet.resourceId.videoId': video_id, |
|
|
|
'snippet.position': ''} |
|
|
|
) |
|
|
|
try: |
|
|
|
youtube.playlistItems().insert( |
|
|
|
body=resource, |
|
|
|
part='snippet' |
|
|
|
).execute() |
|
|
|
except Exception as e: |
|
|
|
if hasattr(e, 'message'): |
|
|
|
logging.error("Youtube: Error: " + str(e.message)) |
|
|
|
else: |
|
|
|
logging.error("Youtube: Error: " + str(e)) |
|
|
|
logging.info('Youtube: Video is correclty added to the playlist.') |
|
|
|
|
|
|
|
|
|
|
|
# This method implements an exponential backoff strategy to resume a |
|
|
|
# failed upload. |
|
|
|
def resumable_upload(request, resource, method): |
|
|
|