animoto 0.0.0.alpha0

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.
Files changed (44) hide show
  1. data/README.md +39 -0
  2. data/lib/animoto/asset.rb +25 -0
  3. data/lib/animoto/client.rb +226 -0
  4. data/lib/animoto/content_type.rb +47 -0
  5. data/lib/animoto/directing_and_rendering_job.rb +19 -0
  6. data/lib/animoto/directing_and_rendering_manifest.rb +25 -0
  7. data/lib/animoto/directing_job.rb +18 -0
  8. data/lib/animoto/directing_manifest.rb +115 -0
  9. data/lib/animoto/errors.rb +3 -0
  10. data/lib/animoto/footage.rb +15 -0
  11. data/lib/animoto/image.rb +14 -0
  12. data/lib/animoto/job.rb +37 -0
  13. data/lib/animoto/manifest.rb +21 -0
  14. data/lib/animoto/rendering_job.rb +24 -0
  15. data/lib/animoto/rendering_manifest.rb +37 -0
  16. data/lib/animoto/resource.rb +153 -0
  17. data/lib/animoto/song.rb +16 -0
  18. data/lib/animoto/standard_envelope.rb +27 -0
  19. data/lib/animoto/storyboard.rb +22 -0
  20. data/lib/animoto/title_card.rb +26 -0
  21. data/lib/animoto/video.rb +29 -0
  22. data/lib/animoto/visual.rb +30 -0
  23. data/lib/animoto.rb +5 -0
  24. data/spec/animoto/asset_spec.rb +1 -0
  25. data/spec/animoto/client_spec.rb +119 -0
  26. data/spec/animoto/directing_and_rendering_job_spec.rb +45 -0
  27. data/spec/animoto/directing_and_rendering_manifest_spec.rb +143 -0
  28. data/spec/animoto/directing_job_spec.rb +48 -0
  29. data/spec/animoto/directing_manifest_spec.rb +186 -0
  30. data/spec/animoto/footage_spec.rb +56 -0
  31. data/spec/animoto/image_spec.rb +41 -0
  32. data/spec/animoto/job_spec.rb +128 -0
  33. data/spec/animoto/rendering_job_spec.rb +57 -0
  34. data/spec/animoto/rendering_manifest_spec.rb +115 -0
  35. data/spec/animoto/resource_spec.rb +55 -0
  36. data/spec/animoto/song_spec.rb +54 -0
  37. data/spec/animoto/standard_envelope_spec.rb +0 -0
  38. data/spec/animoto/storyboard_spec.rb +8 -0
  39. data/spec/animoto/title_card_spec.rb +42 -0
  40. data/spec/animoto/video_spec.rb +1 -0
  41. data/spec/animoto/visual_spec.rb +0 -0
  42. data/spec/animoto_spec.rb +5 -0
  43. data/spec/spec_helper.rb +10 -0
  44. metadata +127 -0
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ Animoto API Client
2
+ ==================
3
+
4
+ ## Workflow
5
+
6
+ require 'animoto/client'
7
+ include Animoto
8
+
9
+ client = Client.new("username", "password")
10
+
11
+ manifest = DirectingManifest.new(:title => "Amazing Title!", :producer => "Fishy Joe")
12
+ manifest << Image.new("http://website.com/picture.png")
13
+ manifest << Image.new("http://website.com/hooray.png", :spotlit => true)
14
+ manifest << TitleCard.new("Woohoo!", "Hooray for everything!")
15
+ manifest << Footage.new("http://website.com/movie.mp4", :duration => 3.5)
16
+ manifest << Song.new("http://website.com/song.mp3", :artist => "Fishy Joe")
17
+
18
+ directing_job = client.direct!(manifest)
19
+ while directing_job.pending?
20
+ sleep(30)
21
+ client.reload!(directing_job)
22
+ end
23
+
24
+ if storyboard = directing_job.storyboard
25
+ manifest = RenderingManifest.new(storyboard, :resolution => "720p", :framerate => 24, :format => 'h264')
26
+ rendering_job = client.render!(manifest)
27
+ while rendering_job.pending?
28
+ sleep(30)
29
+ client.reload!(rendering_job)
30
+ end
31
+
32
+ if video = rendering_job.video
33
+ puts video.url
34
+ else
35
+ raise rendering_job.errors.first
36
+ end
37
+ else
38
+ raise directing_job.errors.first
39
+ end
@@ -0,0 +1,25 @@
1
+ module Animoto
2
+ class Asset
3
+
4
+ attr_accessor :source_url
5
+
6
+ def initialize source_url, options = {}
7
+ @source_url = source_url
8
+ end
9
+
10
+ # Returns a representation of this asset as a Hash. Used mainly for generating
11
+ # manifests.
12
+ #
13
+ # @return [Hash] this asset as a Hash
14
+ def to_hash
15
+ { 'source_url' => @source_url }
16
+ end
17
+
18
+ # Returns a representation of this asset as JSON.
19
+ #
20
+ # @return [String] this asset as JSON
21
+ def to_json
22
+ self.to_hash.to_json
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,226 @@
1
+ require 'uri'
2
+ require 'net/http'
3
+ require 'net/https'
4
+ require 'json'
5
+ require 'yaml'
6
+
7
+ require 'animoto/errors'
8
+ require 'animoto/content_type'
9
+ require 'animoto/standard_envelope'
10
+ require 'animoto/resource'
11
+ require 'animoto/asset'
12
+ require 'animoto/visual'
13
+ require 'animoto/footage'
14
+ require 'animoto/image'
15
+ require 'animoto/song'
16
+ require 'animoto/title_card'
17
+ require 'animoto/manifest'
18
+ require 'animoto/directing_manifest'
19
+ require 'animoto/rendering_manifest'
20
+ require 'animoto/directing_and_rendering_manifest'
21
+ require 'animoto/storyboard'
22
+ require 'animoto/video'
23
+ require 'animoto/job'
24
+ require 'animoto/directing_and_rendering_job'
25
+ require 'animoto/directing_job'
26
+ require 'animoto/rendering_job'
27
+
28
+ module Animoto
29
+ class Client
30
+ API_ENDPOINT = "http://api2-staging.animoto.com/"
31
+ API_VERSION = 1
32
+ BASE_CONTENT_TYPE = "application/vnd.animoto"
33
+ HTTP_METHOD_MAP = {
34
+ :get => Net::HTTP::Get,
35
+ :post => Net::HTTP::Post
36
+ }
37
+
38
+ attr_accessor :key, :secret
39
+ attr_reader :format
40
+
41
+ # Creates a new Client object which handles credentials, versioning, making requests, and
42
+ # parsing responses.
43
+ #
44
+ # If you have your key and secret in ~/.animotorc or /etc/.animotorc, those credentials will
45
+ # be read from those files (in that order) whenever you make a new Client if you don't specify
46
+ # them explicitly.
47
+ #
48
+ # @param [String] key the API key for your account
49
+ # @param [String] secret the secret key for your account
50
+ # @return [Client]
51
+ # @raise [ArgumentError] if no credentials are supplied
52
+ def initialize *args
53
+ @debug = ENV['DEBUG']
54
+ options = args.last.is_a?(Hash) ? args.pop : {}
55
+ @key = args[0]
56
+ @secret = args[1]
57
+ unless @key && @secret
58
+ home_path = File.expand_path '~/.animotorc'
59
+ config = if File.exist?(home_path)
60
+ YAML.load(File.read(home_path))
61
+ elsif File.exist?('/etc/.animotorc')
62
+ YAML.load(File.read('/etc/.animotorc'))
63
+ end
64
+ if config
65
+ @key ||= config['key']
66
+ @secret ||= config['secret']
67
+ else
68
+ raise ArgumentError, "You must supply your key and secret"
69
+ end
70
+ end
71
+ @format = 'json'
72
+ uri = URI.parse(API_ENDPOINT)
73
+ @http = Net::HTTP.new uri.host, uri.port
74
+ # @http.use_ssl = true
75
+ end
76
+
77
+ # Finds a resource by its URL.
78
+ #
79
+ # @param [Class] klass the Resource class you're finding
80
+ # @param [String] url the URL of the resource you want
81
+ # @param [Hash] options
82
+ # @return [Resource] the Resource object found
83
+ def find klass, url, options = {}
84
+ klass.load(find_request(klass, url, options))
85
+ end
86
+
87
+ # Sends a request to start directing a storyboard.
88
+ #
89
+ # @param [DirectingManifest] manifest the manifest to direct
90
+ # @param [Hash] options
91
+ # @return [DirectingJob] a job to monitor the status of the directing
92
+ def direct! manifest, options = {}
93
+ DirectingJob.load(send_manifest(manifest, DirectingJob.endpoint, options))
94
+ end
95
+
96
+ # Sends a request to start rendering a video.
97
+ #
98
+ # @param [RenderingManifest] manifest the manifest to render
99
+ # @param [Hash] options
100
+ # @return [RenderingJob] a job to monitor the status of the rendering
101
+ def render! manifest, options = {}
102
+ RenderingJob.load(send_manifest(manifest, RenderingJob.endpoint, options))
103
+ end
104
+
105
+ # Sends a request to start directing and rendering a video.
106
+ #
107
+ # @param [DirectingAndRenderingManifest] manifest the manifest to direct and render
108
+ # @param [Hash] options
109
+ # @return [DirectingAndRenderingJob] a job to monitor the status of the directing and rendering
110
+ def direct_and_render! manifest, options = {}
111
+ DirectingAndRenderingJob.load(send_manifest(manifest, DirectingAndRenderingJob.endpoint, options))
112
+ end
113
+
114
+ # Update a resource with the latest attributes. Useful to update the state of a Job to
115
+ # see if it's ready if you are not using HTTP callbacks.
116
+ #
117
+ # @param [Resource] resource the resource to update
118
+ # @param [Hash] options
119
+ # @return [Resource] the given resource with the latest attributes
120
+ def reload! resource, options = {}
121
+ resource.load(find_request(resource.class, resource.url, options))
122
+ end
123
+
124
+ private
125
+
126
+ # Builds a request to find a resource.
127
+ #
128
+ # @param [Class] klass the Resource class you're looking for
129
+ # @param [String] url the URL of the resource
130
+ # @param [Hash] options
131
+ # @return [Hash] deserialized JSON response body
132
+ def find_request klass, url, options = {}
133
+ request(:get, URI.parse(url).path, nil, { "Accept" => content_type_of(klass) }, options)
134
+ end
135
+
136
+ # Builds a request requiring a manifest.
137
+ #
138
+ # @param [Manifest] manifest the manifest being acted on
139
+ # @param [String] endpoint the endpoint to send the request to
140
+ # @param [Hash] options
141
+ # @return [Hash] deserialized JSON response body
142
+ def send_manifest manifest, endpoint, options = {}
143
+ request(:post, endpoint, manifest.to_json, { "Accept" => "application/#{format}", "Content-Type" => content_type_of(manifest) }, options)
144
+ end
145
+
146
+ # Makes a request and parses the response.
147
+ #
148
+ # @param [Symbol] method which HTTP method to use (should be lowercase, i.e. :get instead of :GET)
149
+ # @param [String] uri the request path
150
+ # @param [String, nil] body the request body
151
+ # @param [Hash<String,String>] headers the request headers (will be sent as-is, which means you should
152
+ # specify "Content-Type" => "..." instead of, say, :content_type => "...")
153
+ # @param [Hash] options
154
+ # @return [Hash] deserialized JSON response body
155
+ def request method, uri, body, headers = {}, options = {}
156
+ req = build_request method, uri, body, headers, options
157
+ read_response @http.request(req)
158
+ end
159
+
160
+ # Builds the request object.
161
+ #
162
+ # @param [Symbol] method which HTTP method to use (should be lowercase, i.e. :get instead of :GET)
163
+ # @param [String] uri the request path
164
+ # @param [String, nil] body the request body
165
+ # @param [Hash<String,String>] headers the request headers (will be sent as-is, which means you should
166
+ # specify "Content-Type" => "..." instead of, say, :content_type => "...")
167
+ # @param [Hash] options
168
+ # @return [Net::HTTPRequest] the request object
169
+ def build_request method, uri, body, headers, options
170
+ req = HTTP_METHOD_MAP[method].new uri
171
+ req.body = body
172
+ req.initialize_http_header headers
173
+ req.basic_auth key, secret
174
+ req
175
+ end
176
+
177
+ # Verifies and parses the response.
178
+ #
179
+ # @param [Net::HTTPResponse] response the response object
180
+ # @return [Hash] deserialized JSON response body
181
+ def read_response response
182
+ check_status response
183
+ parse_response response
184
+ end
185
+
186
+ # Checks the status of the response to make sure it's successful.
187
+ #
188
+ # @param [Net::HTTPResponse] response the response object
189
+ # @return [nil]
190
+ # @raise [Error,RuntimeError] if the response code isn't in the 200 range
191
+ def check_status response
192
+ unless (200..299).include?(response.code.to_i)
193
+ if response.body
194
+ begin
195
+ json = JSON.parse(response.body)
196
+ errors = json['response']['status']['errors']
197
+ rescue => e
198
+ raise response.message
199
+ else
200
+ raise Animoto::Error.new(errors.collect { |e| e['message'] }.join(', '))
201
+ end
202
+ else
203
+ raise response.message
204
+ end
205
+ end
206
+ end
207
+
208
+ # Parses a JSON response body into a Hash.
209
+ # @param [Net::HTTPResponse] response the response object
210
+ # @return [Hash] deserialized JSON response body
211
+ def parse_response response
212
+ JSON.parse(response.body)
213
+ end
214
+
215
+ # Creates the full content type string given a Resource class or instance
216
+ # @param [Class,ContentType] klass_or_instance the class or instance to build the
217
+ # content type for
218
+ # @return [String] the full content type with the version and format included (i.e.
219
+ # "application/vnd.animoto.storyboard-v1+json")
220
+ def content_type_of klass_or_instance
221
+ klass = klass_or_instance.is_a?(Class) ? klass_or_instance : klass_or_instance.class
222
+ "#{BASE_CONTENT_TYPE}.#{klass.content_type}-v#{API_VERSION}+#{format}"
223
+ end
224
+
225
+ end
226
+ end
@@ -0,0 +1,47 @@
1
+ module Animoto
2
+ module ContentType
3
+
4
+ # When included, includes the InstanceMethods module and extends the
5
+ # ClassMethods module.
6
+ def self.included base
7
+ base.class_eval {
8
+ include Animoto::ContentType::InstanceMethods
9
+ extend Animoto::ContentType::ClassMethods
10
+ }
11
+ end
12
+
13
+ module InstanceMethods
14
+ # Returns the content type for this class.
15
+ #
16
+ # @return [String] the content type
17
+ def content_type
18
+ self.class.content_type
19
+ end
20
+ end
21
+
22
+ module ClassMethods
23
+ # @overload content_type(type)
24
+ # Sets the content type for this class.
25
+ # @param [String] content_type the type
26
+ # @return [String] the content type
27
+ # @overload content_type()
28
+ # Returns the content type for this class.
29
+ # @return [String] the content type
30
+ def content_type type = nil
31
+ @content_type = type if type
32
+ @content_type || infer_content_type
33
+ end
34
+
35
+ private
36
+
37
+ # If no content type is explicitly set, this will infer the name of the content
38
+ # type from the class name by lowercasing and underscoring the base name of the
39
+ # class. For example, Animoto::DirectingJob becomes "directing_job".
40
+ #
41
+ # @return [String] the inferred content type
42
+ def infer_content_type
43
+ name.split('::').last.gsub(/(^)?([A-Z])/) { "#{'_' unless $1}#{$2.downcase}" }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,19 @@
1
+ module Animoto
2
+ class DirectingAndRenderingJob < Animoto::Job
3
+
4
+ endpoint '/jobs/directing_and_rendering'
5
+
6
+ def self.unpack_standard_envelope body
7
+ super.merge(:video_url => body['response']['payload'][payload_key]['links']['video'])
8
+ end
9
+
10
+ attr_reader :video, :video_url
11
+
12
+ def instantiate attributes = {}
13
+ @video_url = attributes[:video_url]
14
+ @video = Animoto::Video.new(:url => @video_url) if @video_url
15
+ super
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ module Animoto
2
+ class DirectingAndRenderingManifest < Animoto::DirectingManifest
3
+
4
+ attr_accessor :resolution, :framerate, :format
5
+
6
+ def initialize options = {}
7
+ super
8
+ @resolution = options[:resolution]
9
+ @framerate = options[:framerate]
10
+ @format = options[:format]
11
+ end
12
+
13
+ def to_hash options = {}
14
+ hash = super
15
+ directing_job = hash.delete('directing_job')
16
+ hash['directing_and_rendering_job'] = directing_job.merge('rendering_manifest' => { 'rendering_profile' => {}})
17
+ profile = hash['directing_and_rendering_job']['rendering_manifest']['rendering_profile']
18
+ profile['vertical_resolution'] = resolution
19
+ profile['framerate'] = framerate
20
+ profile['format'] = format
21
+ hash
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,18 @@
1
+ module Animoto
2
+ class DirectingJob < Animoto::Job
3
+
4
+ endpoint '/jobs/directing'
5
+
6
+ def self.unpack_standard_envelope body
7
+ super.merge(:storyboard_url => body['response']['payload'][payload_key]['links']['storyboard'])
8
+ end
9
+
10
+ attr_reader :storyboard, :storyboard_url
11
+
12
+ def instantiate attributes = {}
13
+ @storyboard_url = attributes[:storyboard_url]
14
+ @storyboard = Animoto::Storyboard.new(:url => @storyboard_url) if @storyboard_url
15
+ super
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,115 @@
1
+ module Animoto
2
+ class DirectingManifest < Animoto::Manifest
3
+
4
+ attr_accessor :title, :producer, :pacing, :http_callback_url, :http_callback_format
5
+ attr_reader :visuals, :song, :style
6
+
7
+ # Creates a new DirectingManifest.
8
+ #
9
+ # @param [Hash] options
10
+ # @option options [String] :title the title of this project
11
+ # @option options [String] :producer the name of the producer of this project
12
+ # @option options ['default','half','double'] :pacing ('default') the pacing for this project
13
+ # @option options [String] :http_callback_url a URL to receive a callback when this job is done
14
+ # @option options ['json','xml'] :http_callback_format the format of the callback
15
+ def initialize options = {}
16
+ @title = options[:title]
17
+ @producer = options[:producer]
18
+ @pacing = options[:pacing] || 'default'
19
+ @style = 'original'
20
+ @visuals = []
21
+ @song = nil
22
+ @http_callback_url = options[:http_callback_url]
23
+ @http_callback_format = options[:http_callback_format]
24
+ end
25
+
26
+ # Adds a TitleCard to this manifest.
27
+ #
28
+ # @see TitleCard
29
+ # @return [TitleCard] the new TitleCard
30
+ def add_title_card *args
31
+ card = TitleCard.new *args
32
+ @visuals << card
33
+ card
34
+ end
35
+
36
+ # Adds an Image to this manifest.
37
+ #
38
+ # @see Image
39
+ # @return [Image] the new Image
40
+ def add_image *args
41
+ image = Image.new *args
42
+ @visuals << image
43
+ image
44
+ end
45
+
46
+ # Adds Footage to this manifest.
47
+ #
48
+ # @see Footage
49
+ # @return [Footage] the new Footage
50
+ def add_footage *args
51
+ footage = Footage.new *args
52
+ @visuals << footage
53
+ footage
54
+ end
55
+
56
+ # Adds a Song to this manifest. Right now, a manifest can only have one song. Adding
57
+ # a second replaces the first.
58
+ #
59
+ # @see Song
60
+ # @return [Song] the new Song
61
+ def add_song *args
62
+ @song = Song.new *args
63
+ end
64
+
65
+ # Adds a visual/song to this manifest.
66
+ #
67
+ # @param [Visual,Song] asset the asset to add
68
+ # @raise [ArgumentError] if the asset isn't a Song or Visual
69
+ def add_visual asset
70
+ case asset
71
+ when Animoto::Song
72
+ @song = asset
73
+ when Animoto::Visual
74
+ @visuals << asset
75
+ else
76
+ raise ArgumentError
77
+ end
78
+ end
79
+
80
+ # Adds a visual/song to this manifest.
81
+ #
82
+ # @param [Visual,Song] asset the asset to add
83
+ # @return [self]
84
+ def << asset
85
+ add_visual asset
86
+ self
87
+ end
88
+
89
+ # Returns a representation of this manifest as a Hash.
90
+ #
91
+ # @return [Hash] the manifest as a Hash
92
+ # @raise [ArgumentError] if a callback URL is specified but not the format
93
+ def to_hash options = {}
94
+ hash = { 'directing_job' => { 'directing_manifest' => {} } }
95
+ job = hash['directing_job']
96
+ if http_callback_url
97
+ raise ArgumentError, "You must specify a http_callback_format (either 'xml' or 'json')" if http_callback_format.nil?
98
+ job['http_callback'] = http_callback_url
99
+ job['http_callback_format'] = http_callback_format
100
+ end
101
+ manifest = job['directing_manifest']
102
+ manifest['style'] = style
103
+ manifest['pacing'] = pacing if pacing
104
+ manifest['title'] = title if title
105
+ manifest['producer_name'] = producer if producer
106
+ manifest['visuals'] = []
107
+ visuals.each do |visual|
108
+ manifest['visuals'] << visual.to_hash
109
+ end
110
+ manifest['song'] = song.to_hash if song
111
+ hash
112
+ end
113
+
114
+ end
115
+ end
@@ -0,0 +1,3 @@
1
+ module Animoto
2
+ class Error < StandardError; end
3
+ end
@@ -0,0 +1,15 @@
1
+ module Animoto
2
+ class Footage < Animoto::Asset
3
+ include Animoto::Visual
4
+
5
+ attr_accessor :audio_mix, :start_time, :duration
6
+
7
+ def to_hash
8
+ hash = super
9
+ hash['audio_mix'] = 'MIX' if audio_mix
10
+ hash['start_time'] = start_time if start_time
11
+ hash['duration'] = duration if duration
12
+ hash
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ module Animoto
2
+ class Image < Animoto::Asset
3
+ include Animoto::Visual
4
+
5
+ attr_accessor :rotation
6
+
7
+ def to_hash
8
+ hash = super
9
+ hash['rotation'] = rotation if rotation
10
+ hash['spotlit'] = spotlit? unless @spotlit.nil?
11
+ hash
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,37 @@
1
+ module Animoto
2
+ class Job < Animoto::Resource
3
+
4
+ def self.unpack_standard_envelope body
5
+ super.merge(:state => body['response']['payload'][payload_key]['state'])
6
+ end
7
+
8
+ attr_reader :url, :state, :errors
9
+
10
+ # Returns true if the state of this job is 'failed'.
11
+ #
12
+ # @return [Boolean] whether or not the job has failed.
13
+ def failed?
14
+ @state == 'failed'
15
+ end
16
+
17
+ # Returns true if the state of this job is 'completed'.
18
+ #
19
+ # @return [Boolean] whether or not the job is completed
20
+ def completed?
21
+ @state == 'completed'
22
+ end
23
+
24
+ # Returns true if the job is neither failed or completed.
25
+ #
26
+ # @return [Boolean] whether or not the job is still pending
27
+ def pending?
28
+ !failed? && !completed?
29
+ end
30
+
31
+ def instantiate attributes = {}
32
+ @state = attributes[:state]
33
+ super
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ module Animoto
2
+ class Manifest
3
+ include ContentType
4
+
5
+ # Returns a representation of this manifest as a Hash, used to populate
6
+ # request bodies when directing, rendering, etc.
7
+ #
8
+ # @return [Hash] the manifest as a Hash
9
+ def to_hash
10
+ {}
11
+ end
12
+
13
+ # Returns a representation of this manifest as JSON.
14
+ #
15
+ # @return [String] the manifest as JSON
16
+ def to_json
17
+ self.to_hash.to_json
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ module Animoto
2
+ class RenderingJob < Animoto::Job
3
+
4
+ endpoint '/jobs/rendering'
5
+
6
+ def self.unpack_standard_envelope body
7
+ super.merge({
8
+ :storyboard_url => body['response']['payload'][payload_key]['links']['storyboard'],
9
+ :video_url => body['response']['payload'][payload_key]['links']['video']
10
+ })
11
+ end
12
+
13
+ attr_reader :storyboard, :storyboard_url, :video, :video_url
14
+
15
+ def instantiate attributes = {}
16
+ @storyboard_url = attributes[:storyboard_url]
17
+ @storyboard = Animoto::Storyboard.new(:url => @storyboard_url) if @storyboard_url
18
+ @video_url = attributes[:video_url]
19
+ @video = Animoto::Video.new(:url => @video_url) if @video_url
20
+ super
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,37 @@
1
+ module Animoto
2
+ class RenderingManifest < Animoto::Manifest
3
+
4
+ attr_accessor :storyboard, :resolution, :framerate, :format,
5
+ :http_callback_url, :http_callback_format
6
+
7
+ def initialize storyboard, options = {}
8
+ @storyboard = storyboard
9
+ @resolution = options[:resolution]
10
+ @framerate = options[:framerate]
11
+ @format = options[:format]
12
+ @http_callback_url = options[:http_callback_url]
13
+ @http_callback_format = options[:http_callback_format]
14
+ end
15
+
16
+ # Returns a representation of this manifest as a Hash.
17
+ #
18
+ # @return [Hash] this manifest as a Hash
19
+ # @raise [ArgumentError] if a callback URL was specified but not the format
20
+ def to_hash
21
+ hash = { 'rendering_job' => { 'rendering_manifest' => { 'rendering_profile' => {} } } }
22
+ job = hash['rendering_job']
23
+ if http_callback_url
24
+ raise ArgumentError, "You must specify a http_callback_format (either 'xml' or 'json')" if http_callback_format.nil?
25
+ job['http_callback'] = http_callback_url
26
+ job['http_callback_format'] = http_callback_format
27
+ end
28
+ manifest = job['rendering_manifest']
29
+ manifest['storyboard_url'] = storyboard.url
30
+ profile = manifest['rendering_profile']
31
+ profile['vertical_resolution'] = resolution
32
+ profile['framerate'] = framerate
33
+ profile['format'] = format
34
+ hash
35
+ end
36
+ end
37
+ end