encoding-dot-com 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.
@@ -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