forked from vimeo/vimeo.py
-
Notifications
You must be signed in to change notification settings - Fork 0
/
upload.py
214 lines (164 loc) · 7.51 KB
/
upload.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#! /usr/bin/env python
# encoding: utf-8
from __future__ import absolute_import
import os
import io
import requests.exceptions
from .exceptions import *
try:
basestring
except NameError:
basestring = str
class UploadVideoMixin(object):
"""Handle uploading a new video to the Vimeo API."""
UPLOAD_ENDPOINT = '/me/videos'
REPLACE_ENDPOINT = '{video_uri}/files'
def upload(self, filename, upgrade_to_1080=False):
"""Upload the named file to Vimeo."""
ticket = self.post(
self.UPLOAD_ENDPOINT,
data={'type': 'streaming',
'upgrade_to_1080': 'true' if upgrade_to_1080 else 'false'},
params={'fields': 'upload_link,complete_uri'})
return self._perform_upload(filename, ticket)
def replace(self, video_uri, filename, upgrade_to_1080=False):
"""Replace the video at the given uri with the named source file."""
uri = self.REPLACE_ENDPOINT.format(video_uri=video_uri)
ticket = self.put(
uri,
data={'type': 'streaming',
'upgrade_to_1080': 'true' if upgrade_to_1080 else 'false'},
params={'fields': 'upload_link,complete_uri'})
return self._perform_upload(filename, ticket)
def _perform_upload(self, filename, ticket):
"""Take an upload ticket and perform the actual upload."""
if ticket.status_code != 201:
raise UploadTicketCreationFailure(ticket, "Failed to create an upload ticket")
ticket = ticket.json()
# Perform the actual upload.
target = ticket['upload_link']
last_byte = 0
# Try to get size of obj by path. If provided obj is not a file path
# find the size of file-like object.
try:
size = os.path.getsize(filename)
with io.open(filename, 'rb') as f:
while last_byte < size:
try:
self._make_pass(target, f, size, last_byte)
except requests.exceptions.Timeout:
# If there is a timeout here, we are okay with it, since
# we'll check and resume.
pass
last_byte = self._get_progress(target, size)
except TypeError:
size = len(filename.read())
f = filename
while last_byte < size:
try:
self._make_pass(target, f, size, last_byte)
except requests.exceptions.Timeout:
# If there is a timeout here, we are okay with it, since
# we'll check and resume.
pass
last_byte = self._get_progress(target, size)
# Perform the finalization and get the location.
finalized_resp = self.delete(ticket['complete_uri'])
if finalized_resp.status_code != 201:
raise VideoCreationFailure(finalized_resp, "Failed to create the video")
return finalized_resp.headers.get('Location', None)
def _get_progress(self, upload_target, filesize):
"""Test the completeness of the upload."""
progress_response = self.put(
upload_target,
headers={'Content-Range': 'bytes */*'})
range_recv = progress_response.headers.get('Range', None)
_, last_byte = range_recv.split('-')
return int(last_byte)
def _make_pass(self, upload_target, f, size, last_byte):
"""Make a pass at uploading.
This particular function may do many things. If this is a large upload
it may terminate without having completed the upload. This can also
occur if there are network issues or any other interruptions. These
can be recovered from by checking with the server to see how much it
has and resuming the connection.
"""
response = self.put(
upload_target,
timeout=None,
headers={
'Content-Length': str(size),
'Content-Range': 'bytes: %d-%d/%d' % (last_byte, size, size)
}, data=f)
if response.status_code != 200:
raise VideoUploadFailure(response, "Unexpected status code on upload")
class UploadPictureMixin(object):
"""Functionality for uploading a picture to Vimeo for another object
(video, user, etc).
"""
BASE_FIELDS = set(('link', 'uri'))
def upload_picture(self, obj, filename, activate=False, fields=None):
"""Upload a picture for the object.
The object (obj) can be the URI for the object or the response/parsed
json for it.
"""
if isinstance(obj, basestring):
obj = self.get(obj, params={'fields': 'metadata.connections.pictures.uri'})
if obj.status_code != 200:
raise ObjectLoadFailure("Failed to load the target object")
obj = obj.json()
if isinstance(fields, basestring):
fields = set((field.strip() for field in fields.split(',')))
fields = self.BASE_FIELDS.union(fields) if fields else self.BASE_FIELDS
# Get the picture object.
picture = self.post(
obj['metadata']['connections']['pictures']['uri'],
params={'fields': ','.join(fields)}
)
if picture.status_code != 201:
raise PictureCreationFailure(picture, "Failed to create a new picture with Vimeo.")
picture = picture.json()
with io.open(filename, 'rb') as f:
upload_resp = self.put(
picture['link'],
data=f,
params={'fields': 'error'})
if upload_resp.status_code != 200:
raise PictureUploadFailure(upload_resp, "Failed uploading picture")
if activate:
active = self.patch(
picture['uri'],
data={"active": "true"},
params={'fields': 'error'})
if active.status_code != 200:
raise PictureActivationFailure(active, "Failed activating picture")
picture['active'] = True
return picture
class UploadTexttrackMixin(object):
"""Functionality for uploading a texttrack to Vimeo for a video.
"""
TEXTTRACK_ENDPOINT = '{video_uri}/texttracks'
BASE_FIELDS = set(('link',))
def upload_texttrack(self, video_uri, track_type, language, filename, fields=None):
"""Upload the texttrack at the given uri with the named source file."""
uri = self.TEXTTRACK_ENDPOINT.format(video_uri=video_uri)
name = filename.split('/')[-1]
if isinstance(fields, basestring):
fields = set((field.strip() for field in fields.split(',')))
fields = self.BASE_FIELDS.union(fields) if fields else self.BASE_FIELDS
texttrack = self.post(uri,
data={'type': track_type,
'language': language,
'name': name},
params={'fields': ','.join(fields)})
if texttrack.status_code != 201:
raise TexttrackCreationFailure(texttrack, "Failed to create a new texttrack with Vimeo")
texttrack = texttrack.json()
with io.open(filename, 'rb') as f:
upload_resp = self.put(texttrack['link'], data=f)
if upload_resp.status_code != 200:
raise TexttrackUploadFailure(upload_resp, "Failed uploading texttrack")
return texttrack
class UploadMixin(UploadVideoMixin, UploadPictureMixin, UploadTexttrackMixin):
"""Handle uploading to the Vimeo API."""
pass