Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(141)

Side by Side Diff: ui/file_manager/file_manager/background/js/media_import_handler.js

Issue 980603003: Move content deduplication into the scan process. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Respond to review comments. Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698