panda 0.6.4 → 1.0.0
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.
- data/Gemfile +10 -0
- data/README.md +388 -21
- data/Rakefile +4 -17
- data/VERSION +1 -1
- data/lib/panda.rb +21 -1
- data/lib/panda/api_authentication.rb +4 -4
- data/lib/panda/base.rb +102 -0
- data/lib/panda/connection.rb +189 -0
- data/lib/panda/error.rb +29 -0
- data/lib/panda/modules/associations.rb +55 -0
- data/lib/panda/modules/builders.rb +34 -0
- data/lib/panda/modules/cloud_connection.rb +7 -0
- data/lib/panda/modules/finders.rb +62 -0
- data/lib/panda/modules/router.rb +55 -0
- data/lib/panda/modules/short_status.rb +13 -0
- data/lib/panda/modules/updatable.rb +27 -0
- data/lib/panda/panda.rb +21 -170
- data/lib/panda/proxies/encoding_scope.rb +48 -0
- data/lib/panda/proxies/profile_scope.rb +7 -0
- data/lib/panda/proxies/proxy.rb +25 -0
- data/lib/panda/proxies/scope.rb +87 -0
- data/lib/panda/proxies/video_scope.rb +28 -0
- data/lib/panda/resources/cloud.rb +49 -0
- data/lib/panda/resources/encoding.rb +30 -0
- data/lib/panda/resources/profile.rb +22 -0
- data/lib/panda/resources/resource.rb +52 -0
- data/lib/panda/resources/video.rb +13 -0
- data/panda.gemspec +36 -12
- data/spec/cloud_spec.rb +80 -0
- data/spec/encoding_spec.rb +232 -0
- data/spec/heroku_spec.rb +22 -0
- data/spec/panda_spec.rb +17 -4
- data/spec/profile_spec.rb +117 -0
- data/spec/spec_helper.rb +8 -1
- data/spec/video_spec.rb +305 -0
- metadata +33 -23
- data/log/debug.log +0 -0
data/Gemfile
ADDED
data/README.md
CHANGED
@@ -4,13 +4,394 @@ Panda gem provides an interface to access the [Panda](http://pandastream.com) AP
|
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
|
-
|
7
|
+
gem install panda
|
8
8
|
|
9
9
|
## How to use it
|
10
10
|
|
11
11
|
require 'rubygems'
|
12
12
|
require 'panda'
|
13
13
|
|
14
|
+
### Creating an instance of the client
|
15
|
+
|
16
|
+
Panda.configure do |config|
|
17
|
+
config.access_key = "panda_access_key"
|
18
|
+
config.secret_key = "panda_secret_key"
|
19
|
+
config.cloud_id = "panda_cloud_id"
|
20
|
+
end
|
21
|
+
|
22
|
+
or Panda.configure ({:access_key => ....})
|
23
|
+
|
24
|
+
### Creating an instance of the client for EU
|
25
|
+
|
26
|
+
Panda.configure do |config|
|
27
|
+
config.access_key = "panda_access_key"
|
28
|
+
config.secret_key = "panda_secret_key"
|
29
|
+
config.cloud_id = "panda_cloud_id"
|
30
|
+
config.region = "eu"
|
31
|
+
end
|
32
|
+
|
33
|
+
### Creating an instance using Heroku
|
34
|
+
|
35
|
+
Panda.configure do |config|
|
36
|
+
config.heroku = ENV['PANDASTREAM_URL']
|
37
|
+
end
|
38
|
+
|
39
|
+
### Typical usage
|
40
|
+
|
41
|
+
In most cases you will have used the [panda\_uploader](http://github.com/newbamboo/panda_uploader) jQuery plugin to upload the video (more details about this are in the [Integrating Panda with Ruby on Rails](http://pandastream.com/docs/integrate_with_rails) tutorial). Then you will want to get the video and screenshots urls of your encoding to display to your users.
|
42
|
+
|
43
|
+
The name of the profile can be found in your [Panda account](http://pandastream.com/encoders) when editing an encoding cloud's profiles.
|
44
|
+
|
45
|
+
encodings = Panda::Video.find("1234").encodings
|
46
|
+
=> [...]
|
47
|
+
|
48
|
+
mp4_encoding = encodings.find_by_profile_name("h264")
|
49
|
+
ogg_encoding = encodings.find_by_profile_name("ogg")
|
50
|
+
|
51
|
+
mp4_encoding.url
|
52
|
+
=> "http://s3.amazonaws.com/my_panda_bucket/4567.mp4"
|
53
|
+
|
54
|
+
mp4_encoding.screenshots[4]
|
55
|
+
=> "http://s3.amazonaws.com/my_panda_bucket/4567_4.jpg"
|
56
|
+
|
57
|
+
ogg_encoding.url
|
58
|
+
=> "http://s3.amazonaws.com/my_panda_bucket/9876.ogg"
|
59
|
+
|
60
|
+
ogg_encoding.screenshots[4]
|
61
|
+
=> "http://s3.amazonaws.com/my_panda_bucket/9876_4.jpg"
|
62
|
+
|
63
|
+
## Complete functionality
|
64
|
+
|
65
|
+
### Videos
|
66
|
+
|
67
|
+
#### Find a video
|
68
|
+
|
69
|
+
video = Panda::Video.find("1234")
|
70
|
+
video.attributes
|
71
|
+
=> {
|
72
|
+
"id"=>"1234",
|
73
|
+
"original_filename"=>"panda.mp4",
|
74
|
+
"source_url"=>"http://www.example.com/original_video.mp4",
|
75
|
+
"extname"=>".mp4",
|
76
|
+
"duration"=>14010,
|
77
|
+
"audio_codec"=>"aac",
|
78
|
+
"video_codec"=>"h264",
|
79
|
+
"file_size" => "44000",
|
80
|
+
"width"=>300,
|
81
|
+
"height"=>240,
|
82
|
+
"fps"=>29,
|
83
|
+
"status"=>"success",
|
84
|
+
"created_at"=>"2010/01/13 16:45:29 +0000",
|
85
|
+
"updated_at"=>"2010/01/13 16:45:35 +0000"
|
86
|
+
}
|
87
|
+
|
88
|
+
video.id
|
89
|
+
=> "1234"
|
90
|
+
|
91
|
+
video.created_at
|
92
|
+
=>"2010/01/13 16:45:29 +0000"
|
93
|
+
|
94
|
+
video = Panda::Video.first
|
95
|
+
|
96
|
+
video = Panda::Video.find("fake_id")
|
97
|
+
=> raise: RecordNotFound: Couldn't find Video with ID=fake_id
|
98
|
+
|
99
|
+
video.to_json
|
100
|
+
=>"{\"duration\":14010,\"created_at\":\"2010/01/13 16:45:29 +0000\",\"original_filename\":\"panda.mp4\"....}"
|
101
|
+
|
102
|
+
video.processing?
|
103
|
+
=> false
|
104
|
+
|
105
|
+
video.fail?
|
106
|
+
=> false
|
107
|
+
|
108
|
+
video.success?
|
109
|
+
=> true
|
110
|
+
|
111
|
+
video.reload
|
112
|
+
=> <Panda::Video:0x1036fd660 ...>
|
113
|
+
|
114
|
+
##### Find encodings of a video
|
115
|
+
|
116
|
+
video = Panda::Video.find("1234")
|
117
|
+
video.encodings
|
118
|
+
=> [...]
|
119
|
+
|
120
|
+
video.encodings.profile("3456")
|
121
|
+
=> [...]
|
122
|
+
|
123
|
+
or
|
124
|
+
|
125
|
+
video.encodings.all(:profile_id => "3456")
|
126
|
+
=> [...]
|
127
|
+
|
128
|
+
##### Find all videos
|
129
|
+
|
130
|
+
videos = Panda::Video.all
|
131
|
+
=> [...]
|
132
|
+
|
133
|
+
videos.first.id
|
134
|
+
=> "3456"
|
135
|
+
|
136
|
+
videos = Panda::Video.page(2).per_page(20)
|
137
|
+
|
138
|
+
or
|
139
|
+
|
140
|
+
videos = Panda::Video.all(:page => 2, :per_page => 20)
|
141
|
+
videos.size
|
142
|
+
=> 20
|
143
|
+
|
144
|
+
##### Find all success videos
|
145
|
+
|
146
|
+
video = Panda::Video.status("success")
|
147
|
+
=> [...]
|
148
|
+
|
149
|
+
or
|
150
|
+
|
151
|
+
videos = Panda::Video.all(:status => "success")
|
152
|
+
=> [...]
|
153
|
+
|
154
|
+
status: success | processing | fail
|
155
|
+
|
156
|
+
#### Create a new video
|
157
|
+
|
158
|
+
from a source
|
159
|
+
|
160
|
+
video = Panda::Video.create(:source_url => "http://mywebsite.com/myvideo.mp4")
|
161
|
+
|
162
|
+
or
|
163
|
+
|
164
|
+
video = Panda::Video.new(:source_url => "http://mywebsite.com/myvideo.mp4")
|
165
|
+
video.create
|
166
|
+
=> true
|
167
|
+
|
168
|
+
# Note that you will need a movie file to test this.
|
169
|
+
# You can grab http://panda-test-harness-videos.s3.amazonaws.com/panda.mp4
|
170
|
+
|
171
|
+
from a local file
|
172
|
+
|
173
|
+
video = Panda::Video.create(:file => File.new("/home/me/panda.mp4"))
|
174
|
+
|
175
|
+
or
|
176
|
+
|
177
|
+
video = Panda::Video.new(:file => File.new("/home/me/panda.mp4"))
|
178
|
+
video.create
|
179
|
+
=> true
|
180
|
+
|
181
|
+
#### Delete a video
|
182
|
+
|
183
|
+
Panda::Video.delete("1234")
|
184
|
+
|
185
|
+
or
|
186
|
+
|
187
|
+
video = Panda::Video.find("1234")
|
188
|
+
video.delete
|
189
|
+
=> true
|
190
|
+
|
191
|
+
### Encodings
|
192
|
+
|
193
|
+
##### Find an encoding
|
194
|
+
|
195
|
+
encoding = Panda::Encoding.find("4567")
|
196
|
+
encoding.attributes
|
197
|
+
=> {
|
198
|
+
"id"=>"4567",
|
199
|
+
"video_id"=>"1234",
|
200
|
+
"extname"=>".mp4",
|
201
|
+
"encoding_progress"=>60,
|
202
|
+
"encoding_time"=>3,
|
203
|
+
"file_size" => "25000",
|
204
|
+
"width"=>300,
|
205
|
+
"height"=>240,
|
206
|
+
"profile_id"=>"6789",
|
207
|
+
"status"=>"success",
|
208
|
+
"started_encoding_at"=>"2010/01/13 16:47:35 +0000",
|
209
|
+
"created_at"=>"2010/01/13 16:45:30 +0000",
|
210
|
+
"updated_at"=>"2010/01/13 16:47:40 +0000"
|
211
|
+
}
|
212
|
+
|
213
|
+
encoding.encoding_progress
|
214
|
+
=> 60
|
215
|
+
|
216
|
+
encoding.processing?
|
217
|
+
=> false
|
218
|
+
|
219
|
+
encoding.fail?
|
220
|
+
=> false
|
221
|
+
|
222
|
+
encoding.success?
|
223
|
+
=> true
|
224
|
+
|
225
|
+
encoding.video.original_filename
|
226
|
+
=> "panda.mp4"
|
227
|
+
|
228
|
+
encoding = Panda::Encoding.first
|
229
|
+
|
230
|
+
##### Find all encodings of a video
|
231
|
+
|
232
|
+
encodings = Panda::Encoding.page(4)
|
233
|
+
|
234
|
+
or
|
235
|
+
|
236
|
+
encodings = Panda::Encoding.all(:page => 4)
|
237
|
+
=> [...]
|
238
|
+
|
239
|
+
encodings = Panda::Encoding.video(video_id)
|
240
|
+
=> [...]
|
241
|
+
|
242
|
+
encoding = Panda::Encoding.video(video_id).profile_name("h264").first
|
243
|
+
|
244
|
+
or
|
245
|
+
|
246
|
+
encoding = Panda::Encoding.find_by :video_id => "video_id", :profile_name => "h264"
|
247
|
+
encoding.encoding_time
|
248
|
+
=> 3
|
249
|
+
|
250
|
+
profile = encodings.first.profile
|
251
|
+
profile.title
|
252
|
+
=> "H264 profile"
|
253
|
+
|
254
|
+
##### Find all success encodings
|
255
|
+
|
256
|
+
encodings = Panda::Encoding.video("1234").status("success")
|
257
|
+
or
|
258
|
+
encodings = Panda::Encoding.all(:video_id => "1234", :status => "success")
|
259
|
+
=> [...]
|
260
|
+
|
261
|
+
or
|
262
|
+
|
263
|
+
video = Panda::Video.find("1234")
|
264
|
+
video.encodings.status("success")
|
265
|
+
|
266
|
+
status: success | processing | fail
|
267
|
+
|
268
|
+
##### Retrieve the encoding
|
269
|
+
|
270
|
+
encoding = Panda::Encoding.find("4567")
|
271
|
+
encoding.url
|
272
|
+
=> "http://s3.amazonaws.com/my_panda_bucket/4567.mp4"
|
273
|
+
|
274
|
+
encoding.screenshots[0]
|
275
|
+
=> "http://s3.amazonaws.com/my_panda_bucket/4567_1.jpg"
|
276
|
+
|
277
|
+
##### Create a new encoding
|
278
|
+
|
279
|
+
encoding = Panda::Encoding.create(:video_id => 1234, :profile_id => 6789)
|
280
|
+
|
281
|
+
or
|
282
|
+
|
283
|
+
video = Panda::Video.find("123")
|
284
|
+
encoding = video.encodings.create(:profile => "profile_id")
|
285
|
+
|
286
|
+
##### Delete an encoding
|
287
|
+
|
288
|
+
Panda::Encoding.delete("4567")
|
289
|
+
|
290
|
+
or
|
291
|
+
|
292
|
+
encoding = Panda::Encoding.find("4567")
|
293
|
+
encoding.delete
|
294
|
+
=> true
|
295
|
+
|
296
|
+
### Profiles
|
297
|
+
|
298
|
+
##### Find a profile
|
299
|
+
|
300
|
+
profile = Panda::Profile.find("6789")
|
301
|
+
|
302
|
+
##### Find all profiles
|
303
|
+
|
304
|
+
profiles = Panda::Profile.all
|
305
|
+
|
306
|
+
##### Create a profile
|
307
|
+
|
308
|
+
profile = Panda::Profile.create(:preset_name => "h264")
|
309
|
+
profile = Panda::Profile.create(:command => "ffmpeg -i $input_file$ -y $output_file$", ....)
|
310
|
+
|
311
|
+
##### Update a profile
|
312
|
+
|
313
|
+
profile = Panda::Profile.find("6789")
|
314
|
+
profile.width = 320
|
315
|
+
profile.height = 280
|
316
|
+
profile.save
|
317
|
+
=> true
|
318
|
+
|
319
|
+
profile.id = "fake_profile_id"
|
320
|
+
profile.save
|
321
|
+
=> false
|
322
|
+
|
323
|
+
profile.errors.last.to_s
|
324
|
+
=> RecordNotFound: Couldn't find Profile with ID=fake_profile_id
|
325
|
+
|
326
|
+
##### Delete a profile
|
327
|
+
|
328
|
+
Panda::Profile.delete("4567")
|
329
|
+
|
330
|
+
or
|
331
|
+
|
332
|
+
profile = Panda::Profile.find("6789")
|
333
|
+
profile.delete
|
334
|
+
=> true
|
335
|
+
|
336
|
+
##### All encoding of a profile
|
337
|
+
|
338
|
+
profile = Panda::Profile.find("6789")
|
339
|
+
profile.encodings
|
340
|
+
=> [...]
|
341
|
+
|
342
|
+
profile.encodings.status("success")
|
343
|
+
or
|
344
|
+
profile.encodings.all(:status => "success")
|
345
|
+
=> [...]
|
346
|
+
|
347
|
+
### Using multiple clouds
|
348
|
+
|
349
|
+
By default Cloud.id uses options defined with: Panda.configure do .. end
|
350
|
+
|
351
|
+
cloud_one = Panda::Cloud.find("cloud_id_1")
|
352
|
+
cloud_two = Panda::Cloud.find("cloud_id_2")
|
353
|
+
|
354
|
+
cloud_one.profiles
|
355
|
+
cloud_two.profiles.find("profile_id")
|
356
|
+
|
357
|
+
cloud_two.videos
|
358
|
+
cloud_two.videos.status("success")
|
359
|
+
cloud_two.videos.all(:status => "success")
|
360
|
+
cloud_two.videos.all(:page => 2)
|
361
|
+
|
362
|
+
cloud_one.videos.find("video_id_1")
|
363
|
+
cloud_two.videos.find("video_id_2")
|
364
|
+
|
365
|
+
cloud_two.profiles
|
366
|
+
cloud_two.profiles.create(:preset_name => "h264")
|
367
|
+
cloud_one.videos.create(:command => "ffmpeg -i $input_file$ -y $output_file$", ....)
|
368
|
+
|
369
|
+
You can also connect directly using Cloud.find
|
370
|
+
|
371
|
+
cloud = Panda::Cloud.find("cloud_id_1", {:access_key => ..., :secret_key => ... })
|
372
|
+
|
373
|
+
|
374
|
+
## Generating signatures
|
375
|
+
|
376
|
+
All requests to your Panda cloud are signed using HMAC-SHA256, based on a timestamp and your Panda secret key. This is handled transparently. However, sometimes you will want to generate only this signature, in order to make a request by means other than this library. This is the case when using the [JavaScript panda_uploader](http://github.com/newbamboo/panda_uploader).
|
377
|
+
|
378
|
+
To do this, a method `signed_params()` is supported:
|
379
|
+
|
380
|
+
Panda.signed_params('POST', '/videos.json')
|
381
|
+
# => {'access_key' => '8df50af4-074f-11df-b278-1231350015b1',
|
382
|
+
# 'cloud_id' => 'your-cloud-id',
|
383
|
+
# 'signature' => 'LejCdm0O83+jk6/Q5SfGmk14WTO1pB6Sh6Z5eA2w5C0=',
|
384
|
+
# 'timestamp' => '2010-02-26T15:01:46.221513'}
|
385
|
+
|
386
|
+
Panda.signed_params('GET', '/videos.json', {'some_params' => 'some_value'})
|
387
|
+
# => {'access_key' => '8df50af4-074f-11df-b278-1231350015b1',
|
388
|
+
# 'cloud_id' => 'your-cloud-id',
|
389
|
+
# 'signature' => 'uHnGZ+kI9mT3C4vW71Iop9z2N7UKCv38v2l2dvREUIQ=',
|
390
|
+
# 'some_param' => 'some_value',
|
391
|
+
# 'timestamp' => '2010-02-26T15:04:27.039620'}
|
392
|
+
|
393
|
+
## Old Panda way, still works
|
394
|
+
|
14
395
|
### Creating an instance of the client
|
15
396
|
|
16
397
|
Panda.connect!({
|
@@ -77,7 +458,7 @@ Panda gem provides an interface to access the [Panda](http://pandastream.com) AP
|
|
77
458
|
"profile_id"=>"00182830-0063-11df-8c8a-1231390041c1",
|
78
459
|
"width"=>300}]
|
79
460
|
|
80
|
-
### Deleting
|
461
|
+
### Deleting an encoding
|
81
462
|
|
82
463
|
Panda.delete('/encodings/0f815986-0063-11df-a433-1231390041c1.json')
|
83
464
|
|
@@ -85,25 +466,6 @@ Panda gem provides an interface to access the [Panda](http://pandastream.com) AP
|
|
85
466
|
|
86
467
|
Panda.delete('/videos/0ee6b656-0063-11df-a433-1231390041c1.json')
|
87
468
|
|
88
|
-
## Generating signatures
|
89
|
-
|
90
|
-
All requests to your Panda cloud are signed using HMAC-SHA256, based on a timestamp and your Panda secret key. This is handled transparently. However, sometimes you will want to generate only this signature, in order to make a request by means other than this library. This is the case when using the [JavaScript panda_uploader](http://github.com/newbamboo/panda_uploader).
|
91
|
-
|
92
|
-
To do this, a method `signed_params()` is supported:
|
93
|
-
|
94
|
-
Panda.signed_params('POST', '/videos.json')
|
95
|
-
# => {'access_key' => '8df50af4-074f-11df-b278-1231350015b1',
|
96
|
-
# 'cloud_id' => 'your-cloud-id',
|
97
|
-
# 'signature' => 'LejCdm0O83+jk6/Q5SfGmk14WTO1pB6Sh6Z5eA2w5C0=',
|
98
|
-
# 'timestamp' => '2010-02-26T15:01:46.221513'}
|
99
|
-
|
100
|
-
Panda.signed_params('GET', '/videos.json', {'some_params' => 'some_value'})
|
101
|
-
# => {'access_key' => '8df50af4-074f-11df-b278-1231350015b1',
|
102
|
-
# 'cloud_id' => 'your-cloud-id',
|
103
|
-
# 'signature' => 'uHnGZ+kI9mT3C4vW71Iop9z2N7UKCv38v2l2dvREUIQ=',
|
104
|
-
# 'some_param' => 'some_value',
|
105
|
-
# 'timestamp' => '2010-02-26T15:04:27.039620'}
|
106
|
-
|
107
469
|
## Hash or JSON
|
108
470
|
Since Panda 0.6, PandaGem returns a Hash by default. If you want PandaGem to return JSON do the following:
|
109
471
|
|
@@ -115,6 +477,11 @@ Since Panda 0.6, PandaGem returns a Hash by default. If you want PandaGem to ret
|
|
115
477
|
})
|
116
478
|
|
117
479
|
|
480
|
+
## Use bundler to setup the test environment (1.0)
|
481
|
+
|
482
|
+
bundler install
|
483
|
+
rake spec
|
484
|
+
|
118
485
|
Copyright
|
119
486
|
---------
|
120
487
|
|