telestream_cloud_qc 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +7 -0
  3. data/README.md +58 -0
  4. data/Rakefile +8 -0
  5. data/docs/Alert.md +13 -0
  6. data/docs/AudioStream.md +13 -0
  7. data/docs/Container.md +9 -0
  8. data/docs/Data.md +10 -0
  9. data/docs/Data1.md +9 -0
  10. data/docs/ExtraFile.md +10 -0
  11. data/docs/Job.md +12 -0
  12. data/docs/JobData.md +9 -0
  13. data/docs/JobDetails.md +9 -0
  14. data/docs/JobDetailsResult.md +8 -0
  15. data/docs/JobsCollection.md +12 -0
  16. data/docs/Media.md +10 -0
  17. data/docs/Options.md +7 -0
  18. data/docs/Project.md +12 -0
  19. data/docs/Proxy.md +12 -0
  20. data/docs/QcApi.md +719 -0
  21. data/docs/UploadSession.md +13 -0
  22. data/docs/VideoStream.md +13 -0
  23. data/docs/VideoUploadBody.md +12 -0
  24. data/git_push.sh +55 -0
  25. data/lib/telestream_cloud_qc/api/qc_api.rb +781 -0
  26. data/lib/telestream_cloud_qc/api_client.rb +389 -0
  27. data/lib/telestream_cloud_qc/api_error.rb +38 -0
  28. data/lib/telestream_cloud_qc/configuration.rb +209 -0
  29. data/lib/telestream_cloud_qc/models/alert.rb +236 -0
  30. data/lib/telestream_cloud_qc/models/audio_stream.rb +238 -0
  31. data/lib/telestream_cloud_qc/models/container.rb +198 -0
  32. data/lib/telestream_cloud_qc/models/data.rb +208 -0
  33. data/lib/telestream_cloud_qc/models/data_1.rb +197 -0
  34. data/lib/telestream_cloud_qc/models/extra_file.rb +221 -0
  35. data/lib/telestream_cloud_qc/models/job.rb +269 -0
  36. data/lib/telestream_cloud_qc/models/job_data.rb +197 -0
  37. data/lib/telestream_cloud_qc/models/job_details.rb +199 -0
  38. data/lib/telestream_cloud_qc/models/job_details_result.rb +190 -0
  39. data/lib/telestream_cloud_qc/models/jobs_collection.rb +230 -0
  40. data/lib/telestream_cloud_qc/models/media.rb +210 -0
  41. data/lib/telestream_cloud_qc/models/options.rb +179 -0
  42. data/lib/telestream_cloud_qc/models/project.rb +262 -0
  43. data/lib/telestream_cloud_qc/models/proxy.rb +224 -0
  44. data/lib/telestream_cloud_qc/models/upload_session.rb +249 -0
  45. data/lib/telestream_cloud_qc/models/video_stream.rb +235 -0
  46. data/lib/telestream_cloud_qc/models/video_upload_body.rb +242 -0
  47. data/lib/telestream_cloud_qc/uploader.rb +244 -0
  48. data/lib/telestream_cloud_qc/version.rb +18 -0
  49. data/lib/telestream_cloud_qc.rb +64 -0
  50. data/spec/api/qc_api_spec.rb +202 -0
  51. data/spec/api_client_spec.rb +226 -0
  52. data/spec/configuration_spec.rb +42 -0
  53. data/spec/models/alert_spec.rb +72 -0
  54. data/spec/models/audio_stream_spec.rb +72 -0
  55. data/spec/models/container_spec.rb +48 -0
  56. data/spec/models/data_1_spec.rb +48 -0
  57. data/spec/models/data_spec.rb +54 -0
  58. data/spec/models/extra_file_spec.rb +54 -0
  59. data/spec/models/job_data_spec.rb +48 -0
  60. data/spec/models/job_details_result_spec.rb +42 -0
  61. data/spec/models/job_details_spec.rb +48 -0
  62. data/spec/models/job_spec.rb +74 -0
  63. data/spec/models/jobs_collection_spec.rb +66 -0
  64. data/spec/models/media_spec.rb +54 -0
  65. data/spec/models/options_spec.rb +36 -0
  66. data/spec/models/project_spec.rb +70 -0
  67. data/spec/models/proxy_spec.rb +66 -0
  68. data/spec/models/upload_session_spec.rb +72 -0
  69. data/spec/models/video_stream_spec.rb +72 -0
  70. data/spec/models/video_upload_body_spec.rb +66 -0
  71. data/spec/spec_helper.rb +111 -0
  72. data/telestream_cloud_qc.gemspec +47 -0
  73. metadata +336 -0
