diff --git a/audio-metadata.js b/audio-metadata.js new file mode 100644 index 0000000..e4b6b97 --- /dev/null +++ b/audio-metadata.js @@ -0,0 +1,1403 @@ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.AudioMetadata = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0; + + if (extendedHeader) { + offset += getUint28(view, offset); + } + + function readFrame( offset ) { + try { + var id = utils.readAscii(view, offset, 4); + var size = getUint28(view, offset + 4); + offset += 8; + var frameFlags = [ view.getUint8(offset++), view.getUint8(offset++) ] + + if ( ( id[0] !== 'T' ) && ( id[0] !== 'W' ) && ( !utils.id3v2.languageFrames[ id ] ) ) { + return !( frame.id && reFrameId.test( frame.id ) ) ? null: { + id: id, + content: Buffer.from( view.buffer.slice( offset, offset + size ) ), + size: size + 10, + flags: frameFlags + }; + } + + var encoding = ( ( id[0] === 'W' ) && ( id !== 'WXXX' ) ) ? + -1 : view.getUint8( offset ++ ); + var data = ''; + + if ((encoding >= 0) && (encoding <= 3)) { + // decode as per the encoding map + data = utils.readText(view, offset, size - 1, encodingMap[ encoding ] ); + } else { + //no encoding info, read it as ascii + data = utils.readAscii(view, offset, size); + } + + //id3v2.4 is supposed to have encoding terminations, but sometimes + //they don't? meh. + data = utils.trimNull(data); + + return { + id: id, + size: size + 10, + flags: frameFlags, + content: data, + encoding: encoding + }; + } catch (e) { + return null; + } + } + + var endOfTags = offset + size, + frames = { '_': { } }, + frame; + + while (offset < endOfTags) { + frame = readFrame(offset); + if (!frame) { + break; + } + + offset += frame.size; + + var id = idMap[frame.id] || frame.id; + if ( ( ( frame.id === 'TXXX' ) || ( frame.id === 'WXXX' ) ) && + ( typeof frame.content === 'string' ) ) { + var nullByte = frame.content.indexOf('\u0000'); + if ( ( nullByte > 0 ) && ( + id = frame.content.substring(0, nullByte) + + ( ( frame.id === 'WXXX' ) ? '_url': '' ) ) ) { + frames[ id ] = frame.content.substring( nullByte + 1 ); + } + } else { + frames[frame.id] = frame.content; + var idx; + // allows bundling TITX, TPEX + if ( ( frame.id !== id ) && + ( ( idx = frame.id.slice( -1 ) ) >= '1' ) && + !isNaN( ( idx = parseInt( idx ) ) ) && idx ) { + if ( !Array.isArray( frames[id] ) ) { + frames[ id ] = [ ]; + } + frames[ id ][ --idx ] = frame.content; + } else { + frames[ id ] = frame.content; + } + } + frame.content = ( utils.id3v2.languageFrames[ frame.id ] || utils.I )( frame.content ); + ( frames._[frame.id] = ( frames._[frame.id] || [ ] ) ).push( frame ); + delete frame.id; + delete frame.size; + } + + // normalizes bundles + for( var id in frames ) { + if ( Array.isArray( frame = frames[ id ] ) ) { + size = 0; + for( offset = frame.length - 1; offset >= 0; offset-- ) { + if ( typeof frame[ offset ] === 'string' ) { + if ( size++ ) { + break; + } + } + } + if ( size <= 1 ) { + frames[ id ] = frame.join( '' ); + } + } + } + return frames; +}; + +function isTagContentInArray( tagName, content, arr ) { + if ( !( tagName && ( tagName = String( tagName ) ) && + content && + arr && Array.isArray( arr ) ) ) { + return undefined; + } + content = ( utils.id3v2.languageFrames[ tagName ] || utils.I )( content ); + for ( var i = arr.length - 1; i >= 0; i-- ) { + if ( arr[ i ].content === content ) { + return i; + } + } + return -1; +} + +var reUrlLike = /^http(?:s)?:/i; +function getContent( frame ) { + return frame.content; }; +var multiFrames = ( function() { + var _content = getContent; + function _prefix( trim, min, frame ) { + var rem = ( frame = _content( frame ) ).slice( 0, Math.min( trim, 3 ) ); + rem = rem + '\0' + ( frame = frame.slice( trim ) ).slice( + 0, ( ( frame = ( min ? frame: '\0' ).indexOf( '\0' ) ) + 1 < min ) ? + undefined: ( frame + ( frame ? 1 : 0 ) ) ); + return rem; }; + var _desc = _prefix.bind( null, 0, 1 ); + var _lang = _prefix.bind( null, 3, 1 ); + return { + TLAN: _content, + TXXX: _desc, + WCOM: _content, + WOAR: _content, + WXXX: _desc, + USLT: _lang, + SYLT: _prefix.bind( null, 5, 1 ), + COMM: _lang, + WCOP: _prefix.bind( null, 3, 0 ) + }; +} )( ); +function serializeTag( frames, opts ) { + + var cache = frames._ || { }; + frames._ = cache; + var arr, content, i, it, array, mappedK, j, l; + + for( i in { tag: 1, frame: 1 } ) { + if ( opts && ( ( ( j = opts[ i + 'Align' ] ) & 0x7 ) || ( j < 1 ) ) ) { + throw new Error( 'Invalid ' + i + ' align option. Should be positive multiple of 8.' ); + } + } + // serialization takes place after preparation happening few passes: + + // NOTE: speacial care must be taken for comment/ COMMM + // 1. upper case mapped to _ items or new items stored in _ + + // 2. lower case mapped to upper case and in case of value discrepancies mapped into + // TXXX or WXXX frames + var pass, k, kU; + for( pass = 0; pass < 2; pass++ ) { + for( k in frames ) { + kU = reFrameId.test( k ); + if ( ( ( pass === 0 ) && !kU ) || + ( ( pass === 1 ) && kU ) || + ( k === '_' ) ) { + continue; + } + content = frames[ k ]; + l = ( ( array = Array.isArray( content ) ) ? content.length: 1 ); + for ( i = 0; i < l; i++ ) { + it = array ? content[ i ]: content; + if ( ( mappedK = ( ( pass === 0 ) ? + // makes sure we are delaing with valid frame id + utils.id3v2.frameMap[ k ] && k : + // ensures the descriptive name is knows and + // subject of translation to a valid frame id + utils.id3v2.frameMapInv[ k ] ) ) && + ( !Array.isArray( mappedK ) ) ) { + mappedK = [ mappedK ]; + } + // if delaing with something unrecognized, + // decide whether it should be listed: + // 1) standalone in case of a buffer content type + // 2) TXXX in case of string non url like content + // 3) WXXX in case of url like string + if ( !( mappedK && ( + mappedK = ( mappedK[ i ] || + ( multiFrames[ mappedK [ 0 ] ] && mappedK[ 0 ] ) ) ) ) ) { + if ( !( mappedK = ( + // look for _url hint + ( ( k.length > 4 && k.slice( -4 ) === '_url' ) ? + ( k = k.slice( 0, -4 ) ): '' ) || + // simple content url test + reUrlLike.test( it ) ) ? + // the content may need adjustment + // as per the WXXX spec + ( ( it = utils.id3v2.ensureLanguage( it, k + '\0', '' ) ) && + 'WXXX' ): ( + ( ( typeof it === 'string' ) && ( !reFrameId.test( k ) ) ) ? + // the content may need adjustment + // as per TXXX spec + ( ( it = utils.id3v2.ensureLanguage( it, k + '\0', '' ) ) && + 'TXXX' ): ( + ( ( pass === 0 ) && + ( k.length === 4 ) && + ( reFrameId.test( k ) ) && + ( Buffer.isBuffer( it ) ) ) ? + k: undefined ) ) ) ) { + throw new Error( 'Unsupported frame ' + k ); + } + if ( !reFrameId.test( mappedK ) ) { + throw new Error( 'Unexpected frame ' + mappedK ); + } + } + if ( !( arr = cache[ mappedK ] ) ) { + cache[ mappedK ] = arr = [ ]; + } + // format the language tags + it = ( utils.id3v2.languageFrames[ mappedK ] || utils.I )( it ); + if ( ( j = isTagContentInArray( mappedK, it, arr ) ) === -1 ) { + // add last + arr.push( it = utils.id3v2.defaultFrame( mappedK, it ) ); + } else if ( j !== undefined ) { + // make sure at the tail + // never mind the duplication, the next step + // handles compaction + arr.push( arr[ j ] ); + } + } + } + } + + // 3. compact the arrays from the cache + for ( k in cache ) { + if ( !( ( arr = cache[ k ] ) && Array.isArray( arr ) && arr.length ) ) { + continue; + } + if ( !( ( it = multiFrames[ k ] ) || + // for non multi ones, use the last entry + ( utils.id3v2.frameMap[ k ] && + ( cache[ k ] = arr = ( arr.length ? [ arr[ arr.length - 1 ] ]: [ ] ) ) && + ( it = getContent ) ) ) ) { + // do not play with unknown tags, just pass through + continue; + } + mappedK = { }; + array = [ ]; + for( i = arr.length - 1; i >= 0; i -- ) { + if ( mappedK[ j = it( arr[ i ] ) ] ) { + continue; + } + array.push( arr[ i ] ); + mappedK[ j ] = 1; + } + cache[ k ] = array; + } + // purge empty entires + for ( k in cache ) { + if ( !cache[ k ].length ) { + delete cache[ k ]; + } + } + + // return the optimized cache, if thats all needed + if ( opts && opts.inCache ) { + return frames; + } + + // frames serialization into buffers. Once again in the cache. + var header, padding, enc, alignment; + l = 0; + alignment = ( ( opts && opts.frameAlign ) || 8 ) / 8; + for ( k in cache ) { + + header = new Buffer( k + ( new Array( 7 ) ).join( '\0' ), 'ascii' ); + if( !( header.length === 10 ) ) { + throw new Error( 'Oops. Invalid frame header length' ); + } + + for( arr = cache[ k ], i = arr.length - 1; i >= 0; i-- ) { + it = arr[ i ]; enc = -1; + it.buffer = ( Buffer.isBuffer( it.content ) ) ? + it.content: + utils.encode( it.content + '\0', encodingMap[ enc = ( it.encoding || -1 ) ] ); + header[ 8 ] = it.flags[ 0 ]; + header[ 9 ] = it.flags[ 1 ]; + enc = new Buffer( ( enc > -1 ) ? [ 0xff & enc ]: [] ); + j = ( header.length + enc.length + it.buffer.length ); + padding = new Buffer.alloc( Math.ceil( j / alignment ) * alignment - j ); + j = ( enc.length + it.buffer.length + padding.length ); + header.writeInt32BE( synchsafe( j ), 4 ); + it.buffer = Buffer.concat( + [ header, enc, it.buffer, padding ], + header.length + j ); + // keep track of total buffer length + l += it.buffer.length; + } + } + + if ( opts && opts.inCacheBuffers ) { + return frames; + } + + // FINALLY dumping everything into a tag frame. We ommit the extended header, + // though padding may be added to make the tag 8x bit aligned. + alignment = ( ( opts && opts.tagAlign ) || 8 ) / 8; + header = new Buffer( [ 0x49, 0x44, 0x33, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] ); + padding = new Buffer.alloc( Math.ceil( ( i = ( header.length + l ) ) / alignment ) * alignment - i ); + header.writeInt32BE( synchsafe( l + padding.length ), 6 ); + array = [ header ]; + for ( k in cache ) { + for( arr = cache[ k ], i = arr.length - 1; i >= 0; i-- ) { + array.push( arr[ i ].buffer ); + } + } + array.push( padding ); + frames = Buffer.concat( array, header.length + l + padding.length ); + + return frames; +} + +parseTag.serialize = serializeTag; + +module.exports = parseTag; + +},{"./utils":6}],4:[function(require,module,exports){ +module.exports = { + '\0\0\0': + 1, // ["Unspecified"] + aar: 1, // ["Afar"] + abk: 1, // ["Abkhazian"] + ace: 1, // ["Achinese"] + ach: 1, // ["Acoli"] + ada: 1, // ["Adangme"] + ady: 1, // ["Adygei","Adyghe"] + afa: 1, // ["Afro-Asiatic Languages"] + afh: 1, // ["Afrihili"] + afr: 1, // ["Afrikaans"] + ain: 1, // ["Ainu"] + aka: 1, // ["Akan"] + akk: 1, // ["Akkadian"] + alb: 1, // ["Albanian"] + sqi: 1, // ["Albanian"] + ale: 1, // ["Aleut"] + alg: 1, // ["Algonquian Languages"] + alt: 1, // ["Southern Altai"] + amh: 1, // ["Amharic"] + ang: 1, // ["English Old (ca.450-1100)"] + anp: 1, // ["Angika"] + apa: 1, // ["Apache Languages"] + ara: 1, // ["Arabic"] + arc: 1, // ["Imperial Aramaic (700-300 BCE)","Official Aramaic (700-300 BCE)"] + arg: 1, // ["Aragonese"] + arm: 1, // ["Armenian"] + hye: 1, // ["Armenian"] + arn: 1, // ["Mapuche","Mapudungun"] + arp: 1, // ["Arapaho"] + art: 1, // ["Artificial Languages"] + arw: 1, // ["Arawak"] + asm: 1, // ["Assamese"] + ast: 1, // ["Asturian","Asturleonese","Bable","Leonese"] + ath: 1, // ["Athapascan Languages"] + aus: 1, // ["Australian Languages"] + ava: 1, // ["Avaric"] + ave: 1, // ["Avestan"] + awa: 1, // ["Awadhi"] + aym: 1, // ["Aymara"] + aze: 1, // ["Azerbaijani"] + bad: 1, // ["Banda Languages"] + bai: 1, // ["Bamileke Languages"] + bak: 1, // ["Bashkir"] + bal: 1, // ["Baluchi"] + bam: 1, // ["Bambara"] + ban: 1, // ["Balinese"] + baq: 1, // ["Basque"] + eus: 1, // ["Basque"] + bas: 1, // ["Basa"] + bat: 1, // ["Baltic Languages"] + bej: 1, // ["Bedawiyet","Beja"] + bel: 1, // ["Belarusian"] + bem: 1, // ["Bemba"] + ben: 1, // ["Bengali"] + ber: 1, // ["Berber Languages"] + bho: 1, // ["Bhojpuri"] + bih: 1, // ["Bihari Languages"] + bik: 1, // ["Bikol"] + bin: 1, // ["Bini","Edo"] + bis: 1, // ["Bislama"] + bla: 1, // ["Siksika"] + bnt: 1, // ["Bantu (Other)"] + bos: 1, // ["Bosnian"] + bra: 1, // ["Braj"] + bre: 1, // ["Breton"] + btk: 1, // ["Batak Languages"] + bua: 1, // ["Buriat"] + bug: 1, // ["Buginese"] + bul: 1, // ["Bulgarian"] + bur: 1, // ["Burmese"] + mya: 1, // ["Burmese"] + byn: 1, // ["Bilin","Blin"] + cad: 1, // ["Caddo"] + cai: 1, // ["Central American Indian Languages"] + car: 1, // ["Galibi Carib"] + cat: 1, // ["Catalan","Valencian"] + cau: 1, // ["Caucasian Languages"] + ceb: 1, // ["Cebuano"] + cel: 1, // ["Celtic Languages"] + cha: 1, // ["Chamorro"] + chb: 1, // ["Chibcha"] + che: 1, // ["Chechen"] + chg: 1, // ["Chagatai"] + chi: 1, // ["Chinese"] + zho: 1, // ["Chinese"] + chk: 1, // ["Chuukese"] + chm: 1, // ["Mari"] + chn: 1, // ["Chinook Jargon"] + cho: 1, // ["Choctaw"] + chp: 1, // ["Chipewyan","Dene Suline"] + chr: 1, // ["Cherokee"] + chu: 1, // ["Church Slavic","Church Slavonic","Old Bulgarian","Old Church Slavonic","Old Slavonic"] + chv: 1, // ["Chuvash"] + chy: 1, // ["Cheyenne"] + cmc: 1, // ["Chamic Languages"] + cop: 1, // ["Coptic"] + cor: 1, // ["Cornish"] + cos: 1, // ["Corsican"] + cpe: 1, // ["Creoles And Pidgins","English Based"] + cpf: 1, // ["Creoles And Pidgins","French-based"] + cpp: 1, // ["Creoles And Pidgins","Portuguese-based"] + cre: 1, // ["Cree"] + crh: 1, // ["Crimean Tatar","Crimean Turkish"] + crp: 1, // ["Creoles And Pidgins"] + csb: 1, // ["Kashubian"] + cus: 1, // ["Cushitic Languages"] + cze: 1, // ["Czech"] + ces: 1, // ["Czech"] + dak: 1, // ["Dakota"] + dan: 1, // ["Danish"] + dar: 1, // ["Dargwa"] + day: 1, // ["Land Dayak Languages"] + del: 1, // ["Delaware"] + den: 1, // ["Slave (Athapascan)"] + dgr: 1, // ["Dogrib"] + din: 1, // ["Dinka"] + div: 1, // ["Dhivehi","Divehi","Maldivian"] + doi: 1, // ["Dogri"] + dra: 1, // ["Dravidian Languages"] + dsb: 1, // ["Lower Sorbian"] + dua: 1, // ["Duala"] + dum: 1, // ["Dutch Middle (ca.1050-1350)"] + dut: 1, // ["Dutch","Flemish"] + nld: 1, // ["Dutch","Flemish"] + dyu: 1, // ["Dyula"] + dzo: 1, // ["Dzongkha"] + efi: 1, // ["Efik"] + egy: 1, // ["Egyptian (Ancient)"] + eka: 1, // ["Ekajuk"] + elx: 1, // ["Elamite"] + eng: 1, // ["English"] + enm: 1, // ["English Middle (1100-1500)"] + epo: 1, // ["Esperanto"] + est: 1, // ["Estonian"] + ewe: 1, // ["Ewe"] + ewo: 1, // ["Ewondo"] + fan: 1, // ["Fang"] + fao: 1, // ["Faroese"] + fat: 1, // ["Fanti"] + fij: 1, // ["Fijian"] + fil: 1, // ["Filipino","Pilipino"] + fin: 1, // ["Finnish"] + fiu: 1, // ["Finno-Ugrian Languages"] + fon: 1, // ["Fon"] + fre: 1, // ["French"] + fra: 1, // ["French"] + frm: 1, // ["French Middle (ca.1400-1600)"] + fro: 1, // ["French Old (842-ca.1400)"] + frr: 1, // ["Northern Frisian"] + frs: 1, // ["Eastern Frisian"] + fry: 1, // ["Western Frisian"] + ful: 1, // ["Fulah"] + fur: 1, // ["Friulian"] + gaa: 1, // ["Ga"] + gay: 1, // ["Gayo"] + gba: 1, // ["Gbaya"] + gem: 1, // ["Germanic Languages"] + geo: 1, // ["Georgian"] + kat: 1, // ["Georgian"] + ger: 1, // ["German"] + deu: 1, // ["German"] + gez: 1, // ["Geez"] + gil: 1, // ["Gilbertese"] + gla: 1, // ["Gaelic","Scottish Gaelic"] + gle: 1, // ["Irish"] + glg: 1, // ["Galician"] + glv: 1, // ["Manx"] + gmh: 1, // ["German Middle High (ca.1050-1500)"] + goh: 1, // ["German Old High (ca.750-1050)"] + gon: 1, // ["Gondi"] + gor: 1, // ["Gorontalo"] + got: 1, // ["Gothic"] + grb: 1, // ["Grebo"] + grc: 1, // ["Greek Ancient (to 1453)"] + gre: 1, // ["Greek Modern (1453-)"] + ell: 1, // ["Greek Modern (1453-)"] + grn: 1, // ["Guarani"] + gsw: 1, // ["Alemannic","Alsatian","Swiss German"] + guj: 1, // ["Gujarati"] + gwi: 1, // ["Gwich'in"] + hai: 1, // ["Haida"] + hat: 1, // ["Haitian","Haitian Creole"] + hau: 1, // ["Hausa"] + haw: 1, // ["Hawaiian"] + heb: 1, // ["Hebrew"] + her: 1, // ["Herero"] + hil: 1, // ["Hiligaynon"] + him: 1, // ["Himachali Languages","Western Pahari Languages"] + hin: 1, // ["Hindi"] + hit: 1, // ["Hittite"] + hmn: 1, // ["Hmong","Mong"] + hmo: 1, // ["Hiri Motu"] + hrv: 1, // ["Croatian"] + hsb: 1, // ["Upper Sorbian"] + hun: 1, // ["Hungarian"] + hup: 1, // ["Hupa"] + iba: 1, // ["Iban"] + ibo: 1, // ["Igbo"] + ice: 1, // ["Icelandic"] + isl: 1, // ["Icelandic"] + ido: 1, // ["Ido"] + iii: 1, // ["Nuosu","Sichuan Yi"] + ijo: 1, // ["Ijo Languages"] + iku: 1, // ["Inuktitut"] + ile: 1, // ["Interlingue","Occidental"] + ilo: 1, // ["Iloko"] + ina: 1, // ["Interlingua (International Auxiliary Language Association)"] + inc: 1, // ["Indic Languages"] + ind: 1, // ["Indonesian"] + ine: 1, // ["Indo-European Languages"] + inh: 1, // ["Ingush"] + ipk: 1, // ["Inupiaq"] + ira: 1, // ["Iranian Languages"] + iro: 1, // ["Iroquoian Languages"] + ita: 1, // ["Italian"] + jav: 1, // ["Javanese"] + jbo: 1, // ["Lojban"] + jpn: 1, // ["Japanese"] + jpr: 1, // ["Judeo-Persian"] + jrb: 1, // ["Judeo-Arabic"] + kaa: 1, // ["Kara-Kalpak"] + kab: 1, // ["Kabyle"] + kac: 1, // ["Jingpho","Kachin"] + kal: 1, // ["Greenlandic","Kalaallisut"] + kam: 1, // ["Kamba"] + kan: 1, // ["Kannada"] + kar: 1, // ["Karen Languages"] + kas: 1, // ["Kashmiri"] + kau: 1, // ["Kanuri"] + kaw: 1, // ["Kawi"] + kaz: 1, // ["Kazakh"] + kbd: 1, // ["Kabardian"] + kha: 1, // ["Khasi"] + khi: 1, // ["Khoisan Languages"] + khm: 1, // ["Central Khmer"] + kho: 1, // ["Khotanese","Sakan"] + kik: 1, // ["Gikuyu","Kikuyu"] + kin: 1, // ["Kinyarwanda"] + kir: 1, // ["Kirghiz","Kyrgyz"] + kmb: 1, // ["Kimbundu"] + kok: 1, // ["Konkani"] + kom: 1, // ["Komi"] + kon: 1, // ["Kongo"] + kor: 1, // ["Korean"] + kos: 1, // ["Kosraean"] + kpe: 1, // ["Kpelle"] + krc: 1, // ["Karachay-Balkar"] + krl: 1, // ["Karelian"] + kro: 1, // ["Kru Languages"] + kru: 1, // ["Kurukh"] + kua: 1, // ["Kuanyama","Kwanyama"] + kum: 1, // ["Kumyk"] + kur: 1, // ["Kurdish"] + kut: 1, // ["Kutenai"] + lad: 1, // ["Ladino"] + lah: 1, // ["Lahnda"] + lam: 1, // ["Lamba"] + lao: 1, // ["Lao"] + lat: 1, // ["Latin"] + lav: 1, // ["Latvian"] + lez: 1, // ["Lezghian"] + lim: 1, // ["Limburgan","Limburger","Limburgish"] + lin: 1, // ["Lingala"] + lit: 1, // ["Lithuanian"] + lol: 1, // ["Mongo"] + loz: 1, // ["Lozi"] + ltz: 1, // ["Letzeburgesch","Luxembourgish"] + lua: 1, // ["Luba-Lulua"] + lub: 1, // ["Luba-Katanga"] + lug: 1, // ["Ganda"] + lui: 1, // ["Luiseno"] + lun: 1, // ["Lunda"] + luo: 1, // ["Luo (Kenya And Tanzania)"] + lus: 1, // ["Lushai"] + mac: 1, // ["Macedonian"] + mkd: 1, // ["Macedonian"] + mad: 1, // ["Madurese"] + mag: 1, // ["Magahi"] + mah: 1, // ["Marshallese"] + mai: 1, // ["Maithili"] + mak: 1, // ["Makasar"] + mal: 1, // ["Malayalam"] + man: 1, // ["Mandingo"] + mao: 1, // ["Maori"] + mri: 1, // ["Maori"] + map: 1, // ["Austronesian Languages"] + mar: 1, // ["Marathi"] + mas: 1, // ["Masai"] + may: 1, // ["Malay"] + msa: 1, // ["Malay"] + mdf: 1, // ["Moksha"] + mdr: 1, // ["Mandar"] + men: 1, // ["Mende"] + mga: 1, // ["Irish Middle (900-1200)"] + mic: 1, // ["Mi'kmaq","Micmac"] + min: 1, // ["Minangkabau"] + mis: 1, // ["Uncoded Languages"] + mkh: 1, // ["Mon-Khmer Languages"] + mlg: 1, // ["Malagasy"] + mlt: 1, // ["Maltese"] + mnc: 1, // ["Manchu"] + mni: 1, // ["Manipuri"] + mno: 1, // ["Manobo Languages"] + moh: 1, // ["Mohawk"] + mon: 1, // ["Mongolian"] + mos: 1, // ["Mossi"] + mul: 1, // ["Multiple Languages"] + mun: 1, // ["Munda Languages"] + mus: 1, // ["Creek"] + mwl: 1, // ["Mirandese"] + mwr: 1, // ["Marwari"] + myn: 1, // ["Mayan Languages"] + myv: 1, // ["Erzya"] + nah: 1, // ["Nahuatl Languages"] + nai: 1, // ["North American Indian Languages"] + nap: 1, // ["Neapolitan"] + nau: 1, // ["Nauru"] + nav: 1, // ["Navaho","Navajo"] + nbl: 1, // ["Ndebele","South"] + nde: 1, // ["Ndebele","North"] + ndo: 1, // ["Ndonga"] + nds: 1, // ["Low","Saxon"] + nep: 1, // ["Nepali"] + new: 1, // ["Nepal Bhasa","Newari"] + nia: 1, // ["Nias"] + nic: 1, // ["Niger-Kordofanian Languages"] + niu: 1, // ["Niuean"] + nno: 1, // ["Norwegian","Nynorsk"] + nob: 1, // ["Bokmål","Norwegian"] + nog: 1, // ["Nogai"] + non: 1, // ["Norse","Old"] + nor: 1, // ["Norwegian"] + nqo: 1, // ["N'Ko"] + nso: 1, // ["Northern Sotho","Pedi","Sepedi"] + nub: 1, // ["Nubian Languages"] + nwc: 1, // ["Classical Nepal Bhasa","Classical Newari","Old Newari"] + nya: 1, // ["Chewa","Chichewa","Nyanja"] + nym: 1, // ["Nyamwezi"] + nyn: 1, // ["Nyankole"] + nyo: 1, // ["Nyoro"] + nzi: 1, // ["Nzima"] + oci: 1, // ["Occitan (post 1500)","Provençal"] + oji: 1, // ["Ojibwa"] + ori: 1, // ["Oriya"] + orm: 1, // ["Oromo"] + osa: 1, // ["Osage"] + oss: 1, // ["Ossetian","Ossetic"] + ota: 1, // ["Turkish Ottoman (1500-1928)"] + oto: 1, // ["Otomian Languages"] + paa: 1, // ["Papuan Languages"] + pag: 1, // ["Pangasinan"] + pal: 1, // ["Pahlavi"] + pam: 1, // ["Kapampangan","Pampanga"] + pan: 1, // ["Panjabi","Punjabi"] + pap: 1, // ["Papiamento"] + pau: 1, // ["Palauan"] + peo: 1, // ["Persian Old (ca.600-400 B.C.)"] + per: 1, // ["Persian"] + fas: 1, // ["Persian"] + phi: 1, // ["Philippine Languages"] + phn: 1, // ["Phoenician"] + pli: 1, // ["Pali"] + pol: 1, // ["Polish"] + pon: 1, // ["Pohnpeian"] + por: 1, // ["Portuguese"] + pra: 1, // ["Prakrit Languages"] + pro: 1, // ["Provençal Old (to 1500)"] + pus: 1, // ["Pashto","Pushto"] + que: 1, // ["Quechua"] + raj: 1, // ["Rajasthani"] + rap: 1, // ["Rapanui"] + rar: 1, // ["Cook Islands Maori","Rarotongan"] + roa: 1, // ["Romance Languages"] + roh: 1, // ["Romansh"] + rom: 1, // ["Romany"] + rum: 1, // ["Moldavian","Moldovan","Romanian"] + ron: 1, // ["Moldavian","Moldovan","Romanian"] + run: 1, // ["Rundi"] + rup: 1, // ["Aromanian","Arumanian","Macedo-Romanian"] + rus: 1, // ["Russian"] + sad: 1, // ["Sandawe"] + sag: 1, // ["Sango"] + sah: 1, // ["Yakut"] + sai: 1, // ["South American Indian (Other)"] + sal: 1, // ["Salishan Languages"] + sam: 1, // ["Samaritan Aramaic"] + san: 1, // ["Sanskrit"] + sas: 1, // ["Sasak"] + sat: 1, // ["Santali"] + scn: 1, // ["Sicilian"] + sco: 1, // ["Scots"] + sel: 1, // ["Selkup"] + sem: 1, // ["Semitic Languages"] + sga: 1, // ["Irish Old (to 900)"] + sgn: 1, // ["Sign Languages"] + shn: 1, // ["Shan"] + sid: 1, // ["Sidamo"] + sin: 1, // ["Sinhala","Sinhalese"] + sio: 1, // ["Siouan Languages"] + sit: 1, // ["Sino-Tibetan Languages"] + sla: 1, // ["Slavic Languages"] + slo: 1, // ["Slovak"] + slk: 1, // ["Slovak"] + slv: 1, // ["Slovenian"] + sma: 1, // ["Southern Sami"] + sme: 1, // ["Northern Sami"] + smi: 1, // ["Sami Languages"] + smj: 1, // ["Lule Sami"] + smn: 1, // ["Inari Sami"] + smo: 1, // ["Samoan"] + sms: 1, // ["Skolt Sami"] + sna: 1, // ["Shona"] + snd: 1, // ["Sindhi"] + snk: 1, // ["Soninke"] + sog: 1, // ["Sogdian"] + som: 1, // ["Somali"] + son: 1, // ["Songhai Languages"] + sot: 1, // ["Sotho","Southern"] + spa: 1, // ["Castilian","Spanish"] + srd: 1, // ["Sardinian"] + srn: 1, // ["Sranan Tongo"] + srp: 1, // ["Serbian"] + srr: 1, // ["Serer"] + ssa: 1, // ["Nilo-Saharan Languages"] + ssw: 1, // ["Swati"] + suk: 1, // ["Sukuma"] + sun: 1, // ["Sundanese"] + sus: 1, // ["Susu"] + sux: 1, // ["Sumerian"] + swa: 1, // ["Swahili"] + swe: 1, // ["Swedish"] + syc: 1, // ["Classical Syriac"] + syr: 1, // ["Syriac"] + tah: 1, // ["Tahitian"] + tai: 1, // ["Tai Languages"] + tam: 1, // ["Tamil"] + tat: 1, // ["Tatar"] + tel: 1, // ["Telugu"] + tem: 1, // ["Timne"] + ter: 1, // ["Tereno"] + tet: 1, // ["Tetum"] + tgk: 1, // ["Tajik"] + tgl: 1, // ["Tagalog"] + tha: 1, // ["Thai"] + tib: 1, // ["Tibetan"] + bod: 1, // ["Tibetan"] + tig: 1, // ["Tigre"] + tir: 1, // ["Tigrinya"] + tiv: 1, // ["Tiv"] + tkl: 1, // ["Tokelau"] + tlh: 1, // ["Klingon","TlhIngan-Hol"] + tli: 1, // ["Tlingit"] + tmh: 1, // ["Tamashek"] + tog: 1, // ["Tonga (Nyasa)"] + ton: 1, // ["Tonga (Tonga Islands)"] + tpi: 1, // ["Tok Pisin"] + tsi: 1, // ["Tsimshian"] + tsn: 1, // ["Tswana"] + tso: 1, // ["Tsonga"] + tuk: 1, // ["Turkmen"] + tum: 1, // ["Tumbuka"] + tup: 1, // ["Tupi Languages"] + tur: 1, // ["Turkish"] + tut: 1, // ["Altaic Languages"] + tvl: 1, // ["Tuvalu"] + twi: 1, // ["Twi"] + tyv: 1, // ["Tuvinian"] + udm: 1, // ["Udmurt"] + uga: 1, // ["Ugaritic"] + uig: 1, // ["Uighur","Uyghur"] + ukr: 1, // ["Ukrainian"] + umb: 1, // ["Umbundu"] + und: 1, // ["Undetermined"] + urd: 1, // ["Urdu"] + uzb: 1, // ["Uzbek"] + vai: 1, // ["Vai"] + ven: 1, // ["Venda"] + vie: 1, // ["Vietnamese"] + vol: 1, // ["Volapük"] + vot: 1, // ["Votic"] + wak: 1, // ["Wakashan Languages"] + wal: 1, // ["Walamo"] + war: 1, // ["Waray"] + was: 1, // ["Washo"] + wel: 1, // ["Welsh"] + cym: 1, // ["Welsh"] + wen: 1, // ["Sorbian Languages"] + wln: 1, // ["Walloon"] + wol: 1, // ["Wolof"] + xal: 1, // ["Kalmyk","Oirat"] + xho: 1, // ["Xhosa"] + yao: 1, // ["Yao"] + yap: 1, // ["Yapese"] + yid: 1, // ["Yiddish"] + yor: 1, // ["Yoruba"] + ypk: 1, // ["Yupik Languages"] + zap: 1, // ["Zapotec"] + zbl: 1, // ["Bliss","Blissymbolics","Blissymbols"] + zen: 1, // ["Zenaga"] + zgh: 1, // ["Standard Moroccan Tamazight"] + zha: 1, // ["Chuang","Zhuang"] + znd: 1, // ["Zande Languages"] + zul: 1, // ["Zulu"] + zun: 1, // ["Zuni"] + zxx: 1, // ["No Linguistic Content","Not Applicable"] + zza: 1 // ["Dimili","Dimli","Kirdki","Kirmanjki","Zaza","Zazaki"] +}; +},{}],5:[function(require,module,exports){ +'use strict' +var utils = require('./utils'); + +function parsePage( view, offset, withPacket ) { + if (view.byteLength < offset + 27) { + return null; + } + + var numPageSegments = view.getUint8(offset + 26), + segmentTable = utils.readBytes(view, offset + 27, numPageSegments), + headerSize = 27 + numPageSegments; + + if (!segmentTable.length) { + return null; + } + + var + pageSize = headerSize + segmentTable.reduce(function(cur, next) { + return cur + next; + }), + length = headerSize + 1 + 'vorbis'.length, + packetView = null; + + if (withPacket) { + packetView = utils.createView(new ArrayBuffer(pageSize - length)); + utils.readBytes(view, offset + length, pageSize - length, packetView); + } + + return { + pageSize: pageSize, + packet: packetView + }; +} + +function parseComments(packet) { + try { + var vendorLength = packet.getUint32(0, true), + commentListLength = packet.getUint32(4 + vendorLength, true), + comments = {}, + offset = 8 + vendorLength, + map = { + tracknumber: 'track' + }; + + for (var i = 0; i < commentListLength; i++) { + var commentLength = packet.getUint32(offset, true), + comment = utils.readUtf8(packet, offset + 4, commentLength), + equals = comment.indexOf('='), + key = comment.substring(0, equals).toLowerCase(); + + comments[map[key] || key] = comments[key] = utils.trimNull(comment.substring(equals + 1)); + offset += 4 + commentLength; + } + + return comments; + + } catch (e) { + //all exceptions are just malformed/truncated data, so we just ignore them + return null; + } +} +/** + * See http://www.ietf.org/rfc/rfc3533.txt + * @param {Buffer|ArrayBuffer} buffer + */ +module.exports = function(buffer, opts) { + var view = utils.createView(buffer); + + var id = parsePage(view, 0); + if (!id) { + return null; + } + + var commentHeader = parsePage(view, id.pageSize, true); + if (!commentHeader) { + return null; + } + + var tag = parseComments(commentHeader.packet); + if ( opts && opts.toId3v2 ) { + tag = utils.id3v2.transformTag( tag ); + } + return tag; +}; +},{"./utils":6}],6:[function(require,module,exports){ +'use strict' + +var reNull = /[\u0000 ]+$/; + +function toArrayBuffer(buffer) { + var arrayBuffer = new ArrayBuffer(buffer.length); + var view = new Uint8Array(arrayBuffer); + for (var i = 0; i < buffer.length; ++i) { + view[i] = buffer[i]; + } + return arrayBuffer; +} + +function sliceView( view, offset, length ) { + if ( ( length < 1 ) || + ( offset < 0 ) || + ( view.byteLength < offset + length ) ) { + return undefined; + } + var buffer = view.buffer.slice( offset, offset + length ); + // workaround for a double BOM problem + // https://github.com/leetreveil/musicmetadata/issues/84 + if ( (buffer[0] === 0xFF && buffer[1] === 0xFE && buffer[2] === 0xFE && buffer[3] === 0xFF) || + (buffer[1] === 0xFF && buffer[0] === 0xFE && buffer[3] === 0xFE && buffer[2] === 0xFF) ) { + buffer = buffer.slice( 2 ) + } + return Buffer.from( buffer ) +} + + +var id3v2Map = { + TALB: 'album', + TOAL: 'original_album', + TPOS: 'part', + TCOM: 'composer', + TIT1: 'title', + TIT2: 'title', + TIT3: 'title', + TSST: 'subtitle', + TPE1: 'artist', + TPE2: 'artist', + TPE3: 'artist', + TPE4: 'artist', + TOPE: 'original_artist', + TEXT: 'lyricist', + TOLY: 'original_lyricist', + TRCK: 'track', + TSSE: 'encoder', + TDRC: 'year', + TCON: 'genre', + TSRC: 'isrc', + TMCL: 'musician_credit', + TIPL: 'involved_credit', + TENC: 'encoded_by', + TBPM: 'beats_per_minute', + TLEN: 'length', + TKEY: 'initial_key', + TLAN: 'language', + TFLT: 'file_type', + TMED: 'media_type', + TMOO: 'mood', + TCOP: 'copyright_message', + TPRO: 'produced_notice', + TPUB: 'publisher', + TOWN: 'licensee', + TRSN: 'internet_radio_station', + TRSO: 'internet_radio_station_owner', + TOFN: 'original_filename', + TDLY: 'playlist_delay', + TDEN: 'encoding_time', + TDOR: 'original_release_time', + TDRL: 'release_time', + TDTG: 'tagging_time', + TSOA: 'album_sort', + TSOP: 'artist_sort', + TSOT: 'title_sort', + WCOM: 'commercial_url', + WCOP: 'copyright_url', + WOAF: 'official_audio_url', + WOAR: 'official_artist_url', + WOAS: 'official_source_url', + WORS: 'official_internet_radio_station_url', + WPAY: 'payment_url', + WPUB: 'publisher_url', + COMM: 'comment', + USLT: 'lyrics', + SYLT: 'lyrics_synchronized', + USER: 'terms_of_use' +}; +var id3v2MapInv = ( function(){ + var map = { }, key, id, i; + for( id in id3v2Map ) { + key = id3v2Map[ id ]; + if ( map[ key ] ) { + if ( !Array.isArray( map[ key ] ) ) { + map[ key ] = [ map[ key ] ]; + } + map[ key ].push( id ); + } else { + map[ key ] = id; + } + } + for( key in map ) { + id = map[ key ]; + if ( Array.isArray( id ) ) { + id.sort(); + for( i = id.length - 1; i >= 0; i-- ) { + map[ [ key, i ].join( '.' ) ] = id[ i ]; + } + } + } + return map; +} )( ); +var id3v2LanguageFrames = { + // default - Unspecified lang, No content descriptor + USLT: function( v ){ return id3v2EnsureLanguage( v, '\0' ); }, + // default - Unspecified lang, $01 Absolute time 32 bit sized, + // Content type $00 is other, No content descriptor + SYLT: function( v ){ return id3v2EnsureLanguage( v, + [ '\u0001\0\0', /^[\u0001\u0002][\u0000-\u0008][^\u0000]*?\u0000/ ] ); }, + // default - Unspecified lang, No content descriptor + COMM: function( v ){ return id3v2EnsureLanguage( v, '\0' ); }, + // default - Unspecified lang + USER: function( v ){ return id3v2EnsureLanguage( v, '' ); } +} +function defaultFrame( id, content ) { + var tag = { flags: [ 0, 0 ] }; + if ( content ) { + tag.content = content; + if ( typeof content === 'string' ) { + tag.encoding = ( id && ( id.slice( 0, 1 ) === 'W' ) && ( id !== 'WXXX' ) ) ? -1: 3; + } + } + return tag; +} + +var iso639; +if ( "browserify" === 'browserify' ) { + iso639 = require( './iso_639_2.min' ); +} else { + iso639 = require( './iso_639_2' ); +} + +function id3v2EnsureLanguage( value, prefix, lang ) { + var langIsStr = ( typeof lang === 'string' ); + var cmp = value.slice( 0, ( langIsStr ? lang.length: 3 ) ).toLowerCase( ) + var hasOne = ( langIsStr && ( cmp === ( lang = lang.toLowerCase() ) ) ) || + ( !lang && cmp && !!iso639[ cmp ] ); + if ( prefix ) { + if ( !Array.isArray( prefix ) ) { + prefix = [ prefix ]; + } + if ( hasOne ) { + hasOne = false; + for ( var i = prefix.length - 1, it; i >= 0; i-- ) { + if ( !( it = prefix[ i ] ) ) { + continue; + } + if ( it.constructor === RegExp ) { + if ( hasOne = it.test( value.slice( 3 ) ) ) { + break; + } + } + else { + it = String( it ); + if ( hasOne = ( ( ( value.slice( 3, 3 + it.length ) ) === it ) || + ( ( it === '\0' ) && ( value.indexOf( it ) > 3 ) ) ) ) { + break; + } + } + } + } + } + return ( hasOne ) ? value: + ( ( langIsStr ? lang : '\0\0\0' ) + ( prefix && prefix[ 0 ] || '' ) + value ) +}; + +function I( v ){ return v; } +function transformTag( tag ) { + var id, inner, value; + if ( !tag._ ) { + tag._ = inner = { }; + } + for( var key in tag ) { + if ( !( id = id3v2MapInv[ key ] ) ) { + continue; + } + if ( Array.isArray( id ) ) { + id = id[ 0 ]; + } + value = tag[ key ]; + tag[ id ] = value = + // make sure language is there for the language + // tied frames, otherwise use the original value + ( id3v2LanguageFrames[ id ] || I )( value ); + ( inner[ id ] = ( inner[ id ] || [ ] ) ).push( + defaultFrame( undefined, value ) ); + } + return tag; +}; + +function decode( buf, enc ) { + var swap = false; + switch( ( enc && enc.toLowerCase() ) || 'utf8' ) { + case 'ascii': + case 'utf8': + return buf.toString( enc ); + + case 'utf16be': + swap = true; + + case 'utf16': + case 'ucs2': + if ( ( buf.length > 2 ) && ( buf[0] === 0xfe && buf[1] === 0xff ) ) { + swap = true; + } + if ( swap ) { + if ( buf.length & 0x01 ) { + throw new Error( 'Invaid buffer size' ); + } + buf.swap16(); + } + case 'utf16le': + if ( buf[0] === 0xff && buf[1] === 0xfe ) { + buf = buf.slice( 2 ); + } + return buf.toString( 'ucs2' ); + + case 'iso88591': + case 'binary': + return buf.toString( 'binary' ) + + default: + throw new Error( 'Unknown encoding ' + enc ); + + } +} + +function encode( str, enc ) { + var swap = true; + switch( ( enc && enc.toLowerCase() ) || 'utf8' ) { + case 'ascii': + case 'utf8': + return new Buffer( str, 'utf8' ); + + + case 'utf16': + case 'ucs2': + case 'utf16le': + if ( str.slice( 0, 1 ) !== '\ufeff' ) { + str = '\ufeff' + str; + } + swap = false; + + case 'utf16be': + str = new Buffer( str, 'ucs2' ); + if ( swap ) { + str.swap16(); + } + return str; + + case 'iso88591': + case 'binary': + return new Buffer( str, 'binary' ); + + default: + throw new Error( 'Unknown encoding ' + enc ); + } +} + +module.exports = { + trimNull: function(s) { + return s.replace(reNull, ''); + }, + + createView: function( buffer ) { + if (typeof(Buffer) !== 'undefined' && buffer instanceof Buffer) { + //convert nodejs buffers to ArrayBuffer + buffer = toArrayBuffer(buffer); + } + + if (!(buffer instanceof ArrayBuffer)) { + throw new Error('Expected instance of Buffer or ArrayBuffer'); + } + + return new DataView(buffer); + }, + + readBytes: function( view, offset, length, target ) { + if (offset + length < 0) { + return []; + } + + var bytes = []; + var max = Math.min(offset + length, view.byteLength); + for (var i = offset; i < max; i++) { + var value = view.getUint8(i); + bytes.push(value); + if (target) { + target.setUint8(i - offset, value); + } + } + + return bytes; + }, + + encode: function( str, enc ) { + return encode( str, enc || 'utf8' ); + }, + + readText: function( view, offset, length, encoding ) { + var buf = this.trimNull( ( ( buf = sliceView( view, offset, length ) ) && + decode( buf, encoding || 'utf8' ) ) || '' ); + return buf; + }, + + readIso8859: function( view, offset, length ) { + return this.readText( view, offset, length, 'iso88591' ); + }, + + readUtf16BE:function( view, offset, length ) { + return this.readText( view, offset, length, 'utf16be' ); + }, + + readUtf16:function( view, offset, length ) { + return this.readText( view, offset, length, 'utf16' ); + }, + + readAscii: function( view, offset, length ) { + return this.readText( view, offset, length, 'ascii' ); + }, + + readUtf8: function( view, offset, length ) { + return this.readText( view, offset, length, 'utf8' ); + }, + + id3v2: { + transformTag: transformTag, + frameMap: id3v2Map, + frameMapInv: id3v2MapInv, + ensureLanguage: id3v2EnsureLanguage, + languageFrames: id3v2LanguageFrames, + defaultFrame: defaultFrame + }, + + I: I + +}; +},{"./iso_639_2":undefined,"./iso_639_2.min":4}]},{},[1])(1) +}); \ No newline at end of file diff --git a/audio-metadata.min.js b/audio-metadata.min.js index c6c4c98..9629ba6 100644 --- a/audio-metadata.min.js +++ b/audio-metadata.min.js @@ -1 +1,2 @@ -!function(e){if("object"==typeof exports)module.exports=e();else if("function"==typeof define&&define.amd)define(e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.AudioMetadata=e()}}(function(){var define,module,exports;return function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o0;if(extendedHeader){offset+=getUint28(view,offset)}function readFrame(offset){try{var id=utils.readAscii(view,offset,4);var size=getUint28(view,offset+4);offset+=10;if(id[0]!=="T"){return{id:id,size:size+10}}var encoding=view.getUint8(offset),data="";if(encoding<=3){offset++;if(encoding===3){data=utils.readUtf8(view,offset,size-1)}else{data=utils.readAscii(view,offset,size-1)}}else{data=utils.readAscii(view,offset,size)}data=utils.trimNull(data);return{id:id,size:size+10,content:data}}catch(e){return null}}var idMap={TALB:"album",TCOM:"composer",TIT1:"title",TIT2:"title",TPE1:"artist",TRCK:"track",TSSE:"encoder",TDRC:"year",TCON:"genre"};var endOfTags=offset+size,frames={};while(offsetu;u++)i(n[u]);return i}({1:[function(r,e){e.exports={ogg:r("./src/ogg"),id3v1:r("./src/id3v1"),id3v2:r("./src/id3v2")}},{"./src/id3v1":2,"./src/id3v2":3,"./src/ogg":5}],2:[function(r,e){"use strict";function t(r){var e=n.readBytes(r,r.byteLength-128,3);return 84===e[0]&&65===e[1]&&71===e[2]}var n=r("./utils");e.exports=function(r,e){var i=n.createView(r);if(!t(i))return null;try{var a=i.byteLength-128+3,u=n.readAscii.bind(n),o=u(i,a,30),s=u(i,a+30,30),f=u(i,a+60,30),l=u(i,a+90,4);a+=94;var c=u(i,a,28),d=null;a+=28,0===i.getUint8(a)?d=i.getUint8(a+1):c+=u(i,a,2),a+=2;var g=i.getUint8(a),h={title:o,artist:s,album:f,year:l,comment:c,track:d,genre:g};return e&&e.toId3v2&&(h=n.id3v2.transformTag(h)),h}catch(m){return null}}},{"./utils":6}],3:[function(r,e){"use strict";function t(r,e){var t=f.readBytes(r,e,3);return 73===t[0]&&68===t[1]&&51===t[2]}function n(r,e){var t=f.readBytes(r,e,4),n=268435455;return(t[0]&n)<<21|(t[1]&n)<<14|(t[2]&n)<<7|t[3]&n}function i(r){for(var e,t=127;2147483647^t;)e=r&~t,e<<=1,e|=r&t,t=(t+1<<8)-1,r=e;return e}function a(r){function e(r){try{var e=f.readAscii(i,r,4),t=n(i,r+4);r+=8;var a=[i.getUint8(r++),i.getUint8(r++)];if("T"!==e[0]&&"W"!==e[0]&&!f.id3v2.languageFrames[e])return g.id&&l.test(g.id)?{id:e,content:Buffer.from(i.buffer.slice(r,r+t)),size:t+10,flags:a}:null;var u="W"===e[0]&&"WXXX"!==e?-1:i.getUint8(r++),o="";return o=u>=0&&3>=u?f.readText(i,r,t-1,d[u]):f.readAscii(i,r,t),o=f.trimNull(o),{id:e,size:t+10,flags:a,content:o,encoding:u}}catch(s){return null}}var i=f.createView(r);if(!t(i,0))return null;var a=3;a+=2;var u=i.getUint8(a);a++;var o=n(i,a);a+=4;var s=(128&u)>0;s&&(a+=n(i,a));for(var g,h=a+o,m={_:{}};h>a&&(g=e(a));){a+=g.size;var p=c[g.id]||g.id;if("TXXX"!==g.id&&"WXXX"!==g.id||"string"!=typeof g.content){m[g.id]=g.content;var v;g.id!==p&&(v=g.id.slice(-1))>="1"&&!isNaN(v=parseInt(v))&&v?(Array.isArray(m[p])||(m[p]=[]),m[p][--v]=g.content):m[p]=g.content}else{var b=g.content.indexOf("\0");b>0&&(p=g.content.substring(0,b)+("WXXX"===g.id?"_url":""))&&(m[p]=g.content.substring(b+1))}g.content=(f.id3v2.languageFrames[g.id]||f.I)(g.content),(m._[g.id]=m._[g.id]||[]).push(g),delete g.id,delete g.size}for(var p in m)if(Array.isArray(g=m[p])){for(o=0,a=g.length-1;a>=0&&("string"!=typeof g[a]||!o++);a--);1>=o&&(m[p]=g.join(""))}return m}function u(r,e,t){if(!(r&&(r+="")&&e&&t&&Array.isArray(t)))return void 0;e=(f.id3v2.languageFrames[r]||f.I)(e);for(var n=t.length-1;n>=0;n--)if(t[n].content===e)return n;return-1}function o(r){return r.content}function s(r,e){var t=r._||{};r._=t;var n,a,s,c,m,p,v,b;for(s in{tag:1,frame:1})if(e&&(7&(v=e[s+"Align"])||1>v))throw Error("Invalid "+s+" align option. Should be positive multiple of 8.");var y,T,k;for(y=0;2>y;y++)for(T in r)if(k=l.test(T),!(0===y&&!k||1===y&&k||"_"===T))for(a=r[T],b=(m=Array.isArray(a))?a.length:1,s=0;b>s;s++){if(c=m?a[s]:a,(p=0===y?f.id3v2.frameMap[T]&&T:f.id3v2.frameMapInv[T])&&!Array.isArray(p)&&(p=[p]),!p||!(p=p[s]||h[p[0]]&&p[0])){if(!(p=(T.length>4&&"_url"===T.slice(-4)?T=T.slice(0,-4):"")||g.test(c)?(c=f.id3v2.ensureLanguage(c,T+"\0",""))&&"WXXX":"string"!=typeof c||l.test(T)?0===y&&4===T.length&&l.test(T)&&Buffer.isBuffer(c)?T:void 0:(c=f.id3v2.ensureLanguage(c,T+"\0",""))&&"TXXX"))throw Error("Unsupported frame "+T);if(!l.test(p))throw Error("Unexpected frame "+p)}(n=t[p])||(t[p]=n=[]),c=(f.id3v2.languageFrames[p]||f.I)(c),-1===(v=u(p,c,n))?n.push(c=f.id3v2.defaultFrame(p,c)):void 0!==v&&n.push(n[v])}for(T in t)if((n=t[T])&&Array.isArray(n)&&n.length&&((c=h[T])||f.id3v2.frameMap[T]&&(t[T]=n=n.length?[n[n.length-1]]:[])&&(c=o))){for(p={},m=[],s=n.length-1;s>=0;s--)p[v=c(n[s])]||(m.push(n[s]),p[v]=1);t[T]=m}for(T in t)t[T].length||delete t[T];if(e&&e.inCache)return r;var w,_,A,B;b=0,B=(e&&e.frameAlign||8)/8;for(T in t){if(w=new Buffer(T+Array(7).join("\0"),"ascii"),10!==w.length)throw Error("Oops. Invalid frame header length");for(n=t[T],s=n.length-1;s>=0;s--)c=n[s],A=-1,c.buffer=Buffer.isBuffer(c.content)?c.content:f.encode(c.content+"\0",d[A=c.encoding||-1]),w[8]=c.flags[0],w[9]=c.flags[1],A=new Buffer(A>-1?[255&A]:[]),v=w.length+A.length+c.buffer.length,_=new Buffer.alloc(Math.ceil(v/B)*B-v),v=A.length+c.buffer.length+_.length,w.writeInt32BE(i(v),4),c.buffer=Buffer.concat([w,A,c.buffer,_],w.length+v),b+=c.buffer.length}if(e&&e.inCacheBuffers)return r;B=(e&&e.tagAlign||8)/8,w=new Buffer([73,68,51,4,0,0,0,0,0,0]),_=new Buffer.alloc(Math.ceil((s=w.length+b)/B)*B-s),w.writeInt32BE(i(b+_.length),6),m=[w];for(T in t)for(n=t[T],s=n.length-1;s>=0;s--)m.push(n[s].buffer);return m.push(_),r=Buffer.concat(m,w.length+b+_.length)}var f=r("./utils"),l=/^[A-Z1-4]{4}$/,c=f.id3v2.frameMap,d=["iso88591","utf16","utf16be","utf8"],g=/^http(?:s)?:/i,h=function(){function r(r,t,n){var i=(n=e(n)).slice(0,Math.min(r,3));return i=i+"\0"+(n=n.slice(r)).slice(0,t>(n=(t?n:"\0").indexOf("\0"))+1?void 0:n+(n?1:0))}var e=o,t=r.bind(null,0,1),n=r.bind(null,3,1);return{TLAN:e,TXXX:t,WCOM:e,WOAR:e,WXXX:t,USLT:n,SYLT:r.bind(null,5,1),COMM:n,WCOP:r.bind(null,3,0)}}();a.serialize=s,e.exports=a},{"./utils":6}],4:[function(r,e){e.exports={"\0\0\0":1,aar:1,abk:1,ace:1,ach:1,ada:1,ady:1,afa:1,afh:1,afr:1,ain:1,aka:1,akk:1,alb:1,sqi:1,ale:1,alg:1,alt:1,amh:1,ang:1,anp:1,apa:1,ara:1,arc:1,arg:1,arm:1,hye:1,arn:1,arp:1,art:1,arw:1,asm:1,ast:1,ath:1,aus:1,ava:1,ave:1,awa:1,aym:1,aze:1,bad:1,bai:1,bak:1,bal:1,bam:1,ban:1,baq:1,eus:1,bas:1,bat:1,bej:1,bel:1,bem:1,ben:1,ber:1,bho:1,bih:1,bik:1,bin:1,bis:1,bla:1,bnt:1,bos:1,bra:1,bre:1,btk:1,bua:1,bug:1,bul:1,bur:1,mya:1,byn:1,cad:1,cai:1,car:1,cat:1,cau:1,ceb:1,cel:1,cha:1,chb:1,che:1,chg:1,chi:1,zho:1,chk:1,chm:1,chn:1,cho:1,chp:1,chr:1,chu:1,chv:1,chy:1,cmc:1,cop:1,cor:1,cos:1,cpe:1,cpf:1,cpp:1,cre:1,crh:1,crp:1,csb:1,cus:1,cze:1,ces:1,dak:1,dan:1,dar:1,day:1,del:1,den:1,dgr:1,din:1,div:1,doi:1,dra:1,dsb:1,dua:1,dum:1,dut:1,nld:1,dyu:1,dzo:1,efi:1,egy:1,eka:1,elx:1,eng:1,enm:1,epo:1,est:1,ewe:1,ewo:1,fan:1,fao:1,fat:1,fij:1,fil:1,fin:1,fiu:1,fon:1,fre:1,fra:1,frm:1,fro:1,frr:1,frs:1,fry:1,ful:1,fur:1,gaa:1,gay:1,gba:1,gem:1,geo:1,kat:1,ger:1,deu:1,gez:1,gil:1,gla:1,gle:1,glg:1,glv:1,gmh:1,goh:1,gon:1,gor:1,got:1,grb:1,grc:1,gre:1,ell:1,grn:1,gsw:1,guj:1,gwi:1,hai:1,hat:1,hau:1,haw:1,heb:1,her:1,hil:1,him:1,hin:1,hit:1,hmn:1,hmo:1,hrv:1,hsb:1,hun:1,hup:1,iba:1,ibo:1,ice:1,isl:1,ido:1,iii:1,ijo:1,iku:1,ile:1,ilo:1,ina:1,inc:1,ind:1,ine:1,inh:1,ipk:1,ira:1,iro:1,ita:1,jav:1,jbo:1,jpn:1,jpr:1,jrb:1,kaa:1,kab:1,kac:1,kal:1,kam:1,kan:1,kar:1,kas:1,kau:1,kaw:1,kaz:1,kbd:1,kha:1,khi:1,khm:1,kho:1,kik:1,kin:1,kir:1,kmb:1,kok:1,kom:1,kon:1,kor:1,kos:1,kpe:1,krc:1,krl:1,kro:1,kru:1,kua:1,kum:1,kur:1,kut:1,lad:1,lah:1,lam:1,lao:1,lat:1,lav:1,lez:1,lim:1,lin:1,lit:1,lol:1,loz:1,ltz:1,lua:1,lub:1,lug:1,lui:1,lun:1,luo:1,lus:1,mac:1,mkd:1,mad:1,mag:1,mah:1,mai:1,mak:1,mal:1,man:1,mao:1,mri:1,map:1,mar:1,mas:1,may:1,msa:1,mdf:1,mdr:1,men:1,mga:1,mic:1,min:1,mis:1,mkh:1,mlg:1,mlt:1,mnc:1,mni:1,mno:1,moh:1,mon:1,mos:1,mul:1,mun:1,mus:1,mwl:1,mwr:1,myn:1,myv:1,nah:1,nai:1,nap:1,nau:1,nav:1,nbl:1,nde:1,ndo:1,nds:1,nep:1,"new":1,nia:1,nic:1,niu:1,nno:1,nob:1,nog:1,non:1,nor:1,nqo:1,nso:1,nub:1,nwc:1,nya:1,nym:1,nyn:1,nyo:1,nzi:1,oci:1,oji:1,ori:1,orm:1,osa:1,oss:1,ota:1,oto:1,paa:1,pag:1,pal:1,pam:1,pan:1,pap:1,pau:1,peo:1,per:1,fas:1,phi:1,phn:1,pli:1,pol:1,pon:1,por:1,pra:1,pro:1,pus:1,que:1,raj:1,rap:1,rar:1,roa:1,roh:1,rom:1,rum:1,ron:1,run:1,rup:1,rus:1,sad:1,sag:1,sah:1,sai:1,sal:1,sam:1,san:1,sas:1,sat:1,scn:1,sco:1,sel:1,sem:1,sga:1,sgn:1,shn:1,sid:1,sin:1,sio:1,sit:1,sla:1,slo:1,slk:1,slv:1,sma:1,sme:1,smi:1,smj:1,smn:1,smo:1,sms:1,sna:1,snd:1,snk:1,sog:1,som:1,son:1,sot:1,spa:1,srd:1,srn:1,srp:1,srr:1,ssa:1,ssw:1,suk:1,sun:1,sus:1,sux:1,swa:1,swe:1,syc:1,syr:1,tah:1,tai:1,tam:1,tat:1,tel:1,tem:1,ter:1,tet:1,tgk:1,tgl:1,tha:1,tib:1,bod:1,tig:1,tir:1,tiv:1,tkl:1,tlh:1,tli:1,tmh:1,tog:1,ton:1,tpi:1,tsi:1,tsn:1,tso:1,tuk:1,tum:1,tup:1,tur:1,tut:1,tvl:1,twi:1,tyv:1,udm:1,uga:1,uig:1,ukr:1,umb:1,und:1,urd:1,uzb:1,vai:1,ven:1,vie:1,vol:1,vot:1,wak:1,wal:1,war:1,was:1,wel:1,cym:1,wen:1,wln:1,wol:1,xal:1,xho:1,yao:1,yap:1,yid:1,yor:1,ypk:1,zap:1,zbl:1,zen:1,zgh:1,zha:1,znd:1,zul:1,zun:1,zxx:1,zza:1}},{}],5:[function(r,e){"use strict";function t(r,e,t){if(e+27>r.byteLength)return null;var n=r.getUint8(e+26),a=i.readBytes(r,e+27,n),u=27+n;if(!a.length)return null;var o=u+a.reduce(function(r,e){return r+e}),s=u+1+"vorbis".length,f=null;return t&&(f=i.createView(new ArrayBuffer(o-s)),i.readBytes(r,e+s,o-s,f)),{pageSize:o,packet:f}}function n(r){try{for(var e=r.getUint32(0,!0),t=r.getUint32(4+e,!0),n={},a=8+e,u={tracknumber:"track"},o=0;t>o;o++){var s=r.getUint32(a,!0),f=i.readUtf8(r,a+4,s),l=f.indexOf("="),c=f.substring(0,l).toLowerCase();n[u[c]||c]=n[c]=i.trimNull(f.substring(l+1)),a+=4+s}return n}catch(d){return null}}var i=r("./utils");e.exports=function(r,e){var a=i.createView(r),u=t(a,0);if(!u)return null;var o=t(a,u.pageSize,!0);if(!o)return null;var s=n(o.packet);return e&&e.toId3v2&&(s=i.id3v2.transformTag(s)),s}},{"./utils":6}],6:[function(r,e){"use strict";function t(r){for(var e=new ArrayBuffer(r.length),t=new Uint8Array(e),n=0;r.length>n;++n)t[n]=r[n];return e}function n(r,e,t){if(1>t||0>e||e+t>r.byteLength)return void 0;var n=r.buffer.slice(e,e+t);return(255===n[0]&&254===n[1]&&254===n[2]&&255===n[3]||255===n[1]&&254===n[0]&&254===n[3]&&255===n[2])&&(n=n.slice(2)),Buffer.from(n)}function i(r,e){var t={flags:[0,0]};return e&&(t.content=e,"string"==typeof e&&(t.encoding=r&&"W"===r.slice(0,1)&&"WXXX"!==r?-1:3)),t}function a(r,e,t){var n="string"==typeof t,i=r.slice(0,n?t.length:3).toLowerCase(),a=n&&i===(t=t.toLowerCase())||!t&&i&&!!l[i];if(e&&(Array.isArray(e)||(e=[e]),a)){a=!1;for(var u,o=e.length-1;o>=0;o--)if(u=e[o])if(u.constructor===RegExp){if(a=u.test(r.slice(3)))break}else if(u+="",a=r.slice(3,3+u.length)===u||"\0"===u&&r.indexOf(u)>3)break}return a?r:(n?t:"\0\0\0")+(e&&e[0]||"")+r}function u(r){return r}function o(r){var e,t,n;r._||(r._=t={});for(var a in r)(e=g[a])&&(Array.isArray(e)&&(e=e[0]),n=r[a],r[e]=n=(h[e]||u)(n),(t[e]=t[e]||[]).push(i(void 0,n)));return r}function s(r,e){var t=!1;switch(e&&e.toLowerCase()||"utf8"){case"ascii":case"utf8":return r.toString(e);case"utf16be":t=!0;case"utf16":case"ucs2":if(r.length>2&&254===r[0]&&255===r[1]&&(t=!0),t){if(1&r.length)throw Error("Invaid buffer size");r.swap16()}case"utf16le":return 255===r[0]&&254===r[1]&&(r=r.slice(2)),r.toString("ucs2");case"iso88591":case"binary":return r.toString("binary");default:throw Error("Unknown encoding "+e)}}function f(r,e){var t=!0;switch(e&&e.toLowerCase()||"utf8"){case"ascii":case"utf8":return new Buffer(r,"utf8");case"utf16":case"ucs2":case"utf16le":""!==r.slice(0,1)&&(r=""+r),t=!1;case"utf16be":return r=new Buffer(r,"ucs2"),t&&r.swap16(),r;case"iso88591":case"binary":return new Buffer(r,"binary");default:throw Error("Unknown encoding "+e)}}var l,c=/[\u0000 ]+$/,d={TALB:"album",TOAL:"original_album",TPOS:"part",TCOM:"composer",TIT1:"title",TIT2:"title",TIT3:"title",TSST:"subtitle",TPE1:"artist",TPE2:"artist",TPE3:"artist",TPE4:"artist",TOPE:"original_artist",TEXT:"lyricist",TOLY:"original_lyricist",TRCK:"track",TSSE:"encoder",TDRC:"year",TCON:"genre",TSRC:"isrc",TMCL:"musician_credit",TIPL:"involved_credit",TENC:"encoded_by",TBPM:"beats_per_minute",TLEN:"length",TKEY:"initial_key",TLAN:"language",TFLT:"file_type",TMED:"media_type",TMOO:"mood",TCOP:"copyright_message",TPRO:"produced_notice",TPUB:"publisher",TOWN:"licensee",TRSN:"internet_radio_station",TRSO:"internet_radio_station_owner",TOFN:"original_filename",TDLY:"playlist_delay",TDEN:"encoding_time",TDOR:"original_release_time",TDRL:"release_time",TDTG:"tagging_time",TSOA:"album_sort",TSOP:"artist_sort",TSOT:"title_sort",WCOM:"commercial_url",WCOP:"copyright_url",WOAF:"official_audio_url",WOAR:"official_artist_url",WOAS:"official_source_url",WORS:"official_internet_radio_station_url",WPAY:"payment_url",WPUB:"publisher_url",COMM:"comment",USLT:"lyrics",SYLT:"lyrics_synchronized",USER:"terms_of_use"},g=function(){var r,e,t,n={};for(e in d)r=d[e],n[r]?(Array.isArray(n[r])||(n[r]=[n[r]]),n[r].push(e)):n[r]=e;for(r in n)if(e=n[r],Array.isArray(e))for(e.sort(),t=e.length-1;t>=0;t--)n[[r,t].join(".")]=e[t];return n}(),h={USLT:function(r){return a(r,"\0")},SYLT:function(r){return a(r,["\0\0",/^[\u0001\u0002][\u0000-\u0008][^\u0000]*?\u0000/])},COMM:function(r){return a(r,"\0")},USER:function(r){return a(r,"")}};l=r("./iso_639_2.min"),e.exports={trimNull:function(r){return r.replace(c,"")},createView:function(r){if("undefined"!=typeof Buffer&&r instanceof Buffer&&(r=t(r)),!(r instanceof ArrayBuffer))throw Error("Expected instance of Buffer or ArrayBuffer");return new DataView(r)},readBytes:function(r,e,t,n){if(0>e+t)return[];for(var i=[],a=Math.min(e+t,r.byteLength),u=e;a>u;u++){var o=r.getUint8(u);i.push(o),n&&n.setUint8(u-e,o)}return i},encode:function(r,e){return f(r,e||"utf8")},readText:function(r,e,t,i){var a=this.trimNull((a=n(r,e,t))&&s(a,i||"utf8")||"");return a},readIso8859:function(r,e,t){return this.readText(r,e,t,"iso88591")},readUtf16BE:function(r,e,t){return this.readText(r,e,t,"utf16be")},readUtf16:function(r,e,t){return this.readText(r,e,t,"utf16")},readAscii:function(r,e,t){return this.readText(r,e,t,"ascii")},readUtf8:function(r,e,t){return this.readText(r,e,t,"utf8")},id3v2:{transformTag:o,frameMap:d,frameMapInv:g,ensureLanguage:a,languageFrames:h,defaultFrame:i},I:u}},{"./iso_639_2":void 0,"./iso_639_2.min":4}]},{},[1])(1)}); +//@ sourceMappingURL=audio-metadata.min.js.map \ No newline at end of file diff --git a/audio-metadata.min.js.map b/audio-metadata.min.js.map new file mode 100644 index 0000000..1a187c8 --- /dev/null +++ b/audio-metadata.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["audio-metadata.js"],"names":["f","exports","module","define","amd","g","window","global","self","this","AudioMetadata","e","t","n","r","s","o","u","a","require","i","Error","code","l","call","length",1,"ogg","id3v1","id3v2","./src/id3v1","./src/id3v2","./src/ogg",2,"checkMagicId3v1","view","id3Magic","utils","readBytes","byteLength","buffer","opts","createView","offset","readAscii","bind","title","artist","album","year","comment","track","getUint8","genre","tag","toId3v2","transformTag","./utils",3,"checkMagicId3","getUint28","sizeBytes","mask","synchsafe","int","out","parseTag","readFrame","id","size","frameFlags","languageFrames","frame","reFrameId","test","content","Buffer","from","slice","flags","encoding","data","readText","encodingMap","trimNull","extendedHeader","endOfTags","frames","_","idMap","idx","isNaN","parseInt","Array","isArray","nullByte","indexOf","substring","I","push","join","isTagContentInArray","tagName","arr","String","undefined","getContent","serializeTag","cache","it","array","mappedK","j","pass","k","kU","frameMap","frameMapInv","multiFrames","reUrlLike","ensureLanguage","isBuffer","defaultFrame","inCache","header","padding","enc","alignment","frameAlign","encode","alloc","Math","ceil","writeInt32BE","concat","inCacheBuffers","tagAlign","_prefix","trim","min","rem","_content","_desc","_lang","TLAN","TXXX","WCOM","WOAR","WXXX","USLT","SYLT","COMM","WCOP","serialize",4,"\u0000\u0000\u0000","aar","abk","ace","ach","ada","ady","afa","afh","afr","ain","aka","akk","alb","sqi","ale","alg","alt","amh","ang","anp","apa","ara","arc","arg","arm","hye","arn","arp","art","arw","asm","ast","ath","aus","ava","ave","awa","aym","aze","bad","bai","bak","bal","bam","ban","baq","eus","bas","bat","bej","bel","bem","ben","ber","bho","bih","bik","bin","bis","bla","bnt","bos","bra","bre","btk","bua","bug","bul","bur","mya","byn","cad","cai","car","cat","cau","ceb","cel","cha","chb","che","chg","chi","zho","chk","chm","chn","cho","chp","chr","chu","chv","chy","cmc","cop","cor","cos","cpe","cpf","cpp","cre","crh","crp","csb","cus","cze","ces","dak","dan","dar","day","del","den","dgr","din","div","doi","dra","dsb","dua","dum","dut","nld","dyu","dzo","efi","egy","eka","elx","eng","enm","epo","est","ewe","ewo","fan","fao","fat","fij","fil","fin","fiu","fon","fre","fra","frm","fro","frr","frs","fry","ful","fur","gaa","gay","gba","gem","geo","kat","ger","deu","gez","gil","gla","gle","glg","glv","gmh","goh","gon","gor","got","grb","grc","gre","ell","grn","gsw","guj","gwi","hai","hat","hau","haw","heb","her","hil","him","hin","hit","hmn","hmo","hrv","hsb","hun","hup","iba","ibo","ice","isl","ido","iii","ijo","iku","ile","ilo","ina","inc","ind","ine","inh","ipk","ira","iro","ita","jav","jbo","jpn","jpr","jrb","kaa","kab","kac","kal","kam","kan","kar","kas","kau","kaw","kaz","kbd","kha","khi","khm","kho","kik","kin","kir","kmb","kok","kom","kon","kor","kos","kpe","krc","krl","kro","kru","kua","kum","kur","kut","lad","lah","lam","lao","lat","lav","lez","lim","lin","lit","lol","loz","ltz","lua","lub","lug","lui","lun","luo","lus","mac","mkd","mad","mag","mah","mai","mak","mal","man","mao","mri","map","mar","mas","may","msa","mdf","mdr","men","mga","mic","mis","mkh","mlg","mlt","mnc","mni","mno","moh","mon","mos","mul","mun","mus","mwl","mwr","myn","myv","nah","nai","nap","nau","nav","nbl","nde","ndo","nds","nep","new","nia","nic","niu","nno","nob","nog","non","nor","nqo","nso","nub","nwc","nya","nym","nyn","nyo","nzi","oci","oji","ori","orm","osa","oss","ota","oto","paa","pag","pal","pam","pan","pap","pau","peo","per","fas","phi","phn","pli","pol","pon","por","pra","pro","pus","que","raj","rap","rar","roa","roh","rom","rum","ron","run","rup","rus","sad","sag","sah","sai","sal","sam","san","sas","sat","scn","sco","sel","sem","sga","sgn","shn","sid","sin","sio","sit","sla","slo","slk","slv","sma","sme","smi","smj","smn","smo","sms","sna","snd","snk","sog","som","son","sot","spa","srd","srn","srp","srr","ssa","ssw","suk","sun","sus","sux","swa","swe","syc","syr","tah","tai","tam","tat","tel","tem","ter","tet","tgk","tgl","tha","tib","bod","tig","tir","tiv","tkl","tlh","tli","tmh","tog","ton","tpi","tsi","tsn","tso","tuk","tum","tup","tur","tut","tvl","twi","tyv","udm","uga","uig","ukr","umb","und","urd","uzb","vai","ven","vie","vol","vot","wak","wal","war","was","wel","cym","wen","wln","wol","xal","xho","yao","yap","yid","yor","ypk","zap","zbl","zen","zgh","zha","znd","zul","zun","zxx","zza",5,"parsePage","withPacket","numPageSegments","segmentTable","headerSize","pageSize","reduce","cur","next","packetView","ArrayBuffer","packet","parseComments","vendorLength","getUint32","commentListLength","comments","tracknumber","commentLength","readUtf8","equals","key","toLowerCase","commentHeader",6,"toArrayBuffer","arrayBuffer","Uint8Array","sliceView","id3v2EnsureLanguage","value","prefix","lang","langIsStr","cmp","hasOne","iso639","constructor","RegExp","v","inner","id3v2MapInv","id3v2LanguageFrames","decode","buf","swap","toString","swap16","str","reNull","id3v2Map","TALB","TOAL","TPOS","TCOM","TIT1","TIT2","TIT3","TSST","TPE1","TPE2","TPE3","TPE4","TOPE","TEXT","TOLY","TRCK","TSSE","TDRC","TCON","TSRC","TMCL","TIPL","TENC","TBPM","TLEN","TKEY","TFLT","TMED","TMOO","TCOP","TPRO","TPUB","TOWN","TRSN","TRSO","TOFN","TDLY","TDEN","TDOR","TDRL","TDTG","TSOA","TSOP","TSOT","WOAF","WOAS","WORS","WPAY","WPUB","USER","sort","replace","DataView","target","bytes","max","setUint8","readIso8859","readUtf16BE","readUtf16","./iso_639_2","./iso_639_2.min"],"mappings":"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ACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLtT,IAAK,EACLuT,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,MAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EACLC,IAAK,EA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"} \ No newline at end of file diff --git a/package.json b/package.json index 7f90fde..055a610 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,21 @@ { "name": "audio-metadata", - "version": "0.3.0", + "version": "0.3.1", "description": "Extract metadata from audio files", - "keywords": [ "id3", "metadata", "mp3", "ogg", "wav", "audio" ], - + "keywords": [ + "id3", + "metadata", + "mp3", + "ogg", + "wav", + "audio" + ], "author": "Tommy Montgomery (http://tmont.com/)", "repository": { "type": "git", "url": "https://github.com/tmont/audio-metadata.git" }, - "bin": "bin/audio-metadata.js", - "files": [ "index.js", "audio-metadata.min.js", @@ -19,21 +23,20 @@ "bin", "README.md" ], - "license": "WTFPL", - + "dependencies": {}, "devDependencies": { - "mocha": "1.16.2", - "should": "2.1.1", - "browserify": "3.19.1", - "uglify-js": "2.4.8", - "serve": "1.3.0" + "browserify": "^14.0.0", + "envify": "^4.0.0", + "mocha": "^1.16.2", + "serve": "^1.3.0", + "should": "^2.1.1", + "uglify-js2": "^2.1.11" }, - "scripts": { "test": "./node_modules/.bin/mocha -R spec tests", "start": "./node_modules/.bin/serve -p 24578 .", - "build": "./node_modules/.bin/browserify -s AudioMetadata -e index.js --bare > audio-metadata.js", - "minify": "npm run build && ./node_modules/.bin/uglifyjs audio-metadata.js > audio-metadata.min.js && rm audio-metadata.js" + "build": "./node_modules/.bin/browserify -t [ envify --NODE_ENV browserify ] -s AudioMetadata -e index.js -u src/iso_639_2.js --bare > audio-metadata.js", + "minify": "npm run build && ./node_modules/.bin/uglifyjs2 -m -c --source-map audio-metadata.min.js.map audio-metadata.js > audio-metadata.min.js" } -} \ No newline at end of file +} diff --git a/src/id3v1.js b/src/id3v1.js index 864e203..73e91fa 100644 --- a/src/id3v1.js +++ b/src/id3v1.js @@ -1,3 +1,5 @@ +'use strict' + var utils = require('./utils'); function checkMagicId3v1(view) { @@ -6,20 +8,16 @@ function checkMagicId3v1(view) { return id3Magic[0] === 84 && id3Magic[1] === 65 && id3Magic[2] === 71; } -module.exports = function(buffer) { +module.exports = function(buffer, opts) { //read last 128 bytes var view = utils.createView(buffer); if (!checkMagicId3v1(view)) { return null; } - function trim(value) { - return value.replace(/[\s\u0000]+$/, ''); - } - try { var offset = view.byteLength - 128 + 3, - readAscii = utils.readAscii; + readAscii = utils.readAscii.bind( utils ); var title = readAscii(view, offset, 30), artist = readAscii(view, offset + 30, 30), album = readAscii(view, offset + 60, 30), @@ -39,15 +37,19 @@ module.exports = function(buffer) { offset += 2; var genre = view.getUint8(offset); - return { - title: trim(title), - artist: trim(artist), - album: trim(album), - year: trim(year), - comment: trim(comment), + var tag = { + title: title, + artist: artist, + album: album, + year: year, + comment: comment, track: track, genre: genre - }; + } + if ( opts && opts.toId3v2 ) { + tag = utils.id3v2.transformTag( tag ); + } + return tag; } catch (e) { return null; } diff --git a/src/id3v2.js b/src/id3v2.js index 2961a70..c9311df 100644 --- a/src/id3v2.js +++ b/src/id3v2.js @@ -1,4 +1,6 @@ -var utils = require('./utils'); +'use strict' + +var utils = require( './utils' ); function checkMagicId3(view, offset) { var id3Magic = utils.readBytes(view, offset, 3); @@ -8,17 +10,37 @@ function checkMagicId3(view, offset) { function getUint28(view, offset) { var sizeBytes = utils.readBytes(view, offset, 4); - var mask = 0xfffffff; + var mask = 0x0fffffff; return ((sizeBytes[0] & mask) << 21) | ((sizeBytes[1] & mask) << 14) | ((sizeBytes[2] & mask) << 7) | (sizeBytes[3] & mask); } +function synchsafe( int ) { + var out, mask = 0x7F; + while (mask ^ 0x7FFFFFFF) { + out = int & ~mask; + out <<= 1; + out |= int & mask; + mask = ((mask + 1) << 8) - 1; + int = out; + } + return out; +} + //http://id3.org/id3v2.3.0 //http://id3.org/id3v2.4.0-structure //http://id3.org/id3v2.4.0-frames -module.exports = function(buffer) { +var reFrameId = /^[A-Z1-4]{4}$/; +var idMap = utils.id3v2.frameMap; +var encodingMap = [ + 'iso88591', // ISO-8859-1 + 'utf16', // BOM + 'utf16be', // NOBOM, UTF-16BE + 'utf8' //UTF8 - null terminated +]; +function parseTag( buffer ) { var view = utils.createView(buffer); if (!checkMagicId3(view, 0)) { return null; @@ -38,35 +60,29 @@ module.exports = function(buffer) { offset += getUint28(view, offset); } - function readFrame(offset) { + function readFrame( offset ) { try { var id = utils.readAscii(view, offset, 4); var size = getUint28(view, offset + 4); - offset += 10; //+2 more for flags we don't care about + offset += 8; + var frameFlags = [ view.getUint8(offset++), view.getUint8(offset++) ] - if (id[0] !== 'T') { - return { + if ( ( id[0] !== 'T' ) && ( id[0] !== 'W' ) && ( !utils.id3v2.languageFrames[ id ] ) ) { + return !( frame.id && reFrameId.test( frame.id ) ) ? null: { id: id, - size: size + 10 + content: Buffer.from( view.buffer.slice( offset, offset + size ) ), + size: size + 10, + flags: frameFlags }; } - var encoding = view.getUint8(offset), - data = ''; - - if (encoding <= 3) { - offset++; - if (encoding === 3) { - //UTF8 - null terminated - data = utils.readUtf8(view, offset, size - 1); - } else { - //ISO-8859-1, UTF-16, UTF-16BE - //UTF-16 and UTF-16BE are $FF $00 terminated - //ISO is null terminated - - //screw these encodings, read it as ascii - data = utils.readAscii(view, offset, size - 1); - } + var encoding = ( ( id[0] === 'W' ) && ( id !== 'WXXX' ) ) ? + -1 : view.getUint8( offset ++ ); + var data = ''; + + if ((encoding >= 0) && (encoding <= 3)) { + // decode as per the encoding map + data = utils.readText(view, offset, size - 1, encodingMap[ encoding ] ); } else { //no encoding info, read it as ascii data = utils.readAscii(view, offset, size); @@ -79,46 +95,297 @@ module.exports = function(buffer) { return { id: id, size: size + 10, - content: data + flags: frameFlags, + content: data, + encoding: encoding }; } catch (e) { return null; } } - var idMap = { - TALB: 'album', - TCOM: 'composer', - TIT1: 'title', - TIT2: 'title', - TPE1: 'artist', - TRCK: 'track', - TSSE: 'encoder', - TDRC: 'year', - TCON: 'genre' - }; - var endOfTags = offset + size, - frames = {}; + frames = { '_': { } }, + frame; + while (offset < endOfTags) { - var frame = readFrame(offset); + frame = readFrame(offset); if (!frame) { break; } offset += frame.size; - if (!frame.content) { - continue; - } + var id = idMap[frame.id] || frame.id; - if (id === 'TXXX') { + if ( ( ( frame.id === 'TXXX' ) || ( frame.id === 'WXXX' ) ) && + ( typeof frame.content === 'string' ) ) { var nullByte = frame.content.indexOf('\u0000'); - id = frame.content.substring(0, nullByte); - frames[id] = frame.content.substring(nullByte + 1); + if ( ( nullByte > 0 ) && ( + id = frame.content.substring(0, nullByte) + + ( ( frame.id === 'WXXX' ) ? '_url': '' ) ) ) { + frames[ id ] = frame.content.substring( nullByte + 1 ); + } } else { - frames[id] = frames[frame.id] = frame.content; + frames[frame.id] = frame.content; + var idx; + // allows bundling TITX, TPEX + if ( ( frame.id !== id ) && + ( ( idx = frame.id.slice( -1 ) ) >= '1' ) && + !isNaN( ( idx = parseInt( idx ) ) ) && idx ) { + if ( !Array.isArray( frames[id] ) ) { + frames[ id ] = [ ]; + } + frames[ id ][ --idx ] = frame.content; + } else { + frames[ id ] = frame.content; + } + } + frame.content = ( utils.id3v2.languageFrames[ frame.id ] || utils.I )( frame.content ); + ( frames._[frame.id] = ( frames._[frame.id] || [ ] ) ).push( frame ); + delete frame.id; + delete frame.size; + } + + // normalizes bundles + for( var id in frames ) { + if ( Array.isArray( frame = frames[ id ] ) ) { + size = 0; + for( offset = frame.length - 1; offset >= 0; offset-- ) { + if ( typeof frame[ offset ] === 'string' ) { + if ( size++ ) { + break; + } + } + } + if ( size <= 1 ) { + frames[ id ] = frame.join( '' ); + } + } + } + return frames; +}; + +function isTagContentInArray( tagName, content, arr ) { + if ( !( tagName && ( tagName = String( tagName ) ) && + content && + arr && Array.isArray( arr ) ) ) { + return undefined; + } + content = ( utils.id3v2.languageFrames[ tagName ] || utils.I )( content ); + for ( var i = arr.length - 1; i >= 0; i-- ) { + if ( arr[ i ].content === content ) { + return i; + } + } + return -1; +} + +var reUrlLike = /^http(?:s)?:/i; +function getContent( frame ) { + return frame.content; }; +var multiFrames = ( function() { + var _content = getContent; + function _prefix( trim, min, frame ) { + var rem = ( frame = _content( frame ) ).slice( 0, Math.min( trim, 3 ) ); + rem = rem + '\0' + ( frame = frame.slice( trim ) ).slice( + 0, ( ( frame = ( min ? frame: '\0' ).indexOf( '\0' ) ) + 1 < min ) ? + undefined: ( frame + ( frame ? 1 : 0 ) ) ); + return rem; }; + var _desc = _prefix.bind( null, 0, 1 ); + var _lang = _prefix.bind( null, 3, 1 ); + return { + TLAN: _content, + TXXX: _desc, + WCOM: _content, + WOAR: _content, + WXXX: _desc, + USLT: _lang, + SYLT: _prefix.bind( null, 5, 1 ), + COMM: _lang, + WCOP: _prefix.bind( null, 3, 0 ) + }; +} )( ); +function serializeTag( frames, opts ) { + + var cache = frames._ || { }; + frames._ = cache; + var arr, content, i, it, array, mappedK, j, l; + + for( i in { tag: 1, frame: 1 } ) { + if ( opts && ( ( ( j = opts[ i + 'Align' ] ) & 0x7 ) || ( j < 1 ) ) ) { + throw new Error( 'Invalid ' + i + ' align option. Should be positive multiple of 8.' ); + } + } + // serialization takes place after preparation happening few passes: + + // NOTE: speacial care must be taken for comment/ COMMM + // 1. upper case mapped to _ items or new items stored in _ + + // 2. lower case mapped to upper case and in case of value discrepancies mapped into + // TXXX or WXXX frames + var pass, k, kU; + for( pass = 0; pass < 2; pass++ ) { + for( k in frames ) { + kU = reFrameId.test( k ); + if ( ( ( pass === 0 ) && !kU ) || + ( ( pass === 1 ) && kU ) || + ( k === '_' ) ) { + continue; + } + content = frames[ k ]; + l = ( ( array = Array.isArray( content ) ) ? content.length: 1 ); + for ( i = 0; i < l; i++ ) { + it = array ? content[ i ]: content; + if ( ( mappedK = ( ( pass === 0 ) ? + // makes sure we are delaing with valid frame id + utils.id3v2.frameMap[ k ] && k : + // ensures the descriptive name is knows and + // subject of translation to a valid frame id + utils.id3v2.frameMapInv[ k ] ) ) && + ( !Array.isArray( mappedK ) ) ) { + mappedK = [ mappedK ]; + } + // if delaing with something unrecognized, + // decide whether it should be listed: + // 1) standalone in case of a buffer content type + // 2) TXXX in case of string non url like content + // 3) WXXX in case of url like string + if ( !( mappedK && ( + mappedK = ( mappedK[ i ] || + ( multiFrames[ mappedK [ 0 ] ] && mappedK[ 0 ] ) ) ) ) ) { + if ( !( mappedK = ( + // look for _url hint + ( ( k.length > 4 && k.slice( -4 ) === '_url' ) ? + ( k = k.slice( 0, -4 ) ): '' ) || + // simple content url test + reUrlLike.test( it ) ) ? + // the content may need adjustment + // as per the WXXX spec + ( ( it = utils.id3v2.ensureLanguage( it, k + '\0', '' ) ) && + 'WXXX' ): ( + ( ( typeof it === 'string' ) && ( !reFrameId.test( k ) ) ) ? + // the content may need adjustment + // as per TXXX spec + ( ( it = utils.id3v2.ensureLanguage( it, k + '\0', '' ) ) && + 'TXXX' ): ( + ( ( pass === 0 ) && + ( k.length === 4 ) && + ( reFrameId.test( k ) ) && + ( Buffer.isBuffer( it ) ) ) ? + k: undefined ) ) ) ) { + throw new Error( 'Unsupported frame ' + k ); + } + if ( !reFrameId.test( mappedK ) ) { + throw new Error( 'Unexpected frame ' + mappedK ); + } + } + if ( !( arr = cache[ mappedK ] ) ) { + cache[ mappedK ] = arr = [ ]; + } + // format the language tags + it = ( utils.id3v2.languageFrames[ mappedK ] || utils.I )( it ); + if ( ( j = isTagContentInArray( mappedK, it, arr ) ) === -1 ) { + // add last + arr.push( it = utils.id3v2.defaultFrame( mappedK, it ) ); + } else if ( j !== undefined ) { + // make sure at the tail + // never mind the duplication, the next step + // handles compaction + arr.push( arr[ j ] ); + } + } + } + } + + // 3. compact the arrays from the cache + for ( k in cache ) { + if ( !( ( arr = cache[ k ] ) && Array.isArray( arr ) && arr.length ) ) { + continue; + } + if ( !( ( it = multiFrames[ k ] ) || + // for non multi ones, use the last entry + ( utils.id3v2.frameMap[ k ] && + ( cache[ k ] = arr = ( arr.length ? [ arr[ arr.length - 1 ] ]: [ ] ) ) && + ( it = getContent ) ) ) ) { + // do not play with unknown tags, just pass through + continue; + } + mappedK = { }; + array = [ ]; + for( i = arr.length - 1; i >= 0; i -- ) { + if ( mappedK[ j = it( arr[ i ] ) ] ) { + continue; + } + array.push( arr[ i ] ); + mappedK[ j ] = 1; + } + cache[ k ] = array; + } + // purge empty entires + for ( k in cache ) { + if ( !cache[ k ].length ) { + delete cache[ k ]; } } + // return the optimized cache, if thats all needed + if ( opts && opts.inCache ) { + return frames; + } + + // frames serialization into buffers. Once again in the cache. + var header, padding, enc, alignment; + l = 0; + alignment = ( ( opts && opts.frameAlign ) || 8 ) / 8; + for ( k in cache ) { + + header = new Buffer( k + ( new Array( 7 ) ).join( '\0' ), 'ascii' ); + if( !( header.length === 10 ) ) { + throw new Error( 'Oops. Invalid frame header length' ); + } + + for( arr = cache[ k ], i = arr.length - 1; i >= 0; i-- ) { + it = arr[ i ]; enc = -1; + it.buffer = ( Buffer.isBuffer( it.content ) ) ? + it.content: + utils.encode( it.content + '\0', encodingMap[ enc = ( it.encoding || -1 ) ] ); + header[ 8 ] = it.flags[ 0 ]; + header[ 9 ] = it.flags[ 1 ]; + enc = new Buffer( ( enc > -1 ) ? [ 0xff & enc ]: [] ); + j = ( header.length + enc.length + it.buffer.length ); + padding = new Buffer.alloc( Math.ceil( j / alignment ) * alignment - j ); + j = ( enc.length + it.buffer.length + padding.length ); + header.writeInt32BE( synchsafe( j ), 4 ); + it.buffer = Buffer.concat( + [ header, enc, it.buffer, padding ], + header.length + j ); + // keep track of total buffer length + l += it.buffer.length; + } + } + + if ( opts && opts.inCacheBuffers ) { + return frames; + } + + // FINALLY dumping everything into a tag frame. We ommit the extended header, + // though padding may be added to make the tag 8x bit aligned. + alignment = ( ( opts && opts.tagAlign ) || 8 ) / 8; + header = new Buffer( [ 0x49, 0x44, 0x33, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] ); + padding = new Buffer.alloc( Math.ceil( ( i = ( header.length + l ) ) / alignment ) * alignment - i ); + header.writeInt32BE( synchsafe( l + padding.length ), 6 ); + array = [ header ]; + for ( k in cache ) { + for( arr = cache[ k ], i = arr.length - 1; i >= 0; i-- ) { + array.push( arr[ i ].buffer ); + } + } + array.push( padding ); + frames = Buffer.concat( array, header.length + l + padding.length ); + return frames; -}; \ No newline at end of file +} + +parseTag.serialize = serializeTag; + +module.exports = parseTag; diff --git a/src/iso_639_2.js b/src/iso_639_2.js new file mode 100644 index 0000000..3c6922b --- /dev/null +++ b/src/iso_639_2.js @@ -0,0 +1,514 @@ +module.exports = { + '\0\0\0': [ 'Unspecified' ], + aar: [ 'Afar' ], + abk: [ 'Abkhazian' ], + ace: [ 'Achinese' ], + ach: [ 'Acoli' ], + ada: [ 'Adangme' ], + ady: [ 'Adygei', 'Adyghe' ], + afa: [ 'Afro-Asiatic Languages' ], + afh: [ 'Afrihili' ], + afr: [ 'Afrikaans' ], + ain: [ 'Ainu' ], + aka: [ 'Akan' ], + akk: [ 'Akkadian' ], + alb: [ 'Albanian' ], + sqi: [ 'Albanian' ], + ale: [ 'Aleut' ], + alg: [ 'Algonquian Languages' ], + alt: [ 'Southern Altai' ], + amh: [ 'Amharic' ], + ang: [ 'English Old (ca.450-1100)' ], + anp: [ 'Angika' ], + apa: [ 'Apache Languages' ], + ara: [ 'Arabic' ], + arc: + [ 'Imperial Aramaic (700-300 BCE)', + 'Official Aramaic (700-300 BCE)' ], + arg: [ 'Aragonese' ], + arm: [ 'Armenian' ], + hye: [ 'Armenian' ], + arn: [ 'Mapuche', 'Mapudungun' ], + arp: [ 'Arapaho' ], + art: [ 'Artificial Languages' ], + arw: [ 'Arawak' ], + asm: [ 'Assamese' ], + ast: [ 'Asturian', 'Asturleonese', 'Bable', 'Leonese' ], + ath: [ 'Athapascan Languages' ], + aus: [ 'Australian Languages' ], + ava: [ 'Avaric' ], + ave: [ 'Avestan' ], + awa: [ 'Awadhi' ], + aym: [ 'Aymara' ], + aze: [ 'Azerbaijani' ], + bad: [ 'Banda Languages' ], + bai: [ 'Bamileke Languages' ], + bak: [ 'Bashkir' ], + bal: [ 'Baluchi' ], + bam: [ 'Bambara' ], + ban: [ 'Balinese' ], + baq: [ 'Basque' ], + eus: [ 'Basque' ], + bas: [ 'Basa' ], + bat: [ 'Baltic Languages' ], + bej: [ 'Bedawiyet', 'Beja' ], + bel: [ 'Belarusian' ], + bem: [ 'Bemba' ], + ben: [ 'Bengali' ], + ber: [ 'Berber Languages' ], + bho: [ 'Bhojpuri' ], + bih: [ 'Bihari Languages' ], + bik: [ 'Bikol' ], + bin: [ 'Bini', 'Edo' ], + bis: [ 'Bislama' ], + bla: [ 'Siksika' ], + bnt: [ 'Bantu (Other)' ], + bos: [ 'Bosnian' ], + bra: [ 'Braj' ], + bre: [ 'Breton' ], + btk: [ 'Batak Languages' ], + bua: [ 'Buriat' ], + bug: [ 'Buginese' ], + bul: [ 'Bulgarian' ], + bur: [ 'Burmese' ], + mya: [ 'Burmese' ], + byn: [ 'Bilin', 'Blin' ], + cad: [ 'Caddo' ], + cai: [ 'Central American Indian Languages' ], + car: [ 'Galibi Carib' ], + cat: [ 'Catalan', 'Valencian' ], + cau: [ 'Caucasian Languages' ], + ceb: [ 'Cebuano' ], + cel: [ 'Celtic Languages' ], + cha: [ 'Chamorro' ], + chb: [ 'Chibcha' ], + che: [ 'Chechen' ], + chg: [ 'Chagatai' ], + chi: [ 'Chinese' ], + zho: [ 'Chinese' ], + chk: [ 'Chuukese' ], + chm: [ 'Mari' ], + chn: [ 'Chinook Jargon' ], + cho: [ 'Choctaw' ], + chp: [ 'Chipewyan', 'Dene Suline' ], + chr: [ 'Cherokee' ], + chu: + [ 'Church Slavic', + 'Church Slavonic', + 'Old Bulgarian', + 'Old Church Slavonic', + 'Old Slavonic' ], + chv: [ 'Chuvash' ], + chy: [ 'Cheyenne' ], + cmc: [ 'Chamic Languages' ], + cop: [ 'Coptic' ], + cor: [ 'Cornish' ], + cos: [ 'Corsican' ], + cpe: [ 'Creoles And Pidgins', 'English Based' ], + cpf: [ 'Creoles And Pidgins', 'French-based' ], + cpp: [ 'Creoles And Pidgins', 'Portuguese-based' ], + cre: [ 'Cree' ], + crh: [ 'Crimean Tatar', 'Crimean Turkish' ], + crp: [ 'Creoles And Pidgins' ], + csb: [ 'Kashubian' ], + cus: [ 'Cushitic Languages' ], + cze: [ 'Czech' ], + ces: [ 'Czech' ], + dak: [ 'Dakota' ], + dan: [ 'Danish' ], + dar: [ 'Dargwa' ], + day: [ 'Land Dayak Languages' ], + del: [ 'Delaware' ], + den: [ 'Slave (Athapascan)' ], + dgr: [ 'Dogrib' ], + din: [ 'Dinka' ], + div: [ 'Dhivehi', 'Divehi', 'Maldivian' ], + doi: [ 'Dogri' ], + dra: [ 'Dravidian Languages' ], + dsb: [ 'Lower Sorbian' ], + dua: [ 'Duala' ], + dum: [ 'Dutch Middle (ca.1050-1350)' ], + dut: [ 'Dutch', 'Flemish' ], + nld: [ 'Dutch', 'Flemish' ], + dyu: [ 'Dyula' ], + dzo: [ 'Dzongkha' ], + efi: [ 'Efik' ], + egy: [ 'Egyptian (Ancient)' ], + eka: [ 'Ekajuk' ], + elx: [ 'Elamite' ], + eng: [ 'English' ], + enm: [ 'English Middle (1100-1500)' ], + epo: [ 'Esperanto' ], + est: [ 'Estonian' ], + ewe: [ 'Ewe' ], + ewo: [ 'Ewondo' ], + fan: [ 'Fang' ], + fao: [ 'Faroese' ], + fat: [ 'Fanti' ], + fij: [ 'Fijian' ], + fil: [ 'Filipino', 'Pilipino' ], + fin: [ 'Finnish' ], + fiu: [ 'Finno-Ugrian Languages' ], + fon: [ 'Fon' ], + fre: [ 'French' ], + fra: [ 'French' ], + frm: [ 'French Middle (ca.1400-1600)' ], + fro: [ 'French Old (842-ca.1400)' ], + frr: [ 'Northern Frisian' ], + frs: [ 'Eastern Frisian' ], + fry: [ 'Western Frisian' ], + ful: [ 'Fulah' ], + fur: [ 'Friulian' ], + gaa: [ 'Ga' ], + gay: [ 'Gayo' ], + gba: [ 'Gbaya' ], + gem: [ 'Germanic Languages' ], + geo: [ 'Georgian' ], + kat: [ 'Georgian' ], + ger: [ 'German' ], + deu: [ 'German' ], + gez: [ 'Geez' ], + gil: [ 'Gilbertese' ], + gla: [ 'Gaelic', 'Scottish Gaelic' ], + gle: [ 'Irish' ], + glg: [ 'Galician' ], + glv: [ 'Manx' ], + gmh: [ 'German Middle High (ca.1050-1500)' ], + goh: [ 'German Old High (ca.750-1050)' ], + gon: [ 'Gondi' ], + gor: [ 'Gorontalo' ], + got: [ 'Gothic' ], + grb: [ 'Grebo' ], + grc: [ 'Greek Ancient (to 1453)' ], + gre: [ 'Greek Modern (1453-)' ], + ell: [ 'Greek Modern (1453-)' ], + grn: [ 'Guarani' ], + gsw: [ 'Alemannic', 'Alsatian', 'Swiss German' ], + guj: [ 'Gujarati' ], + gwi: [ 'Gwich\'in' ], + hai: [ 'Haida' ], + hat: [ 'Haitian', 'Haitian Creole' ], + hau: [ 'Hausa' ], + haw: [ 'Hawaiian' ], + heb: [ 'Hebrew' ], + her: [ 'Herero' ], + hil: [ 'Hiligaynon' ], + him: [ 'Himachali Languages', 'Western Pahari Languages' ], + hin: [ 'Hindi' ], + hit: [ 'Hittite' ], + hmn: [ 'Hmong', 'Mong' ], + hmo: [ 'Hiri Motu' ], + hrv: [ 'Croatian' ], + hsb: [ 'Upper Sorbian' ], + hun: [ 'Hungarian' ], + hup: [ 'Hupa' ], + iba: [ 'Iban' ], + ibo: [ 'Igbo' ], + ice: [ 'Icelandic' ], + isl: [ 'Icelandic' ], + ido: [ 'Ido' ], + iii: [ 'Nuosu', 'Sichuan Yi' ], + ijo: [ 'Ijo Languages' ], + iku: [ 'Inuktitut' ], + ile: [ 'Interlingue', 'Occidental' ], + ilo: [ 'Iloko' ], + ina: [ 'Interlingua (International Auxiliary Language Association)' ], + inc: [ 'Indic Languages' ], + ind: [ 'Indonesian' ], + ine: [ 'Indo-European Languages' ], + inh: [ 'Ingush' ], + ipk: [ 'Inupiaq' ], + ira: [ 'Iranian Languages' ], + iro: [ 'Iroquoian Languages' ], + ita: [ 'Italian' ], + jav: [ 'Javanese' ], + jbo: [ 'Lojban' ], + jpn: [ 'Japanese' ], + jpr: [ 'Judeo-Persian' ], + jrb: [ 'Judeo-Arabic' ], + kaa: [ 'Kara-Kalpak' ], + kab: [ 'Kabyle' ], + kac: [ 'Jingpho', 'Kachin' ], + kal: [ 'Greenlandic', 'Kalaallisut' ], + kam: [ 'Kamba' ], + kan: [ 'Kannada' ], + kar: [ 'Karen Languages' ], + kas: [ 'Kashmiri' ], + kau: [ 'Kanuri' ], + kaw: [ 'Kawi' ], + kaz: [ 'Kazakh' ], + kbd: [ 'Kabardian' ], + kha: [ 'Khasi' ], + khi: [ 'Khoisan Languages' ], + khm: [ 'Central Khmer' ], + kho: [ 'Khotanese', 'Sakan' ], + kik: [ 'Gikuyu', 'Kikuyu' ], + kin: [ 'Kinyarwanda' ], + kir: [ 'Kirghiz', 'Kyrgyz' ], + kmb: [ 'Kimbundu' ], + kok: [ 'Konkani' ], + kom: [ 'Komi' ], + kon: [ 'Kongo' ], + kor: [ 'Korean' ], + kos: [ 'Kosraean' ], + kpe: [ 'Kpelle' ], + krc: [ 'Karachay-Balkar' ], + krl: [ 'Karelian' ], + kro: [ 'Kru Languages' ], + kru: [ 'Kurukh' ], + kua: [ 'Kuanyama', 'Kwanyama' ], + kum: [ 'Kumyk' ], + kur: [ 'Kurdish' ], + kut: [ 'Kutenai' ], + lad: [ 'Ladino' ], + lah: [ 'Lahnda' ], + lam: [ 'Lamba' ], + lao: [ 'Lao' ], + lat: [ 'Latin' ], + lav: [ 'Latvian' ], + lez: [ 'Lezghian' ], + lim: [ 'Limburgan', 'Limburger', 'Limburgish' ], + lin: [ 'Lingala' ], + lit: [ 'Lithuanian' ], + lol: [ 'Mongo' ], + loz: [ 'Lozi' ], + ltz: [ 'Letzeburgesch', 'Luxembourgish' ], + lua: [ 'Luba-Lulua' ], + lub: [ 'Luba-Katanga' ], + lug: [ 'Ganda' ], + lui: [ 'Luiseno' ], + lun: [ 'Lunda' ], + luo: [ 'Luo (Kenya And Tanzania)' ], + lus: [ 'Lushai' ], + mac: [ 'Macedonian' ], + mkd: [ 'Macedonian' ], + mad: [ 'Madurese' ], + mag: [ 'Magahi' ], + mah: [ 'Marshallese' ], + mai: [ 'Maithili' ], + mak: [ 'Makasar' ], + mal: [ 'Malayalam' ], + man: [ 'Mandingo' ], + mao: [ 'Maori' ], + mri: [ 'Maori' ], + map: [ 'Austronesian Languages' ], + mar: [ 'Marathi' ], + mas: [ 'Masai' ], + may: [ 'Malay' ], + msa: [ 'Malay' ], + mdf: [ 'Moksha' ], + mdr: [ 'Mandar' ], + men: [ 'Mende' ], + mga: [ 'Irish Middle (900-1200)' ], + mic: [ 'Mi\'kmaq', 'Micmac' ], + min: [ 'Minangkabau' ], + mis: [ 'Uncoded Languages' ], + mkh: [ 'Mon-Khmer Languages' ], + mlg: [ 'Malagasy' ], + mlt: [ 'Maltese' ], + mnc: [ 'Manchu' ], + mni: [ 'Manipuri' ], + mno: [ 'Manobo Languages' ], + moh: [ 'Mohawk' ], + mon: [ 'Mongolian' ], + mos: [ 'Mossi' ], + mul: [ 'Multiple Languages' ], + mun: [ 'Munda Languages' ], + mus: [ 'Creek' ], + mwl: [ 'Mirandese' ], + mwr: [ 'Marwari' ], + myn: [ 'Mayan Languages' ], + myv: [ 'Erzya' ], + nah: [ 'Nahuatl Languages' ], + nai: [ 'North American Indian Languages' ], + nap: [ 'Neapolitan' ], + nau: [ 'Nauru' ], + nav: [ 'Navaho', 'Navajo' ], + nbl: [ 'Ndebele', 'South' ], + nde: [ 'Ndebele', 'North' ], + ndo: [ 'Ndonga' ], + nds: [ 'Low', 'Saxon' ], + nep: [ 'Nepali' ], + new: [ 'Nepal Bhasa', 'Newari' ], + nia: [ 'Nias' ], + nic: [ 'Niger-Kordofanian Languages' ], + niu: [ 'Niuean' ], + nno: [ 'Norwegian', 'Nynorsk' ], + nob: [ 'Bokmål', 'Norwegian' ], + nog: [ 'Nogai' ], + non: [ 'Norse', 'Old' ], + nor: [ 'Norwegian' ], + nqo: [ 'N\'Ko' ], + nso: [ 'Northern Sotho', 'Pedi', 'Sepedi' ], + nub: [ 'Nubian Languages' ], + nwc: [ 'Classical Nepal Bhasa', 'Classical Newari', 'Old Newari' ], + nya: [ 'Chewa', 'Chichewa', 'Nyanja' ], + nym: [ 'Nyamwezi' ], + nyn: [ 'Nyankole' ], + nyo: [ 'Nyoro' ], + nzi: [ 'Nzima' ], + oci: [ 'Occitan (post 1500)', 'Provençal' ], + oji: [ 'Ojibwa' ], + ori: [ 'Oriya' ], + orm: [ 'Oromo' ], + osa: [ 'Osage' ], + oss: [ 'Ossetian', 'Ossetic' ], + ota: [ 'Turkish Ottoman (1500-1928)' ], + oto: [ 'Otomian Languages' ], + paa: [ 'Papuan Languages' ], + pag: [ 'Pangasinan' ], + pal: [ 'Pahlavi' ], + pam: [ 'Kapampangan', 'Pampanga' ], + pan: [ 'Panjabi', 'Punjabi' ], + pap: [ 'Papiamento' ], + pau: [ 'Palauan' ], + peo: [ 'Persian Old (ca.600-400 B.C.)' ], + per: [ 'Persian' ], + fas: [ 'Persian' ], + phi: [ 'Philippine Languages' ], + phn: [ 'Phoenician' ], + pli: [ 'Pali' ], + pol: [ 'Polish' ], + pon: [ 'Pohnpeian' ], + por: [ 'Portuguese' ], + pra: [ 'Prakrit Languages' ], + pro: [ 'Provençal Old (to 1500)' ], + pus: [ 'Pashto', 'Pushto' ], + que: [ 'Quechua' ], + raj: [ 'Rajasthani' ], + rap: [ 'Rapanui' ], + rar: [ 'Cook Islands Maori', 'Rarotongan' ], + roa: [ 'Romance Languages' ], + roh: [ 'Romansh' ], + rom: [ 'Romany' ], + rum: [ 'Moldavian', 'Moldovan', 'Romanian' ], + ron: [ 'Moldavian', 'Moldovan', 'Romanian' ], + run: [ 'Rundi' ], + rup: [ 'Aromanian', 'Arumanian', 'Macedo-Romanian' ], + rus: [ 'Russian' ], + sad: [ 'Sandawe' ], + sag: [ 'Sango' ], + sah: [ 'Yakut' ], + sai: [ 'South American Indian (Other)' ], + sal: [ 'Salishan Languages' ], + sam: [ 'Samaritan Aramaic' ], + san: [ 'Sanskrit' ], + sas: [ 'Sasak' ], + sat: [ 'Santali' ], + scn: [ 'Sicilian' ], + sco: [ 'Scots' ], + sel: [ 'Selkup' ], + sem: [ 'Semitic Languages' ], + sga: [ 'Irish Old (to 900)' ], + sgn: [ 'Sign Languages' ], + shn: [ 'Shan' ], + sid: [ 'Sidamo' ], + sin: [ 'Sinhala', 'Sinhalese' ], + sio: [ 'Siouan Languages' ], + sit: [ 'Sino-Tibetan Languages' ], + sla: [ 'Slavic Languages' ], + slo: [ 'Slovak' ], + slk: [ 'Slovak' ], + slv: [ 'Slovenian' ], + sma: [ 'Southern Sami' ], + sme: [ 'Northern Sami' ], + smi: [ 'Sami Languages' ], + smj: [ 'Lule Sami' ], + smn: [ 'Inari Sami' ], + smo: [ 'Samoan' ], + sms: [ 'Skolt Sami' ], + sna: [ 'Shona' ], + snd: [ 'Sindhi' ], + snk: [ 'Soninke' ], + sog: [ 'Sogdian' ], + som: [ 'Somali' ], + son: [ 'Songhai Languages' ], + sot: [ 'Sotho', 'Southern' ], + spa: [ 'Castilian', 'Spanish' ], + srd: [ 'Sardinian' ], + srn: [ 'Sranan Tongo' ], + srp: [ 'Serbian' ], + srr: [ 'Serer' ], + ssa: [ 'Nilo-Saharan Languages' ], + ssw: [ 'Swati' ], + suk: [ 'Sukuma' ], + sun: [ 'Sundanese' ], + sus: [ 'Susu' ], + sux: [ 'Sumerian' ], + swa: [ 'Swahili' ], + swe: [ 'Swedish' ], + syc: [ 'Classical Syriac' ], + syr: [ 'Syriac' ], + tah: [ 'Tahitian' ], + tai: [ 'Tai Languages' ], + tam: [ 'Tamil' ], + tat: [ 'Tatar' ], + tel: [ 'Telugu' ], + tem: [ 'Timne' ], + ter: [ 'Tereno' ], + tet: [ 'Tetum' ], + tgk: [ 'Tajik' ], + tgl: [ 'Tagalog' ], + tha: [ 'Thai' ], + tib: [ 'Tibetan' ], + bod: [ 'Tibetan' ], + tig: [ 'Tigre' ], + tir: [ 'Tigrinya' ], + tiv: [ 'Tiv' ], + tkl: [ 'Tokelau' ], + tlh: [ 'Klingon', 'TlhIngan-Hol' ], + tli: [ 'Tlingit' ], + tmh: [ 'Tamashek' ], + tog: [ 'Tonga (Nyasa)' ], + ton: [ 'Tonga (Tonga Islands)' ], + tpi: [ 'Tok Pisin' ], + tsi: [ 'Tsimshian' ], + tsn: [ 'Tswana' ], + tso: [ 'Tsonga' ], + tuk: [ 'Turkmen' ], + tum: [ 'Tumbuka' ], + tup: [ 'Tupi Languages' ], + tur: [ 'Turkish' ], + tut: [ 'Altaic Languages' ], + tvl: [ 'Tuvalu' ], + twi: [ 'Twi' ], + tyv: [ 'Tuvinian' ], + udm: [ 'Udmurt' ], + uga: [ 'Ugaritic' ], + uig: [ 'Uighur', 'Uyghur' ], + ukr: [ 'Ukrainian' ], + umb: [ 'Umbundu' ], + und: [ 'Undetermined' ], + urd: [ 'Urdu' ], + uzb: [ 'Uzbek' ], + vai: [ 'Vai' ], + ven: [ 'Venda' ], + vie: [ 'Vietnamese' ], + vol: [ 'Volapük' ], + vot: [ 'Votic' ], + wak: [ 'Wakashan Languages' ], + wal: [ 'Walamo' ], + war: [ 'Waray' ], + was: [ 'Washo' ], + wel: [ 'Welsh' ], + cym: [ 'Welsh' ], + wen: [ 'Sorbian Languages' ], + wln: [ 'Walloon' ], + wol: [ 'Wolof' ], + xal: [ 'Kalmyk', 'Oirat' ], + xho: [ 'Xhosa' ], + yao: [ 'Yao' ], + yap: [ 'Yapese' ], + yid: [ 'Yiddish' ], + yor: [ 'Yoruba' ], + ypk: [ 'Yupik Languages' ], + zap: [ 'Zapotec' ], + zbl: [ 'Bliss', 'Blissymbolics', 'Blissymbols' ], + zen: [ 'Zenaga' ], + zgh: [ 'Standard Moroccan Tamazight' ], + zha: [ 'Chuang', 'Zhuang' ], + znd: [ 'Zande Languages' ], + zul: [ 'Zulu' ], + zun: [ 'Zuni' ], + zxx: [ 'No Linguistic Content', 'Not Applicable' ], + zza: [ 'Dimili', 'Dimli', 'Kirdki', 'Kirmanjki', 'Zaza', 'Zazaki' ] } \ No newline at end of file diff --git a/src/iso_639_2.min.js b/src/iso_639_2.min.js new file mode 100644 index 0000000..cd9ae32 --- /dev/null +++ b/src/iso_639_2.min.js @@ -0,0 +1,509 @@ +module.exports = { + '\0\0\0': + 1, // ["Unspecified"] + aar: 1, // ["Afar"] + abk: 1, // ["Abkhazian"] + ace: 1, // ["Achinese"] + ach: 1, // ["Acoli"] + ada: 1, // ["Adangme"] + ady: 1, // ["Adygei","Adyghe"] + afa: 1, // ["Afro-Asiatic Languages"] + afh: 1, // ["Afrihili"] + afr: 1, // ["Afrikaans"] + ain: 1, // ["Ainu"] + aka: 1, // ["Akan"] + akk: 1, // ["Akkadian"] + alb: 1, // ["Albanian"] + sqi: 1, // ["Albanian"] + ale: 1, // ["Aleut"] + alg: 1, // ["Algonquian Languages"] + alt: 1, // ["Southern Altai"] + amh: 1, // ["Amharic"] + ang: 1, // ["English Old (ca.450-1100)"] + anp: 1, // ["Angika"] + apa: 1, // ["Apache Languages"] + ara: 1, // ["Arabic"] + arc: 1, // ["Imperial Aramaic (700-300 BCE)","Official Aramaic (700-300 BCE)"] + arg: 1, // ["Aragonese"] + arm: 1, // ["Armenian"] + hye: 1, // ["Armenian"] + arn: 1, // ["Mapuche","Mapudungun"] + arp: 1, // ["Arapaho"] + art: 1, // ["Artificial Languages"] + arw: 1, // ["Arawak"] + asm: 1, // ["Assamese"] + ast: 1, // ["Asturian","Asturleonese","Bable","Leonese"] + ath: 1, // ["Athapascan Languages"] + aus: 1, // ["Australian Languages"] + ava: 1, // ["Avaric"] + ave: 1, // ["Avestan"] + awa: 1, // ["Awadhi"] + aym: 1, // ["Aymara"] + aze: 1, // ["Azerbaijani"] + bad: 1, // ["Banda Languages"] + bai: 1, // ["Bamileke Languages"] + bak: 1, // ["Bashkir"] + bal: 1, // ["Baluchi"] + bam: 1, // ["Bambara"] + ban: 1, // ["Balinese"] + baq: 1, // ["Basque"] + eus: 1, // ["Basque"] + bas: 1, // ["Basa"] + bat: 1, // ["Baltic Languages"] + bej: 1, // ["Bedawiyet","Beja"] + bel: 1, // ["Belarusian"] + bem: 1, // ["Bemba"] + ben: 1, // ["Bengali"] + ber: 1, // ["Berber Languages"] + bho: 1, // ["Bhojpuri"] + bih: 1, // ["Bihari Languages"] + bik: 1, // ["Bikol"] + bin: 1, // ["Bini","Edo"] + bis: 1, // ["Bislama"] + bla: 1, // ["Siksika"] + bnt: 1, // ["Bantu (Other)"] + bos: 1, // ["Bosnian"] + bra: 1, // ["Braj"] + bre: 1, // ["Breton"] + btk: 1, // ["Batak Languages"] + bua: 1, // ["Buriat"] + bug: 1, // ["Buginese"] + bul: 1, // ["Bulgarian"] + bur: 1, // ["Burmese"] + mya: 1, // ["Burmese"] + byn: 1, // ["Bilin","Blin"] + cad: 1, // ["Caddo"] + cai: 1, // ["Central American Indian Languages"] + car: 1, // ["Galibi Carib"] + cat: 1, // ["Catalan","Valencian"] + cau: 1, // ["Caucasian Languages"] + ceb: 1, // ["Cebuano"] + cel: 1, // ["Celtic Languages"] + cha: 1, // ["Chamorro"] + chb: 1, // ["Chibcha"] + che: 1, // ["Chechen"] + chg: 1, // ["Chagatai"] + chi: 1, // ["Chinese"] + zho: 1, // ["Chinese"] + chk: 1, // ["Chuukese"] + chm: 1, // ["Mari"] + chn: 1, // ["Chinook Jargon"] + cho: 1, // ["Choctaw"] + chp: 1, // ["Chipewyan","Dene Suline"] + chr: 1, // ["Cherokee"] + chu: 1, // ["Church Slavic","Church Slavonic","Old Bulgarian","Old Church Slavonic","Old Slavonic"] + chv: 1, // ["Chuvash"] + chy: 1, // ["Cheyenne"] + cmc: 1, // ["Chamic Languages"] + cop: 1, // ["Coptic"] + cor: 1, // ["Cornish"] + cos: 1, // ["Corsican"] + cpe: 1, // ["Creoles And Pidgins","English Based"] + cpf: 1, // ["Creoles And Pidgins","French-based"] + cpp: 1, // ["Creoles And Pidgins","Portuguese-based"] + cre: 1, // ["Cree"] + crh: 1, // ["Crimean Tatar","Crimean Turkish"] + crp: 1, // ["Creoles And Pidgins"] + csb: 1, // ["Kashubian"] + cus: 1, // ["Cushitic Languages"] + cze: 1, // ["Czech"] + ces: 1, // ["Czech"] + dak: 1, // ["Dakota"] + dan: 1, // ["Danish"] + dar: 1, // ["Dargwa"] + day: 1, // ["Land Dayak Languages"] + del: 1, // ["Delaware"] + den: 1, // ["Slave (Athapascan)"] + dgr: 1, // ["Dogrib"] + din: 1, // ["Dinka"] + div: 1, // ["Dhivehi","Divehi","Maldivian"] + doi: 1, // ["Dogri"] + dra: 1, // ["Dravidian Languages"] + dsb: 1, // ["Lower Sorbian"] + dua: 1, // ["Duala"] + dum: 1, // ["Dutch Middle (ca.1050-1350)"] + dut: 1, // ["Dutch","Flemish"] + nld: 1, // ["Dutch","Flemish"] + dyu: 1, // ["Dyula"] + dzo: 1, // ["Dzongkha"] + efi: 1, // ["Efik"] + egy: 1, // ["Egyptian (Ancient)"] + eka: 1, // ["Ekajuk"] + elx: 1, // ["Elamite"] + eng: 1, // ["English"] + enm: 1, // ["English Middle (1100-1500)"] + epo: 1, // ["Esperanto"] + est: 1, // ["Estonian"] + ewe: 1, // ["Ewe"] + ewo: 1, // ["Ewondo"] + fan: 1, // ["Fang"] + fao: 1, // ["Faroese"] + fat: 1, // ["Fanti"] + fij: 1, // ["Fijian"] + fil: 1, // ["Filipino","Pilipino"] + fin: 1, // ["Finnish"] + fiu: 1, // ["Finno-Ugrian Languages"] + fon: 1, // ["Fon"] + fre: 1, // ["French"] + fra: 1, // ["French"] + frm: 1, // ["French Middle (ca.1400-1600)"] + fro: 1, // ["French Old (842-ca.1400)"] + frr: 1, // ["Northern Frisian"] + frs: 1, // ["Eastern Frisian"] + fry: 1, // ["Western Frisian"] + ful: 1, // ["Fulah"] + fur: 1, // ["Friulian"] + gaa: 1, // ["Ga"] + gay: 1, // ["Gayo"] + gba: 1, // ["Gbaya"] + gem: 1, // ["Germanic Languages"] + geo: 1, // ["Georgian"] + kat: 1, // ["Georgian"] + ger: 1, // ["German"] + deu: 1, // ["German"] + gez: 1, // ["Geez"] + gil: 1, // ["Gilbertese"] + gla: 1, // ["Gaelic","Scottish Gaelic"] + gle: 1, // ["Irish"] + glg: 1, // ["Galician"] + glv: 1, // ["Manx"] + gmh: 1, // ["German Middle High (ca.1050-1500)"] + goh: 1, // ["German Old High (ca.750-1050)"] + gon: 1, // ["Gondi"] + gor: 1, // ["Gorontalo"] + got: 1, // ["Gothic"] + grb: 1, // ["Grebo"] + grc: 1, // ["Greek Ancient (to 1453)"] + gre: 1, // ["Greek Modern (1453-)"] + ell: 1, // ["Greek Modern (1453-)"] + grn: 1, // ["Guarani"] + gsw: 1, // ["Alemannic","Alsatian","Swiss German"] + guj: 1, // ["Gujarati"] + gwi: 1, // ["Gwich'in"] + hai: 1, // ["Haida"] + hat: 1, // ["Haitian","Haitian Creole"] + hau: 1, // ["Hausa"] + haw: 1, // ["Hawaiian"] + heb: 1, // ["Hebrew"] + her: 1, // ["Herero"] + hil: 1, // ["Hiligaynon"] + him: 1, // ["Himachali Languages","Western Pahari Languages"] + hin: 1, // ["Hindi"] + hit: 1, // ["Hittite"] + hmn: 1, // ["Hmong","Mong"] + hmo: 1, // ["Hiri Motu"] + hrv: 1, // ["Croatian"] + hsb: 1, // ["Upper Sorbian"] + hun: 1, // ["Hungarian"] + hup: 1, // ["Hupa"] + iba: 1, // ["Iban"] + ibo: 1, // ["Igbo"] + ice: 1, // ["Icelandic"] + isl: 1, // ["Icelandic"] + ido: 1, // ["Ido"] + iii: 1, // ["Nuosu","Sichuan Yi"] + ijo: 1, // ["Ijo Languages"] + iku: 1, // ["Inuktitut"] + ile: 1, // ["Interlingue","Occidental"] + ilo: 1, // ["Iloko"] + ina: 1, // ["Interlingua (International Auxiliary Language Association)"] + inc: 1, // ["Indic Languages"] + ind: 1, // ["Indonesian"] + ine: 1, // ["Indo-European Languages"] + inh: 1, // ["Ingush"] + ipk: 1, // ["Inupiaq"] + ira: 1, // ["Iranian Languages"] + iro: 1, // ["Iroquoian Languages"] + ita: 1, // ["Italian"] + jav: 1, // ["Javanese"] + jbo: 1, // ["Lojban"] + jpn: 1, // ["Japanese"] + jpr: 1, // ["Judeo-Persian"] + jrb: 1, // ["Judeo-Arabic"] + kaa: 1, // ["Kara-Kalpak"] + kab: 1, // ["Kabyle"] + kac: 1, // ["Jingpho","Kachin"] + kal: 1, // ["Greenlandic","Kalaallisut"] + kam: 1, // ["Kamba"] + kan: 1, // ["Kannada"] + kar: 1, // ["Karen Languages"] + kas: 1, // ["Kashmiri"] + kau: 1, // ["Kanuri"] + kaw: 1, // ["Kawi"] + kaz: 1, // ["Kazakh"] + kbd: 1, // ["Kabardian"] + kha: 1, // ["Khasi"] + khi: 1, // ["Khoisan Languages"] + khm: 1, // ["Central Khmer"] + kho: 1, // ["Khotanese","Sakan"] + kik: 1, // ["Gikuyu","Kikuyu"] + kin: 1, // ["Kinyarwanda"] + kir: 1, // ["Kirghiz","Kyrgyz"] + kmb: 1, // ["Kimbundu"] + kok: 1, // ["Konkani"] + kom: 1, // ["Komi"] + kon: 1, // ["Kongo"] + kor: 1, // ["Korean"] + kos: 1, // ["Kosraean"] + kpe: 1, // ["Kpelle"] + krc: 1, // ["Karachay-Balkar"] + krl: 1, // ["Karelian"] + kro: 1, // ["Kru Languages"] + kru: 1, // ["Kurukh"] + kua: 1, // ["Kuanyama","Kwanyama"] + kum: 1, // ["Kumyk"] + kur: 1, // ["Kurdish"] + kut: 1, // ["Kutenai"] + lad: 1, // ["Ladino"] + lah: 1, // ["Lahnda"] + lam: 1, // ["Lamba"] + lao: 1, // ["Lao"] + lat: 1, // ["Latin"] + lav: 1, // ["Latvian"] + lez: 1, // ["Lezghian"] + lim: 1, // ["Limburgan","Limburger","Limburgish"] + lin: 1, // ["Lingala"] + lit: 1, // ["Lithuanian"] + lol: 1, // ["Mongo"] + loz: 1, // ["Lozi"] + ltz: 1, // ["Letzeburgesch","Luxembourgish"] + lua: 1, // ["Luba-Lulua"] + lub: 1, // ["Luba-Katanga"] + lug: 1, // ["Ganda"] + lui: 1, // ["Luiseno"] + lun: 1, // ["Lunda"] + luo: 1, // ["Luo (Kenya And Tanzania)"] + lus: 1, // ["Lushai"] + mac: 1, // ["Macedonian"] + mkd: 1, // ["Macedonian"] + mad: 1, // ["Madurese"] + mag: 1, // ["Magahi"] + mah: 1, // ["Marshallese"] + mai: 1, // ["Maithili"] + mak: 1, // ["Makasar"] + mal: 1, // ["Malayalam"] + man: 1, // ["Mandingo"] + mao: 1, // ["Maori"] + mri: 1, // ["Maori"] + map: 1, // ["Austronesian Languages"] + mar: 1, // ["Marathi"] + mas: 1, // ["Masai"] + may: 1, // ["Malay"] + msa: 1, // ["Malay"] + mdf: 1, // ["Moksha"] + mdr: 1, // ["Mandar"] + men: 1, // ["Mende"] + mga: 1, // ["Irish Middle (900-1200)"] + mic: 1, // ["Mi'kmaq","Micmac"] + min: 1, // ["Minangkabau"] + mis: 1, // ["Uncoded Languages"] + mkh: 1, // ["Mon-Khmer Languages"] + mlg: 1, // ["Malagasy"] + mlt: 1, // ["Maltese"] + mnc: 1, // ["Manchu"] + mni: 1, // ["Manipuri"] + mno: 1, // ["Manobo Languages"] + moh: 1, // ["Mohawk"] + mon: 1, // ["Mongolian"] + mos: 1, // ["Mossi"] + mul: 1, // ["Multiple Languages"] + mun: 1, // ["Munda Languages"] + mus: 1, // ["Creek"] + mwl: 1, // ["Mirandese"] + mwr: 1, // ["Marwari"] + myn: 1, // ["Mayan Languages"] + myv: 1, // ["Erzya"] + nah: 1, // ["Nahuatl Languages"] + nai: 1, // ["North American Indian Languages"] + nap: 1, // ["Neapolitan"] + nau: 1, // ["Nauru"] + nav: 1, // ["Navaho","Navajo"] + nbl: 1, // ["Ndebele","South"] + nde: 1, // ["Ndebele","North"] + ndo: 1, // ["Ndonga"] + nds: 1, // ["Low","Saxon"] + nep: 1, // ["Nepali"] + new: 1, // ["Nepal Bhasa","Newari"] + nia: 1, // ["Nias"] + nic: 1, // ["Niger-Kordofanian Languages"] + niu: 1, // ["Niuean"] + nno: 1, // ["Norwegian","Nynorsk"] + nob: 1, // ["Bokmål","Norwegian"] + nog: 1, // ["Nogai"] + non: 1, // ["Norse","Old"] + nor: 1, // ["Norwegian"] + nqo: 1, // ["N'Ko"] + nso: 1, // ["Northern Sotho","Pedi","Sepedi"] + nub: 1, // ["Nubian Languages"] + nwc: 1, // ["Classical Nepal Bhasa","Classical Newari","Old Newari"] + nya: 1, // ["Chewa","Chichewa","Nyanja"] + nym: 1, // ["Nyamwezi"] + nyn: 1, // ["Nyankole"] + nyo: 1, // ["Nyoro"] + nzi: 1, // ["Nzima"] + oci: 1, // ["Occitan (post 1500)","Provençal"] + oji: 1, // ["Ojibwa"] + ori: 1, // ["Oriya"] + orm: 1, // ["Oromo"] + osa: 1, // ["Osage"] + oss: 1, // ["Ossetian","Ossetic"] + ota: 1, // ["Turkish Ottoman (1500-1928)"] + oto: 1, // ["Otomian Languages"] + paa: 1, // ["Papuan Languages"] + pag: 1, // ["Pangasinan"] + pal: 1, // ["Pahlavi"] + pam: 1, // ["Kapampangan","Pampanga"] + pan: 1, // ["Panjabi","Punjabi"] + pap: 1, // ["Papiamento"] + pau: 1, // ["Palauan"] + peo: 1, // ["Persian Old (ca.600-400 B.C.)"] + per: 1, // ["Persian"] + fas: 1, // ["Persian"] + phi: 1, // ["Philippine Languages"] + phn: 1, // ["Phoenician"] + pli: 1, // ["Pali"] + pol: 1, // ["Polish"] + pon: 1, // ["Pohnpeian"] + por: 1, // ["Portuguese"] + pra: 1, // ["Prakrit Languages"] + pro: 1, // ["Provençal Old (to 1500)"] + pus: 1, // ["Pashto","Pushto"] + que: 1, // ["Quechua"] + raj: 1, // ["Rajasthani"] + rap: 1, // ["Rapanui"] + rar: 1, // ["Cook Islands Maori","Rarotongan"] + roa: 1, // ["Romance Languages"] + roh: 1, // ["Romansh"] + rom: 1, // ["Romany"] + rum: 1, // ["Moldavian","Moldovan","Romanian"] + ron: 1, // ["Moldavian","Moldovan","Romanian"] + run: 1, // ["Rundi"] + rup: 1, // ["Aromanian","Arumanian","Macedo-Romanian"] + rus: 1, // ["Russian"] + sad: 1, // ["Sandawe"] + sag: 1, // ["Sango"] + sah: 1, // ["Yakut"] + sai: 1, // ["South American Indian (Other)"] + sal: 1, // ["Salishan Languages"] + sam: 1, // ["Samaritan Aramaic"] + san: 1, // ["Sanskrit"] + sas: 1, // ["Sasak"] + sat: 1, // ["Santali"] + scn: 1, // ["Sicilian"] + sco: 1, // ["Scots"] + sel: 1, // ["Selkup"] + sem: 1, // ["Semitic Languages"] + sga: 1, // ["Irish Old (to 900)"] + sgn: 1, // ["Sign Languages"] + shn: 1, // ["Shan"] + sid: 1, // ["Sidamo"] + sin: 1, // ["Sinhala","Sinhalese"] + sio: 1, // ["Siouan Languages"] + sit: 1, // ["Sino-Tibetan Languages"] + sla: 1, // ["Slavic Languages"] + slo: 1, // ["Slovak"] + slk: 1, // ["Slovak"] + slv: 1, // ["Slovenian"] + sma: 1, // ["Southern Sami"] + sme: 1, // ["Northern Sami"] + smi: 1, // ["Sami Languages"] + smj: 1, // ["Lule Sami"] + smn: 1, // ["Inari Sami"] + smo: 1, // ["Samoan"] + sms: 1, // ["Skolt Sami"] + sna: 1, // ["Shona"] + snd: 1, // ["Sindhi"] + snk: 1, // ["Soninke"] + sog: 1, // ["Sogdian"] + som: 1, // ["Somali"] + son: 1, // ["Songhai Languages"] + sot: 1, // ["Sotho","Southern"] + spa: 1, // ["Castilian","Spanish"] + srd: 1, // ["Sardinian"] + srn: 1, // ["Sranan Tongo"] + srp: 1, // ["Serbian"] + srr: 1, // ["Serer"] + ssa: 1, // ["Nilo-Saharan Languages"] + ssw: 1, // ["Swati"] + suk: 1, // ["Sukuma"] + sun: 1, // ["Sundanese"] + sus: 1, // ["Susu"] + sux: 1, // ["Sumerian"] + swa: 1, // ["Swahili"] + swe: 1, // ["Swedish"] + syc: 1, // ["Classical Syriac"] + syr: 1, // ["Syriac"] + tah: 1, // ["Tahitian"] + tai: 1, // ["Tai Languages"] + tam: 1, // ["Tamil"] + tat: 1, // ["Tatar"] + tel: 1, // ["Telugu"] + tem: 1, // ["Timne"] + ter: 1, // ["Tereno"] + tet: 1, // ["Tetum"] + tgk: 1, // ["Tajik"] + tgl: 1, // ["Tagalog"] + tha: 1, // ["Thai"] + tib: 1, // ["Tibetan"] + bod: 1, // ["Tibetan"] + tig: 1, // ["Tigre"] + tir: 1, // ["Tigrinya"] + tiv: 1, // ["Tiv"] + tkl: 1, // ["Tokelau"] + tlh: 1, // ["Klingon","TlhIngan-Hol"] + tli: 1, // ["Tlingit"] + tmh: 1, // ["Tamashek"] + tog: 1, // ["Tonga (Nyasa)"] + ton: 1, // ["Tonga (Tonga Islands)"] + tpi: 1, // ["Tok Pisin"] + tsi: 1, // ["Tsimshian"] + tsn: 1, // ["Tswana"] + tso: 1, // ["Tsonga"] + tuk: 1, // ["Turkmen"] + tum: 1, // ["Tumbuka"] + tup: 1, // ["Tupi Languages"] + tur: 1, // ["Turkish"] + tut: 1, // ["Altaic Languages"] + tvl: 1, // ["Tuvalu"] + twi: 1, // ["Twi"] + tyv: 1, // ["Tuvinian"] + udm: 1, // ["Udmurt"] + uga: 1, // ["Ugaritic"] + uig: 1, // ["Uighur","Uyghur"] + ukr: 1, // ["Ukrainian"] + umb: 1, // ["Umbundu"] + und: 1, // ["Undetermined"] + urd: 1, // ["Urdu"] + uzb: 1, // ["Uzbek"] + vai: 1, // ["Vai"] + ven: 1, // ["Venda"] + vie: 1, // ["Vietnamese"] + vol: 1, // ["Volapük"] + vot: 1, // ["Votic"] + wak: 1, // ["Wakashan Languages"] + wal: 1, // ["Walamo"] + war: 1, // ["Waray"] + was: 1, // ["Washo"] + wel: 1, // ["Welsh"] + cym: 1, // ["Welsh"] + wen: 1, // ["Sorbian Languages"] + wln: 1, // ["Walloon"] + wol: 1, // ["Wolof"] + xal: 1, // ["Kalmyk","Oirat"] + xho: 1, // ["Xhosa"] + yao: 1, // ["Yao"] + yap: 1, // ["Yapese"] + yid: 1, // ["Yiddish"] + yor: 1, // ["Yoruba"] + ypk: 1, // ["Yupik Languages"] + zap: 1, // ["Zapotec"] + zbl: 1, // ["Bliss","Blissymbolics","Blissymbols"] + zen: 1, // ["Zenaga"] + zgh: 1, // ["Standard Moroccan Tamazight"] + zha: 1, // ["Chuang","Zhuang"] + znd: 1, // ["Zande Languages"] + zul: 1, // ["Zulu"] + zun: 1, // ["Zuni"] + zxx: 1, // ["No Linguistic Content","Not Applicable"] + zza: 1 // ["Dimili","Dimli","Kirdki","Kirmanjki","Zaza","Zazaki"] +}; \ No newline at end of file diff --git a/src/ogg.js b/src/ogg.js index 7ec3d5c..b43908f 100644 --- a/src/ogg.js +++ b/src/ogg.js @@ -1,79 +1,84 @@ +'use strict' var utils = require('./utils'); -/** - * See http://www.ietf.org/rfc/rfc3533.txt - * @param {Buffer|ArrayBuffer} buffer - */ -module.exports = function(buffer) { - var view = utils.createView(buffer); - - function parsePage(offset, withPacket) { - if (view.byteLength < offset + 27) { - return null; - } - - var numPageSegments = view.getUint8(offset + 26), - segmentTable = utils.readBytes(view, offset + 27, numPageSegments), - headerSize = 27 + numPageSegments; +function parsePage( view, offset, withPacket ) { + if (view.byteLength < offset + 27) { + return null; + } - if (!segmentTable.length) { - return null; - } + var numPageSegments = view.getUint8(offset + 26), + segmentTable = utils.readBytes(view, offset + 27, numPageSegments), + headerSize = 27 + numPageSegments; - var - pageSize = headerSize + segmentTable.reduce(function(cur, next) { - return cur + next; - }), - length = headerSize + 1 + 'vorbis'.length, - packetView = null; + if (!segmentTable.length) { + return null; + } - if (withPacket) { - packetView = utils.createView(new ArrayBuffer(pageSize - length)); - utils.readBytes(view, offset + length, pageSize - length, packetView); - } + var + pageSize = headerSize + segmentTable.reduce(function(cur, next) { + return cur + next; + }), + length = headerSize + 1 + 'vorbis'.length, + packetView = null; - return { - pageSize: pageSize, - packet: packetView - }; + if (withPacket) { + packetView = utils.createView(new ArrayBuffer(pageSize - length)); + utils.readBytes(view, offset + length, pageSize - length, packetView); } - function parseComments(packet) { - try { - var vendorLength = packet.getUint32(0, true), - commentListLength = packet.getUint32(4 + vendorLength, true), - comments = {}, - offset = 8 + vendorLength, - map = { - tracknumber: 'track' - }; + return { + pageSize: pageSize, + packet: packetView + }; +} - for (var i = 0; i < commentListLength; i++) { - var commentLength = packet.getUint32(offset, true), - comment = utils.readUtf8(packet, offset + 4, commentLength), - equals = comment.indexOf('='), - key = comment.substring(0, equals).toLowerCase(); +function parseComments(packet) { + try { + var vendorLength = packet.getUint32(0, true), + commentListLength = packet.getUint32(4 + vendorLength, true), + comments = {}, + offset = 8 + vendorLength, + map = { + tracknumber: 'track' + }; - comments[map[key] || key] = comments[key] = utils.trimNull(comment.substring(equals + 1)); - offset += 4 + commentLength; - } + for (var i = 0; i < commentListLength; i++) { + var commentLength = packet.getUint32(offset, true), + comment = utils.readUtf8(packet, offset + 4, commentLength), + equals = comment.indexOf('='), + key = comment.substring(0, equals).toLowerCase(); - return comments; - } catch (e) { - //all exceptions are just malformed/truncated data, so we just ignore them - return null; + comments[map[key] || key] = comments[key] = utils.trimNull(comment.substring(equals + 1)); + offset += 4 + commentLength; } + + return comments; + + } catch (e) { + //all exceptions are just malformed/truncated data, so we just ignore them + return null; } +} +/** + * See http://www.ietf.org/rfc/rfc3533.txt + * @param {Buffer|ArrayBuffer} buffer + */ +module.exports = function(buffer, opts) { + var view = utils.createView(buffer); - var id = parsePage(0); + var id = parsePage(view, 0); if (!id) { return null; } - var commentHeader = parsePage(id.pageSize, true); + var commentHeader = parsePage(view, id.pageSize, true); if (!commentHeader) { return null; } - return parseComments(commentHeader.packet); + var tag = parseComments(commentHeader.packet); + if ( opts && opts.toId3v2 ) { + tag = utils.id3v2.transformTag( tag ); + } + return tag; }; \ No newline at end of file diff --git a/src/utils.js b/src/utils.js index f68081a..0b172e4 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,3 +1,7 @@ +'use strict' + +var reNull = /[\u0000 ]+$/; + function toArrayBuffer(buffer) { var arrayBuffer = new ArrayBuffer(buffer.length); var view = new Uint8Array(arrayBuffer); @@ -7,12 +11,269 @@ function toArrayBuffer(buffer) { return arrayBuffer; } +function sliceView( view, offset, length ) { + if ( ( length < 1 ) || + ( offset < 0 ) || + ( view.byteLength < offset + length ) ) { + return undefined; + } + var buffer = view.buffer.slice( offset, offset + length ); + // workaround for a double BOM problem + // https://github.com/leetreveil/musicmetadata/issues/84 + if ( (buffer[0] === 0xFF && buffer[1] === 0xFE && buffer[2] === 0xFE && buffer[3] === 0xFF) || + (buffer[1] === 0xFF && buffer[0] === 0xFE && buffer[3] === 0xFE && buffer[2] === 0xFF) ) { + buffer = buffer.slice( 2 ) + } + return Buffer.from( buffer ) +} + + +var id3v2Map = { + TALB: 'album', + TOAL: 'original_album', + TPOS: 'part', + TCOM: 'composer', + TIT1: 'title', + TIT2: 'title', + TIT3: 'title', + TSST: 'subtitle', + TPE1: 'artist', + TPE2: 'artist', + TPE3: 'artist', + TPE4: 'artist', + TOPE: 'original_artist', + TEXT: 'lyricist', + TOLY: 'original_lyricist', + TRCK: 'track', + TSSE: 'encoder', + TDRC: 'year', + TCON: 'genre', + TSRC: 'isrc', + TMCL: 'musician_credit', + TIPL: 'involved_credit', + TENC: 'encoded_by', + TBPM: 'beats_per_minute', + TLEN: 'length', + TKEY: 'initial_key', + TLAN: 'language', + TFLT: 'file_type', + TMED: 'media_type', + TMOO: 'mood', + TCOP: 'copyright_message', + TPRO: 'produced_notice', + TPUB: 'publisher', + TOWN: 'licensee', + TRSN: 'internet_radio_station', + TRSO: 'internet_radio_station_owner', + TOFN: 'original_filename', + TDLY: 'playlist_delay', + TDEN: 'encoding_time', + TDOR: 'original_release_time', + TDRL: 'release_time', + TDTG: 'tagging_time', + TSOA: 'album_sort', + TSOP: 'artist_sort', + TSOT: 'title_sort', + WCOM: 'commercial_url', + WCOP: 'copyright_url', + WOAF: 'official_audio_url', + WOAR: 'official_artist_url', + WOAS: 'official_source_url', + WORS: 'official_internet_radio_station_url', + WPAY: 'payment_url', + WPUB: 'publisher_url', + COMM: 'comment', + USLT: 'lyrics', + SYLT: 'lyrics_synchronized', + USER: 'terms_of_use' +}; +var id3v2MapInv = ( function(){ + var map = { }, key, id, i; + for( id in id3v2Map ) { + key = id3v2Map[ id ]; + if ( map[ key ] ) { + if ( !Array.isArray( map[ key ] ) ) { + map[ key ] = [ map[ key ] ]; + } + map[ key ].push( id ); + } else { + map[ key ] = id; + } + } + for( key in map ) { + id = map[ key ]; + if ( Array.isArray( id ) ) { + id.sort(); + for( i = id.length - 1; i >= 0; i-- ) { + map[ [ key, i ].join( '.' ) ] = id[ i ]; + } + } + } + return map; +} )( ); +var id3v2LanguageFrames = { + // default - Unspecified lang, No content descriptor + USLT: function( v ){ return id3v2EnsureLanguage( v, '\0' ); }, + // default - Unspecified lang, $01 Absolute time 32 bit sized, + // Content type $00 is other, No content descriptor + SYLT: function( v ){ return id3v2EnsureLanguage( v, + [ '\u0001\0\0', /^[\u0001\u0002][\u0000-\u0008][^\u0000]*?\u0000/ ] ); }, + // default - Unspecified lang, No content descriptor + COMM: function( v ){ return id3v2EnsureLanguage( v, '\0' ); }, + // default - Unspecified lang + USER: function( v ){ return id3v2EnsureLanguage( v, '' ); } +} +function defaultFrame( id, content ) { + var tag = { flags: [ 0, 0 ] }; + if ( content ) { + tag.content = content; + if ( typeof content === 'string' ) { + tag.encoding = ( id && ( id.slice( 0, 1 ) === 'W' ) && ( id !== 'WXXX' ) ) ? -1: 3; + } + } + return tag; +} + +var iso639; +if ( process.env.NODE_ENV === 'browserify' ) { + iso639 = require( './iso_639_2.min' ); +} else { + iso639 = require( './iso_639_2' ); +} + +function id3v2EnsureLanguage( value, prefix, lang ) { + var langIsStr = ( typeof lang === 'string' ); + var cmp = value.slice( 0, ( langIsStr ? lang.length: 3 ) ).toLowerCase( ) + var hasOne = ( langIsStr && ( cmp === ( lang = lang.toLowerCase() ) ) ) || + ( !lang && cmp && !!iso639[ cmp ] ); + if ( prefix ) { + if ( !Array.isArray( prefix ) ) { + prefix = [ prefix ]; + } + if ( hasOne ) { + hasOne = false; + for ( var i = prefix.length - 1, it; i >= 0; i-- ) { + if ( !( it = prefix[ i ] ) ) { + continue; + } + if ( it.constructor === RegExp ) { + if ( hasOne = it.test( value.slice( 3 ) ) ) { + break; + } + } + else { + it = String( it ); + if ( hasOne = ( ( ( value.slice( 3, 3 + it.length ) ) === it ) || + ( ( it === '\0' ) && ( value.indexOf( it ) > 3 ) ) ) ) { + break; + } + } + } + } + } + return ( hasOne ) ? value: + ( ( langIsStr ? lang : '\0\0\0' ) + ( prefix && prefix[ 0 ] || '' ) + value ) +}; + +function I( v ){ return v; } +function transformTag( tag ) { + var id, inner, value; + if ( !tag._ ) { + tag._ = inner = { }; + } + for( var key in tag ) { + if ( !( id = id3v2MapInv[ key ] ) ) { + continue; + } + if ( Array.isArray( id ) ) { + id = id[ 0 ]; + } + value = tag[ key ]; + tag[ id ] = value = + // make sure language is there for the language + // tied frames, otherwise use the original value + ( id3v2LanguageFrames[ id ] || I )( value ); + ( inner[ id ] = ( inner[ id ] || [ ] ) ).push( + defaultFrame( undefined, value ) ); + } + return tag; +}; + +function decode( buf, enc ) { + var swap = false; + switch( ( enc && enc.toLowerCase() ) || 'utf8' ) { + case 'ascii': + case 'utf8': + return buf.toString( enc ); + + case 'utf16be': + swap = true; + + case 'utf16': + case 'ucs2': + if ( ( buf.length > 2 ) && ( buf[0] === 0xfe && buf[1] === 0xff ) ) { + swap = true; + } + if ( swap ) { + if ( buf.length & 0x01 ) { + throw new Error( 'Invaid buffer size' ); + } + buf.swap16(); + } + case 'utf16le': + if ( buf[0] === 0xff && buf[1] === 0xfe ) { + buf = buf.slice( 2 ); + } + return buf.toString( 'ucs2' ); + + case 'iso88591': + case 'binary': + return buf.toString( 'binary' ) + + default: + throw new Error( 'Unknown encoding ' + enc ); + + } +} + +function encode( str, enc ) { + var swap = true; + switch( ( enc && enc.toLowerCase() ) || 'utf8' ) { + case 'ascii': + case 'utf8': + return new Buffer( str, 'utf8' ); + + + case 'utf16': + case 'ucs2': + case 'utf16le': + if ( str.slice( 0, 1 ) !== '\ufeff' ) { + str = '\ufeff' + str; + } + swap = false; + + case 'utf16be': + str = new Buffer( str, 'ucs2' ); + if ( swap ) { + str.swap16(); + } + return str; + + case 'iso88591': + case 'binary': + return new Buffer( str, 'binary' ); + + default: + throw new Error( 'Unknown encoding ' + enc ); + } +} + module.exports = { trimNull: function(s) { - return s.replace(/\u0000+$/, ''); + return s.replace(reNull, ''); }, - createView: function(buffer) { + createView: function( buffer ) { if (typeof(Buffer) !== 'undefined' && buffer instanceof Buffer) { //convert nodejs buffers to ArrayBuffer buffer = toArrayBuffer(buffer); @@ -25,7 +286,7 @@ module.exports = { return new DataView(buffer); }, - readBytes: function(view, offset, length, target) { + readBytes: function( view, offset, length, target ) { if (offset + length < 0) { return []; } @@ -42,28 +303,46 @@ module.exports = { return bytes; }, + + encode: function( str, enc ) { + return encode( str, enc || 'utf8' ); + }, - readAscii: function(view, offset, length) { - if (view.byteLength < offset + length) { - return ''; - } - var s = ''; - for (var i = 0; i < length; i++) { - s += String.fromCharCode(view.getUint8(offset + i)); - } + readText: function( view, offset, length, encoding ) { + var buf = this.trimNull( ( ( buf = sliceView( view, offset, length ) ) && + decode( buf, encoding || 'utf8' ) ) || '' ); + return buf; + }, - return s; + readIso8859: function( view, offset, length ) { + return this.readText( view, offset, length, 'iso88591' ); }, - readUtf8: function(view, offset, length) { - if (view.byteLength < offset + length) { - return ''; - } + readUtf16BE:function( view, offset, length ) { + return this.readText( view, offset, length, 'utf16be' ); + }, - var buffer = view.buffer.slice(offset, offset + length); + readUtf16:function( view, offset, length ) { + return this.readText( view, offset, length, 'utf16' ); + }, + + readAscii: function( view, offset, length ) { + return this.readText( view, offset, length, 'ascii' ); + }, + + readUtf8: function( view, offset, length ) { + return this.readText( view, offset, length, 'utf8' ); + }, + + id3v2: { + transformTag: transformTag, + frameMap: id3v2Map, + frameMapInv: id3v2MapInv, + ensureLanguage: id3v2EnsureLanguage, + languageFrames: id3v2LanguageFrames, + defaultFrame: defaultFrame + }, + + I: I - //http://stackoverflow.com/a/17192845 - convert byte array to UTF8 string - var encodedString = String.fromCharCode.apply(null, new Uint8Array(buffer)); - return decodeURIComponent(escape(encodedString)); - } }; \ No newline at end of file diff --git a/tests/metadata-tests.js b/tests/metadata-tests.js index aa62e38..571d535 100644 --- a/tests/metadata-tests.js +++ b/tests/metadata-tests.js @@ -12,7 +12,7 @@ describe('ogg', function() { return; } - var metadata = metaDataReader.ogg(buffer); + var metadata = metaDataReader.ogg(buffer, { toId3v2: true } ); should.exist(metadata); metadata.should.have.property('title', 'Contra Base Snippet'); metadata.should.have.property('artist', 'Konami'); @@ -33,7 +33,7 @@ describe('ogg', function() { return; } - var metadata = metaDataReader.ogg(buffer); + var metadata = metaDataReader.ogg(buffer, { toId3v2: true } ); should.exist(metadata); metadata.should.have.property('title', 'Contra Base Snippet'); metadata.should.have.property('artist', 'Konami'); @@ -148,7 +148,7 @@ describe('id3', function() { return; } - var metadata = metaDataReader.id3v1(buffer); + var metadata = metaDataReader.id3v1(buffer,{ toId3v2: true }); should.exist(metadata); metadata.should.have.property('title', 'Foobar'); metadata.should.have.property('artist', 'The Foobars'); @@ -169,7 +169,7 @@ describe('id3', function() { return; } - var metadata = metaDataReader.id3v1(buffer); + var metadata = metaDataReader.id3v1(buffer,{ toId3v2: true }); should.exist(metadata); metadata.should.have.property('title', 'Foobar'); metadata.should.have.property('artist', 'The Foobars'); @@ -184,7 +184,7 @@ describe('id3', function() { it('should not explode if ID3v1 tags don\'t exist', function(done) { var buffer = new Buffer(1); - var metadata = metaDataReader.id3v1(buffer); + var metadata = metaDataReader.id3v1(buffer, { toId3v2: true }); should.deepEqual(metadata, null); done(); }); @@ -195,4 +195,261 @@ describe('id3', function() { should.deepEqual(metadata, null); done(); }); + + it('should pass-through unknown frames during ID3v2 serialization', function(done) { + var tag = { + 'ZZZZ': new Buffer( [ 1, 2, 3 ] ), + _: { + 'ZZZY': [ + { "flags": [ 23, 66 ], + "content": new Buffer( [ 2, 3, 4 ] ) }, + { "flags": [ 44, 66 ], + "content": new Buffer( [ 3, 4, 5 ] ) } ] } } + var metadata = metaDataReader.id3v2.serialize(tag,{ inCache: true }); + should.deepEqual( metadata._, { + "ZZZZ": [ { + "flags": [ 0, 0 ], + "content": new Buffer( [ 1, 2, 3 ] ) } ], + 'ZZZY': [ + { "flags": [ 23, 66 ], + "content": new Buffer( [ 2, 3, 4 ] ) }, + { "flags": [ 44, 66 ], + "content": new Buffer( [ 3, 4, 5 ] ) } ] } ); + done(); + }); + + it('should normalize language tied frames during ID3v2 serialization', function(done) { + var tag = { + USLT: [ 'blah, blah <- to be overwritten' ], + lyrics: [ 'blah, blah', 'bul\0дрън дрън', 'pol\0bla bla' ], + lyrics_synchronized: 'blah, blah', + SYLT: [ 'blah, blah <- to be overwritten', + "eng" + String.fromCharCode( 1 ) + "\0final\0some\0sync\0lyrics\0ignored", + "eng" + String.fromCharCode( 2 ) + String.fromCharCode( 1 ) + + "final\0lyrics" ], + COMM: '\0\0\0\0should be overwritten', + comment: [ 'just a comment', + 'eng\0just a comment', + 'eng\0school awaits!' ], + USER: 'default terms', + terms_of_use: 'better legal here' } + var metadata = metaDataReader.id3v2.serialize(tag,{ inCache: true }); + should.deepEqual( metadata._, { + USLT: [ + { "flags": [ 0, 0 ], "encoding": 3, + "content": 'pol\0bla bla' }, + { "flags": [ 0, 0 ], "encoding": 3, + "content": 'bul\0дрън дрън' }, + { "flags": [ 0, 0 ], "encoding": 3, + "content": '\0\0\0\0blah, blah' } ], + SYLT: [ + { "flags": [ 0, 0 ], "encoding": 3, + "content": '\0\0\0' + String.fromCharCode( 1 ) + '\0\0blah, blah' }, + { "flags": [ 0, 0 ], "encoding": 3, + "content": "eng" + String.fromCharCode( 2 ) + String.fromCharCode( 1 ) + + "final\0lyrics" } ], + COMM: [ + { "flags": [ 0, 0 ], "encoding": 3, + "content": "eng\0school awaits!" }, + { "flags": [ 0, 0 ], "encoding": 3, + "content": '\0\0\0\0just a comment' } ], + USER: [ + { "flags": [ 0, 0 ], "encoding": 3, + "content": "\0\0\0better legal here" } ] } ); + done(); + }); + + it('should support multiple titles/artists spilling over to TXXX', function(done) { + var tag = { + title: [ 'a', 'b', 'c', 'd' ], + artist: [ 'a', 'b', 'c', 'd', 'e' ], + TPE2: 'z <- to be overwritten', + TIT1: 'z <- to be overwritten' } + var metadata = metaDataReader.id3v2.serialize(tag,{ inCache: true }); + // console.log( JSON.stringify( metadata._, '', '\t' ) ); + should.deepEqual( metadata._, { + TIT1: [ { "flags": [ 0, 0 ], "encoding": 3, + "content": 'a' } ], + TIT2: [ { "flags": [ 0, 0 ], "encoding": 3, + "content": 'b' } ], + TIT3: [ { "flags": [ 0, 0 ], "encoding": 3, + "content": 'c' } ], + TPE1: [ { "flags": [ 0, 0 ], "encoding": 3, + "content": 'a' } ], + TPE2: [ { "flags": [ 0, 0 ], "encoding": 3, + "content": 'b' } ], + TPE3: [ { "flags": [ 0, 0 ], "encoding": 3, + "content": 'c' } ], + TPE4: [ { "flags": [ 0, 0 ], "encoding": 3, + "content": 'd' } ], + TXXX: [ + { "flags": [ 0, 0 ], "encoding": 3, + "content": 'artist\0e' }, + { "flags": [ 0, 0 ], "encoding": 3, + "content": 'title\0d' } ] } ); + done(); + }); + + it('should spill unknown text tags over TXXX and WXXX if url is suggested', function(done) { + var tag = { + author_url: 'treated as url because of _url suffix; to be overwritten -> www.ladygaga.com', + author: [ 'a', 'b', 'c', 'd', 'http://home.dieantwoord.com/' ] } + var metadata = metaDataReader.id3v2.serialize(tag,{ inCache: true }); + should.deepEqual( metadata._, { + TXXX: [ + { "flags": [ 0, 0 ], "encoding": 3, + "content": 'author\0d' } ], + WXXX: [ + { "flags": [ 0, 0 ], "encoding": 3, + "content": 'author\0http://home.dieantwoord.com/' } ] } ); + done(); + }); + + it('should throw in case of unknown text content XXXX tags', function(done) { + var tag = { + XOXO: 'this text cannot be handled; \ + use lower case key if aiming for TXXX tag spill' } + var metadata = null, x; + try { + metadata = metaDataReader.id3v2.serialize(tag,{ inCache: true }); + } catch( e ) { + x = e; + } + should.deepEqual(metadata, null); + should.exist(x); + done(); + }); + + it('should pass through Buffer content in case of XXXX tags', function(done) { + var tag = { + XOXO: new Buffer( 'this text shoould be handled', 'utf8' ) } + var metadata = metaDataReader.id3v2.serialize(tag,{ inCache: true }); + should.deepEqual(metadata._, { + XOXO: [ + { "flags": [ 0, 0 ], + "content": new Buffer( 'this text shoould be handled', 'utf8' ) } ] } ); + done(); + }); + + it('should not overwrite frame flags and encoding if content is same', function(done) { + var tag = { + TIT1: 'untitled', + comment: 'hi there', + _: { + TIT1: [ + { "flags": [ 1, 2 ], "encoding": 0, + "content": 'untitled' } ], + COMM: [ + { "flags": [ 2, 3 ], "encoding": 1, + "content": '\0\0\0\0hi there' } ] + } } + var metadata = metaDataReader.id3v2.serialize(tag,{ inCache: true }); + should.deepEqual(metadata._, { + TIT1: [ + { "flags": [ 1, 2 ], "encoding": 0, + "content": 'untitled' } ], + COMM: [ + { "flags": [ 2, 3 ], "encoding": 1, + "content": '\0\0\0\0hi there' } ] } ); + done(); + }); + + it('should encourage human readable frame names, boosting their weight', function(done) { + var tag = { + TIT2: 'untitled', + title: [ , 'fink you freaky' ] } + var metadata = metaDataReader.id3v2.serialize(tag,{ inCache: true }); + should.deepEqual(metadata._, { + TIT2: [ + { "flags": [ 0, 0 ], "encoding": 3, + "content": 'fink you freaky' } ] } ); + done(); + }); + + it('must output encoding for textual fields', function(done) { + var tag = { + TIT2: 'untitled', + title: [ , 'fink you freaky' ] } + var metadata = metaDataReader.id3v2.serialize(tag,{ inCacheBuffers: true }); + should.equal(metadata._.TIT2[ 0 ].buffer[ 10 ], 3 ); + done(); + }); + + it('must null terminate textual fields', function(done) { + var tag = { + TIT2: 'untitled', + title: [ , 'fink you freaky' ] } + var metadata = metaDataReader.id3v2.serialize(tag,{ inCacheBuffers: true }); + should.equal(metadata._.TIT2[ 0 ].buffer.slice( -1 )[ 0 ], 0 ); + done(); + }); + + it('must not output encoding for url fields, except WXXX', function(done) { + var tag = { + some_url: 'untitled', + WPUB: 'fink you freaky' } + var metadata = metaDataReader.id3v2.serialize(tag,{ inCacheBuffers: true }); + should.equal(metadata._.WPUB[ 0 ].buffer[ 10 ], 'f'.charCodeAt( 0 ) ); + should.equal(metadata._.WXXX[ 0 ].buffer[ 10 ], 3 ); + done(); + }); + + it('should align the frames at 32 bit boundary', function(done) { + var tag = { + some_url: 'untitled', + some1_url: 'untitled' } + var metadata = metaDataReader.id3v2.serialize(tag,{ inCacheBuffers: true, frameAlign: 32 }); + should.equal( metadata._.WXXX[ 0 ].buffer.length & 0x3 , 0 ); + should.equal( metadata._.WXXX[ 1 ].buffer.length & 0x3 , 0 ); + should.equal( metadata._.WXXX[ 0 ].buffer.length, metadata._.WXXX[ 1 ].buffer.length ); + done(); + }); + + + it('should not accept frame alignment different than 8 * n', function(done) { + var tag = { + some_url: 'untitled', + some1_url: 'untitled' } + var metadata = null, x; + try { + metaDataReader.id3v2.serialize(tag,{ frameAlign: 33 }); + } catch( e ) { + x = e; + } + should.deepEqual(metadata, null); + should.exist(x); + done(); + }); + + it('should align the entire tag frame at 128 bit boundary', function(done) { + var tag = { + some_url: 'untitled', + some1_url: 'untitled' } + var tag2 = { + some_url: 'untitled1', + some1_url: 'untitled' }; + var buf = metaDataReader.id3v2.serialize(tag, { tagAlign: 128 }); + var buf2 = metaDataReader.id3v2.serialize(tag2,{ tagAlign: 128 }); + should.equal( buf.length & 0xf , 0 ); + should.equal( buf2.length & 0xf , 0 ); + should.equal( buf.length, buf2.length ); + done(); + }); + + it('should not accept tag alignment different than 8 * n', function(done) { + var tag = { + some_url: 'untitled', + some1_url: 'untitled' } + var metadata = null, x; + try { + metaDataReader.id3v2.serialize(tag,{ tagAlign: 127 }); + } catch( e ) { + x = e; + } + should.deepEqual(metadata, null); + should.exist(x); + done(); + }); + + // console.log( JSON.stringify( metadata._, '', '\t' ) ) }); \ No newline at end of file