Reputation: 51
I have written a script to do, among other things, resumable uploads of a .mp4 file to YouTube. The code responsible for handling this was mostly reused from https://learndataanalysis.org/how-to-upload-a-video-to-youtube-using-youtube-data-api-in-python/ and https://developers.google.com/youtube/v3/guides/uploading_a_video, though I made a few minor modifications to outdated syntax and libraries.
Here's my code:
def create_service(client_secret_file, api_name, api_version, *scopes):
CLIENT_SECRET_FILE = client_secret_file
API_SERVICE_NAME = api_name
API_VERSION = api_version
SCOPES = [scope for scope in scopes[0]]
cred = None
pickle_file = f'token_{API_SERVICE_NAME}_{API_VERSION}.pickle'
if os.path.exists(pickle_file):
with open(pickle_file, 'rb') as token:
cred = pickle.load(token)
if not cred or not cred.valid:
if cred and cred.expired and cred.refresh_token:
cred.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRET_FILE, SCOPES)
cred = flow.run_local_server()
with open(pickle_file, 'wb') as token:
pickle.dump(cred, token)
try:
service = build(API_SERVICE_NAME, API_VERSION, credentials=cred)
print(API_SERVICE_NAME, 'service created successfully')
return service
except Exception as e:
print('Unable to connect.')
print(e)
return None
def upload_video(game_id, slugs): # (durations)
API_NAME = 'youtube'
API_VERSION = 'v3'
SCOPES = ['https://www.googleapis.com/auth/youtube']
service = create_service(YOUTUBE_CS_FILENAME, API_NAME, API_VERSION, SCOPES)
# Get playlist ID, title, and video count
playlist_id, playlist_title, video_count = get_playlist(game_id, service)
upload_request_body = {
'snippet': {
'categoryId': 20,
'title': generate_title(playlist_title, video_count),
'description': 'test', # generate_description(durations, slugs),
'tags': ['Test', 'multiple', 'tags']
},
'status': {
'privacyStatus': 'private',
'selfDeclaredMadeForKids': False
}
}
mediaFile = MediaFileUpload('final.mp4', chunksize=-1, resumable=True)
video_insert_request = service.videos().insert(
part="snippet,status",
body=upload_request_body,
media_body=mediaFile
)
# 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, http.client.NotConnected,
http.client.IncompleteRead, http.client.ImproperConnectionState,
http.client.CannotSendRequest, http.client.CannotSendHeader,
http.client.ResponseNotReady, http.client.BadStatusLine)
# Always retry when an apiclient.errors.HttpError with one of these status
# codes is raised.
RETRIABLE_STATUS_CODES = [500, 502, 503, 504]
# Upload the video... finally.
response = None
error = None
retry = 0
video_id = ''
while response is None:
try:
print("Uploading file...")
print(video_insert_request)
status, response = video_insert_request.next_chunk()
if response is not None:
if 'id' in response:
video_id = response['id']
print("Video id '%s' was successfully uploaded." % 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)
insert_to_playlist(service, playlist_id, video_id)
Everything runs successfully, up to the point of beginning the resumable upload. The call to next_chunk() is causing the 404 Error. I've included debug info from httplib2.debuglevel = 4
:
connect: (youtube.googleapis.com, 443)
send: b'POST /upload/youtube/v3/videos?part=snippet%2Cstatus&alt=json&uploadType=resumable HTTP/1.1\r\nHost: youtube.googleapis.com\r\naccept: application/json\r\naccept-encoding: gzip, deflate\r\nuser-agent: (gzip)\r\nx-goog-api-client: gdcl/1.12.1 gl-python/3.6.4\r\ncontent-type: application/json\r\nx-upload-content-type: video/mp4\r\nx-upload-content-length: 38653386\r\ncontent-length: 204\r\nauthorization: Bearer [REDACTED OAUTH TOKEN]'
send: b'{"snippet": {"categoryId": 20, "title": "Test title", "description": "test", "tags": ["Test", "multiple", "tags"]}, "status": {"privacyStatus": "private", "selfDeclaredMadeForKids": false}}'
reply: 'HTTP/1.1 404 Not Found\r\n'
header: Content-Type header: X-GUploader-UploadID header: Content-Length header: Date header: Server header: Alt-Svc Traceback (most recent call last):
File "./create_video.py", line 354, in <module>
main()
File "./create_video.py", line 348, in main
args.func(args)
File "./create_video.py", line 26, in run
upload_video(game_id, slugs) # (durations)
File "./create_video.py", line 251, in upload_video
status, response = video_insert_request.next_chunk()
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/googleapiclient/_helpers.py", line 134, in positional_wrapper
return wrapped(*args, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/googleapiclient/http.py", line 991, in next_chunk
raise ResumableUploadError(resp, content)
googleapiclient.errors.ResumableUploadError: <HttpError 404 "Not Found">
Upvotes: 5
Views: 425