From a18c04acd4e4da9ba31c0a0ad98ef6290695e0ec Mon Sep 17 00:00:00 2001 From: RemixDev Date: Tue, 21 Sep 2021 13:54:02 +0200 Subject: [PATCH] Add POPM for ID3v2 & FLAC tag --- deemix/settings.js | 1 + deemix/tagger.js | 13 +++++++++++++ deemix/types/Track.js | 1 + deemix/utils/id3-writer.js | 2 +- 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/deemix/settings.js b/deemix/settings.js index 3ff5a45..1a68a8a 100644 --- a/deemix/settings.js +++ b/deemix/settings.js @@ -89,6 +89,7 @@ const DEFAULTS = { composer: false, involvedPeople: false, source: false, + rating: false, savePlaylistAsCompilation: false, useNullSeparator: false, saveID3v1: true, diff --git a/deemix/tagger.js b/deemix/tagger.js index c1c7d51..0f9a8e6 100644 --- a/deemix/tagger.js +++ b/deemix/tagger.js @@ -112,6 +112,14 @@ function tagID3(path, track, save){ }) } + if (save.rating) { + let rank = (track.rank / 10000) * 2.55; + rank = rank > 255 ? 255 : Math.round(rank); + tag.setFrame('POPM', { + rating: rank + }) + } + if (save.cover && track.album.embeddedCoverPath){ const coverArrayBuffer = fs.readFileSync(track.album.embeddedCoverPath) if (coverArrayBuffer.length != 0){ @@ -218,6 +226,11 @@ function tagFLAC(path, track, save){ flac.setTag(`SOURCEID=${track.id}`) } + if (save.rating) { + let rank = Math.round(track.rank / 10000); + flac.setTag(`RATING=${rank}`) + } + if (save.cover && track.album.embeddedCoverPath){ let picture = fs.readFileSync(track.album.embeddedCoverPath) if (picture.length != 0) flac.importPicture(picture) diff --git a/deemix/types/Track.js b/deemix/types/Track.js index 92f62cf..dcd610a 100644 --- a/deemix/types/Track.js +++ b/deemix/types/Track.js @@ -223,6 +223,7 @@ class Track { this.ISRC = trackAPI_gw.ISRC this.trackNumber = trackAPI_gw.TRACK_NUMBER this.contributors = trackAPI_gw.SNG_CONTRIBUTORS + this.rank = trackAPI_gw.RANK_SNG this.lyrics = new Lyrics(trackAPI_gw.LYRICS_ID || "0") diff --git a/deemix/utils/id3-writer.js b/deemix/utils/id3-writer.js index 9c9e760..a677d80 100644 --- a/deemix/utils/id3-writer.js +++ b/deemix/utils/id3-writer.js @@ -1 +1 @@ -!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="TDAT"===e?String(parseInt(t,10)).padStart(4,"0"):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}()}); \ No newline at end of file +!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="TDAT"===e?String(parseInt(t,10)).padStart(4,"0"):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._setPopularimeterFrame=function(e,t,r){var a=e.toString();t=parseInt(t,10),void 0!==r&&(r=parseInt(Math.abs(r),10)),this.frames.push({name:"POPM",value:t,email:a,count:r,size:10+e.length+1+1+(e=void(e=0)!==(r=r)?Math.max(Math.ceil(Math.log2(r+1)/8),4):e),__type__:"PopularimeterFrame"})},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});break;case"PopularimeterFrame":s=o(e.email),n.set(s,i),i+=s.length,i+=1,s=[e.value],n.set(s,i),i+=s.length,void 0!==e.count&&(s=function(e,t){for(var r=Math.ceil(Math.log2(e+1)/8),a=[],n=0;n