ubiquity-mediasilo-api-v3 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/Gemfile +6 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +100 -0
  6. data/Rakefile +2 -0
  7. data/bin/ubiquity-mediasilo-api-v3 +26 -0
  8. data/lib/ubiquity/cli.rb +127 -0
  9. data/lib/ubiquity/mediasilo/api/v3.rb +11 -0
  10. data/lib/ubiquity/mediasilo/api/v3/cli.rb +94 -0
  11. data/lib/ubiquity/mediasilo/api/v3/client.rb +712 -0
  12. data/lib/ubiquity/mediasilo/api/v3/client/paginator.rb +133 -0
  13. data/lib/ubiquity/mediasilo/api/v3/client/requests.rb +18 -0
  14. data/lib/ubiquity/mediasilo/api/v3/client/requests/asset_copy_to_folder.rb +19 -0
  15. data/lib/ubiquity/mediasilo/api/v3/client/requests/asset_copy_to_project.rb +19 -0
  16. data/lib/ubiquity/mediasilo/api/v3/client/requests/asset_create.rb +25 -0
  17. data/lib/ubiquity/mediasilo/api/v3/client/requests/asset_delete.rb +15 -0
  18. data/lib/ubiquity/mediasilo/api/v3/client/requests/asset_get_by_id.rb +14 -0
  19. data/lib/ubiquity/mediasilo/api/v3/client/requests/asset_move_to_folder.rb +16 -0
  20. data/lib/ubiquity/mediasilo/api/v3/client/requests/asset_move_to_project.rb +16 -0
  21. data/lib/ubiquity/mediasilo/api/v3/client/requests/assets_get.rb +56 -0
  22. data/lib/ubiquity/mediasilo/api/v3/client/requests/assets_get_by_folder_id.rb +14 -0
  23. data/lib/ubiquity/mediasilo/api/v3/client/requests/assets_get_by_project_id.rb +14 -0
  24. data/lib/ubiquity/mediasilo/api/v3/client/requests/base_request.rb +250 -0
  25. data/lib/ubiquity/mediasilo/api/v3/client/requests/batch.rb +20 -0
  26. data/lib/ubiquity/mediasilo/api/v3/client/requests/project_create.rb +15 -0
  27. data/lib/ubiquity/mediasilo/api/v3/client/requests/quicklink_create.rb +54 -0
  28. data/lib/ubiquity/mediasilo/api/v3/client/requests/quicklink_share.rb +32 -0
  29. data/lib/ubiquity/mediasilo/api/v3/http_client.rb +259 -0
  30. data/lib/ubiquity/mediasilo/api/v3/utilities.rb +1145 -0
  31. data/lib/ubiquity/mediasilo/api/v3/version.rb +9 -0
  32. data/ubiquity-mediasilo-api-v3.gemspec +23 -0
  33. metadata +104 -0
