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

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: Don't report error count when zero, and don't report an explicit END to import...since that is impl… 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 =
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
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
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
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
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 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698