encoding-dot-com 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,127 @@
1
+ require 'nokogiri'
2
+
3
+ module EncodingDotCom
4
+ # A remote facade to the encoding.com queue.
5
+ #
6
+ # The queue is stateless and can be reused for multiple requests.
7
+ class Queue
8
+ # Where encoding.com expects messages to be posted to.
9
+ ENDPOINT = "http://manage.encoding.com/"
10
+
11
+ # Creates a new facacde given an encoding.com user id & key, and
12
+ # an HTTP library implementation.
13
+ #
14
+ # +user_id+:: your encoding.com user id
15
+ # +user_key+:: your encoding.com secret key
16
+ # +http+:: should respond to post, and return an object responding to
17
+ # +#code+ and +#to_s+
18
+ def initialize(user_id, user_key, http=HttpAdapters::CurbAdapter.new)
19
+ @user_id, @user_key, @http = user_id, user_key, http
20
+ end
21
+
22
+ # Add a video/image to the encoding.com queue to be encoded in
23
+ # various formats. Item will be processed after being added.
24
+ #
25
+ # +source+:: the source url
26
+ # +formats+:: a hash of destination urls => format objects
27
+ def add_and_process(source, formats={})
28
+ add_request("AddMedia", source, formats)
29
+ end
30
+
31
+ # Add a video/image to the encoding.com queue to be encoded in
32
+ # various formats.
33
+ #
34
+ # +source+:: the source url
35
+ # +formats+:: a hash of destination urls => format objects
36
+ def add(source, formats={})
37
+ add_request("AddMediaBenchmark", source, formats)
38
+ end
39
+
40
+ # Returns the status string of a particular media item in the
41
+ # encoding.com queue. The status will be for the job as a whole,
42
+ # rather than individual ouput formats
43
+ def status(media_id)
44
+ response = make_request("GetStatus") {|q| q.mediaid media_id }
45
+ response.xpath("/response/status").text
46
+ end
47
+
48
+ # Returns the full status of an entry in the encoding.com queue,
49
+ # including details about the status of individual formats
50
+ def full_status(media_id) #:nodoc:
51
+ response = make_request("GetStatus") do |q|
52
+ q.mediaid media_id
53
+ end
54
+ end
55
+
56
+ # Returns a list of media in the encoding.com queue
57
+ def list
58
+ make_request("GetMediaList").xpath("/response/media").map {|node| MediaListItem.new(node) }
59
+ end
60
+
61
+ # Cancels a media item in the encoding.com queue
62
+ def cancel(media_id)
63
+ make_request("CancelMedia") {|q| q.mediaid media_id }
64
+ end
65
+
66
+ # Process an item already in the encoding.com queue
67
+ def process(media_id)
68
+ make_request("ProcessMedia") {|q| q.mediaid media_id }
69
+ end
70
+
71
+ # Replaces all the formats in an item on the encoding.com queue
72
+ # with the formats provided. This also kicks off the encoding
73
+ # process - there is no need to call process after this.
74
+ #
75
+ # +media_id+:: id of the item in the encoding.com queue
76
+ # +formats+:: a hash of destination urls => Format objects
77
+ def update(media_id, formats={})
78
+ make_request("UpdateMedia") do |q|
79
+ q.mediaid media_id
80
+ formats.each {|url, format| format.build_xml(q, url) }
81
+ end
82
+ end
83
+
84
+ # Returns a MediaInfo object with some attributes of the video
85
+ # identified by media_id.
86
+ def info(media_id)
87
+ response = make_request("GetMediaInfo") {|q| q.mediaid media_id }
88
+ MediaInfo.new(response)
89
+ end
90
+
91
+ private
92
+
93
+ def add_request(action, source, formats)
94
+ response = make_request(action) do |q|
95
+ q.source source
96
+ formats.each {|url, format| format.build_xml(q, url) }
97
+ end
98
+ media_id = response.xpath("/response/MediaID").text
99
+ media_id.to_i if media_id
100
+ end
101
+
102
+ def make_request(action_name, &block)
103
+ query = build_query(action_name, &block)
104
+ response = @http.post(ENDPOINT, :xml => query)
105
+ raise AvailabilityError.new unless response.code == "200"
106
+ xml = Nokogiri::XML(response.body)
107
+ check_for_response_errors(xml)
108
+ xml
109
+ end
110
+
111
+ def build_query(action)
112
+ query = Nokogiri::XML::Builder.new do |q|
113
+ q.query {
114
+ q.userid @user_id
115
+ q.userkey @user_key
116
+ q.action action
117
+ yield q if block_given?
118
+ }
119
+ end.to_xml
120
+ end
121
+
122
+ def check_for_response_errors(xml)
123
+ errors = xml.xpath("/response/errors/error").map {|e| e.text }
124
+ raise MessageError.new(errors.join(", ")) unless errors.empty?
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,40 @@
1
+ module EncodingDotCom
2
+ # A Thumbnail output format
3
+ class ThumbnailFormat < Format #:nodoc:
4
+ allowed_attributes :output, :time, :width, :height
5
+
6
+ # Creates a new ThumbnailFormat. You should be calling
7
+ # Format.create(attributes) rather than this constructor directly.
8
+ def initialize(attributes={})
9
+ @attributes = attributes.merge("output" => "thumbnail")
10
+ validate_attributes
11
+ end
12
+
13
+ def validate_attributes
14
+ validate_time
15
+ validate_height
16
+ validate_width
17
+ end
18
+
19
+ private
20
+
21
+ def validate_time
22
+ unless time.nil? || time.to_f > 0.01 || time.to_s =~ /\d\d:[0-5]\d:[0-5]\d(\.\d+)?/
23
+ raise IllegalFormatAttribute.new("Time must be a number greater than 0.01 or HH:MM:SS.ms, was #{time}")
24
+ end
25
+ end
26
+
27
+ def validate_height
28
+ unless height.nil? || height.to_i > 0
29
+ raise IllegalFormatAttribute.new("Height must be a positive integer, was #{height}")
30
+ end
31
+ end
32
+
33
+ def validate_width
34
+ unless width.nil? || width.to_i > 0
35
+ raise IllegalFormatAttribute.new("Width must be a positive integer, was #{width}")
36
+ end
37
+ end
38
+ end
39
+ end
40
+
@@ -0,0 +1,40 @@
1
+ module EncodingDotCom
2
+ class VideoFormat < Format #:nodoc:
3
+ ALLOWED_OUTPUT_FORMATS = %w{flv fl9 wmv 3gp mp4 m4v ipod iphone appletv psp zune mp3 wma}.freeze
4
+
5
+ allowed_attributes :output, :size, :bitrate, :framerate, :video_codec, :audio_bitrate, :audio_sample_rate, :audio_codec, :audio_channels_number, :audio_volume, :maxrate, :minrate, :bufsize, :keyframe, :start, :duration, :rc_init_occupancy, :crop_top, :crop_left, :crop_right, :crop_bottom, :logo_source, :logo_x, :logo_y, :logo_mode, :logo_threshold
6
+ boolean_attributes :two_pass, :cbr, :deinterlacing, :add_meta, :turbo
7
+
8
+ def initialize(attributes={})
9
+ @attributes = attributes
10
+ check_valid_output_format
11
+ mixin_output_attribute_restrictions
12
+ validate_attributes
13
+ end
14
+
15
+ private
16
+
17
+ def check_valid_output_format
18
+ unless ALLOWED_OUTPUT_FORMATS.include?(@attributes["output"])
19
+ raise IllegalFormatAttribute.new("Output format #{@attributes["output"]} is not allowed.")
20
+ end
21
+ end
22
+
23
+ def mixin_output_attribute_restrictions
24
+ module_name = "AttributeRestrictions#{@attributes["output"].capitalize}"
25
+ restrictions = EncodingDotCom.const_get(module_name)
26
+ (class << self; self; end).send(:include, restrictions)
27
+ end
28
+
29
+ def validate_attributes
30
+ validate_video_codec
31
+ validate_size
32
+ end
33
+
34
+ def validate_size
35
+ end
36
+
37
+ def validate_video_codec
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,2 @@
1
+ require 'encoding-dot-com'
2
+
@@ -0,0 +1,35 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe "CurbAdapter" do
4
+ before :each do
5
+ @http = EncodingDotCom::HttpAdapters::CurbAdapter.new
6
+ @mock_easy = mock("Curl::Easy mock", :null_object => true)
7
+ @mock_easy.should_receive(:follow_location=).with(true)
8
+ Curl::Easy.should_receive(:new).with("http://example.com").and_yield(@mock_easy).and_return(@mock_easy)
9
+ end
10
+
11
+ it "should respond to post with a url and hash of post parameters" do
12
+ @mock_easy.should_receive(:http_post)
13
+ Curl::PostField.should_receive(:content).with("foo", "bar")
14
+
15
+ @http.post("http://example.com", {:foo => "bar"})
16
+ end
17
+
18
+ it "should raise an AvailabilityError if a Curl exception is raised" do
19
+ @mock_easy.should_receive(:http_post).and_raise(StandardError.new("Boom!"))
20
+
21
+ lambda { @http.post("http://example.com") }.should raise_error(EncodingDotCom::AvailabilityError, "Boom!")
22
+ end
23
+
24
+ it "should return a response with a status code" do
25
+ @mock_easy.should_receive(:http_post)
26
+ @mock_easy.should_receive(:response_code).and_return(200)
27
+ @http.post("http://example.com").code.should == "200"
28
+ end
29
+
30
+ it "should return a response with a body" do
31
+ @mock_easy.should_receive(:http_post)
32
+ @mock_easy.should_receive(:body_str).and_return("Hello!")
33
+ @http.post("http://example.com").body.should == "Hello!"
34
+ end
35
+ end
@@ -0,0 +1,37 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
2
+
3
+ describe "NetHttpAdapter" do
4
+ before :each do
5
+ @http = EncodingDotCom::HttpAdapters::NetHttpAdapter.new
6
+
7
+ end
8
+
9
+ it "should respond to post with a url and hash of post parameters" do
10
+ Net::HTTP.should_receive(:post_form).with(URI.parse("http://example.com"), {:foo => "bar"})
11
+ @http.post("http://example.com", {:foo => "bar"})
12
+ end
13
+
14
+ it "should raise an AvailabilityError if an exception is raised" do
15
+ Net::HTTP.should_receive(:post_form).and_raise(Net::HTTPBadResponse.new("bad!"))
16
+ lambda { @http.post("http://example.com") }.should raise_error(EncodingDotCom::AvailabilityError, "bad!")
17
+ end
18
+
19
+ it "should raise an AvailabilityError if a timeout error is raised" do
20
+ Net::HTTP.should_receive(:post_form).and_raise(Timeout::Error.new("bad!"))
21
+ lambda { @http.post("http://example.com") }.should raise_error(EncodingDotCom::AvailabilityError, "bad!")
22
+ end
23
+
24
+ it "should return a response with a status code" do
25
+ response = stub(:code => "200", :body => "Hello!")
26
+ Net::HTTP.should_receive(:post_form).and_return(response)
27
+
28
+ @http.post("http://example.com").code.should == "200"
29
+ end
30
+
31
+ it "should return a response with a body" do
32
+ response = stub(:code => "200", :body => "Hello!")
33
+ Net::HTTP.should_receive(:post_form).and_return(response)
34
+
35
+ @http.post("http://example.com").body.should == "Hello!"
36
+ end
37
+ end
@@ -0,0 +1,44 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Encoding.com FLV VP6 Format" do
4
+
5
+ it "should have an output attribute of 'thumbnail'" do
6
+ EncodingDotCom::FLVVP6Format.new.output.should == "flv"
7
+ end
8
+
9
+ it "should return a ThumbnailFormat if the output is 'thumbnail'" do
10
+ EncodingDotCom::Format.create("output" => "flv", "video_codec" => "vp6").should be_instance_of(EncodingDotCom::FLVVP6Format)
11
+ end
12
+
13
+ def format_xml(attributes={})
14
+ format = EncodingDotCom::FLVVP6Format.new(attributes)
15
+ Nokogiri::XML::Builder.new {|b| format.build_xml(b, "http://example.com") }.to_xml
16
+ end
17
+
18
+ it "should produce a format node in the xml output" do
19
+ format_xml.should have_xpath("/format")
20
+ end
21
+
22
+ it "should produce an output node in the xml output" do
23
+ format_xml.should have_xpath("/format/output[text()='flv']")
24
+ end
25
+
26
+ it "should produce a video_codec node in the xml output" do
27
+ format_xml.should have_xpath("/format/video_codec[text()='vp6']")
28
+ end
29
+
30
+ it "should produce a size node in the xml output" do
31
+ format_xml("size" => "16x16").should have_xpath("/format/size[text()='16x16']")
32
+ end
33
+
34
+ it "should produce a destination node in the output" do
35
+ format_xml.should have_xpath("/format/destination[text()='http://example.com']")
36
+ end
37
+
38
+ describe "valid sizes" do
39
+ it "should have widths and heights in multiples of 16" do
40
+ lambda { EncodingDotCom::FLVVP6Format.new("size" => "32x32") }.should_not raise_error
41
+ lambda { EncodingDotCom::FLVVP6Format.new("size" => "33x33") }.should raise_error(EncodingDotCom::IllegalFormatAttribute)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,167 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Encoding.com video format" do
4
+
5
+ describe "#create" do
6
+ it "should return a video format" do
7
+ EncodingDotCom::Format.create("output" => "mp4").should be_instance_of(EncodingDotCom::VideoFormat)
8
+ end
9
+ end
10
+
11
+ describe "#output" do
12
+ it "should have a output attribute" do
13
+ EncodingDotCom::VideoFormat.new("output" => "flv").output.should == "flv"
14
+ end
15
+
16
+ it "should be required" do
17
+ lambda { EncodingDotCom::VideoFormat.new }.should raise_error(EncodingDotCom::IllegalFormatAttribute)
18
+ end
19
+
20
+ it "should not allow an illegal format" do
21
+ lambda { EncodingDotCom::VideoFormat.new("output" => "foo") }.should raise_error(EncodingDotCom::IllegalFormatAttribute)
22
+ end
23
+
24
+ %w{flv fl9 wmv 3gp mp4 m4v ipod iphone appletv psp zune mp3 wma}.each do |format|
25
+ it "should allow #{format} as an output format" do
26
+ lambda { EncodingDotCom::VideoFormat.new("output" => format) }.should_not raise_error
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "specifying the size of the output video" do
32
+ it "should have a size attribute" do
33
+ EncodingDotCom::VideoFormat.new("output" => "flv", "size" => "640x480").size.should == "640x480"
34
+ end
35
+
36
+ it "should be nil if neither width nor height are specified" do
37
+ EncodingDotCom::VideoFormat.new("output" => "flv").size.should be_nil
38
+ end
39
+
40
+ describe "size restrictions" do
41
+ it "should have a sizes of 320x120 or 320x180 if the output format is zune" do
42
+ lambda { EncodingDotCom::VideoFormat.new("output" => "zune", "size" => "400x400") }.should raise_error(EncodingDotCom::IllegalFormatAttribute)
43
+ lambda { EncodingDotCom::VideoFormat.new("output" => "zune", "size" => "320x120") }.should_not raise_error
44
+ lambda { EncodingDotCom::VideoFormat.new("output" => "zune", "size" => "320x180") }.should_not raise_error
45
+ end
46
+
47
+ it "should have sizes 320x240 or 640x480 if the output format is ipod" do
48
+ lambda { EncodingDotCom::VideoFormat.new("output" => "ipod", "size" => "400x400") }.should raise_error(EncodingDotCom::IllegalFormatAttribute)
49
+ end
50
+ end
51
+ end
52
+
53
+ describe "allowed video codecs for output formats" do
54
+ %w{flv libx264 vp6}.each do |codec|
55
+ it "can be #{codec} if the output format is FLV" do
56
+ lambda { EncodingDotCom::VideoFormat.new("output" => "flv", "video_codec" => codec) }.should_not raise_error
57
+ end
58
+ end
59
+
60
+ it "cannot be any other codec if output if Flv" do
61
+ lambda { EncodingDotCom::VideoFormat.new("output" => "flv", "video_codec" => "foo") }.should raise_error(EncodingDotCom::IllegalFormatAttribute)
62
+ end
63
+
64
+ %w{mpeg4 libx264}.each do |codec|
65
+ it "can be #{codec} if the output format is MP4" do
66
+ lambda { EncodingDotCom::VideoFormat.new("output" => "mp4", "video_codec" => codec) }.should_not raise_error
67
+ end
68
+ end
69
+
70
+ it "cannot be any other codec if output if MP4" do
71
+ lambda { EncodingDotCom::VideoFormat.new("output" => "mp4", "video_codec" => "foo") }.should raise_error(EncodingDotCom::IllegalFormatAttribute)
72
+ end
73
+ end
74
+
75
+ describe "#build_xml" do
76
+ it "should create a format node" do
77
+ format = EncodingDotCom::VideoFormat.new("output" => "flv")
78
+ Nokogiri::XML::Builder.new do |b|
79
+ format.build_xml(b)
80
+ end.to_xml.should have_xpath("/format")
81
+ end
82
+
83
+ it "should create an output node" do
84
+ format = EncodingDotCom::VideoFormat.new("output" => "flv")
85
+ Nokogiri::XML::Builder.new do |b|
86
+ format.build_xml(b)
87
+ end.to_xml.should have_xpath("/format/output[text()='flv']")
88
+ end
89
+
90
+ it "should not create a size node if size is not specified" do
91
+ format = EncodingDotCom::VideoFormat.new("output" => "flv")
92
+ Nokogiri::XML::Builder.new do |b|
93
+ format.build_xml(b)
94
+ end.to_xml.should_not have_xpath("/format/size")
95
+ end
96
+
97
+ it "should create a size node" do
98
+ format = EncodingDotCom::VideoFormat.new("output" => "flv", "size" => "0x480")
99
+ Nokogiri::XML::Builder.new do |b|
100
+ format.build_xml(b)
101
+ end.to_xml.should have_xpath("/format/size[text()='0x480']")
102
+ end
103
+
104
+ it "should create a logo node for logo attributes" do
105
+ format = EncodingDotCom::VideoFormat.new("output" => "flv", "logo_x" => 30)
106
+ Nokogiri::XML::Builder.new do |b|
107
+ format.build_xml(b)
108
+ end.to_xml.should have_xpath("/format/logo/logo_x[text()='30']")
109
+ end
110
+
111
+ it "should have a destination node with the url passed to build xml" do
112
+ format = EncodingDotCom::VideoFormat.new("output" => "flv")
113
+ Nokogiri::XML::Builder.new do |b|
114
+ format.build_xml(b, "http://example.com")
115
+ end.to_xml.should have_xpath("/format/destination[text()='http://example.com']")
116
+ end
117
+
118
+ %w{two_pass cbr deinterlacing add_meta turbo}.each do |bool|
119
+ it "should output boolean attribute #{bool} as yes when true" do
120
+ format = EncodingDotCom::VideoFormat.new("output" => "flv", bool => true)
121
+ Nokogiri::XML::Builder.new do |b|
122
+ format.build_xml(b, "http://example.com")
123
+ end.to_xml.should have_xpath("/format/#{bool}[text()='yes']")
124
+ end
125
+
126
+ it "should output boolean attribute #{bool} as no when false" do
127
+ format = EncodingDotCom::VideoFormat.new("output" => "flv", bool => false)
128
+ Nokogiri::XML::Builder.new do |b|
129
+ format.build_xml(b, "http://example.com")
130
+ end.to_xml.should have_xpath("/format/#{bool}[text()='no']")
131
+ end
132
+ end
133
+ end
134
+
135
+ describe "setting allowed attributes on a format" do
136
+ it "should have an attribute :foo" do
137
+ class ExampleFormat1 < EncodingDotCom::Format
138
+ allowed_attributes :foo
139
+ end
140
+ ExampleFormat1.allowed_attributes.should == ["foo"]
141
+ end
142
+
143
+ it "should be able to set allowed attributes cumulatively" do
144
+ class ExampleFormat2 < EncodingDotCom::Format
145
+ allowed_attributes :foo
146
+ allowed_attributes :bar
147
+ end
148
+ ExampleFormat2.allowed_attributes.should == ["foo", "bar"]
149
+ end
150
+ end
151
+
152
+ describe "setting boolean attributes on a format" do
153
+ it "should also create an allowed attribute" do
154
+ class ExampleFormat3 < EncodingDotCom::Format
155
+ boolean_attributes :foo
156
+ end
157
+ ExampleFormat3.allowed_attributes.should == ["foo"]
158
+ end
159
+
160
+ it "should create a boolean attribute" do
161
+ class ExampleFormat4 < EncodingDotCom::Format
162
+ boolean_attributes :foo
163
+ end
164
+ ExampleFormat4.boolean_attributes.should == ["foo"]
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,18 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "MediaListItem" do
4
+ it "should have a nil date for dates returned from encoding.com like 0000-00-00 00:00:00" do
5
+ node = Nokogiri::XML(<<-END
6
+ <media>
7
+ <mediafile>foo.wmv</mediafile>
8
+ <mediaid>1234</mediaid>
9
+ <mediastatus>Closed</mediastatus>
10
+ <createdate>2009-01-01 12:00:01</createdate>
11
+ <startdate>2009-01-01 12:00:02</startdate>
12
+ <finishdate>0000-00-00 00:00:00</finishdate>
13
+ </media>
14
+ END
15
+ )
16
+ EncodingDotCom::MediaListItem.new(node).finish_date.should be_nil
17
+ end
18
+ end