OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * Class representing the results of a scan operation. | 6 * Class representing the results of a scan operation. |
7 * | 7 * |
8 * @interface | 8 * @interface |
9 */ | 9 */ |
10 importer.MediaScanner = function() {}; | 10 importer.MediaScanner = function() {}; |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
96 if (index > -1) { | 96 if (index > -1) { |
97 this.observers_.splice(index, 1); | 97 this.observers_.splice(index, 1); |
98 } else { | 98 } else { |
99 console.warn('Ignoring request to remove observer that is not registered.'); | 99 console.warn('Ignoring request to remove observer that is not registered.'); |
100 } | 100 } |
101 }; | 101 }; |
102 | 102 |
103 /** @override */ | 103 /** @override */ |
104 importer.DefaultMediaScanner.prototype.scanDirectory = function(directory) { | 104 importer.DefaultMediaScanner.prototype.scanDirectory = function(directory) { |
105 var scan = this.createScanResult_(); | 105 var scan = this.createScanResult_(); |
106 console.log(scan.name + ': Scanning directory ' + directory.fullPath); | |
mtomasz
2015/04/01 02:28:45
nit: Are these logs necessary? If not, let's remov
Steve McKay
2015/04/01 03:52:52
Useful. Changed to console.info and console.debug.
| |
107 | |
106 var watcher = this.watcherFactory_( | 108 var watcher = this.watcherFactory_( |
107 /** @this {importer.DefaultMediaScanner} */ | 109 /** @this {importer.DefaultMediaScanner} */ |
108 function() { | 110 function() { |
109 scan.invalidateScan(); | 111 scan.cancel(); |
110 this.notify_(importer.ScanEvent.INVALIDATED, scan); | 112 this.notify_(importer.ScanEvent.INVALIDATED, scan); |
111 }.bind(this)); | 113 }.bind(this)); |
112 | 114 |
113 this.crawlDirectory_(directory, watcher) | 115 this.crawlDirectory_(directory, watcher) |
114 .then(this.scanMediaFiles_.bind(this, scan)) | 116 .then(this.scanMediaFiles_.bind(this, scan)) |
115 .then(scan.resolve) | 117 .then(scan.resolve) |
116 .catch(scan.reject); | 118 .catch(scan.reject); |
117 | 119 |
118 scan.whenFinal() | 120 scan.whenFinal() |
119 .then( | 121 .then( |
120 /** @this {importer.DefaultMediaScanner} */ | 122 /** @this {importer.DefaultMediaScanner} */ |
121 function() { | 123 function() { |
124 console.log(scan.name + ': Finished.'); | |
122 this.notify_(importer.ScanEvent.FINALIZED, scan); | 125 this.notify_(importer.ScanEvent.FINALIZED, scan); |
123 }.bind(this)); | 126 }.bind(this)); |
124 | 127 |
125 return scan; | 128 return scan; |
126 }; | 129 }; |
127 | 130 |
128 /** @override */ | 131 /** @override */ |
129 importer.DefaultMediaScanner.prototype.scanFiles = function(entries) { | 132 importer.DefaultMediaScanner.prototype.scanFiles = function(entries) { |
130 if (entries.length === 0) { | 133 if (entries.length === 0) { |
131 throw new Error('Cannot scan empty list.'); | 134 throw new Error('Cannot scan empty list.'); |
132 } | 135 } |
133 var scan = this.createScanResult_(); | 136 var scan = this.createScanResult_(); |
137 console.log( | |
138 scan.name + ': Scanning fixed set of ' + | |
139 entries.length + ' entries.'); | |
140 | |
134 var watcher = this.watcherFactory_( | 141 var watcher = this.watcherFactory_( |
135 /** @this {importer.DefaultMediaScanner} */ | 142 /** @this {importer.DefaultMediaScanner} */ |
136 function() { | 143 function() { |
137 scan.invalidateScan(); | 144 scan.cancel(); |
138 this.notify_(importer.ScanEvent.INVALIDATED, scan); | 145 this.notify_(importer.ScanEvent.INVALIDATED, scan); |
139 }.bind(this)); | 146 }.bind(this)); |
140 | 147 |
141 var scanPromises = entries.map(this.onUniqueFileFound_.bind(this, scan)); | 148 var scanPromises = entries.map(this.onUniqueFileFound_.bind(this, scan)); |
142 | 149 |
143 Promise.all(scanPromises) | 150 Promise.all(scanPromises) |
144 .then(scan.resolve) | 151 .then(scan.resolve) |
145 .catch(scan.reject); | 152 .catch(scan.reject); |
146 | 153 |
147 scan.whenFinal() | 154 scan.whenFinal() |
148 .then( | 155 .then( |
149 /** @this {importer.DefaultMediaScanner} */ | 156 /** @this {importer.DefaultMediaScanner} */ |
150 function() { | 157 function() { |
151 this.notify_(importer.ScanEvent.FINALIZED, scan); | 158 this.notify_(importer.ScanEvent.FINALIZED, scan); |
152 }.bind(this)); | 159 }.bind(this)); |
153 | 160 |
154 return scan; | 161 return scan; |
155 }; | 162 }; |
156 | 163 |
157 /** @const {number} */ | 164 /** @const {number} */ |
158 importer.DefaultMediaScanner.SCAN_BATCH_SIZE = 1; | 165 importer.DefaultMediaScanner.SCAN_BATCH_SIZE = 1; |
159 | 166 |
160 /** | 167 /** |
161 * @param {!importer.DefaultScanResult} scan | 168 * @param {!importer.DefaultScanResult} scan |
162 * @param {!Array<!FileEntry>} entries | 169 * @param {!Array<!FileEntry>} entries |
163 * @return {!Promise} Resolves when scanning is finished. | 170 * @return {!Promise} Resolves when scanning is finished. |
mtomasz
2015/04/01 02:28:45
nit: ... or cancelled. Shall we add?
Steve McKay
2015/04/01 03:52:52
Done.
| |
164 * @private | 171 * @private |
165 */ | 172 */ |
166 importer.DefaultMediaScanner.prototype.scanMediaFiles_ = | 173 importer.DefaultMediaScanner.prototype.scanMediaFiles_ = |
167 function(scan, entries) { | 174 function(scan, entries) { |
168 var handleFileEntry = this.onFileEntryFound_.bind(this, scan); | 175 var handleFileEntry = this.onFileEntryFound_.bind(this, scan); |
169 | 176 |
170 /** | 177 /** |
171 * @param {number} begin The beginning offset in the list of entries | 178 * @param {number} begin The beginning offset in the list of entries |
172 * to process. | 179 * to process. |
173 * @return {!Promise} | 180 * @return {!Promise} |
174 */ | 181 */ |
175 var scanChunk = function(begin) { | 182 var scanBatch = function(begin) { |
183 if (scan.canceled()) { | |
184 console.log( | |
185 scan.name + ': Skipping remaining ' + | |
186 (entries.length - begin) + | |
187 ' entries. Scan was canceled.'); | |
188 return Promise.resolve(); | |
189 } | |
190 | |
176 // the second arg to slice is an exclusive end index, so we +1 batch size. | 191 // the second arg to slice is an exclusive end index, so we +1 batch size. |
177 var end = begin + importer.DefaultMediaScanner.SCAN_BATCH_SIZE + 1; | 192 var end = begin + importer.DefaultMediaScanner.SCAN_BATCH_SIZE ; |
193 console.log(scan.name + ': Processing batch ' + begin + '-' + (end - 1)); | |
194 var batch = entries.slice(begin, end); | |
195 | |
178 return Promise.all( | 196 return Promise.all( |
179 entries.slice(begin, end).map(handleFileEntry)) | 197 batch.map(handleFileEntry)) |
180 .then( | 198 .then( |
199 /** @this {importer.DefaultMediaScanner} */ | |
181 function() { | 200 function() { |
182 if (end < entries.length) { | 201 if (end < entries.length) { |
183 return scanChunk(end); | 202 return scanBatch(end); |
184 } | 203 } |
185 }); | 204 }); |
186 }; | 205 }; |
187 | 206 |
188 return scanChunk(0); | 207 return scanBatch(0); |
189 }; | 208 }; |
190 | 209 |
191 /** | 210 /** |
192 * Notifies all listeners at some point in the near future. | 211 * Notifies all listeners at some point in the near future. |
193 * | 212 * |
194 * @param {!importer.ScanEvent} event | 213 * @param {!importer.ScanEvent} event |
195 * @param {!importer.DefaultScanResult} result | 214 * @param {!importer.DefaultScanResult} result |
196 * @private | 215 * @private |
197 */ | 216 */ |
198 importer.DefaultMediaScanner.prototype.notify_ = function(event, result) { | 217 importer.DefaultMediaScanner.prototype.notify_ = function(event, result) { |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
314 * @interface | 333 * @interface |
315 */ | 334 */ |
316 importer.ScanResult = function() {}; | 335 importer.ScanResult = function() {}; |
317 | 336 |
318 /** | 337 /** |
319 * @return {boolean} true if scanning is complete. | 338 * @return {boolean} true if scanning is complete. |
320 */ | 339 */ |
321 importer.ScanResult.prototype.isFinal; | 340 importer.ScanResult.prototype.isFinal; |
322 | 341 |
323 /** | 342 /** |
324 * @return {boolean} true if scanning is invalidated. | 343 * Notifies the scan to stop working. Some in progress work |
344 * may continue, but no new work will be undertaken. | |
325 */ | 345 */ |
326 importer.ScanResult.prototype.isInvalidated; | 346 importer.ScanResult.prototype.cancel; |
347 | |
348 /** | |
349 * @return {boolean} True if the scan has been canceled. Some | |
350 * work started prior to cancelation may still be ongoing. | |
351 */ | |
352 importer.ScanResult.prototype.canceled; | |
327 | 353 |
328 /** | 354 /** |
329 * Returns all files entries discovered so far. The list will be | 355 * Returns all files entries discovered so far. The list will be |
330 * complete only after scanning has completed and {@code isFinal} | 356 * complete only after scanning has completed and {@code isFinal} |
331 * returns {@code true}. | 357 * returns {@code true}. |
332 * | 358 * |
333 * @return {!Array<!FileEntry>} | 359 * @return {!Array<!FileEntry>} |
334 */ | 360 */ |
335 importer.ScanResult.prototype.getFileEntries; | 361 importer.ScanResult.prototype.getFileEntries; |
336 | 362 |
337 /** | 363 /** |
338 * Returns a promise that fires when scanning is complete. | 364 * Returns a promise that fires when scanning is finished |
365 * normally or has been canceled. | |
339 * | 366 * |
340 * @return {!Promise<!importer.ScanResult>} | 367 * @return {!Promise<!importer.ScanResult>} |
341 */ | 368 */ |
342 importer.ScanResult.prototype.whenFinal; | 369 importer.ScanResult.prototype.whenFinal; |
343 | 370 |
344 /** | 371 /** |
345 * @return {!importer.ScanResult.Statistics} | 372 * @return {!importer.ScanResult.Statistics} |
346 */ | 373 */ |
347 importer.ScanResult.prototype.getStatistics; | 374 importer.ScanResult.prototype.getStatistics; |
348 | 375 |
349 /** | 376 /** |
350 * @typedef {{ | 377 * @typedef {{ |
351 * scanDuration: number, | 378 * scanDuration: number, |
352 * newFileCount: number, | 379 * newFileCount: number, |
353 * duplicates: !Object<!importer.Disposition, number>, | 380 * duplicates: !Object<!importer.Disposition, number>, |
354 * sizeBytes: number | 381 * sizeBytes: number |
355 * }} | 382 * }} |
356 */ | 383 */ |
357 importer.ScanResult.Statistics; | 384 importer.ScanResult.Statistics; |
358 | 385 |
359 /** | 386 /** |
360 * Results of a scan operation. The object is "live" in that data can and | 387 * Results of a scan operation. The object is "live" in that data can and |
361 * will change as the scan operation discovers files. | 388 * will change as the scan operation discovers files. |
362 * | 389 * |
363 * <p>The scan is complete, and the object will become static once the | 390 * <p>The scan is complete, and the object will become static once the |
364 * {@code whenFinal} promise resolves. | 391 * {@code whenFinal} promise resolves. |
365 * | 392 * |
393 * Note that classes implementing this should provide a read-only | |
394 * {@code name} field. | |
395 * | |
366 * @constructor | 396 * @constructor |
367 * @struct | 397 * @struct |
368 * @implements {importer.ScanResult} | 398 * @implements {importer.ScanResult} |
369 * | 399 * |
370 * @param {function(!FileEntry): !Promise.<string>} hashGenerator Hash-code | 400 * @param {function(!FileEntry): !Promise.<string>} hashGenerator Hash-code |
371 * generator used to dedupe within the scan results itself. | 401 * generator used to dedupe within the scan results itself. |
372 */ | 402 */ |
373 importer.DefaultScanResult = function(hashGenerator) { | 403 importer.DefaultScanResult = function(hashGenerator) { |
374 | 404 |
mtomasz
2015/04/01 02:28:45
nit: Remove \n.
Steve McKay
2015/04/01 03:52:52
Done.
| |
405 /** @private {number} */ | |
406 this.scanId_ = importer.generateId(); | |
407 | |
375 /** @private {function(!FileEntry): !Promise.<string>} */ | 408 /** @private {function(!FileEntry): !Promise.<string>} */ |
376 this.createHashcode_ = hashGenerator; | 409 this.createHashcode_ = hashGenerator; |
377 | 410 |
378 /** | 411 /** |
379 * List of file entries found while scanning. | 412 * List of file entries found while scanning. |
380 * @private {!Array<!FileEntry>} | 413 * @private {!Array<!FileEntry>} |
381 */ | 414 */ |
382 this.fileEntries_ = []; | 415 this.fileEntries_ = []; |
383 | 416 |
384 /** | 417 /** |
(...skipping 18 matching lines...) Expand all Loading... | |
403 | 436 |
404 /** | 437 /** |
405 * The point in time when the last scan activity occured. | 438 * The point in time when the last scan activity occured. |
406 * @type {Date} | 439 * @type {Date} |
407 */ | 440 */ |
408 this.lastScanActivity_ = this.scanStarted_; | 441 this.lastScanActivity_ = this.scanStarted_; |
409 | 442 |
410 /** | 443 /** |
411 * @private {boolean} | 444 * @private {boolean} |
412 */ | 445 */ |
413 this.invalidated_ = false; | 446 this.canceled_ = false; |
414 | 447 |
415 /** @private {!importer.Resolver.<!importer.ScanResult>} */ | 448 /** @private {!importer.Resolver.<!importer.ScanResult>} */ |
416 this.resolver_ = new importer.Resolver(); | 449 this.resolver_ = new importer.Resolver(); |
417 }; | 450 }; |
418 | 451 |
419 /** @struct */ | 452 /** @struct */ |
420 importer.DefaultScanResult.prototype = { | 453 importer.DefaultScanResult.prototype = { |
421 | 454 |
mtomasz
2015/04/01 02:28:45
nit: Remove \n.
Steve McKay
2015/04/01 03:52:52
Done.
| |
455 /** @return {string} */ | |
456 get name() { return 'ScanResult(' + this.scanId_ + ')' }, | |
457 | |
422 /** @return {function()} */ | 458 /** @return {function()} */ |
423 get resolve() { return this.resolver_.resolve.bind(null, this); }, | 459 get resolve() { return this.resolver_.resolve.bind(null, this); }, |
424 | 460 |
425 /** @return {function(*=)} */ | 461 /** @return {function(*=)} */ |
426 get reject() { return this.resolver_.reject; } | 462 get reject() { return this.resolver_.reject; } |
427 }; | 463 }; |
428 | 464 |
429 /** @override */ | 465 /** @override */ |
430 importer.DefaultScanResult.prototype.isFinal = function() { | 466 importer.DefaultScanResult.prototype.isFinal = function() { |
431 return this.resolver_.settled; | 467 return this.resolver_.settled; |
432 }; | 468 }; |
433 | 469 |
434 importer.DefaultScanResult.prototype.isInvalidated = function() { | |
435 return this.invalidated_; | |
436 }; | |
437 | |
438 /** @override */ | 470 /** @override */ |
439 importer.DefaultScanResult.prototype.getFileEntries = function() { | 471 importer.DefaultScanResult.prototype.getFileEntries = function() { |
440 return this.fileEntries_; | 472 return this.fileEntries_; |
441 }; | 473 }; |
442 | 474 |
443 /** @override */ | 475 /** @override */ |
444 importer.DefaultScanResult.prototype.whenFinal = function() { | 476 importer.DefaultScanResult.prototype.whenFinal = function() { |
445 return this.resolver_.promise; | 477 return this.resolver_.promise; |
446 }; | 478 }; |
447 | 479 |
448 /** | 480 /** @override */ |
449 * Invalidates this scan. | 481 importer.DefaultScanResult.prototype.cancel = function() { |
450 */ | 482 this.canceled_ = true; |
451 importer.DefaultScanResult.prototype.invalidateScan = function() { | 483 }; |
452 this.invalidated_ = true; | 484 |
485 /** @override */ | |
486 importer.DefaultScanResult.prototype.canceled = function() { | |
487 return this.canceled_; | |
453 }; | 488 }; |
454 | 489 |
455 /** | 490 /** |
456 * Adds a file to results. | 491 * Adds a file to results. |
457 * | 492 * |
458 * @param {!FileEntry} entry | 493 * @param {!FileEntry} entry |
459 * @return {!Promise.<boolean>} True if the file as added, false if it was | 494 * @return {!Promise.<boolean>} True if the file as added, false if it was |
460 * rejected as a dupe. | 495 * rejected as a dupe. |
461 */ | 496 */ |
462 importer.DefaultScanResult.prototype.addFileEntry = function(entry) { | 497 importer.DefaultScanResult.prototype.addFileEntry = function(entry) { |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
590 if (!this.watchedDirectories_[event.entry.toURL()]) | 625 if (!this.watchedDirectories_[event.entry.toURL()]) |
591 return; | 626 return; |
592 this.triggered = true; | 627 this.triggered = true; |
593 for (var url in this.watchedDirectories_) { | 628 for (var url in this.watchedDirectories_) { |
594 chrome.fileManagerPrivate.removeFileWatch(url, function() {}); | 629 chrome.fileManagerPrivate.removeFileWatch(url, function() {}); |
595 } | 630 } |
596 chrome.fileManagerPrivate.onDirectoryChanged.removeListener( | 631 chrome.fileManagerPrivate.onDirectoryChanged.removeListener( |
597 assert(this.listener_)); | 632 assert(this.listener_)); |
598 this.callback_(); | 633 this.callback_(); |
599 }; | 634 }; |
OLD | NEW |