diff options
author | zvorygin@chromium.org <zvorygin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-20 13:41:47 +0000 |
---|---|---|
committer | zvorygin@chromium.org <zvorygin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-20 13:41:47 +0000 |
commit | dcadb3559e103423f86ac4ec7d2497fd79283919 (patch) | |
tree | bfca2f4ae3b27b1940c246582fbf14302b9bbbe2 /chrome/browser | |
parent | d08b6ce1370d4c5a9e26649065a5a907a4786b13 (diff) | |
download | chromium_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')
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)); + } }; |