cantemo-portal-agent 1.0.9 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/exe/cantemo-portal-agent +8 -6
  3. data/lib/cantemo/portal/agent/WatchFolderUtility/foreman.rb +59 -0
  4. data/lib/cantemo/portal/agent/cli/commands/watch_folders-working.rb +237 -0
  5. data/lib/cantemo/portal/agent/cli/commands/watch_folders.rb +63 -0
  6. data/lib/cantemo/portal/agent/version.rb +1 -1
  7. data/lib/envoi/aspera/watch_service/client.rb +397 -0
  8. data/lib/envoi/aspera/watch_service/snapshot.rb +11 -0
  9. data/lib/envoi/aspera/watch_service/subscription.rb +0 -0
  10. data/lib/envoi/aspera/watch_service/watch_folder.rb +322 -0
  11. data/lib/envoi/mam/agent/cli/commands/cantemo-agent.rb +62 -0
  12. data/lib/envoi/mam/agent/cli/commands/cantemo-watch_folders.rb +41 -0
  13. data/lib/envoi/mam/agent/cli/commands/cantemo.rb +5 -0
  14. data/lib/envoi/mam/agent/cli/commands/iconik.rb +21 -11
  15. data/lib/envoi/mam/agent/cli/commands/mediasilo.rb +1 -1
  16. data/lib/envoi/mam/agent/cli/commands/vidispine.rb +7 -4
  17. data/lib/envoi/mam/agent/cli/commands/wiredrive.rb +15 -2
  18. data/lib/envoi/mam/agent/cli/commands.rb +4 -4
  19. data/lib/envoi/mam/agent/cli.rb +3 -3
  20. data/lib/envoi/mam/agent/transfer_client/aspera.rb +145 -7
  21. data/lib/envoi/mam/agent/version.rb +1 -1
  22. data/lib/envoi/mam/agent/watch_folder_utility/foreman.rb +76 -0
  23. data/lib/envoi/mam/agent.rb +6 -1
  24. data/lib/envoi/mam/cantemo/agent/watch_folder_handler-working.rb +111 -0
  25. data/lib/envoi/mam/cantemo/agent/watch_folder_handler.rb +176 -0
  26. data/lib/envoi/mam/cantemo/agent/watch_folder_handler_aspera.rb +112 -0
  27. data/lib/envoi/mam/cantemo/agent.rb +288 -0
  28. data/lib/envoi/mam/iconik/agent.rb +15 -3
  29. data/lib/envoi/mam/vidispine/agent.rb +6 -1
  30. data/lib/envoi/watch_folder_utility/watch_folder/handler/listen.rb +189 -0
  31. metadata +48 -4
