telestream_cloud 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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +2 -0
  5. data/LICENSE +20 -0
  6. data/README.md +357 -0
  7. data/Rakefile +9 -0
  8. data/lib/telestream_cloud.rb +30 -0
  9. data/lib/telestream_cloud/api_authentication.rb +66 -0
  10. data/lib/telestream_cloud/base.rb +111 -0
  11. data/lib/telestream_cloud/config.rb +87 -0
  12. data/lib/telestream_cloud/connection.rb +101 -0
  13. data/lib/telestream_cloud/errors.rb +17 -0
  14. data/lib/telestream_cloud/faraday.rb +84 -0
  15. data/lib/telestream_cloud/flip.rb +9 -0
  16. data/lib/telestream_cloud/modules/associations.rb +55 -0
  17. data/lib/telestream_cloud/modules/builders.rb +45 -0
  18. data/lib/telestream_cloud/modules/destroyers.rb +25 -0
  19. data/lib/telestream_cloud/modules/factory_connection.rb +7 -0
  20. data/lib/telestream_cloud/modules/finders.rb +68 -0
  21. data/lib/telestream_cloud/modules/router.rb +62 -0
  22. data/lib/telestream_cloud/modules/updatable.rb +31 -0
  23. data/lib/telestream_cloud/modules/video_state.rb +16 -0
  24. data/lib/telestream_cloud/proxies/encoding_scope.rb +56 -0
  25. data/lib/telestream_cloud/proxies/profile_scope.rb +7 -0
  26. data/lib/telestream_cloud/proxies/proxy.rb +27 -0
  27. data/lib/telestream_cloud/proxies/scope.rb +94 -0
  28. data/lib/telestream_cloud/proxies/video_scope.rb +28 -0
  29. data/lib/telestream_cloud/resources/encoding.rb +47 -0
  30. data/lib/telestream_cloud/resources/factory.rb +72 -0
  31. data/lib/telestream_cloud/resources/profile.rb +22 -0
  32. data/lib/telestream_cloud/resources/resource.rb +48 -0
  33. data/lib/telestream_cloud/resources/video.rb +39 -0
  34. data/lib/telestream_cloud/telestream_cloud.rb +69 -0
  35. data/lib/telestream_cloud/upload_session.rb +102 -0
  36. data/lib/telestream_cloud/version.rb +3 -0
  37. data/spec/cloud_spec.rb +132 -0
  38. data/spec/encoding_spec.rb +260 -0
  39. data/spec/heroku_spec.rb +32 -0
  40. data/spec/panda_spec.rb +206 -0
  41. data/spec/profile_spec.rb +117 -0
  42. data/spec/spec_helper.rb +18 -0
  43. data/spec/video_spec.rb +399 -0
  44. data/telestream_cloud.gemspec +30 -0
  45. metadata +191 -0
