cantemo-portal-agent 1.0.9 → 1.1.0

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