@@ -0,0 +1,112 @@
1
+ require 'envoi/mam/cantemo/agent'
2
+ require 'envoi/aspera/watch_service/watch_folder'
3
+
4
+ module Envoi::Mam::Cantemo
5
+
6
+ class Agent
7
+
8
+ class WatchFolderHandler
9
+
10
+ AWF = Envoi::Aspera::WatchService::WatchFolder
11
+ LWF = Envoi::WatchFolderUtility::WatchFolder::Listen
12
+
13
+ attr_accessor :logger, :agent, :config, :watch_folders
14
+
15
+ def initialize(args = { })
16
+ initialize_logger(args)
17
+
18
+ @agent = Envoi::Mam::Cantemo::Agent.load_from_config_file(args)
19
+ @config = agent.config
20
+ cantemo_config = config[:cantemo] || config['cantemo']
21
+ watch_folder_defs = cantemo_config[:watch_folders] || cantemo_config['watch_folders']
22
+
23
+ @ignored_file_paths_by_watch_folder = Hash.new { |h, k| h[k] = [] }
24
+
25
+ @watch_folders = AWF.process_watch_folder_defs(watch_folder_defs)
26
+ end
27
+
28
+ def initialize_logger(args = { })
29
+ @logger = args[:logger] ||= Logger.new(args[:log_to] || STDOUT)
30
+ log_level = args[:log_level]
31
+ if log_level
32
+ @logger.level = log_level
33
+ args[:logger] = @logger
34
+ end
35
+ @logger
36
+ end
37
+
38
+ def add_to_ignore(wf, file)
39
+ @ignored_file_paths_by_watch_folder[wf] << file.path
40
+ end
41
+
42
+ def process_file(watch_folder, file, storage_id = nil, quarantine_directory_path = nil)
43
+ full_file_path = File.join(watch_folder.path, file.path)
44
+ if storage_id && agent.upload(file_path: full_file_path, storage_id: storage_id)
45
+ FileUtils.rm full_file_path
46
+ else
47
+ if Dir.exist?(quarantine_directory_path)
48
+ FileUtils.mv full_file_path, quarantine_directory_path
49
+ else
50
+ add_to_ignore(watch_folder, file)
51
+ end
52
+ end
53
+ end
54
+
55
+ def process_watch_folder(wf)
56
+ storage_id = wf.definition['upload_to_storage_id'] || wf.definition['storage_id']
57
+ quarantine_directory_path = wf.definition['quarantine_path']
58
+ exclude = wf.definition['exclude']
59
+ min_stable_poll_count = wf.definition['stable_poll_count'] || 3
60
+
61
+ maps = wf.state.details[:maps]
62
+ stable_paths = maps[:stable]
63
+ ignored_files = @ignored_file_paths_by_watch_folder[wf]
64
+ stable_paths.each do |fp, file|
65
+ if exclude
66
+ next if ignored_files.include?(file.path)
67
+ if [*exclude].find { |ep| File.fnmatch(ep, file.path) }
68
+ logger.debug { "Adding File to Ignore Cache: '#{file.path}'"}
69
+ ignored_files << file.path
70
+ next
71
+ end
72
+ end
73
+
74
+ # full_file_path = File.join(wf.path, file.path)
75
+
76
+ # pp file
77
+ # puts file_path
78
+ stable_poll_count = file[:stable_poll_count]
79
+
80
+ if stable_poll_count && stable_poll_count > min_stable_poll_count
81
+ process_file(wf, file, storage_id, quarantine_directory_path)
82
+ end
83
+ end
84
+
85
+ # process_watch_folder
86
+ end
87
+
88
+ def run
89
+ # AWF.run_once(watch_folders) { |wf| pp wf }
90
+ AWF.run(watch_folders) { |wf| process_watch_folder(wf) }
91
+ end
92
+
93
+ def run_once
94
+ AWF.run_once(watch_folders) { |wf| process_watch_folder(wf) }
95
+ end
96
+
97
+ def self.run(args)
98
+ w = self.new(args)
99
+ w.run
100
+ end
101
+
102
+ def self.run_as_daemon(args)
103
+ # ARGV.unshift 'run' unless %w(start stop restart run zap killall status).include? ARGV.first
104
+ require 'daemons'
105
+ Daemons.run_proc('cantemo-portal-watch-folders') { self.run(args) }
106
+ end
107
+
108
+ end
109
+
110
+ end
111
+
112
+ end
@@ -0,0 +1,288 @@
1
+ require 'envoi/mam/agent/transfer_client/aspera'
2
+ require 'envoi/mam/agent/transfer_client/s3'
3
+
4
+ require 'vidispine/api/utilities'
5
+
6
+ module Envoi
7
+
8
+ module Mam
9
+
10
+ module Cantemo
11
+
12
+ class Agent < Envoi::Mam::Agent
13
+
14
+ DEFAULT_SHAPE_TAG = 'original'
15
+ DEFAULT_DESTINATION_PATH = '.'
16
+
17
+ attr_accessor :default_aspera_ascp_args,
18
+ :default_aspera_ascp_path,
19
+ :default_vidispine_shape_tag
20
+
21
+ def after_initialize
22
+ args = initial_args
23
+ @default_aspera_ascp_path = args[:default_aspera_ascp_path]
24
+ @default_aspera_args = args[:default_ascp_args] || Envoi::Mam::Agent::TransferClient::Aspera::DEFAULT_ASCP_ARGS
25
+ end
26
+
27
+ def dry_run?; @dry_run end
28
+
29
+ def agent_config
30
+ @agent_config ||= config['cantemo'] || config['vidispine'] || { }
31
+ end
32
+
33
+ def initialize_api_client(args = { })
34
+ api_config = agent_config
35
+ @api_client = args[:api_client] || begin
36
+
37
+ api_host = api_config['host']
38
+ api_host_use_ssl = api_config['use_ssl']
39
+ api_port = api_config['port']
40
+ api_username = api_config['username']
41
+ api_password = api_config['password']
42
+ api_auth_token = api_config['api_auth_token']
43
+ api_base_path = api_config['api_base_path']
44
+ api_default_query_data = api_config['default_query_data']
45
+
46
+ api_url = api_config['url'] || api_config['uri']
47
+ if api_url
48
+ api_uri = URI(api_url)
49
+ api_host ||= api_uri.host
50
+ api_port ||= api_uri.port
51
+ api_userinfo = api_uri.userinfo
52
+ if api_userinfo
53
+ _api_username, _api_password = api_userinfo.split(':')
54
+ api_username ||= _api_username
55
+ api_password ||= _api_password
56
+ end
57
+ api_uri_query = api_uri.query
58
+ api_default_query_data ||= Hash[api_uri_query.split('&').map { |kp| kp.split('=') }]
59
+ api_base_path ||= api_uri.path
60
+ end
61
+
62
+ api_port ||= (api_host_use_ssl ? 443 : 80)
63
+ api_base_path ||= 'VSAPI/'
64
+
65
+ client_args = { }
66
+ client_args[:http_host_address] = api_host if api_host
67
+ client_args[:http_host_port] = api_port if api_port
68
+ client_args[:http_host_use_ssl] = api_host_use_ssl if api_host_use_ssl
69
+ client_args[:username] = api_username if api_username
70
+ client_args[:password] = api_password if api_password
71
+ client_args[:default_base_path] = api_base_path if api_base_path
72
+ client_args[:default_query_data] = api_default_query_data if api_default_query_data
73
+
74
+ if api_auth_token
75
+ # Cantemo Portal supports an auth token for authentication, replace basic auth with auth-token
76
+ client_args[:authorization_header_key] = 'Auth-Token'
77
+ client_args[:authorization_header_value] = api_auth_token
78
+ end
79
+
80
+ _client = ::Vidispine::API::Utilities.new(client_args)
81
+ _client
82
+ end
83
+
84
+
85
+
86
+ @default_vidispine_shape_tag = args[:default_shape_tag] || api_config['default_shape_tag'] || api_config['shape_tag'] || DEFAULT_SHAPE_TAG
87
+
88
+ end
89
+
90
+ def item_get_shape_by_tag(item_id, shape_tag)
91
+ item_shapes_get_response = api_client.item_shapes_get(:item_id => item_id, :tag => shape_tag)
92
+ shape_id = item_shapes_get_response['uri'].first
93
+ end
94
+
95
+ def download(args = { })
96
+
97
+ item_id = args[:item_id]
98
+ shape_id = args[:shape_id]
99
+ unless shape_id && !shape_id.empty?
100
+ shape_tag = args[:shape_tag] || default_vidispine_shape_tag
101
+ shape_id = item_get_shape_by_tag(item_id, shape_tag)
102
+ end
103
+
104
+ logger.info { "Getting file path for Item ID: #{item_id} Shape ID: #{shape_id}"}
105
+ item_shape_get_response = api_client.item_shape_get(:item_id => item_id, :shape_id => shape_id)
106
+
107
+ files = item_shape_get_response['containerComponent']['file']
108
+ logger.debug { "Files: #{files}"}
109
+
110
+ # file = files.first
111
+ files = [ files.first ] # just do the first file for now
112
+ files.each do |file|
113
+ begin
114
+ download_file(args, file)
115
+ rescue => e
116
+ logger.warn { "Exception: #{$!}" }
117
+ end
118
+ end
119
+ logger.info { 'DONE' }
120
+ end
121
+
122
+ def download_file(args, file)
123
+ logger.debug { "File: #{file}"}
124
+ transfer_type = args[:transfer_type]
125
+
126
+ file_storage_id = file['storage']
127
+ file_path = file['path']
128
+
129
+ file_storage_config = agent_config['storages'][file_storage_id]
130
+
131
+ unless file_storage_config && !file_storage_config.empty?
132
+ raise Exception, "No configuration found for storage '#{file_storage_id}'"
133
+ end
134
+
135
+ logger.info { "Transferring File Path: '#{file_path}'" }
136
+ preserve_path = args.fetch(:preserve_path, file_storage_config.fetch('preserve_path', true))
137
+
138
+ destination_path = args[:destination_path] || file_storage_config['destination_path'] || DEFAULT_DESTINATION_PATH
139
+ relative_path = preserve_path ? File.dirname(file_path) : nil
140
+ relative_path = nil if relative_path == '.'
141
+
142
+ target_path = relative_path ? File.join(destination_path, relative_path) : destination_path
143
+ target_path = target_path[0..-1] if target_path.start_with?('/') && !destination_path.start_with?('/')
144
+
145
+ aspera_config = file_storage_config['aspera']
146
+ if (transfer_type.empty? || transfer_type == :aspera) && (aspera_config && !aspera_config.empty?)
147
+ client = Envoi::Mam::Agent::TransferClient::Aspera.new(agent: self)
148
+ return client.download(aspera_config, file_path, target_path)
149
+ # download_using_aspera(aspera_config, file_path, target_path)
150
+ end
151
+
152
+ s3_config = file_storage_config['s3']
153
+ if (transfer_type.empty? || transfer_type == :s3) && (s3_config && !s3_config.empty?)
154
+ target_path = File.expand_path(target_path) if target_path == '.'
155
+ target_path = File.join(target_path, File.basename(file_path))
156
+ client = Envoi::Mam::Agent::TransferClient::S3.new(agent: self)
157
+ return client.download(s3_config, file_path, target_path)
158
+ end
159
+
160
+ logger.warn { "No Supported TransferClient Configuration#{transfer_type && !transfer_type.empty? ? " for transfer type '#{transfer_type}' " : ''}Found in Storage Configuration." }
161
+ end
162
+
163
+ def upload(args = { })
164
+ file_path = args[:file_path]
165
+ raise ArgumentError, "Path not found: '#{file_path}'" unless File.exists?(file_path)
166
+
167
+ if File.directory?(file_path)
168
+ # Non-recursive directory upload
169
+ file_paths = Dir.glob(File.join(file_path, '*.*'))
170
+ logger.debug { "File Paths: #{file_paths}"}
171
+ file_paths.map { |fp| upload(args.merge(file_path: fp))}
172
+ return file_paths
173
+ end
174
+ logger.debug { "Preparing to upload '#{file_path}'" }
175
+
176
+ transfer_type = args[:transfer_type] || ''
177
+ storage_id = args[:storage_id]
178
+ vidispine_storage_config = agent_config['storages'][storage_id]
179
+
180
+ unless vidispine_storage_config && !vidispine_storage_config.empty?
181
+ raise "No configuration found for storage '#{storage_id}'"
182
+ end
183
+
184
+ should_import_file = args.fetch(:import_file, vidispine_storage_config.fetch('import', true))
185
+
186
+ should_preserve_path = args.fetch(:preserve_path, vidispine_storage_config.fetch('preserve_path', true))
187
+
188
+ destination_path = args[:destination_path] || vidispine_storage_config['destination_path'] || '/'
189
+ relative_path = should_preserve_path ? File.dirname(file_path) : nil
190
+ relative_path = File.expand_path(relative_path) if relative_path == '.'
191
+
192
+ target_path = relative_path ? File.join(destination_path, relative_path) : destination_path
193
+ target_path = target_path[0..-1] if target_path.start_with?('/') && !destination_path.start_with?('/')
194
+
195
+
196
+ # upload file
197
+
198
+ transfer_response = begin
199
+ response = nil
200
+ aspera_config = vidispine_storage_config['aspera']
201
+ begin
202
+ if (transfer_type.empty? || transfer_type == :aspera) && (aspera_config && !aspera_config.empty?)
203
+ client = Envoi::Mam::Agent::TransferClient::Aspera.new(agent: self)
204
+ response = client.upload(aspera_config, file_path, target_path)
205
+ end
206
+ rescue => e
207
+ logger.error { "Aspera Transfer Failed. '#{e.message}'" }
208
+ end
209
+
210
+ s3_config = vidispine_storage_config['s3']
211
+ begin
212
+ if !response && (transfer_type.empty? || transfer_type == :s3) && (s3_config && !s3_config.empty?)
213
+ _target_path = target_path
214
+ _target_path = File.expand_path(_target_path) if target_path == '.'
215
+ _target_path = File.join(_target_path, File.basename(file_path))
216
+ client = Envoi::Mam::Agent::TransferClient::S3.new(agent: self)
217
+ response = client.upload(s3_config, file_path, _target_path)
218
+ end
219
+ rescue => e
220
+ logger.error { "S3 Transfer Failed. '#{e.message}'" }
221
+ end
222
+
223
+ response
224
+ end
225
+
226
+ logger.warn { "No supported TransferClient configuration#{transfer_type && !transfer_type.empty? ? " for transfer type '#{transfer_type}' " : ''}found in storage configuration." } unless transfer_response != nil
227
+
228
+ _response = { transfer_response: transfer_response }
229
+
230
+ return _response unless should_import_file
231
+
232
+ item_id = args[:item_id]
233
+ shape_tag = args[:shape_tag] || default_vidispine_shape_tag
234
+
235
+ # attach file to item as shape
236
+ path_on_storage = File.join(target_path, File.basename(file_path))
237
+ file_create_response = api_client.storage_file_create storage_id: storage_id, path: path_on_storage, state: 'CLOSED'
238
+ file_id = file_create_response['id']
239
+
240
+ if item_id
241
+ item_shape_import_response = api_client.item_shape_import item_id: item_id, tag: shape_tag, fileId: file_id
242
+ else
243
+ item_shape_import_response = api_client.item_add_using_file_path storage_id: storage_id,
244
+ file_path: file_path,
245
+ file_id: file_id,
246
+ storage_path_map: { '/' => storage_id }
247
+ end
248
+ _response[:import_response] = item_shape_import_response
249
+
250
+ _response
251
+ end
252
+
253
+ def upload_transfer_spec_get(file_paths)
254
+
255
+ end
256
+
257
+
258
+
259
+ def ingest_file
260
+ # 1) export ASPERA_SCP_TOKEN="ATB3_AEA9dUI5dd2u1k8XBMVD0qInR-Lyylkz8AylTXLVt5_SMVGR8SO7Sdc0lj6RkoHK_DvAAEWac8bllF_Yan1NbbDTyPj_3BTA"
261
+ # 2) ascp -i asperaweb_id_dsa.openssh -P 33001 -l 20m /Users/nicholasstokes/Desktop/CantemoAgent.mov xfer@ats-aws-us-west-2.aspera.io:
262
+ #
263
+ # single line equivalent
264
+ # ascp -i asperaweb_id_dsa.openssh -W 'ATB3_AEAiBdrf-7sMQu782sPoQ4Q7Yh4LadgvYK9BP4Kt1hcVlZGR8SO7Sdc0lj6RkoHK_DvAAG5XlBBIZGEINicYOxMRE7g_3BTA' -P 33001 -l 20m /Users/nicholasstokes/Desktop/CantemoAgent.mov xfer@ats-aws-us-west-2.aspera.io:
265
+
266
+ # Aspera:
267
+ # `GET /aspera/ping/` - to check if Aspera upload is available
268
+ # `POST /vs/item/new/` to create a placeholder to upload to
269
+ # `POST /aspera/upload_setup/` - Aspera token
270
+ # ... upload the file with Aspera ...
271
+ # `POST /API/v1/placeholder/VX-3012/associatefile/?filepath=Screen%20Shot%202018-08-07%20at%2013.06.58.png&storageid=VX-11&platform=mac` - associate new file to placeholder
272
+
273
+ # HTTP upload:
274
+ # `POST /vs/item/new/` - create a placeholder
275
+ # then streamind POST (?) requests to `VSAPI/item/VX-3014/shape/essence/raw?transferId=2D30030A-6EC3-4364-9598-5BF20972A218&throttle=false&filename=Screen%20Shot%202018-08-07%20at%2014.34.15.png&jobmetadata=portal_groups:StringArray%3dAdmin` to upload the data.
276
+ # that end-point for HTTP is documented in http://apidoc.vidispine.com/latest/ref/item/shape.html#import-a-shape-using-the-request-body
277
+ #
278
+ end
279
+
280
+ # Agent
281
+ end
282
+
283
+ # Cantemo
284
+ end
285
+
286
+ end
287
+
288
+ end
@@ -112,11 +112,13 @@ module Envoi
112
112
  if File.directory?(file_path)
