museria 0.2.49 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc +10 -2
- package/.github/workflows/build.yml +3 -3
- package/.github/workflows/publish.yml +3 -3
- package/README.md +55 -59
- package/bin/actions.js +28 -28
- package/bin/index.js +4 -4
- package/bin/runner.js +1 -1
- package/bin/utils.js +6 -2
- package/dist/client/museria.client.js +7 -7
- package/dist/face/45a265d0f07b31cde85f.ttf +0 -0
- package/dist/face/6205fd00fb1b573e9f0f.ttf +0 -0
- package/dist/face/8d3cabfc66809162fb4d.woff2 +0 -0
- package/dist/face/fb8184add5a3101ad0a3.woff2 +0 -0
- package/dist/face/museria.face.js +33 -13
- package/dist/face/style.css +13 -11
- package/package.json +41 -40
- package/src/browser/client/index.js +2 -1
- package/src/browser/face/client.js +2 -1
- package/src/browser/face/controllers/app/app.html +77 -69
- package/src/browser/face/controllers/app/app.js +14 -7
- package/src/browser/face/controllers/app/app.scss +2 -22
- package/src/browser/face/index.js +3 -3
- package/src/browser/face/styles/main.scss +91 -11
- package/src/browser/face/styles/vars.scss +0 -1
- package/src/client.js +73 -74
- package/src/collection/transports/music/index.js +20 -18
- package/src/db/transports/database/index.js +7 -5
- package/src/db/transports/loki/index.js +30 -25
- package/src/errors.js +2 -1
- package/src/index.js +8 -6
- package/src/node.js +312 -323
- package/src/schema.js +27 -29
- package/src/server/transports/express/api/butler/controllers.js +7 -10
- package/src/server/transports/express/api/butler/routes.js +5 -5
- package/src/server/transports/express/api/master/controllers.js +7 -10
- package/src/server/transports/express/api/master/routes.js +5 -5
- package/src/server/transports/express/api/node/controllers.js +52 -61
- package/src/server/transports/express/api/node/routes.js +10 -10
- package/src/server/transports/express/api/routes.js +1 -1
- package/src/server/transports/express/api/slave/controllers.js +7 -10
- package/src/server/transports/express/api/slave/routes.js +6 -6
- package/src/server/transports/express/client/controllers.js +40 -61
- package/src/server/transports/express/client/routes.js +33 -39
- package/src/server/transports/express/controllers.js +10 -21
- package/src/server/transports/express/index.js +23 -20
- package/src/server/transports/express/midds.js +67 -67
- package/src/server/transports/express/routes.js +12 -12
- package/src/utils.js +175 -184
- package/test/client.js +311 -305
- package/test/db/database.js +32 -28
- package/test/db/loki.js +78 -74
- package/test/group.js +161 -156
- package/test/index.js +20 -10
- package/test/node.js +461 -460
- package/test/routes.js +404 -399
- package/test/server/express.js +35 -31
- package/test/services.js +25 -18
- package/test/tools.js +8 -6
- package/test/utils.js +236 -234
- package/webpack.client.js +9 -7
- package/webpack.face.js +8 -6
- package/dist/face/fa-brands-400.eot +0 -0
- package/dist/face/fa-brands-400.svg +0 -3717
- package/dist/face/fa-brands-400.ttf +0 -0
- package/dist/face/fa-brands-400.woff +0 -0
- package/dist/face/fa-brands-400.woff2 +0 -0
- package/dist/face/fa-solid-900.eot +0 -0
- package/dist/face/fa-solid-900.svg +0 -5034
- package/dist/face/fa-solid-900.ttf +0 -0
- package/dist/face/fa-solid-900.woff +0 -0
- package/dist/face/fa-solid-900.woff2 +0 -0
- /package/dist/face/{open-sans.ttf → 17e98b9e5586529b13cc.ttf} +0 -0
- /package/dist/face/{proxima-nova.ttf → 326601dfabd91e3f016c.ttf} +0 -0
- /package/dist/face/{logo.svg → ee9c6af64aa224827cec.svg} +0 -0
package/src/utils.js
CHANGED
@@ -1,18 +1,17 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
import NodeID3 from "node-id3";
|
2
|
+
import fse from "fs-extra";
|
3
|
+
import pick from "lodash-es/pick.js";
|
4
|
+
import utilsStoracle from "storacle/src/utils.js";
|
5
|
+
import utilsMetastocle from "metastocle/src/utils.js";
|
6
|
+
import emojiStrip from "emoji-strip";
|
7
|
+
import mm from "music-metadata";
|
8
|
+
import base64url from "base64url";
|
9
|
+
|
10
|
+
const utils = Object.assign({}, utilsStoracle, utilsMetastocle);
|
11
11
|
utils.regexSongLinks = /(([a-z]+:\/\/)?[-\p{L}\p{N}]+\.[\p{L}]{2,}|[a-z]+:\/\/(\[:*[\w\d]+:[\w\d:]+\]|\d+\.[\d.]+))\S*/igu;
|
12
12
|
utils.regexSongFeats = /[([\s]+((ft\.|feat\.)[\s]+((?!(\s+[-([)\]]+))[^)\]])+)\s*[)\]]*([\s]+[-([]+|$)/iu;
|
13
|
-
|
14
13
|
utils.heritableSongTags = [
|
15
|
-
'TALB', 'TCOM', 'TCON', 'TCOP', 'TDAT', 'TEXT', 'TIT1', 'TIT3', 'TLAN',
|
14
|
+
'TALB', 'TCOM', 'TCON', 'TCOP', 'TDAT', 'TEXT', 'TIT1', 'TIT3', 'TLAN',
|
16
15
|
'TOAL', 'TOLY', 'TOPE', 'TORY', 'TPE2', 'TPE3', 'TPE4', 'APIC'
|
17
16
|
];
|
18
17
|
|
@@ -23,51 +22,48 @@ utils.MusicDocumentsHandler = class extends utils.DocumentsHandler {
|
|
23
22
|
$mus(value, filter) {
|
24
23
|
return utils.getSongSimilarity(value, filter.value, { min: filter.similarity, beautify: filter.beautify }) >= filter.similarity;
|
25
24
|
}
|
26
|
-
|
27
25
|
$art(value, filter) {
|
28
26
|
filter = filter.toLowerCase();
|
29
27
|
const artists = utils.getSongArtists(value);
|
30
28
|
return !!artists.find(a => a.toLowerCase() == filter);
|
31
29
|
}
|
32
|
-
|
33
|
-
|
34
|
-
if(/^\s*[(]?(feat[.]?|remix?)\s*$/i.test(filter)) {
|
30
|
+
$milk(value, filter) {
|
31
|
+
if (/^\s*[(]?(feat[.]?|remix?)\s*$/i.test(filter)) {
|
35
32
|
return false;
|
36
33
|
}
|
37
|
-
|
38
34
|
return this.$ilk(value, filter);
|
39
35
|
}
|
40
|
-
}
|
36
|
+
};
|
41
37
|
|
42
38
|
/**
|
43
|
-
* @see
|
39
|
+
* @see utilsStoracle.getFileInfo
|
44
40
|
*/
|
45
41
|
utils.getFileInfo = async function () {
|
46
|
-
const info = await
|
42
|
+
const info = await utilsStoracle.getFileInfo.apply(this, arguments);
|
47
43
|
info.ext == 'mpga' && (info.ext = 'mp3');
|
48
44
|
return info;
|
49
|
-
}
|
45
|
+
};
|
50
46
|
|
51
47
|
/**
|
52
48
|
* Check the link is valid as an audio
|
53
|
-
*
|
54
|
-
* @see
|
49
|
+
*
|
50
|
+
* @see utilsStoracle.isValidFileLink
|
55
51
|
*/
|
56
52
|
utils.isValidSongAudioLink = function (link) {
|
57
|
-
if(typeof link != 'string' || !link.split('?')[0].match(/\.(mp3|mpeg|mpga)$/i)) {
|
53
|
+
if (typeof link != 'string' || !link.split('?')[0].match(/\.(mp3|mpeg|mpga)$/i)) {
|
58
54
|
return false;
|
59
55
|
}
|
60
56
|
|
61
|
-
return this.isValidFileLink(link, { action: 'audio'})
|
57
|
+
return this.isValidFileLink(link, { action: 'audio' });
|
62
58
|
};
|
63
59
|
|
64
60
|
/**
|
65
61
|
* Check the link is valid as a cover
|
66
|
-
*
|
67
|
-
* @see
|
62
|
+
*
|
63
|
+
* @see utilsStoracle.isValidFileLink
|
68
64
|
*/
|
69
65
|
utils.isValidSongCoverLink = function (link) {
|
70
|
-
if(typeof link != 'string' || !link.split('?')[0].match(/\.(jpe?g|png|jfif)$/i)) {
|
66
|
+
if (typeof link != 'string' || !link.split('?')[0].match(/\.(jpe?g|png|jfif)$/i)) {
|
71
67
|
return false;
|
72
68
|
}
|
73
69
|
|
@@ -76,46 +72,46 @@ utils.isValidSongCoverLink = function (link) {
|
|
76
72
|
|
77
73
|
/**
|
78
74
|
* Prepare the string to find songs
|
79
|
-
*
|
75
|
+
*
|
80
76
|
* @param {string} str
|
81
|
-
* @returns {string}
|
77
|
+
* @returns {string}
|
82
78
|
*/
|
83
79
|
utils.prepareSongFindingString = function (str) {
|
84
|
-
if(typeof str != 'string') {
|
80
|
+
if (typeof str != 'string') {
|
85
81
|
return '';
|
86
82
|
}
|
87
|
-
|
83
|
+
|
88
84
|
str = this.prepareComparisonSongTitle(str, { beautify: false })
|
89
85
|
.trim()
|
90
86
|
.replace(/[–—]+/g, '-')
|
91
87
|
.replace(/[\sᅠ]+/g, ' ');
|
92
88
|
return str;
|
93
|
-
}
|
89
|
+
};
|
94
90
|
|
95
91
|
/**
|
96
92
|
* Split the song title
|
97
|
-
*
|
93
|
+
*
|
98
94
|
* @param {string} title
|
99
|
-
* @returns {string[]}
|
95
|
+
* @returns {string[]}
|
100
96
|
*/
|
101
97
|
utils.splitSongTitle = function (title) {
|
102
|
-
if(typeof title != 'string') {
|
98
|
+
if (typeof title != 'string') {
|
103
99
|
return ['', ''];
|
104
100
|
}
|
105
101
|
|
106
102
|
const delim = ' - ';
|
107
103
|
const arr = title.split(delim);
|
108
104
|
return [arr[0], arr.slice(1).join(delim)];
|
109
|
-
}
|
105
|
+
};
|
110
106
|
|
111
107
|
/**
|
112
108
|
* Beautify the song title
|
113
|
-
*
|
109
|
+
*
|
114
110
|
* @param {string} title
|
115
|
-
* @returns {string}
|
111
|
+
* @returns {string}
|
116
112
|
*/
|
117
113
|
utils.beautifySongTitle = function (title) {
|
118
|
-
if(typeof title != 'string') {
|
114
|
+
if (typeof title != 'string') {
|
119
115
|
return '';
|
120
116
|
}
|
121
117
|
|
@@ -125,17 +121,17 @@ utils.beautifySongTitle = function (title) {
|
|
125
121
|
.replace(/[\sᅠ]+/g, ' ')
|
126
122
|
.replace(/([([])\s+/g, '$1')
|
127
123
|
.replace(/\s+([)\]])/g, '$1')
|
128
|
-
.replace(/([([]+)(featuring|feat|ft)(\s)/ig, '$1feat.$3')
|
124
|
+
.replace(/([([]+)(featuring|feat|ft)(\s)/ig, '$1feat.$3')
|
129
125
|
.replace(/([([\s]+)(feat\.|ft\.)(\s)/ig, '$1feat.$3')
|
130
126
|
.toLowerCase();
|
131
|
-
|
132
|
-
if(!/[^\s]+ - [^\s]+/.test(title)) {
|
127
|
+
|
128
|
+
if (!/[^\s]+ - [^\s]+/.test(title)) {
|
133
129
|
return '';
|
134
130
|
}
|
135
|
-
|
131
|
+
|
136
132
|
const arr = title.split(/\(?feat\./i);
|
137
133
|
|
138
|
-
if(arr.length > 2) {
|
134
|
+
if (arr.length > 2) {
|
139
135
|
arr.splice(2, arr.length - 2);
|
140
136
|
title = arr.join('(feat.').trim();
|
141
137
|
}
|
@@ -145,60 +141,60 @@ utils.beautifySongTitle = function (title) {
|
|
145
141
|
const mainArtist = artists[0];
|
146
142
|
artists.shift();
|
147
143
|
|
148
|
-
if(!mainArtist) {
|
144
|
+
if (!mainArtist) {
|
149
145
|
return '';
|
150
146
|
}
|
151
|
-
|
147
|
+
|
152
148
|
const match = sides[1].match(this.regexSongFeats);
|
153
|
-
let feats = (match? match[1]: '').replace(/,([^\s])/, ', $1').trim();
|
149
|
+
let feats = (match ? match[1] : '').replace(/,([^\s])/, ', $1').trim();
|
154
150
|
title = `${mainArtist} - ${sides[1]}`;
|
155
|
-
title = title.replace(this.regexSongFeats, '$5');
|
151
|
+
title = title.replace(this.regexSongFeats, '$5');
|
156
152
|
feats && (artists = artists.concat(feats.replace(/feat\./i, '').split(',')));
|
157
153
|
artists = [...new Set(artists.map(a => a.trim()).filter(v => v))];
|
158
|
-
|
159
|
-
if(artists.length) {
|
160
|
-
feats = `feat. ${ artists.join(', ') }`;
|
161
|
-
}
|
162
154
|
|
155
|
+
if (artists.length) {
|
156
|
+
feats = `feat. ${artists.join(', ')}`;
|
157
|
+
}
|
158
|
+
|
163
159
|
feats && (title += ` (${feats})`);
|
164
160
|
title = title
|
165
161
|
.replace(/\[\]|\(\)/g, '')
|
166
|
-
.replace(/\s+/g, ' ')
|
162
|
+
.replace(/\s+/g, ' ')
|
167
163
|
.split(' ')
|
168
|
-
.map(p => p? (p[0].toUpperCase() + p.slice(1)): p)
|
164
|
+
.map(p => p ? (p[0].toUpperCase() + p.slice(1)) : p)
|
169
165
|
.join(' ')
|
170
|
-
.trim();
|
166
|
+
.trim();
|
171
167
|
return title;
|
172
168
|
};
|
173
169
|
|
174
170
|
/**
|
175
171
|
* Prepare a comparison song title
|
176
|
-
*
|
172
|
+
*
|
177
173
|
* @param {string} title
|
178
174
|
* @returns {string}
|
179
175
|
*/
|
180
|
-
|
181
|
-
if(typeof title != 'string') {
|
176
|
+
utils.prepareComparisonSongTitle = function (title, options = {}) {
|
177
|
+
if (typeof title != 'string') {
|
182
178
|
return '';
|
183
179
|
}
|
184
|
-
|
180
|
+
|
185
181
|
options.beautify !== false && (title = this.beautifySongTitle(title));
|
186
182
|
return this.stringifyNumbers(this.normalizeString(title));
|
187
|
-
}
|
183
|
+
};
|
188
184
|
|
189
185
|
/**
|
190
186
|
* Check it is a right song title
|
191
|
-
*
|
187
|
+
*
|
192
188
|
* @param {string} title
|
193
189
|
* @param {object} [options]
|
194
|
-
* @returns {boolean}
|
190
|
+
* @returns {boolean}
|
195
191
|
*/
|
196
192
|
utils.isSongTitle = function (title, options = {}) {
|
197
|
-
if(options.beautify || options.beautify === undefined) {
|
193
|
+
if (options.beautify || options.beautify === undefined) {
|
198
194
|
title = this.beautifySongTitle(title);
|
199
195
|
}
|
200
196
|
|
201
|
-
if(typeof title != 'string' || Buffer.byteLength(title) > 1024) {
|
197
|
+
if (typeof title != 'string' || Buffer.byteLength(title) > 1024) {
|
202
198
|
return false;
|
203
199
|
}
|
204
200
|
|
@@ -207,76 +203,76 @@ utils.isSongTitle = function (title, options = {}) {
|
|
207
203
|
|
208
204
|
/**
|
209
205
|
* Get the song name
|
210
|
-
*
|
206
|
+
*
|
211
207
|
* @param {string} title
|
212
208
|
* @param {object} [options]
|
213
|
-
* @returns {string}
|
209
|
+
* @returns {string}
|
214
210
|
*/
|
215
|
-
utils.getSongName = function (title, options = {}) {
|
216
|
-
if(options.beautify || options.beautify === undefined) {
|
211
|
+
utils.getSongName = function (title, options = {}) {
|
212
|
+
if (options.beautify || options.beautify === undefined) {
|
217
213
|
title = this.beautifySongTitle(title);
|
218
214
|
}
|
219
215
|
|
220
|
-
if(!this.isSongTitle(title, { beautify: false })) {
|
216
|
+
if (!this.isSongTitle(title, { beautify: false })) {
|
221
217
|
return '';
|
222
218
|
}
|
223
|
-
|
219
|
+
|
224
220
|
return (this.splitSongTitle(title)[1] || '').replace(this.regexSongFeats, '$5').trim();
|
225
221
|
};
|
226
222
|
|
227
223
|
/**
|
228
224
|
* Get the song artists
|
229
|
-
*
|
225
|
+
*
|
230
226
|
* @param {string} title
|
231
227
|
* @param {object} [options]
|
232
|
-
* @returns {string[]}
|
228
|
+
* @returns {string[]}
|
233
229
|
*/
|
234
|
-
utils.getSongArtists = function (title, options = {}) {
|
235
|
-
if(options.beautify || options.beautify === undefined) {
|
230
|
+
utils.getSongArtists = function (title, options = {}) {
|
231
|
+
if (options.beautify || options.beautify === undefined) {
|
236
232
|
title = this.beautifySongTitle(title);
|
237
233
|
}
|
238
234
|
|
239
|
-
if(!this.isSongTitle(title, { beautify: false })) {
|
235
|
+
if (!this.isSongTitle(title, { beautify: false })) {
|
240
236
|
return [];
|
241
237
|
}
|
242
238
|
|
243
239
|
const sides = this.splitSongTitle(title);
|
244
240
|
let artists = sides[0].split(/,/);
|
245
241
|
const match = title.match(this.regexSongFeats);
|
246
|
-
let feats = (match? match[1]: '').replace(/^feat\./i, '');
|
242
|
+
let feats = (match ? match[1] : '').replace(/^feat\./i, '');
|
247
243
|
return [...new Set(artists.concat(feats.split(',')).map(v => v.trim()).filter(v => v))];
|
248
244
|
};
|
249
245
|
|
250
246
|
/**
|
251
247
|
* Get the song similarity
|
252
|
-
*
|
248
|
+
*
|
253
249
|
* @param {string} source
|
254
250
|
* @param {string} target
|
255
251
|
* @param {object} [options]
|
256
|
-
* @returns {float}
|
252
|
+
* @returns {float}
|
257
253
|
*/
|
258
254
|
utils.getSongSimilarity = function (source, target, options = {}) {
|
259
255
|
const tp = options.titlePriority || 0.5;
|
260
|
-
|
261
|
-
if(options.beautify || options.beautify === undefined) {
|
256
|
+
|
257
|
+
if (options.beautify || options.beautify === undefined) {
|
262
258
|
source = this.beautifySongTitle(source);
|
263
259
|
target = this.beautifySongTitle(target);
|
264
260
|
}
|
265
261
|
|
266
262
|
source = source.toLowerCase();
|
267
263
|
target = target.toLowerCase();
|
268
|
-
|
269
|
-
if(!source || !target) {
|
264
|
+
|
265
|
+
if (!source || !target) {
|
270
266
|
return 0;
|
271
267
|
}
|
272
|
-
|
268
|
+
|
273
269
|
const min = options.min || 0;
|
274
270
|
const mcoef = (min - 0.5) / 0.5;
|
275
271
|
const sourceName = this.getSongName(source, { beautify: false });
|
276
272
|
const targetName = this.getSongName(target, { beautify: false });
|
277
273
|
const t = this.getStringSimilarity(sourceName, targetName, { min: mcoef });
|
278
|
-
|
279
|
-
if(min && !t) {
|
274
|
+
|
275
|
+
if (min && !t) {
|
280
276
|
return 0;
|
281
277
|
}
|
282
278
|
|
@@ -286,28 +282,27 @@ utils.getSongSimilarity = function (source, target, options = {}) {
|
|
286
282
|
const targets = targetArtists.join(',');
|
287
283
|
const a = this.getStringSimilarity(sources, targets);
|
288
284
|
const res = (t * (1 + tp) + a * (1 - tp)) / 2;
|
289
|
-
return res >= min? res: 0;
|
285
|
+
return res >= min ? res : 0;
|
290
286
|
};
|
291
287
|
|
292
288
|
/**
|
293
289
|
* Create the song tags
|
294
|
-
*
|
290
|
+
*
|
295
291
|
* @param {object} [tags]
|
296
292
|
* @returns {object}
|
297
293
|
*/
|
298
294
|
utils.createSongTags = function (tags = {}) {
|
299
295
|
const obj = {};
|
300
296
|
const self = this;
|
301
|
-
|
302
297
|
Object.defineProperty(obj, 'fullTitle', {
|
303
298
|
enumerable: false,
|
304
299
|
get: function () {
|
305
|
-
return `${
|
300
|
+
return `${this.TPE1 || ''} - ${this.TIT2 || ''}`;
|
306
301
|
},
|
307
302
|
set: function (val) {
|
308
303
|
const title = self.beautifySongTitle(val);
|
309
|
-
|
310
|
-
if(!title) {
|
304
|
+
|
305
|
+
if (!title) {
|
311
306
|
delete this.TPE1;
|
312
307
|
delete this.TIT2;
|
313
308
|
return;
|
@@ -319,7 +314,7 @@ utils.createSongTags = function (tags = {}) {
|
|
319
314
|
}
|
320
315
|
});
|
321
316
|
|
322
|
-
for(let key in tags) {
|
317
|
+
for (let key in tags) {
|
323
318
|
obj[key] = tags[key];
|
324
319
|
}
|
325
320
|
|
@@ -328,7 +323,7 @@ utils.createSongTags = function (tags = {}) {
|
|
328
323
|
|
329
324
|
/**
|
330
325
|
* Merge the song tags
|
331
|
-
*
|
326
|
+
*
|
332
327
|
* @param {object} source
|
333
328
|
* @param {object} dest
|
334
329
|
* @returns {object}
|
@@ -346,42 +341,42 @@ utils.mergeSongTags = function (source, dest) {
|
|
346
341
|
|
347
342
|
/**
|
348
343
|
* Prepare the song tags to get
|
349
|
-
*
|
344
|
+
*
|
350
345
|
* @async
|
351
346
|
* @param {object} tags
|
352
347
|
* @returns {object}
|
353
348
|
*/
|
354
349
|
utils.prepareSongTagsToGet = async function (tags) {
|
355
350
|
tags = this.createSongTags(tags);
|
356
|
-
|
357
|
-
if(tags.APIC && typeof tags.APIC == 'object' && !Buffer.isBuffer(tags.APIC)) {
|
351
|
+
|
352
|
+
if (tags.APIC && typeof tags.APIC == 'object' && !Buffer.isBuffer(tags.APIC)) {
|
358
353
|
tags.APIC = tags.APIC.imageBuffer;
|
359
|
-
}
|
354
|
+
}
|
360
355
|
|
361
356
|
return tags;
|
362
357
|
};
|
363
358
|
|
364
359
|
/**
|
365
360
|
* Prepare the song tags to set
|
366
|
-
*
|
361
|
+
*
|
367
362
|
* @async
|
368
363
|
* @param {object} tags
|
369
364
|
* @returns {object}
|
370
365
|
*/
|
371
366
|
utils.prepareSongTagsToSet = async function (tags) {
|
372
367
|
tags = this.createSongTags(tags);
|
373
|
-
|
374
|
-
if(tags.image) {
|
368
|
+
|
369
|
+
if (tags.image) {
|
375
370
|
tags.APIC = tags.image;
|
376
371
|
delete tags.image;
|
377
372
|
}
|
378
|
-
|
379
|
-
if(this.isFileReadStream(tags.APIC)) {
|
373
|
+
|
374
|
+
if (this.isFileReadStream(tags.APIC)) {
|
380
375
|
tags.APIC.destroy();
|
381
|
-
tags.APIC = tags.APIC.path;
|
376
|
+
tags.APIC = tags.APIC.path;
|
382
377
|
}
|
383
378
|
|
384
|
-
if(typeof Blob == 'function' && tags.APIC instanceof Blob) {
|
379
|
+
if (typeof Blob == 'function' && tags.APIC instanceof Blob) {
|
385
380
|
tags.APIC = await this.blobToBuffer(tags.APIC);
|
386
381
|
}
|
387
382
|
|
@@ -390,76 +385,74 @@ utils.prepareSongTagsToSet = async function (tags) {
|
|
390
385
|
|
391
386
|
/**
|
392
387
|
* Prepare the song Blob file
|
393
|
-
*
|
388
|
+
*
|
394
389
|
* @async
|
395
390
|
* @param {Buffer} buffer
|
396
|
-
* @param {Blob|File} blob
|
391
|
+
* @param {Blob|File} blob
|
397
392
|
* @returns {Blob|File}
|
398
393
|
*/
|
399
394
|
utils.prepareSongBlobFile = async function (buffer, blob) {
|
400
395
|
const opts = { type: blob.type };
|
401
|
-
return blob instanceof File? new File([buffer], blob.name, opts): new Blob([buffer], opts);
|
396
|
+
return blob instanceof File ? new File([buffer], blob.name, opts) : new Blob([buffer], opts);
|
402
397
|
};
|
403
398
|
|
404
399
|
/**
|
405
400
|
* Get the song tags
|
406
|
-
*
|
401
|
+
*
|
407
402
|
* @async
|
408
|
-
* @param {string|Buffer|
|
409
|
-
* @returns {object}
|
403
|
+
* @param {string|Buffer|fse.ReadStream|Blob} file
|
404
|
+
* @returns {object}
|
410
405
|
*/
|
411
|
-
utils.getSongTags = async function (file) {
|
412
|
-
if(typeof Blob == 'function' && file instanceof Blob) {
|
406
|
+
utils.getSongTags = async function (file) {
|
407
|
+
if (typeof Blob == 'function' && file instanceof Blob) {
|
413
408
|
file = await this.blobToBuffer(file);
|
414
409
|
}
|
415
|
-
|
416
|
-
if(Buffer.isBuffer(file)) {
|
410
|
+
|
411
|
+
if (Buffer.isBuffer(file)) {
|
417
412
|
const tags = NodeID3.read(file);
|
418
|
-
return await this.prepareSongTagsToGet(tags? tags.raw: {});
|
413
|
+
return await this.prepareSongTagsToGet(tags ? tags.raw : {});
|
419
414
|
}
|
420
415
|
|
421
416
|
return new Promise((resolve, reject) => {
|
422
417
|
NodeID3.read(file.path || file, async (err, tags) => {
|
423
|
-
if(err) {
|
418
|
+
if (err) {
|
424
419
|
return reject(err);
|
425
420
|
}
|
426
|
-
|
427
421
|
resolve(await this.prepareSongTagsToGet(tags.raw || {}));
|
428
422
|
});
|
429
|
-
});
|
423
|
+
});
|
430
424
|
};
|
431
425
|
|
432
426
|
/**
|
433
427
|
* Get the song tags
|
434
|
-
*
|
428
|
+
*
|
435
429
|
* @async
|
436
|
-
* @param {string|Buffer|
|
430
|
+
* @param {string|Buffer|fse.ReadStream|Blob} file
|
437
431
|
* @param {object} tags
|
438
|
-
* @returns {string|Buffer|
|
432
|
+
* @returns {string|Buffer|fse.ReadStream|Blob}
|
439
433
|
*/
|
440
434
|
utils.setSongTags = async function (file, tags) {
|
441
435
|
tags = await this.prepareSongTagsToSet(tags);
|
442
|
-
|
443
|
-
if(typeof Blob == 'function' && file instanceof Blob) {
|
436
|
+
|
437
|
+
if (typeof Blob == 'function' && file instanceof Blob) {
|
444
438
|
const buffer = NodeID3.write(tags, await this.blobToBuffer(file));
|
445
439
|
return this.prepareSongBlobFile(buffer, file);
|
446
440
|
}
|
447
441
|
|
448
|
-
if(Buffer.isBuffer(file)) {
|
442
|
+
if (Buffer.isBuffer(file)) {
|
449
443
|
return NodeID3.write(tags, file);
|
450
444
|
}
|
451
445
|
|
452
446
|
return new Promise((resolve, reject) => {
|
453
447
|
NodeID3.write(tags, file.path || file, (err) => {
|
454
|
-
if(err) {
|
448
|
+
if (err) {
|
455
449
|
return reject(err);
|
456
450
|
}
|
457
451
|
|
458
|
-
if(file.path) {
|
452
|
+
if (file.path) {
|
459
453
|
file.destroy();
|
460
|
-
file =
|
454
|
+
file = fse.createReadStream(file.path);
|
461
455
|
}
|
462
|
-
|
463
456
|
resolve(file);
|
464
457
|
});
|
465
458
|
});
|
@@ -467,167 +460,165 @@ utils.setSongTags = async function (file, tags) {
|
|
467
460
|
|
468
461
|
/**
|
469
462
|
* Add the song tags
|
470
|
-
*
|
463
|
+
*
|
471
464
|
* @async
|
472
|
-
* @param {string|Buffer|
|
465
|
+
* @param {string|Buffer|fse.ReadStream|Blob} file
|
473
466
|
* @param {object} tags
|
474
|
-
* @returns {string|Buffer|
|
467
|
+
* @returns {string|Buffer|fse.ReadStream|Blob}
|
475
468
|
*/
|
476
469
|
utils.addSongTags = async function (file, tags) {
|
477
470
|
tags = await this.prepareSongTagsToSet(tags);
|
478
|
-
|
479
|
-
if(typeof Blob == 'function' && file instanceof Blob) {
|
471
|
+
|
472
|
+
if (typeof Blob == 'function' && file instanceof Blob) {
|
480
473
|
const buffer = NodeID3.update(tags, await this.blobToBuffer(file));
|
481
474
|
return this.prepareSongBlobFile(buffer, file);
|
482
475
|
}
|
483
476
|
|
484
|
-
if(Buffer.isBuffer(file)) {
|
477
|
+
if (Buffer.isBuffer(file)) {
|
485
478
|
return NodeID3.update(tags, file);
|
486
479
|
}
|
487
480
|
|
488
481
|
return new Promise((resolve, reject) => {
|
489
482
|
NodeID3.update(tags, file.path || file, (err) => {
|
490
|
-
if(err) {
|
483
|
+
if (err) {
|
491
484
|
return reject(err);
|
492
485
|
}
|
493
486
|
|
494
|
-
if(file.path) {
|
487
|
+
if (file.path) {
|
495
488
|
file.destroy();
|
496
|
-
file =
|
489
|
+
file = fse.createReadStream(file.path);
|
497
490
|
}
|
498
491
|
|
499
492
|
resolve(file);
|
500
493
|
});
|
501
|
-
});
|
494
|
+
});
|
502
495
|
};
|
503
496
|
|
504
497
|
/**
|
505
498
|
* Remove the song
|
506
|
-
*
|
499
|
+
*
|
507
500
|
* @async
|
508
|
-
* @param {string|Buffer|
|
509
|
-
* @returns {string|Buffer|
|
501
|
+
* @param {string|Buffer|fse.ReadStream|Blob} file
|
502
|
+
* @returns {string|Buffer|fse.ReadStream|Blob}
|
510
503
|
*/
|
511
504
|
utils.removeSongTags = async function (file) {
|
512
|
-
if(typeof Blob == 'function' && file instanceof Blob) {
|
505
|
+
if (typeof Blob == 'function' && file instanceof Blob) {
|
513
506
|
const buffer = NodeID3.removeTagsFromBuffer(await this.blobToBuffer(file));
|
514
507
|
return this.prepareSongBlobFile(buffer, file);
|
515
508
|
}
|
516
509
|
|
517
|
-
if(Buffer.isBuffer(file)) {
|
510
|
+
if (Buffer.isBuffer(file)) {
|
518
511
|
return NodeID3.removeTagsFromBuffer(file);
|
519
512
|
}
|
520
513
|
|
521
514
|
return new Promise((resolve, reject) => {
|
522
515
|
NodeID3.removeTags(file.path || file, (err) => {
|
523
|
-
if(err) {
|
516
|
+
if (err) {
|
524
517
|
return reject(err);
|
525
518
|
}
|
526
519
|
|
527
|
-
if(file.path) {
|
520
|
+
if (file.path) {
|
528
521
|
file.destroy();
|
529
|
-
file =
|
522
|
+
file = fse.createReadStream(file.path);
|
530
523
|
}
|
531
|
-
|
532
524
|
resolve(file);
|
533
525
|
});
|
534
|
-
});
|
526
|
+
});
|
535
527
|
};
|
536
528
|
|
537
529
|
/**
|
538
530
|
* Get the song metadata
|
539
|
-
*
|
531
|
+
*
|
540
532
|
* @async
|
541
|
-
* @param {string|Buffer|
|
542
|
-
* @returns {object}
|
533
|
+
* @param {string|Buffer|fse.ReadStream|Blob} file
|
534
|
+
* @returns {object}
|
543
535
|
*/
|
544
536
|
utils.getSongMetadata = async function (file) {
|
545
|
-
if(typeof Blob == 'function' && file instanceof Blob) {
|
546
|
-
file= await this.blobToBuffer(file);
|
537
|
+
if (typeof Blob == 'function' && file instanceof Blob) {
|
538
|
+
file = await this.blobToBuffer(file);
|
547
539
|
}
|
548
540
|
|
549
|
-
if(utils.isFileReadStream(file)) {
|
541
|
+
if (utils.isFileReadStream(file)) {
|
550
542
|
file = file.path;
|
551
543
|
}
|
552
|
-
|
553
|
-
const data = await mm[typeof file == 'string'? 'parseFile': 'parseBuffer'](file, { duration: true });
|
544
|
+
|
545
|
+
const data = await mm[typeof file == 'string' ? 'parseFile' : 'parseBuffer'](file, { duration: true });
|
554
546
|
return data.format;
|
555
547
|
};
|
556
548
|
|
557
549
|
/**
|
558
550
|
* Encode the song title
|
559
|
-
*
|
551
|
+
*
|
560
552
|
* @param {string} title
|
561
553
|
* @returns {string}
|
562
554
|
*/
|
563
555
|
utils.encodeSongTitle = function (title) {
|
564
556
|
return base64url(title);
|
565
|
-
}
|
557
|
+
};
|
566
558
|
|
567
559
|
/**
|
568
560
|
* Decode the song title
|
569
|
-
*
|
561
|
+
*
|
570
562
|
* @param {string} title
|
571
563
|
* @returns {string}
|
572
564
|
*/
|
573
565
|
utils.decodeSongTitle = function (title) {
|
574
566
|
return base64url.decode(title);
|
575
|
-
}
|
567
|
+
};
|
576
568
|
|
577
569
|
/**
|
578
570
|
* Check the value is a valid song priority
|
579
|
-
*
|
571
|
+
*
|
580
572
|
* @param {*} title
|
581
573
|
* @returns {boolean}
|
582
574
|
*/
|
583
575
|
utils.isValidSongPriority = function (value) {
|
584
576
|
return [0, 1, -1].includes(value);
|
585
|
-
}
|
577
|
+
};
|
586
578
|
|
587
579
|
/**
|
588
580
|
* Calculate two strings similarity
|
589
|
-
*
|
581
|
+
*
|
590
582
|
* @param {string} first
|
591
583
|
* @param {string} second
|
592
584
|
* @param {object} [options]
|
593
585
|
* @param {number} [options.min]
|
594
586
|
* @param {boolean} [options.ignoreOrder]
|
595
|
-
*
|
596
|
-
* @returns {number}
|
587
|
+
*
|
588
|
+
* @returns {number}
|
597
589
|
*/
|
598
|
-
utils.getStringSimilarity = function(first, second, options = {}) {
|
599
|
-
const min = options.min || 0;
|
590
|
+
utils.getStringSimilarity = function (first, second, options = {}) {
|
591
|
+
const min = options.min || 0;
|
600
592
|
let short = first;
|
601
593
|
let long = second;
|
602
594
|
|
603
|
-
if(second.length < first.length) {
|
595
|
+
if (second.length < first.length) {
|
604
596
|
short = second;
|
605
597
|
long = first;
|
606
598
|
}
|
607
|
-
|
599
|
+
|
608
600
|
long = long.toLowerCase().split('');
|
609
601
|
short = short.toLowerCase().split('');
|
610
602
|
const coef = Math.sqrt(short.length * long.length);
|
611
603
|
let matches = 0;
|
612
604
|
|
613
|
-
for(let i = 0; i < short.length; i++) {
|
605
|
+
for (let i = 0; i < short.length; i++) {
|
614
606
|
let index = -1;
|
615
607
|
let dist = 0;
|
616
608
|
|
617
|
-
while(dist < long.length) {
|
618
|
-
if(long[i + dist] === short[i]) {
|
609
|
+
while (dist < long.length) {
|
610
|
+
if (long[i + dist] === short[i]) {
|
619
611
|
index = i + dist;
|
620
612
|
break;
|
621
613
|
}
|
622
|
-
else if(long[i - dist] === short[i]) {
|
614
|
+
else if (long[i - dist] === short[i]) {
|
623
615
|
index = i - dist;
|
624
616
|
break;
|
625
617
|
}
|
626
|
-
|
627
618
|
dist++;
|
628
619
|
}
|
629
620
|
|
630
|
-
if(index != -1) {
|
621
|
+
if (index != -1) {
|
631
622
|
let coef = 1;
|
632
623
|
!options.ignoreOrder && (coef = 1 - Math.abs(index - i) / short.length);
|
633
624
|
matches += coef;
|
@@ -636,36 +627,36 @@ utils.getStringSimilarity = function(first, second, options = {}) {
|
|
636
627
|
|
637
628
|
const res = short.length + matches - i - 1;
|
638
629
|
|
639
|
-
if(res / coef < min) {
|
630
|
+
if (res / coef < min) {
|
640
631
|
return 0;
|
641
632
|
}
|
642
633
|
}
|
643
634
|
|
644
635
|
return matches / coef;
|
645
|
-
}
|
636
|
+
};
|
646
637
|
|
647
638
|
/**
|
648
639
|
* Normalize the string
|
649
|
-
*
|
640
|
+
*
|
650
641
|
* @param {string} str
|
651
642
|
* @returns {string}
|
652
643
|
*/
|
653
644
|
utils.normalizeString = function (str) {
|
654
645
|
return str.normalize("NFD").replace(/(?!\^)\p{Diacritic}/gu, "");
|
655
|
-
}
|
646
|
+
};
|
656
647
|
|
657
648
|
/**
|
658
649
|
* Stringfy numbers in the string
|
659
|
-
*
|
650
|
+
*
|
660
651
|
* @param {string} str
|
661
652
|
* @returns {string}
|
662
653
|
*/
|
663
654
|
utils.stringifyNumbers = function (str) {
|
664
655
|
const arr = [
|
665
|
-
'zero', 'one', 'two', 'three', 'four',
|
656
|
+
'zero', 'one', 'two', 'three', 'four',
|
666
657
|
'five', 'six', 'seven', 'eight', 'nine'
|
667
658
|
];
|
668
659
|
return str.replace(/[0-9]{1}/g, m => arr[+m]);
|
669
|
-
}
|
660
|
+
};
|
670
661
|
|
671
|
-
|
662
|
+
export default utils;
|