OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 importScripts('function_sequence.js'); | 5 importScripts('function_sequence.js'); |
6 importScripts('function_parallel.js'); | 6 importScripts('function_parallel.js'); |
7 | 7 |
8 function Id3Parser(parent) { | 8 function Id3Parser(parent) { |
9 MetadataParser.call(this, parent, 'id3', /\.(mp3)$/i); | 9 MetadataParser.call(this, parent, 'id3', /\.(mp3)$/i); |
10 } | 10 } |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
160 Id3Parser.prototype.readFrame_ = function(reader, majorVersion) { | 160 Id3Parser.prototype.readFrame_ = function(reader, majorVersion) { |
161 if (reader.eof()) | 161 if (reader.eof()) |
162 return null; | 162 return null; |
163 | 163 |
164 var frame = {}; | 164 var frame = {}; |
165 | 165 |
166 reader.pushSeek(reader.tell(), ByteReader.SEEK_BEG); | 166 reader.pushSeek(reader.tell(), ByteReader.SEEK_BEG); |
167 | 167 |
168 var position = reader.tell(); | 168 var position = reader.tell(); |
169 | 169 |
170 frame.name = (majorVersion == 2) | 170 frame.name = (majorVersion == 2) ? reader.readNullTerminatedString(3) : |
171 ? reader.readNullTerminatedString(3) | 171 reader.readNullTerminatedString(4); |
172 : reader.readNullTerminatedString(4); | |
173 | 172 |
174 if (frame.name == '') | 173 if (frame.name == '') |
175 return null; | 174 return null; |
176 | 175 |
177 this.vlog('Found frame ' + (frame.name) + ' at position ' + position ); | 176 this.vlog('Found frame ' + (frame.name) + ' at position ' + position); |
178 | 177 |
179 switch (majorVersion) { | 178 switch (majorVersion) { |
180 case 2: | 179 case 2: |
181 frame.size = Id3Parser.readUInt24_(reader); | 180 frame.size = Id3Parser.readUInt24_(reader); |
182 frame.headerSize = 6; | 181 frame.headerSize = 6; |
183 break; | 182 break; |
184 case 3: | 183 case 3: |
185 frame.size = reader.readScalar(4, false); | 184 frame.size = reader.readScalar(4, false); |
186 frame.headerSize = 10; | 185 frame.headerSize = 10; |
187 frame.flags = reader.readScalar(2, false); | 186 frame.flags = reader.readScalar(2, false); |
188 break; | 187 break; |
189 case 4: | 188 case 4: |
190 frame.size = Id3Parser.readSynchSafe_(reader, 4); | 189 frame.size = Id3Parser.readSynchSafe_(reader, 4); |
191 frame.headerSize = 10; | 190 frame.headerSize = 10; |
192 frame.flags = reader.readScalar(2, false); | 191 frame.flags = reader.readScalar(2, false); |
193 break; | 192 break; |
194 } | 193 } |
195 | 194 |
196 this.vlog('Found frame [' + frame.name + '] with size ['+frame.size+']'); | 195 this.vlog('Found frame [' + frame.name + '] with size [' + frame.size + ']'); |
197 | 196 |
198 if (Id3Parser.v2.HANDLERS[frame.name]) { | 197 if (Id3Parser.v2.HANDLERS[frame.name]) { |
199 Id3Parser.v2.HANDLERS[frame.name].call( | 198 Id3Parser.v2.HANDLERS[frame.name].call( |
200 this, | 199 this, |
201 reader, | 200 reader, |
202 majorVersion, | 201 majorVersion, |
203 frame, | 202 frame, |
204 reader.tell() + frame.size); | 203 reader.tell() + frame.size); |
205 } else if (frame.name.charAt(0) == 'T' || frame.name.charAt(0) == 'W') { | 204 } else if (frame.name.charAt(0) == 'T' || frame.name.charAt(0) == 'W') { |
206 this.readTextFrame_( | 205 this.readTextFrame_( |
207 reader, | 206 reader, |
208 majorVersion, | 207 majorVersion, |
209 frame, | 208 frame, |
210 reader.tell() + frame.size); | 209 reader.tell() + frame.size); |
211 } | 210 } |
212 | 211 |
213 reader.popSeek(); | 212 reader.popSeek(); |
214 | 213 |
215 reader.seek(frame.size + frame.headerSize, ByteReader.SEEK_CUR); | 214 reader.seek(frame.size + frame.headerSize, ByteReader.SEEK_CUR); |
216 | 215 |
217 return frame; | 216 return frame; |
218 }; | 217 }; |
219 | 218 |
220 Id3Parser.prototype.parse = function (file, metadata, callback, onError) { | 219 Id3Parser.prototype.parse = function(file, metadata, callback, onError) { |
221 var self = this; | 220 var self = this; |
222 | 221 |
223 this.log('Starting id3 parser for ' + file.name); | 222 this.log('Starting id3 parser for ' + file.name); |
224 | 223 |
225 var id3v1Parser = new FunctionSequence( | 224 var id3v1Parser = new FunctionSequence( |
226 'id3v1parser', | 225 'id3v1parser', |
227 [ | 226 [ |
228 /** | 227 /** |
229 * Reads last 128 bytes of file in bytebuffer, | 228 * Reads last 128 bytes of file in bytebuffer, |
230 * which passes further. | 229 * which passes further. |
231 * In last 128 bytes should be placed ID3v1 tag if available. | 230 * In last 128 bytes should be placed ID3v1 tag if available. |
232 * @param file - file which bytes to read. | 231 * @param file - file which bytes to read. |
233 */ | 232 */ |
234 function readTail(file) { | 233 function readTail(file) { |
235 util.readFileBytes(file, file.size - 128, file.size, | 234 util.readFileBytes(file, file.size - 128, file.size, |
236 this.nextStep, this.onError, this); | 235 this.nextStep, this.onError, this); |
237 }, | 236 }, |
238 | 237 |
239 /** | 238 /** |
240 * Attempts to extract ID3v1 tag from 128 bytes long ByteBuffer | 239 * Attempts to extract ID3v1 tag from 128 bytes long ByteBuffer |
241 * @param file file which tags are being extracted. | 240 * @param file file which tags are being extracted. |
242 * Could be used for logging purposes. | 241 * Could be used for logging purposes. |
243 * @param {ByteReader} reader ByteReader of 128 bytes. | 242 * @param {ByteReader} reader ByteReader of 128 bytes. |
244 */ | 243 */ |
245 function extractId3v1(file, reader) { | 244 function extractId3v1(file, reader) { |
246 if ( reader.readString(3) == 'TAG') { | 245 if (reader.readString(3) == 'TAG') { |
247 this.logger.vlog('id3v1 found'); | 246 this.logger.vlog('id3v1 found'); |
248 var id3v1 = metadata.id3v1 = {}; | 247 var id3v1 = metadata.id3v1 = {}; |
249 | 248 |
250 var title = reader.readNullTerminatedString(30).trim(); | 249 var title = reader.readNullTerminatedString(30).trim(); |
251 | 250 |
252 if (title.length > 0) { | 251 if (title.length > 0) { |
253 metadata.title = title; | 252 metadata.title = title; |
254 } | 253 } |
255 | 254 |
256 reader.seek(3 + 30, ByteReader.SEEK_BEG); | 255 reader.seek(3 + 30, ByteReader.SEEK_BEG); |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
303 }, | 302 }, |
304 | 303 |
305 /** | 304 /** |
306 * Extracts all ID3v2 frames from given bytebuffer. | 305 * Extracts all ID3v2 frames from given bytebuffer. |
307 * @param file being parsed. | 306 * @param file being parsed. |
308 * @param {ByteReader} reader to use for metadata extraction. | 307 * @param {ByteReader} reader to use for metadata extraction. |
309 */ | 308 */ |
310 function extractFrames(file, reader) { | 309 function extractFrames(file, reader) { |
311 var id3v2 = metadata.id3v2; | 310 var id3v2 = metadata.id3v2; |
312 | 311 |
313 if ((id3v2.major > 2) | 312 if ((id3v2.major > 2) && |
314 && (id3v2.flags & Id3Parser.v2.FLAG_EXTENDED_HEADER != 0)) { | 313 (id3v2.flags & Id3Parser.v2.FLAG_EXTENDED_HEADER != 0)) { |
315 // Skip extended header if found | 314 // Skip extended header if found |
316 if (id3v2.major == 3) { | 315 if (id3v2.major == 3) { |
317 reader.seek(reader.readScalar(4, false) - 4); | 316 reader.seek(reader.readScalar(4, false) - 4); |
318 } else if (id3v2.major == 4) { | 317 } else if (id3v2.major == 4) { |
319 reader.seek(Id3Parser.readSynchSafe_(reader, 4) - 4); | 318 reader.seek(Id3Parser.readSynchSafe_(reader, 4) - 4); |
320 } | 319 } |
321 } | 320 } |
322 | 321 |
323 var frame; | 322 var frame; |
324 | 323 |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
364 break; | 363 break; |
365 } | 364 } |
366 } | 365 } |
367 } | 366 } |
368 | 367 |
369 extract('album', 'TALB', 'TAL'); | 368 extract('album', 'TALB', 'TAL'); |
370 extract('title', 'TIT2', 'TT2'); | 369 extract('title', 'TIT2', 'TT2'); |
371 extract('artist', 'TPE1', 'TP1'); | 370 extract('artist', 'TPE1', 'TP1'); |
372 | 371 |
373 metadata.description.sort(function(a, b) { | 372 metadata.description.sort(function(a, b) { |
374 return Id3Parser.METADATA_ORDER.indexOf(a.key)- | 373 return Id3Parser.METADATA_ORDER.indexOf(a.key) - |
375 Id3Parser.METADATA_ORDER.indexOf(b.key); | 374 Id3Parser.METADATA_ORDER.indexOf(b.key); |
376 }); | 375 }); |
377 this.nextStep(); | 376 this.nextStep(); |
378 } | 377 } |
379 ], | 378 ], |
380 this | 379 this |
381 ); | 380 ); |
382 | 381 |
383 var metadataParser = new FunctionParallel( | 382 var metadataParser = new FunctionParallel( |
384 'mp3metadataParser', | 383 'mp3metadataParser', |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
432 /** | 431 /** |
433 * id3v1 constants | 432 * id3v1 constants |
434 */ | 433 */ |
435 Id3Parser.v1 = { | 434 Id3Parser.v1 = { |
436 /** | 435 /** |
437 * Genres list as described in id3 documentation. We aren't going to | 436 * Genres list as described in id3 documentation. We aren't going to |
438 * localize this list, because at least in Russian (and I think most | 437 * localize this list, because at least in Russian (and I think most |
439 * other languages), translation exists at least fo 10% and most time | 438 * other languages), translation exists at least fo 10% and most time |
440 * translation would degrade to transliteration. | 439 * translation would degrade to transliteration. |
441 */ | 440 */ |
442 GENRES : [ | 441 GENRES: [ |
443 'Blues', | 442 'Blues', |
444 'Classic Rock', | 443 'Classic Rock', |
445 'Country', | 444 'Country', |
446 'Dance', | 445 'Dance', |
447 'Disco', | 446 'Disco', |
448 'Funk', | 447 'Funk', |
449 'Grunge', | 448 'Grunge', |
450 'Hip-Hop', | 449 'Hip-Hop', |
451 'Jazz', | 450 'Jazz', |
452 'Metal', | 451 'Metal', |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
597 Id3Parser.v2 = { | 596 Id3Parser.v2 = { |
598 FLAG_EXTENDED_HEADER: 1 << 5, | 597 FLAG_EXTENDED_HEADER: 1 << 5, |
599 | 598 |
600 ENCODING: { | 599 ENCODING: { |
601 /** | 600 /** |
602 * ISO-8859-1 [ISO-8859-1]. Terminated with $00. | 601 * ISO-8859-1 [ISO-8859-1]. Terminated with $00. |
603 * | 602 * |
604 * @const | 603 * @const |
605 * @type {int} | 604 * @type {int} |
606 */ | 605 */ |
607 ISO_8859_1 : 0, | 606 ISO_8859_1: 0, |
608 | 607 |
609 | 608 |
610 /** | 609 /** |
611 * [UTF-16] encoded Unicode [UNICODE] with BOM. All | 610 * [UTF-16] encoded Unicode [UNICODE] with BOM. All |
612 * strings in the same frame SHALL have the same byteorder. | 611 * strings in the same frame SHALL have the same byteorder. |
613 * Terminated with $00 00. | 612 * Terminated with $00 00. |
614 * | 613 * |
615 * @const | 614 * @const |
616 * @type {int} | 615 * @type {int} |
617 */ | 616 */ |
618 UTF_16 : 1, | 617 UTF_16: 1, |
619 | 618 |
620 /** | 619 /** |
621 * UTF-16BE [UTF-16] encoded Unicode [UNICODE] without BOM. | 620 * UTF-16BE [UTF-16] encoded Unicode [UNICODE] without BOM. |
622 * Terminated with $00 00. | 621 * Terminated with $00 00. |
623 * | 622 * |
624 * @const | 623 * @const |
625 * @type {int} | 624 * @type {int} |
626 */ | 625 */ |
627 UTF_16BE : 2, | 626 UTF_16BE: 2, |
628 | 627 |
629 /** | 628 /** |
630 * UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00. | 629 * UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00. |
631 * | 630 * |
632 * @const | 631 * @const |
633 * @type {int} | 632 * @type {int} |
634 */ | 633 */ |
635 UTF_8 : 3 | 634 UTF_8: 3 |
636 }, | 635 }, |
637 HANDLERS: { | 636 HANDLERS: { |
638 //User defined text information frame | 637 //User defined text information frame |
639 TXX: Id3Parser.prototype.readUserDefinedTextFrame_, | 638 TXX: Id3Parser.prototype.readUserDefinedTextFrame_, |
640 //User defined URL link frame | 639 //User defined URL link frame |
641 WXX: Id3Parser.prototype.readUserDefinedTextFrame_, | 640 WXX: Id3Parser.prototype.readUserDefinedTextFrame_, |
642 | 641 |
643 //User defined text information frame | 642 //User defined text information frame |
644 TXXX: Id3Parser.prototype.readUserDefinedTextFrame_, | 643 TXXX: Id3Parser.prototype.readUserDefinedTextFrame_, |
645 | 644 |
(...skipping 24 matching lines...) Expand all Loading... |
670 TYER: 'ID3_YEAR', | 669 TYER: 'ID3_YEAR', |
671 WCOP: 'ID3_COPYRIGHT', | 670 WCOP: 'ID3_COPYRIGHT', |
672 WOAF: 'ID3_OFFICIAL_AUDIO_FILE_WEBPAGE', | 671 WOAF: 'ID3_OFFICIAL_AUDIO_FILE_WEBPAGE', |
673 WOAR: 'ID3_OFFICIAL_ARTIST', | 672 WOAR: 'ID3_OFFICIAL_ARTIST', |
674 WOAS: 'ID3_OFFICIAL_AUDIO_SOURCE_WEBPAGE', | 673 WOAS: 'ID3_OFFICIAL_AUDIO_SOURCE_WEBPAGE', |
675 WPUB: 'ID3_PUBLISHERS_OFFICIAL_WEBPAGE' | 674 WPUB: 'ID3_PUBLISHERS_OFFICIAL_WEBPAGE' |
676 } | 675 } |
677 }; | 676 }; |
678 | 677 |
679 MetadataDispatcher.registerParserClass(Id3Parser); | 678 MetadataDispatcher.registerParserClass(Id3Parser); |
OLD | NEW |