@@ -0,0 +1,20 @@
1
+ module Ubiquity::MediaSilo::API::V3::Client::Requests
2
+
3
+ # @see http://docs.mediasilo.com/v3.0/docs/batch-overview
4
+ class Batch < BaseRequest
5
+
6
+ HTTP_METHOD = :post
7
+ HTTP_PATH = 'batch'
8
+ HTTP_SUCCESS_CODE = 200
9
+
10
+ PARAMETERS = [
11
+ { :name => :requests, :send_in => :body, :send_key => false }
12
+ ]
13
+
14
+ def body
15
+ @body ||= arguments[:requests]
16
+ end
17
+
18
+ end
19
+
20
+ end
@@ -0,0 +1,15 @@
1
+ module Ubiquity::MediaSilo::API::V3::Client::Requests
2
+
3
+ class ProjectCreate < BaseRequest
4
+
5
+ HTTP_METHOD = :post
6
+ HTTP_PATH = '/projects'
7
+
8
+ PARAMETERS = [
9
+ { :name => :name, :required => true },
10
+ :description,
11
+ ]
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,54 @@
1
+ module Ubiquity::MediaSilo::API::V3::Client::Requests
2
+
3
+ class QuicklinkCreate < BaseRequest
4
+
5
+ HTTP_METHOD = :post
6
+ HTTP_PATH = '/quicklinks'
7
+ DEFAULT_PARAMETER_SEND_IN_VALUE = :body
8
+
9
+ PARAMETERS = [
10
+ { :name => :title, :required => true },
11
+ :description,
12
+ :assetIds,
13
+ :configuration,
14
+ :expires,
15
+ :authorizedUserIds,
16
+
17
+ # :settings,
18
+ # :configuration_id,
19
+ # :audience,
20
+ # :playback,
21
+ # :allow_download,
22
+ # :allow_feedback,
23
+ # :show_metadata,
24
+ # :notify_email,
25
+ # :include_directlink,
26
+ # :password
27
+ ]
28
+
29
+ # def post_process_arguments
30
+ # settings = arguments.delete(:settings)
31
+ # audience = arguments.delete(:audience)
32
+ # playback = arguments.delete(:playback)
33
+ # allow_download = arguments.delete(:allow_download)
34
+ # allow_feedback = arguments.delete(:allow_feedback)
35
+ # show_metadata = arguments.delete(:show_metadata)
36
+ # notify_email = arguments.delete(:notify_email)
37
+ # include_directlink = arguments.delete(:include_directlink)
38
+ # password = arguments.delete(:password)
39
+ #
40
+ # configuration = arguments[:configuration] || { }
41
+ # configuration_id = arguments[:configuration_id]
42
+ #
43
+ # # TODO Symbolize Configuration Keys
44
+ # configuration[:id] ||= configuration_id || ''
45
+ #
46
+ # settings ||= configuration[:settings] || { }
47
+ #
48
+ # settings[:audience] = audience
49
+ #
50
+ # end
51
+
52
+ end
53
+
54
+ end
@@ -0,0 +1,32 @@
1
+ module Ubiquity::MediaSilo::API::V3::Client::Requests
2
+
3
+ class QuicklinkShare < BaseRequest
4
+
5
+ HTTP_METHOD = :post
6
+ HTTP_PATH = '/shares'
7
+ DEFAULT_PARAMETER_SEND_IN_VALUE = :body
8
+
9
+ PARAMETERS = [
10
+ { :name => :targetObjectId, :required => true },
11
+ :emailShare,
12
+ :subject,
13
+ :message,
14
+ :audience
15
+ ]
16
+
17
+ def post_process_arguments
18
+ _email_share = arguments[:emailShare]
19
+ _email_share ||= { }
20
+ _audience = arguments[:audience] || _email_share[:audience] || _email_share['audience'] || [ ]
21
+ _subject = arguments[:subject] || _email_share[:subject] || _email_share['subject']
22
+ _message = arguments[:message] || _email_share[:message] || _email_share['message']
23
+ _email_share[:audience] ||= _audience
24
+ _email_share[:subject] ||= _subject if _subject
25
+ _email_share[:message] ||= _message if _message
26
+
27
+ arguments[:emailShare] = _email_share
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,259 @@
1
+ require 'cgi'
2
+ require 'json'
3
+ require 'logger'
4
+ require 'net/http'
5
+ require 'net/https'
6
+
7
+ require 'ubiquity/mediasilo/api/v3'
8
+
9
+ module Ubiquity
10
+ module MediaSilo
11
+ class API
12
+ module V3
13
+
14
+ class HTTPClient
15
+
16
+ class RateLimitException < StandardError; end
17
+
18
+ # Ruby uses all lower case headers but Jetty uses case sensitive headers
19
+ class CaseSensitiveHeaderKey < String
20
+ def downcase; self end
21
+ def capitalize; self end
22
+ end
23
+
24
+
25
+ attr_accessor :logger, :http, :http_host_address, :http_host_port, :base_uri
26
+ attr_accessor :hostname, :username, :password
27
+
28
+ attr_accessor :default_request_headers
29
+
30
+ attr_accessor :log_request_body, :log_response_body, :log_pretty_print_body
31
+
32
+ attr_accessor :request, :response
33
+
34
+ # DEFAULT_HTTP_HOST_ADDRESS = 'api.mediasilo.com'
35
+ DEFAULT_HTTP_HOST_ADDRESS = 'p-api-new.mediasilo.com'
36
+ DEFAULT_HTTP_HOST_PORT = 443
37
+
38
+ def initialize(args = { })
39
+ args = args.dup
40
+ initialize_logger(args)
41
+ initialize_http(args)
42
+
43
+ @hostname = args[:hostname] || ''
44
+ @username = args[:username] || ''
45
+ @password = args[:password] || ''
46
+ @api_key = args[:api_key]
47
+ @base_uri = args[:base_uri] || "http#{http.use_ssl? ? 's' : ''}://#{http.address}:#{http.port}/v3/"
48
+
49
+ @user_agent_default = "#{@hostname}:#{@username} Ubiquity Ruby SDK Version #{Ubiquity::MediaSilo::API::V3::VERSION}"
50
+
51
+ authorization_header_name = CaseSensitiveHeaderKey.new('Authorization')
52
+ authorization_header_value = 'Basic ' + ["#{username}:#{password}"].pack('m').delete("\r\n")
53
+
54
+ host_context_header_name = CaseSensitiveHeaderKey.new('MediaSiloHostContext')
55
+ host_context_header_value = hostname.downcase
56
+
57
+ @default_request_headers = {
58
+ 'user-agent' => @user_agent_default,
59
+ 'Content-Type' => 'application/json; charset=utf-8',
60
+ 'Accept' => 'application/json',
61
+ host_context_header_name => host_context_header_value,
62
+ authorization_header_name => authorization_header_value,
63
+ }
64
+
65
+ if @api_key
66
+ api_key_header_name = CaseSensitiveHeaderKey.new('MediaSiloApiKey')
67
+ default_request_headers[api_key_header_name] = @api_key
68
+ end
69
+
70
+ @log_request_body = args.fetch(:log_request_body, true)
71
+ @log_response_body = args.fetch(:log_response_body, true)
72
+ @log_pretty_print_body = args.fetch(:log_pretty_print_body, true)
73
+
74
+ @delay_between_rate_limit_retries = 300
75
+ @cancelled = false
76
+ @parse_response = args.fetch(:parse_response, true)
77
+ end
78
+
79
+ def initialize_logger(args = { })
80
+ @logger = args[:logger] ||= Logger.new(args[:log_to] || STDOUT)
81
+ log_level = args[:log_level]
82
+ if log_level
83
+ @logger.level = log_level
84
+ args[:logger] = @logger
85
+ end
86
+ @logger
87
+ end
88
+
89
+ def initialize_http(args = { })
90
+ @http_host_address = args[:http_host_address] ||= DEFAULT_HTTP_HOST_ADDRESS
91
+ @http_host_port = args[:http_host_port] ||= DEFAULT_HTTP_HOST_PORT
92
+ @http = Net::HTTP.new(http_host_address, http_host_port)
93
+ http.use_ssl = true
94
+
95
+ # TODO Add SSL Patch
96
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
97
+
98
+ http
99
+ end
100
+
101
+ # Formats a HTTPRequest or HTTPResponse body for log output.
102
+ # @param [HTTPRequest|HTTPResponse] obj
103
+ # @return [String]
104
+ def format_body_for_log_output(obj)
105
+ if obj.content_type == 'application/json'
106
+ if @log_pretty_print_body
107
+ _body = obj.body
108
+ output = "\n" << JSON.pretty_generate(JSON.parse(_body)) rescue _body
109
+ return output
110
+ else
111
+ return obj.body
112
+ end
113
+ else
114
+ return obj.body.inspect
115
+ end
116
+ end
117
+
118
+ # @param [Net::HTTPRequest] request
119
+ def send_request(request)
120
+ @response_parsed = nil
121
+ @request = request
122
+
123
+ begin
124
+ logger.debug { %(REQUEST: #{request.method} http#{http.use_ssl? ? 's' : ''}://#{http.address}:#{http.port}#{request.path} HEADERS: #{request.to_hash.inspect} #{log_request_body and request.request_body_permitted? ? "BODY: #{format_body_for_log_output(request)}" : ''}) }
125
+
126
+ @response = http.request(request)
127
+ logger.debug { %(RESPONSE: #{response.inspect} HEADERS: #{response.to_hash.inspect} #{log_response_body and response.respond_to?(:body) ? "BODY: #{format_body_for_log_output(response)}" : ''}) }
128
+ raise RateLimitException, "#{response.to_hash.inspect}" if response.code == '420'
129
+ rescue RateLimitException => e
130
+ logger.warn { "Rate Limited. Will retry in #{@delay_between_rate_limit_retries} seconds." }
131
+ sleep_break @delay_between_rate_limit_retries
132
+ retry unless @cancelled
133
+ end
134
+
135
+ @parse_response ? response_parsed : response.body
136
+ end
137
+
138
+ def sleep_break(seconds)
139
+ while (seconds > 0)
140
+ sleep(1)
141
+ seconds -= 1
142
+ break if @cancelled
143
+ end
144
+ end
145
+
146
+ def response_parsed
147
+ @response_parsed ||= begin
148
+ response_content_type = response.content_type
149
+ logger.debug { "Parsing Response: #{response_content_type}" }
150
+
151
+ case response_content_type
152
+ when 'application/json'
153
+ JSON.parse(response.body) rescue response
154
+ # when 'text/html'
155
+ # when 'text/plain'
156
+ else
157
+ response.body
158
+ end
159
+ end
160
+ end
161
+
162
+ def build_uri(path = '', query = nil)
163
+ _query = query.is_a?(Hash) ? query.map { |k,v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v)}" }.join('&') : query
164
+ _path = "#{path}#{_query and _query.respond_to?(:empty?) and !_query.empty? ? "?#{_query}" : ''}"
165
+ URI.parse(File.join(base_uri, _path))
166
+ end
167
+
168
+ if RUBY_VERSION.start_with? '1.8.'
169
+ def request_method_name_to_class_name(method_name)
170
+ method_name.to_s.capitalize
171
+ end
172
+ else
173
+ def request_method_name_to_class_name(method_name)
174
+ method_name.to_s.capitalize.to_sym
175
+ end
176
+ end
177
+
178
+ # @param [Symbol] method_name (:get)
179
+ # @param [Hash] args
180
+ # @option args [Hash] :headers ({})
181
+ # @option args [String] :path ('')
182
+ # @option args [Hash] :query ({})
183
+ # @option args [Any] :body (nil)
184
+ # @param [Hash] options
185
+ # @option options [Hash] :default_request_headers (@default_request_headers)
186
+ def call_method(method_name = :get, args = { }, options = { })
187
+ headers = args[:headers] || options[:headers] || { }
188
+ path = args[:path] || ''
189
+ query = args[:query] || { }
190
+ body = args[:body]
191
+
192
+ # Allow the default request headers to be overridden
193
+ _default_request_headers = options.fetch(:default_request_headers, default_request_headers)
194
+ _default_request_headers ||= { }
195
+ _headers = _default_request_headers.merge(headers)
196
+
197
+ @uri = build_uri(path, query)
198
+ klass_name = request_method_name_to_class_name(method_name)
199
+ klass = Net::HTTP.const_get(klass_name)
200
+
201
+ request = klass.new(@uri.request_uri, _headers)
202
+
203
+ if request.request_body_permitted?
204
+ _body = (body and !body.is_a?(String)) ? JSON.generate(body) : body
205
+ logger.debug { "Processing Body: '#{_body}'" }
206
+ request.body = _body if _body
207
+ end
208
+
209
+ send_request(request)
210
+ end
211
+
212
+ def delete(path, options = { })
213
+ query = options.fetch(:query, { })
214
+ @uri = build_uri(path, query)
215
+
216
+
217
+ request = Net::HTTP::Delete.new(@uri.request_uri, default_request_headers)
218
+ body = options[:body]
219
+ if body
220
+ body = JSON.generate(body) unless body.is_a?(String)
221
+ request.body = body
222
+ end
223
+
224
+ send_request(request)
225
+ end
226
+
227
+ def get(path, query = nil, options = { })
228
+ query ||= options.fetch(:query, { })
229
+ @uri = build_uri(path, query)
230
+ request = Net::HTTP::Get.new(@uri.request_uri, default_request_headers)
231
+ send_request(request)
232
+ end
233
+
234
+ def put(path, body, options = { })
235
+ query = options.fetch(:query, { })
236
+ @uri = build_uri(path, query)
237
+ body = JSON.generate(body) unless body.is_a?(String)
238
+
239
+ request = Net::HTTP::Put.new(@uri.request_uri, default_request_headers)
240
+ request.body = body
241
+ send_request(request)
242
+ end
243
+
244
+ def post(path, body, options = { })
245
+ query = options.fetch(:query, { })
246
+ @uri = build_uri(path, query)
247
+ body = JSON.generate(body) unless body.is_a?(String)
248
+
249
+ request = Net::HTTP::Post.new(@uri.request_uri, default_request_headers)
250
+ request.body = body
251
+ send_request(request)
252
+ end
253
+
254
+ end
255
+
256
+ end
257
+ end
258
+ end
259
+ end
@@ -0,0 +1,1145 @@
1
+ require 'ubiquity/mediasilo/api/v3/client'
2
+ require 'open-uri' # for download_file
3
+
4
+ class Exception
5
+ def prefix_message(message_prefix = nil)
6
+ begin
7
+ raise self, "#{message_prefix ? "#{message_prefix} " : ''}#{message}", backtrace
8
+ rescue Exception => e
9
+ return e
10
+ end
11
+ end
12
+ end
13
+
14
+ class Ubiquity::MediaSilo::API::V3::Utilities < Ubiquity::MediaSilo::API::V3::Client
15
+
16
+ # VALID_ASSET_SEARCH_FIELD_NAMES = %w(approvalStatus archiveStatus averageRating commentCount dateCreated dateModified derivatives description external fileName folderId id myRating permissions private progress projectId tags title transcriptStatus type uploadedBy)
17
+ # VALID_ASSET_SEARCH_FIELDS = {
18
+ # :approvalstatus => 'approvalStatus',
19
+ # :archivestatus => 'archiveStatus',
20
+ # :averagerating => 'averageRating',
21
+ # :commentcount => 'commentCount',
22
+ # :datecreated => 'dateCreated',
23
+ # :datemodified => 'dateModified',
24
+ # :derivatives => 'derivatives',
25
+ # :description => 'description',
26
+ # :external => 'external',
27
+ # :filename => 'fileName',
28
+ # :folderid => 'folderId',
29
+ # :id => 'id',
30
+ # :metadatamatch => 'metadatamatch',
31
+ # :myrating => 'myRating', :permissions => 'permissions', :private => 'private', :progress => 'progress', :projectid => 'projectId', :tags => 'tags', :title => 'title', :transcriptstatus => 'transcriptStatus', :type => 'type', :uploadedby => 'uploadedBy' }
32
+
33
+
34
+ def default_case_sensitive_search
35
+ true
36
+ end
37
+
38
+ # Creates an asset building any missing parts of the path (Project/Folder/Asset)
39
+ #
40
+ # Required Parameters
41
+ # :url
42
+ # :mediasilo_path or :file_path
43
+ #
44
+ # Optional Parameters
45
+ # :metadata
46
+ # :overwrite_existing_asset
47
+ # :asset_search_field_name
48
+ def asset_create_using_path(args = { })
49
+
50
+ file_url = args[:url] || args[:file_url] || args[:source_url]
51
+ raise ArgumentError ':url is a required parameter.' unless file_url
52
+
53
+ file_path = args[:mediasilo_path] || args[:file_path]
54
+ raise ArgumentError ':mediasilo_path is a required parameter.' unless file_path
55
+
56
+ ms_metadata = args[:metadata]
57
+
58
+ asset_title = args[:title]
59
+ asset_description = args[:description]
60
+ additional_asset_create_params = { }
61
+ additional_asset_create_params['title'] = asset_title if asset_title
62
+ additional_asset_create_params['description'] = asset_description if asset_description
63
+
64
+ overwrite_existing_asset = args.fetch(:overwrite_existing_asset, false)
65
+ asset_search_field_name = args[:asset_search_field_name] || :filename
66
+
67
+ #logger.info { "Creating Asset on MediaSilo Using File Path: '#{file_path}'. File URL: #{file_url}" }
68
+
69
+ #ms_uuid = ms.asset_create(file_url, { }, ms_project)
70
+ path_create_options = {
71
+ :overwrite_existing_asset => overwrite_existing_asset,
72
+ :additional_asset_create_params => additional_asset_create_params,
73
+ :asset_search_field_name => asset_search_field_name
74
+ }
75
+ begin
76
+ result = path_create(file_path, true, file_url, ms_metadata, path_create_options)
77
+ # rescue => e
78
+ # raise e, "Exception Creating Asset Using File Path. #{e.message}", e.backtrace
79
+ end
80
+
81
+ output_values = { }
82
+ output_values[:result] = result
83
+
84
+ return false unless result and result.has_key?(:asset)
85
+ #return publish_error('Error creating asset.') unless result and result.has_key?(:asset)
86
+
87
+ result_asset = result[:asset]
88
+ if result_asset == false
89
+ ms_asset_id = false
90
+ elsif result[:asset_missing] == false
91
+ ms_asset_id = result[:existing][:asset]['id'] # if not result[:existing][:asset]['uuid'].nil?
92
+ elsif result_asset.is_a?(Array)
93
+
94
+ #Metadata creation failed but asset was created Array(false, "uuid")
95
+ #TODO: HANDLE CASE WHERE METADATA IS NOT CREATED BUT THE ASSET IS
96
+ ms_asset_id = result_asset[1]
97
+
98
+ else
99
+ ms_asset_id = result_asset['id']
100
+ ms_asset_id ||= result_asset
101
+ end
102
+ #Setting metadata during asset_create doesn't work so we set it here
103
+ #response = ms.metadata_create(ms_uuid, ms_metadata) unless ms_metadata.nil?
104
+
105
+ if ms_asset_id
106
+ output_values[:asset_id] = ms_asset_id
107
+ return output_values
108
+ else
109
+ #return publish_error("Error creating asset.\nResponse: #{response}\nMS UUID: #{ms_uuid}\nMS Creating Missing Path Result: #{result}")
110
+ return false
111
+ end
112
+ end
113
+ alias :asset_create_using_file_path :asset_create_using_path
114
+
115
+ def asset_derivatives_transform_to_hash(derivatives)
116
+ return { } unless derivatives.is_a?(Array)
117
+ _derivatives = derivatives.map do |d|
118
+ strategies = d['strategies']
119
+
120
+ d['strategies'] = Hash[strategies.map { |s| [ s['type'], s ] }] if strategies
121
+
122
+ [ d['type'], d ]
123
+ end
124
+ Hash[_derivatives]
125
+ end
126
+
127
+ # Downloads a file from a URI or file location and saves it to a local path
128
+ #
129
+ # @param [String] download_file_path The source path of the file being downloaded
130
+ # @param [String] destination_file_path The destination path for the file being downloaded
131
+ # @param [Boolean] overwrite Determines if the destination file will be overwritten if it is found to exist
132
+ #
133
+ # @return [Hash]
134
+ # * :download_file_path [String] The source path of the file being downloaded
135
+ # * :overwrite [Boolean] The value of the overwrite parameter when the method was called
136
+ # * :file_downloaded [Boolean] Indicates if the file was downloaded, will be false if overwrite was true and the file existed
137
+ # * :destination_file_existed [String|Boolean] The value will be 'unknown' if overwrite is true because the file exist check will not have been run inside of the method
138
+ # * :destination_file_path [String] The destination path for the file being downloaded
139
+ def download_file(download_file_path, destination_file_path, overwrite = false)
140
+ logger.debug { "Downloading '#{download_file_path}' -> '#{destination_file_path}' Overwrite: #{overwrite}" }
141
+ file_existed = 'unknown'
142
+ if overwrite or not(file_existed = File.exists?(destination_file_path))
143
+ File.open(destination_file_path, 'wb') { |tf|
144
+ open(download_file_path) { |sf| tf.write sf.read }
145
+ }
146
+ file_downloaded = true
147
+ else
148
+ file_downloaded = false
149
+ end
150
+ return { :download_file_path => download_file_path, :overwrite => overwrite, :file_downloaded => file_downloaded, :destination_file_existed => file_existed, :destination_file_path => destination_file_path }
151
+ end
152
+
153
+ # @option [String] derivative_name Known options are proxy, proxy2m, source, and waveform
154
+ def asset_download_derivative(derivative_name, asset, destination_file_path, overwrite = false, field_name = 'url')
155
+ return asset.map { |a| asset_download_derivative(derivative_name, a, destination_file_path, overwrite) } if asset.is_a?(Array)
156
+ asset = asset_get_by_id_extended( { :asset_id => asset }, :include_derivatives_hash => true ) if asset.is_a? String
157
+ _derivatives = asset['derivatives_hash'] || begin
158
+ asset_derivatives_transform_to_hash(asset['derivatives'])
159
+ end
160
+
161
+ file_to_download = _derivatives[derivative_name][field_name]
162
+ asset_download_resource(file_to_download, destination_file_path, overwrite)
163
+ end
164
+
165
+ def asset_download_poster_frame_file(asset, destination_file_path, overwrite = false)
166
+ return asset.map { |a| asset_download_poster_frame_file(a, destination_file_path, overwrite) } if asset.is_a?(Array)
167
+ asset = asset_get_by_id(:asset_id => asset ) if asset.is_a? String
168
+
169
+ file_to_download = asset['posterFrame']
170
+ asset_download_resource(file_to_download, destination_file_path, overwrite)
171
+ end
172
+
173
+ # @param [String|Hash] asset
174
+ # @param [String] destination_file_path
175
+ # @param [Boolean] overwrite
176
+ # @return [Hash] see (MediaSilo#download_file)
177
+ def asset_download_proxy_file(asset, destination_file_path, overwrite = false)
178
+ asset_download_derivative('proxy', asset, destination_file_path, overwrite)
179
+ end
180
+
181
+ # @param [String] download_file_path
182
+ # @param [String] destination_file_path
183
+ # @param [Boolean] overwrite
184
+ def asset_download_resource(download_file_path, destination_file_path, overwrite = false)
185
+ # download_file_path = URI.encode(download_file_path)
186
+
187
+ destination_file_path = File.join(destination_file_path, File.basename(URI.decode(download_file_path))) if File.directory? destination_file_path
188
+ download_file(download_file_path, destination_file_path, overwrite)
189
+ end
190
+
191
+ # @param [String|Hash] asset
192
+ # @param [String] destination_file_path
193
+ # @param [Boolean] overwrite
194
+ # @return [Hash] see (MediaSilo#download_file)
195
+ def asset_download_source_file(asset, destination_file_path, overwrite = false)
196
+ asset_download_derivative('source', asset, destination_file_path, overwrite)
197
+ end
198
+
199
+ def asset_download_proxy_poster_frame_file(asset, destination_file_path, overwrite = false)
200
+ asset_download_derivative('proxy', asset, destination_file_path, overwrite, 'posterFrame')
201
+ end
202
+
203
+ def asset_download_proxy_thumbnail_file(asset, destination_file_path, overwrite = false)
204
+ asset_download_derivative('proxy', asset, destination_file_path, overwrite, 'thumbnail')
205
+ end
206
+
207
+
208
+ # @param [String] asset_uuid
209
+ # @param [Hash] args A hash of arguments
210
+ # @option args [String] :asset_id The uuid of the asset to edit
211
+ # @option args [Hash] :metadata The asset's metadata.
212
+ # @option args [Boolean] :mirror_metadata If set to true then the metadata will mirror :metadata which means that any keys existing on MediaSilo that don't exists in :metadata will be deleted from MediaSilo.
213
+ # @option args [Array] :tags_to_add_to_asset An array of tag names to add to the asset
214
+ # @option args [Array] :tags_to_remove_from_asset An array of tag names to remove from the asset
215
+ # @option args [Boolean|Hash|Array] :add_quicklink_to_asset
216
+ # @option args (see MediaSilo#asset_edit)
217
+ def asset_edit_extended(asset_id, args = { }, options = { })
218
+ logger.debug { "Asset Edit Extended: #{asset_id} ARGS: #{args.inspect}" }
219
+ if asset_id.is_a?(Hash)
220
+ options = args.dup
221
+ args = asset_id.dup
222
+ asset_id = args[:asset_id]
223
+ raise ArgumentError, 'Error Editing Asset. Missing required argument :asset_id' unless asset_id
224
+ else
225
+ args = args.dup
226
+ args[:asset_id] = asset_id
227
+ end
228
+
229
+ ms_metadata = args.delete(:metadata) { false }
230
+ mirror_metadata = args.delete(:mirror_metadata) { false }
231
+ #
232
+ add_tag_to_asset = args.delete(:tags_to_add) { [ ] }
233
+ add_tag_to_asset = args.delete(:tags_to_add_to_asset) { add_tag_to_asset }
234
+
235
+ remove_tag_from_asset = args.delete(:tags_to_remove) { [ ] }
236
+ remove_tag_from_asset = args.delete(:tags_to_remove_from_asset) { remove_tag_from_asset }
237
+ #
238
+ # add_quicklink_to_asset = args.delete(:add_quicklink_to_asset) { false }
239
+
240
+ _response = { :success => false }
241
+ if args[:title] or args[:description]
242
+ result = asset_edit(args)
243
+ _response[:asset_edit_result] = result
244
+ _response[:asset_edit_response] = response
245
+ _response[:asset_edit_success] = success?
246
+ unless success?
247
+ _response[:error_message] = "Error Editing Asset. #{error_message}"
248
+ return _response
249
+ end
250
+ end
251
+
252
+ if ms_metadata.is_a?(Hash)
253
+ if mirror_metadata
254
+ result = metadata_mirror(:asset_id => asset_id, :metadata => ms_metadata)
255
+ else
256
+ result = metadata_create_or_update(:asset_id => asset_id, :metadata => ms_metadata)
257
+ end
258
+ _response[:metadata_edit_result] = result
259
+ _response[:metadata_edit_response] = response
260
+ _response[:metadata_edit_success] = success?
261
+ unless success?
262
+ _response[:error_message] = "Error Editing Asset's Metadata. #{error_message}"
263
+ return _response
264
+ end
265
+ end
266
+
267
+ batch_execute do
268
+ unless remove_tag_from_asset.nil? or remove_tag_from_asset.empty?
269
+ [*remove_tag_from_asset].uniq.each { |tag_to_remove| asset_tag_remove(:asset_id => asset_id, :tag => tag_to_remove) if tag_to_remove.is_a?(String); }
270
+ end
271
+ unless add_tag_to_asset.nil? or add_tag_to_asset.empty?
272
+ [*add_tag_to_asset].uniq.each { |tag_to_add| asset_tag_add(:asset_id => asset_id, :tags => tag_to_add) if tag_to_add.is_a?(String) }
273
+ end
274
+ end
275
+
276
+ #
277
+ # if add_quicklink_to_asset
278
+ # add_quicklink_to_asset.is_a?(Hash) ? quicklink_create(asset_id, add_quicklink_to_asset) : quicklink_create(asset_id)
279
+ # _response[:quicklink_create_response] = response.body_parsed
280
+ # _response[:quicklink_create_success] = success?
281
+ # unless success?
282
+ # _response[:error_message] = "Error Adding Quicklink to Asset. #{error_message}"
283
+ # return _response
284
+ # end
285
+ # end
286
+
287
+ _response[:success] = true
288
+ _response
289
+ end
290
+
291
+ # @param [String] search_field_name
292
+ # @param [String] search_value
293
+ # @param [String|nil] project_id
294
+ # @param [String|nil] folder_id
295
+ # @param [Hash] options
296
+ # @option options [Boolean] :limit_to_project_root (true)
297
+ # @return [Array]
298
+ def asset_get_by_field_search(search_field_name, search_value, project_id = nil, folder_id = nil, options = { })
299
+
300
+ path = if folder_id and !folder_id == 0
301
+ limit_to_project_root = false
302
+ "/folders/#{folder_id}/assets"
303
+ elsif project_id
304
+ limit_to_project_root = options.fetch(:limit_to_project_root, true)
305
+ "/projects/#{project_id}/assets"
306
+ else
307
+ limit_to_project_root = false
308
+ '/assets'
309
+ end
310
+
311
+ # _search_field_name = VALID_ASSET_SEARCH_FIELDS[search_field_name.to_s.downcase.to_sym]
312
+ # raise ArgumentError, "The argument for the search_field_name parameter is invalid. '#{search_field_name}' not found in #{VALID_ASSET_SEARCH_FIELDS.values.inspect}" unless _search_field_name
313
+
314
+ _search_field_name = search_field_name.to_s
315
+ search_operator_default = search_value.is_a?(Array) ? 'in' : nil
316
+ search_operator = options[:operator] || search_operator_default
317
+ case search_operator
318
+ when nil
319
+ search_field_query = { _search_field_name => search_value }
320
+ else
321
+ search_field_query = { _search_field_name => %({"#{search_operator}":"#{[*search_value].join(',')}"}) }
322
+ end
323
+
324
+ # query = { :type => '{"in":"video,image,document,archive,audio"}' }.merge((options[:query] || { })).merge(search_field_query)
325
+ query = (options[:query] || { }).merge(search_field_query)
326
+ assets = http_client.get(path, query)
327
+ assets = [ ] if http_client.response.code == '404'
328
+ limit_to_project_root ? assets.delete_if { |v| v['folderId'] } : assets
329
+ end
330
+
331
+ def asset_get_by_id_extended(args = { }, options = { })
332
+ include_metadata = options.delete(:include_metadata)
333
+ include_derivatives_hash = options.delete(:include_derivatives_hash) { true }
334
+ transform_metadata = options.delete(:transform_metadata) { true }
335
+
336
+ asset_id = args[:asset_id]
337
+
338
+ asset = asset_get_by_id({ :asset_id => asset_id }, options)
339
+
340
+ if include_metadata
341
+ md = metadata_get( { :asset_id => asset['id'] }, options )
342
+ md = metadata_transform_to_hash(md) if transform_metadata
343
+ asset['metadata'] = md
344
+ end
345
+
346
+ if include_derivatives_hash
347
+ _derivatives = asset['derivatives']
348
+ asset['derivatives_hash'] = asset_derivatives_transform_to_hash(_derivatives)
349
+ end
350
+
351
+ asset
352
+ end
353
+
354
+ # @param [Hash] args
355
+ # @param [Hash] options
356
+ # @option options [Boolean] :include_metadata
357
+ # @option options [Boolean] :transform_metadata Will cause metadata to be transformed to a simple Hash
358
+ def assets_get_extended(args = { }, options = { })
359
+ include_metadata = options.delete(:include_metadata)
360
+ include_derivatives_hash = options.delete(:include_derivatives_hash) { true }
361
+ transform_metadata = options.delete(:transform_metadata) { true }
362
+
363
+ assets = assets_get(args, options)
364
+
365
+ assets.map! do |a|
366
+
367
+ if include_metadata
368
+ md = metadata_get(:asset_id => a['id'])
369
+ md = metadata_transform_to_hash(md) if transform_metadata
370
+ a['metadata'] = md
371
+ end
372
+
373
+ if include_derivatives_hash
374
+ _derivatives = a['derivatives'].map do |d|
375
+ strategies = d['strategies']
376
+
377
+ d['strategies'] = Hash[strategies.map { |s| [s['type'], s] }] if strategies
378
+
379
+ [ d['type'], d ]
380
+ end
381
+
382
+ a['derivatives_hash'] = Hash[_derivatives]
383
+ end
384
+
385
+ a
386
+ end if (include_metadata || include_derivatives_hash)
387
+
388
+ assets
389
+ end
390
+
391
+
392
+
393
+ # Refines asset_get_by_field_search results to exact (full string case sensitive) matches
394
+ #
395
+ # @param [String] search_value
396
+ # @param [String] field_name The field to search.
397
+ # ["approvalstatus", "archivestatus", "averagerating", "datecreated", "datemodified", "description",
398
+ # "duration", "external", "filename", "height", "progress", "rating", "secure", "size", "thumbnail_large",
399
+ # "thumbnail_small", "title", "totalcomments", "transcriptstatus", "type", "uploaduser", "uuid", "width"]
400
+ # @param [Array] assets The assets as returned by asset_adavanced_search_by_results
401
+ # @param [Hash] options
402
+ # @option options [Boolean] :return_first_match (false)
403
+ # @option options [Boolean] :match_full_string (true)
404
+ # @option options [Boolean] :case_sensitive (#default_case_sensitive_search)
405
+ def refine_asset_get_by_field_search_results(field_name, search_value, assets, options)
406
+ return unless assets
407
+ return_first_match = options.fetch(:return_first_match, false)
408
+ match_full_string = options.fetch(:match_full_string, true)
409
+ case_sensitive = options.fetch(:case_sensitive, default_case_sensitive_search)
410
+ search_value = search_value.to_s.downcase unless case_sensitive
411
+ method = return_first_match ? :drop_while : :delete_if
412
+ assets.send(method) do |asset|
413
+ asset_value = case_sensitive ? asset[field_name] : asset[field_name].to_s.downcase
414
+ nomatch = match_full_string ? (asset_value != search_value) : (!asset_value.include?(search_value))
415
+ #logger.debug "COMPARING: '#{search_value}' #{nomatch ? '!' : '='}= '#{asset_value}'"
416
+ nomatch
417
+ end if assets and (match_full_string or case_sensitive)
418
+ return assets.first if assets and return_first_match
419
+ assets
420
+ end
421
+
422
+ def asset_get_by_filename(asset_name, project_id = nil, folder_id = nil, options = { })
423
+ assets = asset_get_by_field_search(:filename, asset_name, project_id, folder_id, options)
424
+ refine_asset_get_by_field_search_results('fileName', asset_name, assets, options)
425
+ end
426
+
427
+ def asset_get_by_title(asset_name, project_id = nil, folder_id = nil, options = { })
428
+ assets = asset_get_by_field_search(:title, asset_name, project_id, folder_id, options)
429
+ refine_asset_get_by_field_search_results('title', asset_name, assets, options)
430
+ end
431
+
432
+ def folder_get_by_name(project_id, folder_name, parent_id, options = { })
433
+ logger.debug { "Searching For Folder by Name: '#{folder_name}' Project Id: '#{project_id}' Parent Id: '#{parent_id}'"}
434
+ case_sensitive = options.fetch(:case_sensitive, default_case_sensitive_search)
435
+ return_all_matches = !options.fetch(:return_first_match, false)
436
+
437
+ folders = options[:folders] || (parent_id ? folders_get_by_parent_id(:parent_id => parent_id) : folders_get_by_project_id(:project_id => project_id))
438
+ return false unless folders
439
+
440
+ # Use delete_if instead of keep_if to be ruby 1.8 compatible
441
+ folders.dup.delete_if do |folder|
442
+ folder_name_to_test = folder['name']
443
+ folder_name_to_test.upcase! unless case_sensitive
444
+ no_match = (folder_name_to_test != folder_name)
445
+ return folder unless no_match or return_all_matches
446
+ no_match
447
+ end
448
+ return nil unless return_all_matches
449
+ folders
450
+ end
451
+
452
+ # # A metadata creation utility method that checks for existing keys to avoid duplication
453
+ # #
454
+ # # @param [String] asset_id
455
+ # # @param [Hash] metadata
456
+ # # @return [Boolean]
457
+ # def metadata_create_if_not_exists(asset_id, metadata)
458
+ # return asset_id.map { |au| metadata_create_if_not_exists(au, metadata) } if asset_id.is_a?(Array)
459
+ # return unless metadata.is_a?(Hash) and !metadata.empty?
460
+ #
461
+ # md_existing = metadata_get_by_asset_uuid(asset_id)
462
+ # md_existing_keys = {}
463
+ # md_existing.each do |ms_md|
464
+ # md_existing_keys[ms_md['key']] = ms_md
465
+ # end if md_existing.is_a?(Array)
466
+ #
467
+ # md_to_create = [ ]
468
+ # md_to_edit = [ ]
469
+ #
470
+ # metadata.each do |key, value|
471
+ # if md_existing_keys.has_key?(key)
472
+ # md_current = md_existing_keys[key]
473
+ # md_to_edit << { 'key' => key, 'value' => value } unless (md_current['value'] == value)
474
+ # else
475
+ # md_to_create << { 'key' => key, 'value' => value }
476
+ # end
477
+ # end
478
+ #
479
+ # # logger.debug { <<-MD
480
+ # #
481
+ # # Metadata
482
+ # #
483
+ # # Create
484
+ # # #{PP.pp(md_to_create, '')}
485
+ # #
486
+ # # Edit
487
+ # # #{PP.pp(md_to_edit, '')}
488
+ # # MD
489
+ # # }
490
+ #
491
+ # batch_execute do
492
+ # #md_to_edit.each { |data| metadata_update(data.merge({ :asset_id => asset_id })) }
493
+ # #md_to_create.each { |data| next unless data and !data.empty?; metadata_create(data.merge({ :asset_id => asset_id })) }
494
+ #
495
+ # md_to_edit.each_slice(50) { |data| metadata_update(:asset_id => asset_id, :metadata => data) }
496
+ # md_to_create.each_slice(50) { |data| logger.debug { "Data: #{data.inspect}" }; metadata_create(:asset_id => asset_id, :metadata => data) }
497
+ #
498
+ # end
499
+ #
500
+ # end
501
+
502
+ # # A method that mirrors the metadata passed to the asset, deleting any keys that don't exist in the metadata param
503
+ # #
504
+ # # @param [String] asset_id
505
+ # # @param [Hash] metadata
506
+ # def metadata_mirror(asset_id, metadata = { })
507
+ # return asset_id.map { |au| metadata_mirror(au, metadata) } if asset_id.is_a?(Array)
508
+ #
509
+ # md_to_delete = [ ]
510
+ # md_existing_keys = { }
511
+ #
512
+ # md_existing = metadata_get_by_asset_uuid(asset_id)
513
+ # md_existing.each do |ms_md|
514
+ # ms_md_key = ms_md['key']
515
+ # if metadata.key?(ms_md_key)
516
+ # md_existing_keys[ms_md_key] = ms_md
517
+ # else
518
+ # md_to_delete << ms_md_key
519
+ # end
520
+ # end
521
+ #
522
+ # md_to_create = [ ]
523
+ # md_to_edit = [ ]
524
+ #
525
+ # metadata.each do |key, value|
526
+ # if md_existing_keys.has_key?(key)
527
+ # md_current = md_existing_keys[key]
528
+ # md_to_edit << { 'key' => key, 'value' => value } unless (md_current['value'] == value)
529
+ # else
530
+ # md_to_create << { 'key' => key, 'value' => value }
531
+ # end
532
+ # end
533
+ #
534
+ # batch_execute do
535
+ # md_to_edit.each { |data| metadata_update(data.merge({ :asset_id => asset_id })) }
536
+ # md_to_create.each { |data| metadata_create(data.merge({ :asset_id => asset_id })) }
537
+ # md_to_delete.each { |data| metadata_delete({ :asset_id => asset_id, :metadata_key => data }) }
538
+ # end
539
+ #
540
+ # end
541
+
542
+ # @param [Array] metadata_in
543
+ def metadata_transform_to_hash(metadata_in)
544
+ case metadata_in
545
+ when Array
546
+ # This is what we want
547
+ return Hash[ metadata_in.map { |m| [m['key'], m['value'] ] } ]
548
+ when Hash
549
+ return metadata_in
550
+ else
551
+ return { }
552
+ end
553
+ end
554
+
555
+ # Checks to see if a project/folder/asset path exists and records each as existing or missing
556
+ #
557
+ # @param [String] path The path to be checked for existence. Format: project[/folder][/filename]
558
+ # @param [Boolean] path_contains_asset (false) Indicates that the path contains an asset filename
559
+ # @param [Hash] options ({ })
560
+ # @option options [Boolean] :asset_name_field (:filename)
561
+ # (@see #resolve_path)
562
+ def path_check(path, path_contains_asset = false, options = { })
563
+ return false unless path
564
+
565
+ # Remove any and all instances of '/' from the beginning of the path
566
+ path = path[1..-1] while path.start_with? '/'
567
+
568
+ path_ary = path.split('/')
569
+
570
+ existing_path_result = resolve_path(path, path_contains_asset, options)
571
+ existing_path_ary = existing_path_result[:id_path_ary]
572
+ check_path_length = path_ary.length
573
+
574
+ # Get a count of the number of elements which were found to exist
575
+ existing_path_length = existing_path_ary.length
576
+
577
+ # Drop the first n elements of the array which corresponds to the number of elements found to be existing
578
+ missing_path = path_ary.drop(existing_path_length)
579
+ # In the following logic tree the goal is indicate what was searched for and what was found. If we didn't search
580
+ # for the component (folder/asset) then we don't want to set the missing indicator var
581
+ # (folder_missing/asset_missing) for component as a boolean but instead leave it nil.
582
+ missing_path_length = missing_path.length
583
+ if missing_path_length > 0
584
+ # something is missing
585
+
586
+ if missing_path_length == check_path_length
587
+ # everything is missing in our path
588
+
589
+ project_missing = true
590
+ if path_contains_asset
591
+ # we are missing everything and we were looking for an asset so it must be missing
592
+ asset_missing = true
593
+
594
+ if check_path_length > 2
595
+ #if we were looking for more than two things (project, folder, and asset) and we are missing everything then folders are missing also
596
+ searched_folders = true
597
+ folder_missing = true
598
+ else
599
+
600
+ #if we are only looking for 2 things then that is only project and asset, folders weren't in the path so we aren't missing them
601
+ searched_folders = false
602
+ folder_missing = false
603
+ end
604
+ else
605
+ if check_path_length > 1
606
+ # If we are looking for more than one thing then it was project and folder and both are missing
607
+ searched_folders = true
608
+ folder_missing = true
609
+ else
610
+ searched_folders = false
611
+ folder_missing = false
612
+ end
613
+ end
614
+ else
615
+ #we have found at least one thing and it starts with project
616
+ project_missing = false
617
+ if path_contains_asset
618
+ #missing at least 1 and the asset is at the end so we know it's missing
619
+ asset_missing = true
620
+ if missing_path_length == 1
621
+ #if we are only missing one thing and it's the asset then it's not a folder!
622
+ folder_missing = false
623
+ searched_folders = check_path_length > 2
624
+ else
625
+ # missing_path_length is more than 1
626
+ if check_path_length > 2
627
+ #we are looking for project, folder, and asset and missing at least 3 things so they are all missing
628
+ searched_folders = true
629
+ folder_missing = true
630
+ else
631
+ #we are only looking for project and asset so no folders are missing
632
+ searched_folders = false
633
+ folder_missing = false
634
+ end
635
+ end
636
+ else
637
+ #if we are missing something and the project was found and there was no asset then it must be a folder
638
+ searched_folders = true
639
+ folder_missing = true
640
+ end
641
+ end
642
+ else
643
+ searched_folders = !existing_path_result[:folders].empty?
644
+ project_missing = folder_missing = asset_missing = false
645
+ end
646
+
647
+ {
648
+ :check_path_ary => path_ary,
649
+ :existing => existing_path_result,
650
+ :missing_path => missing_path,
651
+ :searched_folders => searched_folders,
652
+ :project_missing => project_missing,
653
+ :folder_missing => folder_missing,
654
+ :asset_missing => asset_missing,
655
+ }
656
+ end
657
+ alias :check_path :path_check
658
+
659
+
660
+ # Calls check_path to see if any part of a project/folder/asset path are missing from MediaSilo and creates any part that is missing
661
+ #
662
+ # @param [String] path The path to create inside of MediaSilo
663
+ # @param [Boolean] contains_asset see #path_resolve
664
+ # @param [String|nil] asset_url
665
+ # @param [Hash|nil] metadata
666
+ # @param [Hash] options
667
+ # @option options [Hash] :additional_asset_create_params Additional arguments to pass to the asset_create call
668
+ # @option options [Boolean] :overwrite_asset Will cause the asset to be deleted and recreated
669
+ # @option options [Boolean] :path_creation_delay A delay between folder_create calls to allow
670
+ # @return [Hash]
671
+ # {
672
+ # :check_path_ary=>["create_missing_path_test"],
673
+ # :existing=>{
674
+ # :name_path=>"/",
675
+ # :id_path=>"/",
676
+ # :name_path_ary=>[],
677
+ # :id_path_ary=>[],
678
+ # :project=>false,
679
+ # :asset=>nil,
680
+ # :folders=>[]
681
+ # },
682
+ # :missing_path=>[],
683
+ # :searched_folders=>false,
684
+ # :project_missing=>true,
685
+ # :folder_missing=>false,
686
+ # :asset_missing=>nil,
687
+ # :project=>{
688
+ # "id"=>30620,
689
+ # "datecreated"=>"June, 05 2013 15:20:15",
690
+ # "description"=>"",
691
+ # "uuid"=>"15C84A5F-B2D9-0E2F-507D94189F8A1FDC",
692
+ # "name"=>"create_missing_path_test"
693
+ # },
694
+ # :project_id=>30620,
695
+ # :parent_folder_id=>0
696
+ # }
697
+ def path_create(path, contains_asset = false, asset_url = nil, metadata = nil, options = { })
698
+ overwrite_asset = options.fetch(:overwrite_asset, false)
699
+ additional_asset_create_params = options[:additional_asset_create_params] || { }
700
+ path_creation_delay = options[:path_creation_delay] || 10
701
+ asset_search_field_name = options[:asset_search_field_name] || :filename
702
+
703
+ cp_result = check_path(path, contains_asset, :asset_search_field_name => asset_search_field_name)
704
+ logger.debug { "CHECK PATH RESULT #{cp_result.inspect}" }
705
+ return false unless cp_result
706
+
707
+ project_missing = cp_result[:project_missing]
708
+ folder_missing = cp_result[:folder_missing]
709
+ asset_missing = cp_result[:asset_missing]
710
+
711
+ existing = cp_result[:existing]
712
+
713
+ asset = existing[:asset]
714
+
715
+ # This was meant as a Bypass if nothing needed to be done, complicated code maintenance
716
+ # unless project_missing or folder_missing or asset_missing or (!asset_missing and overwrite_asset)
717
+ # project_id = cp_result[:existing][:id_path_ary].first
718
+ # asset = cp_result[:existing][:asset]
719
+ # if contains_asset
720
+ # asset_id = cp_result[:existing][:id_path_ary].last
721
+ # parent_folder_id = cp_result[:existing][:id_path_ary].fetch(-2)
722
+ #
723
+ # asset_edit_extended(asset_id, additional_asset_create_params) if metadata and !metadata.empty?
724
+ # # metadata_create_if_not_exists(asset_id, metadata) if metadata and !metadata.empty?
725
+ # else
726
+ # asset_id = nil
727
+ # parent_folder_id = cp_result[:existing][:id_path_ary].last
728
+ # end
729
+ #
730
+ # result = cp_result.merge({ :project_id => project_id, :parent_folder_id => parent_folder_id, :asset_id => asset_id, :asset => asset })
731
+ # logger.debug { "Create Missing Path Result: #{result.inspect}" }
732
+ # return result
733
+ # end
734
+ searched_folders = cp_result[:searched_folders]
735
+
736
+ missing_path = cp_result[:missing_path]
737
+
738
+ project_name = cp_result[:check_path_ary][0]
739
+ #logger.debug "PMP: #{missing_path}"
740
+ if project_missing
741
+ logger.debug { "Missing Project - Creating Project '#{project_name}'" }
742
+ project = project_create(project_name)
743
+ raise "Error Creating Project. Response: #{project}" unless project.is_a?(Hash)
744
+
745
+ cp_result[:project] = project
746
+ project_id = project['id']
747
+ missing_path.shift
748
+ logger.debug { "Created Project '#{project_name}' - #{project_id}" }
749
+ else
750
+ project_id = existing[:id_path_ary].first
751
+ end
752
+
753
+ if searched_folders
754
+ if folder_missing
755
+ # logger.debug "FMP: #{missing_path}"
756
+
757
+ parent_folder_id = (existing[:id_path_ary].length <= 1) ? 0 : existing[:id_path_ary].last
758
+
759
+ asset_name = missing_path.pop if contains_asset
760
+
761
+ previous_missing = project_missing
762
+ missing_path.each do |folder_name|
763
+ sleep path_creation_delay if path_creation_delay and previous_missing
764
+ begin
765
+ logger.debug { "Creating folder '#{folder_name}' parent id: #{parent_folder_id} project id: #{project_id}" }
766
+ new_folder = folder_create(:name => folder_name, :project_id => project_id, :parent_id => parent_folder_id)
767
+ raise "Error Creating Folder. Response: #{new_folder}" unless new_folder.is_a?(Hash)
768
+
769
+ logger.debug { "New Folder: #{new_folder.inspect}" }
770
+ parent_folder_id = new_folder['id']
771
+ logger.debug { "Folder Created #{new_folder} - #{parent_folder_id}" }
772
+ rescue => e
773
+ raise e.prefix_message("Failed to create folder '#{folder_name}' parent id: '#{parent_folder_id}' project id: '#{project_id}'. Exception:")
774
+ end
775
+ previous_missing = true
776
+ end
777
+
778
+ else
779
+ if contains_asset and not asset_missing
780
+ parent_folder_id = existing[:id_path_ary].fetch(-2)
781
+ else
782
+ parent_folder_id = existing[:id_path_ary].last
783
+ end
784
+ end
785
+ else
786
+ parent_folder_id = 0
787
+ end
788
+
789
+ if contains_asset
790
+ additional_asset_create_params = { } unless additional_asset_create_params.is_a?(Hash)
791
+ additional_asset_create_params['folderId'] = parent_folder_id
792
+ additional_asset_create_params['projectId'] = project_id
793
+ additional_asset_create_params[:metadata] = metadata
794
+ additional_asset_create_params[:source_url] = asset_url
795
+
796
+ if asset_missing
797
+ asset = asset_create(additional_asset_create_params)
798
+ raise "Error Creating Asset: #{asset.inspect} Args: #{additional_asset_create_params.inspect}" unless success?
799
+ else
800
+ if overwrite_asset
801
+ asset_id = existing[:id_path_ary].last
802
+ begin
803
+ raise "Error Message: #{error_message}" unless asset_delete(asset_id)
804
+ rescue => e
805
+ raise e.prefix_message("Error Deleting Existing Asset. Asset ID: #{asset_id} Exception: ")
806
+ end
807
+ asset = asset_create(additional_asset_create_params)
808
+ raise "Error Creating Asset: #{asset.inspect} Args: #{additional_asset_create_params.inspect}" unless success?
809
+ end
810
+ end
811
+
812
+ additional_asset_create_params = additional_asset_create_params.delete_if { |k,v| asset[k] == v }
813
+ asset_edit_extended(asset['id'], additional_asset_create_params) unless additional_asset_create_params.empty?
814
+ # metadata_create_if_not_exists(asset['id'], metadata) if metadata and !metadata.empty?
815
+
816
+ cp_result[:asset] = asset
817
+ end
818
+ result = cp_result.merge({ :project_id => project_id, :parent_folder_id => parent_folder_id })
819
+ logger.debug { "Create Missing Path Result: #{result.inspect}" }
820
+ return result
821
+ end
822
+ alias :create_path :path_create
823
+
824
+ def path_delete(path, options = { })
825
+ raise_exception_on_error = options.fetch(:raise_exception_on_error, true)
826
+ recursive = (options.fetch(:recursive, false) === true) ? true : false
827
+ include_assets = (options.fetch(:include_assets, false) === true) ? true : false
828
+
829
+ path = path[1..-1] if path.start_with? '/' # Remove the leading slash if it is present
830
+ path_ary = path.split('/') # Turn the path into an array of names
831
+
832
+ raise ArgumentError, 'Path is empty. Nothing to do.' if path_ary.empty?
833
+
834
+ if path_ary.last == '*'
835
+ path_ary.pop
836
+
837
+ if path_ary.empty?
838
+ delete_all_projects = options.fetch(:delete_all_projects)
839
+ raise ArgumentError, 'Wildcard Project Deletion is not Enabled.' unless (delete_all_projects === true)
840
+
841
+ projects = projects_get
842
+ return projects.map { |project| path_delete(project['name'], options)}
843
+ end
844
+
845
+ delete_contents_only = true
846
+ else
847
+ delete_contents_only = options.fetch(:delete_contents_only, false)
848
+ end
849
+
850
+ result = check_path(path_ary.join('/'))
851
+ raise "Error checking path. '#{error_message}'" unless result
852
+
853
+ existing_path = result[:existing][:id_path_ary]
854
+ missing_path = result[:missing_path]
855
+
856
+ #The path was not found
857
+ raise "Path not found. Path: '#{path}' Check Path Result: #{result.inspect}" unless missing_path.empty?
858
+
859
+ id_path_ary = existing_path
860
+
861
+ project_id = id_path_ary.shift # Pull the project_id out of the beginning of the array
862
+
863
+ if id_path_ary.empty?
864
+ folder_id = 0
865
+ else
866
+ folder_id = id_path_ary.last
867
+ end
868
+
869
+ path_delete_by_id(project_id, folder_id, recursive, include_assets, delete_contents_only, options)
870
+ rescue ArgumentError, RuntimeError => e
871
+ raise e if raise_exception_on_error
872
+ return false
873
+ end
874
+
875
+ # Deletes a project's and/or folder's contents.
876
+ #
877
+ # @param [String|Integer] project_id The id of the project that you wish to delete.
878
+ # @param [String|Integer] folder_id (0) The parent_folder in the project that you would want to delete the
879
+ # contents of. Defaults to 0 which is the root folder of the project.
880
+ # @param [Boolean] recursive (false) Tells the method to recurse into any sub-folders. Usually you would want
881
+ # this to be true, but the default requires you to be explicit about wanting to delete sub-folders so the
882
+ # default is false.
883
+ # @param [Boolean] include_assets (false) Tells the method to delete any assets in any directory that it
884
+ # traverses into. Usually you would want this to be true, but the default requires you to be explicit about
885
+ # wanting to delete assets so the default is false
886
+ # @param [Boolean] delete_contents_only (true) Tells the method to not delete the parent object
887
+ # (project or folder) unless this is set to true.
888
+ # @param [Hash] options
889
+ # @option option [Boolean] :dry_run
890
+ # @option option [Boolean] :delete_assets_only Will only delete assets along the path but will leave the project
891
+ # and folders in place
892
+ def path_delete_by_id(project_id, folder_id = 0, recursive = false, include_assets = false, delete_contents_only = true, options = { })
893
+ dry_run = options.fetch(:dry_run, false)
894
+ delete_assets_only = options.fetch(:delete_assets_only, false)
895
+
896
+ raise ArgumentError, 'include_assets must be true to use the delete_assets_only option.' if delete_assets_only and !include_assets
897
+
898
+ @logger.debug { "Deleting Path By ID - Project ID: #{project_id} Folder ID: #{folder_id} Recursive: #{recursive} Include Assets: #{include_assets} Delete Contents Only: #{delete_contents_only} Options: #{options.inspect}" }
899
+
900
+ if folder_id and folder_id != 0
901
+ folders = folders_get_by_parent_id(folder_id) || [ ]
902
+ else
903
+ folders = folders_get_by_project_id(project_id) || [ ]
904
+ end
905
+ folders = [ ] unless folders.is_a?(Array)
906
+ if recursive
907
+ folders = [ ] if folders.is_a?(String)
908
+ total_folders = folders.length
909
+ folder_counter = 0
910
+ folders.delete_if do |folder|
911
+ folder_counter += 1
912
+
913
+ @logger.debug { "Deleting Contents of Folder #{folder_counter} of #{total_folders} - #{folder}" }
914
+
915
+ # Pass delete_assets_only as the delete_contents_only argument. This way if we aren't deleting assets only then
916
+ # sub-folders and assets will get deleted recursively, otherwise only assets will be deleted
917
+ path_delete_by_id(project_id, folder['id'], recursive, include_assets, delete_assets_only, options)
918
+ end
919
+ end
920
+
921
+
922
+ if include_assets
923
+ if folder_id == 0
924
+ assets = assets_get_by_project_id(:id => project_id)
925
+ else
926
+ assets = assets_get_by_folder_id(:id => folder_id)
927
+ end
928
+ assets = [ ] unless assets.is_a?(Array)
929
+ total_assets = assets.length
930
+ asset_counter = 0
931
+
932
+ # batch_execute {
933
+ # assets.each { |asset| asset_delete(asset['id']) }
934
+ # }
935
+
936
+ response = assets.map { |asset| asset_delete(asset['id']) }
937
+
938
+ # if dry_run
939
+ # assets = [ ]
940
+ # else
941
+ # assets = response.dup.delete_if { |r| %w(204 404).include?(r['httpStatus']) } unless assets.empty?
942
+ # end
943
+
944
+ assets = [ ]
945
+
946
+ # assets.delete_if { |asset|
947
+ # asset_counter += 1
948
+ #
949
+ # dry_run ? true : asset_delete(asset['id'])
950
+ # }
951
+ else
952
+ assets = [ ] # make assets.empty? pass later on. let ms throw an error if the project/folder isn't empty
953
+ end
954
+
955
+ unless delete_contents_only or delete_assets_only
956
+ if folders.empty? and assets.empty?
957
+ if folder_id === 0
958
+ @logger.debug { "Deleting Project #{project_id}" }
959
+ return ( dry_run ? true : project_delete(project_id) )
960
+ else
961
+ @logger.debug { "Deleting Folder #{folder_id}" }
962
+ return ( dry_run ? true : folder_delete(folder_id) )
963
+ end
964
+ else
965
+ return true if dry_run
966
+ warn "Assets remaining in project/folder: #{project_id}/#{folder_id} : Assets: #{assets.inspect}" unless assets.empty?
967
+ warn "Folders remaining in project/folder: #{project_id}/#{folder_id} : Folders: #{folders.inspect}" unless folders.empty?
968
+ return false
969
+ end
970
+ end
971
+
972
+ return true
973
+ end
974
+
975
+
976
+ def project_get_by_name(args = { }, options = { })
977
+ project_name = case args
978
+ when String; args
979
+ when Hash; args[:name] || args[:project_name]
980
+ end
981
+
982
+ case_sensitive = options.fetch(:case_sensitive, default_case_sensitive_search)
983
+ return_all_matches = !options.fetch(:return_first_match, false)
984
+
985
+
986
+ projects = options[:projects] || projects_get
987
+ logger.debug { "Searching Projects: #{projects.inspect}" }
988
+ return false unless projects
989
+
990
+ project_name.upcase! unless case_sensitive
991
+
992
+ # Use delete_if instead of keep_if to be ruby 1.8 compatible
993
+ projects = projects.dup.delete_if do |project|
994
+ project_name_to_test = project['name']
995
+ project_name_to_test.upcase! unless case_sensitive
996
+ no_match = (project_name_to_test != project_name)
997
+ logger.debug { "Comparing: #{project_name_to_test} #{no_match ? '!' : '='}= #{project_name}"}
998
+ return project unless no_match or return_all_matches
999
+ no_match
1000
+ end
1001
+ return nil unless return_all_matches
1002
+ projects
1003
+ end
1004
+
1005
+ def path_resolve(path, path_contains_asset = false, options = { })
1006
+ logger.debug { "Resolving Path: '#{path}' Path Contains Asset: #{path_contains_asset} Options: #{options}" }
1007
+
1008
+ return_first_matching_asset = options.fetch(:return_first_matching_asset, true)
1009
+
1010
+ id_path_ary = [ ]
1011
+ name_path_ary = [ ]
1012
+
1013
+ if path.is_a?(String)
1014
+ # Remove any leading slashes
1015
+ path = path[1..-1] while path.start_with?('/')
1016
+
1017
+ path_ary = path.split('/')
1018
+ elsif path.is_a?(Array)
1019
+ path_ary = path.dup
1020
+ else
1021
+ raise ArgumentError, "path is required to be a String or an Array. Path Class Name: #{path.class.name}"
1022
+ end
1023
+
1024
+ asset_name = path_ary.pop if path_contains_asset
1025
+
1026
+ # The first element must be the name of the project
1027
+ project_name = path_ary.shift
1028
+ raise ArgumentError, 'path must contain a project name.' unless project_name
1029
+ logger.debug { "Search for Project Name: #{project_name}" }
1030
+ project = project_get_by_name(project_name, :return_first_match => true, :projects => options[:projects])
1031
+ return {
1032
+ :name_path => '/',
1033
+ :name_path_ary => [ ],
1034
+
1035
+ :id_path => '/',
1036
+ :id_path_ary => [ ],
1037
+
1038
+ :project => nil,
1039
+ :asset => nil,
1040
+ :folders => [ ]
1041
+ } if !project or project.empty?
1042
+
1043
+ project_id = project['id']
1044
+ id_path_ary << project_id
1045
+ name_path_ary << project_name
1046
+
1047
+ parsed_folders = (project && project['folderCount'] > 0) ? resolve_folder_path(project_id, path_ary) : nil
1048
+ if parsed_folders.nil?
1049
+ asset_folder_id = 0
1050
+ folders = []
1051
+ else
1052
+ id_path_ary.concat(parsed_folders[:id_path_ary])
1053
+ name_path_ary.concat(parsed_folders[:name_path_ary])
1054
+ asset_folder_id = parsed_folders[:id_path_ary].last if path_contains_asset
1055
+ folders = parsed_folders.fetch(:folder_ary, [])
1056
+ end
1057
+
1058
+ asset = nil
1059
+ if path_contains_asset and (asset_folder_id or path_ary.length == 2)
1060
+ # The name of the attribute to search the asset name for (Valid options are :title or :filename)
1061
+ asset_name_field = options[:asset_search_field_name] || :filename
1062
+ case asset_name_field.to_s.downcase.to_sym
1063
+ when :filename, 'filename'
1064
+ asset = asset_get_by_filename(asset_name, project_id, asset_folder_id, :return_first_match => return_first_matching_asset)
1065
+ when :title, 'title'
1066
+ asset = asset_get_by_title(asset_name, project_id, asset_folder_id, :return_first_match => return_first_matching_asset)
1067
+ else
1068
+ raise ArgumentError, ":asset_name_field value is not a valid option. It must be :title or :filename. Current value: #{asset_name_field}"
1069
+ end
1070
+
1071
+ if asset
1072
+ if asset.empty?
1073
+
1074
+
1075
+ elsif asset.is_a?(Array)
1076
+ # Just add the whole array to the array
1077
+ id_path_ary << asset.map { |_asset| _asset['id'] }
1078
+ name_path_ary << asset.map { |_asset| _asset['fileName'] }
1079
+ else
1080
+ id_path_ary << asset['id']
1081
+ name_path_ary << asset['fileName']
1082
+ end
1083
+ end
1084
+ end
1085
+
1086
+ return {
1087
+ :name_path => "/#{name_path_ary.join('/')}",
1088
+ :name_path_ary => name_path_ary,
1089
+
1090
+ :id_path => "/#{id_path_ary.join('/')}",
1091
+ :id_path_ary => id_path_ary,
1092
+
1093
+ :project => project,
1094
+ :asset => asset,
1095
+ :folders => folders
1096
+ }
1097
+ end
1098
+ alias :resolve_path :path_resolve
1099
+
1100
+ # Takes a file system type path and resolves the MediaSilo id's for each of the folders of that path
1101
+ #
1102
+ # @param [Integer] project_id The id of the project the folder resides in
1103
+ # @param [String] path A directory path separated by / of folders to traverse
1104
+ # @param [Integer] parent_id The ID of the parent folder to begin the search in
1105
+ def resolve_folder_path(project_id, path, parent_id = nil)
1106
+ if path.is_a?(Array)
1107
+ path_ary = path.dup
1108
+ elsif path.is_a? String
1109
+ path = path[1..-1] while path.start_with?('/')
1110
+ path_ary = path.split('/')
1111
+ end
1112
+
1113
+ return nil if !path_ary or path_ary.empty?
1114
+
1115
+ id_path_ary = [ ]
1116
+ name_path_ary = [ ]
1117
+
1118
+ folder_name = path_ary.shift
1119
+ name_path_ary << folder_name
1120
+
1121
+ folder = folder_get_by_name(project_id, folder_name, parent_id, :return_first_match => true)
1122
+ return nil unless folder
1123
+
1124
+ folder_ary = [ folder ]
1125
+
1126
+ folder_id = folder['id']
1127
+
1128
+ id_path_ary << folder_id.to_s
1129
+
1130
+ resolved_folder_path = (folder and folder['folderCount'] > 0) ? resolve_folder_path(project_id, path_ary, folder_id) : nil
1131
+
1132
+ unless resolved_folder_path.nil?
1133
+ id_path_ary.concat(resolved_folder_path[:id_path_ary] || [ ])
1134
+ name_path_ary.concat(resolved_folder_path[:name_path_ary] || [ ])
1135
+ folder_ary.concat(resolved_folder_path[:folder_ary] || [ ])
1136
+ end
1137
+
1138
+ return {
1139
+ :id_path_ary => id_path_ary,
1140
+ :name_path_ary => name_path_ary,
1141
+ :folder_ary => folder_ary
1142
+ }
1143
+ end
1144
+
1145
+ end