| 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 // Namespace | 5 // Namespace |
| 6 var importer = importer || {}; | 6 var importer = importer || {}; |
| 7 | 7 |
| 8 /** | 8 /** |
| 9 * Interface providing access to information about active import processes. | 9 * Interface providing access to information about active import processes. |
| 10 * | 10 * |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 | 25 |
| 26 /** | 26 /** |
| 27 * Handler for importing media from removable devices into the user's Drive. | 27 * Handler for importing media from removable devices into the user's Drive. |
| 28 * | 28 * |
| 29 * @constructor | 29 * @constructor |
| 30 * @implements {importer.ImportRunner} | 30 * @implements {importer.ImportRunner} |
| 31 * @struct | 31 * @struct |
| 32 * | 32 * |
| 33 * @param {!ProgressCenter} progressCenter | 33 * @param {!ProgressCenter} progressCenter |
| 34 * @param {!importer.HistoryLoader} historyLoader | 34 * @param {!importer.HistoryLoader} historyLoader |
| 35 * @param {!importer.DuplicateFinder.Factory} duplicateFinderFactory | |
| 36 * @param {!analytics.Tracker} tracker | 35 * @param {!analytics.Tracker} tracker |
| 37 */ | 36 */ |
| 38 importer.MediaImportHandler = | 37 importer.MediaImportHandler = function(progressCenter, historyLoader, tracker) { |
| 39 function(progressCenter, historyLoader, duplicateFinderFactory, tracker) { | |
| 40 /** @private {!ProgressCenter} */ | 38 /** @private {!ProgressCenter} */ |
| 41 this.progressCenter_ = progressCenter; | 39 this.progressCenter_ = progressCenter; |
| 42 | 40 |
| 43 /** @private {!importer.HistoryLoader} */ | 41 /** @private {!importer.HistoryLoader} */ |
| 44 this.historyLoader_ = historyLoader; | 42 this.historyLoader_ = historyLoader; |
| 45 | 43 |
| 46 /** @private {!importer.TaskQueue} */ | 44 /** @private {!importer.TaskQueue} */ |
| 47 this.queue_ = new importer.TaskQueue(); | 45 this.queue_ = new importer.TaskQueue(); |
| 48 | 46 |
| 49 // Prevent the system from sleeping while imports are active. | 47 // Prevent the system from sleeping while imports are active. |
| 50 this.queue_.setActiveCallback(function() { | 48 this.queue_.setActiveCallback(function() { |
| 51 chrome.power.requestKeepAwake('system'); | 49 chrome.power.requestKeepAwake('system'); |
| 52 }); | 50 }); |
| 53 this.queue_.setIdleCallback(function() { | 51 this.queue_.setIdleCallback(function() { |
| 54 chrome.power.releaseKeepAwake(); | 52 chrome.power.releaseKeepAwake(); |
| 55 }); | 53 }); |
| 56 | 54 |
| 57 /** @private {!importer.DuplicateFinder.Factory} */ | |
| 58 this.duplicateFinderFactory_ = duplicateFinderFactory; | |
| 59 | |
| 60 /** @private {!analytics.Tracker} */ | 55 /** @private {!analytics.Tracker} */ |
| 61 this.tracker_ = tracker; | 56 this.tracker_ = tracker; |
| 62 | 57 |
| 63 /** @private {number} */ | 58 /** @private {number} */ |
| 64 this.nextTaskId_ = 0; | 59 this.nextTaskId_ = 0; |
| 65 }; | 60 }; |
| 66 | 61 |
| 67 /** @override */ | 62 /** @override */ |
| 68 importer.MediaImportHandler.prototype.importFromScanResult = | 63 importer.MediaImportHandler.prototype.importFromScanResult = |
| 69 function(scanResult, destination, directoryPromise) { | 64 function(scanResult, destination, directoryPromise) { |
| 70 | 65 |
| 71 var task = new importer.MediaImportHandler.ImportTask( | 66 var task = new importer.MediaImportHandler.ImportTask( |
| 72 this.generateTaskId_(), | 67 this.generateTaskId_(), |
| 73 this.historyLoader_, | 68 this.historyLoader_, |
| 74 scanResult, | 69 scanResult, |
| 75 directoryPromise, | 70 directoryPromise, |
| 76 this.duplicateFinderFactory_.create(), | |
| 77 destination, | 71 destination, |
| 78 this.tracker_); | 72 this.tracker_); |
| 79 | 73 |
| 80 task.addObserver(this.onTaskProgress_.bind(this, task)); | 74 task.addObserver(this.onTaskProgress_.bind(this, task)); |
| 81 | 75 |
| 82 this.queue_.queueTask(task); | 76 this.queue_.queueTask(task); |
| 83 | 77 |
| 84 return task; | 78 return task; |
| 85 }; | 79 }; |
| 86 | 80 |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 148 * FileOperationManager.CopyTask) but this is a temporary state of affairs. | 142 * FileOperationManager.CopyTask) but this is a temporary state of affairs. |
| 149 * | 143 * |
| 150 * @constructor | 144 * @constructor |
| 151 * @extends {importer.TaskQueue.BaseTask} | 145 * @extends {importer.TaskQueue.BaseTask} |
| 152 * @struct | 146 * @struct |
| 153 * | 147 * |
| 154 * @param {string} taskId | 148 * @param {string} taskId |
| 155 * @param {!importer.HistoryLoader} historyLoader | 149 * @param {!importer.HistoryLoader} historyLoader |
| 156 * @param {!importer.ScanResult} scanResult | 150 * @param {!importer.ScanResult} scanResult |
| 157 * @param {!Promise<!DirectoryEntry>} directoryPromise | 151 * @param {!Promise<!DirectoryEntry>} directoryPromise |
| 158 * @param {!importer.DuplicateFinder} duplicateFinder A duplicate-finder linked | |
| 159 * to the import destination, that will be used to deduplicate imports. | |
| 160 * @param {!importer.Destination} destination The logical destination. | 152 * @param {!importer.Destination} destination The logical destination. |
| 161 * @param {!analytics.Tracker} tracker | 153 * @param {!analytics.Tracker} tracker |
| 162 */ | 154 */ |
| 163 importer.MediaImportHandler.ImportTask = function( | 155 importer.MediaImportHandler.ImportTask = function( |
| 164 taskId, | 156 taskId, |
| 165 historyLoader, | 157 historyLoader, |
| 166 scanResult, | 158 scanResult, |
| 167 directoryPromise, | 159 directoryPromise, |
| 168 duplicateFinder, | |
| 169 destination, | 160 destination, |
| 170 tracker) { | 161 tracker) { |
| 171 | 162 |
| 172 importer.TaskQueue.BaseTask.call(this, taskId); | 163 importer.TaskQueue.BaseTask.call(this, taskId); |
| 173 /** @private {string} */ | 164 /** @private {string} */ |
| 174 this.taskId_ = taskId; | 165 this.taskId_ = taskId; |
| 175 | 166 |
| 176 /** @private {!importer.Destination} */ | 167 /** @private {!importer.Destination} */ |
| 177 this.destination_ = destination; | 168 this.destination_ = destination; |
| 178 | 169 |
| 179 /** @private {!Promise<!DirectoryEntry>} */ | 170 /** @private {!Promise<!DirectoryEntry>} */ |
| 180 this.directoryPromise_ = directoryPromise; | 171 this.directoryPromise_ = directoryPromise; |
| 181 | 172 |
| 182 /** @private {!importer.DuplicateFinder} */ | |
| 183 this.deduplicator_ = duplicateFinder; | |
| 184 | |
| 185 /** @private {!importer.ScanResult} */ | 173 /** @private {!importer.ScanResult} */ |
| 186 this.scanResult_ = scanResult; | 174 this.scanResult_ = scanResult; |
| 187 | 175 |
| 188 /** @private {!importer.HistoryLoader} */ | 176 /** @private {!importer.HistoryLoader} */ |
| 189 this.historyLoader_ = historyLoader; | 177 this.historyLoader_ = historyLoader; |
| 190 | 178 |
| 191 /** @private {!analytics.Tracker} */ | 179 /** @private {!analytics.Tracker} */ |
| 192 this.tracker_ = tracker; | 180 this.tracker_ = tracker; |
| 193 | 181 |
| 194 /** @private {number} */ | 182 /** @private {number} */ |
| 195 this.totalBytes_ = 0; | 183 this.totalBytes_ = 0; |
| 196 | 184 |
| 197 /** @private {number} */ | 185 /** @private {number} */ |
| 198 this.processedBytes_ = 0; | 186 this.processedBytes_ = 0; |
| 199 | 187 |
| 200 /** @private {number} */ | 188 /** @private {number} */ |
| 201 this.remainingFilesCount_ = 0; | 189 this.remainingFilesCount_ = 0; |
| 202 | 190 |
| 203 /** @private {?function()} */ | 191 /** @private {?function()} */ |
| 204 this.cancelCallback_ = null; | 192 this.cancelCallback_ = null; |
| 205 | 193 |
| 206 /** @private {boolean} Indicates whether this task was canceled. */ | 194 /** @private {boolean} Indicates whether this task was canceled. */ |
| 207 this.canceled_ = false; | 195 this.canceled_ = false; |
| 208 | 196 |
| 209 /** @private {number} Number of files deduped by content dedupe. */ | |
| 210 this.dedupeCount_ = 0; | |
| 211 | |
| 212 /** @private {number} */ | 197 /** @private {number} */ |
| 213 this.errorCount_ = 0; | 198 this.errorCount_ = 0; |
| 214 }; | 199 }; |
| 215 | 200 |
| 216 /** @struct */ | 201 /** @struct */ |
| 217 importer.MediaImportHandler.ImportTask.prototype = { | 202 importer.MediaImportHandler.ImportTask.prototype = { |
| 218 /** @return {number} Number of imported bytes */ | 203 /** @return {number} Number of imported bytes */ |
| 219 get processedBytes() { return this.processedBytes_; }, | 204 get processedBytes() { return this.processedBytes_; }, |
| 220 | 205 |
| 221 /** @return {number} Total number of bytes to import */ | 206 /** @return {number} Total number of bytes to import */ |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 270 // (including calling #requestCancel again). | 255 // (including calling #requestCancel again). |
| 271 var cancelCallback = this.cancelCallback_; | 256 var cancelCallback = this.cancelCallback_; |
| 272 this.cancelCallback_ = null; | 257 this.cancelCallback_ = null; |
| 273 cancelCallback(); | 258 cancelCallback(); |
| 274 } | 259 } |
| 275 }; | 260 }; |
| 276 | 261 |
| 277 /** @private */ | 262 /** @private */ |
| 278 importer.MediaImportHandler.ImportTask.prototype.initialize_ = function() { | 263 importer.MediaImportHandler.ImportTask.prototype.initialize_ = function() { |
| 279 var stats = this.scanResult_.getStatistics(); | 264 var stats = this.scanResult_.getStatistics(); |
| 280 | |
| 281 this.remainingFilesCount_ = stats.newFileCount; | 265 this.remainingFilesCount_ = stats.newFileCount; |
| 282 this.totalBytes_ = stats.sizeBytes; | 266 this.totalBytes_ = stats.sizeBytes; |
| 283 this.notify(importer.TaskQueue.UpdateType.PROGRESS); | 267 this.notify(importer.TaskQueue.UpdateType.PROGRESS); |
| 284 | 268 |
| 285 this.tracker_.send(metrics.ImportEvents.STARTED); | 269 this.tracker_.send(metrics.ImportEvents.STARTED); |
| 286 this.tracker_.send(metrics.ImportEvents.HISTORY_DEDUPE_COUNT | |
| 287 .value(stats.duplicateFileCount)); | |
| 288 }; | 270 }; |
| 289 | 271 |
| 290 /** | 272 /** |
| 291 * Initiates an import to the given location. This should only be called once | 273 * Initiates an import to the given location. This should only be called once |
| 292 * the scan result indicates that it is ready. | 274 * the scan result indicates that it is ready. |
| 293 * | 275 * |
| 294 * @private | 276 * @private |
| 295 */ | 277 */ |
| 296 importer.MediaImportHandler.ImportTask.prototype.importScanEntries_ = | 278 importer.MediaImportHandler.ImportTask.prototype.importScanEntries_ = |
| 297 function() { | 279 function() { |
| (...skipping 16 matching lines...) Expand all Loading... |
| 314 */ | 296 */ |
| 315 importer.MediaImportHandler.ImportTask.prototype.importOne_ = | 297 importer.MediaImportHandler.ImportTask.prototype.importOne_ = |
| 316 function(destinationDirectory, completionCallback, entry) { | 298 function(destinationDirectory, completionCallback, entry) { |
| 317 if (this.canceled_) { | 299 if (this.canceled_) { |
| 318 this.notify(importer.TaskQueue.UpdateType.CANCELED); | 300 this.notify(importer.TaskQueue.UpdateType.CANCELED); |
| 319 this.tracker_.send(metrics.ImportEvents.CANCELLED); | 301 this.tracker_.send(metrics.ImportEvents.CANCELLED); |
| 320 this.sendImportStats_(); | 302 this.sendImportStats_(); |
| 321 return; | 303 return; |
| 322 } | 304 } |
| 323 | 305 |
| 324 this.deduplicator_.checkDuplicate(entry) | 306 this.copy_(entry, destinationDirectory) |
| 325 .then( | |
| 326 /** @param {boolean} isDuplicate */ | |
| 327 function(isDuplicate) { | |
| 328 if (isDuplicate) { | |
| 329 // If the given file is a duplicate, don't import it again. Just | |
| 330 // update the progress indicator. | |
| 331 this.dedupeCount_++; | |
| 332 this.markAsImported_(entry); | |
| 333 this.processedBytes_ += entry.size; | |
| 334 this.notify(importer.TaskQueue.UpdateType.PROGRESS); | |
| 335 return Promise.resolve(); | |
| 336 } else { | |
| 337 return this.copy_(entry, destinationDirectory); | |
| 338 } | |
| 339 }.bind(this)) | |
| 340 // Regardless of the result of this copy, push on to the next file. | 307 // Regardless of the result of this copy, push on to the next file. |
| 341 .then(completionCallback) | 308 .then(completionCallback) |
| 342 .catch( | 309 .catch( |
| 343 /** @param {*} error */ | 310 /** @param {*} error */ |
| 344 function(error) { | 311 function(error) { |
| 345 importer.getLogger().catcher('import-task-import-one')(error); | 312 importer.getLogger().catcher('import-task-import-one')(error); |
| 346 completionCallback(); | 313 completionCallback(); |
| 347 }); | 314 }); |
| 348 }; | 315 }; |
| 349 | 316 |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 472 /** @param {!importer.ImportHistory} history */ | 439 /** @param {!importer.ImportHistory} history */ |
| 473 function(history) { | 440 function(history) { |
| 474 history.markImported(entry, this.destination_); | 441 history.markImported(entry, this.destination_); |
| 475 }.bind(this)) | 442 }.bind(this)) |
| 476 .catch(importer.getLogger().catcher('import-task-mark-as-imported')); | 443 .catch(importer.getLogger().catcher('import-task-mark-as-imported')); |
| 477 }; | 444 }; |
| 478 | 445 |
| 479 /** @private */ | 446 /** @private */ |
| 480 importer.MediaImportHandler.ImportTask.prototype.onSuccess_ = function() { | 447 importer.MediaImportHandler.ImportTask.prototype.onSuccess_ = function() { |
| 481 this.notify(importer.TaskQueue.UpdateType.COMPLETE); | 448 this.notify(importer.TaskQueue.UpdateType.COMPLETE); |
| 482 this.tracker_.send(metrics.ImportEvents.ENDED); | |
| 483 this.sendImportStats_(); | 449 this.sendImportStats_(); |
| 484 }; | 450 }; |
| 485 | 451 |
| 486 /** | 452 /** |
| 487 * Sends import statistics to analytics. | 453 * Sends import statistics to analytics. |
| 488 */ | 454 */ |
| 489 importer.MediaImportHandler.ImportTask.prototype.sendImportStats_ = function() { | 455 importer.MediaImportHandler.ImportTask.prototype.sendImportStats_ = |
| 456 function() { |
| 457 |
| 458 var scanStats = this.scanResult_.getStatistics(); |
| 459 |
| 490 this.tracker_.send( | 460 this.tracker_.send( |
| 491 metrics.ImportEvents.CONTENT_DEDUPE_COUNT | 461 metrics.ImportEvents.MEGABYTES_IMPORTED.value( |
| 492 .value(this.dedupeCount_)); | 462 // Make megabytes of our bytes. |
| 493 // TODO(kenobi): Send correct import byte counts. | 463 Math.floor(this.processedBytes_ / (1024 * 1024)))); |
| 494 var importFileCount = this.scanResult_.getStatistics().newFileCount - | 464 |
| 495 (this.dedupeCount_ + this.remainingFilesCount_); | |
| 496 this.tracker_.send( | 465 this.tracker_.send( |
| 497 metrics.ImportEvents.FILE_COUNT | 466 metrics.ImportEvents.FILES_IMPORTED.value( |
| 498 .value(importFileCount)); | 467 // Substract the remaining files, in case the task was cancelled. |
| 468 scanStats.newFileCount - this.remainingFilesCount_)); |
| 499 | 469 |
| 500 this.tracker_.send(metrics.ImportEvents.ERROR.value(this.errorCount_)); | 470 if (this.errorCount_ > 0) { |
| 471 this.tracker_.send(metrics.ImportEvents.ERRORS.value(this.errorCount_)); |
| 472 } |
| 501 | 473 |
| 502 // Send aggregate deduplication timings, to avoid flooding analytics with one | 474 // Finally we want to report on the number of duplicates |
| 503 // timing per file. | 475 // that were identified during scanning. |
| 504 var deduplicatorStats = this.deduplicator_.getStatistics(); | 476 var totalDeduped = 0; |
| 505 this.tracker_.sendTiming( | 477 Object.keys(scanStats.duplicates).forEach( |
| 506 metrics.Categories.ACQUISITION, | 478 /** |
| 507 metrics.timing.Variables.COMPUTE_HASH, | 479 * @param {!importer.Disposition} disposition |
| 508 deduplicatorStats.computeHashTime, | 480 * @this {importer.MediaImportHandler.ImportTask} |
| 509 'In Place'); | 481 */ |
| 510 this.tracker_.sendTiming( | 482 function(disposition) { |
| 511 metrics.Categories.ACQUISITION, | 483 var count = scanStats.duplicates[disposition]; |
| 512 metrics.timing.Variables.SEARCH_BY_HASH, | 484 totalDeduped += count; |
| 513 deduplicatorStats.searchHashTime); | 485 this.tracker_.send( |
| 486 metrics.ImportEvents.FILES_DEDUPLICATED |
| 487 .label(disposition) |
| 488 .value(count)); |
| 489 }.bind(this)); |
| 514 | 490 |
| 491 this.tracker_.send( |
| 492 metrics.ImportEvents.FILES_DEDUPLICATED |
| 493 .label('all-duplicates') |
| 494 .value(totalDeduped)); |
| 515 }; | 495 }; |
| OLD | NEW |