| 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));
|
| };
|
|
|