Index: ui/file_manager/file_manager/background/js/media_scanner.js |
diff --git a/ui/file_manager/file_manager/background/js/media_scanner.js b/ui/file_manager/file_manager/background/js/media_scanner.js |
index a3acfa6890483f7cd1a805fe69f3e7c34c4e4f56..9c7e40fd596d852a83a6e5ce0aee6ec3b7e5ae1c 100644 |
--- a/ui/file_manager/file_manager/background/js/media_scanner.js |
+++ b/ui/file_manager/file_manager/background/js/media_scanner.js |
@@ -3,56 +3,234 @@ |
// found in the LICENSE file. |
/** |
+ * Class representing the results of a scan operation. |
+ * |
+ * @interface |
+ */ |
+importer.MediaScanner = function() {}; |
+ |
+/** |
+ * Initiates scanning. |
+ * |
+ * @param {!Array.<!Entry>} entries Must be non-empty. |
+ * @return {!importer.ScanResult} ScanResult object representing the scan |
+ * job both while in-progress and when completed. |
+ */ |
+importer.MediaScanner.prototype.scan; |
+ |
+/** |
+ * Class representing the results of a scan operation. |
+ * |
+ * @interface |
+ */ |
+importer.ScanResult = function() {}; |
+ |
+/** |
+ * Returns all files entries discovered so far. The list will be |
+ * complete only after scanning has completed and {@code isFinished} |
+ * returns {@code true}. |
+ * |
+ * @return {!Array.<!FileEntry>} |
+ */ |
+importer.ScanResult.prototype.getFileEntries; |
+ |
+/** |
+ * Returns the aggregate size, in bytes, of all FileEntries discovered |
+ * during scanning. |
+ * |
+ * @return {number} |
+ */ |
+importer.ScanResult.prototype.getTotalBytes; |
+ |
+/** |
+ * Returns the scan duration in milliseconds. |
+ * |
+ * @return {number} |
+ */ |
+importer.ScanResult.prototype.getScanDurationMs; |
+ |
+/** |
+ * Returns a promise that fires when scanning is complete. |
+ * |
+ * @return {!Promise.<!importer.ScanResult>} |
+ */ |
+importer.ScanResult.prototype.whenFinished; |
+ |
+/** |
* Recursively scans through a list of given files and directories, and creates |
* a list of media files. |
* |
* @constructor |
+ * @struct |
+ * @implements {importer.MediaScanner} |
+ */ |
+importer.DefaultMediaScanner = function() {}; |
+ |
+/** @override */ |
+importer.DefaultMediaScanner.prototype.scan = function(entries) { |
+ if (entries.length == 0) { |
+ throw new Error('Cannot scan empty list of entries.'); |
+ } |
+ |
+ var scanResult = new importer.DefaultScanResult(); |
+ var scanPromises = entries.map(this.scanEntry_.bind(this, scanResult)); |
+ Promise.all(scanPromises) |
+ .then(scanResult.resolveScan.bind(scanResult)) |
+ .catch(scanResult.rejectScan.bind(scanResult)); |
+ |
+ return scanResult; |
+}; |
+ |
+/** |
+ * Resolves the entry to a list of {@code FileEntry}. |
+ * |
+ * @param {!importer.DefaultScanResult} result |
+ * @param {!Entry} entry |
+ * @return {!Promise} |
+ * @private |
+ */ |
+importer.DefaultMediaScanner.prototype.scanEntry_ = |
+ function(result, entry) { |
+ return entry.isFile ? |
+ result.addFileEntry(/** @type {!FileEntry} */ (entry)) : |
+ this.scanDirectory_(result, /** @type {!DirectoryEntry} */ (entry)); |
+}; |
+ |
+/** |
+ * Finds all files beneath directory. |
+ * |
+ * @param {!importer.DefaultScanResult} result |
+ * @param {!DirectoryEntry} entry |
+ * @return {!Promise} |
+ * @private |
*/ |
-function MediaScanner() {} |
+importer.DefaultMediaScanner.prototype.scanDirectory_ = |
+ function(result, entry) { |
+ return new Promise( |
+ function(resolve, reject) { |
+ // Collect promises for all files being added to results. |
+ // The directory scan promise can't resolve until all |
+ // file entries are completely promised. |
+ var promises = []; |
+ fileOperationUtil.findFilesRecursively( |
+ entry, |
+ /** @param {!FileEntry} fileEntry */ |
+ function(fileEntry) { |
+ promises.push(result.addFileEntry(fileEntry)); |
+ }) |
+ .then( |
+ /** @this {importer.DefaultScanResult} */ |
+ function() { |
+ Promise.all(promises).then(resolve).catch(reject); |
+ }); |
+ }); |
+}; |
/** |
- * Scans a list of directory and file entries, returning image and video files. |
- * @param {!Array<!Entry>} entries A list of file and directory entries. File |
- * entries are added directly to the media list; directory entries are |
- * recursively traversed to find files, which are added to the media list. |
- * @return {!Promise<!Array<!FileEntry>>} |
+ * Results of a scan operation. The object is "live" in that data can and |
+ * will change as the scan operation discovers files. |
+ * |
+ * <p>The scan is complete, and the object will become static once the |
+ * {@code whenFinished} promise resolves. |
+ * |
+ * @constructor |
+ * @struct |
+ * @implements {importer.ScanResult} |
*/ |
-MediaScanner.prototype.scan = function(entries) { |
+importer.DefaultScanResult = function() { |
/** |
- * Returns files and directories found under the given Entry. |
- * @param {!Entry} entry |
- * @return {!Promise<!Array<!Entry>>} |
+ * List of file entries found while scanning. |
+ * @private {!Array.<!FileEntry>} |
*/ |
- var scanRecurse = function(entry) { |
- if (entry.isFile) { |
- return Promise.resolve([entry]); |
- } else { |
- return fileOperationUtil.gatherEntriesRecursively( |
- /** @type {!DirectoryEntry} */ (entry)); |
- } |
- }; |
+ this.fileEntries_ = []; |
+ |
+ /** @private {number} */ |
+ this.totalBytes_ = 0; |
/** |
- * Flattens a nested list of Entries. |
- * @param {!Array<!Array<!Entry>>} array |
- * @return {!Array<!Entry>} |
+ * The point in time when the scan was started. |
+ * @type {Date} |
*/ |
- var flatten = function(array) { |
- return array.reduce(function(prev, curr) { |
- return prev.concat(curr); |
- }, []); |
- }; |
+ this.scanStarted_ = new Date(); |
/** |
- * Filters non-image and non-video files out of the given list. |
- * @param {!Array<!Entry>} array |
- * @return {!Array<!FileEntry>} |
+ * The point in time when the last scan activity occured. |
+ * @type {Date} |
*/ |
- var filter = function(array) { |
- return array.filter(FileType.isImageOrVideo); |
- }; |
+ this.lastScanActivity_ = this.scanStarted_; |
+ |
+ /** @type {function()} */ |
+ this.resolveScan; |
+ |
+ /** @type {function(*)} */ |
+ this.rejectScan; |
+ |
+ /** @private {!Promise.<!importer.ScanResult>} */ |
+ this.finishedPromise_ = new Promise( |
+ function(resolve, reject) { |
+ this.resolveScan = function() { |
+ resolve(this); |
+ }; |
+ this.rejectScan = reject; |
+ }.bind(this)); |
+}; |
+ |
+/** @override */ |
+importer.DefaultScanResult.prototype.getFileEntries = function() { |
+ return this.fileEntries_; |
+}; |
+ |
+/** @override */ |
+importer.DefaultScanResult.prototype.getTotalBytes = function() { |
+ return this.totalBytes_; |
+}; |
+ |
+/** @override */ |
+importer.DefaultScanResult.prototype.getScanDurationMs = function() { |
+ return this.lastScanActivity_.getTime() - this.scanStarted_.getTime(); |
+}; |
+ |
+/** @override */ |
+importer.DefaultScanResult.prototype.whenFinished = function() { |
+ return this.finishedPromise_; |
+}; |
+ |
+/** |
+ * Handles files discovered during scanning. |
+ * |
+ * @param {!FileEntry} entry |
+ * @return {!Promise} Resolves once file entry has been processed |
+ * and is represented in results. |
+ * @private |
+ */ |
+importer.DefaultScanResult.prototype.addFileEntry = function(entry) { |
+ this.lastScanActivity_ = new Date(); |
- return Promise.all(entries.map(scanRecurse)) |
- .then(flatten) |
- .then(filter); |
+ if (!FileType.isImageOrVideo(entry)) { |
+ return Promise.resolve(); |
+ } |
+ return new Promise( |
+ function(resolve, reject) { |
+ // TODO(smckay): Update to use MetadataCache. |
+ entry.getMetadata( |
+ /** |
+ * @param {!Metadata} metadata |
+ * @this {importer.DefaultScanResult} |
+ */ |
+ function(metadata) { |
+ this.lastScanActivity_ = new Date(); |
+ if ('size' in metadata) { |
+ this.totalBytes_ += metadata['size']; |
+ this.fileEntries_.push(entry); |
+ // Closure compiler currently requires an arg to resolve |
+ // and reject. If this is 2015, you can probably remove it. |
+ resolve(undefined); |
+ } else { |
+ // Closure compiler currently requires an arg to resolve |
+ // and reject. If this is 2015, you can probably remove it. |
+ reject(undefined); |
+ } |
+ }.bind(this), |
+ reject); |
+ }.bind(this)); |
}; |