113
113
  file_paths = Dir.glob(File.join(file_path, '*.*'))
114
114
  logger.debug { "File Paths: #{file_paths}"}
115
- file_paths.map { |fp| upload(args.merge(file_path: fp))}
116
- return file_paths
115
+ return file_paths.map { |fp| upload(args.merge(file_path: fp))}
117
116
  end
118
117
  logger.debug { "Preparing to upload '#{file_path}'" }
119
118
 
119
+ ingest_after_upload = args[:ingest_after_upload]
120
+ analyze_asset = args[:analyze_asset]
121
+
120
122
  transfer_type = args[:transfer_type] || ''
121
123
  storage_id = args[:storage_id]
122
124
  path_on_storage = nil
@@ -170,7 +172,17 @@ module Envoi
170
172
  file_size = File.exist?(file_path) ? File.size?(file_path) : 1024
171
173
  path_on_storage ||= File.join(target_path, File.basename(file_path))
172
174
 
173
- api_client.asset_add_using_file_path(file_path: path_on_storage, storage_id: storage_id, file_size: file_size)
175
+ if ingest_after_upload
176
+ asset_create_response = api_client.asset_add_using_file_path(file_path: path_on_storage,
177
+ storage_id: storage_id,
178
+ file_size: file_size)
179
+ if analyze_asset
180
+ asset_id = asset_create_response[:asset]['id']
181
+ api_client.asset_analyze(:asset_id => asset_id)
182
+ end
183
+
184
+ end
185
+
174
186
  end
