summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorzvorygin@chromium.org <zvorygin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-20 13:41:47 +0000
committerzvorygin@chromium.org <zvorygin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-20 13:41:47 +0000
commitdcadb3559e103423f86ac4ec7d2497fd79283919 (patch)
treebfca2f4ae3b27b1940c246582fbf14302b9bbbe2 /chrome/browser
parentd08b6ce1370d4c5a9e26649065a5a907a4786b13 (diff)
downloadchromium_src-dcadb3559e103423f86ac4ec7d2497fd79283919.zip
chromium_src-dcadb3559e103423f86ac4ec7d2497fd79283919.tar.gz
chromium_src-dcadb3559e103423f86ac4ec7d2497fd79283919.tar.bz2
Minor code style cleanup. Added id3 metadata parser to filemanager.
BUG=14879 TEST= Review URL: http://codereview.chromium.org/7217007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@93187 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r--chrome/browser/resources/component_extension_resources.grd4
-rw-r--r--chrome/browser/resources/file_manager/js/byte_reader.js44
-rw-r--r--chrome/browser/resources/file_manager/js/file_manager.js11
-rw-r--r--chrome/browser/resources/file_manager/js/function_parallel.js70
-rw-r--r--chrome/browser/resources/file_manager/js/function_sequence.js77
-rw-r--r--chrome/browser/resources/file_manager/js/id3_parser.js583
-rw-r--r--chrome/browser/resources/file_manager/js/metadata_dispatcher.js1
-rw-r--r--chrome/browser/resources/file_manager/js/util.js16
8 files changed, 805 insertions, 1 deletions
diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd
index 24cf7ea..bdff237 100644
--- a/chrome/browser/resources/component_extension_resources.grd
+++ b/chrome/browser/resources/component_extension_resources.grd
@@ -29,8 +29,12 @@
<include name="IDR_FILE_MANAGER_METADATA_DISPATCHER" file="file_manager/js/metadata_dispatcher.js" type="BINDATA" />
<include name="IDR_FILE_MANAGER_METADATA_READER" file="file_manager/js/byte_reader.js" type="BINDATA" />
<include name="IDR_FILE_MANAGER_METADATA_PARSER" file="file_manager/js/metadata_parser.js" type="BINDATA" />
+ <include name="IDR_FILE_MANAGER_UTIL" file="file_manager/js/util.js" type="BINDATA" />
<include name="IDR_FILE_MANAGER_EXIF_PARSER" file="file_manager/js/exif_parser.js" type="BINDATA" />
<include name="IDR_FILE_MANAGER_MPEG_PARSER" file="file_manager/js/mpeg_parser.js" type="BINDATA" />
+ <include name="IDR_FILE_MANAGER_ID3_PARSER" file="file_manager/js/id3_parser.js" type="BINDATA" />
+ <include name="IDR_FILE_MANAGER_PARALLEL" file="file_manager/js/function_parallel.js" type="BINDATA" />
+ <include name="IDR_FILE_MANAGER_SEQUENCE" file="file_manager/js/function_sequence.js" type="BINDATA" />
<include name="IDR_FILE_MANAGER_PREVIEW_ICON" file="file_manager/images/icon_preview_16x16.png" type="BINDATA" />
<include name="IDR_FILE_MANAGER_MEDIA_PLAY_ICON" file="file_manager/images/icon_play_16x16.png" type="BINDATA" />
<include name="IDR_FILE_MANAGER_MEDIA_ENQUEUE_ICON" file="file_manager/images/icon_add_to_queue_16x16.png" type="BINDATA" />
diff --git a/chrome/browser/resources/file_manager/js/byte_reader.js b/chrome/browser/resources/file_manager/js/byte_reader.js
index a0e4a4e..9ba7743 100644
--- a/chrome/browser/resources/file_manager/js/byte_reader.js
+++ b/chrome/browser/resources/file_manager/js/byte_reader.js
@@ -49,6 +49,26 @@ ByteReader.readString = function(dataView, pos, size, opt_end) {
return String.fromCharCode.apply(null, codes);
};
+/**
+ * Read as a sequence of characters, returning them as a single string.
+ *
+ * This is a static utility function. There is a member function with the
+ * same name which side-effects the current read position.
+ */
+ByteReader.readNullTerminatedString = function(dataView, pos, size, opt_end) {
+ ByteReader.validateRead(pos, size, opt_end || dataView.byteLength);
+
+ var codes = [];
+
+ for (var i = 0; i < size; ++i) {
+ var code = dataView.getUint8(pos + i);
+ if (code == 0) break;
+ codes.push(code);
+ }
+
+ return String.fromCharCode.apply(null, codes);
+};
+
ByteReader.base64Alphabet_ =
('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/').
split('');
@@ -209,6 +229,30 @@ ByteReader.prototype.readString = function(size, opt_end) {
return rv;
};
+
+/**
+ * Read as a sequence of characters, returning them as a single string.
+ *
+ * Adjusts the current position on success. Throws an exception if the
+ * read would go past the end of the buffer.
+ */
+ByteReader.prototype.readNullTerminatedString = function(size, opt_end) {
+ var rv = ByteReader.readNullTerminatedString(this.view_,
+ this.pos_,
+ size,
+ opt_end);
+ this.pos_ += rv.length;
+
+ if (rv.length < size) {
+ // If we've stopped reading because we found '0' but didn't hit size limit
+ // then we should skip additional '0' character
+ this.pos_++;
+ }
+
+ return rv;
+};
+
+
/**
* Read as an array of numbers.
*
diff --git a/chrome/browser/resources/file_manager/js/file_manager.js b/chrome/browser/resources/file_manager/js/file_manager.js
index 815d2ed..ee5bd49 100644
--- a/chrome/browser/resources/file_manager/js/file_manager.js
+++ b/chrome/browser/resources/file_manager/js/file_manager.js
@@ -2,6 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+const EMPTY_IMAGE_URI = 'data:image/gif;base64,'
+ + 'R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw%3D%3D';
+
+// Thumbnail view is painful without the exif reader.
+const ENABLE_THUMBNAIL_VIEW = ENABLE_METADATA;
+
var g_slideshow_data = null;
/**
@@ -1402,7 +1408,10 @@ FileManager.prototype = {
*/
FileManager.prototype.updatePreview_ = function() {
// Clear the preview image first, in case the thumbnail takes long to load.
- this.previewImage_.src = '';
+ // Do not set url to empty string in plugins because it crashes browser,
+ // instead we use empty 1x1 gif
+ this.previewImage_.src = EMPTY_IMAGE_URI;
+
// The transparent-background class is used to display the checkerboard
// background for image thumbnails. We don't want to display it for
// non-thumbnail preview images.
diff --git a/chrome/browser/resources/file_manager/js/function_parallel.js b/chrome/browser/resources/file_manager/js/function_parallel.js
new file mode 100644
index 0000000..759f7b8
--- /dev/null
+++ b/chrome/browser/resources/file_manager/js/function_parallel.js
@@ -0,0 +1,70 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @constructor
+ * @class FunctionSequence to invoke steps in sequence
+ *
+ * @param steps array of functions to invoke in parallel
+ * @param callback callback to invoke on success
+ * @param failureCallback callback to invoke on failure
+ */
+function FunctionParallel(name, steps, logger, callback, failureCallback) {
+ // Private variables hidden in closure
+ this.currentStepIdx_ = -1;
+ this.failed_ = false;
+ this.steps_ = steps;
+ this.callback_ = callback;
+ this.failureCallback_ = failureCallback;
+ this.logger = logger;
+ this.name = name;
+
+ this.remaining = this.steps_.length;
+
+ this.nextStep = this.nextStep_.bind(this);
+ this.onError = this.onError_.bind(this);
+ this.apply = this.start.bind(this);
+}
+
+
+/**
+ * Error handling function, which fires error callback.
+ *
+ * @param err error message
+ */
+FunctionParallel.prototype.onError_ = function(err) {
+ if (!this.failed_) {
+ this.failed_ = true;
+ this.failureCallback_(err);
+ }
+};
+
+/**
+ * Advances to next step. This method should not be used externally. In external
+ * cases should be used nextStep function, which is defined in closure and thus
+ * has access to internal variables of functionsequence.
+ */
+FunctionParallel.prototype.nextStep_ = function() {
+ if (--this.remaining == 0 && !this.failed_) {
+ this.callback_();
+ }
+};
+
+/**
+ * This function should be called only once on start, so start all the children
+ * at once
+ */
+FunctionParallel.prototype.start = function(var_args) {
+ this.logger.vlog('Starting [' + this.steps_.length + '] parallel tasks with '
+ + arguments.length + ' argument(s)');
+ if (this.logger.verbose) {
+ for (var j = 0; j < arguments.length; j++) {
+ this.logger.vlog(arguments[j]);
+ }
+ }
+ for (var i=0; i < this.steps_.length; i++) {
+ this.logger.vlog('Attempting to start step [' + this.steps_[i].name + ']');
+ this.steps_[i].apply(this, arguments);
+ }
+};
diff --git a/chrome/browser/resources/file_manager/js/function_sequence.js b/chrome/browser/resources/file_manager/js/function_sequence.js
new file mode 100644
index 0000000..3ac6152
--- /dev/null
+++ b/chrome/browser/resources/file_manager/js/function_sequence.js
@@ -0,0 +1,77 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @constructor
+ * @class FunctionSequence to invoke steps in sequence
+ *
+ * @param steps array of functions to invoke in sequence
+ * @param callback callback to invoke on success
+ * @param failureCallback callback to invoke on failure
+ */
+function FunctionSequence(steps, callback, failureCallback, logger) {
+ // Private variables hidden in closure
+ var _currentStepIdx = -1;
+ var _self = this;
+ var _failed = false;
+
+ this.onError = function(err) {
+ logger.vlog('Failed step: ' + steps[_currentStepIdx].name + ': ' + err);
+ if (!_failed) {
+ _failed = true;
+ failureCallback.apply(this, err, steps[_currentStepIdx].name);
+ }
+ };
+
+ this.finish = function() {
+ if (!_failed) {
+ _currentStepIdx = steps.length;
+ callback();
+ }
+ };
+
+ this.nextStep = function(var_args) {
+ logger.vlog('Starting new step');
+
+ if (_failed) {
+ return;
+ }
+
+ _currentStepIdx++;
+
+ if (_currentStepIdx >= steps.length) {
+ callback.apply(_self, arguments);
+ return;
+ }
+
+ var currentStep = steps[_currentStepIdx];
+
+ logger.vlog('Current step type is' + (typeof currentStep));
+
+ if (typeof currentStep == 'function') {
+ logger.vlog('nextstep : ' + currentStep.name);
+ currentStep.apply(_self, arguments);
+ } else {
+ logger.vlog('nextsep forking ' + currentStep.length + ' chains');
+ var remaining = currentStep.length;
+
+ function resume() {
+ if (--remaining == 0) {
+ logger.vlog('barrier passed');
+ _self.nextStep();
+ } else {
+ logger.vlog('waiting for [' + remaining + '] at barrier');
+ }
+ }
+
+ for (var i = 0; i < currentStep.length; i++) {
+ var sequence = new FunctionSequence(currentStep[i], resume,
+ _self.onError, logger);
+ sequence.start.apply(sequence, arguments);
+ }
+ }
+ };
+
+ this.start = this.nextStep;
+}
diff --git a/chrome/browser/resources/file_manager/js/id3_parser.js b/chrome/browser/resources/file_manager/js/id3_parser.js
new file mode 100644
index 0000000..89243d0
--- /dev/null
+++ b/chrome/browser/resources/file_manager/js/id3_parser.js
@@ -0,0 +1,583 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+importScripts('function_sequence.js');
+importScripts('function_parallel.js');
+importScripts('util.js');
+
+function Id3Parser(parent) {
+ MetadataParser.apply(this, [parent]);
+ this.verbose = true;
+}
+
+Id3Parser.parserType = 'mpeg';
+
+Id3Parser.prototype = {__proto__: MetadataParser.prototype};
+
+Id3Parser.prototype.urlFilter = /\.(mp3)$/i;
+
+/**
+ * id3v1 constants
+ */
+
+Id3Parser.v1 = {
+ /**
+ * Genres list as described in id3 documentation. We aren't going to
+ * localize this list, because at least in Russian (and I think most
+ * other languages), translation exists at least fo 10% and most time
+ * translation would degrade to transliteration.
+ */
+ GENRES : [
+ 'Blues',
+ 'Classic Rock',
+ 'Country',
+ 'Dance',
+ 'Disco',
+ 'Funk',
+ 'Grunge',
+ 'Hip-Hop',
+ 'Jazz',
+ 'Metal',
+ 'New Age',
+ 'Oldies',
+ 'Other',
+ 'Pop',
+ 'R&B',
+ 'Rap',
+ 'Reggae',
+ 'Rock',
+ 'Techno',
+ 'Industrial',
+ 'Alternative',
+ 'Ska',
+ 'Death Metal',
+ 'Pranks',
+ 'Soundtrack',
+ 'Euro-Techno',
+ 'Ambient',
+ 'Trip-Hop',
+ 'Vocal',
+ 'Jazz+Funk',
+ 'Fusion',
+ 'Trance',
+ 'Classical',
+ 'Instrumental',
+ 'Acid',
+ 'House',
+ 'Game',
+ 'Sound Clip',
+ 'Gospel',
+ 'Noise',
+ 'AlternRock',
+ 'Bass',
+ 'Soul',
+ 'Punk',
+ 'Space',
+ 'Meditative',
+ 'Instrumental Pop',
+ 'Instrumental Rock',
+ 'Ethnic',
+ 'Gothic',
+ 'Darkwave',
+ 'Techno-Industrial',
+ 'Electronic',
+ 'Pop-Folk',
+ 'Eurodance',
+ 'Dream',
+ 'Southern Rock',
+ 'Comedy',
+ 'Cult',
+ 'Gangsta',
+ 'Top 40',
+ 'Christian Rap',
+ 'Pop/Funk',
+ 'Jungle',
+ 'Native American',
+ 'Cabaret',
+ 'New Wave',
+ 'Psychadelic',
+ 'Rave',
+ 'Showtunes',
+ 'Trailer',
+ 'Lo-Fi',
+ 'Tribal',
+ 'Acid Punk',
+ 'Acid Jazz',
+ 'Polka',
+ 'Retro',
+ 'Musical',
+ 'Rock & Roll',
+ 'Hard Rock',
+ 'Folk',
+ 'Folk-Rock',
+ 'National Folk',
+ 'Swing',
+ 'Fast Fusion',
+ 'Bebob',
+ 'Latin',
+ 'Revival',
+ 'Celtic',
+ 'Bluegrass',
+ 'Avantgarde',
+ 'Gothic Rock',
+ 'Progressive Rock',
+ 'Psychedelic Rock',
+ 'Symphonic Rock',
+ 'Slow Rock',
+ 'Big Band',
+ 'Chorus',
+ 'Easy Listening',
+ 'Acoustic',
+ 'Humour',
+ 'Speech',
+ 'Chanson',
+ 'Opera',
+ 'Chamber Music',
+ 'Sonata',
+ 'Symphony',
+ 'Booty Bass',
+ 'Primus',
+ 'Porn Groove',
+ 'Satire',
+ 'Slow Jam',
+ 'Club',
+ 'Tango',
+ 'Samba',
+ 'Folklore',
+ 'Ballad',
+ 'Power Ballad',
+ 'Rhythmic Soul',
+ 'Freestyle',
+ 'Duet',
+ 'Punk Rock',
+ 'Drum Solo',
+ 'A capella',
+ 'Euro-House',
+ 'Dance Hall',
+ 'Goa',
+ 'Drum & Bass',
+ 'Club-House',
+ 'Hardcore',
+ 'Terror',
+ 'Indie',
+ 'BritPop',
+ 'Negerpunk',
+ 'Polsk Punk',
+ 'Beat',
+ 'Christian Gangsta Rap',
+ 'Heavy Metal',
+ 'Black Metal',
+ 'Crossover',
+ 'Contemporary Christian',
+ 'Christian Rock',
+ 'Merengue',
+ 'Salsa',
+ 'Thrash Metal',
+ 'Anime',
+ 'Jpop',
+ 'Synthpop'
+ ]
+};
+
+/**
+ * Reads synchsafe integer.
+ * 'SynchSafe' term is taken from id3 documentation.
+ *
+ * @param {ByteReader} reader - reader to use
+ * @param {int} length - bytes to read
+ * @return {int}
+ */
+Id3Parser.readSynchSafe_ = function(reader, length) {
+ var rv = 0;
+
+ switch (length) {
+ case 4:
+ rv = reader.readScalar(1, false) << 21;
+ case 3:
+ rv |= reader.readScalar(1, false) << 14;
+ case 2:
+ rv |= reader.readScalar(1, false) << 7;
+ case 1:
+ rv |= reader.readScalar(1, false);
+ }
+
+ return rv;
+};
+
+/**
+ * Reads 3bytes integer.
+ *
+ * @param {ByteReader} reader - reader to use
+ * @return {int}
+ */
+Id3Parser.readUInt24_ = function(reader) {
+ return reader.readScalar(2, false) << 16 | reader.readScalar(1, false);
+};
+
+/**
+ * Reads string from reader with specified encoding
+ *
+ * @param {ByteReader} reader reader to use
+ * @param {int} encoding string encoding.
+ * @param {int} size maximum string size. Actual result may be shorter.
+ *
+ */
+Id3Parser.prototype.readString_ = function(reader, encoding, size) {
+ switch (encoding) {
+ case Id3Parser.v2.ENCODING.ISO_8859_1:
+ return reader.readNullTerminatedString(size);
+ case Id3Parser.v2.ENCODING.UTF_16BE:
+ case Id3Parser.v2.ENCODING.UTF_16:
+ case Id3Parser.v2.ENCODING.UTF_8:
+ default: {
+ // TODO: implement reading of unicode strings
+ this.error('Reading of unicode strings in ID3 tags is unimplemented');
+ return '';
+ }
+ }
+};
+
+/**
+ * Reads text frame from reader.
+ *
+ * @param {ByteReader} reader reader to use
+ * @param {int} majorVersion major id3 version to use
+ * @param {Object} frame frame so store data at
+ * @param {int} end frame end position in reader
+ */
+Id3Parser.prototype.readTextFrame_ = function(reader,
+ majorVersion,
+ frame,
+ end) {
+ frame.encoding = reader.readScalar(1, false, end);
+ frame.value = this.readString_(reader, frame.encoding, end - reader.tell());
+};
+
+/**
+ * Reads user defined text frame from reader.
+ *
+ * @param {ByteReader} reader reader to use
+ * @param {int} majorVersion major id3 version to use
+ * @param {Object} frame frame so store data at
+ * @param {int} end frame end position in reader
+ */
+Id3Parser.prototype.readUserDefinedTextFrame_ = function(reader,
+ majorVersion,
+ frame,
+ end) {
+ frame.encoding = reader.readScalar(1, false, end);
+
+ frame.description = this.readString_(
+ reader,
+ frame.encoding,
+ end - reader.tell());
+
+ frame.value = this.readString_(
+ reader,
+ frame.encoding,
+ end - reader.tell());
+};
+
+Id3Parser.prototype.readPIC_ = function(reader, majorVersion, frame, end) {
+ frame.encoding = reader.readScalar(1, false, end);
+ frame.format = reader.readNullTerminatedString(3, end - reader.tell());
+ frame.pictureType = reader.readScalar(1, false, end);
+ frame.description = this.readString_(reader,
+ frame.encoding,
+ end - reader.tell());
+
+
+ if (frame.format == '-->') {
+ frame.imageUrl = reader.readNullTerminatedString(end - reader.tell());
+ } else {
+ frame.imageUrl = reader.readImage(end - reader.tell());
+ }
+};
+
+Id3Parser.prototype.readAPIC_ = function(reader, majorVersion, frame, end) {
+ this.vlog('Extracting picture');
+ frame.encoding = reader.readScalar(1, false, end);
+ frame.mime = reader.readNullTerminatedString(end - reader.tell());
+ frame.pictureType = reader.readScalar(1, false, end);
+ frame.description = this.readString_(
+ reader,
+ frame.encoding,
+ end - reader.tell());
+
+ if (frame.mime == '-->') {
+ frame.imageUrl = reader.readNullTerminatedString(end - reader.tell());
+ } else {
+ frame.imageUrl = reader.readImage(end - reader.tell());
+ }
+};
+
+/**
+ * Reads string from reader with specified encoding
+ *
+ * @param {ByteReader} reader reader to use
+ * @return {Object} frame read
+ */
+Id3Parser.prototype.readFrame_ = function(reader, majorVersion) {
+ var frame = {};
+
+ reader.pushSeek(reader.tell(), ByteReader.SEEK_BEG);
+
+ var position = reader.tell();
+
+ frame.name = (majorVersion == 2)
+ ? reader.readNullTerminatedString(3)
+ : reader.readNullTerminatedString(4);
+
+ if (frame.name == '')
+ return null;
+
+ this.vlog('Found frame ' + (frame.name) + ' at position ' + position );
+
+ var dataSize = 0;
+
+ switch (majorVersion) {
+ case 2:
+ frame.size = Id3Parser.readUInt24_(reader);
+ frame.headerSize = 6;
+ break;
+ case 3:
+ frame.size = reader.readScalar(4, false);
+ frame.headerSize = 10;
+ frame.flags = reader.readScalar(2, false);
+ break;
+ case 4:
+ frame.size = Id3Parser.readSynchSafe_(reader, 4);
+ frame.headerSize = 10;
+ frame.flags = reader.readScalar(2, false);
+ break;
+ }
+
+ this.vlog('Found frame [' + frame.name + '] with size ['+frame.size+']');
+
+ if (Id3Parser.v2.HANDLERS[frame.name]) {
+ Id3Parser.v2.HANDLERS[frame.name].call(
+ this,
+ reader,
+ majorVersion,
+ frame,
+ reader.tell() + frame.size);
+ } else if (frame.name.charAt(0) == 'T' || frame.name.charAt(0) == 'W') {
+ this.readTextFrame_(
+ reader,
+ majorVersion,
+ frame,
+ reader.tell() + frame.size);
+ }
+
+ reader.popSeek();
+
+ reader.seek(frame.size + frame.headerSize, ByteReader.SEEK_CUR);
+
+ return frame;
+};
+
+Id3Parser.prototype.parse = function (file, callback, onError) {
+ var metadata = {};
+
+ var self = this;
+
+ this.log('Starting id3 parser for ' + file.name);
+
+ var id3v1Parser = new FunctionSequence(
+ 'id3v1parser',
+ [
+ /**
+ * Reads last 128 bytes of file in bytebuffer,
+ * which passes further.
+ * In last 128 bytes should be placed ID3v1 tag if available.
+ * @param file - file which bytes to read.
+ */
+ function readTail(file) {
+ util.readFileBytes(file, file.size - 128, file.size,
+ this.nextStep, this.onError, this);
+ },
+
+ /**
+ * Attempts to extract ID3v1 tag from 128 bytes long ByteBuffer
+ * @param file file which tags are being extracted.
+ * Could be used for logging purposes.
+ * @param {ByteReader} reader ByteReader of 128 bytes.
+ */
+ function extractId3v1(file, reader) {
+ if ( reader.readString(3) == 'TAG') {
+ this.logger.vlog('id3v1 found');
+ var id3v1 = metadata.id3v1 = {};
+
+ var title = reader.readNullTerminatedString(30).trim();
+
+ if (title.length > 0) {
+ id3v1.title = title;
+ }
+
+ reader.seek(3 + 30, ByteReader.SEEK_BEG);
+
+ var artist = reader.readNullTerminatedString(30).trim();
+ if (artist.length > 0) {
+ id3v1.artist = artist;
+ }
+
+ reader.seek(3 + 30 + 30, ByteReader.SEEK_BEG);
+
+ var album = reader.readNullTerminatedString(30).trim();
+ if (album.length > 0) {
+ id3v1.album = album;
+ }
+ }
+ this.nextStep();
+ }
+ ],
+ this
+ );
+
+ var id3v2Parser = new FunctionSequence(
+ 'id3v2parser',
+ [
+ function readHead(file) {
+ util.readFileBytes(file, 0, 10, this.nextStep, this.onError,
+ this);
+ },
+
+ /**
+ * Check if passed array of 10 bytes contains ID3 header.
+ * @param file to check and continue reading if ID3 metadata found
+ * @param {ByteReader} reader reader to fill with stream bytes.
+ */
+ function checkId3v2(file, reader) {
+ if (reader.readString(3) == 'ID3') {
+ this.logger.vlog('id3v2 found');
+ var id3v2 = metadata.id3v2 = {};
+ id3v2.major = reader.readScalar(1, false);
+ id3v2.minor = reader.readScalar(1, false);
+ id3v2.flags = reader.readScalar(1, false);
+ id3v2.size = Id3Parser.readSynchSafe_(reader, 4);
+
+ util.readFileBytes(file, 10, 10 + id3v2.size, this.nextStep,
+ this.onError, this);
+ } else {
+ this.finish();
+ }
+ },
+
+ /**
+ * Extracts all ID3v2 frames from given bytebuffer.
+ * @param file being parsed.
+ * @param {ByteReader} reader to use for metadata extraction.
+ */
+ function extractFrames(file, reader) {
+ var frameStart = 0;
+
+ var id3v2 = metadata.id3v2;
+
+ if ((id3v2.major > 2)
+ && (id3v2.flags & Id3Parser.v2.FLAG_EXTENDED_HEADER != 0)) {
+ // Skip extended header if found
+ if (id3v2.major == 3) {
+ reader.seek(reader.readScalar(4, false) - 4);
+ } else if (id3v2.major == 4) {
+ reader.seek(Id3Parser.readSynchSafe_(reader, 4) - 4);
+ }
+ }
+
+ var frame;
+
+ while (frame = self.readFrame_(reader, id3v2.major)) {
+ metadata.id3v2[frame.name] = frame;
+ }
+
+ this.nextStep();
+ }
+ ],
+ this
+ );
+
+ var metadataParser = new FunctionParallel(
+ 'mp3metadataParser',
+ [id3v1Parser, id3v2Parser],
+ this,
+ function() {
+ callback.call(null, metadata);
+ },
+ onError
+ );
+
+ id3v1Parser.setCallback(metadataParser.nextStep);
+ id3v2Parser.setCallback(metadataParser.nextStep);
+
+ id3v1Parser.setFailureCallback(metadataParser.onError);
+ id3v2Parser.setFailureCallback(metadataParser.onError);
+
+ this.vlog('Passed argument : ' + file);
+
+ metadataParser.start(file);
+};
+
+/**
+ * id3v2 constants
+ */
+Id3Parser.v2 = {
+ FLAG_EXTENDED_HEADER: 1 << 5,
+
+ ENCODING: {
+ /**
+ * ISO-8859-1 [ISO-8859-1]. Terminated with $00.
+ *
+ * @const
+ * @type {int}
+ */
+ ISO_8859_1 : 0,
+
+
+ /**
+ * UTF-16 [UTF-16] encoded Unicode [UNICODE] with BOM. All
+ * strings in the same frame SHALL have the same byteorder.
+ * Terminated with $00 00.
+ *
+ * @const
+ * @type {int}
+ */
+ UTF_16BE : 1,
+
+ /**
+ * UTF-16BE [UTF-16] encoded Unicode [UNICODE] without BOM.
+ * Terminated with $00 00.
+ *
+ * @const
+ * @type {int}
+ */
+ UTF_16 : 2,
+
+ /**
+ * UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00.
+ *
+ * @const
+ * @type {int}
+ */
+ UTF_8 : 3
+ },
+ HANDLERS: {
+ //User defined text information frame
+ TXX: Id3Parser.prototype.readUserDefinedTextFrame_,
+ //User defined URL link frame
+ WXX: Id3Parser.prototype.readUserDefinedTextFrame_,
+
+ //User defined text information frame
+ TXXX: Id3Parser.prototype.readUserDefinedTextFrame_,
+
+ //User defined URL link frame
+ WXXX: Id3Parser.prototype.readUserDefinedTextFrame_,
+
+ //User attached image
+ PIC: Id3Parser.prototype.readPIC_,
+
+ //User attached image
+ APIC: Id3Parser.prototype.readAPIC_
+ }
+};
+
+MetadataDispatcher.registerParserClass(Id3Parser);
diff --git a/chrome/browser/resources/file_manager/js/metadata_dispatcher.js b/chrome/browser/resources/file_manager/js/metadata_dispatcher.js
index d62aacb..fcf5378 100644
--- a/chrome/browser/resources/file_manager/js/metadata_dispatcher.js
+++ b/chrome/browser/resources/file_manager/js/metadata_dispatcher.js
@@ -19,6 +19,7 @@ importScripts('byte_reader.js');
function MetadataDispatcher() {
importScripts('exif_parser.js');
importScripts('mpeg_parser.js');
+ importScripts('id3_parser.js');
var patterns = [];
diff --git a/chrome/browser/resources/file_manager/js/util.js b/chrome/browser/resources/file_manager/js/util.js
index bd3b08a..ec14e95 100644
--- a/chrome/browser/resources/file_manager/js/util.js
+++ b/chrome/browser/resources/file_manager/js/util.js
@@ -166,4 +166,20 @@ var util = {
return fmt(this.scale_[i], this.units_[i]);
},
+ /**
+ * Utility function to read specified range of bytes from file
+ * @param file {File} file to read
+ * @param begin {int} starting byte(included)
+ * @param end {int} last byte(excluded)
+ * @param callback {function(File, Uint8Array)} callback to invoke
+ * @param onError {function(err)} error handler
+ */
+ readFileBytes: function(file, begin, end, callback, onError) {
+ var fileReader = new FileReader();
+ fileReader.onerror = onError;
+ fileReader.onloadend = function() {
+ callback(file, new ByteReader(fileReader.result))
+ };
+ fileReader.readAsArrayBuffer(file.webkitSlice(begin, end));
+ }
};