vidispine 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +296 -0
- data/Rakefile +2 -0
- data/bin/vidispine +7 -0
- data/bin/vidispine-notification-handler +339 -0
- data/bin/vidispine-utilities-http-server +13 -0
- data/dist/vidispine-1.4.0.gem +0 -0
- data/lib/vidispine.rb +5 -0
- data/lib/vidispine/api/client.rb +981 -0
- data/lib/vidispine/api/client/http_client.rb +291 -0
- data/lib/vidispine/api/client/requests.rb +27 -0
- data/lib/vidispine/api/client/requests/base_request.rb +234 -0
- data/lib/vidispine/api/client/requests/collection_access_add.rb +30 -0
- data/lib/vidispine/api/client/requests/collection_access_delete.rb +26 -0
- data/lib/vidispine/api/client/requests/collection_metadata_set.rb +24 -0
- data/lib/vidispine/api/client/requests/import_placeholder.rb +36 -0
- data/lib/vidispine/api/client/requests/import_placeholder_item.rb +38 -0
- data/lib/vidispine/api/client/requests/import_sidecar_file.rb +28 -0
- data/lib/vidispine/api/client/requests/import_using_uri.rb +44 -0
- data/lib/vidispine/api/client/requests/item_access_add.rb +30 -0
- data/lib/vidispine/api/client/requests/item_access_delete.rb +26 -0
- data/lib/vidispine/api/client/requests/item_delete.rb +17 -0
- data/lib/vidispine/api/client/requests/item_export.rb +44 -0
- data/lib/vidispine/api/client/requests/item_metadata_get.rb +37 -0
- data/lib/vidispine/api/client/requests/item_metadata_set.rb +29 -0
- data/lib/vidispine/api/client/requests/item_search.rb +0 -0
- data/lib/vidispine/api/client/requests/item_transcode.rb +23 -0
- data/lib/vidispine/api/client/requests/items_search.rb +40 -0
- data/lib/vidispine/api/client/requests/search.rb +59 -0
- data/lib/vidispine/api/client/requests/storage_file_create.rb +23 -0
- data/lib/vidispine/api/client/requests/storage_file_get.rb +40 -0
- data/lib/vidispine/api/client/requests/storage_files_get.rb +42 -0
- data/lib/vidispine/api/utilities.rb +1608 -0
- data/lib/vidispine/api/utilities/cli.rb +168 -0
- data/lib/vidispine/api/utilities/http_server.rb +230 -0
- data/lib/vidispine/api/utilities/http_server/cli.rb +77 -0
- data/lib/vidispine/cli.rb +174 -0
- data/lib/vidispine/version.rb +3 -0
- data/resources/postman/Vidispine API (From WADL v4.11).postman_collection.json +47437 -0
- data/vidispine.gemspec +26 -0
- metadata +117 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
module Vidispine::API::Client::Requests
|
2
|
+
|
3
|
+
# @see http://apidoc.vidispine.com/latest/ref/item/item.html#search-items
|
4
|
+
class ItemsSearch < BaseRequest
|
5
|
+
|
6
|
+
HTTP_METHOD = :put
|
7
|
+
HTTP_PATH = '/item'
|
8
|
+
DEFAULT_PARAMETER_SEND_IN_VALUE = :matrix
|
9
|
+
|
10
|
+
PARAMETERS = [
|
11
|
+
{ :name => :result, :send_in => :query },
|
12
|
+
{ :name => :content, :send_in => :query },
|
13
|
+
|
14
|
+
|
15
|
+
:library,
|
16
|
+
:first,
|
17
|
+
:number,
|
18
|
+
:libraryId,
|
19
|
+
:autoRefresh,
|
20
|
+
:updateMode,
|
21
|
+
:updateFrequency,
|
22
|
+
|
23
|
+
{ :name => :ItemSearchDocument, :send_in => :body }
|
24
|
+
]
|
25
|
+
|
26
|
+
def body
|
27
|
+
@body ||= arguments[:ItemSearchDocument]
|
28
|
+
end
|
29
|
+
|
30
|
+
def body_as_xml
|
31
|
+
<<-XML
|
32
|
+
<ItemSearchDocument xmlns="http://xml.vidispine.com/schema/vidispine">
|
33
|
+
</ItemSearchDocument>
|
34
|
+
XML
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Vidispine::API::Client::Requests
|
2
|
+
|
3
|
+
class Search < BaseRequest
|
4
|
+
|
5
|
+
HTTP_METHOD = :put
|
6
|
+
HTTP_PATH = 'search'
|
7
|
+
|
8
|
+
PARAMETERS = [
|
9
|
+
:content,
|
10
|
+
:interval,
|
11
|
+
:field,
|
12
|
+
:group,
|
13
|
+
:language,
|
14
|
+
:samplerate,
|
15
|
+
:track,
|
16
|
+
:terse,
|
17
|
+
:include,
|
18
|
+
:type,
|
19
|
+
:tag,
|
20
|
+
:scheme,
|
21
|
+
:closedFiles,
|
22
|
+
'noauth-url',
|
23
|
+
:defaultValue,
|
24
|
+
:methodType,
|
25
|
+
:version,
|
26
|
+
:revision,
|
27
|
+
|
28
|
+
{ :name => :first, :send_in => :matrix },
|
29
|
+
|
30
|
+
{ :name => :ItemSearchDocument, :default_value => { }, :send_in => :body },
|
31
|
+
]
|
32
|
+
|
33
|
+
# {
|
34
|
+
# "field": [
|
35
|
+
# {
|
36
|
+
# "name": "portal_mf48881",
|
37
|
+
# "value": [
|
38
|
+
# {
|
39
|
+
# "value": "something"
|
40
|
+
# }
|
41
|
+
# ]
|
42
|
+
# }
|
43
|
+
# ]
|
44
|
+
# }
|
45
|
+
def body
|
46
|
+
@body ||= arguments[:ItemSearchDocument]
|
47
|
+
end
|
48
|
+
|
49
|
+
def body_as_xml
|
50
|
+
<<-XML
|
51
|
+
<ItemSearchDocument xmlns="http://xml.vidispine.com/schema/vidispine">
|
52
|
+
</ItemDocument>
|
53
|
+
XML
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Vidispine::API::Client::Requests
|
2
|
+
|
3
|
+
# @see http://apidoc.vidispine.com/4.2/ref/storage/file.html#list-files-in-storage
|
4
|
+
class StorageFileCreate < BaseRequest
|
5
|
+
|
6
|
+
HTTP_METHOD = :post
|
7
|
+
HTTP_PATH = '/storage/#{path_arguments[:storage_id]}/file'
|
8
|
+
|
9
|
+
PARAMETERS = [
|
10
|
+
# Path Parameters
|
11
|
+
{ :name => :storage_id, :required => true, :send_in => :path },
|
12
|
+
|
13
|
+
# Query Parameters
|
14
|
+
:createOnly,
|
15
|
+
:state,
|
16
|
+
|
17
|
+
# Body Parameters
|
18
|
+
{ :name => :path, :send_in => :body }
|
19
|
+
]
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Vidispine::API::Client::Requests
|
2
|
+
|
3
|
+
# Exposes two functions
|
4
|
+
# 1. Get status of file in storage
|
5
|
+
# @see http://apidoc.vidispine.com/4.2.3/ref/storage/file.html#get-status-of-file-in-storage
|
6
|
+
#
|
7
|
+
# 2. Get direct download access to file in storage
|
8
|
+
# @see http://apidoc.vidispine.com/4.2.3/ref/storage/file.html#get-direct-download-access-to-file-in-storage
|
9
|
+
class StorageFileGet < BaseRequest
|
10
|
+
|
11
|
+
HTTP_PATH = '/storage/#{path_arguments[:storage_id]}/file/#{path_arguments[:file_id]}'
|
12
|
+
|
13
|
+
PARAMETERS = [
|
14
|
+
# Path Parameters
|
15
|
+
{ :name => :storage_id, :required => true, :send_in => :path },
|
16
|
+
{ :name => :file_id, :required => true, :send_in => :path },
|
17
|
+
|
18
|
+
# Matrix Parameters
|
19
|
+
{ :name => :includeItem, :send_in => :matrix },
|
20
|
+
{ :name => :path, :send_in => :matrix },
|
21
|
+
{ :name => :uri, :send_in => :matrix },
|
22
|
+
|
23
|
+
|
24
|
+
# Query Parameters
|
25
|
+
:methodType
|
26
|
+
]
|
27
|
+
|
28
|
+
def after_process_parameters
|
29
|
+
# URI Needs to be escaped twice, so we do it once here and then again when the query is built
|
30
|
+
# @see http://apidoc.vidispine.com/4.2.6/storage/uri.html#api-calls
|
31
|
+
_uri = arguments[:uri]
|
32
|
+
arguments[:uri] = CGI.escape(_uri).gsub('+', '%20') if _uri
|
33
|
+
|
34
|
+
_path =arguments[:path]
|
35
|
+
arguments[:path] = CGI.escape(_path).gsub('+', '%20') if _path
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Vidispine::API::Client::Requests
|
2
|
+
|
3
|
+
class StorageFilesGet < BaseRequest
|
4
|
+
# @see http://apidoc.vidispine.com/4.2/ref/storage/file.html#list-files-in-storage
|
5
|
+
|
6
|
+
HTTP_PATH = '/storage/#{path_arguments[:storage_id]}/file'
|
7
|
+
|
8
|
+
PARAMETERS = [
|
9
|
+
# Path Parameters
|
10
|
+
{ :name => :storage_id, :required => true, :send_in => :path },
|
11
|
+
|
12
|
+
# Matrix Parameters
|
13
|
+
{ :name => :start, :send_in => :matrix },
|
14
|
+
{ :name => :number, :send_in => :matrix },
|
15
|
+
{ :name => :filter, :send_in => :matrix },
|
16
|
+
{ :name => :includeItem, :send_in => :matrix },
|
17
|
+
{ :name => :excludeQueued, :send_in => :matrix },
|
18
|
+
{ :name => :ignorecase, :send_in => :matrix },
|
19
|
+
{ :name => :sort, :send_in => :matrix },
|
20
|
+
{ :name => :storage, :send_in => :matrix },
|
21
|
+
|
22
|
+
# Query Parameters
|
23
|
+
:path,
|
24
|
+
:id,
|
25
|
+
:recursive,
|
26
|
+
:wildcard,
|
27
|
+
:type,
|
28
|
+
:hash,
|
29
|
+
:algorithm,
|
30
|
+
:count,
|
31
|
+
]
|
32
|
+
|
33
|
+
def after_process_parameters
|
34
|
+
# Path needs to be escaped twice, so we do it once here and then again when the query is built
|
35
|
+
# @see http://apidoc.vidispine.com/4.2.6/storage/uri.html#api-calls
|
36
|
+
# _path = arguments[:path]
|
37
|
+
# arguments[:path] = CGI.escape(_path).gsub('+', '%20') if _path
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,1608 @@
|
|
1
|
+
require 'vidispine/api/client'
|
2
|
+
require 'rexml/document'
|
3
|
+
|
4
|
+
module Vidispine
|
5
|
+
|
6
|
+
module API
|
7
|
+
|
8
|
+
class Utilities < Client
|
9
|
+
|
10
|
+
attr_accessor :default_metadata_map, :default_storage_map
|
11
|
+
|
12
|
+
def initialize(args = { })
|
13
|
+
@default_storage_map = args[:storage_map] || { }
|
14
|
+
@default_metadata_map = args[:metadata_map] || { }
|
15
|
+
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
# Converts hash keys to symbols
|
20
|
+
#
|
21
|
+
# @param [Hash] value hash
|
22
|
+
# @param [Boolean] recursive Will recurse into any values that are hashes or arrays
|
23
|
+
def symbolize_keys (value, recursive = true)
|
24
|
+
case value
|
25
|
+
when Hash
|
26
|
+
new_val = {}
|
27
|
+
value.each { |k,v|
|
28
|
+
k = (k.to_sym rescue k)
|
29
|
+
v = symbolize_keys(v, true) if recursive and (v.is_a? Hash or v.is_a? Array)
|
30
|
+
new_val[k] = v
|
31
|
+
}
|
32
|
+
return new_val
|
33
|
+
when Array
|
34
|
+
return value.map { |v| symbolize_keys(v, true) }
|
35
|
+
else
|
36
|
+
return value
|
37
|
+
end
|
38
|
+
end # symbolize_keys
|
39
|
+
|
40
|
+
# Tries to find a collection using either the collection id, name, or a file path with a collection name position.
|
41
|
+
# For use in other methods that need to perform the same type of lookup
|
42
|
+
# @param [Hash] :collection
|
43
|
+
# @param [String] :collection_id
|
44
|
+
# @param [String] :collection_name
|
45
|
+
def determine_collection(args, options = { })
|
46
|
+
collection = args[:collection] || { }
|
47
|
+
|
48
|
+
# 3 Get Collection
|
49
|
+
collection_id = args[:collection_id] || collection['id']
|
50
|
+
unless collection_id
|
51
|
+
collection_name = args[:collection_name] || args[:name]
|
52
|
+
unless collection_name
|
53
|
+
file_path_collection_name_position = args[:file_path_collection_name_position]
|
54
|
+
raise ArgumentError, ':collection_id, :collection_name, or :file_path_collection_name_position argument is required.' unless file_path_collection_name_position
|
55
|
+
|
56
|
+
file_path = args[:file_path]
|
57
|
+
raise ArgumentError, ':file_path is a required argument when using :file_path_collection_name_position' unless file_path
|
58
|
+
|
59
|
+
file_path_split = (file_path.start_with?('/') ? file_path[1..-1] : file_path).split('/')
|
60
|
+
collection_name = file_path_split[file_path_collection_name_position]
|
61
|
+
raise ArgumentError, 'Unable to determine collection name from path.' unless collection_name
|
62
|
+
logger.debug { "Using '#{collection_name}' as collection_name. File Path Array: #{file_path_split.inspect}" }
|
63
|
+
end
|
64
|
+
# Determine Collection
|
65
|
+
#collection = collection_create_if_not_exists(:collection_name => collection_name)
|
66
|
+
collection = collection_get_by_name({ :collection_name => collection_name }, options)
|
67
|
+
else
|
68
|
+
collection = collection_get(:collection_id => collection_id)
|
69
|
+
raise ArgumentError, 'Collection not found.' unless collection and collection['id']
|
70
|
+
end
|
71
|
+
collection
|
72
|
+
end
|
73
|
+
|
74
|
+
# Adds an Item to a Collection but Gives Multiple ways of Determining the Collection
|
75
|
+
# @param [Hash] :item
|
76
|
+
# @param [Hash] :collection
|
77
|
+
# @param [String] :collection_id
|
78
|
+
# @param [String] :collection_name
|
79
|
+
# @param [String] :file_path Required if using :file_path_collection_name_position
|
80
|
+
# @param [Integer] :file_path_collection_name_position
|
81
|
+
def collection_item_add_extended(args = { }, options = { })
|
82
|
+
args = symbolize_keys(args, false)
|
83
|
+
|
84
|
+
_response = { }
|
85
|
+
|
86
|
+
item = args[:item] || { }
|
87
|
+
item_id = args[:item_id] || item['id']
|
88
|
+
|
89
|
+
# # 3 Get Collection
|
90
|
+
# collection_id = args[:collection_id] || collection['id']
|
91
|
+
# unless collection_id
|
92
|
+
# collection_name = args[:collection_name]
|
93
|
+
# unless collection_name
|
94
|
+
# file_path_collection_name_position = args[:file_path_collection_name_position]
|
95
|
+
# raise ArgumentError, ':collection_id, :collection_name, or :file_path_collection_name_position argument is required.' unless file_path_collection_name_position
|
96
|
+
#
|
97
|
+
# file_path_split = (file_path.start_with?('/') ? file_path[1..-1] : file_path).split('/')
|
98
|
+
# collection_name = file_path_split[file_path_collection_name_position]
|
99
|
+
# raise ArgumentError, 'Unable to determine collection name from path.' unless collection_name
|
100
|
+
# logger.debug { "Using '#{collection_name}' as collection_name. File Path Array: #{file_path_split.inspect}" }
|
101
|
+
# end
|
102
|
+
# # Determine Collection
|
103
|
+
# collection = collection_create_if_not_exists(:collection_name => collection_name)
|
104
|
+
# collection_id = collection['id']
|
105
|
+
# else
|
106
|
+
# collection ||= collection_get(:collection_id => collection_id)
|
107
|
+
# raise ArgumentError, 'Collection not found.' unless collection
|
108
|
+
# end
|
109
|
+
collection = determine_collection(args)
|
110
|
+
_response[:collection] = collection
|
111
|
+
collection_id = collection['id']
|
112
|
+
|
113
|
+
# 5. Add Item to the Collection
|
114
|
+
logger.debug { 'Adding Item to Collection.' }
|
115
|
+
collection_object_add_response = collection_object_add(:collection_id => collection_id, :object_id => item_id)
|
116
|
+
_response[:collection_object_add] = collection_object_add_response
|
117
|
+
|
118
|
+
_response
|
119
|
+
end
|
120
|
+
|
121
|
+
# # Transforms metadata from key value to field format
|
122
|
+
# # { k1 => v1, k2 => v2} becomes [ { :name => k1, :value => [ { :value => v1 } ] }, { :name => k2, :value => [ { :value => v2 } ] } ]
|
123
|
+
# def transform_metadata_to_fields(metadata_in, options = { })
|
124
|
+
# _metadata_map = default_metadata_map.merge(options[:metadata_map] || { })
|
125
|
+
# metadata_in.map { |k,v| { :name => (_metadata_map[k] || k), :value => [ { :value => v } ] } }
|
126
|
+
# end
|
127
|
+
|
128
|
+
# Transforms metadata from key value to MetadataDocument field and group sequences
|
129
|
+
# { k1 => v1, k2 => v2 } becomes
|
130
|
+
# {
|
131
|
+
# :field => [
|
132
|
+
# { :name => map[k1][:field], :value => [ { :value => v1 } ] },
|
133
|
+
# { :name => map[k2][:field], :value => [ { :value => v2 } ] }
|
134
|
+
# ],
|
135
|
+
# :group => [ ]
|
136
|
+
# }
|
137
|
+
#
|
138
|
+
# Metadata Map Example
|
139
|
+
# metadata_map = {
|
140
|
+
# 'Campaign Title' => { :group => 'Film', :field => 'portal_mf409876' },
|
141
|
+
# 'Client' => { :group => 'Editorial and Film', :field => 'portal_mf982459' },
|
142
|
+
# 'Product' => { :group => 'Film', :field => 'portal_mf264604' },
|
143
|
+
# 'Studio Tracking ID' => { :group => 'Film', :field => 'portal_mf846239' },
|
144
|
+
# #'File Path' => { :field => 'portal_mf48881', :group => 'Film' }
|
145
|
+
# #'File Path' => { :field => 'portal_mf48881' }
|
146
|
+
# 'File Path' => 'portal_mf48881'
|
147
|
+
# }
|
148
|
+
#
|
149
|
+
# @see http://apidoc.vidispine.com/4.2.3/ref/xml-schema.html#schema-element-MetadataDocument
|
150
|
+
#
|
151
|
+
# @param [Hash] metadata_in Key value pair where the key is an alias for a vidispine metadata field name
|
152
|
+
# @param [Hash] map A mapping of metadata field name aliases to proper metadata field name and optionally
|
153
|
+
# metadata group name { 'Some Field' => { :field => 'properFieldName', :group => 'groupName' } }
|
154
|
+
# @param [Hash] options
|
155
|
+
# @option options [Hash] :default_metadata_map (default_metadata_map)
|
156
|
+
# @option options [Hash] :metadata_map
|
157
|
+
def transform_metadata_to_fields(metadata_in, map = { }, options = { })
|
158
|
+
map = (options[:default_metadata_map] || default_metadata_map).merge(map.merge(options[:metadata_map] || { }))
|
159
|
+
groups = { }
|
160
|
+
metadata_in.each do |k,v|
|
161
|
+
_map = map[k]
|
162
|
+
next unless _map
|
163
|
+
[*_map].each do |_map_|
|
164
|
+
_map_ = { :field => _map_ } if _map_.is_a?(String)
|
165
|
+
(groups[_map_[:group]] ||= { })[_map_[:field]] = v
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
_field = groups.delete(nil) { { } }.map { |fname, values| { :name => fname, :value => [*values].map { |v| { :value => v } } } }
|
170
|
+
_group = groups.map { |name, fields| { :name => name, :field => fields.map { |fname, values| { :name => fname, :value => [*values].map { |v| { :value => v } } } } } }
|
171
|
+
|
172
|
+
# metadata_out = { }
|
173
|
+
# metadata_out[:field] = _field unless _field.empty?
|
174
|
+
# metadata_out[:group] = _group unless _group.empty?
|
175
|
+
# metadata_out
|
176
|
+
{ :field => _field, :group => _group }
|
177
|
+
end
|
178
|
+
|
179
|
+
# Reads an item's metadata array and return a hash version of the metadata
|
180
|
+
#
|
181
|
+
# {
|
182
|
+
# "item": [
|
183
|
+
# {
|
184
|
+
# "metadata": {
|
185
|
+
# "revision": "VX-348,VX-766,VX-350,VX-352,VX-767,VX-353,VX-816,VX-815,VX-346",
|
186
|
+
# "group": [
|
187
|
+
# "Film"
|
188
|
+
# ],
|
189
|
+
# "timespan": [
|
190
|
+
# {
|
191
|
+
# "start": "-INF",
|
192
|
+
# "end": "+INF",
|
193
|
+
# "field": [
|
194
|
+
# {
|
195
|
+
# "name": "portal_mf778031",
|
196
|
+
# "uuid": "4150479f-b15e-475b-bc48-80ef85d3c2cf",
|
197
|
+
# "change": "VX-767",
|
198
|
+
# "user": "admin",
|
199
|
+
# "value": [
|
200
|
+
# {
|
201
|
+
# "uuid": "a7f91e7c-ffc6-4ba1-9658-3458bec886e9",
|
202
|
+
# "change": "VX-767",
|
203
|
+
# "user": "admin",
|
204
|
+
# "value": "556dd36a02a760d6bd000071",
|
205
|
+
# "timestamp": "2015-07-06T22:25:17.926+0000"
|
206
|
+
# }
|
207
|
+
# ],
|
208
|
+
# "timestamp": "2015-07-06T22:25:17.926+0000"
|
209
|
+
# },
|
210
|
+
# {
|
211
|
+
# "name": "portal_mf897662"
|
212
|
+
# },
|
213
|
+
# {
|
214
|
+
# "name": "portal_mf396149"
|
215
|
+
# }
|
216
|
+
# ],
|
217
|
+
# "group": [
|
218
|
+
#
|
219
|
+
# ]
|
220
|
+
# }
|
221
|
+
# ]
|
222
|
+
# },
|
223
|
+
# "id": "VX-84"
|
224
|
+
# }
|
225
|
+
# ]
|
226
|
+
# }
|
227
|
+
#
|
228
|
+
# @param [Hash] _response A vidispine item or hash with the item key
|
229
|
+
# @return [Hash]
|
230
|
+
def self.transform_metadata_get_response_to_hash(_response, options = { })
|
231
|
+
items = _response['item'] || _response
|
232
|
+
item = items.is_a?(Array) ? items.first : items
|
233
|
+
item_metadata = item['metadata'] || item
|
234
|
+
metadata = { }
|
235
|
+
|
236
|
+
# group = item_metadata['group'].first
|
237
|
+
timespans = item_metadata['timespan']
|
238
|
+
timespans.each do |t|
|
239
|
+
metadata.merge!(transform_metadata_group(t))
|
240
|
+
end
|
241
|
+
metadata
|
242
|
+
end
|
243
|
+
|
244
|
+
def transform_metadata_get_response_to_hash(_response, options = { })
|
245
|
+
self.class.transform_metadata_get_response_to_hash(_response, options)
|
246
|
+
end
|
247
|
+
|
248
|
+
# @param [Hash] group
|
249
|
+
# @param [Array] breadcrumbs
|
250
|
+
# @return [Hash]
|
251
|
+
def self.transform_metadata_group(group, breadcrumbs = [ ])
|
252
|
+
metadata = { }
|
253
|
+
name = group['name']
|
254
|
+
|
255
|
+
_breadcrumbs = breadcrumbs.dup << name
|
256
|
+
bc = _breadcrumbs.compact.join(':')
|
257
|
+
bc << ':' unless bc.empty?
|
258
|
+
|
259
|
+
groups = group['group']
|
260
|
+
if groups.length == 1 and (_group_name = groups.first).is_a?(String)
|
261
|
+
# group_name = _group_name.is_a?(String) ? _group_name : ''
|
262
|
+
else
|
263
|
+
# group_name = ''
|
264
|
+
groups.each do |g|
|
265
|
+
metadata.merge!(transform_metadata_group(g, _breadcrumbs))
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
fields = group['field']
|
270
|
+
fields.each do |f|
|
271
|
+
# field_name = "#{group_name}#{group_name.empty? ? '' : ':'}#{f['name']}"
|
272
|
+
field_name = "#{bc}#{f['name']}"
|
273
|
+
field_value_raw = f['value']
|
274
|
+
if field_value_raw.is_a?(Array)
|
275
|
+
field_value = field_value_raw.map { |v| v['value'] }
|
276
|
+
field_value = field_value.first if field_value.length == 1
|
277
|
+
else
|
278
|
+
field_value = field_value_raw
|
279
|
+
end
|
280
|
+
metadata[field_name] = field_value
|
281
|
+
end
|
282
|
+
metadata
|
283
|
+
end
|
284
|
+
|
285
|
+
# @param [Hash] criteria
|
286
|
+
# @return [Hash]
|
287
|
+
def build_item_search_document(criteria, options = { })
|
288
|
+
fields = criteria[:fields] || criteria
|
289
|
+
item_search_document = {
|
290
|
+
:field => fields.map { |fname, values|
|
291
|
+
if values.is_a?(Hash)
|
292
|
+
#_values = values.map { |k, values| { k => [ { :value => [*values].map { |v| { :value => v } } } ] } }.inject({}) { |hash, value| hash.merge(value) }
|
293
|
+
_values = Hash[ values.map { |k, values| [ k, [ { :value => [*values].map { |v| { :value => v } } } ] ] } ]
|
294
|
+
else
|
295
|
+
_values = { :value => [*values].map { |v| { :value => v } } }
|
296
|
+
end
|
297
|
+
{ :name => fname }.merge(_values)
|
298
|
+
}
|
299
|
+
}
|
300
|
+
item_search_document
|
301
|
+
end
|
302
|
+
|
303
|
+
# @param [Hash] metadata_in
|
304
|
+
# @param [Hash] map A hash of alias field names to vidispine field or field and groups.
|
305
|
+
# Will merge into @default_metadata_map, options[:default_metadata_map], and options[:metadata_map]
|
306
|
+
# @param [Hash] options
|
307
|
+
# @option options [Hash] :default_metadata_map Allows for overriding @default_metadata_map
|
308
|
+
# @option options [Hash] :metadata_map An option for passing the map in options
|
309
|
+
# @return [Hash]
|
310
|
+
def self.build_metadata_document(metadata_in, map = { }, options = { })
|
311
|
+
# map = (options[:default_metadata_map]).merge((options[:metadata_map] || { }).merge(map))
|
312
|
+
groups = { }
|
313
|
+
metadata_in.each do |k,v|
|
314
|
+
_map = map[k]
|
315
|
+
next unless _map
|
316
|
+
_map = [ _map ] unless _map.is_a?(Array)
|
317
|
+
_map.each do |_map_|
|
318
|
+
_map_ = { :field => _map_ } if _map_.is_a?(String)
|
319
|
+
#puts "##{_map_[:group].inspect} #{_map_.class.name} #{_map_.inspect}"
|
320
|
+
(groups[_map_[:group]] ||= { })[_map_[:field]] = v
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
map_options = map[:__build_metadata_document_options] || map['__build_metadata_document_options'] || { }
|
325
|
+
options.merge!(map_options)
|
326
|
+
parent_group_name = options[:parent_group_name]
|
327
|
+
|
328
|
+
_field = groups.delete(nil) { { } }.map { |fname, values| { :name => fname, :value => [*values].map { |v| { :value => v } } } }
|
329
|
+
_group = groups.map { |name, fields| { :name => name, :field => fields.map { |fname, values| { :name => fname, :value => [*values].map { |v| { :value => v } } } } } }
|
330
|
+
|
331
|
+
# metadata_out = { }
|
332
|
+
# metadata_out[:field] = _field unless _field.empty?
|
333
|
+
# metadata_out[:group] = _group unless _group.empty?
|
334
|
+
# metadata_out
|
335
|
+
|
336
|
+
#{ :field => _field, :group => _group }
|
337
|
+
if (_field.empty? && _group.length == 1 && (!parent_group_name || parent_group_name.empty?))
|
338
|
+
_group = _group.first
|
339
|
+
group_name = _group[:name]
|
340
|
+
group_fields = _group[:field]
|
341
|
+
return { :group => [ group_name ], :timespan => [ { :start => '-INF', :end => '+INF', :field => group_fields } ] }
|
342
|
+
end
|
343
|
+
|
344
|
+
_data_out = { :timespan => [ { :start => '-INF', :end => '+INF', :field => _field, :group => _group } ] }
|
345
|
+
_data_out[:group] ||= [ parent_group_name ] if parent_group_name
|
346
|
+
|
347
|
+
_data_out
|
348
|
+
end
|
349
|
+
|
350
|
+
def build_metadata_document(metadata_in, map = { }, options = { })
|
351
|
+
map = (options[:default_metadata_map] || default_metadata_map).merge((options[:metadata_map] || { }).merge(map))
|
352
|
+
self.class.build_metadata_document(metadata_in, map, options)
|
353
|
+
end
|
354
|
+
|
355
|
+
# @return [Array]
|
356
|
+
def build_metadata_documents(metadata_in, map = { }, options = { })
|
357
|
+
map = (options[:default_metadata_map] || default_metadata_map).merge(map.merge(options[:metadata_map] || { }))
|
358
|
+
groups = { }
|
359
|
+
metadata_in.each do |k,v|
|
360
|
+
_map = map[k]
|
361
|
+
next unless _map
|
362
|
+
_map = [ _map ] unless _map.is_a?(Array)
|
363
|
+
_map.each do |_map_|
|
364
|
+
_map_ = { :field => _map_ } if _map_.is_a?(String)
|
365
|
+
#puts "##{_map_[:group].inspect} #{_map_.class.name} #{_map_.inspect}"
|
366
|
+
(groups[_map_[:group]] ||= { })[_map_[:field]] = v
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
_field = groups.delete(nil) { { } }.map { |fname, values| { :name => fname, :value => [*values].map { |v| { :value => v } } } }
|
371
|
+
_groups = groups.map { |name, fields| { :name => name, :field => fields.map { |fname, values| { :name => fname, :value => [*values].map { |v| { :value => v } } } } } }
|
372
|
+
|
373
|
+
docs = [ ]
|
374
|
+
|
375
|
+
if !_field.empty?
|
376
|
+
docs << { :timespan => [ { :start => '-INF', :end => '+INF', :field => _field, :group => [ ] } ] }
|
377
|
+
end
|
378
|
+
|
379
|
+
_groups.each do |_group|
|
380
|
+
group_name = _group[:name]
|
381
|
+
group_fields = _group[:field]
|
382
|
+
docs << { :group => [ group_name ], :timespan => [ { :start => '-INF', :end => '+INF', :field => group_fields } ] }
|
383
|
+
end
|
384
|
+
|
385
|
+
docs
|
386
|
+
end
|
387
|
+
|
388
|
+
# @param [String|Hash] field_group
|
389
|
+
# @return [Hash]
|
390
|
+
def cantemo_metadata_field_group_map(field_group, options = { })
|
391
|
+
field_group = metadata_field_group_get(:group_name => field_group, :include_values => true, :traverse => true) if field_group.is_a?(String)
|
392
|
+
|
393
|
+
field_map = { }
|
394
|
+
group_name = field_group['schema']['name']
|
395
|
+
fields = field_group['field']
|
396
|
+
fields.each do |field|
|
397
|
+
cp_field = cantemo_metadata_field_process(field)
|
398
|
+
cp_field[:group] = group_name
|
399
|
+
cp_field[:vs_def] = field
|
400
|
+
|
401
|
+
field_map[cp_field[:label]] = cp_field
|
402
|
+
end
|
403
|
+
|
404
|
+
field_map
|
405
|
+
end
|
406
|
+
|
407
|
+
# @param [Hash] field
|
408
|
+
# @param [Hash] options
|
409
|
+
# @return [Hash]
|
410
|
+
def cantemo_metadata_field_process(field, options = { })
|
411
|
+
field_name = field['name']
|
412
|
+
field_data = field['data']
|
413
|
+
field_data = JSON.parse(field['data']) if field_data.is_a?(String) && field_data.start_with?('{')
|
414
|
+
|
415
|
+
field_extra_data = field_data.find { |fd| fd['key'] == 'extradata' } || { }
|
416
|
+
field_extra_data_value = field_extra_data['value']
|
417
|
+
field_extra_data_value = JSON.parse(field_extra_data_value) if field_extra_data_value.is_a?(String) && field_extra_data_value.start_with?('{')
|
418
|
+
return nil unless field_extra_data_value.is_a?(Hash)
|
419
|
+
|
420
|
+
field_label = field_extra_data_value['name']
|
421
|
+
|
422
|
+
cp_field_type = field_extra_data_value['type']
|
423
|
+
cp_field = {
|
424
|
+
:name => field_name,
|
425
|
+
:type => cp_field_type,
|
426
|
+
:label => field_label,
|
427
|
+
:cp_def => field_extra_data_value
|
428
|
+
}
|
429
|
+
|
430
|
+
case cp_field_type
|
431
|
+
when 'checkbox', 'dropdown'
|
432
|
+
_values = field_extra_data_value['values']
|
433
|
+
choices = Hash[ _values.map { |v| [ v['key'], v['value'] ] } ]
|
434
|
+
cp_field[:choices] = choices
|
435
|
+
when 'lookup'
|
436
|
+
field_data__values = (field_data.find { |fd| fd['key'] == '__values' } || { })['value']
|
437
|
+
if field_data__values
|
438
|
+
__values_xml = field_data__values.gsub('\"', '"')
|
439
|
+
__values_doc = REXML::Document.new(StringIO.new(__values_xml))
|
440
|
+
|
441
|
+
choices = { }
|
442
|
+
__values_doc.elements.each('SimpleMetadataDocument/field') do |e|
|
443
|
+
choices[e.elements['key'].text] = e.elements['value'].text
|
444
|
+
end
|
445
|
+
cp_field[:choices] = choices
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
cp_field
|
450
|
+
end
|
451
|
+
|
452
|
+
# Searches for a collection by name and if a match is not found then a new collection is created
|
453
|
+
# This method will only return the first match if an existing collection is found.
|
454
|
+
#
|
455
|
+
# @param [Hash] args
|
456
|
+
# @option args [String] :collection_name
|
457
|
+
# @param [Hash] options
|
458
|
+
# @option options [Boolean] :case_sensitive (true)
|
459
|
+
#
|
460
|
+
# @return [Hash]
|
461
|
+
def collection_create_if_not_exists(args = { }, options = { })
|
462
|
+
return args.map { |v| collection_create_if_not_exists(v, options) } if args.is_a?(Array)
|
463
|
+
args = args.is_a?(Hash) ? args : { :collection_name => args }
|
464
|
+
|
465
|
+
collection_name = args[:collection_name] || args[:name]
|
466
|
+
raise ArgumentError, 'collection_name is required.' unless collection_name
|
467
|
+
case_sensitive = options.fetch(:case_sensitive, true)
|
468
|
+
|
469
|
+
collection = collection_get_by_name( :collection_name => collection_name, :case_sensitive => case_sensitive )
|
470
|
+
collection_already_existed = collection && !collection.empty? ? true : false
|
471
|
+
collection ||= collection_create(collection_name)
|
472
|
+
options[:extended_response] ?
|
473
|
+
{ :collection => collection, :collection_already_existed => collection_already_existed } :
|
474
|
+
collection
|
475
|
+
end
|
476
|
+
|
477
|
+
# Searches for a collection by name
|
478
|
+
# @param [Hash] args
|
479
|
+
# @option args [String] :collection_name
|
480
|
+
# @param [Hash] options
|
481
|
+
# @option options [Boolean] :return_first_match (true)
|
482
|
+
# @option options [Boolean] :case_sensitive (true)
|
483
|
+
#
|
484
|
+
# @return [Hash|Array|nil]
|
485
|
+
def collection_get_by_name(args = { }, options = { })
|
486
|
+
return collection_create_if_not_exists(args, options) if options[:collection_create_if_not_exists]
|
487
|
+
args = args.is_a?(Hash) ? args : { :collection_name => args }
|
488
|
+
|
489
|
+
collection_name = args[:collection_name] || args[:name]
|
490
|
+
return_first_match = options.fetch(:return_first_match, true)
|
491
|
+
|
492
|
+
unless collection_name
|
493
|
+
raise ArgumentError, 'collection_name is required.'
|
494
|
+
end
|
495
|
+
|
496
|
+
# collections = ( (collections_get || { })['collection'] || [ ] )
|
497
|
+
|
498
|
+
first = 1
|
499
|
+
limit = 1000
|
500
|
+
collections = [ ]
|
501
|
+
loop do
|
502
|
+
r = collections_get(:first => first, :number => limit)
|
503
|
+
_collections = r['collection']
|
504
|
+
break if _collections.empty?
|
505
|
+
collections.concat _collections
|
506
|
+
first += _collections.length
|
507
|
+
end
|
508
|
+
|
509
|
+
comparison_method, comparison_value = options.fetch(:case_sensitive, true) ? [ :eql?, true ] : [ :casecmp, 0 ]
|
510
|
+
collections_search_method = return_first_match ? :find : :select
|
511
|
+
collections.send(collections_search_method) { |c| c['name'].send(comparison_method, collection_name) == comparison_value }
|
512
|
+
end
|
513
|
+
|
514
|
+
# Adds a file using the files path
|
515
|
+
#
|
516
|
+
# @param [Hash] args
|
517
|
+
# @option args [String] :file_path
|
518
|
+
# @option args [Hash|null] :storage_path_map
|
519
|
+
# @option args [String] :storage_method_type ('file')
|
520
|
+
# @option args [Hash] :metadata ({})
|
521
|
+
# @option args [Hash] :metadata_map ({})
|
522
|
+
# @option args [Hash] :file
|
523
|
+
# @option args [String] :file_id
|
524
|
+
# @option args [Boolean] :create_thumbnails (true)
|
525
|
+
# @option args [Integer|false] :create_posters (3)
|
526
|
+
#
|
527
|
+
# @param [Hash] options
|
528
|
+
# @option options [Boolean] :add_item_to_collection
|
529
|
+
# @option options [Boolean] :wait_for_transcode_job (false)
|
530
|
+
# @option options [Boolean] :skip_transcode_if_shape_with_tag_exists (true)
|
531
|
+
#
|
532
|
+
# @return [Hash]
|
533
|
+
def item_add_using_file_path(args = { }, options = { })
|
534
|
+
args = symbolize_keys(args, false)
|
535
|
+
_response = { }
|
536
|
+
|
537
|
+
# 1. Receive a File Path
|
538
|
+
file_path = args[:file_path]
|
539
|
+
raise ArgumentError, ':file_path is a required argument.' unless file_path
|
540
|
+
|
541
|
+
# 2. Determine Storage ID
|
542
|
+
storage_method = args[:storage_method] || 'file'
|
543
|
+
storage_path_map = args[:storage_path_map]
|
544
|
+
storage_path_map = storage_file_path_map_create(:storage_method => storage_method) unless storage_path_map and !storage_path_map.empty?
|
545
|
+
|
546
|
+
storage_id = args[:storage_id]
|
547
|
+
if storage_id
|
548
|
+
volume_path, storage = storage_path_map.find { |_, id| id == storage_id }
|
549
|
+
raise "Unable to find match in storage path map for '#{storage_id}'. Storage Map: #{storage_path_map.inspect}" unless volume_path
|
550
|
+
else
|
551
|
+
volume_path, storage = storage_path_map.find { |path, _| file_path.start_with?(path) }
|
552
|
+
raise "Unable to find match in storage path map for '#{file_path}'. Storage Map: #{storage_path_map.inspect}" unless volume_path
|
553
|
+
end
|
554
|
+
|
555
|
+
|
556
|
+
file_path_relative_to_storage_path = file_path.sub(volume_path, '')
|
557
|
+
logger.debug { "File Path Relative to Storage Path: #{file_path_relative_to_storage_path}" }
|
558
|
+
|
559
|
+
storage = storage_get(:id => storage) if storage.is_a?(String)
|
560
|
+
_response[:storage] = storage
|
561
|
+
storage_id = storage['id']
|
562
|
+
raise "Error Retrieving Storage Record. Storage: #{storage.inspect}" unless storage_id
|
563
|
+
|
564
|
+
# The URI Lookup part was commented out as it should be handled by the storage_file_path_map_create
|
565
|
+
# The method type of the URI to lookup
|
566
|
+
# storage_method_type = args[:storage_method_type] ||= 'file'
|
567
|
+
#
|
568
|
+
# storage_uri_method = "#{storage_method_type}:"
|
569
|
+
# storage_uri_raw = (storage['method'].find { |v| v['uri'].start_with?(storage_uri_method) } || { })['uri'] rescue nil
|
570
|
+
# raise "Error Getting URI from Storage Method. Storage: #{storage.inspect}" unless storage_uri_raw
|
571
|
+
# storage_uri = URI.parse(storage_uri_raw)
|
572
|
+
#
|
573
|
+
# vidispine_file_path = File.join(storage_uri.path, file_path_relative_to_storage_path)
|
574
|
+
|
575
|
+
vidispine_file_path = File.join(volume_path, file_path_relative_to_storage_path)
|
576
|
+
logger.debug { "Vidispine File Path: '#{vidispine_file_path}'" }
|
577
|
+
_response[:vidispine_file_path] = vidispine_file_path
|
578
|
+
|
579
|
+
_metadata = args[:metadata] || { }
|
580
|
+
_metadata_map = args[:metadata_map] || { }
|
581
|
+
|
582
|
+
# map metadata assuming 1 value per field
|
583
|
+
#_metadata_as_fields = transform_metadata_to_fields(_metadata, _metadata_map, options)
|
584
|
+
#metadata_document = build_metadata_document(_metadata, _metadata_map, options)
|
585
|
+
metadata_documents = build_metadata_documents(_metadata, _metadata_map, options)
|
586
|
+
metadata_document = metadata_documents.shift || { }
|
587
|
+
|
588
|
+
# Allow the file to be passed in
|
589
|
+
file = args[:file]
|
590
|
+
unless file
|
591
|
+
file_id = args[:file_id]
|
592
|
+
file = { 'id' => file_id }
|
593
|
+
end
|
594
|
+
|
595
|
+
if file and !file['item']
|
596
|
+
# If the passed file doesn't have an item then requery to verify that the item is absent
|
597
|
+
storage_file_get_response = storage_file_get(:storage_id => storage_id, :file_id => file_id, :include_item => true)
|
598
|
+
|
599
|
+
raise "Error Getting Storage File. '#{storage_file_get_response.inspect}'" unless storage_file_get_response and storage_file_get_response['id']
|
600
|
+
_response[:storage_file_get_response] = storage_file_get_response
|
601
|
+
|
602
|
+
file = storage_file_get_response
|
603
|
+
else
|
604
|
+
storage_file_get_or_create_response = storage_file_get_or_create(storage_id, file_path_relative_to_storage_path, :extended_response => true)
|
605
|
+
_response[:storage_file_get_or_create_response] = storage_file_get_or_create_response
|
606
|
+
file = storage_file_get_or_create_response[:file]
|
607
|
+
file_found = storage_file_get_or_create_response[:file_already_existed]
|
608
|
+
end
|
609
|
+
|
610
|
+
if file
|
611
|
+
_response[:item] = item = file['item']
|
612
|
+
file_found = true
|
613
|
+
end
|
614
|
+
|
615
|
+
_response[:file_already_existed] = file_found
|
616
|
+
_response[:item_already_existed] = !!item
|
617
|
+
return _response if item
|
618
|
+
|
619
|
+
file_id = file['id']
|
620
|
+
|
621
|
+
unless item
|
622
|
+
# 4.2 Create a Placeholder
|
623
|
+
logger.debug { 'Creating Placeholder.' }
|
624
|
+
#placeholder_args = args[:placeholder_args] ||= { :container => 1, :video => 1, :metadata_document => { :group => [ 'Film' ], :timespan => [ { :field => [ { :name => metadata_file_path_field_id, :value => [ { :value => vidispine_file_path } ] } ], :start => '-INF', :end => '+INF' } ] } }
|
625
|
+
#placeholder_args = args[:placeholder_args] ||= { :container => 1, :video => 1, :metadata_document => { :timespan => [ { :start => '-INF', :end => '+INF' }.merge(_metadata_as_fields) ] } }
|
626
|
+
#placeholder_args = args[:placeholder_args] ||= { :container => 1, :metadata_document => { :timespan => [ { :start => '-INF', :end => '+INF' }.merge(_metadata_as_fields) ] } }
|
627
|
+
placeholder_args = args[:placeholder_args] ||= { :container => 1, :metadata_document => metadata_document }
|
628
|
+
_response[:item] = item = import_placeholder(placeholder_args)
|
629
|
+
end
|
630
|
+
item_id = item['id']
|
631
|
+
shape = item['shape']
|
632
|
+
|
633
|
+
raise "Error Creating Placeholder: #{item.inspect}" unless item_id
|
634
|
+
|
635
|
+
# Add any additional metadata (Vidispine will only take one group at a time)
|
636
|
+
metadata_documents.each do |metadata_document|
|
637
|
+
item_metadata_set(:item_id => item_id, :metadata_document => metadata_document)
|
638
|
+
end
|
639
|
+
|
640
|
+
if options[:add_item_to_collection]
|
641
|
+
logger.debug { 'Determining Collection to Add the Item to.' }
|
642
|
+
collection = determine_collection(args, options)
|
643
|
+
_response[:collection] = collection
|
644
|
+
collection_id = collection['id']
|
645
|
+
|
646
|
+
logger.debug { 'Adding Item to Collection.' }
|
647
|
+
collection_object_add_response = collection_object_add(:collection_id => collection_id, :object_id => item_id)
|
648
|
+
_response[:collection_object_add] = collection_object_add_response
|
649
|
+
end
|
650
|
+
|
651
|
+
unless shape
|
652
|
+
# 6. Add the file as the original shape
|
653
|
+
logger.debug { 'Adding the file as the Original Shape.' }
|
654
|
+
item_shape_import_response = item_shape_import(:item_id => item_id, :file_id => file_id, :tag => 'original')
|
655
|
+
_response[:item_shape_import] = item_shape_import_response
|
656
|
+
|
657
|
+
job_id = item_shape_import_response['jobId']
|
658
|
+
unless job_id
|
659
|
+
invalid_input = item_shape_import_response['invalidInput']
|
660
|
+
if invalid_input
|
661
|
+
explanation = invalid_input['explanation']
|
662
|
+
job_id = $1 if explanation.match(/.*\[(.*)\]$/)
|
663
|
+
end
|
664
|
+
end
|
665
|
+
raise "Error Creating Item Shape Import Job. Response: #{item_shape_import_response.inspect}" unless job_id
|
666
|
+
|
667
|
+
job_monitor_response = wait_for_job_completion(:job_id => job_id) { |env|
|
668
|
+
logger.debug { "Waiting for Item Shape Import Job to Complete. Time Elapsed: #{Time.now - env[:time_started]} seconds" }
|
669
|
+
}
|
670
|
+
last_response = job_monitor_response[:last_response]
|
671
|
+
raise "Error Adding file As Original Shape. Response: #{last_response.inspect}" unless last_response['status'] == 'FINISHED'
|
672
|
+
|
673
|
+
# 7. Generate the Transcode of the item
|
674
|
+
transcode_tag = args.fetch(:transcode_tag, 'lowres')
|
675
|
+
if transcode_tag and !transcode_tag.empty? and transcode_tag.to_s.downcase != 'false'
|
676
|
+
wait_for_transcode_job = options[:wait_for_transcode_job]
|
677
|
+
skip_transcode_if_shape_with_tag_exists = options.fetch(:skip_transcode_if_shape_with_tag_exists, true)
|
678
|
+
[*transcode_tag].each do |_transcode_tag|
|
679
|
+
transcode_response = item_transcode_shape({
|
680
|
+
:item_id => item_id,
|
681
|
+
:transcode_tag => _transcode_tag
|
682
|
+
},
|
683
|
+
{
|
684
|
+
:wait_for_transcode_job => wait_for_transcode_job,
|
685
|
+
:skip_if_shape_with_tag_exists => skip_transcode_if_shape_with_tag_exists
|
686
|
+
})
|
687
|
+
(_response[:transcode] ||= { })[transcode_tag] = transcode_response
|
688
|
+
|
689
|
+
# each transcode_tag
|
690
|
+
end
|
691
|
+
|
692
|
+
# if transcode_tag
|
693
|
+
end
|
694
|
+
|
695
|
+
# 8. Generate the Thumbnails and Poster Frame
|
696
|
+
create_thumbnails = args.fetch(:create_thumbnails, true)
|
697
|
+
create_posters = args.fetch(:create_posters, 3)
|
698
|
+
if (create_thumbnails or create_posters)
|
699
|
+
logger.debug { 'Generating Thumbnails(s) and Poster Frame.' }
|
700
|
+
args_out = { :item_id => item_id }
|
701
|
+
args_out[:create_thumbnails] = create_thumbnails if create_thumbnails
|
702
|
+
args_out[:create_posters] = create_posters if create_posters
|
703
|
+
item_thumbnail_response = item_thumbnail(args_out)
|
704
|
+
_response[:item_thumbnail] = item_thumbnail_response
|
705
|
+
end
|
706
|
+
end
|
707
|
+
|
708
|
+
_response
|
709
|
+
end
|
710
|
+
|
711
|
+
# Add an item to the system using file path metadata field as the key
|
712
|
+
# 1. Search for pre existing asset
|
713
|
+
# 2. Create a placeholder with metadata (if asset doesn't exist)
|
714
|
+
# 3. Create an original shape.
|
715
|
+
# 4. Poll the Job status of the shape creation
|
716
|
+
# 5. Trigger the Transcode of the Proxy, thumbnails
|
717
|
+
# 6. Trigger the Transcode of the thumbnail.
|
718
|
+
# 7. Trigger the Transcode of the poster frame
|
719
|
+
# This was an early experiment
|
720
|
+
def item_add_using_file_path_metadata(args = { }, options = { })
|
721
|
+
args = symbolize_keys(args, false)
|
722
|
+
_response = { }
|
723
|
+
|
724
|
+
# 1. Receive a File Path
|
725
|
+
file_path = args[:file_path]
|
726
|
+
raise ArgumentError, ':file_path is a required argument.' unless file_path
|
727
|
+
|
728
|
+
metadata_file_path_field_id = args[:metadata_file_path_field_id]
|
729
|
+
raise ArgumentError, ':metadata_file_path_field_id is a required argument.' unless metadata_file_path_field_id
|
730
|
+
|
731
|
+
# 2. Determine Storage ID
|
732
|
+
storage_path_map = args[:storage_path_map]
|
733
|
+
raise ArgumentError, ':storage_path_map is a required argument.' unless storage_path_map
|
734
|
+
|
735
|
+
# Make sure the keys are strings
|
736
|
+
storage_path_map = Hash[storage_path_map.map { |k,v| [k.to_s, v] }] if storage_path_map.is_a?(Hash)
|
737
|
+
|
738
|
+
volume_path, storage = storage_path_map.find { |path, _| file_path.start_with?(path) }
|
739
|
+
raise "Unable to find match in storage path map for '#{file_path}'. Storage Map: #{storage_path_map.inspect}" unless volume_path
|
740
|
+
|
741
|
+
file_path_relative_to_storage_path = file_path.sub(volume_path, '')
|
742
|
+
logger.debug { "File Path Relative to Storage Path: #{file_path_relative_to_storage_path}" }
|
743
|
+
|
744
|
+
storage = storage_get(:id => storage) if storage.is_a?(String)
|
745
|
+
_response[:storage] = storage
|
746
|
+
raise "Error Retrieving Storage Record. Storage Id: #{storage.inspect}" unless storage
|
747
|
+
|
748
|
+
storage_id = storage['id']
|
749
|
+
storage_uri_raw = storage['method'].first['uri']
|
750
|
+
storage_uri = URI.parse(storage_uri_raw)
|
751
|
+
|
752
|
+
vidispine_file_path = File.join(storage_uri.path, file_path_relative_to_storage_path)
|
753
|
+
logger.debug { "Vidispine File Path: '#{vidispine_file_path}'" }
|
754
|
+
_response[:vidispine_file_path] = vidispine_file_path
|
755
|
+
|
756
|
+
_metadata = args[:metadata] || { }
|
757
|
+
_metadata[metadata_file_path_field_id] ||= vidispine_file_path
|
758
|
+
|
759
|
+
_metadata_map = args[:metadata_map] || { }
|
760
|
+
|
761
|
+
# map metadata assuming 1 value per field
|
762
|
+
_metadata_as_fields = transform_metadata_to_fields(_metadata, _metadata_map, options)
|
763
|
+
|
764
|
+
# 4.1 Search for Item using File Path
|
765
|
+
search_response = search(:content => 'metadata', :item_search_document => { :field => [ { :name => metadata_file_path_field_id, :value => [ { :value => vidispine_file_path } ] } ] } ) || { 'entry' => [ ] }
|
766
|
+
_response[:search] = search_response
|
767
|
+
|
768
|
+
item = (search_response['entry'].first || { })['item']
|
769
|
+
unless item
|
770
|
+
# If the item wasn't found then get the file id for the file
|
771
|
+
# 4.1 Search for the storage file record
|
772
|
+
storage_file_get_response = storage_files_get(:storage_id => storage_id, :path => file_path_relative_to_storage_path) || { 'file' => [ ] }
|
773
|
+
raise "Error Getting Storage File. '#{response.inspect}'" unless storage_file_get_response and storage_file_get_response['id']
|
774
|
+
file = storage_file_get_response['file'].first
|
775
|
+
_response[:storage_file_get_response] = storage_file_get_response
|
776
|
+
|
777
|
+
unless file
|
778
|
+
# 4.1.1 Create the storage file record if it does not exist
|
779
|
+
file = storage_file_create_response = storage_file_create(:storage_id => storage_id, :path => file_path_relative_to_storage_path, :state => 'CLOSED')
|
780
|
+
raise "Error Creating File on Storage. Response: #{response}" unless file
|
781
|
+
_response[:storage_file_create_response] = storage_file_create_response
|
782
|
+
end
|
783
|
+
|
784
|
+
# 4.2 Create a Placeholder
|
785
|
+
logger.debug { 'Creating Placeholder.' }
|
786
|
+
#placeholder_args = args[:placeholder_args] ||= { :container => 1, :video => 1, :metadata_document => { :group => [ 'Film' ], :timespan => [ { :field => [ { :name => metadata_file_path_field_id, :value => [ { :value => vidispine_file_path } ] } ], :start => '-INF', :end => '+INF' } ] } }
|
787
|
+
placeholder_args = args[:placeholder_args] ||= { :container => 1, :video => 1, :metadata_document => { :timespan => [ { :start => '-INF', :end => '+INF' }.merge(_metadata_as_fields) ] } }
|
788
|
+
item = placeholder = import_placeholder(placeholder_args)
|
789
|
+
_response[:placeholder] = placeholder
|
790
|
+
end
|
791
|
+
_response[:item] = item
|
792
|
+
item_id = item['id']
|
793
|
+
|
794
|
+
if options[:add_item_to_collection]
|
795
|
+
logger.debug { 'Determining Collection to Add the Item to.' }
|
796
|
+
collection = determine_collection(args, options)
|
797
|
+
_response[:collection] = collection
|
798
|
+
collection_id = collection['id']
|
799
|
+
|
800
|
+
logger.debug { 'Adding Item to Collection.' }
|
801
|
+
collection_object_add_response = collection_object_add(:collection_id => collection_id, :object_id => item_id)
|
802
|
+
_response[:collection_object_add] = collection_object_add_response
|
803
|
+
end
|
804
|
+
|
805
|
+
# Item was already in the system so exit here
|
806
|
+
return _response unless file
|
807
|
+
|
808
|
+
file_id = file['id']
|
809
|
+
raise "File Id Not Found. #{file.inspect}" unless file_id
|
810
|
+
|
811
|
+
# 6. Add the file as the original shape
|
812
|
+
logger.debug { 'Adding the file as the Original Shape.' }
|
813
|
+
item_shape_import_response = item_shape_import(:item_id => item_id, :file_id => file_id, :tag => 'original')
|
814
|
+
_response[:item_shape_import] = item_shape_import_response
|
815
|
+
|
816
|
+
job_id = item_shape_import_response['jobId']
|
817
|
+
job_monitor_response = wait_for_job_completion(:job_id => job_id) { |env|
|
818
|
+
logger.debug { "Waiting for Item Shape Import Job to Complete. Time Elapsed: #{Time.now - env[:time_started]} seconds" }
|
819
|
+
}
|
820
|
+
last_response = job_monitor_response[:last_response]
|
821
|
+
raise "Error Adding file As Original Shape. Response: #{last_response.inspect}" unless last_response['status'] == 'FINISHED'
|
822
|
+
|
823
|
+
# 7. Generate the Transcode of the item
|
824
|
+
transcode_tag = args[:transcode_tag] || 'lowres'
|
825
|
+
logger.debug { 'Generating Transcode of the Item.' }
|
826
|
+
item_transcode_response = item_transcode(:item_id => item_id, :tag => transcode_tag)
|
827
|
+
_response[:item_transcode] = item_transcode_response
|
828
|
+
|
829
|
+
# 8. Generate the Thumbnails and Poster Frame
|
830
|
+
create_thumbnails = args.fetch(:create_thumbnails, true)
|
831
|
+
create_posters = args[:create_posters] || 3
|
832
|
+
logger.debug { 'Generating Thumbnails(s) and Poster Frame.' }
|
833
|
+
item_thumbnail_response = item_thumbnail(:item_id => item_id, :createThumbnails => create_thumbnails, :createPosters => create_posters)
|
834
|
+
_response[:item_thumbnail] = item_thumbnail_response
|
835
|
+
|
836
|
+
_response
|
837
|
+
end
|
838
|
+
|
839
|
+
def item_export_extended(args = { }, options = { })
|
840
|
+
logger.debug { "#{__method__} Args: #{args.inspect} Opts: #{options.inspect}" }
|
841
|
+
_options = options.dup
|
842
|
+
wait_for_job_completion = _options.delete(:wait_for_job_completion) { }
|
843
|
+
item_export_response = item_export(args, _options)
|
844
|
+
|
845
|
+
if wait_for_job_completion
|
846
|
+
job_id = item_export_response['jobId']
|
847
|
+
job_monitor_callback = options[:job_monitor_callback_function]
|
848
|
+
job_monitor_response = wait_for_job_completion(:job_id => job_id) do |env|
|
849
|
+
logger.debug { "Waiting for '#{args.inspect}' Export Job to Complete. Time Elapsed: #{Time.now - env[:time_started]} seconds" }
|
850
|
+
job_monitor_callback.call(env) if job_monitor_callback
|
851
|
+
end
|
852
|
+
|
853
|
+
last_response = job_monitor_response[:last_response]
|
854
|
+
# if last_response['status'] == 'FINISHED'
|
855
|
+
# data = last_response['data']
|
856
|
+
# data = Hash[ data.map { |d| [ d['key'], d['value'] ] } ]
|
857
|
+
# end
|
858
|
+
|
859
|
+
# if wait_for_transcode_job
|
860
|
+
end
|
861
|
+
|
862
|
+
last_response || item_export_response
|
863
|
+
end
|
864
|
+
|
865
|
+
|
866
|
+
def item_shape_add_using_file_path(args = { }, options = { })
|
867
|
+
logger.debug { "#{__method__}:#{args.inspect}" }
|
868
|
+
_response = { }
|
869
|
+
|
870
|
+
storage_path_map = args[:storage_path_map]
|
871
|
+
|
872
|
+
item = args[:item] || { }
|
873
|
+
item_id = args[:item_id] || item['id']
|
874
|
+
|
875
|
+
tag = args[:tag]
|
876
|
+
|
877
|
+
file = args[:file] || { }
|
878
|
+
file_id = args[:file_id] || file['id']
|
879
|
+
|
880
|
+
unless file_id
|
881
|
+
|
882
|
+
storage = args[:storage] || { }
|
883
|
+
storage_id = args[:storage_id] || storage['id']
|
884
|
+
|
885
|
+
file_path = args[:file_path]
|
886
|
+
file_path_relative_to_storage_path = args[:relative_file_path]
|
887
|
+
|
888
|
+
unless file_path_relative_to_storage_path
|
889
|
+
if storage_id
|
890
|
+
# Process file path using storage information
|
891
|
+
else
|
892
|
+
process_file_path_response = process_file_path_using_storage_map(file_path, storage_path_map)
|
893
|
+
end
|
894
|
+
|
895
|
+
file_path_relative_to_storage_path = process_file_path_response[:relative_file_path]
|
896
|
+
storage_id ||= process_file_path_response[:storage_id]
|
897
|
+
end
|
898
|
+
|
899
|
+
file = storage_file_get_or_create(storage_id, file_path_relative_to_storage_path)
|
900
|
+
|
901
|
+
file_id = file['id']
|
902
|
+
raise "File Error: #{file} Response: #{_response}" unless file_id
|
903
|
+
end
|
904
|
+
|
905
|
+
item_shape_import_args = { :item_id => item_id, :file_id => file_id, :tag => tag }
|
906
|
+
item_shape_import(item_shape_import_args)
|
907
|
+
end
|
908
|
+
alias :item_add_shape_using_file_path :item_shape_add_using_file_path
|
909
|
+
|
910
|
+
#
|
911
|
+
# @param [Hash] args
|
912
|
+
# @param [Hash] options
|
913
|
+
def item_shapes_get_extended(args = { }, options = { })
|
914
|
+
item_id = args[:item_id]
|
915
|
+
tag = args[:tag]
|
916
|
+
return_as_hash = options.fetch(:return_as_hash, false)
|
917
|
+
|
918
|
+
if return_as_hash
|
919
|
+
key_by_field = options[:hash_key] || 'id'
|
920
|
+
end
|
921
|
+
|
922
|
+
shapes_response = item_shapes_get(:item_id => item_id, :tag => tag)
|
923
|
+
|
924
|
+
shape_ids = shapes_response['uri'] || [ ]
|
925
|
+
shapes = [ ]
|
926
|
+
shape_ids.each do |shape_id|
|
927
|
+
shape = item_shape_get(:item_id => item_id, :shape_id => shape_id)
|
928
|
+
shape.dup.each do |k, v|
|
929
|
+
shape[k] = v.first if v.is_a?(Array) and v.length == 1 and !['metadata'].include?(k)
|
930
|
+
end
|
931
|
+
shapes << shape
|
932
|
+
end
|
933
|
+
|
934
|
+
shapes_formatted = return_as_hash ? Hash[ shapes.map { |v| [ v[key_by_field], v ] } ] : shapes
|
935
|
+
|
936
|
+
shapes_response['shapes'] = shapes_formatted
|
937
|
+
shapes_response
|
938
|
+
end
|
939
|
+
|
940
|
+
# @param [Hash] args
|
941
|
+
# @param [Hash] options
|
942
|
+
# @option options [Boolean] :skip_if_shape_with_tag_exists
|
943
|
+
def item_transcode_shape(args = { }, options = { })
|
944
|
+
_response = { }
|
945
|
+
item_id = args[:item_id]
|
946
|
+
transcode_tag = args[:tag] || args[:transcode_tag] || 'lowres'
|
947
|
+
skip_if_tag_exists = options.fetch(:skip_if_shape_with_tag_exists, false)
|
948
|
+
|
949
|
+
if skip_if_tag_exists
|
950
|
+
item_shapes_response = item_shapes_get(:item_id => item_id, :tag => transcode_tag)
|
951
|
+
shape_ids = item_shapes_response['uri'] || [ ]
|
952
|
+
proxy_shape_id = shape_ids.last
|
953
|
+
_response[:tag_existed_on_shape] = !!proxy_shape_id
|
954
|
+
end
|
955
|
+
|
956
|
+
unless proxy_shape_id
|
957
|
+
logger.debug { "Generating Transcode of the Item. Tag: '#{transcode_tag}'" }
|
958
|
+
item_transcode_response = item_transcode(:item_id => item_id, :tag => transcode_tag)
|
959
|
+
_response[:item_transcode] = item_transcode_response
|
960
|
+
|
961
|
+
job_id = item_transcode_response['jobId']
|
962
|
+
_response[:job_id] = job_id
|
963
|
+
if options[:wait_for_transcode_job]
|
964
|
+
job_monitor_callback = options[:job_monitor_callback_function]
|
965
|
+
job_monitor_response = wait_for_job_completion(:job_id => job_id) do |env|
|
966
|
+
logger.debug { "Waiting for '#{transcode_tag}' Transcode Job to Complete. Time Elapsed: #{Time.now - env[:time_started]} seconds" }
|
967
|
+
job_monitor_callback.call(env) if job_monitor_callback
|
968
|
+
end
|
969
|
+
|
970
|
+
last_response = job_monitor_response[:last_response]
|
971
|
+
if last_response['status'] == 'FINISHED'
|
972
|
+
data = last_response['data']
|
973
|
+
data = Hash[ data.map { |d| [ d['key'], d['value'] ] } ]
|
974
|
+
|
975
|
+
proxy_shape_ids = data['shapeIds']
|
976
|
+
proxy_shape_id = proxy_shape_ids
|
977
|
+
end
|
978
|
+
|
979
|
+
# if wait_for_transcode_job
|
980
|
+
end
|
981
|
+
|
982
|
+
end
|
983
|
+
|
984
|
+
if proxy_shape_id
|
985
|
+
item_shape_files = item_shape_files_get(:item_id => item_id, :shape_id => proxy_shape_id)
|
986
|
+
proxy_file = (((item_shape_files || { })['file'] || [ ]).first || { })
|
987
|
+
proxy_file_uri = (proxy_file['uri'] || [ ]).first
|
988
|
+
_response[:file] = proxy_file
|
989
|
+
_response[:shape_id] = proxy_shape_id
|
990
|
+
_response[:file_uri] = proxy_file_uri
|
991
|
+
_response[:file_path] = URI.decode(URI(proxy_file_uri).path)
|
992
|
+
end
|
993
|
+
|
994
|
+
_response
|
995
|
+
end
|
996
|
+
|
997
|
+
# @param [Hash] args
|
998
|
+
# @option args [String] :file_path (Required)
|
999
|
+
# @option args [String] :metadata_file_path_field_id (Required)
|
1000
|
+
# @option args [Hash] :storage_path_map (Required)
|
1001
|
+
# @option args [String] :collection_id Required if :collection_name or :file_path_collection_name_position is not set
|
1002
|
+
# @option args [String] :collection_name Required if :collection_id or :file_path_collection_name_position is not set
|
1003
|
+
# @option args [Integer] :file_path_collection_name_position Required if :collection_id or :collection_name is not set
|
1004
|
+
# @option args [Hash] :placeholder_args ({ :container => 1, :video => 1 })
|
1005
|
+
def collection_file_add_using_path(args = { }, options = { })
|
1006
|
+
args = symbolize_keys(args, false)
|
1007
|
+
_response = { }
|
1008
|
+
|
1009
|
+
# 1. Receive a File Path
|
1010
|
+
file_path = args[:file_path]
|
1011
|
+
raise ArgumentError, ':file_path is a required argument.' unless file_path
|
1012
|
+
|
1013
|
+
metadata_file_path_field_id = args[:metadata_file_path_field_id]
|
1014
|
+
raise ArgumentError, ':metadata_file_path_field_id is a required argument.' unless metadata_file_path_field_id
|
1015
|
+
|
1016
|
+
# 2. Determine Storage ID
|
1017
|
+
storage_path_map = args[:storage_path_map] || args[:storage_map]
|
1018
|
+
raise ArgumentError, ':storage_path_map is a required argument.' unless storage_path_map
|
1019
|
+
|
1020
|
+
# Make sure the keys are strings
|
1021
|
+
storage_path_map = Hash[storage_path_map.map { |k,v| [k.to_s, v] }] if storage_path_map.is_a?(Hash)
|
1022
|
+
|
1023
|
+
volume_path, storage = storage_path_map.find { |path, _| file_path.start_with?(path) }
|
1024
|
+
raise "Unable to find match in storage path map for '#{file_path}'. Storage Map: #{storage_path_map.inspect}" unless volume_path
|
1025
|
+
|
1026
|
+
file_path_relative_to_storage_path = file_path.sub(volume_path, '')
|
1027
|
+
logger.debug { "File Path Relative to Storage Path: #{file_path_relative_to_storage_path}" }
|
1028
|
+
|
1029
|
+
storage = storage_get(:id => storage) if storage.is_a?(String)
|
1030
|
+
_response[:storage] = storage
|
1031
|
+
raise 'Error Retrieving Storage Record. Storage Id: #{' unless storage
|
1032
|
+
|
1033
|
+
storage_id = storage['id']
|
1034
|
+
storage_uri_raw = storage['method'].first['uri']
|
1035
|
+
storage_uri = URI.parse(storage_uri_raw)
|
1036
|
+
|
1037
|
+
vidispine_file_path = File.join(storage_uri.path, file_path_relative_to_storage_path)
|
1038
|
+
logger.debug { "Vidispine File Path: '#{vidispine_file_path}'" }
|
1039
|
+
_response[:vidispine_file_path] = vidispine_file_path
|
1040
|
+
|
1041
|
+
# 3 Get Collection
|
1042
|
+
collection_id = args[:collection_id]
|
1043
|
+
unless collection_id
|
1044
|
+
collection_name = args[:collection_name]
|
1045
|
+
unless collection_name
|
1046
|
+
file_path_collection_name_position = args[:file_path_collection_name_position]
|
1047
|
+
raise ArgumentError, ':collection_id, :collection_name, or :file_path_collection_name_position argument is required.' unless file_path_collection_name_position
|
1048
|
+
|
1049
|
+
file_path_split = (file_path_relative_to_storage_path.start_with?('/') ? file_path_relative_to_storage_path[1..-1] : file_path_relative_to_storage_path).split('/')
|
1050
|
+
collection_name = file_path_split[file_path_collection_name_position]
|
1051
|
+
raise ArgumentError, 'Unable to determine collection name from path.' unless collection_name
|
1052
|
+
logger.debug { "Using '#{collection_name}' as collection_name. File Path Array: #{file_path_split.inspect}" }
|
1053
|
+
end
|
1054
|
+
# Determine Collection
|
1055
|
+
collection = collection_create_if_not_exists(:collection_name => collection_name)
|
1056
|
+
collection_id = collection['id']
|
1057
|
+
else
|
1058
|
+
collection = collection_get(:collection_id => collection_id)
|
1059
|
+
raise ArgumentError, 'Collection not found.' unless collection
|
1060
|
+
end
|
1061
|
+
_response[:collection] = collection
|
1062
|
+
#return
|
1063
|
+
|
1064
|
+
# 4.1 Search for Item using File Path
|
1065
|
+
search_response = search(:content => 'metadata', :item_search_document => { :field => [ { :name => metadata_file_path_field_id, :value => [ { :value => vidispine_file_path } ] } ] } ) || { 'entry' => [ ] }
|
1066
|
+
_response[:search] = search_response
|
1067
|
+
|
1068
|
+
item = (search_response['entry'].first || { })['item']
|
1069
|
+
unless item
|
1070
|
+
storage_file_get_or_create_response = storage_file_get_or_create(storage_id, file_path_relative_to_storage_path, :extended_response => true)
|
1071
|
+
_response[:storage_file_get_or_create_response] = storage_file_get_or_create_response
|
1072
|
+
file = storage_file_get_or_create_response[:file]
|
1073
|
+
|
1074
|
+
item = placeholder = file['item']
|
1075
|
+
|
1076
|
+
unless item
|
1077
|
+
# 4.2 Create a Placeholder
|
1078
|
+
logger.debug { 'Creating Placeholder.' }
|
1079
|
+
#placeholder_args = args[:placeholder_args] ||= { :container => 1, :video => 1, :metadata_document => { :group => [ 'Film' ], :timespan => [ { :field => [ { :name => metadata_file_path_field_id, :value => [ { :value => vidispine_file_path } ] } ], :start => '-INF', :end => '+INF' } ] } }
|
1080
|
+
placeholder_args = args[:placeholder_args] ||= { :container => 1, :video => 1, :metadata_document => { :timespan => [ { :field => [ { :name => metadata_file_path_field_id, :value => [ { :value => vidispine_file_path } ] } ], :start => '-INF', :end => '+INF' } ] } }
|
1081
|
+
item = placeholder = import_placeholder(placeholder_args)
|
1082
|
+
end
|
1083
|
+
_response[:placeholder] = placeholder
|
1084
|
+
end
|
1085
|
+
|
1086
|
+
_response[:item] = item
|
1087
|
+
item_id = item['id']
|
1088
|
+
|
1089
|
+
# 5. Add Item to the Collection
|
1090
|
+
logger.debug { 'Adding Item to Collection.' }
|
1091
|
+
collection_object_add_response = collection_object_add(:collection_id => collection_id, :object_id => item_id)
|
1092
|
+
_response[:collection_object_add] = collection_object_add_response
|
1093
|
+
|
1094
|
+
# Item was already in the system so exit here
|
1095
|
+
return _response unless file
|
1096
|
+
|
1097
|
+
file_id = file['id']
|
1098
|
+
raise "File Id Not Found. #{file.inspect}" unless file_id
|
1099
|
+
|
1100
|
+
# 6. Add the file as the original shape
|
1101
|
+
logger.debug { 'Adding the file as the Original Shape.' }
|
1102
|
+
item_shape_import_response = item_shape_import(:item_id => item_id, :file_id => file_id, :tag => 'original')
|
1103
|
+
_response[:item_shape_import] = item_shape_import_response
|
1104
|
+
|
1105
|
+
job_id = item_shape_import_response['jobId']
|
1106
|
+
if job_id
|
1107
|
+
job_monitor_response = wait_for_job_completion(:job_id => job_id) { |env|
|
1108
|
+
logger.debug { "Waiting for Item Shape Import Job to Complete. Time Elapsed: #{Time.now - env[:time_started]} seconds" }
|
1109
|
+
}
|
1110
|
+
last_response = job_monitor_response[:last_response]
|
1111
|
+
raise "Error Adding file As Original Shape. Response: #{last_response.inspect}" unless last_response['status'] == 'FINISHED'
|
1112
|
+
_response[:item_shape_import_job] = job_monitor_response
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
# 7. Generate the Transcode of the item
|
1116
|
+
transcode_tag = args[:transcode_tag] || 'lowres'
|
1117
|
+
logger.debug { 'Generating Transcode of the Item.' }
|
1118
|
+
item_transcode_response = item_transcode(:item_id => item_id, :tag => transcode_tag)
|
1119
|
+
_response[:item_transcode] = item_transcode_response
|
1120
|
+
|
1121
|
+
# 8. Generate the Thumbnails and Poster Frame
|
1122
|
+
create_thumbnails = args.fetch(:create_thumbnails, true)
|
1123
|
+
create_posters = args[:create_posters] || 3
|
1124
|
+
logger.debug { 'Generating Thumbnails(s) and Poster Frame.' }
|
1125
|
+
item_thumbnail_response = item_thumbnail(:item_id => item_id, :createThumbnails => create_thumbnails, :createPosters => create_posters)
|
1126
|
+
_response[:item_thumbnail] = item_thumbnail_response
|
1127
|
+
|
1128
|
+
_response
|
1129
|
+
end
|
1130
|
+
|
1131
|
+
# SEQUENCE THAT CREATES AN ITEM AND THE PROXY USING THE FILE ID
|
1132
|
+
# @deprecated
|
1133
|
+
#
|
1134
|
+
# @param [Hash] args
|
1135
|
+
# @option args [String] :original_file_path
|
1136
|
+
# @option args [String] :lowres_file_path
|
1137
|
+
# @option args [String] :storage_id
|
1138
|
+
# @option args [Hash] :placeholder_args ({ :container => 1, :video => 1 })
|
1139
|
+
# @option args [Boolean] :create_posters (False)
|
1140
|
+
# @option args [Boolean] :create_thumbnails (True)
|
1141
|
+
#
|
1142
|
+
# @return [Hash] :item_id, :original_file_id, :lowres_file_id
|
1143
|
+
def item_create_with_proxy_using_storage_file_paths(args = { }, options = { })
|
1144
|
+
|
1145
|
+
original_file_path = args[:original_file_path] || args[:original]
|
1146
|
+
lowres_file_path = args[:lowres_file_path] || args[:lowres]
|
1147
|
+
|
1148
|
+
placeholder_args = args[:placeholder_args] ||= { :container => 1, :video => 1 }
|
1149
|
+
|
1150
|
+
storage_id = args[:storage_id]
|
1151
|
+
|
1152
|
+
create_posters = args[:create_posters] #|| '300@NTSC'
|
1153
|
+
create_thumbnails = args.fetch(:create_thumbnails, true)
|
1154
|
+
|
1155
|
+
# Create a placeholder
|
1156
|
+
# /API/import/placeholder/?container=1&video=1
|
1157
|
+
logger.debug { "Creating Placeholder: #{placeholder_args.inspect}" }
|
1158
|
+
place_holder = import_placeholder(placeholder_args)
|
1159
|
+
item_id = place_holder['id']
|
1160
|
+
|
1161
|
+
raise 'Placeholder Create Failed.' unless success?
|
1162
|
+
|
1163
|
+
# /API/storage/VX-2/file/?path=storages/test/test_orginal2.mp4
|
1164
|
+
_original_file = original_file = storage_file_get(:storage_id => storage_id, :path => original_file_path)
|
1165
|
+
raise "Unexpected Response Format. Expecting Hash instead of #{original_file.class.name} #{original_file}" unless _original_file.is_a?(Hash)
|
1166
|
+
|
1167
|
+
original_file = original_file['file']
|
1168
|
+
begin
|
1169
|
+
original_file = original_file.first
|
1170
|
+
rescue => e
|
1171
|
+
raise "Error Getting File from Response. #{$!}\n#{original_file.inspect}\n#{_original_file.inspect}"
|
1172
|
+
end
|
1173
|
+
raise "File Not Found. '#{original_file_path}' in storage '#{storage_id}'" unless original_file
|
1174
|
+
original_file_id = original_file['id']
|
1175
|
+
|
1176
|
+
# /API/item/VX-98/shape?tag=original&fileId=[FileIDofOriginal]
|
1177
|
+
item_shape_import(:item_id => item_id, :tag => 'original', :file_id => original_file_id)
|
1178
|
+
|
1179
|
+
# /API/storage/VX-2/file/?path=storages/test/test_proxy2.mp4
|
1180
|
+
lowres_file = storage_file_get(:storage_id => storage_id, :path => lowres_file_path)
|
1181
|
+
lowres_file_id = lowres_file['file'].first['id']
|
1182
|
+
|
1183
|
+
# /API/item/VX-98/shape?tag=lowres&fileId=[FileIDofProxy]
|
1184
|
+
item_shape_import(:item_id => item_id, :tag => 'lowres', :file_id => lowres_file_id)
|
1185
|
+
|
1186
|
+
# /API/item/VX-98/thumbnail/?createThumbnails=true&createPoster
|
1187
|
+
item_thumbnail_args = { :item_id => item_id }
|
1188
|
+
item_thumbnail_args[:createThumbnails] = create_thumbnails
|
1189
|
+
item_thumbnail_args[:createPosters] = create_posters if create_posters
|
1190
|
+
item_thumbnail(item_thumbnail_args)
|
1191
|
+
|
1192
|
+
{ :item_id => item_id, :original_file_id => original_file_id, :lowres_file_id => lowres_file_id }
|
1193
|
+
end
|
1194
|
+
|
1195
|
+
# SEQUENCE THAT CREATE AN ITEM AND THE PROXY USING THE FILE URI/PATH
|
1196
|
+
# @deprecated
|
1197
|
+
def item_create_with_proxy_using_file_uri(args = { }, options = { })
|
1198
|
+
original_file_uri = args[:original_file_uri] || args[:original]
|
1199
|
+
file_type = args[:file_type] || 'video'
|
1200
|
+
|
1201
|
+
import_tag = args[:import_tag] || 'lowres'
|
1202
|
+
|
1203
|
+
placeholder_args = args[:placeholder_args] ||= { :container => 1, :video => 1 }
|
1204
|
+
|
1205
|
+
storage_id = args[:storage_id]
|
1206
|
+
|
1207
|
+
# /API/import/placeholder/?container=1&video=1
|
1208
|
+
placeholder = import_placeholder(placeholder_args)
|
1209
|
+
placeholder_item_id = placeholder['id']
|
1210
|
+
|
1211
|
+
# /API/import/placeholder/VX-99/video/?uri=file%3A%2F%2F%2Fsrv%2Fmedia1%2Ftest_orginal2.mp4&tag=lowres
|
1212
|
+
item = import_placeholder_item(:item_id => placeholder_item_id, :type => file_type, :uri => original_file_uri, :tag => import_tag)
|
1213
|
+
item_id = item['file'].first['id']
|
1214
|
+
|
1215
|
+
# /API/item/VX-99/shape?tag=lowres&fileId=VX-136
|
1216
|
+
#item_shape_import(:item_id => item_id, :tag => nil, :file_id => nil)
|
1217
|
+
|
1218
|
+
# /API/storage/VX-2/file/?path=storages/test/test_orginal2.mp4
|
1219
|
+
#storage_file_get(:storage_id => storage_id)
|
1220
|
+
end
|
1221
|
+
|
1222
|
+
# @note THIS IS A CANTEMO SPECIFIC CALL
|
1223
|
+
def item_annotation_create(args = { }, options = { })
|
1224
|
+
# _args = args
|
1225
|
+
# item_id = _args[:item_id]
|
1226
|
+
#
|
1227
|
+
# in_point = _args[:in_point]
|
1228
|
+
# out_point = _args[:out_point]
|
1229
|
+
# title = _args[:title]
|
1230
|
+
#
|
1231
|
+
# body = { }
|
1232
|
+
# body[:title] = title if title
|
1233
|
+
# body[:inpoint] = in_point if in_point
|
1234
|
+
# body[:outpoint] = out_point if out_point
|
1235
|
+
#
|
1236
|
+
# http(:post, "v1/item/#{item_id}/annotation", :body => body)
|
1237
|
+
|
1238
|
+
_request = Requests::BaseRequest.new(
|
1239
|
+
args,
|
1240
|
+
{
|
1241
|
+
:http_path => 'v1/item/#{arguments[:item_id]}/annotation',
|
1242
|
+
:http_method => :post,
|
1243
|
+
:default_parameter_send_in_value => :body,
|
1244
|
+
:parameters => [
|
1245
|
+
{ :name => :item_id, :aliases => [ :id ], :required => true, :send_in => :path },
|
1246
|
+
|
1247
|
+
:inpoint,
|
1248
|
+
:outpoint,
|
1249
|
+
{ :name => :title, :default => '' },
|
1250
|
+
]
|
1251
|
+
}.merge(options)
|
1252
|
+
)
|
1253
|
+
process_request(_request)
|
1254
|
+
end
|
1255
|
+
|
1256
|
+
# @note THIS IS A CANTEMO SPECIFIC CALL
|
1257
|
+
def item_annotation_get(args = { }, options = { })
|
1258
|
+
args = { :item_id => args } if args.is_a?(String)
|
1259
|
+
# item_id = args[:item_id]
|
1260
|
+
# http(:get, "v1/item/#{item_id}/annotation")
|
1261
|
+
|
1262
|
+
_request = Requests::BaseRequest.new(
|
1263
|
+
args,
|
1264
|
+
{
|
1265
|
+
:http_path => 'v1/item/#{arguments[:item_id]}/annotation',
|
1266
|
+
:parameters => [
|
1267
|
+
{ :name => :item_id, :aliases => [ :id ], :required => true, :send_in => :path }
|
1268
|
+
]
|
1269
|
+
}.merge(options)
|
1270
|
+
)
|
1271
|
+
process_request(_request)
|
1272
|
+
end
|
1273
|
+
|
1274
|
+
def items_search_extended(args = { }, options = { })
|
1275
|
+
_args = symbolize_keys(args, false)
|
1276
|
+
_data = Requests::BaseRequest.process_parameters([ { :name => :fields }, { :name => :item_search_document } ], _args)
|
1277
|
+
_args = _args.merge(_data[:arguments_out])
|
1278
|
+
|
1279
|
+
fields = _args.delete(:fields)
|
1280
|
+
_args[:item_search_document] ||= build_item_search_document(:fields => fields)
|
1281
|
+
|
1282
|
+
items_search(_args, options)
|
1283
|
+
end
|
1284
|
+
alias :item_search_extended :items_search_extended
|
1285
|
+
|
1286
|
+
# Will process a file path through a storage path map
|
1287
|
+
# @param [String] file_path
|
1288
|
+
# @param [Hash] storage_path_map (#storage_file_path_map_create)
|
1289
|
+
# @return [Hash] :relative_file_path, :storage_id, :storage_path_map, :volume_path
|
1290
|
+
def process_file_path_using_storage_map(file_path, storage_path_map = nil)
|
1291
|
+
logger.debug { "Method: #{__method__} Args: #{{:file_path => file_path, :storage_path_map => storage_path_map}.inspect}"}
|
1292
|
+
|
1293
|
+
storage_path_map = storage_file_path_map_create unless storage_path_map and !storage_path_map.empty?
|
1294
|
+
|
1295
|
+
volume_path, storage_id = storage_path_map.find { |path, _| file_path.start_with?(path) }
|
1296
|
+
file_path_relative_to_storage_path = file_path.sub(volume_path, '')
|
1297
|
+
|
1298
|
+
_response = { :relative_file_path => file_path_relative_to_storage_path,
|
1299
|
+
:storage_id => storage_id,
|
1300
|
+
:storage_path_map => storage_path_map,
|
1301
|
+
:volume_path => volume_path }
|
1302
|
+
|
1303
|
+
logger.debug { "Method: #{__method__} Response: #{_response.inspect}" }
|
1304
|
+
_response
|
1305
|
+
end
|
1306
|
+
|
1307
|
+
|
1308
|
+
def storage_file_copy_extended(args = { }, options = { })
|
1309
|
+
_args = symbolize_keys(args, false)
|
1310
|
+
_data = Requests::BaseRequest.process_parameters([ { :name => :use_source_filename }, { :name => :file_id }, { :name => :filename }, { :name => :source_storage_id } ], _args)
|
1311
|
+
_args = _args.merge(_data[:arguments_out])
|
1312
|
+
|
1313
|
+
# Get the source file name and set it as the destination file name
|
1314
|
+
if options[:use_source_filename]
|
1315
|
+
file_id = _args[:file_id]
|
1316
|
+
source_storage_id = _args[:source_storage_id]
|
1317
|
+
file = storage_file_get(:storage_id => source_storage_id, :file_id => file_id)
|
1318
|
+
args[:filename] = file[:filename]
|
1319
|
+
end
|
1320
|
+
|
1321
|
+
storage_file_copy(args, options)
|
1322
|
+
end
|
1323
|
+
|
1324
|
+
def storage_file_create_extended(args = { }, options = { })
|
1325
|
+
_args = symbolize_keys(args, false)
|
1326
|
+
_params = Requests::StorageFileCreate::PARAMETERS.concat [ { :name => :directory, :aliases => [ :dir ], :send_in => :none }, { :name => :storage_map, :send_in => :none } ]
|
1327
|
+
_data = Requests::BaseRequest.process_parameters(_params, _args)
|
1328
|
+
_args = _args.merge(_data[:arguments_out])
|
1329
|
+
|
1330
|
+
storage_path_map = _args.delete(:storage_map) { }
|
1331
|
+
if storage_path_map.empty?
|
1332
|
+
storage_path_map = storage_file_path_map_create
|
1333
|
+
else
|
1334
|
+
storage_path_map = Hash[storage_path_map.map { |k,v| [k.to_s, v] }] if storage_path_map.is_a?(Hash)
|
1335
|
+
end
|
1336
|
+
|
1337
|
+
|
1338
|
+
dir = _args.delete(:directory) { }
|
1339
|
+
if dir
|
1340
|
+
# raise ArgumentError, ':storage_map is a required argument.' unless storage_path_map
|
1341
|
+
|
1342
|
+
volume_path, storage = storage_path_map.find { |path, _| dir.start_with?(path) }
|
1343
|
+
raise "Unable to find match in storage path map for '#{dir}'. Storage Map: #{storage_path_map.inspect}" unless volume_path
|
1344
|
+
|
1345
|
+
dir_path_relative_to_storage = dir.sub(volume_path, '')
|
1346
|
+
|
1347
|
+
storage = storage_get(:id => storage) if storage.is_a?(String)
|
1348
|
+
raise 'Error Retrieving Storage Record' unless storage
|
1349
|
+
|
1350
|
+
storage_id = storage['id']
|
1351
|
+
_args[:storage_id] = storage_id
|
1352
|
+
storage_uri_raw = storage['method'].first['uri']
|
1353
|
+
storage_uri = URI.parse(storage_uri_raw)
|
1354
|
+
|
1355
|
+
vidispine_dir_path = File.join(storage_uri.path, dir_path_relative_to_storage)
|
1356
|
+
logger.debug { "Vidispine Dir Path: '#{vidispine_dir_path}'" }
|
1357
|
+
|
1358
|
+
|
1359
|
+
glob_path = dir.end_with?('*') ? vidispine_dir_path : File.join(vidispine_dir_path, '*')
|
1360
|
+
paths = Dir.glob(glob_path)
|
1361
|
+
|
1362
|
+
|
1363
|
+
return paths.map do |local_absolute_path|
|
1364
|
+
logger.debug { "Found Path: '#{local_absolute_path}'" }
|
1365
|
+
_path = local_absolute_path
|
1366
|
+
file_path_relative_to_storage_path = _path.sub(volume_path, '')
|
1367
|
+
logger.debug { "File Path Relative to Storage Path: #{file_path_relative_to_storage_path}" }
|
1368
|
+
|
1369
|
+
_args[:path] = file_path_relative_to_storage_path
|
1370
|
+
storage_file_create(_args, options)
|
1371
|
+
end
|
1372
|
+
end
|
1373
|
+
|
1374
|
+
storage_file_create(_args, options)
|
1375
|
+
end
|
1376
|
+
|
1377
|
+
# @param [Hash] args
|
1378
|
+
# @option args [String] :file_path
|
1379
|
+
# @option args [Hash] :storage_path_map
|
1380
|
+
# @option args [Boolean] :create_if_not_exists (True)
|
1381
|
+
# @option args [Boolean] :include_item (True)
|
1382
|
+
# @return [Hash|nil]
|
1383
|
+
def storage_file_get_using_file_path(args = { })
|
1384
|
+
_response = { }
|
1385
|
+
file_path = args[:file_path]
|
1386
|
+
return_extended_response = args.fetch(:extended_response, true)
|
1387
|
+
volume_path = args[:volume_path]
|
1388
|
+
|
1389
|
+
# The method type of the URI to lookup
|
1390
|
+
storage_method_type = (args[:storage_method_type] ||= 'file').to_s.downcase
|
1391
|
+
logger.debug { "Storage Method Type: '#{storage_method_type}'" }
|
1392
|
+
|
1393
|
+
storage_path_map = args[:storage_path_map]
|
1394
|
+
storage_path_map = storage_file_path_map_create(:storage_method => storage_method_type) unless storage_path_map && !storage_path_map.empty?
|
1395
|
+
logger.debug { "Storage Path Map: #{storage_path_map.inspect}" }
|
1396
|
+
|
1397
|
+
sm_volume_path, storage = storage_path_map.find { |path, _| file_path.start_with?(path) }
|
1398
|
+
raise "Unable to find match in storage path map for '#{file_path}'. Storage Map: #{storage_path_map.inspect}" unless sm_volume_path
|
1399
|
+
|
1400
|
+
storage = storage_get(:id => storage) if storage.is_a?(String)
|
1401
|
+
_response[:storage] = storage
|
1402
|
+
storage_id = storage['id']
|
1403
|
+
raise "Error Retrieving Storage Record. Storage: #{storage}" unless storage_id
|
1404
|
+
logger.debug { "Storage ID: #{storage_id}" }
|
1405
|
+
|
1406
|
+
## Storage Determined Now Start Digging for the File
|
1407
|
+
|
1408
|
+
case storage_method_type
|
1409
|
+
when 'file', 'http', 's3'
|
1410
|
+
storage_uri_method = "#{storage_method_type}:"
|
1411
|
+
storage_uri_raw = (storage['method'].find do |v|
|
1412
|
+
((v['lastSuccess'] || '') >= (v['lastFailure'] || '')) && (storage_method_type == 'any' || v['uri'].start_with?(storage_uri_method))
|
1413
|
+
end || { })['uri'] rescue nil
|
1414
|
+
raise "Error Getting URI from Storage Method. Storage: #{storage.inspect}" unless storage_uri_raw
|
1415
|
+
|
1416
|
+
if storage_method_type == 's3'
|
1417
|
+
# URI was returning URI::InvalidURIError: the scheme s3 does not accept registry part
|
1418
|
+
volume_path = storage_uri_raw.split('@').last.sub('s3://', '').split('?').first
|
1419
|
+
else
|
1420
|
+
storage_uri = URI.parse(storage_uri_raw)
|
1421
|
+
volume_path = storage_uri.path
|
1422
|
+
end
|
1423
|
+
|
1424
|
+
when 'vxa'
|
1425
|
+
vxa_local_path = storage['metadata']['field'].find { |m| m['key'] == 'vxaLocalPath' }['value']
|
1426
|
+
volume_path = vxa_local_path
|
1427
|
+
|
1428
|
+
else
|
1429
|
+
volume_path = sm_volume_path
|
1430
|
+
|
1431
|
+
end unless volume_path
|
1432
|
+
logger.debug { "Volume Path: '#{volume_path}'" }
|
1433
|
+
|
1434
|
+
file_path_relative_to_storage_path = file_path.sub(volume_path, '')
|
1435
|
+
logger.debug { "File Path Relative to Storage Path: '#{file_path_relative_to_storage_path}'" }
|
1436
|
+
|
1437
|
+
vidispine_file_path = File.join(volume_path, file_path_relative_to_storage_path)
|
1438
|
+
logger.debug { "Vidispine File Path: '#{vidispine_file_path}'" }
|
1439
|
+
|
1440
|
+
create_if_not_exists = args.fetch(:create_if_not_exists, true)
|
1441
|
+
include_item = args.fetch(:include_item, true)
|
1442
|
+
options_out = { :include_item => include_item }
|
1443
|
+
if create_if_not_exists
|
1444
|
+
storage_file_get_or_create_response = storage_file_get_or_create(storage_id, file_path_relative_to_storage_path, options_out.merge(:extended_response => true))
|
1445
|
+
_response[:storage_file_get_or_create_response] = storage_file_get_or_create_response
|
1446
|
+
file = storage_file_get_or_create_response[:file]
|
1447
|
+
else
|
1448
|
+
storage_file_get_response = storage_files_get({ :storage_id => storage_id, :path => file_path_relative_to_storage_path }.merge(options_out)) || { 'file' => [ ] }
|
1449
|
+
file = ((storage_file_get_response || { })['file'] || [ ]).first
|
1450
|
+
end
|
1451
|
+
|
1452
|
+
_response[:file] = file
|
1453
|
+
|
1454
|
+
return_extended_response ? _response : file
|
1455
|
+
end
|
1456
|
+
|
1457
|
+
# Will search for a relative file path on a storage and if not found will trigger a storage_file_create
|
1458
|
+
# @param [String] storage_id
|
1459
|
+
# @param [String] file_path_relative_to_storage_path
|
1460
|
+
# @return [Hash]
|
1461
|
+
def storage_file_get_or_create(storage_id, file_path_relative_to_storage_path, options = { })
|
1462
|
+
logger.debug { "Method: #{__method__} Args:#{{:storage_id => storage_id, :file_path_relative_to_storage_path => file_path_relative_to_storage_path, :options => options }.inspect}" }
|
1463
|
+
include_item = options.fetch(:include_item, true)
|
1464
|
+
creation_state = options[:creation_state] || 'CLOSED'
|
1465
|
+
storage_file_get_response = storage_files_get(:storage_id => storage_id, :path => file_path_relative_to_storage_path, :include_item => include_item) || { 'file' => [ ] }
|
1466
|
+
file = ((storage_file_get_response || { })['file'] || [ ]).first
|
1467
|
+
if file
|
1468
|
+
file_already_existed = true
|
1469
|
+
else
|
1470
|
+
file_already_existed = false
|
1471
|
+
# 4.1.1 Create the storage file record if it does not exist
|
1472
|
+
storage_file_create_response = file = storage_file_create(:storage_id => storage_id, :path => file_path_relative_to_storage_path, :state => creation_state)
|
1473
|
+
if (file || { })['fileAlreadyExists']
|
1474
|
+
file_already_existed = true
|
1475
|
+
_message = file['fileAlreadyExists']
|
1476
|
+
logger.warn { "Running Recreation of Existing File Work Around: #{_message}" }
|
1477
|
+
storage_file_get_response = file = storage_file_get(:storage_id => storage_id, :file_id => _message['fileId'], :include_item => include_item)
|
1478
|
+
end
|
1479
|
+
raise "Error Creating File on Storage. Response: #{response.inspect}" unless (file || { })['id']
|
1480
|
+
end
|
1481
|
+
|
1482
|
+
logger.debug { "Method: #{__method__} Response: #{file.inspect}" }
|
1483
|
+
|
1484
|
+
if options[:extended_response]
|
1485
|
+
return {
|
1486
|
+
:file => file,
|
1487
|
+
:file_already_existed => file_already_existed,
|
1488
|
+
:storage_file_get_response => storage_file_get_response,
|
1489
|
+
:storage_file_create_response => storage_file_create_response
|
1490
|
+
}
|
1491
|
+
end
|
1492
|
+
file
|
1493
|
+
end
|
1494
|
+
|
1495
|
+
# Generates a storage file path map from the current storages.
|
1496
|
+
# This is meant as a default, on most methods you can provide your own storage map that can be used to resolve
|
1497
|
+
# local paths to storage paths.
|
1498
|
+
#
|
1499
|
+
# @param [Hash] args
|
1500
|
+
# @option args [String] :storage_method ('file')
|
1501
|
+
# @option args [Hash] :storages_response (#storages_get)
|
1502
|
+
# @option args [Array] :storages
|
1503
|
+
#
|
1504
|
+
# @return [Hash]
|
1505
|
+
def storage_file_path_map_create(args = {})
|
1506
|
+
method = args[:storage_method] || 'file'
|
1507
|
+
|
1508
|
+
storages = args[:storages] || begin
|
1509
|
+
storages_response = args[:storages_response] || storages_get
|
1510
|
+
storages_response['storage']
|
1511
|
+
end
|
1512
|
+
|
1513
|
+
storage_file_path_map = { }
|
1514
|
+
storages.each do |storage|
|
1515
|
+
storage_methods = storage['method']
|
1516
|
+
file_storage_method = storage_methods.find { |m| m['uri'].start_with?("#{method}:") }
|
1517
|
+
next unless file_storage_method
|
1518
|
+
case method
|
1519
|
+
when 'vxa'
|
1520
|
+
md = Hash[ storage['metadata']['field'].map { |m| [ m['key'], m['value'] ] } ]
|
1521
|
+
address = md['vxaLocalPath']
|
1522
|
+
address.concat('/') if address && !(address.empty? || address.end_with?('/'))
|
1523
|
+
when 's3'
|
1524
|
+
# "s3://{access_key}:_VSENC__{encrypted_secret_access_key}@{bucket_name}/?region=us-east-1?sseAlgorithm=aws:kms?signer=AWSS3V4SignerType/"
|
1525
|
+
uri = file_storage_method['uri']
|
1526
|
+
user_info, bucket_info = uri.split('@')
|
1527
|
+
address, args = bucket_info.split('?').first
|
1528
|
+
address.chomp!('/') if address.end_with?('/')
|
1529
|
+
else
|
1530
|
+
uri = file_storage_method['uri']
|
1531
|
+
match = uri.match(/(.*):\/\/(.*)/)
|
1532
|
+
address = match[2]
|
1533
|
+
end
|
1534
|
+
storage_file_path_map[address] = storage['id']
|
1535
|
+
end
|
1536
|
+
storage_file_path_map
|
1537
|
+
end
|
1538
|
+
|
1539
|
+
# Waits for a job to complete
|
1540
|
+
#
|
1541
|
+
# @param [Hash] args
|
1542
|
+
# @option args [String] :job_id (Required)
|
1543
|
+
# @option args [Integer] :delay (15)
|
1544
|
+
# @option args [Integer] :timeout The timeout in seconds
|
1545
|
+
#
|
1546
|
+
# Accepts a block where the following is exposed:
|
1547
|
+
# :time_started [TIme]
|
1548
|
+
# :poll_interval [Integer]
|
1549
|
+
# :latest_response [Hash]
|
1550
|
+
# :job_status [String]
|
1551
|
+
# :continue_monitoring [Boolean] Can be set to false and job monitoring will exit
|
1552
|
+
# :delay [Integer]
|
1553
|
+
#
|
1554
|
+
# @return [Hash]
|
1555
|
+
# :last_response [Hash]
|
1556
|
+
# :time_started [Time]
|
1557
|
+
# :time_ended [Time] Includes the last poll interval
|
1558
|
+
# :timed_out [Boolean]
|
1559
|
+
def wait_for_job_completion(args = { })
|
1560
|
+
job_id = args[:job_id]
|
1561
|
+
raise ArgumentError, 'job_id is a required argument.' unless job_id
|
1562
|
+
delay = args[:delay] || 15
|
1563
|
+
|
1564
|
+
timeout = args[:timeout]
|
1565
|
+
time_started = Time.now
|
1566
|
+
|
1567
|
+
_response = { }
|
1568
|
+
continue_monitoring = true
|
1569
|
+
timed_out = false
|
1570
|
+
loop do
|
1571
|
+
_response = job_get(:job_id => job_id)
|
1572
|
+
break unless _response
|
1573
|
+
|
1574
|
+
job_status = _response['status']
|
1575
|
+
break if %w(FAILED_TOTAL FINISHED FINISHED_WARNING FINISHED_TOTAL ABORTED).include?(job_status)
|
1576
|
+
|
1577
|
+
break if timeout and (timed_out = ((Time.now - time_started) > timeout))
|
1578
|
+
|
1579
|
+
if block_given?
|
1580
|
+
yield_out = {
|
1581
|
+
:time_started => time_started,
|
1582
|
+
:poll_interval => delay,
|
1583
|
+
:latest_response => _response,
|
1584
|
+
:job_status => job_status,
|
1585
|
+
:continue_monitoring => continue_monitoring,
|
1586
|
+
:delay => delay
|
1587
|
+
}
|
1588
|
+
yield yield_out
|
1589
|
+
break unless continue_monitoring
|
1590
|
+
else
|
1591
|
+
logger.debug { "Waiting for job completion. Job: #{job_status} Poll Interval: #{delay}" }
|
1592
|
+
end
|
1593
|
+
|
1594
|
+
sleep(delay)
|
1595
|
+
end
|
1596
|
+
time_ended = Time.now
|
1597
|
+
|
1598
|
+
{ :last_response => _response, :time_started => time_started, :time_ended => time_ended, :timed_out => timed_out }
|
1599
|
+
end
|
1600
|
+
|
1601
|
+
# Utilities
|
1602
|
+
end
|
1603
|
+
|
1604
|
+
# API
|
1605
|
+
end
|
1606
|
+
|
1607
|
+
# Vidispine
|
1608
|
+
end
|