175
187
 
176
188
 
@@ -34,12 +34,17 @@ module Envoi
34
34
  def initialize_api_client(args = { })
35
35
  _vidispine_config = vidispine_config
36
36
  @api_client = args[:vidispine_api_client] || begin
37
-
37
+
38
38
  vidispine_host = _vidispine_config['host']
39
+ vidispine_port = _vidispine_config['port']
40
+ vidispine_host_use_ssl = _vidispine_config['use_ssl']
39
41
  vidispine_username = _vidispine_config['username']
40
42
  vidispine_password = _vidispine_config['password']
43
+
41
44
  client_args = { }
42
45
  client_args[:http_host_address] = vidispine_host if vidispine_host
46
+ client_args[:http_host_port] = vidispine_port if vidispine_port
47
+ client_args[:http_host_use_ssl] = vidispine_host_use_ssl if vidispine_host_use_ssl
43
48
  client_args[:username] = vidispine_username if vidispine_username
44
49
  client_args[:password] = vidispine_password if vidispine_password
45
50
  ::Vidispine::API::Utilities.new(client_args)
@@ -0,0 +1,189 @@
1
+ require 'listen' # https://github.com/guard/listen
2
+ require 'pp'
3
+ module Envoi
4
+ module WatchFolderUtility
5
+ class WatchFolder
6
+ class Handler
7
+ class Listen
8
+
9
+ attr_accessor :logger, :definition,
10
+ :known_path_map,
11
+ :last_poll_time,
12
+ :min_stable_poll_count,
13
+ :paths,
14
+ :poll_interval,
15
+
16
+ :listener, :lock,
17
+
18
+
19
+ :quarantine_directory_path
20
+
21
+ def initialize(args = { })
22
+ initialize_logger(args)
23
+ logger.debug { "Initializing Work Folder. Args: #{args}" }
24
+
25
+ @definition = args[:definition]
26
+ raise ArgumentError, "Definition is a required argument." unless definition
27
+
28
+
29
+
30
+ # @paths = [ File.expand_path('~/watch_folders/aspera') ]
31
+ @paths = definition['paths'] || []
32
+ path = definition['path']
33
+ @paths << path if path
34
+
35
+ @known_path_map = { }
36
+ @ignored_files_map = { }
37
+
38
+ @lock = Mutex.new
39
+ # @quarantine_directory_path = definition['quarantine_directory_path'] || definition['quarantine_path']
40
+ @poll_interval = definition['poll_interval'] || 15
41
+ @min_stable_poll_count = 3
42
+ @processing_limit = 10
43
+ @processing = { }
44
+
45
+ initialize_listener
46
+ end
47
+
48
+ def initialize_logger(args = { })
49
+ @logger = args[:logger] ||= Logger.new(args[:log_to] || STDOUT)
50
+ log_level = args[:log_level] || Logger::DEBUG
51
+ if log_level
52
+ @logger.level = log_level
53
+ args[:logger] = @logger
54
+ end
55
+ @logger
56
+ end
57
+
58
+ def process_add_or_modified_path(path, event_type)
59
+ return path.each { |path| process_add_or_modified_path(path, event_type) } if path.is_a?(Array)
60
+ logger.debug { "PATH #{event_type} '#{path}'" }
61
+ lock.synchronize { known_path_map[path] = OpenStruct.new({ type: event_type, path: path, stable_poll_count: 0, timestamp: Time.now}) }
62
+ end
63
+
64
+ def initialize_listener(args = { })
65
+ # @directories = directories.map do |directory|
66
+ # Pathname.new(directory.to_s).realpath
67
+ # end
68
+
69
+
70
+ @listener = ::Listen.to(*paths, {}) do |m, a, r|
71
+ if !r.empty?
72
+ log "#{r.length} file(s) removed."
73
+ lock.synchronize { r.each { |f| known_path_map.delete(f) } }
74
+ end
75
+
76
+ if !m.empty?
77
+ log "#{m.length} file(s) modified."
78
+ lock.synchronize { process_add_or_modified_path(m, :modified) }
79
+ end
80
+
81
+ if !a.empty?
82
+ log "#{a.length} file(s) added."
83
+ lock.synchronize { process_add_or_modified_path(a, :added) }
84
+ end
85
+
86
+ end
87
+ _config = @listener.instance_variable_get(:@backend).instance_variable_get(:@adapter).config
88
+
89
+ _snapshots ||= {}
90
+ # change_config = ::Listen::Change::Config.new(_config.queue, _config.silencer)
91
+ _config.directories.each do |dir|
92
+ record = ::Listen::Record.new(dir)
93
+ record.build
94
+ record.instance_variable_get(:@tree).each do |rel_path, fstats|
95
+ _path = File.join(dir, rel_path)
96
+ process_add_or_modified_path(_path, :added)
97
+ end
98
+ # snapshot = ::Listen::Change.new(change_config, record)
99
+ # _config.optimize_changes(snapshot)
100
+ # _snapshots[dir] = snapshot
101
+ end
102
+
103
+ # pp _snapshots
104
+ end
105
+
106
+ def log(message)
107
+ puts "#{Time.now} #{message}"
108
+ end
109
+
110
+ def add_to_ignore(file)
111
+ @ignored_files_map[file.path] = file
112
+ end
113
+
114
+ # def handle_stable_file(file, quarantine_directory_path = @quarantine_directory_path)
115
+ # full_file_path = file[:path]
116
+ # logger.info { "Processing File Path: #{full_file_path}..." }
117
+ # if true
118
+ # FileUtils.rm full_file_path
119
+ # else
120
+ # if quarantine_directory_path
121
+ # FileUtils.mv full_file_path, quarantine_directory_path
122
+ # end
123
+ # end
124
+ # end
125
+
126
+ def poll
127
+ @previous_poll_time = @last_poll_time
128
+ @last_poll_time = Time.now
129
+ stable_files = []
130
+ lock.synchronize do
131
+ @known_path_map.each do |fp, f|
132
+ logger.debug { "Incrementing Stable Count: #{f.inspect}" }
133
+ f[:stable_poll_count] += 1
134
+ if f[:stable_poll_count] >= @min_stable_poll_count
135
+ stable_files << f
136
+ # handle_stable_file(f)
137
+ end
138
+ end
139
+ end
140
+ stable_files
141
+ end
142
+
143
+ def run(options = { })
144
+ logger.info { "Starting handler for #{paths}" }
145
+ listener.start
146
+ end
147
+
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+
154
+
155
+ # watcher = Envoi::WatchFolderUtility::WatchFolder::Handler::Listen.new
156
+ # watcher.run
157
+ #
158
+ #
159
+ #
160
+ #
161
+ # class WatchFolder
162
+ #
163
+ # attr_accessor :definition, :state, :client, :watcher,
164
+ # :previous_poll_time, :last_poll_time
165
+ #
166
+ # def initialize(args = { })
167
+ #
168
+ # end
169
+ #
170
+ # def poll(&block)
171
+ #
172
+ # end
173
+ #
174
+ # def run_once(&block)
175
+ # poll(&block)
176
+ # end
177
+ #
178
+ # def run(poll_interval = 15, &block)
179
+ # loop do
180
+ # begin
181
+ # run_once(&block)
182
+ # sleep poll_interval
183
+ # rescue SystemExit, Interrupt
184
+ # break
185
+ # end
186
+ # end
187
+ # end
188
+ #
189
+ # end