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 15 matching lines...) Expand all Loading... |
26 importer.MediaScanner.prototype.addObserver; | 26 importer.MediaScanner.prototype.addObserver; |
27 | 27 |
28 /** | 28 /** |
29 * Remove a previously registered observer. | 29 * Remove a previously registered observer. |
30 * | 30 * |
31 * @param {!importer.ScanObserver} observer | 31 * @param {!importer.ScanObserver} observer |
32 */ | 32 */ |
33 importer.MediaScanner.prototype.removeObserver; | 33 importer.MediaScanner.prototype.removeObserver; |
34 | 34 |
35 /** | 35 /** |
36 * Class representing the results of a scan operation. | |
37 * | |
38 * @interface | |
39 */ | |
40 importer.ScanResult = function() {}; | |
41 | |
42 /** | |
43 * @return {boolean} true if scanning is complete. | |
44 */ | |
45 importer.ScanResult.prototype.isFinal; | |
46 | |
47 /** | |
48 * @return {boolean} true if scanning is invalidated. | |
49 */ | |
50 importer.ScanResult.prototype.isInvalidated; | |
51 | |
52 /** | |
53 * Returns all files entries discovered so far. The list will be | |
54 * complete only after scanning has completed and {@code isFinal} | |
55 * returns {@code true}. | |
56 * | |
57 * @return {!Array.<!FileEntry>} | |
58 */ | |
59 importer.ScanResult.prototype.getFileEntries; | |
60 | |
61 /** | |
62 * Returns a promise that fires when scanning is complete. | |
63 * | |
64 * @return {!Promise.<!importer.ScanResult>} | |
65 */ | |
66 importer.ScanResult.prototype.whenFinal; | |
67 | |
68 /** | |
69 * @return {!importer.ScanResult.Statistics} | |
70 */ | |
71 importer.ScanResult.prototype.getStatistics; | |
72 | |
73 /** | |
74 * @typedef {{ | |
75 * scanDuration: number, | |
76 * newFileCount: number, | |
77 * duplicateFileCount: number, | |
78 * sizeBytes: number | |
79 * }} | |
80 */ | |
81 importer.ScanResult.Statistics; | |
82 | |
83 /** | |
84 * Recursively scans through a list of given files and directories, and creates | 36 * Recursively scans through a list of given files and directories, and creates |
85 * a list of media files. | 37 * a list of media files. |
86 * | 38 * |
87 * @constructor | 39 * @constructor |
88 * @struct | 40 * @struct |
89 * @implements {importer.MediaScanner} | 41 * @implements {importer.MediaScanner} |
90 * | 42 * |
91 * @param {function(!FileEntry): !Promise.<string>} hashGenerator | 43 * @param {function(!FileEntry): !Promise.<string>} hashGenerator |
92 * @param {!importer.HistoryLoader} historyLoader | 44 * @param {function(!FileEntry, !importer.Destination): |
| 45 * !Promise<!importer.Disposition>} dispositionChecker |
93 * @param {!importer.DirectoryWatcherFactory} watcherFactory | 46 * @param {!importer.DirectoryWatcherFactory} watcherFactory |
94 */ | 47 */ |
95 importer.DefaultMediaScanner = function( | 48 importer.DefaultMediaScanner = |
96 hashGenerator, historyLoader, watcherFactory) { | 49 function(hashGenerator, dispositionChecker, watcherFactory) { |
97 | |
98 /** @private {!importer.HistoryLoader} */ | |
99 this.historyLoader_ = historyLoader; | |
100 | 50 |
101 /** | 51 /** |
102 * A little factory for DefaultScanResults which allows us to forgo | 52 * A little factory for DefaultScanResults which allows us to forgo |
103 * the saving it's dependencies in our fields. | 53 * the saving it's dependencies in our fields. |
104 * @return {!importer.DefaultScanResult} | 54 * @return {!importer.DefaultScanResult} |
105 */ | 55 */ |
106 this.createScanResult_ = function() { | 56 this.createScanResult_ = function() { |
107 return new importer.DefaultScanResult(hashGenerator); | 57 return new importer.DefaultScanResult(hashGenerator); |
108 }; | 58 }; |
109 | 59 |
110 /** @private {!Array.<!importer.ScanObserver>} */ | 60 /** @private {!Array.<!importer.ScanObserver>} */ |
111 this.observers_ = []; | 61 this.observers_ = []; |
112 | 62 |
113 /** | 63 /** |
| 64 * @param {!FileEntry} entry |
| 65 * @param {!importer.Destination} destination |
| 66 * @return {!Promise<!importer.Disposition>} |
| 67 */ |
| 68 this.getDisposition_ = dispositionChecker; |
| 69 |
| 70 /** |
114 * @private {!importer.DirectoryWatcherFactory} | 71 * @private {!importer.DirectoryWatcherFactory} |
115 * @const | 72 * @const |
116 */ | 73 */ |
117 this.watcherFactory_ = watcherFactory; | 74 this.watcherFactory_ = watcherFactory; |
118 }; | 75 }; |
119 | 76 |
120 /** @override */ | 77 /** @override */ |
121 importer.DefaultMediaScanner.prototype.addObserver = function(observer) { | 78 importer.DefaultMediaScanner.prototype.addObserver = function(observer) { |
122 this.observers_.push(observer); | 79 this.observers_.push(observer); |
123 }; | 80 }; |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
251 /** | 208 /** |
252 * Finds all files beneath directory. | 209 * Finds all files beneath directory. |
253 * | 210 * |
254 * @param {!importer.DefaultScanResult} scan | 211 * @param {!importer.DefaultScanResult} scan |
255 * @param {!FileEntry} entry | 212 * @param {!FileEntry} entry |
256 * @return {!Promise} | 213 * @return {!Promise} |
257 * @private | 214 * @private |
258 */ | 215 */ |
259 importer.DefaultMediaScanner.prototype.onFileEntryFound_ = | 216 importer.DefaultMediaScanner.prototype.onFileEntryFound_ = |
260 function(scan, entry) { | 217 function(scan, entry) { |
261 return this.hasHistoryDuplicate_(entry) | 218 return this.getDisposition_(entry, importer.Destination.GOOGLE_DRIVE) |
262 .then( | 219 .then( |
263 /** | 220 /** |
264 * @param {boolean} duplicate | 221 * @param {!importer.Disposition} disposition The disposition |
| 222 * of the entry. Either some sort of dupe, or an original. |
265 * @return {!Promise} | 223 * @return {!Promise} |
266 * @this {importer.DefaultMediaScanner} | 224 * @this {importer.DefaultMediaScanner} |
267 */ | 225 */ |
268 function(duplicate) { | 226 function(disposition) { |
269 return duplicate ? | 227 return disposition === importer.Disposition.ORIGINAL ? |
270 this.onDuplicateFileFound_(scan, entry) : | 228 this.onUniqueFileFound_(scan, entry) : |
271 this.onUniqueFileFound_(scan, entry); | 229 this.onDuplicateFileFound_(scan, entry, disposition); |
272 }.bind(this)); | 230 }.bind(this)); |
273 }; | 231 }; |
274 | 232 |
275 /** | 233 /** |
276 * Adds a newly discovered file to the given scan result. | 234 * Adds a newly discovered file to the given scan result. |
277 * | 235 * |
278 * @param {!importer.DefaultScanResult} scan | 236 * @param {!importer.DefaultScanResult} scan |
279 * @param {!FileEntry} entry | 237 * @param {!FileEntry} entry |
280 * @return {!Promise} | 238 * @return {!Promise} |
281 * @private | 239 * @private |
(...skipping 17 matching lines...) Expand all Loading... |
299 } | 257 } |
300 }.bind(this)); | 258 }.bind(this)); |
301 }; | 259 }; |
302 | 260 |
303 /** | 261 /** |
304 * Adds a duplicate file to the given scan result. This is to track the number | 262 * Adds a duplicate file to the given scan result. This is to track the number |
305 * of duplicates that are being encountered. | 263 * of duplicates that are being encountered. |
306 * | 264 * |
307 * @param {!importer.DefaultScanResult} scan | 265 * @param {!importer.DefaultScanResult} scan |
308 * @param {!FileEntry} entry | 266 * @param {!FileEntry} entry |
| 267 * @param {!importer.Disposition} disposition |
309 * @return {!Promise} | 268 * @return {!Promise} |
310 * @private | 269 * @private |
311 */ | 270 */ |
312 importer.DefaultMediaScanner.prototype.onDuplicateFileFound_ = | 271 importer.DefaultMediaScanner.prototype.onDuplicateFileFound_ = |
313 function(scan, entry) { | 272 function(scan, entry, disposition) { |
314 scan.addDuplicateEntry(entry); | 273 scan.addDuplicateEntry(entry, disposition); |
315 return Promise.resolve(); | 274 return Promise.resolve(); |
316 }; | 275 }; |
317 | 276 |
318 /** | 277 /** |
319 * @param {!FileEntry} entry | 278 * Class representing the results of a scan operation. |
320 * @return {!Promise.<boolean>} True if there is a history-entry-duplicate | 279 * |
321 * for the file. | 280 * @interface |
322 * @private | |
323 */ | 281 */ |
324 importer.DefaultMediaScanner.prototype.hasHistoryDuplicate_ = function(entry) { | 282 importer.ScanResult = function() {}; |
325 return this.historyLoader_.getHistory() | 283 |
326 .then( | 284 /** |
327 /** | 285 * @return {boolean} true if scanning is complete. |
328 * @param {!importer.ImportHistory} history | 286 */ |
329 * @return {!Promise} | 287 importer.ScanResult.prototype.isFinal; |
330 * @this {importer.DefaultMediaScanner} | 288 |
331 */ | 289 /** |
332 function(history) { | 290 * @return {boolean} true if scanning is invalidated. |
333 return Promise.all([ | 291 */ |
334 history.wasCopied(entry, importer.Destination.GOOGLE_DRIVE), | 292 importer.ScanResult.prototype.isInvalidated; |
335 history.wasImported(entry, importer.Destination.GOOGLE_DRIVE) | 293 |
336 ]).then( | 294 /** |
337 /** | 295 * Returns all files entries discovered so far. The list will be |
338 * @param {!Array.<boolean>} results | 296 * complete only after scanning has completed and {@code isFinal} |
339 * @return {!Promise} | 297 * returns {@code true}. |
340 * @this {importer.DefaultMediaScanner} | 298 * |
341 */ | 299 * @return {!Array<!FileEntry>} |
342 function(results) { | 300 */ |
343 return results[0] || results[1]; | 301 importer.ScanResult.prototype.getFileEntries; |
344 }.bind(this)); | 302 |
345 }.bind(this)); | 303 /** |
346 }; | 304 * Returns a promise that fires when scanning is complete. |
| 305 * |
| 306 * @return {!Promise<!importer.ScanResult>} |
| 307 */ |
| 308 importer.ScanResult.prototype.whenFinal; |
| 309 |
| 310 /** |
| 311 * @return {!importer.ScanResult.Statistics} |
| 312 */ |
| 313 importer.ScanResult.prototype.getStatistics; |
| 314 |
| 315 /** |
| 316 * @typedef {{ |
| 317 * scanDuration: number, |
| 318 * newFileCount: number, |
| 319 * duplicates: !Object<!importer.Disposition, number>, |
| 320 * sizeBytes: number |
| 321 * }} |
| 322 */ |
| 323 importer.ScanResult.Statistics; |
347 | 324 |
348 /** | 325 /** |
349 * Results of a scan operation. The object is "live" in that data can and | 326 * Results of a scan operation. The object is "live" in that data can and |
350 * will change as the scan operation discovers files. | 327 * will change as the scan operation discovers files. |
351 * | 328 * |
352 * <p>The scan is complete, and the object will become static once the | 329 * <p>The scan is complete, and the object will become static once the |
353 * {@code whenFinal} promise resolves. | 330 * {@code whenFinal} promise resolves. |
354 * | 331 * |
355 * @constructor | 332 * @constructor |
356 * @struct | 333 * @struct |
(...skipping 17 matching lines...) Expand all Loading... |
374 * Hashcodes of all files included captured by this result object so-far. | 351 * Hashcodes of all files included captured by this result object so-far. |
375 * Used to dedupe newly discovered files against other files withing | 352 * Used to dedupe newly discovered files against other files withing |
376 * the ScanResult. | 353 * the ScanResult. |
377 * @private {!Object.<string, !FileEntry>} | 354 * @private {!Object.<string, !FileEntry>} |
378 */ | 355 */ |
379 this.fileHashcodes_ = {}; | 356 this.fileHashcodes_ = {}; |
380 | 357 |
381 /** @private {number} */ | 358 /** @private {number} */ |
382 this.totalBytes_ = 0; | 359 this.totalBytes_ = 0; |
383 | 360 |
384 /** @private {number} */ | 361 /** @private {!Object<!importer.Disposition, number>} */ |
385 this.duplicateFileCount_ = 0; | 362 this.duplicates_ = {}; |
386 | 363 |
387 /** | 364 /** |
388 * The point in time when the scan was started. | 365 * The point in time when the scan was started. |
389 * @type {Date} | 366 * @type {Date} |
390 */ | 367 */ |
391 this.scanStarted_ = new Date(); | 368 this.scanStarted_ = new Date(); |
392 | 369 |
393 /** | 370 /** |
394 * The point in time when the last scan activity occured. | 371 * The point in time when the last scan activity occured. |
395 * @type {Date} | 372 * @type {Date} |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
462 return this.createHashcode_(entry) | 439 return this.createHashcode_(entry) |
463 .then( | 440 .then( |
464 /** | 441 /** |
465 * @param {string} hashcode | 442 * @param {string} hashcode |
466 * @this {importer.DefaultScanResult} | 443 * @this {importer.DefaultScanResult} |
467 */ | 444 */ |
468 function(hashcode) { | 445 function(hashcode) { |
469 this.lastScanActivity_ = new Date(); | 446 this.lastScanActivity_ = new Date(); |
470 | 447 |
471 if (hashcode in this.fileHashcodes_) { | 448 if (hashcode in this.fileHashcodes_) { |
472 this.addDuplicateEntry(entry); | 449 this.addDuplicateEntry( |
| 450 entry, |
| 451 importer.Disposition.SCAN_DUPLICATE); |
473 return false; | 452 return false; |
474 } | 453 } |
475 | 454 |
476 entry.size = metadata.size; | 455 entry.size = metadata.size; |
477 this.totalBytes_ += metadata.size; | 456 this.totalBytes_ += metadata.size; |
478 this.fileHashcodes_[hashcode] = entry; | 457 this.fileHashcodes_[hashcode] = entry; |
479 this.fileEntries_.push(entry); | 458 this.fileEntries_.push(entry); |
480 return true; | 459 return true; |
481 }.bind(this)); | 460 }.bind(this)); |
482 | 461 |
483 }.bind(this)); | 462 }.bind(this)); |
484 }; | 463 }; |
485 | 464 |
486 /** | 465 /** |
487 * Logs the fact that a duplicate file entry was discovered during the scan. | 466 * Logs the fact that a duplicate file entry was discovered during the scan. |
488 * @param {!FileEntry} entry | 467 * @param {!FileEntry} entry |
| 468 * @param {!importer.Disposition} disposition |
489 */ | 469 */ |
490 importer.DefaultScanResult.prototype.addDuplicateEntry = function(entry) { | 470 importer.DefaultScanResult.prototype.addDuplicateEntry = |
491 this.duplicateFileCount_++; | 471 function(entry, disposition) { |
| 472 if (!(disposition in this.duplicates_)) { |
| 473 this.duplicates_[disposition] = 0; |
| 474 } |
| 475 this.duplicates_[disposition]++; |
492 }; | 476 }; |
493 | 477 |
494 /** @override */ | 478 /** @override */ |
495 importer.DefaultScanResult.prototype.getStatistics = function() { | 479 importer.DefaultScanResult.prototype.getStatistics = function() { |
496 return { | 480 return { |
497 scanDuration: | 481 scanDuration: |
498 this.lastScanActivity_.getTime() - this.scanStarted_.getTime(), | 482 this.lastScanActivity_.getTime() - this.scanStarted_.getTime(), |
499 newFileCount: this.fileEntries_.length, | 483 newFileCount: this.fileEntries_.length, |
500 duplicateFileCount: this.duplicateFileCount_, | 484 duplicates: this.duplicates_, |
501 sizeBytes: this.totalBytes_ | 485 sizeBytes: this.totalBytes_ |
502 }; | 486 }; |
503 }; | 487 }; |
504 | 488 |
505 /** | 489 /** |
506 * Watcher for directories. | 490 * Watcher for directories. |
507 * @interface | 491 * @interface |
508 */ | 492 */ |
509 importer.DirectoryWatcher = function() {}; | 493 importer.DirectoryWatcher = function() {}; |
510 | 494 |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
572 if (!this.watchedDirectories_[event.entry.toURL()]) | 556 if (!this.watchedDirectories_[event.entry.toURL()]) |
573 return; | 557 return; |
574 this.triggered = true; | 558 this.triggered = true; |
575 for (var url in this.watchedDirectories_) { | 559 for (var url in this.watchedDirectories_) { |
576 chrome.fileManagerPrivate.removeFileWatch(url, function() {}); | 560 chrome.fileManagerPrivate.removeFileWatch(url, function() {}); |
577 } | 561 } |
578 chrome.fileManagerPrivate.onDirectoryChanged.removeListener( | 562 chrome.fileManagerPrivate.onDirectoryChanged.removeListener( |
579 assert(this.listener_)); | 563 assert(this.listener_)); |
580 this.callback_(); | 564 this.callback_(); |
581 }; | 565 }; |
OLD | NEW |