vidispine 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|