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.
- data/README.md +39 -0
- data/lib/animoto/asset.rb +25 -0
- data/lib/animoto/client.rb +226 -0
- data/lib/animoto/content_type.rb +47 -0
- data/lib/animoto/directing_and_rendering_job.rb +19 -0
- data/lib/animoto/directing_and_rendering_manifest.rb +25 -0
- data/lib/animoto/directing_job.rb +18 -0
- data/lib/animoto/directing_manifest.rb +115 -0
- data/lib/animoto/errors.rb +3 -0
- data/lib/animoto/footage.rb +15 -0
- data/lib/animoto/image.rb +14 -0
- data/lib/animoto/job.rb +37 -0
- data/lib/animoto/manifest.rb +21 -0
- data/lib/animoto/rendering_job.rb +24 -0
- data/lib/animoto/rendering_manifest.rb +37 -0
- data/lib/animoto/resource.rb +153 -0
- data/lib/animoto/song.rb +16 -0
- data/lib/animoto/standard_envelope.rb +27 -0
- data/lib/animoto/storyboard.rb +22 -0
- data/lib/animoto/title_card.rb +26 -0
- data/lib/animoto/video.rb +29 -0
- data/lib/animoto/visual.rb +30 -0
- data/lib/animoto.rb +5 -0
- data/spec/animoto/asset_spec.rb +1 -0
- data/spec/animoto/client_spec.rb +119 -0
- data/spec/animoto/directing_and_rendering_job_spec.rb +45 -0
- data/spec/animoto/directing_and_rendering_manifest_spec.rb +143 -0
- data/spec/animoto/directing_job_spec.rb +48 -0
- data/spec/animoto/directing_manifest_spec.rb +186 -0
- data/spec/animoto/footage_spec.rb +56 -0
- data/spec/animoto/image_spec.rb +41 -0
- data/spec/animoto/job_spec.rb +128 -0
- data/spec/animoto/rendering_job_spec.rb +57 -0
- data/spec/animoto/rendering_manifest_spec.rb +115 -0
- data/spec/animoto/resource_spec.rb +55 -0
- data/spec/animoto/song_spec.rb +54 -0
- data/spec/animoto/standard_envelope_spec.rb +0 -0
- data/spec/animoto/storyboard_spec.rb +8 -0
- data/spec/animoto/title_card_spec.rb +42 -0
- data/spec/animoto/video_spec.rb +1 -0
- data/spec/animoto/visual_spec.rb +0 -0
- data/spec/animoto_spec.rb +5 -0
- data/spec/spec_helper.rb +10 -0
- 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,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
|
data/lib/animoto/job.rb
ADDED
@@ -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
|