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 = |
mtomasz
2015/03/05 04:36:53
nit: Will it fit in one line? If not, then we brea
Steve McKay
2015/03/05 21:39:03
Done.
| |
39 function(progressCenter, historyLoader, duplicateFinderFactory, tracker) { | 38 function(progressCenter, historyLoader, tracker) { |
40 /** @private {!ProgressCenter} */ | 39 /** @private {!ProgressCenter} */ |
41 this.progressCenter_ = progressCenter; | 40 this.progressCenter_ = progressCenter; |
42 | 41 |
43 /** @private {!importer.HistoryLoader} */ | 42 /** @private {!importer.HistoryLoader} */ |
44 this.historyLoader_ = historyLoader; | 43 this.historyLoader_ = historyLoader; |
45 | 44 |
46 /** @private {!importer.TaskQueue} */ | 45 /** @private {!importer.TaskQueue} */ |
47 this.queue_ = new importer.TaskQueue(); | 46 this.queue_ = new importer.TaskQueue(); |
48 | 47 |
49 // Prevent the system from sleeping while imports are active. | 48 // Prevent the system from sleeping while imports are active. |
50 this.queue_.setActiveCallback(function() { | 49 this.queue_.setActiveCallback(function() { |
51 chrome.power.requestKeepAwake('system'); | 50 chrome.power.requestKeepAwake('system'); |
52 }); | 51 }); |
53 this.queue_.setIdleCallback(function() { | 52 this.queue_.setIdleCallback(function() { |
54 chrome.power.releaseKeepAwake(); | 53 chrome.power.releaseKeepAwake(); |
55 }); | 54 }); |
56 | 55 |
57 /** @private {!importer.DuplicateFinder.Factory} */ | |
58 this.duplicateFinderFactory_ = duplicateFinderFactory; | |
59 | |
60 /** @private {!analytics.Tracker} */ | 56 /** @private {!analytics.Tracker} */ |
61 this.tracker_ = tracker; | 57 this.tracker_ = tracker; |
62 | 58 |
63 /** @private {number} */ | 59 /** @private {number} */ |
64 this.nextTaskId_ = 0; | 60 this.nextTaskId_ = 0; |
65 }; | 61 }; |
66 | 62 |
67 /** @override */ | 63 /** @override */ |
68 importer.MediaImportHandler.prototype.importFromScanResult = | 64 importer.MediaImportHandler.prototype.importFromScanResult = |
69 function(scanResult, destination, directoryPromise) { | 65 function(scanResult, destination, directoryPromise) { |
70 | 66 |
71 var task = new importer.MediaImportHandler.ImportTask( | 67 var task = new importer.MediaImportHandler.ImportTask( |
72 this.generateTaskId_(), | 68 this.generateTaskId_(), |
73 this.historyLoader_, | 69 this.historyLoader_, |
74 scanResult, | 70 scanResult, |
75 directoryPromise, | 71 directoryPromise, |
76 this.duplicateFinderFactory_.create(), | |
77 destination, | 72 destination, |
78 this.tracker_); | 73 this.tracker_); |
79 | 74 |
80 task.addObserver(this.onTaskProgress_.bind(this, task)); | 75 task.addObserver(this.onTaskProgress_.bind(this, task)); |
81 | 76 |
82 this.queue_.queueTask(task); | 77 this.queue_.queueTask(task); |
83 | 78 |
84 return task; | 79 return task; |
85 }; | 80 }; |
86 | 81 |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
148 * FileOperationManager.CopyTask) but this is a temporary state of affairs. | 143 * FileOperationManager.CopyTask) but this is a temporary state of affairs. |
149 * | 144 * |
150 * @constructor | 145 * @constructor |
151 * @extends {importer.TaskQueue.BaseTask} | 146 * @extends {importer.TaskQueue.BaseTask} |
152 * @struct | 147 * @struct |
153 * | 148 * |
154 * @param {string} taskId | 149 * @param {string} taskId |
155 * @param {!importer.HistoryLoader} historyLoader | 150 * @param {!importer.HistoryLoader} historyLoader |
156 * @param {!importer.ScanResult} scanResult | 151 * @param {!importer.ScanResult} scanResult |
157 * @param {!Promise<!DirectoryEntry>} directoryPromise | 152 * @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. | 153 * @param {!importer.Destination} destination The logical destination. |
161 * @param {!analytics.Tracker} tracker | 154 * @param {!analytics.Tracker} tracker |
162 */ | 155 */ |
163 importer.MediaImportHandler.ImportTask = function( | 156 importer.MediaImportHandler.ImportTask = function( |
164 taskId, | 157 taskId, |
165 historyLoader, | 158 historyLoader, |
166 scanResult, | 159 scanResult, |
167 directoryPromise, | 160 directoryPromise, |
168 duplicateFinder, | |
169 destination, | 161 destination, |
170 tracker) { | 162 tracker) { |
171 | 163 |
172 importer.TaskQueue.BaseTask.call(this, taskId); | 164 importer.TaskQueue.BaseTask.call(this, taskId); |
173 /** @private {string} */ | 165 /** @private {string} */ |
174 this.taskId_ = taskId; | 166 this.taskId_ = taskId; |
175 | 167 |
176 /** @private {!importer.Destination} */ | 168 /** @private {!importer.Destination} */ |
177 this.destination_ = destination; | 169 this.destination_ = destination; |
178 | 170 |
179 /** @private {!Promise<!DirectoryEntry>} */ | 171 /** @private {!Promise<!DirectoryEntry>} */ |
180 this.directoryPromise_ = directoryPromise; | 172 this.directoryPromise_ = directoryPromise; |
181 | 173 |
182 /** @private {!importer.DuplicateFinder} */ | |
183 this.deduplicator_ = duplicateFinder; | |
184 | |
185 /** @private {!importer.ScanResult} */ | 174 /** @private {!importer.ScanResult} */ |
186 this.scanResult_ = scanResult; | 175 this.scanResult_ = scanResult; |
187 | 176 |
188 /** @private {!importer.HistoryLoader} */ | 177 /** @private {!importer.HistoryLoader} */ |
189 this.historyLoader_ = historyLoader; | 178 this.historyLoader_ = historyLoader; |
190 | 179 |
191 /** @private {!analytics.Tracker} */ | 180 /** @private {!analytics.Tracker} */ |
192 this.tracker_ = tracker; | 181 this.tracker_ = tracker; |
193 | 182 |
194 /** @private {number} */ | 183 /** @private {number} */ |
195 this.totalBytes_ = 0; | 184 this.totalBytes_ = 0; |
196 | 185 |
197 /** @private {number} */ | 186 /** @private {number} */ |
198 this.processedBytes_ = 0; | 187 this.processedBytes_ = 0; |
199 | 188 |
200 /** @private {number} */ | 189 /** @private {number} */ |
201 this.remainingFilesCount_ = 0; | 190 this.remainingFilesCount_ = 0; |
202 | 191 |
203 /** @private {?function()} */ | 192 /** @private {?function()} */ |
204 this.cancelCallback_ = null; | 193 this.cancelCallback_ = null; |
205 | 194 |
206 /** @private {boolean} Indicates whether this task was canceled. */ | 195 /** @private {boolean} Indicates whether this task was canceled. */ |
207 this.canceled_ = false; | 196 this.canceled_ = false; |
208 | 197 |
209 /** @private {number} Number of files deduped by content dedupe. */ | |
210 this.dedupeCount_ = 0; | |
211 | |
212 /** @private {number} */ | 198 /** @private {number} */ |
213 this.errorCount_ = 0; | 199 this.errorCount_ = 0; |
214 }; | 200 }; |
215 | 201 |
216 /** @struct */ | 202 /** @struct */ |
217 importer.MediaImportHandler.ImportTask.prototype = { | 203 importer.MediaImportHandler.ImportTask.prototype = { |
218 /** @return {number} Number of imported bytes */ | 204 /** @return {number} Number of imported bytes */ |
219 get processedBytes() { return this.processedBytes_; }, | 205 get processedBytes() { return this.processedBytes_; }, |
220 | 206 |
221 /** @return {number} Total number of bytes to import */ | 207 /** @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). | 256 // (including calling #requestCancel again). |
271 var cancelCallback = this.cancelCallback_; | 257 var cancelCallback = this.cancelCallback_; |
272 this.cancelCallback_ = null; | 258 this.cancelCallback_ = null; |
273 cancelCallback(); | 259 cancelCallback(); |
274 } | 260 } |
275 }; | 261 }; |
276 | 262 |
277 /** @private */ | 263 /** @private */ |
278 importer.MediaImportHandler.ImportTask.prototype.initialize_ = function() { | 264 importer.MediaImportHandler.ImportTask.prototype.initialize_ = function() { |
279 var stats = this.scanResult_.getStatistics(); | 265 var stats = this.scanResult_.getStatistics(); |
280 | |
281 this.remainingFilesCount_ = stats.newFileCount; | 266 this.remainingFilesCount_ = stats.newFileCount; |
282 this.totalBytes_ = stats.sizeBytes; | 267 this.totalBytes_ = stats.sizeBytes; |
283 this.notify(importer.TaskQueue.UpdateType.PROGRESS); | 268 this.notify(importer.TaskQueue.UpdateType.PROGRESS); |
284 | 269 |
285 this.tracker_.send(metrics.ImportEvents.STARTED); | 270 this.tracker_.send(metrics.ImportEvents.STARTED); |
286 this.tracker_.send(metrics.ImportEvents.HISTORY_DEDUPE_COUNT | |
287 .value(stats.duplicateFileCount)); | |
288 }; | 271 }; |
289 | 272 |
290 /** | 273 /** |
291 * Initiates an import to the given location. This should only be called once | 274 * Initiates an import to the given location. This should only be called once |
292 * the scan result indicates that it is ready. | 275 * the scan result indicates that it is ready. |
293 * | 276 * |
294 * @private | 277 * @private |
295 */ | 278 */ |
296 importer.MediaImportHandler.ImportTask.prototype.importScanEntries_ = | 279 importer.MediaImportHandler.ImportTask.prototype.importScanEntries_ = |
297 function() { | 280 function() { |
(...skipping 11 matching lines...) Expand all Loading... | |
309 * @param {!DirectoryEntry} destinationDirectory | 292 * @param {!DirectoryEntry} destinationDirectory |
310 * @param {function()} completionCallback Called after this operation is | 293 * @param {function()} completionCallback Called after this operation is |
311 * complete. | 294 * complete. |
312 * @param {!FileEntry} entry The entry to import. | 295 * @param {!FileEntry} entry The entry to import. |
313 * @private | 296 * @private |
314 */ | 297 */ |
315 importer.MediaImportHandler.ImportTask.prototype.importOne_ = | 298 importer.MediaImportHandler.ImportTask.prototype.importOne_ = |
316 function(destinationDirectory, completionCallback, entry) { | 299 function(destinationDirectory, completionCallback, entry) { |
317 if (this.canceled_) { | 300 if (this.canceled_) { |
318 this.notify(importer.TaskQueue.UpdateType.CANCELED); | 301 this.notify(importer.TaskQueue.UpdateType.CANCELED); |
319 this.tracker_.send(metrics.ImportEvents.CANCELLED); | 302 this.sendImportStats_(false); |
320 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); | 449 this.sendImportStats_(true); |
mtomasz
2015/03/05 04:36:53
nit: enum or /* arg_name */ for this true? It's im
Steve McKay
2015/03/05 21:39:03
Done.
| |
483 this.sendImportStats_(); | |
484 }; | 450 }; |
485 | 451 |
486 /** | 452 /** |
487 * Sends import statistics to analytics. | 453 * Sends import statistics to analytics. |
454 * | |
455 * @param {boolean} completedSuccessfully True if task | |
Ben Kwa
2015/03/05 20:05:10
suggestion: You could reuse the TaskQueue.UpdateTy
Steve McKay
2015/03/05 21:39:03
Done.
| |
456 * wasn't cancelled or err'd out. | |
488 */ | 457 */ |
489 importer.MediaImportHandler.ImportTask.prototype.sendImportStats_ = function() { | 458 importer.MediaImportHandler.ImportTask.prototype.sendImportStats_ = |
459 function(completedSuccessfully) { | |
460 var scanStats = this.scanResult_.getStatistics(); | |
461 | |
462 if (!completedSuccessfully) { | |
463 this.tracker_.send(metrics.ImportEvents.CANCELLED); | |
464 } else { | |
465 // Ideally we'd report bytes imported for cancelled imports too. | |
466 // We just don't have the information needed to do this accurately. | |
Ben Kwa
2015/03/05 20:05:10
The processedBytes_ counter should be a fairly acc
Steve McKay
2015/03/05 21:39:03
Perfect!
| |
467 var megaBytes = Math.floor(scanStats.sizeBytes / (1024 * 1024)); | |
468 this.tracker_.send( | |
Ben Kwa
2015/03/05 20:05:10
We also need to do this.tracker_.send(metrics.Impo
Steve McKay
2015/03/05 21:39:03
Removed that as there are plenty of surrogate even
| |
469 metrics.ImportEvents.MEGABYTES_IMPORTED.value(megaBytes)); | |
470 } | |
471 | |
472 // FYI, if the task was cancelled we substract the remaining files | |
473 // from the number of files in the scanStats. | |
474 var importFileCount = scanStats.newFileCount - this.remainingFilesCount_; | |
490 this.tracker_.send( | 475 this.tracker_.send( |
491 metrics.ImportEvents.CONTENT_DEDUPE_COUNT | 476 metrics.ImportEvents.FILES_IMPORTED.value(importFileCount)); |
492 .value(this.dedupeCount_)); | 477 |
493 // TODO(kenobi): Send correct import byte counts. | 478 if (this.errorCount_ > 0) { |
494 var importFileCount = this.scanResult_.getStatistics().newFileCount - | 479 this.tracker_.send(metrics.ImportEvents.ERRORS.value(this.errorCount_)); |
495 (this.dedupeCount_ + this.remainingFilesCount_); | 480 } |
481 | |
482 // Finally we want to report on the number of duplicates | |
483 // that were identified during scanning. | |
484 var totalDeduped = 0; | |
485 Object.keys(scanStats.duplicates).forEach( | |
486 /** | |
487 * @param {!importer.Disposition} disposition | |
488 * @this {importer.MediaImportHandler.ImportTask} | |
489 */ | |
490 function(disposition) { | |
491 var count = scanStats.duplicates[disposition]; | |
492 totalDeduped += count; | |
493 this.tracker_.send( | |
494 metrics.ImportEvents.FILES_DEDUPLICATED | |
495 .label(disposition) | |
496 .value(count)); | |
497 }.bind(this)); | |
498 | |
496 this.tracker_.send( | 499 this.tracker_.send( |
497 metrics.ImportEvents.FILE_COUNT | 500 metrics.ImportEvents.FILES_DEDUPLICATED |
498 .value(importFileCount)); | 501 .label('all-duplicates') |
499 | 502 .value(totalDeduped)); |
500 this.tracker_.send(metrics.ImportEvents.ERROR.value(this.errorCount_)); | |
501 | |
502 // Send aggregate deduplication timings, to avoid flooding analytics with one | |
503 // timing per file. | |
504 var deduplicatorStats = this.deduplicator_.getStatistics(); | |
505 this.tracker_.sendTiming( | |
506 metrics.Categories.ACQUISITION, | |
507 metrics.timing.Variables.COMPUTE_HASH, | |
508 deduplicatorStats.computeHashTime, | |
509 'In Place'); | |
510 this.tracker_.sendTiming( | |
511 metrics.Categories.ACQUISITION, | |
512 metrics.timing.Variables.SEARCH_BY_HASH, | |
513 deduplicatorStats.searchHashTime); | |
514 | |
515 }; | 503 }; |
OLD | NEW |