diff --git a/deemix/tagger.js b/deemix/tagger.js index d265d4c..e6000ad 100644 --- a/deemix/tagger.js +++ b/deemix/tagger.js @@ -5,22 +5,22 @@ const fs = require('fs') function tagID3(path, track, save){ const songBuffer = fs.readFileSync(path) const tag = new ID3Writer(songBuffer) - if (save.useNullSeparator) tag.separator = String.fromCharCode(0) + tag.separateWithNull = String.fromCharCode(0) if (save.title) tag.setFrame('TIT2', track.title) if (save.artist && track.artists.length){ if (save.multiArtistSeparator == "default"){ - tag.setArrayFrame('TPE1', track.artists) + tag.setFrame('TPE1', track.artists) }else{ if (save.multiArtistSeparator == "nothing"){ - tag.setArrayFrame('TPE1', [track.mainArtist.name]) + tag.setFrame('TPE1', [track.mainArtist.name]) } else { - tag.setArrayFrame('TPE1', [track.artistsString]) + tag.setFrame('TPE1', [track.artistsString]) } // Tag ARTISTS is added to keep the multiartist support when using a non standard tagging method // https://picard-docs.musicbrainz.org/en/appendices/tag_mapping.html#artists - tag.setArrayFrame('TXXX', { + tag.setFrame('TXXX', { description: 'ARTISTS', value: track.artists }) @@ -31,9 +31,9 @@ function tagID3(path, track, save){ if (save.albumArtist && track.album.artists.length){ if (save.singleAlbumArtist && track.album.mainArtist.save){ - tag.setArrayFrame('TPE2', [track.album.mainArtist.name]) + tag.setFrame('TPE2', [track.album.mainArtist.name]) } else { - tag.setArrayFrame('TPE2', track.album.artists) + tag.setFrame('TPE2', track.album.artists) } } @@ -48,7 +48,7 @@ function tagID3(path, track, save){ tag.setFrame('TPOS', discNumber) } - if (save.genre) tag.setArrayFrame('TCON', track.album.genre) + if (save.genre) tag.setFrame('TCON', track.album.genre) if (save.year) tag.setFrame('TYER', track.date.year) // Referencing ID3 standard @@ -78,15 +78,12 @@ function tagID3(path, track, save){ language: 'XXX' }) - // TODO: Uncomment when implemented in lib - /* if (save.syncedLyrics && track.lyrics.syncID3) tag.setFrame('SYLT', { text: track.lyrics.syncID3, type: 1, timestampFormat: 2, useUnicodeEncoding: true }) - */ let involvedPeople = [] Object.keys(track.contributors).forEach(role => { @@ -98,12 +95,11 @@ function tagID3(path, track, save){ tag.setFrame('TCOM', track.contributors.composer) } }) - // TODO: Uncomment when implemented in lib - // if (involvedPeople.length && save.involvedPeople) tag.setFrame('IPLS', involvedPeople) + if (involvedPeople.length && save.involvedPeople) tag.setFrame('IPLS', involvedPeople) if (save.copyright) tag.setFrame('TCOP', track.copyright) if (save.savePlaylistAsCompilation && track.playlist || track.album.recordType == "compile") - tag._setIntegerFrame('TCMP', 1) // tag.setFrame('TCMP', 1) + tag.setFrame('TCMP', '1') if (save.source){ tag.setFrame('TXXX', { diff --git a/deemix/utils/id3-writer.js b/deemix/utils/id3-writer.js index 62c687e..495ed93 100644 --- a/deemix/utils/id3-writer.js +++ b/deemix/utils/id3-writer.js @@ -1,46 +1 @@ -const ID3Writer = require('browser-id3-writer') - -class CustomID3Writer extends ID3Writer{ - constructor(buffer){ - super(buffer) - this.separator = undefined - } - - setArrayFrame(frameName, frameValue){ - switch (frameName) { - case 'TPE1': // song artists - case 'TCOM': // song composers - case 'TPE2': // album artist - case 'TCON': { // song genres - if (!Array.isArray(frameValue)) { - throw new Error(`${frameName} frame value should be an array of strings`) - } - - const delemiter = this.separator || (frameName === 'TCON' ? ';' : '/') - const value = frameValue.join(delemiter) - - this._setStringFrame(frameName, value) - break; - } - case 'TXXX': { // user defined text information - if (typeof frameValue !== 'object' || !('description' in frameValue) || !('value' in frameValue)) { - throw new Error('TXXX frame value should be an object with keys description and value'); - } - - if (Array.isArray(frameValue.value)) { - const delemiter = this.separator || '/'; - frameValue.value = frameValue.value.join(delemiter); - } - - this._setUserStringFrame(frameValue.description, frameValue.value); - break; - } - default: { - throw new Error(`Unsupported frame ${frameName} with array value`); - } - } - return this; - } -} - -module.exports = CustomID3Writer +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).ID3Writer=t()}(this,function(){"use strict";function r(e){return String(e).split("").map(function(e){return e.charCodeAt(0)})}function o(e){return new Uint8Array(r(e))}function c(e){var t=new Uint8Array(2*e.length);return new Uint16Array(t.buffer).set(r(e)),t}function u(e){var t=255;return[e>>>24&t,e>>>16&t,e>>>8&t,e&t]}return function(){var e=t.prototype;function t(e){if(!(e&&"object"==typeof e&&"byteLength"in e))throw new Error("First argument should be an instance of ArrayBuffer or Buffer");this.arrayBuffer=e,this.padding=4096,this.frames=[],this.url="",this.separateWithNull=!1}return e._setIntegerFrame=function(e,t){t=parseInt(t,10);this.frames.push({name:e,value:t,size:11+t.toString().length,__type__:"Integer"})},e._setStringFrame=function(e,t){t=t.toString();this.frames.push({name:e,value:t,size:13+2*t.length,__type__:"String"})},e._setPictureFrame=function(e,t,r,a){var n=function(e){if(!e||!e.length)return null;if(255===e[0]&&216===e[1]&&255===e[2])return"image/jpeg";if(137===e[0]&&80===e[1]&&78===e[2]&&71===e[3])return"image/png";if(71===e[0]&&73===e[1]&&70===e[2])return"image/gif";if(87===e[8]&&69===e[9]&&66===e[10]&&80===e[11])return"image/webp";var t=73===e[0]&&73===e[1]&&42===e[2]&&0===e[3],r=77===e[0]&&77===e[1]&&0===e[2]&&42===e[3];return t||r?"image/tiff":66===e[0]&&77===e[1]?"image/bmp":0===e[0]&&0===e[1]&&1===e[2]&&0===e[3]?"image/x-icon":null}(new Uint8Array(t)),i=r.toString();if(!n)throw new Error("Unknown picture MIME type");r||(a=!1),this.frames.push({name:"APIC",value:t,pictureType:e,mimeType:n,useUnicodeEncoding:a,description:i,size:(t=t.byteLength,n=n.length,i=i.length,11+n+1+1+(a?2+2*(i+1):i+1)+t),__type__:"Picture"})},e._setLyricsFrame=function(e,t,r){e=e.split("").map(function(e){return e.charCodeAt(0)}),t=t.toString(),r=r.toString();this.frames.push({name:"USLT",value:r,language:e,description:t,size:(t=t.length,r=r.length,16+2*t+2+2+2*r),__type__:"Lyrics"})},e._setCommentFrame=function(e,t,r){e=e.split("").map(function(e){return e.charCodeAt(0)}),t=t.toString(),r=r.toString();this.frames.push({name:"COMM",value:r,language:e,description:t,size:(t=t.length,r=r.length,16+2*t+2+2+2*r),__type__:"Comment"})},e._setPrivateFrame=function(e,t){e=e.toString();this.frames.push({name:"PRIV",value:t,id:e,size:(e=e.length,t=t.byteLength,10+e+1+t),__type__:"Private"})},e._setUserStringFrame=function(e,t){e=e.toString(),t=t.toString();this.frames.push({name:"TXXX",description:e,value:t,size:(e=e.length,t=t.length,13+2*e+2+2+2*t),__type__:"UserString"})},e._setUrlLinkFrame=function(e,t){t=t.toString();this.frames.push({name:e,value:t,size:10+t.length,__type__:"UrlLink"})},e._setPairedTextFrame=function(e,t){var r;this.frames.push({name:e,value:t,size:(r=0,t.forEach(function(e){r+=2+2*e[0].length+2+2+2*e[1].length+2}),11+r),__type__:"PairedText"})},e._setSynchronisedLyricsFrame=function(e,t,r,a){var n,a=a.split("").map(function(e){return e.charCodeAt(0)});this.frames.push({name:"SYLT",value:t,language:a,type:e,timestampFormat:r,size:(n=0,t.forEach(function(e){n+=2+2*e[0].length+2+4}),16+n),__type__:"SynchronisedLyrics"})},e.setFrame=function(e,t){switch(e){case"TPE1":case"TCOM":case"TPE2":case"TCON":if(!Array.isArray(t))throw new Error(e+" frame value should be an array of strings");var r=this.separateWithNull?String.fromCharCode(0):"TCON"===e?";":"/",r=t.join(r);this._setStringFrame(e,r);break;case"TLAN":case"TIT1":case"TIT2":case"TIT3":case"TALB":case"TPE3":case"TPE4":case"TRCK":case"TPOS":case"TMED":case"TPUB":case"TCOP":case"TCMP":case"TKEY":case"TEXT":case"TSRC":this._setStringFrame(e,t);break;case"TBPM":case"TLEN":case"TDAT":case"TYER":this._setIntegerFrame(e,t);break;case"USLT":if(t.language=t.language||"eng",!("object"==typeof t&&"description"in t&&"lyrics"in t))throw new Error("USLT frame value should be an object with keys description and lyrics");if(t.language&&!t.language.match(/[a-z]{3}/i))throw new Error("Language must be coded following the ISO 639-2 standards");this._setLyricsFrame(t.language,t.description,t.lyrics);break;case"APIC":if(!("object"==typeof t&&"type"in t&&"data"in t&&"description"in t))throw new Error("APIC frame value should be an object with keys type, data and description");if(t.type<0||20>>21&(r=127),e>>>14&r,e>>>7&r,e&r],n.set(s,i),i+=s.length,this.frames.forEach(function(e){switch(s=o(e.name),n.set(s,i),i+=s.length,s=u(e.size-10),n.set(s,i),i+=s.length,i+=2,e.__type__){case"UrlLink":s=o(e.value),n.set(s,i),i+=s.length;break;case"String":s=[1].concat(t),n.set(s,i),i+=s.length,s=c(e.value),n.set(s,i),i+=s.length;break;case"UserString":case"Lyrics":case"Comment":s=[1],s=(s="USLT"===e.name||"COMM"===e.name?s.concat(e.language):s).concat(t),n.set(s,i),i+=s.length,s=c(e.description),n.set(s,i),i+=s.length,s=[0,0].concat(t),n.set(s,i),i+=s.length,s=c(e.value),n.set(s,i),i+=s.length;break;case"Integer":i++,s=o(e.value),n.set(s,i),i+=s.length;break;case"Private":s=o(e.id),n.set(s,i),i+=s.length,i++,n.set(new Uint8Array(e.value),i),i+=e.value.byteLength;break;case"Picture":s=[e.useUnicodeEncoding?1:0],n.set(s,i),i+=s.length,s=o(e.mimeType),n.set(s,i),i+=s.length,s=[0,e.pictureType],n.set(s,i),i+=s.length,e.useUnicodeEncoding?(s=[].concat(t),n.set(s,i),i+=s.length,s=c(e.description),n.set(s,i),i+=s.length,i+=2):(s=o(e.description),n.set(s,i),i+=s.length,i++),n.set(new Uint8Array(e.value),i),i+=e.value.byteLength;break;case"PairedText":s=[1],n.set(s,i),i+=s.length,e.value.forEach(function(e){s=[].concat(t),n.set(s,i),i+=s.length,s=c(e[0].toString()),n.set(s,i),i+=s.length,s=[0,0].concat(t),n.set(s,i),i+=s.length,s=c(e[1].toString()),n.set(s,i),i+=s.length,s=[0,0],n.set(s,i),i+=s.length});break;case"SynchronisedLyrics":s=(s=(s=(s=[1]).concat(e.language)).concat(e.timestampFormat)).concat(e.type),n.set(s,i),i+=s.length,e.value.forEach(function(e){s=[].concat(t),n.set(s,i),i+=s.length,s=c(e[0].toString()),n.set(s,i),i+=s.length,s=[0,0],n.set(s,i),i+=s.length,s=u(e[1]),n.set(s,i),i+=s.length})}}),i+=this.padding,n.set(new Uint8Array(this.arrayBuffer),i),this.arrayBuffer=a},e.getBlob=function(){return new Blob([this.arrayBuffer],{type:"audio/mpeg"})},e.getURL=function(){return this.url||(this.url=URL.createObjectURL(this.getBlob())),this.url},e.revokeURL=function(){URL.revokeObjectURL(this.url)},t}()}); diff --git a/package.json b/package.json index c09a427..398a327 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ "license": "GPL-3.0-or-later", "dependencies": { "async": "^3.2.0", - "browser-id3-writer": "^4.4.0", "crypto": "^1.0.1", "deezer-js": "^0.0.8", "got": "^11.8.2", diff --git a/yarn.lock b/yarn.lock index 268e843..0ceafc8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -202,11 +202,6 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -browser-id3-writer@^4.4.0: - version "4.4.0" - resolved "https://registry.npmjs.org/browser-id3-writer/-/browser-id3-writer-4.4.0.tgz" - integrity sha512-8xce9wo4VoKNR4udEGOAf8vndYxhToqQS+1wyrjdYVPQKRc4Wm6xwGG6XrKYgax28y5AvrbCkqK6t1RplPN2Ew== - cacheable-lookup@^5.0.3: version "5.0.4" resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz"