1+ #!/usr/bin/python
2+
3+
4+ # This sample sets a custom banner to user's channel by:
5+ #
6+ # 1. Uploading a banner image with "youtube.channelBanners.insert" method via resumable upload
7+ # 2. Getting user's channel object with "youtube.channels.list" method and "mine" parameter
8+ # 3. Updating channel's banner external URL with "youtube.channels.update" method
9+ #
10+ # @author Ibrahim Ulukaya
11+
12+ import httplib
13+ import httplib2
14+ import os
15+ import random
16+ import sys
17+ import time
18+
19+ from apiclient .discovery import build
20+ from apiclient .errors import HttpError
21+ from apiclient .http import MediaFileUpload
22+ from oauth2client .client import flow_from_clientsecrets
23+ from oauth2client .file import Storage
24+ from oauth2client .tools import argparser , run_flow
25+
26+
27+ # Explicitly tell the underlying HTTP transport library not to retry, since
28+ # we are handling retry logic ourselves.
29+ httplib2 .RETRIES = 1
30+
31+ # Maximum number of times to retry before giving up.
32+ MAX_RETRIES = 10
33+
34+ # Always retry when these exceptions are raised.
35+ RETRIABLE_EXCEPTIONS = (httplib2 .HttpLib2Error , IOError , httplib .NotConnected ,
36+ httplib .IncompleteRead , httplib .ImproperConnectionState ,
37+ httplib .CannotSendRequest , httplib .CannotSendHeader ,
38+ httplib .ResponseNotReady , httplib .BadStatusLine )
39+
40+ # Always retry when an apiclient.errors.HttpError with one of these status
41+ # codes is raised.
42+ RETRIABLE_STATUS_CODES = [500 , 502 , 503 , 504 ]
43+
44+ # CLIENT_SECRETS_FILE, name of a file containing the OAuth 2.0 information for
45+ # this application, including client_id and client_secret. You can acquire an
46+ # ID/secret pair from the APIs & auth tab at
47+ # https://cloud.google.com/console
48+ # For more information about using OAuth2 to access Google APIs, please visit:
49+ # https://developers.google.com/accounts/docs/OAuth2
50+ # For more information about the client_secrets.json file format, please visit:
51+ # https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
52+ # Please ensure that you have enabled the YouTube Data API for your project.
53+ CLIENT_SECRETS_FILE = "client_secrets.json"
54+
55+ # An OAuth 2 access scope that allows for full read/write access.
56+ YOUTUBE_READ_WRITE_SCOPE = "https://www.googleapis.com/auth/youtube"
57+ YOUTUBE_API_SERVICE_NAME = "youtube"
58+ YOUTUBE_API_VERSION = "v3"
59+
60+ # Helpful message to display if the CLIENT_SECRETS_FILE is missing.
61+ MISSING_CLIENT_SECRETS_MESSAGE = """
62+ WARNING: Please configure OAuth 2.0
63+
64+ To make this sample run you will need to populate the client_secrets.json file
65+ found at:
66+
67+ %s
68+
69+ with information from the APIs Console
70+ https://cloud.google.com/console
71+
72+ For more information about the client_secrets.json file format, please visit:
73+ https://developers.google.com/api-client-library/python/guide/aaa_client_secrets
74+ """ % os .path .abspath (os .path .join (os .path .dirname (__file__ ),
75+ CLIENT_SECRETS_FILE ))
76+
77+ def get_authenticated_service (args ):
78+ flow = flow_from_clientsecrets (CLIENT_SECRETS_FILE ,
79+ scope = YOUTUBE_READ_WRITE_SCOPE ,
80+ message = MISSING_CLIENT_SECRETS_MESSAGE )
81+
82+ storage = Storage ("%s-oauth2.json" % sys .argv [0 ])
83+ credentials = storage .get ()
84+
85+ if credentials is None or credentials .invalid :
86+ credentials = run_flow (flow , storage , args )
87+
88+ return build (YOUTUBE_API_SERVICE_NAME , YOUTUBE_API_VERSION ,
89+ http = credentials .authorize (httplib2 .Http ()))
90+
91+ def upload_banner (youtube , image_file ):
92+ insert_request = youtube .channelBanners ().insert (
93+ media_body = MediaFileUpload (image_file , chunksize = - 1 , resumable = True )
94+ )
95+
96+ image_url = resumable_upload (insert_request )
97+ set_banner (image_url )
98+
99+ def resumable_upload (insert_request ):
100+ response = None
101+ error = None
102+ retry = 0
103+ while response is None :
104+ try :
105+ print "Uploading file..."
106+ status , response = insert_request .next_chunk ()
107+ if 'url' in response :
108+ print "Banner was successfully uploaded to '%s'." % (
109+ response ['url' ])
110+ else :
111+ exit ("The upload failed with an unexpected response: %s" % response )
112+ except HttpError , e :
113+ if e .resp .status in RETRIABLE_STATUS_CODES :
114+ error = "A retriable HTTP error %d occurred:\n %s" % (e .resp .status ,
115+ e .content )
116+ else :
117+ raise
118+ except RETRIABLE_EXCEPTIONS , e :
119+ error = "A retriable error occurred: %s" % e
120+
121+ if error is not None :
122+ print error
123+ retry += 1
124+ if retry > MAX_RETRIES :
125+ exit ("No longer attempting to retry." )
126+
127+ max_sleep = 2 ** retry
128+ sleep_seconds = random .random () * max_sleep
129+ print "Sleeping %f seconds and then retrying..." % sleep_seconds
130+ time .sleep (sleep_seconds )
131+
132+ return response ['url' ]
133+
134+ def set_banner (banner_url ):
135+ channels_response = youtube .channels ().list (
136+ mine = True ,
137+ part = "brandingSettings"
138+ ).execute ()
139+
140+ if "brandingSettings" not in channels_response ["items" ][0 ]:
141+ channels_response ["items" ][0 ]["brandingSettings" ]["image" ]["bannerExternalUrl" ] = []
142+
143+ channel_brandingSettings = channels_response ["items" ][0 ]["brandingSettings" ]
144+
145+ channel_brandingSettings ["image" ]["bannerExternalUrl" ] = banner_url
146+
147+ channels_update_response = youtube .channels ().update (
148+ part = 'brandingSettings' ,
149+ body = dict (
150+ brandingSettings = channel_brandingSettings ,
151+ id = channels_response ["items" ][0 ]["id" ]
152+ )).execute ()
153+
154+ banner_mobile_url = channels_update_response ["brandingSettings" ]["image" ]["bannerMobileImageUrl" ]
155+ print "Banner is set to '%s'." % (banner_mobile_url )
156+
157+ if __name__ == "__main__" :
158+ argparser .add_argument ("--file" , required = True ,
159+ help = "Path to banner image file." )
160+ args = argparser .parse_args ()
161+
162+ if not os .path .exists (args .file ):
163+ exit ("Please specify a valid file using the --file= parameter." )
164+
165+ youtube = get_authenticated_service (args )
166+ try :
167+ upload_banner (youtube , args .file )
168+ except HttpError , e :
169+ print "An HTTP error %d occurred:\n %s" % (e .resp .status , e .content )
170+ else :
171+ print "The custom banner was successfully uploaded."
0 commit comments