@@ -0,0 +1,242 @@
1
+ =begin
2
+ #Qc API
3
+
4
+ #QC API
5
+
6
+ OpenAPI spec version: 2.0.0
7
+ Contact: cloudsupport@telestream.net
8
+ Generated by: https://github.com/swagger-api/swagger-codegen.git
9
+ Swagger Codegen version: 2.3.0
10
+
11
+ =end
12
+
13
+ require 'date'
14
+
15
+ module TelestreamCloud::Qc
16
+
17
+ class VideoUploadBody
18
+ # Size of the file that will be uploaded in `bytes`.
19
+ attr_accessor :file_size
20
+
21
+ # Name of the file that will be uploaded.
22
+ attr_accessor :file_name
23
+
24
+ # A list of names of additional files that will be uploaded.
25
+ attr_accessor :extra_files
26
+
27
+ # A comma-separated list of profile names or IDs to be used during encoding. Alternatively, specify none so no encodings will created right away.
28
+ attr_accessor :profiles
29
+
30
+ attr_accessor :multi_chunk
31
+
32
+
33
+ # Attribute mapping from ruby-style variable name to JSON key.
34
+ def self.attribute_map
35
+ {
36
+ :'file_size' => :'file_size',
37
+ :'file_name' => :'file_name',
38
+ :'extra_files' => :'extra_files',
39
+ :'profiles' => :'profiles',
40
+ :'multi_chunk' => :'multi_chunk'
41
+ }
42
+ end
43
+
44
+ # Attribute type mapping.
45
+ def self.swagger_types
46
+ {
47
+ :'file_size' => :'Integer',
48
+ :'file_name' => :'String',
49
+ :'extra_files' => :'Array<ExtraFile>',
50
+ :'profiles' => :'String',
51
+ :'multi_chunk' => :'BOOLEAN'
52
+ }
53
+ end
54
+
55
+ # Initializes the object
56
+ # @param [Hash] attributes Model attributes in the form of hash
57
+ def initialize(attributes = {})
58
+ return unless attributes.is_a?(Hash)
59
+
60
+ # convert string to symbol for hash key
61
+ attributes = attributes.each_with_object({}){|(k,v), h| h[k.to_sym] = v}
62
+
63
+ if attributes.has_key?(:'file_size')
64
+ self.file_size = attributes[:'file_size']
65
+ end
66
+
67
+ if attributes.has_key?(:'file_name')
68
+ self.file_name = attributes[:'file_name']
69
+ end
70
+
71
+ if attributes.has_key?(:'extra_files')
72
+ if (value = attributes[:'extra_files']).is_a?(Array)
73
+ self.extra_files = value
74
+ end
75
+ end
76
+
77
+ if attributes.has_key?(:'profiles')
78
+ self.profiles = attributes[:'profiles']
79
+ end
80
+
81
+ if attributes.has_key?(:'multi_chunk')
82
+ self.multi_chunk = attributes[:'multi_chunk']
83
+ else
84
+ self.multi_chunk = true
85
+ end
86
+
87
+ end
88
+
89
+ # Show invalid properties with the reasons. Usually used together with valid?
90
+ # @return Array for valid properties with the reasons
91
+ def list_invalid_properties
92
+ invalid_properties = Array.new
93
+ if @file_size.nil?
94
+ invalid_properties.push("invalid value for 'file_size', file_size cannot be nil.")
95
+ end
96
+
97
+ if @file_name.nil?
98
+ invalid_properties.push("invalid value for 'file_name', file_name cannot be nil.")
99
+ end
100
+
101
+ return invalid_properties
102
+ end
103
+
104
+ # Check to see if the all the properties in the model are valid
105
+ # @return true if the model is valid
106
+ def valid?
107
+ return false if @file_size.nil?
108
+ return false if @file_name.nil?
109
+ return true
110
+ end
111
+
112
+ # Checks equality by comparing each attribute.
113
+ # @param [Object] Object to be compared
114
+ def ==(o)
115
+ return true if self.equal?(o)
116
+ self.class == o.class &&
117
+ file_size == o.file_size &&
118
+ file_name == o.file_name &&
119
+ extra_files == o.extra_files &&
120
+ profiles == o.profiles &&
121
+ multi_chunk == o.multi_chunk
122
+ end
123
+
124
+ # @see the `==` method
125
+ # @param [Object] Object to be compared
126
+ def eql?(o)
127
+ self == o
128
+ end
129
+
130
+ # Calculates hash code according to all attributes.
131
+ # @return [Fixnum] Hash code
132
+ def hash
133
+ [file_size, file_name, extra_files, profiles, multi_chunk].hash
134
+ end
135
+
136
+ # Builds the object from hash
137
+ # @param [Hash] attributes Model attributes in the form of hash
138
+ # @return [Object] Returns the model itself
139
+ def build_from_hash(attributes)
140
+ return nil unless attributes.is_a?(Hash)
141
+ self.class.swagger_types.each_pair do |key, type|
142
+ if type =~ /\AArray<(.*)>/i
143
+ # check to ensure the input is an array given that the the attribute
144
+ # is documented as an array but the input is not
145
+ if attributes[self.class.attribute_map[key]].is_a?(Array)
146
+ self.send("#{key}=", attributes[self.class.attribute_map[key]].map{ |v| _deserialize($1, v) } )
147
+ end
148
+ elsif !attributes[self.class.attribute_map[key]].nil?
149
+ self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]]))
150
+ end # or else data not found in attributes(hash), not an issue as the data can be optional
151
+ end
152
+
153
+ self
154
+ end
155
+
156
+ # Deserializes the data based on type
157
+ # @param string type Data type
158
+ # @param string value Value to be deserialized
159
+ # @return [Object] Deserialized data
160
+ def _deserialize(type, value)
161
+ case type.to_sym
162
+ when :DateTime
163
+ DateTime.parse(value)
164
+ when :Date
165
+ Date.parse(value)
166
+ when :String
167
+ value.to_s
168
+ when :Integer
169
+ value.to_i
170
+ when :Float
171
+ value.to_f
172
+ when :BOOLEAN
173
+ if value.to_s =~ /\A(true|t|yes|y|1)\z/i
174
+ true
175
+ else
176
+ false
177
+ end
178
+ when :Object
179
+ # generic object (usually a Hash), return directly
180
+ value
181
+ when /\AArray<(?<inner_type>.+)>\z/
182
+ inner_type = Regexp.last_match[:inner_type]
183
+ value.map { |v| _deserialize(inner_type, v) }
184
+ when /\AHash<(?<k_type>.+?), (?<v_type>.+)>\z/
185
+ k_type = Regexp.last_match[:k_type]
186
+ v_type = Regexp.last_match[:v_type]
187
+ {}.tap do |hash|
188
+ value.each do |k, v|
189
+ hash[_deserialize(k_type, k)] = _deserialize(v_type, v)
190
+ end
191
+ end
192
+ else # model
193
+ temp_model = TelestreamCloud::Qc.const_get(type).new
194
+ temp_model.build_from_hash(value)
195
+ end
196
+ end
197
+
198
+ # Returns the string representation of the object
199
+ # @return [String] String presentation of the object
200
+ def to_s
201
+ to_hash.to_s
202
+ end
203
+
204
+ # to_body is an alias to to_hash (backward compatibility)
205
+ # @return [Hash] Returns the object in the form of hash
206
+ def to_body
207
+ to_hash
208
+ end
209
+
210
+ # Returns the object in the form of hash
211
+ # @return [Hash] Returns the object in the form of hash
212
+ def to_hash
213
+ hash = {}
214
+ self.class.attribute_map.each_pair do |attr, param|
215
+ value = self.send(attr)
216
+ next if value.nil?
217
+ hash[param] = _to_hash(value)
218
+ end
219
+ hash
220
+ end
221
+
222
+ # Outputs non-array value in the form of hash
223
+ # For object, use to_hash. Otherwise, just return the value
224
+ # @param [Object] value Any valid value
225
+ # @return [Hash] Returns the value in the form of hash
226
+ def _to_hash(value)
227
+ if value.is_a?(Array)
228
+ value.compact.map{ |v| _to_hash(v) }
229
+ elsif value.is_a?(Hash)
230
+ {}.tap do |hash|
231
+ value.each { |k, v| hash[k] = _to_hash(v) }
232
+ end
233
+ elsif value.respond_to? :to_hash
234
+ value.to_hash
235
+ else
236
+ value
237
+ end
238
+ end
239
+
240
+ end
241
+
242
+ end
@@ -0,0 +1,244 @@
1
+ require 'pathname'
2
+ require 'json'
3
+ require 'concurrent'
4
+
5
+ module TelestreamCloud::Qc
6
+ class Uploader
7
+ class FileReader
8
+ attr_reader :file
9
+
10
+ def initialize(path)
11
+ @mutex = Mutex.new
12
+ @path = path
13
+ reopen
14
+ end
15
+
16
+ def [](range)
17
+ @mutex.synchronize do
18
+ file.seek(range.begin)
19
+ file.read(range.size)
20
+ end
21
+ end
22
+
23
+ def size
24
+ @size ||= file.size
25
+ end
26
+
27
+ def name
28
+ Pathname.new(file.path).basename
29
+ end
30
+
31
+ def close
32
+ @file.close
33
+ @file = nil
34
+ end
35
+
36
+ def reopen
37
+ @file ||= File.open(@path)
38
+ end
39
+ end
40
+
41
+ class FileUploadSession
42
+ attr_reader :uploader, :reader, :tag
43
+
44
+ def initialize(uploader, path, tag = nil)
45
+ @uploader = uploader
46
+ @reader = FileReader.new(path)
47
+ @tag = tag
48
+ @status = :ready
49
+ end
50
+
51
+ def success?
52
+ @status == :success
53
+ end
54
+
55
+ def params
56
+ upload_params = {
57
+ file_size: reader.size,
58
+ file_name: reader.name
59
+ }
60
+
61
+ upload_params[:tag] = @tag if @tag
62
+ upload_params
63
+ end
64
+
65
+ def upload!
66
+ reader.reopen
67
+
68
+ upload_chunks(missing_parts)
69
+
70
+ raise MissingPartError unless missing_parts.empty?
71
+
72
+ @status = :success
73
+ ensure
74
+ reader.close
75
+ end
76
+
77
+ private
78
+
79
+ def upload_chunks(indexes)
80
+ indexes.each do |index|
81
+ send_chunk(index)
82
+ end
83
+ end
84
+
85
+ def session
86
+ uploader.send(:session)
87
+ end
88
+
89
+ def send_chunk(index)
90
+ range = (index * part_size...[(index + 1) * part_size, reader.size].min)
91
+
92
+ # TODO: remove when 1.9 dropped
93
+ unless range.respond_to?(:size)
94
+ def range.size; self.end - self.begin + (exclude_end? ? 0 : 1); end
95
+ end
96
+
97
+ response = Typhoeus.put(
98
+ session.location,
99
+ body: reader[range],
100
+ headers: {
101
+ 'Content-Type' => 'application/octet-stream',
102
+ 'X-Part' => index.to_s,
103
+ 'Content-Length' => range.size.to_s,
104
+ 'X-Extra-File-Tag' => tag,
105
+ }.reject { |_, v| v.nil? }
106
+ )
107
+
108
+ puts response.body
109
+
110
+ uploader.send(:set_resource_id, response)
111
+ response
112
+ end
113
+
114
+ def missing_parts
115
+ upload_status['missing_parts']
116
+ end
117
+
118
+ def upload_status
119
+ response = Typhoeus.get(
120
+ session.location,
121
+ headers: {
122
+ 'X-Extra-File-Tag' => @tag
123
+ }.reject { |_, v| v.nil? }
124
+ )
125
+ JSON.parse(response.body)
126
+ end
127
+
128
+ def parts; session.parts; end
129
+ def part_size; session.part_size; end
130
+ def max_connections; session.max_connections; end
131
+ end
132
+
133
+ class ConcurrentFileUploadSession < FileUploadSession
134
+ def upload_chunks(indexes)
135
+
136
+ indexes.map do |index|
137
+ Concurrent::Future.execute(executor: uploader.thread_pool) do
138
+ send_chunk(index)
139
+ end
140
+ end.map(&:value)
141
+ end
142
+ end
143
+
144
+ class UploadError < StandardError; end
145
+ class MissingPartError < StandardError; end
146
+
147
+ attr_reader :reader, :status, :error, :thread_pool, :concurrency
148
+
149
+ def self.upload(options)
150
+ retries = options.delete(:retries)
151
+ new(options).tap { |x| x.upload(*[retries].compact) }.video
152
+ end
153
+
154
+ def initialize(client, options)
155
+ @client = client
156
+
157
+ @thread_pool = Concurrent::ThreadPoolExecutor.new(
158
+ min_threads: 5,
159
+ max_threads: 5,
160
+ max_queue: 100,
161
+ fallback_policy: :caller_runs
162
+ )
163
+
164
+ file = options.delete(:file) do |key|
165
+ raise KeyError, "key not found: #{key}"
166
+ end
167
+
168
+ @extra_files = parse_extra_files(options.delete(:extra_files))
169
+
170
+ @upload_session = upload_session(file)
171
+
172
+ defaults = {
173
+ multi_chunk: true
174
+ }.merge(@upload_session.params)
175
+
176
+ @params = defaults.merge(options)
177
+
178
+ @params[:extra_files] = @extra_files.map(&:params) if @extra_files
179
+
180
+ @status = :ready
181
+ end
182
+
183
+ def parse_extra_files(extra_files)
184
+ return [] unless extra_files
185
+
186
+ extra_files.flat_map do |tag, paths|
187
+ paths = [paths] unless paths.kind_of?(Array)
188
+
189
+ paths.each_with_index.map do |path, i|
190
+ upload_session(path, paths.size > 1 ? "#{tag}.index-#{i}" : tag)
191
+ end
192
+ end
193
+ end
194
+
195
+ def upload(retries = 5)
196
+ session
197
+
198
+ count = 0
199
+ begin
200
+ count += 1
201
+ upload!
202
+ raise MissingPartError unless success?
203
+ rescue StandardError => e
204
+ puts e
205
+ @error = e
206
+ retry if count < retries
207
+ end
208
+
209
+ @resource_id
210
+ end
211
+
212
+ def success?
213
+ @resource_id && @upload_session.success? && Array(@extra_files).all?(&:success?)
214
+ end
215
+
216
+ def upload_session(path, tag = nil)
217
+ ConcurrentFileUploadSession.new(self, path, tag)
218
+ end
219
+
220
+ private
221
+
222
+ def upload!
223
+ @upload_session.upload!
224
+ @extra_files.each(&:upload!)
225
+ end
226
+
227
+ def set_resource_id(response)
228
+ return if response.code != 200 || response.body.empty?
229
+ @resource_id = JSON.parse(response.body).fetch('id')
230
+ end
231
+
232
+ def session
233
+ return @session if defined?(@session)
234
+ @session = case @client.class.name
235
+ when "TelestreamCloud::Flip::FlipApi"
236
+ @factory_id ||= @params.delete(:factory_id) do |key|
237
+ raise KeyError, "key not found: #{key}"
238
+ end
239
+
240
+ @client.upload_video(@factory_id, @params)
241
+ end
242
+ end
243
+ end
244
+ end
@@ -0,0 +1,18 @@
1
+ module TelestreamCloud
2
+ end
3
+
4
+ =begin
5
+ #Qc API
6
+
7
+ #QC API
8
+
9
+ OpenAPI spec version: 2.0.0
10
+ Contact: cloudsupport@telestream.net
11
+ Generated by: https://github.com/swagger-api/swagger-codegen.git
12
+ Swagger Codegen version: 2.3.0
13
+
14
+ =end
15
+
16
+ module TelestreamCloud::Qc
17
+ VERSION = "2.0.0"
18
+ end
@@ -0,0 +1,64 @@
1
+ module TelestreamCloud
2
+ end
3
+
4
+ =begin
5
+ #Qc API
6
+
7
+ #QC API
8
+
9
+ OpenAPI spec version: 2.0.0
10
+ Contact: cloudsupport@telestream.net
11
+ Generated by: https://github.com/swagger-api/swagger-codegen.git
12
+ Swagger Codegen version: 2.3.0
13
+
14
+ =end
15
+
16
+ # Common files
17
+ require 'telestream_cloud_qc/api_client'
18
+ require 'telestream_cloud_qc/api_error'
19
+ require 'telestream_cloud_qc/version'
20
+ require 'telestream_cloud_qc/configuration'
21
+
22
+ # Models
23
+ require 'telestream_cloud_qc/models/alert'
24
+ require 'telestream_cloud_qc/models/audio_stream'
25
+ require 'telestream_cloud_qc/models/container'
26
+ require 'telestream_cloud_qc/models/data'
27
+ require 'telestream_cloud_qc/models/data_1'
28
+ require 'telestream_cloud_qc/models/extra_file'
29
+ require 'telestream_cloud_qc/models/job'
30
+ require 'telestream_cloud_qc/models/job_data'
31
+ require 'telestream_cloud_qc/models/job_details'
32
+ require 'telestream_cloud_qc/models/job_details_result'
33
+ require 'telestream_cloud_qc/models/jobs_collection'
34
+ require 'telestream_cloud_qc/models/media'
35
+ require 'telestream_cloud_qc/models/options'
36
+ require 'telestream_cloud_qc/models/project'
37
+ require 'telestream_cloud_qc/models/proxy'
38
+ require 'telestream_cloud_qc/models/upload_session'
39
+ require 'telestream_cloud_qc/models/video_stream'
40
+ require 'telestream_cloud_qc/models/video_upload_body'
41
+
42
+ # APIs
43
+ require 'telestream_cloud_qc/api/qc_api'
44
+
45
+ # Uploader
46
+ require 'telestream_cloud_qc/uploader'
47
+
48
+ module TelestreamCloud::Qc
49
+ class << self
50
+ # Customize default settings for the SDK using block.
51
+ # TelestreamCloud::Qc.configure do |config|
52
+ # config.username = "xxx"
53
+ # config.password = "xxx"
54
+ # end
55
+ # If no block given, return the default Configuration object.
56
+ def configure
57
+ if block_given?
58
+ yield(Configuration.default)
59
+ else
60
+ Configuration.default
61
+ end
62
+ end
63
+ end
64
+ end