I wrote the code below to upload files to my Google Photos account. Because uploads of very large files tended to fail, I used the resumable upload REST protocol using the "single request" approach recommended at https://developers.google.com/photos/library/guides/resumable-uploads . However, when I run it, the upload either completes successfully with the initial put request or fails with the error
error HTTPSConnectionPool(host='photoslibrary.googleapis.com', port=443): Max
retries exceeded with url: /v1/uploads?upload_id=...&upload_protocol=resumable
(Caused by SSLError(SSLEOFError(8, 'EOF occurred in violation of protocol
(_ssl.c:2393)')))
so the "resumable" loop is never entered. What am I misunderstanding?
# Create resumable session
headers = {
'Authorization': f'Bearer {creds.token}',
'X-Goog-Upload-Content-Type': content_type,
'X-Goog-Upload-Protocol': 'resumable',
'X-Goog-Upload-Command': 'start',
'X-Goog-Upload-Raw-Size': str(file_size)
}
response = requests.post('https://photoslibrary.googleapis.com/v1/uploads',
headers=headers)
session_url = response.headers['X-Goog-Upload-URL']
printstr(f"created session ... ")
# Perform the upload
with open(file_path, 'rb') as f:
# Initiate the upload
headers = {
'Authorization': 'Bearer ' + creds.token,
'Content-Length': str(file_size),
'X-Goog-Upload-Content-Type': content_type,
'X-Goog-Upload-Command': 'upload, finalize',
'X-Goog-Upload-Offset': '0'
}
response = requests.post(session_url, data=f, headers=headers)
printstr(f"uploading ...")
# Keep resuming as necessary
while response.status_code != 200: # THIS LOOP IS NEVER ENTERED - WHY?
# Determine how much has been uploaded
query_headers = {
'Authorization': 'Bearer ' + creds.token,
'Content-Length': str(0),
'X-Goog-Upload-Command': 'query'
}
printstr(f"put broke status {response.status_code} ... ")
query_response = requests.post(session_url, headers=query_headers)
if query_response.status_code != 200:
raise RuntimeError(f"Media upload query failed status {response.status_code}")
offset = int(response.headers['X-Goog-Upload-Size-Received'])
printstr("resuming at {offset} ... ")
# Resume upload where it left off
f.seek(offset)
headers['X-Goog-Upload-Offset'] = str(offset)
response = requests.put(session_url, headers=headers, data=f.read(file_size - offset))
Well, no answers here, but after a lot of experimentation I figured it out. Some resumable errors throw an exception but others do not. A solution is to check for a non-200 exit code for errors that don't throw exceptions and then throw an exception directly, and handle both cases in the except: section.
Here's my full code that others might find useful.