@@ -0,0 +1,28 @@
1
+ module TelestreamCloud
2
+ class VideoScope < Scope
3
+
4
+ def initialize(parent)
5
+ super(parent, Video)
6
+ end
7
+
8
+ def non_delegate_methods
9
+ super + [:status, :page, :per_page]
10
+ end
11
+
12
+ def page(this_page)
13
+ @scoped_attributes[:page] = this_page
14
+ self
15
+ end
16
+
17
+ def per_page(this_per_page)
18
+ @scoped_attributes[:per_page] = this_per_page
19
+ self
20
+ end
21
+
22
+ def status(this_status)
23
+ @scoped_attributes[:status] = this_status
24
+ self
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,47 @@
1
+ module TelestreamCloud
2
+ class Encoding < Resource
3
+ include VideoState
4
+
5
+ belongs_to :video
6
+ has_one :profile
7
+
8
+ class << self
9
+ def first
10
+ EncodingScope.new(self).per_page(1).first
11
+ end
12
+ end
13
+
14
+ def url(options={})
15
+ default_options = {:https => false}
16
+ options = default_options.merge(options)
17
+ full_path = "#{path}#{extname}"
18
+ get_url(full_path, options[:https]) if files.include?(full_path)
19
+ end
20
+
21
+ def urls(options={})
22
+ default_options = {:https => false}
23
+ options = default_options.merge(options)
24
+ files.map {|f| get_url(f, options[:https])}
25
+ end
26
+
27
+ def screenshots(options={})
28
+ default_options = {:https => false}
29
+ options = default_options.merge(options)
30
+ ((1..7).map{|i| get_url("#{path}_#{i}.jpg", options[:https])} if success?) || []
31
+ end
32
+
33
+ def cancel
34
+ connection.post("/encodings/#{id}/cancel.json")['canceled']
35
+ end
36
+
37
+ def retry
38
+ connection.post("/encodings/#{id}/retry.json")['retried']
39
+ end
40
+
41
+ private
42
+
43
+ def get_url(end_path, https)
44
+ "#{https ? factory.https_url : factory.url}#{end_path}"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,72 @@
1
+ module TelestreamCloud
2
+ class Factory < Base
3
+ include TelestreamCloud::Updatable
4
+
5
+ attr_reader :connection
6
+
7
+ def initialize(attributes={})
8
+ super(attributes)
9
+ connection_params = TelestreamCloud.connection.to_hash.merge!(:factory_id => id)
10
+ @connection = Connection.new(connection_params)
11
+ TelestreamCloud.factories[id] = self
12
+ end
13
+
14
+ class << self
15
+
16
+ def connection
17
+ TelestreamCloud.connection
18
+ end
19
+
20
+ private
21
+
22
+ def build_resource(attributes)
23
+ resource = TelestreamCloud::Factory.new(attributes)
24
+ end
25
+ end
26
+
27
+ def https_url
28
+ uri = URI.parse(url)
29
+ uri.scheme = "https"
30
+ uri.to_s
31
+ end
32
+
33
+ def eu?
34
+ region == "eu"
35
+ end
36
+
37
+ def us?
38
+ region == "us"
39
+ end
40
+
41
+ def region
42
+ return 'eu' if connection.api_host == TelestreamCloud::EU_API_HOST
43
+ return 'us' if connection.api_host == TelestreamCloud::US_API_HOST
44
+ return 'gce' if connection.api_host == TelestreamCloud::GCE_API_HOST
45
+ end
46
+
47
+ def videos
48
+ VideoScope.new(self)
49
+ end
50
+
51
+ def encodings
52
+ EncodingScope.new(self)
53
+ end
54
+
55
+ def profiles
56
+ ProfileScope.new(self)
57
+ end
58
+
59
+ def method_missing(method_symbol, *arguments)
60
+ lazy_load
61
+ super
62
+ end
63
+
64
+ private
65
+
66
+ def lazy_load
67
+ reload unless @loaded
68
+ end
69
+
70
+ end
71
+ end
72
+
@@ -0,0 +1,22 @@
1
+ module TelestreamCloud
2
+ class Profile < Resource
3
+ include TelestreamCloud::Updatable
4
+
5
+ has_many :encodings
6
+
7
+ def preset?
8
+ !preset_name
9
+ end
10
+
11
+ # override attributes command and preset_name
12
+ # to ovoid <method undefined> when profile is a preset or not
13
+ def command
14
+ @attributes['command']
15
+ end
16
+
17
+ def preset_name
18
+ @attributes['preset_name']
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,48 @@
1
+ module TelestreamCloud
2
+ class Resource < Base
3
+ include TelestreamCloud::Destroyers
4
+ include TelestreamCloud::Associations
5
+ include TelestreamCloud::FactoryConnection
6
+
7
+ def initialize(attributes={})
8
+ super(attributes)
9
+ @attributes['factory_id'] ||= TelestreamCloud.factory.id
10
+ end
11
+
12
+ class << self
13
+ include TelestreamCloud::FactoryConnection
14
+
15
+ def factory
16
+ TelestreamCloud.factory
17
+ end
18
+
19
+ # delegate to the scope if the method exists
20
+ def method_missing(method_symbol, *args, &block)
21
+ scope = TelestreamCloud::const_get("#{sti_name}Scope").new(self)
22
+ if scope.respond_to?(method_symbol)
23
+ scope.send(method_symbol, *args, &block)
24
+ else
25
+ super
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+ def factory
32
+ TelestreamCloud.factories[factory_id]
33
+ end
34
+
35
+ def reload
36
+ perform_reload("factory_id" => factory_id)
37
+ reset_associations
38
+ self
39
+ end
40
+
41
+ private
42
+
43
+ def load_and_reset(response)
44
+ load_response(response.merge('factory_id' => factory.id))
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,39 @@
1
+ module TelestreamCloud
2
+ class Video < Resource
3
+ include VideoState
4
+ has_many :encodings
5
+
6
+ class << self
7
+ def first
8
+ VideoScope.new(self).per_page(1).first
9
+ end
10
+ end
11
+
12
+ def metadata
13
+ connection.get("/videos/#{id}/metadata.json")
14
+ end
15
+
16
+ def delete_source
17
+ connection.delete("/videos/#{id}/source.json")
18
+ end
19
+
20
+ def preview_url(options={})
21
+ default_options = {:https => false}
22
+ options = default_options.merge(options)
23
+ get_url("#{path}_1.jpg", options[:https]) if success?
24
+ end
25
+
26
+ def url(options={})
27
+ default_options = {:https => false}
28
+ options = default_options.merge(options)
29
+ get_url("#{path}#{extname}", options[:https]) if success?
30
+ end
31
+
32
+ private
33
+
34
+ def get_url(end_path, https)
35
+ "#{https ? factory.https_url : factory.url}#{end_path}"
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,69 @@
1
+ module TelestreamCloud
2
+
3
+ extend self
4
+ extend Forwardable
5
+
6
+ attr_reader :factory, :factories
7
+ attr_reader :connection
8
+
9
+ def_delegators :connection, :get, :post, :put, :delete, :api_url, :setup_bucket, :signed_params
10
+
11
+ RESOURCES = {
12
+ flip: TelestreamCloud::Flip
13
+ }
14
+
15
+ def configure(auth_params=nil, &block)
16
+ raise ArgumentError, "missing auth params or block" unless auth_params || block_given?
17
+
18
+ if !auth_params
19
+ config = Config.new
20
+ if (block.arity > 0)
21
+ block.call(config)
22
+ else
23
+ config.instance_eval(&block)
24
+ end
25
+ elsif auth_params.is_a?(String)
26
+ config = Config.from_panda_url(auth_params)
27
+ else
28
+ config = Config.from_hash(auth_params)
29
+ end
30
+
31
+ configure_with_auth_params(config)
32
+ true
33
+ end
34
+
35
+ def configure_heroku
36
+ configure_with_auth_params Config.from_panda_url(ENV['PANDASTREAM_URL'])
37
+ true
38
+ end
39
+
40
+ def connect!(auth_params)
41
+ @connection = Connection.new(auth_params)
42
+ end
43
+
44
+ def connection
45
+ raise TelestreamCloud::ConfigurationError, "Telestream Cloud is not configured!" unless @connection
46
+ @connection
47
+ end
48
+
49
+ def default_adapter=(adapter_name)
50
+ @adapter = adapter_name.to_sym
51
+ end
52
+
53
+ def default_adapter
54
+ @adapter
55
+ end
56
+
57
+ def resources(resource_name)
58
+ RESOURCES[resource_name].new
59
+ end
60
+
61
+ private
62
+
63
+ def configure_with_auth_params(config)
64
+ config.validate!
65
+ connect!(config.to_hash)
66
+ @factories = {}
67
+ @factory = Factory::new(:id => @connection.factory_id)
68
+ end
69
+ end
@@ -0,0 +1,102 @@
1
+ CHUNK_SIZE = 100 * 1024 # 5 * 1024 * 1024
2
+
3
+ class File
4
+ def each_chunk_with_index()
5
+ i = 0
6
+ until eof?
7
+ yield(read(CHUNK_SIZE), i)
8
+ i = i+1
9
+ end
10
+ end
11
+ end
12
+
13
+ module TelestreamCloud
14
+ class UploadSession
15
+ attr_reader :location, :status, :video, :error_message, :file_name, :file_size
16
+
17
+ def initialize(file_name, options={})
18
+ @file_name = file_name
19
+ @file_size = File.size(file_name)
20
+ params = {
21
+ :file_size => @file_size,
22
+ :file_name => @file_name,
23
+ }.merge(options)
24
+
25
+ data = TelestreamCloud.post("/videos/upload.json", params)
26
+ @location = URI.parse(data["location"])
27
+ @status = "initialized"
28
+ @video = nil
29
+ @error_message = nil
30
+ end
31
+
32
+ def start(pos=0)
33
+ if @status == "initialized"
34
+ @status = "uploading"
35
+ open(@file_name, "rb") do |f|
36
+ f.seek(pos)
37
+ f.each_chunk_with_index() { |chunk, i|
38
+ begin
39
+ index = i * CHUNK_SIZE
40
+ uri = URI.parse(URI.encode(@location.to_s))
41
+ https = Net::HTTP.new(uri.host, uri.port)
42
+ https.use_ssl = true
43
+
44
+ request = Net::HTTP::Post.new(uri.request_uri, initheader = {
45
+ 'Content-Type' =>'application/octet-stream',
46
+ 'Cache-Control' => 'no-cache',
47
+ 'Content-Range' => "bytes #{pos+index}-#{pos+index+CHUNK_SIZE-1}/#{@file_size}",
48
+ 'Content-Length' => "#{CHUNK_SIZE}"
49
+ })
50
+ request.body = chunk
51
+
52
+ response = https.request(request)
53
+ if response.code == '200'
54
+ @status = "uploaded"
55
+ @video = TelestreamCloud::Video.new(JSON.parse(response.body))
56
+ elsif response.code != '204'
57
+ @statys = "error"
58
+ break
59
+ end
60
+ rescue Exception => e
61
+ @status = "error"
62
+ @error_mesage = e.message
63
+ raise e
64
+ end
65
+ }
66
+ end
67
+ else
68
+ raise "Already started"
69
+ end
70
+ end
71
+
72
+ def resume()
73
+ if @status != "uploaded"
74
+ uri = URI.parse(URI.encode(@location.to_s))
75
+ https = Net::HTTP.new(uri.host, uri.port)
76
+ https.use_ssl = true
77
+
78
+ request = Net::HTTP::Post.new(uri.request_uri, initheader = {
79
+ 'Content-Type' =>'application/octet-stream',
80
+ 'Cache-Control' => 'no-cache',
81
+ 'Content-Range' => "bytes */#{@file_size}",
82
+ 'Content-Length' => "0"
83
+ })
84
+ response = https.request(request)
85
+ pos = response["Range"].split("-")[1]
86
+ @status = "initialized"
87
+ self.start(pos.to_i)
88
+ else
89
+ raise ("Already succeed")
90
+ end
91
+ end
92
+
93
+ def abort()
94
+ if @status != "success"
95
+ @status = "aborted"
96
+ @error_message = nil
97
+ else
98
+ raise "Already succeed"
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,3 @@
1
+ module TelestreamCloud
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,132 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe TelestreamCloud::Factory do
4
+
5
+ describe "region" do
6
+ it "should tell the region" do
7
+ TelestreamCloud.configure do
8
+ access_key "my_access_key"
9
+ secret_key "my_secret_key"
10
+ api_host "api.pandastream.com"
11
+ factory_id 'my_cloud_id'
12
+ api_port 85
13
+ end
14
+
15
+ TelestreamCloud.factory.region.should == "us"
16
+ TelestreamCloud.factory.us?.should == true
17
+ end
18
+
19
+ it "should tell the region" do
20
+ TelestreamCloud.configure do
21
+ access_key "my_access_key"
22
+ secret_key "my_secret_key"
23
+ api_host "api-eu.pandastream.com"
24
+ factory_id 'my_cloud_id'
25
+ api_port 85
26
+ end
27
+
28
+ TelestreamCloud.factory.region.should == "eu"
29
+ TelestreamCloud.factory.eu?.should == true
30
+ end
31
+
32
+ end
33
+
34
+
35
+
36
+
37
+ describe "Using configure bloc" do
38
+ before(:each) do
39
+ TelestreamCloud.configure do
40
+ access_key "my_access_key"
41
+ secret_key "my_secret_key"
42
+ api_host "api.example.com"
43
+ factory_id 'my_cloud_id'
44
+ api_port 85
45
+ end
46
+ end
47
+
48
+ describe "using a cloud" do
49
+ before(:each) do
50
+ cloud_json = "{\"s3_videos_bucket\":\"my_bucket1\",\"id\":\"cloud1\"}"
51
+ stub_http_request(:get, /api.example.com:85\/v3.0\/factories\/cloud1.json/).
52
+ to_return(:body => cloud_json)
53
+ @factory = TelestreamCloud::Factory.find "cloud1"
54
+ end
55
+
56
+ it "should find all videos" do
57
+ videos_json = "[{\"source_url\":\"my_source_url\",\"id\":\"123\"}]"
58
+ stub_http_request(:get, /api.example.com:85\/v3.0\/videos.json/).
59
+ with{|r| r.uri.query =~ /factory_id=cloud1/}.
60
+ to_return(:body => videos_json)
61
+
62
+ @factory.videos.first.id.should == "123"
63
+ end
64
+
65
+ it "should find all videos with params" do
66
+ videos_json = "[{\"source_url\":\"my_source_url\",\"id\":\"134\"}]"
67
+ stub_http_request(:get, /api.example.com:85\/v3.0\/videos.json/).
68
+ with{|r| r.uri.query =~ /status=success/ && r.uri.query =~ /factory_id=cloud1/}.
69
+ to_return(:body => videos_json)
70
+ videos = @factory.videos.all(:status => "success")
71
+ videos.first.id.should == "134"
72
+ end
73
+
74
+ it "should find a video by id" do
75
+ video_json = "{\"source_url\":\"my_source_url\",\"id\":\"123\"}"
76
+ stub_http_request(:get, /api.example.com:85\/v3.0\/videos\/123.json/).
77
+ with{|r| r.uri.query =~ /factory_id=cloud1/}.
78
+ to_return(:body => video_json)
79
+
80
+ video = @factory.videos.find "123"
81
+ video.id.should == "123"
82
+ end
83
+
84
+ it "should create a video using cloud" do
85
+ videos_json = "{\"source_url\":\"my_source_url\",\"id\":\"123\"}"
86
+ stub_http_request(:post, /api.example.com:85\/v3.0\/videos.json/).
87
+ with{|r| r.body =~ /factory_id=cloud1/}.
88
+ to_return(:body => videos_json)
89
+ @factory.videos.create(:source_url => "my_source_url")
90
+ end
91
+
92
+ it "should find return all cloud" do
93
+ clouds_json = "[{\"s3_videos_bucket\":\"my_bucket1\",\"id\":\"cloud1\"},{\"s3_videos_bucket\":\"my_bucket2\",\"id\":\"cloud2\"}]"
94
+
95
+ stub_http_request(:get, /api.example.com:85\/v3.0\/factories.json/).to_return(:body => clouds_json)
96
+
97
+ factory = TelestreamCloud::Factory.all
98
+ factory.first.id.should == 'cloud1'
99
+ factory.first.s3_videos_bucket.should == "my_bucket1"
100
+ factory.size.should == 2
101
+ end
102
+ end
103
+
104
+
105
+ it "should create a cloud" do
106
+ cloud_json = "{\"s3_videos_bucket\":\"videobucket\",\"id\":\"my_cloud_id\"}"
107
+ stub_http_request(:post, /http:\/\/api.example.com:85\/v3.0\/factories.json/).
108
+ with{|r| r.body =~ /s3_videos_bucket=videobucket/ && r.body =~ /user_aws_key=myaccesskey/ && r.body =~ /user_aws_secret=mysecretkey/ }.
109
+ to_return(:body => cloud_json)
110
+
111
+ @cloud = TelestreamCloud::Factory.create(:s3_videos_bucket => 'videobucket', :user_aws_key => 'myaccesskey', :user_aws_secret => 'mysecretkey')
112
+ @cloud.s3_videos_bucket.should == "videobucket"
113
+ end
114
+
115
+
116
+ it "should create a cloud and reload" do
117
+ cloud_json = "{\"s3_videos_bucket\":\"videobucket\",\"id\":\"my_new_cloud_id\"}"
118
+ stub_http_request(:post, /http:\/\/api.example.com:85\/v3.0\/factories.json/).
119
+ to_return(:body => cloud_json)
120
+
121
+ stub_http_request(:get, /api.example.com:85\/v3.0\/factories\/my_new_cloud_id.json/).
122
+ to_return(:body => cloud_json)
123
+
124
+ @cloud = TelestreamCloud::Factory.create(:s3_videos_bucket => 'videobucket', :user_aws_key => 'myaccesskey', :user_aws_secret => 'mysecretkey')
125
+ @cloud.reload.should == @cloud
126
+ end
127
+
128
+ end
129
+
130
+
131
+
132
+ end