stupeflixclient 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/fixtures/movie.xml +348 -0
- data/lib/stupeflixclient.rb +31 -0
- data/lib/stupeflixclient/connection.rb +123 -0
- data/lib/stupeflixclient/constants.rb +4 -0
- data/lib/stupeflixclient/settings.rb +4 -0
- data/lib/stupeflixclient/stupeflix_base.rb +273 -0
- data/lib/stupeflixclient/stupeflix_client.rb +354 -0
- data/lib/stupeflixclient/version.rb +3 -0
- data/lib/stupeflixclient/video.rb +8 -0
- data/spec/spec_helper.rb +14 -0
- data/stupeflixclient.gemspec +26 -0
- metadata +131 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Stupeflixclient
|
5
|
+
|
6
|
+
class Connection
|
7
|
+
def initialize(server, base_url)
|
8
|
+
@server = server
|
9
|
+
@base_url = base_url
|
10
|
+
@MAX_NETWORK_RETRY = 1
|
11
|
+
@debuglevel = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def request_get( resource, args = nil, headers={})
|
15
|
+
return request(resource, "get", args, body = nil, filename = nil, headers=headers)
|
16
|
+
end
|
17
|
+
|
18
|
+
def request_delete( resource, args = nil, headers={})
|
19
|
+
return request(resource, "delete", args, headers=headers)
|
20
|
+
end
|
21
|
+
|
22
|
+
def request_head( resource, args = nil, headers={})
|
23
|
+
return request(resource, "head", args, headers=headers)
|
24
|
+
end
|
25
|
+
|
26
|
+
def request_post( resource, args = nil, body = nil, filename=nil, headers={})
|
27
|
+
return request(resource, "post", args , body = body, filename=filename, headers=headers)
|
28
|
+
end
|
29
|
+
|
30
|
+
def request_put(resource, args = nil, body = nil, filename=nil, headers={}, sendcallback = nil)
|
31
|
+
dump("In request_put in connection", resource, "PUT")
|
32
|
+
resp = request(resource, "put", args , body = body, filename=filename, headers=headers, sendcallback = sendcallback)
|
33
|
+
return resp
|
34
|
+
end
|
35
|
+
|
36
|
+
def dump( message, request_uri, method)
|
37
|
+
if @debuglevel > 0
|
38
|
+
print message, " " , time.asctime, " " , request_uri, "\n"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def request_method(verb)
|
43
|
+
Net::HTTP.const_get(verb.to_s.capitalize)
|
44
|
+
end
|
45
|
+
|
46
|
+
def fetch(uri_str, limit = 10)
|
47
|
+
# You should choose better exception.
|
48
|
+
raise ArgumentError, 'HTTP redirect too deep' if limit == 0
|
49
|
+
|
50
|
+
response = Net::HTTP.get_response(URI.parse(uri_str))
|
51
|
+
case response
|
52
|
+
when Net::HTTPSuccess then response
|
53
|
+
when Net::HTTPRedirection then fetch(response['location'], limit - 1)
|
54
|
+
else
|
55
|
+
response.error!
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
def request( resource, method = "get", args = nil, body = nil, filename=nil, headers={}, sendcallback = nil)
|
61
|
+
params = nil
|
62
|
+
path = resource
|
63
|
+
|
64
|
+
if headers == nil
|
65
|
+
headers = {}
|
66
|
+
end
|
67
|
+
|
68
|
+
headers['User-Agent'] = 'Basic Agent'
|
69
|
+
|
70
|
+
# TEMPORARY : add support for streaming
|
71
|
+
if method != "get"
|
72
|
+
if not body and filename
|
73
|
+
bodystream = File.open(filename, 'r')
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
if args
|
78
|
+
path += "?" + urllib.urlencode(args)
|
79
|
+
end
|
80
|
+
|
81
|
+
url = 'http://' + @server + @base_url + path
|
82
|
+
|
83
|
+
dump("Connection Request starting", path, method.upcase)
|
84
|
+
|
85
|
+
for i in 0..@MAX_NETWORK_RETRY
|
86
|
+
begin
|
87
|
+
# http.set_debug_output $stdout
|
88
|
+
# headers['Expect'] = '100-Continue'
|
89
|
+
|
90
|
+
response = nil
|
91
|
+
while true
|
92
|
+
url = URI.parse(url)
|
93
|
+
request = request_method(method).new(url.path + "?" + url.query, headers)
|
94
|
+
if bodystream
|
95
|
+
request.body_stream = bodystream
|
96
|
+
elsif body
|
97
|
+
request.body = body
|
98
|
+
end
|
99
|
+
http = Net::HTTP.new(url.host, url.port)
|
100
|
+
result = http.start {|http|
|
101
|
+
response = http.request(request)
|
102
|
+
}
|
103
|
+
case response
|
104
|
+
when Net::HTTPRedirection
|
105
|
+
then
|
106
|
+
url = response['location']
|
107
|
+
else
|
108
|
+
break
|
109
|
+
end
|
110
|
+
end
|
111
|
+
break
|
112
|
+
rescue StandardError => e
|
113
|
+
if i == (@MAX_NETWORK_RETRY - 1)
|
114
|
+
raise
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
response["status"] = response.code.to_s
|
119
|
+
return {'headers' => response, 'body' => response.body}
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,273 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'digest'
|
3
|
+
require 'base64'
|
4
|
+
require 'stupeflixclient/connection'
|
5
|
+
require 'digest/md5'
|
6
|
+
module Stupeflixclient
|
7
|
+
class StupeflixBase
|
8
|
+
def initialize( accessKey, privateKey, host = "http://services.stupeflix.com", service = 'stupeflix-1.0', debug = false)
|
9
|
+
@accessKey = accessKey
|
10
|
+
@privateKey = privateKey
|
11
|
+
len = host.length - 7
|
12
|
+
if host[host.length - 1, 1] == "/"
|
13
|
+
host = host[0, host.length - 1]
|
14
|
+
end
|
15
|
+
|
16
|
+
@host = host[7,len]
|
17
|
+
@base_url = service
|
18
|
+
@debug = debug
|
19
|
+
@service = service
|
20
|
+
@TEXT_XML_CONTENT_TYPE = "text/xml"
|
21
|
+
@APPLICATION_ZIP_CONTENT_TYPE = "application/zip"
|
22
|
+
@APPLICATION_JSON_CONTENT_TYPE = "application/json"
|
23
|
+
@APPLICATION_URLENCODED_CONTENT_TYPE = "application/x-www-form-urlencoded"
|
24
|
+
@PROFILES_PARAMETER = "Profiles"
|
25
|
+
@XML_PARAMETER = "ProfilesXML"
|
26
|
+
@MARKER_PARAMETER = "Marker"
|
27
|
+
@MAXKEYS_PARAMETER = "MaxKeys"
|
28
|
+
# Currently there is only the Marker parameter (used for partial enumeration)
|
29
|
+
@parametersToAdd = [@MARKER_PARAMETER, @MAXKEYS_PARAMETER]
|
30
|
+
@sleepTime = 1.0
|
31
|
+
@maxRetry = 4
|
32
|
+
@base = true
|
33
|
+
end
|
34
|
+
|
35
|
+
def connectionGet
|
36
|
+
return Connection.new(@host, "/" + @base_url)
|
37
|
+
end
|
38
|
+
|
39
|
+
def paramString( parameters)
|
40
|
+
paramStr = ""
|
41
|
+
if parameters != nil
|
42
|
+
for p in @parametersToAdd
|
43
|
+
if parameters.include?(p)
|
44
|
+
paramStr += sprintf( "%s\n%s\n", p, parameters[p])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
return paramStr
|
49
|
+
end
|
50
|
+
|
51
|
+
def strToSign( method, resource, md5, mime, datestr, parameters)
|
52
|
+
paramStr = paramString(parameters)
|
53
|
+
stringToSign = sprintf( "%s\n%s\n%s\n%s\n%s\n%s", method, md5, mime, datestr, '/' + @service + resource, paramStr)
|
54
|
+
return stringToSign
|
55
|
+
end
|
56
|
+
|
57
|
+
def sign( strToSign, secretKey)
|
58
|
+
digest = OpenSSL::Digest::Digest.new('sha1')
|
59
|
+
return OpenSSL::HMAC.hexdigest(digest, secretKey, strToSign)
|
60
|
+
end
|
61
|
+
|
62
|
+
def signUrl( url, method, md5, mime, parameters = {})
|
63
|
+
now =Time.now.to_i
|
64
|
+
strToSign = strToSign(method, url, md5, mime, now, parameters)
|
65
|
+
signature = sign(strToSign, @privateKey)
|
66
|
+
url += sprintf( "?Date=%s&AccessKey=%s&Signature=%s", now, @accessKey,signature)
|
67
|
+
if parameters
|
68
|
+
parameters.each_pair do |k,v|
|
69
|
+
url += sprintf("&%s=%s", k,v)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
return url
|
73
|
+
end
|
74
|
+
|
75
|
+
def md5FileOrBody( filename, body = nil)
|
76
|
+
md5 = Digest::MD5.new()
|
77
|
+
|
78
|
+
if body != nil
|
79
|
+
md5.update(body)
|
80
|
+
else
|
81
|
+
chunksize=1024
|
82
|
+
f = File.open(filename, 'r')
|
83
|
+
|
84
|
+
while true
|
85
|
+
chunk = f.read(chunksize)
|
86
|
+
if not chunk
|
87
|
+
break
|
88
|
+
end
|
89
|
+
md5.update(chunk)
|
90
|
+
end
|
91
|
+
f.close
|
92
|
+
end
|
93
|
+
|
94
|
+
digest = md5.digest
|
95
|
+
|
96
|
+
return [digest, md5.hexdigest, Base64.encode64(digest).strip]
|
97
|
+
end
|
98
|
+
|
99
|
+
def isZip( filename)
|
100
|
+
f = File.open(filename, 'r')
|
101
|
+
header = f.read(4)
|
102
|
+
return header == 'PK'+3.chr+4.chr
|
103
|
+
end
|
104
|
+
|
105
|
+
def logdebug( s)
|
106
|
+
if @debug
|
107
|
+
print s.to_s
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def error( message)
|
112
|
+
logdebug(message)
|
113
|
+
raise StandardError, message
|
114
|
+
end
|
115
|
+
|
116
|
+
def answer_error( answer, message)
|
117
|
+
raise StandardError, sprintf( "%s\n%s", message, answer['body'])
|
118
|
+
end
|
119
|
+
|
120
|
+
# sendcallback is an object with
|
121
|
+
# - a 'sendCallBack' member function that accept a unique int argument (=number of bytes written so far)
|
122
|
+
# - a 'sendBlockSize' member function with no argument which return the size of block to be sent
|
123
|
+
def sendContent( method, url, contentType, filename = nil, body = nil, parameters = nil, sendcallback = nil)
|
124
|
+
|
125
|
+
# SEND DATA
|
126
|
+
conn = connectionGet()
|
127
|
+
|
128
|
+
md5, md5hex, md5base64 = md5FileOrBody(filename, body)
|
129
|
+
|
130
|
+
if filename
|
131
|
+
size = File.stat(filename).size
|
132
|
+
else
|
133
|
+
size = body.length
|
134
|
+
end
|
135
|
+
|
136
|
+
headers = {'Content-MD5' => md5base64.to_s,
|
137
|
+
'Content-Length' => size.to_s,
|
138
|
+
'Content-Type' => contentType}
|
139
|
+
|
140
|
+
url = signUrl(url, method, md5base64, contentType, parameters)
|
141
|
+
|
142
|
+
# LAUNCH THE REQUEST : TODO : pass filename instead of body
|
143
|
+
if method == "PUT"
|
144
|
+
answer = conn.request_put(url, args = nil, body = body, filename = filename, headers = headers, sendcallback = sendcallback)
|
145
|
+
elsif method == "POST"
|
146
|
+
answer = conn.request_post(url, args = nil, body = body, filename = filename, headers = headers)
|
147
|
+
elsif method == "DELETE"
|
148
|
+
answer = conn.request_delete(url, headers = headers)
|
149
|
+
end
|
150
|
+
|
151
|
+
headers = answer['headers']
|
152
|
+
|
153
|
+
logdebug(headers)
|
154
|
+
logdebug(answer['body'])
|
155
|
+
|
156
|
+
# NOW CHECK THAT EVERYTHING IS OK
|
157
|
+
status = headers['status']
|
158
|
+
if status != '200'
|
159
|
+
msg = sprintf( "sendContent : bad STATUS %s", status )
|
160
|
+
answer_error(answer, msg)
|
161
|
+
end
|
162
|
+
|
163
|
+
if headers['etag'] == nil
|
164
|
+
msg = "corrupted answer: no etag in headers. Response body is " + answer['body']
|
165
|
+
error(msg)
|
166
|
+
end
|
167
|
+
|
168
|
+
obtainedMD5 = headers['etag']
|
169
|
+
|
170
|
+
if obtainedMD5 != md5hex
|
171
|
+
msg = sprintf( "sendContent : bad returned etags %s =! %s (ref)", obtainedMD5, md5hex)
|
172
|
+
error(msg)
|
173
|
+
end
|
174
|
+
|
175
|
+
return answer
|
176
|
+
end
|
177
|
+
|
178
|
+
def getContentUrl( url, method, parameters)
|
179
|
+
return signUrl(url, method, "", "", parameters)
|
180
|
+
end
|
181
|
+
|
182
|
+
def getContent( url, filename = nil, parameters = nil)
|
183
|
+
sleepTime = @sleepTime
|
184
|
+
|
185
|
+
for i in 0..@maxRetry
|
186
|
+
raiseExceptionOn404 = (i + 1) == @maxRetry
|
187
|
+
ret = getContent_(url, filename, parameters, raiseExceptionOn404)
|
188
|
+
if ret["status"] != 404
|
189
|
+
return ret
|
190
|
+
end
|
191
|
+
# Wait for amazon S3 ...
|
192
|
+
sleep(1)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def getContent_( url, filename = nil, parameters = nil, raiseExceptionOn404 = true)
|
197
|
+
method = "GET"
|
198
|
+
url = getContentUrl(url, method, parameters)
|
199
|
+
|
200
|
+
# GET DATA
|
201
|
+
conn = connectionGet()
|
202
|
+
answer = conn.request_get(url)
|
203
|
+
body = answer['body']
|
204
|
+
|
205
|
+
headers = answer['headers']
|
206
|
+
status = headers['status'].to_i
|
207
|
+
|
208
|
+
if status == 204
|
209
|
+
# there was no content
|
210
|
+
obtainedSize = 0
|
211
|
+
if body.length != 0
|
212
|
+
error("204 status with non empty body.")
|
213
|
+
end
|
214
|
+
elsif status == 200
|
215
|
+
obtainedSize =headers['content-length'].to_i
|
216
|
+
elsif status == 404 and not raiseExceptionOn404
|
217
|
+
return {"url" => headers['content-location'], "status" => 404}
|
218
|
+
else
|
219
|
+
msg = sprintf( "getContent : bad STATUS %s", status )
|
220
|
+
answer_error(answer, msg)
|
221
|
+
end
|
222
|
+
|
223
|
+
if body.length != obtainedSize
|
224
|
+
error("Non matching body length and content-length")
|
225
|
+
end
|
226
|
+
|
227
|
+
if filename != nil
|
228
|
+
f = File.open(filename, 'w')
|
229
|
+
f.write(body)
|
230
|
+
f.close
|
231
|
+
|
232
|
+
if obtainedSize == 0
|
233
|
+
File.unlink(filename)
|
234
|
+
else
|
235
|
+
filesize = File.stat(filename).size
|
236
|
+
if obtainedSize != filesize
|
237
|
+
File.unlink(filename)
|
238
|
+
error(sprintf( "file size is incorrect : file size = %d, body size = %d", filesize, obtainedSize))
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
# NOW CHECK EVERYTHING IS OK
|
244
|
+
md5, md5hex, md5base64 = md5FileOrBody(filename, body)
|
245
|
+
|
246
|
+
if status != 204
|
247
|
+
obtainedMD5 = headers['etag'].gsub(/"/,"")
|
248
|
+
if obtainedMD5 != md5hex
|
249
|
+
if filename
|
250
|
+
File.unlink(filename)
|
251
|
+
end
|
252
|
+
error(sprintf( "getDefinition : bad returned etag %s =! %s (ref)", md5hex, obtainedMD5))
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
if status == 200
|
257
|
+
logdebug(sprintf( "headers = %s", headers) )
|
258
|
+
url = headers['content-location']
|
259
|
+
ret = {'size' => obtainedSize, 'url' => url, 'headers' => headers}
|
260
|
+
else
|
261
|
+
ret = {'size' => obtainedSize, 'url' => url}
|
262
|
+
end
|
263
|
+
|
264
|
+
if not filename
|
265
|
+
ret['body'] = body
|
266
|
+
end
|
267
|
+
|
268
|
+
ret["status"] = status
|
269
|
+
|
270
|
+
return ret
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
@@ -0,0 +1,354 @@
|
|
1
|
+
require 'stupeflixclient/stupeflix_base'
|
2
|
+
require 'stupeflixclient/connection'
|
3
|
+
require 'cgi'
|
4
|
+
require 'json.rb'
|
5
|
+
module Stupeflixclient
|
6
|
+
class StupeflixClient < StupeflixBase
|
7
|
+
def initialize(accessKey, privateKey, host = "http://services.stupeflix.com", service = 'stupeflix-1.0', debug = false)
|
8
|
+
super(accessKey, privateKey, host, service, debug)
|
9
|
+
@batch = false
|
10
|
+
@batchData = ""
|
11
|
+
end
|
12
|
+
|
13
|
+
# Start a batch, used for speeduping video definition upload
|
14
|
+
# Operation that can be batched : sendDefinition and createProfiles
|
15
|
+
# Operation
|
16
|
+
# Only works for xml definition, not zip, and xml must be in UTF8
|
17
|
+
def batchStart( maxSize = 1000000)
|
18
|
+
@batch = true
|
19
|
+
@batchData = "<batch>"
|
20
|
+
@batchMaxSize = maxSize
|
21
|
+
end
|
22
|
+
|
23
|
+
# End a batch: actually send data
|
24
|
+
def batchEnd
|
25
|
+
@batchData += "</batch>"
|
26
|
+
sendDefinitionBatch(body = @batchData)
|
27
|
+
@batchData = ""
|
28
|
+
@batch = false
|
29
|
+
end
|
30
|
+
|
31
|
+
# Send a definition file to the API
|
32
|
+
def sendDefinition( user, resource, filename = nil, body = nil)
|
33
|
+
url = definitionUrl(user, resource)
|
34
|
+
if body
|
35
|
+
contentType = @TEXT_XML_CONTENT_TYPE;
|
36
|
+
elsif isZip(filename)
|
37
|
+
contentType = @APPLICATION_ZIP_CONTENT_TYPE
|
38
|
+
else
|
39
|
+
contentType = @TEXT_XML_CONTENT_TYPE
|
40
|
+
end
|
41
|
+
if @batch and contentType == @TEXT_XML_CONTENT_TYPE
|
42
|
+
@batchData += sprintf("<task user=\"%s\" resource=\"%s\">", user, resource)
|
43
|
+
if body
|
44
|
+
@batchData += body
|
45
|
+
else
|
46
|
+
@batchData += File.open(filename).read
|
47
|
+
end
|
48
|
+
else
|
49
|
+
|
50
|
+
return sendContent("PUT", url, contentType, filename, body)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Send a definition file to the API
|
55
|
+
def sendDefinitionBatch( filename = nil, body = nil)
|
56
|
+
url = @definitionBatchUrl
|
57
|
+
contentType = @TEXT_XML_CONTENT_TYPE;
|
58
|
+
return sendContent("PUT", url, contentType, filename, body)
|
59
|
+
end
|
60
|
+
|
61
|
+
def getDefinition( user, resource, filename)
|
62
|
+
url = definitionUrl(user, resource)
|
63
|
+
return getContent(url, filename)['size']
|
64
|
+
end
|
65
|
+
|
66
|
+
def _getAbsoluteUrl( url, followRedirect = false)
|
67
|
+
urlPart = getContentUrl(url, 'GET', nil)
|
68
|
+
if followRedirect
|
69
|
+
conn = connection.Connection(@base_url, followRedirect = false)
|
70
|
+
response = conn.request_get(urlPart)
|
71
|
+
return response["headers"]["location"]
|
72
|
+
else
|
73
|
+
return @base_url + urlPart
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def getProfileUrl( user, resource, profile, followRedirect = false)
|
78
|
+
url = profileUrl(user, resource, profile)
|
79
|
+
return _getAbsoluteUrl(url, followRedirect)
|
80
|
+
end
|
81
|
+
|
82
|
+
def getProfile( user, resource, profile, filename)
|
83
|
+
url = profileUrl(user, resource, profile)
|
84
|
+
getContent(url, filename)
|
85
|
+
end
|
86
|
+
|
87
|
+
def getProfileThumbUrl( user, resource, profile, followRedirect = false)
|
88
|
+
url = profileThumbUrl(user, resource, profile, "thumb.jpg")
|
89
|
+
return _getAbsoluteUrl(url, followRedirect)
|
90
|
+
end
|
91
|
+
|
92
|
+
def getProfileThumb( user, resource, profile, filename)
|
93
|
+
url = profileThumbUrl(user, resource, profile, "thumb.jpg")
|
94
|
+
getContent(url, filename)
|
95
|
+
end
|
96
|
+
|
97
|
+
def getProfileReportUrl( user, resource, profile, followRedirect = false)
|
98
|
+
url = profileReportUrl(user, resource, profile)
|
99
|
+
return _getAbsoluteUrl(url, followRedirect)
|
100
|
+
end
|
101
|
+
|
102
|
+
def getProfileReport( user, resource, profile, filename)
|
103
|
+
url = profileReportUrl(user, resource, profile)
|
104
|
+
getContent(url, filename)
|
105
|
+
end
|
106
|
+
|
107
|
+
def createProfiles( user, resource, profiles)
|
108
|
+
profileData = profiles.xmlGet
|
109
|
+
if @batch
|
110
|
+
@batchData += profileData
|
111
|
+
@batchData += "</task>"
|
112
|
+
if @batchData.length >= @batchMaxSize
|
113
|
+
begin
|
114
|
+
@batchEnd
|
115
|
+
finally
|
116
|
+
batchStart(@batchMaxSize)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
else
|
120
|
+
url, parameters = profileCreateUrl(user, resource, profileData)
|
121
|
+
contentType = @APPLICATION_URLENCODED_CONTENT_TYPE
|
122
|
+
body = ""
|
123
|
+
parameters.each_pair do |k,v|
|
124
|
+
body += CGI::escape(k) + "=" + CGI::escape(v)
|
125
|
+
end
|
126
|
+
return sendContent("POST", url, contentType, filename = nil, body = body)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def getStatus( user = nil, resource = nil, profile = nil, marker = nil, maxKeys = nil)
|
131
|
+
url, parameters = statusUrl(user, resource, profile, marker, maxKeys)
|
132
|
+
ret = getContent(url, filename = nil, parameters = parameters)
|
133
|
+
status = JSON.parse(ret['body'])
|
134
|
+
return status
|
135
|
+
end
|
136
|
+
|
137
|
+
def getMarker( status)
|
138
|
+
if status.length == 0
|
139
|
+
return nil
|
140
|
+
end
|
141
|
+
lastStatus = status[-1]
|
142
|
+
#return map(lambda x: lastStatus[x], ["user", "resource", "profile"])
|
143
|
+
return []
|
144
|
+
end
|
145
|
+
|
146
|
+
# helper functions : build non signed urls for each kind of action
|
147
|
+
def definitionUrl( user, resource)
|
148
|
+
return sprintf( "/%s/%s/definition/", user, resource)
|
149
|
+
end
|
150
|
+
|
151
|
+
# helper functions : build non signed urls for each kind of action
|
152
|
+
def definitionBatchUrl
|
153
|
+
return "/batch/"
|
154
|
+
end
|
155
|
+
|
156
|
+
def profileUrl( user, resource, profile)
|
157
|
+
return sprintf( "/%s/%s/%s/", user, resource, profile)
|
158
|
+
end
|
159
|
+
|
160
|
+
def profileThumbUrl( user, resource, profile, thumbname)
|
161
|
+
return sprintf( "/%s/%s/%s/%s/", user, resource, profile, thumbname)
|
162
|
+
end
|
163
|
+
|
164
|
+
def profileReportUrl( user, resource, profile)
|
165
|
+
return sprintf( "/%s/%s/%s/%s/", user, resource, profile, "report.xml")
|
166
|
+
end
|
167
|
+
|
168
|
+
def profileCreateUrl( user, resource, profiles)
|
169
|
+
s = sprintf( "/%s/%s/", user, resource)
|
170
|
+
parameters = {@XML_PARAMETER => profiles}
|
171
|
+
return s, parameters
|
172
|
+
end
|
173
|
+
|
174
|
+
def actionUrl( user, resource, profile, action)
|
175
|
+
path = [user, resource, profile]
|
176
|
+
s = ""
|
177
|
+
path.each do |p|
|
178
|
+
if p == nil
|
179
|
+
break
|
180
|
+
end
|
181
|
+
s += sprintf( "/%s", p )
|
182
|
+
end
|
183
|
+
s += sprintf( "/%s/", action )
|
184
|
+
return s
|
185
|
+
end
|
186
|
+
|
187
|
+
def statusUrl( user, resource, profile, marker = nil, maxKeys = nil)
|
188
|
+
params = {}
|
189
|
+
if marker != nil
|
190
|
+
params[@MARKER_PARAMETER] = marker.join('/')
|
191
|
+
end
|
192
|
+
if maxKeys != nil
|
193
|
+
params[@MAXKEYS_PARAMETER] = maxKeys
|
194
|
+
end
|
195
|
+
|
196
|
+
return [actionUrl(user, resource, profile, "status"), params]
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
class StupeflixXMLNode
|
201
|
+
def initialize( nodeName, attributes = nil, children = nil, text = nil)
|
202
|
+
@children = children
|
203
|
+
@attributes = attributes
|
204
|
+
@nodeName = nodeName
|
205
|
+
@text = text
|
206
|
+
end
|
207
|
+
|
208
|
+
def xmlGet
|
209
|
+
docXML = '<' + @nodeName
|
210
|
+
if @attributes and @attributes.length != 0
|
211
|
+
|
212
|
+
@attributes.each_pair do |k, v|
|
213
|
+
docXML += " "
|
214
|
+
if v == nil
|
215
|
+
v = ""
|
216
|
+
end
|
217
|
+
k = k.to_s
|
218
|
+
v = v.to_s
|
219
|
+
docXML += k + '="' + CGI.escapeHTML(v) + '"'
|
220
|
+
end
|
221
|
+
end
|
222
|
+
docXML += '>'
|
223
|
+
if @children
|
224
|
+
for c in @children
|
225
|
+
docXML += c.xmlGet
|
226
|
+
end
|
227
|
+
end
|
228
|
+
if @text
|
229
|
+
docXML += @text
|
230
|
+
end
|
231
|
+
docXML += '</' + @nodeName + '>'
|
232
|
+
|
233
|
+
return docXML
|
234
|
+
end
|
235
|
+
|
236
|
+
def metaChildrenAppend( meta = nil, notify = nil, children = nil)
|
237
|
+
childrenArray = []
|
238
|
+
if meta
|
239
|
+
childrenArray += [meta]
|
240
|
+
end
|
241
|
+
if notify
|
242
|
+
childrenArray += [notify]
|
243
|
+
end
|
244
|
+
if children
|
245
|
+
childrenArray += children
|
246
|
+
end
|
247
|
+
return childrenArray
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
class StupeflixMeta < StupeflixXMLNode
|
252
|
+
def initialize(dict)
|
253
|
+
children = []
|
254
|
+
|
255
|
+
dict.all? {|k, v|
|
256
|
+
children += [StupeflixXMLNode.new(k, nil, nil, v)]
|
257
|
+
}
|
258
|
+
super("meta", {}, children)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
class StupeflixProfileSet < StupeflixXMLNode
|
263
|
+
def initialize( profiles, meta = nil, notify = nil)
|
264
|
+
children = metaChildrenAppend(meta, notify, profiles)
|
265
|
+
super("profiles", {}, children)
|
266
|
+
end
|
267
|
+
|
268
|
+
def deflt(profiles)
|
269
|
+
profSet = []
|
270
|
+
for p in profiles
|
271
|
+
upload = StupeflixDefaultUpload
|
272
|
+
profSet += [StupeflixProfile(p, [upload])]
|
273
|
+
end
|
274
|
+
|
275
|
+
return StupeflixProfileSet.new(profSet)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
class StupeflixProfile < StupeflixXMLNode
|
280
|
+
def initialize( profileName, uploads = nil, meta = nil, notify = nil)
|
281
|
+
children = metaChildrenAppend(meta, notify, uploads)
|
282
|
+
super("profile", {"name" => profileName}, children)
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
class StupeflixNotify < StupeflixXMLNode
|
287
|
+
def initialize( url, statusRegexp)
|
288
|
+
super("notify", {"url" => url, "statusRegexp" => statusRegexp})
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
class StupeflixHttpHeader < StupeflixXMLNode
|
293
|
+
def initialize(key, value)
|
294
|
+
super("header", {"key" => key, "value" => value})
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
class StupeflixUpload < StupeflixXMLNode
|
299
|
+
def initialize( name, parameters, meta = nil, notify = nil, children = nil)
|
300
|
+
children = metaChildrenAppend(meta, notify, children)
|
301
|
+
super(name, parameters, children)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
class StupeflixHttpPOSTUpload < StupeflixUpload
|
306
|
+
def initialize( url, meta = nil, notify = nil)
|
307
|
+
super("httpPOST", {"url" => url}, meta, notify)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
class StupeflixHttpPUTUpload < StupeflixUpload
|
312
|
+
def initialize( url, meta = nil, notify = nil, headers = nil)
|
313
|
+
super("httpPUT", {"url" => url}, meta, notify, headers)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
class StupeflixYoutubeUpload < StupeflixUpload
|
318
|
+
def initialize(login, password, meta = nil, notify = nil)
|
319
|
+
super("youtube", {"login" => login, "password" => password}, meta, notify)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
class StupeflixBrightcoveUpload < StupeflixUpload
|
324
|
+
def initialize(token, reference_id = nil, meta = nil, notify = nil)
|
325
|
+
parameters = {"sid" => token}
|
326
|
+
if reference_id != nil
|
327
|
+
parameters["reference_id"] = reference_id
|
328
|
+
end
|
329
|
+
super("brightcove", parameters, meta, notify)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
class StupeflixDefaultUpload < StupeflixUpload
|
334
|
+
def initialize( meta = nil, notify = nil)
|
335
|
+
children = metaChildrenAppend(meta)
|
336
|
+
super("stupeflixStore", {}, meta, notify)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
class StupeflixS3Upload < StupeflixUpload
|
341
|
+
def initialize(bucket, resourcePrefix, accesskey = nil, secretkey = nil, meta = nil, notify = nil)
|
342
|
+
children = metaChildrenAppend(meta)
|
343
|
+
parameters = {"bucket" => bucket, "resourcePrefix" => resourcePrefix}
|
344
|
+
if accesskey != nil
|
345
|
+
parameters["accesskey"] = accesskey
|
346
|
+
end
|
347
|
+
if secretkey != nil
|
348
|
+
parameters["secretkey"] = secretkey
|
349
|
+
end
|
350
|
+
super("s3", parameters, meta, notify)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
end
|