vidispine 1.4.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 (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/Gemfile +11 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +296 -0
  6. data/Rakefile +2 -0
  7. data/bin/vidispine +7 -0
  8. data/bin/vidispine-notification-handler +339 -0
  9. data/bin/vidispine-utilities-http-server +13 -0
  10. data/dist/vidispine-1.4.0.gem +0 -0
  11. data/lib/vidispine.rb +5 -0
  12. data/lib/vidispine/api/client.rb +981 -0
  13. data/lib/vidispine/api/client/http_client.rb +291 -0
  14. data/lib/vidispine/api/client/requests.rb +27 -0
  15. data/lib/vidispine/api/client/requests/base_request.rb +234 -0
  16. data/lib/vidispine/api/client/requests/collection_access_add.rb +30 -0
  17. data/lib/vidispine/api/client/requests/collection_access_delete.rb +26 -0
  18. data/lib/vidispine/api/client/requests/collection_metadata_set.rb +24 -0
  19. data/lib/vidispine/api/client/requests/import_placeholder.rb +36 -0
  20. data/lib/vidispine/api/client/requests/import_placeholder_item.rb +38 -0
  21. data/lib/vidispine/api/client/requests/import_sidecar_file.rb +28 -0
  22. data/lib/vidispine/api/client/requests/import_using_uri.rb +44 -0
  23. data/lib/vidispine/api/client/requests/item_access_add.rb +30 -0
  24. data/lib/vidispine/api/client/requests/item_access_delete.rb +26 -0
  25. data/lib/vidispine/api/client/requests/item_delete.rb +17 -0
  26. data/lib/vidispine/api/client/requests/item_export.rb +44 -0
  27. data/lib/vidispine/api/client/requests/item_metadata_get.rb +37 -0
  28. data/lib/vidispine/api/client/requests/item_metadata_set.rb +29 -0
  29. data/lib/vidispine/api/client/requests/item_search.rb +0 -0
  30. data/lib/vidispine/api/client/requests/item_transcode.rb +23 -0
  31. data/lib/vidispine/api/client/requests/items_search.rb +40 -0
  32. data/lib/vidispine/api/client/requests/search.rb +59 -0
  33. data/lib/vidispine/api/client/requests/storage_file_create.rb +23 -0
  34. data/lib/vidispine/api/client/requests/storage_file_get.rb +40 -0
  35. data/lib/vidispine/api/client/requests/storage_files_get.rb +42 -0
  36. data/lib/vidispine/api/utilities.rb +1608 -0
  37. data/lib/vidispine/api/utilities/cli.rb +168 -0
  38. data/lib/vidispine/api/utilities/http_server.rb +230 -0
  39. data/lib/vidispine/api/utilities/http_server/cli.rb +77 -0
  40. data/lib/vidispine/cli.rb +174 -0
  41. data/lib/vidispine/version.rb +3 -0
  42. data/resources/postman/Vidispine API (From WADL v4.11).postman_collection.json +47437 -0
  43. data/vidispine.gemspec +26 -0
  44. metadata +117 -0
@@ -0,0 +1,168 @@
1
+ require 'json'
2
+ require 'pp'
3
+ require 'rexml/document'
4
+
5
+ require 'vidispine/cli'
6
+ require 'vidispine/api/utilities'
7
+
8
+ module Vidispine
9
+
10
+ module API
11
+
12
+ class Utilities
13
+
14
+ class CLI < Vidispine::CLI
15
+
16
+ def self.define_parameters
17
+ default_http_host_address = Vidispine::API::Client::HTTPClient::DEFAULT_HTTP_HOST_ADDRESS
18
+ default_http_host_port = Vidispine::API::Client::HTTPClient::DEFAULT_HTTP_HOST_PORT
19
+ default_accepts_header = Vidispine::API::Client::HTTPClient::DEFAULT_HEADER_ACCEPTS
20
+ default_content_type_header = Vidispine::API::Client::HTTPClient::DEFAULT_HEADER_CONTENT_TYPE
21
+
22
+ argument_parser.on('--host-address HOSTADDRESS', 'The address of the server to communicate with.', "\tdefault: #{default_http_host_address}") { |v| arguments[:http_host_address] = v }
23
+ argument_parser.on('--host-port HOSTPORT', 'The port to use when communicating with the server.', "\tdefault: #{default_http_host_port}") { |v| arguments[:http_host_port] = v }
24
+ argument_parser.on('--username USERNAME', 'The account username to authenticate with.') { |v| arguments[:username] = v }
25
+ argument_parser.on('--password PASSWORD', 'The account password to authenticate with.') { |v| arguments[:password] = v }
26
+
27
+ argument_parser.on('--accept-header VALUE', 'The value for the Accept header sent in each request.', "\tdefault: #{default_accepts_header}") { |v| arguments[:accepts_header] = v }
28
+ argument_parser.on('--content-type VALUE', 'The value for the Content-Type header sent in each request.', "\tdefault: #{default_content_type_header}") { |v| arguments[:content_type] = v }
29
+ argument_parser.on('--method-name METHODNAME', 'The name of the method to call.') { |v| arguments[:method_name] = v }
30
+ argument_parser.on('--method-arguments JSON', 'The arguments to pass when calling the method.') { |v| arguments[:method_arguments] = v }
31
+ argument_parser.on('--storage-map JSON', 'A map of file paths to storage ids to use in utility methods.') { |v| arguments[:storage_map] = v }
32
+ argument_parser.on('--metadata-map JSON', 'A map of field aliases to field names to use in utility methods.') { |v| arguments[:metadata_map] = v }
33
+ argument_parser.on('--pretty-print', 'Will format the output to be more human readable.') { |v| arguments[:pretty_print] = v }
34
+
35
+ argument_parser.on('--log-to FILENAME', 'Log file location.', "\tdefault: #{log_to_as_string}") { |v| arguments[:log_to] = v }
36
+ argument_parser.on('--log-level LEVEL', LOGGING_LEVELS.keys, "Logging level. Available Options: #{LOGGING_LEVELS.keys.join(', ')}",
37
+ "\tdefault: #{LOGGING_LEVELS.invert[arguments[:log_level]]}") { |v| arguments[:log_level] = LOGGING_LEVELS[v] }
38
+
39
+ argument_parser.on('--[no-]options-file [FILENAME]', 'Path to a file which contains default command line arguments.', "\tdefault: #{arguments[:options_file_path]}" ) { |v| arguments[:options_file_path] = v}
40
+ argument_parser.on_tail('-h', '--help', 'Display this message.') { puts help; exit }
41
+ end
42
+
43
+ attr_accessor :logger, :api
44
+
45
+ def after_initialize
46
+ initialize_api(arguments)
47
+ end
48
+
49
+ def initialize_api(args = { })
50
+ @api = Vidispine::API::Utilities.new(args )
51
+ end
52
+
53
+ def run(args = arguments, opts = options)
54
+ storage_map = args[:storage_map]
55
+ @api.default_storage_map = JSON.parse(storage_map) if storage_map.is_a?(String)
56
+
57
+ metadata_map = args[:metadata_map]
58
+ @api.default_metadata_map = JSON.parse(metadata_map) if metadata_map.is_a?(String)
59
+
60
+ method_name = args[:method_name]
61
+ send(method_name, args[:method_arguments], :pretty_print => args[:pretty_print]) if method_name
62
+
63
+ self
64
+ end
65
+
66
+ def send(method_name, method_arguments, params = {})
67
+ method_name = method_name.to_sym
68
+ logger.debug { "Executing Method: #{method_name}" }
69
+
70
+ send_arguments = [ method_name ]
71
+
72
+ if method_arguments
73
+ method_arguments = JSON.parse(method_arguments, :symbolize_names => true) if method_arguments.is_a?(String) and method_arguments.start_with?('{', '[')
74
+ send_arguments.concat method_arguments.is_a?(Array) ? [ *method_arguments ] : [ method_arguments ]
75
+ end
76
+ #puts "Send Arguments: #{send_arguments.inspect}"
77
+ response = api.__send__(*send_arguments)
78
+
79
+ # if response.code.to_i.between?(500,599)
80
+ # puts parsed_response
81
+ # exit
82
+ # end
83
+ #
84
+ # if ResponseHandler.respond_to?(method_name)
85
+ # ResponseHandler.client = api
86
+ # ResponseHandler.response = response
87
+ # response = ResponseHandler.__send__(*send_arguments)
88
+ # end
89
+
90
+ if params[:pretty_print]
91
+ if response.is_a?(String)
92
+ _response_cleaned = response.strip
93
+ if _response_cleaned.start_with?('{', '[')
94
+ puts prettify_json(response)
95
+ elsif _response_cleaned.start_with?('<') and _response_cleaned.end_with?('>')
96
+ puts prettify_xml(response)
97
+ else
98
+ #pp response.is_a?(String) ? response : JSON.pretty_generate(response) rescue response
99
+ puts response.is_a?(String) ? response : JSON.pretty_generate(response) rescue response
100
+ end
101
+ else
102
+ pp response.is_a?(String) ? response : JSON.pretty_generate(response) rescue response
103
+ end
104
+ else
105
+ response = JSON.generate(response) if response.is_a?(Hash) or response.is_a?(Array)
106
+ puts response
107
+ end
108
+ # send
109
+ end
110
+
111
+ def prettify_json(json)
112
+ JSON.pretty_generate(JSON.parse(json))
113
+ end
114
+
115
+ def prettify_xml(xml, options = { })
116
+ document = REXML::Document.new(xml)
117
+ document.write(output = '', options[:indent] || 1)
118
+
119
+ return output unless options.fetch(:collapse_tags, true)
120
+
121
+ last_open_tag = ''
122
+ #last_matching_close_tag = ''
123
+ last_was_tag = false
124
+ last_was_matching_close_tag = false
125
+
126
+ output.lines.map do |v|
127
+ _v = v.strip
128
+
129
+ is_tag = _v.start_with?('<') and _v.end_with?('>')
130
+ is_open_tag = (is_tag and !_v.start_with?('</'))
131
+ is_matching_close_tag = (is_tag and !is_open_tag and _v == "</#{(last_open_tag || '')[1..-1]}")
132
+
133
+ _output = if is_open_tag
134
+ "#{(last_was_tag and !last_was_matching_close_tag) ? "\n" : ''}#{v.rstrip}"
135
+ elsif is_matching_close_tag
136
+ "#{_v}\n"
137
+ elsif !is_tag
138
+ _v
139
+ else
140
+ v
141
+ end
142
+ #puts "V: '#{_v}' IT: #{is_tag} IOT: #{is_open_tag} ICT: #{is_matching_close_tag} LOT: '#{last_open_tag}' LCT: #{last_matching_close_tag} OUTPUT: '#{_output}'"
143
+
144
+ last_was_tag = is_tag
145
+ last_was_matching_close_tag = is_matching_close_tag
146
+ if is_open_tag
147
+ last_open_tag = _v.split(' ').first
148
+ last_open_tag << '>' unless last_open_tag.end_with?('>')
149
+ elsif is_matching_close_tag
150
+ #last_matching_close_tag = _v
151
+ end
152
+
153
+ _output
154
+ end.join('')
155
+ end
156
+
157
+ # CLI
158
+ end
159
+
160
+ # Utilities
161
+ end
162
+
163
+ # API
164
+ end
165
+
166
+ # Vidispine
167
+ end
168
+ def cli; @cli ||= Vidispine::API::Utilities::CLI end
@@ -0,0 +1,230 @@
1
+ require 'pp'
2
+
3
+ require 'sinatra/base'
4
+ require 'vidispine/api/utilities'
5
+
6
+ module Vidispine
7
+
8
+ module API
9
+
10
+ class Utilities
11
+
12
+ class HTTPServer < Sinatra::Base
13
+
14
+ configure do
15
+ enable :show_exceptions
16
+ enable :dump_errors
17
+ #enable :lock # ensure single request concurrency with a mutex lock
18
+ end
19
+
20
+ DEFAULT_ADDRESS_BINDING = 'localhost'
21
+ DEFAULT_PORT = '4567'
22
+
23
+ attr_accessor :global_arguments,
24
+ :metadata_file_path_field_id,
25
+ :relative_file_path_collection_name_position,
26
+ :storage_path_map
27
+
28
+ # @!group Routes
29
+
30
+ get '/collection-by-name/:collection_name' do
31
+ log_request_match(__method__)
32
+ _response = api.dup.collection_get_by_name(params)
33
+ logger.debug { "Response: #{_response}" }
34
+ format_response(_response)
35
+ end
36
+
37
+ post '/collection-file-add-using-path/?' do
38
+ log_request_match(__method__)
39
+ _params = merge_params_from_body
40
+
41
+ #args_out = _params
42
+ args_out = indifferent_hash.merge({
43
+ :storage_path_map => initial_arguments[:storage_path_map],
44
+ :relative_file_path_collection_name_position => initial_arguments[:relative_file_path_collection_name_position],
45
+ :metadata_file_path_field_id => initial_arguments[:metadata_file_path_field_id],
46
+ })
47
+
48
+ args_out.merge!(_params)
49
+ _response = api.dup.collection_file_add_using_path(args_out)
50
+ format_response(_response)
51
+ end
52
+
53
+ # Shows what gems are within scope. Used for diagnostics and troubleshooting.
54
+ get '/gems' do
55
+ stdout_str = `gem list -b`
56
+ stdout_str ? stdout_str.gsub("\n", '<br/>') : stdout_str
57
+ end
58
+
59
+ get '/favicon.ico' do; end
60
+
61
+ get '/:method_name?' do
62
+ logger.debug { "GET #{params.inspect}" }
63
+ method_name = params[:method_name]
64
+ pass unless api.respond_to?(method_name)
65
+ log_request_match(__method__)
66
+ _params = merge_params_from_body
67
+
68
+ _response = api.dup.send(method_name, _params)
69
+ format_response(_response)
70
+ end
71
+
72
+ post '/:method_name?' do
73
+ logger.debug { "POST #{params.inspect}" }
74
+ method_name = params[:method_name]
75
+ pass unless api.respond_to?(method_name)
76
+ log_request_match(__method__)
77
+
78
+ _params = merge_params_from_body
79
+
80
+ _response = api.dup.send(method_name, _params)
81
+ format_response(_response)
82
+ end
83
+
84
+
85
+
86
+ ### CATCH ALL ROUTES BEGIN
87
+ get /.*/ do
88
+ log_request_match(__method__)
89
+ request_to_s.gsub("\n", '<br/>')
90
+ end
91
+
92
+ post /.*/ do
93
+ log_request_match(__method__)
94
+ end
95
+ ### CATCH ALL ROUTES END
96
+
97
+ # @!endgroup Routes
98
+
99
+ def format_response(response, args = { })
100
+ supported_types = %w(application/json application/xml text/xml)
101
+ case request.preferred_type(supported_types)
102
+ when 'application/json'
103
+ content_type :json
104
+ _response = (response.is_a?(Hash) || response.is_a?(Array)) ? JSON.generate(response) : response
105
+ #when 'application/xml', 'text/xml'
106
+ # content_type :xml
107
+ # _response = XmlSimple.xml_out(response, { :root_name => 'response' })
108
+ else
109
+ content_type :json
110
+ _response = (response.is_a?(Hash) || response.is_a?(Array)) ? JSON.generate(response) : response
111
+ end
112
+ _response
113
+ end # output_response
114
+
115
+ def parse_body
116
+ if request.media_type == 'application/json'
117
+ request.body.rewind
118
+ body_contents = request.body.read
119
+ logger.debug { "Parsing: '#{body_contents}'" }
120
+ if body_contents
121
+ json_params = JSON.parse(body_contents)
122
+ return json_params
123
+ end
124
+ end
125
+
126
+ end # parse_body
127
+
128
+ # Will try to convert a body to parameters and merge them into the params hash
129
+ # Params will override the body parameters
130
+ #
131
+ # @params [Hash] _params (params) The parameters parsed from the query and form fields
132
+ def merge_params_from_body(_params = params)
133
+ _params = _params.dup
134
+ _params_from_body = parse_body
135
+ _params = _params_from_body.merge(_params) if _params_from_body.is_a?(Hash)
136
+ indifferent_hash.merge(_params)
137
+ end # merge_params_from_body
138
+
139
+ # @param [Hash] args
140
+ # @option args [Request] :request
141
+ def request_to_s(args = { })
142
+ _request = args[:request] || request
143
+ output = <<-OUTPUT
144
+ ------------------------------------------------------------------------------------------------------------------------
145
+ REQUEST
146
+ Method: #{_request.request_method}
147
+ URI: #{_request.url}
148
+
149
+ Host: #{_request.host}
150
+ Path: #{_request.path}
151
+ Script Name: #{_request.script_name}
152
+ Query String: #{_request.query_string}
153
+ XHR? #{_request.xhr?}
154
+
155
+ Remote
156
+ Host: #{_request.env['REMOTE_HOST']}
157
+ IP: #{_request.ip}
158
+ User Agent: #{_request.user_agent}
159
+ Cookies: #{_request.cookies}
160
+ Accepts: #{_request.accept.inspect}
161
+ Preferred Type: #{_request.preferred_type}
162
+
163
+ Media Type: #{_request.media_type}
164
+ BODY BEGIN:
165
+ #{_request.body.read}
166
+ BODY END.
167
+
168
+ Parsed Parameters:
169
+ #{PP.pp(_request.params, '', 60)}
170
+
171
+ ------------------------------------------------------------------------------------------------------------------------
172
+ OUTPUT
173
+ _request.body.rewind
174
+ output
175
+ end # request_to_s
176
+
177
+ def log_request(route = '')
178
+ return if request.path == '/favicon.ico'
179
+ logger.debug { "\n#{request_to_s}" }
180
+ #puts requests.insert(request_to_hash)
181
+ end # log_request
182
+
183
+ def log_request_match(route)
184
+ log_request(route)
185
+ logger.debug { "MATCHED: #{request.url} -> #{route}\nParsed Parameters: #{params.inspect}" }
186
+ end # log_request_match
187
+
188
+ def self.initialize_logger(args = { })
189
+ logger = args[:logger] ||= Logger.new(args[:log_to] || STDOUT)
190
+ logger.level = args[:log_level] if args[:log_level]
191
+ logger
192
+ end
193
+
194
+ # @param [Hash] args
195
+ # @option args [Logger] :logger
196
+ # @option args [String] :binding
197
+ # @option args [String] :local_port
198
+ def self.init(args = {})
199
+ logger = initialize_logger(args)
200
+ set(:logger, logger)
201
+
202
+ logger.debug { "Initializing HTTP Server. Arguments: #{args.inspect}" }
203
+
204
+ _binding = args.delete(:binding) { DEFAULT_ADDRESS_BINDING }
205
+ _port = args.delete(:port) { DEFAULT_PORT }
206
+ set(:bind, _binding)
207
+ set(:port, _port)
208
+ set(:initial_arguments, args)
209
+
210
+ set(:api, args[:api])
211
+ end
212
+
213
+ def logger
214
+ #self.class.logger
215
+ settings.logger
216
+ end
217
+
218
+ def api
219
+ #self.class.api
220
+ settings.api
221
+ end
222
+
223
+ end
224
+
225
+ end
226
+
227
+ end
228
+
229
+ end
230
+
@@ -0,0 +1,77 @@
1
+ require 'vidispine/cli'
2
+ require 'vidispine/api/utilities/http_server'
3
+
4
+ module Vidispine
5
+
6
+ module API
7
+
8
+ class Utilities
9
+
10
+ class HTTPServer
11
+
12
+ class CLI < Vidispine::CLI
13
+
14
+ def self.define_parameters
15
+ default_http_host_address = Vidispine::API::Client::HTTPClient::DEFAULT_HTTP_HOST_ADDRESS
16
+ default_http_host_port = Vidispine::API::Client::HTTPClient::DEFAULT_HTTP_HOST_PORT
17
+ default_vidispine_username = Vidispine::API::Client::HTTPClient::DEFAULT_USERNAME
18
+ default_vidispine_password = Vidispine::API::Client::HTTPClient::DEFAULT_PASSWORD
19
+
20
+ default_port = Vidispine::API::Utilities::HTTPServer::DEFAULT_PORT
21
+
22
+ argument_parser.on('--vidispine-http-host-address HOSTADDRESS', 'The address of the server to communicate with.', "\tdefault: #{default_http_host_address}") { |v| arguments[:http_host_address] = v }
23
+ argument_parser.on('--vidispine-http-host-port HOSTPORT', 'The port to use when communicating with the server.', "\tdefault: #{default_http_host_port}") { |v| arguments[:http_host_port] = v }
24
+ argument_parser.on('--vidispine-username USERNAME', 'The account username to authenticate with.', "\tdefault: #{default_vidispine_username}") { |v| arguments[:username] = v }
25
+ argument_parser.on('--vidispine-password PASSWORD', 'The account password to authenticate with.', "\tdefault: #{default_vidispine_password}") { |v| arguments[:password] = v }
26
+
27
+ argument_parser.on('--storage-path-map MAP', 'A path=>storage-id mapping to match incoming file paths to storages.') { |v| arguments[:storage_path_map] = v }
28
+ argument_parser.on('--metadata-field-map MAP', 'A path=>portal-field-id mapping to match incoming metadata fields to portal metadata fields.') { |v| arguments[:metadata_map] = v }
29
+ argument_parser.on('--relative-file-path-collection-name-position NUM',
30
+ 'The relative position from the storage base path in which to select the collection name.',
31
+ "\tdefault: 0") { |v| arguments[:relative_file_path_collection_name_position] = v }
32
+ argument_parser.on('--metadata-file-path-field-id ID',
33
+ 'The Id of the metadata field where the file path is to be stored.'
34
+ ) { |v| arguments[:metadata_file_path_field_id] = v }
35
+ argument_parser.on('--bind-to-address ADDRESS', 'The local address to bind the server to.') { |v| arguments[:bind] = v }
36
+ argument_parser.on('--port PORT', 'The port to bind to.', "\tdefault: #{default_port}") { |v| arguments[:port] = v }
37
+ argument_parser.on('--log-to FILENAME', 'Log file location.', "\tdefault: #{log_to_as_string}") { |v| arguments[:log_to] = v }
38
+ argument_parser.on('--log-level LEVEL', LOGGING_LEVELS.keys, "Logging level. Available Options: #{LOGGING_LEVELS.keys.join(', ')}",
39
+ "\tdefault: #{LOGGING_LEVELS.invert[arguments[:log_level]]}") { |v| arguments[:log_level] = LOGGING_LEVELS[v] }
40
+
41
+ argument_parser.on('--[no-]options-file [FILENAME]', 'Path to a file which contains default command line arguments.', "\tdefault: #{arguments[:options_file_path]}" ) { |v| arguments[:options_file_path] = v}
42
+ argument_parser.on_tail('-h', '--help', 'Display this message.') { puts help; exit }
43
+ end
44
+
45
+ attr_accessor :logger, :api, :app
46
+
47
+ def after_initialize
48
+ initialize_api(arguments)
49
+ initialize_app(arguments)
50
+ end
51
+
52
+ def initialize_api(args = { })
53
+ @api = Vidispine::API::Utilities.new(args)
54
+ end
55
+
56
+ def initialize_app(args = { })
57
+ @app = Vidispine::API::Utilities::HTTPServer
58
+ args_out = args
59
+ args_out[:api] = api
60
+ app.init(args)
61
+ end
62
+
63
+ def run(args = arguments, opts = options)
64
+ app.run!
65
+ self
66
+ end
67
+
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+
76
+ end
77
+ def cli; @cli ||= Vidispine::API::Utilities::HTTPServer::CLI end