museria 0.2.48 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. package/.eslintrc +10 -2
  2. package/.github/workflows/build.yml +3 -3
  3. package/.github/workflows/publish.yml +3 -3
  4. package/README.md +56 -60
  5. package/bin/actions.js +28 -28
  6. package/bin/index.js +4 -4
  7. package/bin/runner.js +1 -1
  8. package/bin/utils.js +6 -2
  9. package/dist/client/museria.client.js +7 -7
  10. package/dist/face/45a265d0f07b31cde85f.ttf +0 -0
  11. package/dist/face/6205fd00fb1b573e9f0f.ttf +0 -0
  12. package/dist/face/8d3cabfc66809162fb4d.woff2 +0 -0
  13. package/dist/face/fb8184add5a3101ad0a3.woff2 +0 -0
  14. package/dist/face/museria.face.js +33 -13
  15. package/dist/face/style.css +13 -11
  16. package/package.json +41 -40
  17. package/src/browser/client/index.js +2 -1
  18. package/src/browser/face/client.js +2 -1
  19. package/src/browser/face/controllers/app/app.html +77 -69
  20. package/src/browser/face/controllers/app/app.js +14 -7
  21. package/src/browser/face/controllers/app/app.scss +2 -22
  22. package/src/browser/face/index.js +3 -3
  23. package/src/browser/face/styles/main.scss +91 -11
  24. package/src/browser/face/styles/vars.scss +0 -1
  25. package/src/client.js +73 -74
  26. package/src/collection/transports/music/index.js +20 -18
  27. package/src/db/transports/database/index.js +7 -5
  28. package/src/db/transports/loki/index.js +30 -25
  29. package/src/errors.js +2 -1
  30. package/src/index.js +8 -6
  31. package/src/node.js +315 -323
  32. package/src/schema.js +27 -29
  33. package/src/server/transports/express/api/butler/controllers.js +7 -10
  34. package/src/server/transports/express/api/butler/routes.js +5 -5
  35. package/src/server/transports/express/api/master/controllers.js +7 -10
  36. package/src/server/transports/express/api/master/routes.js +5 -5
  37. package/src/server/transports/express/api/node/controllers.js +52 -61
  38. package/src/server/transports/express/api/node/routes.js +10 -10
  39. package/src/server/transports/express/api/routes.js +1 -1
  40. package/src/server/transports/express/api/slave/controllers.js +7 -10
  41. package/src/server/transports/express/api/slave/routes.js +6 -6
  42. package/src/server/transports/express/client/controllers.js +40 -61
  43. package/src/server/transports/express/client/routes.js +33 -39
  44. package/src/server/transports/express/controllers.js +10 -21
  45. package/src/server/transports/express/index.js +23 -20
  46. package/src/server/transports/express/midds.js +67 -67
  47. package/src/server/transports/express/routes.js +12 -12
  48. package/src/utils.js +175 -184
  49. package/test/client.js +311 -305
  50. package/test/db/database.js +32 -28
  51. package/test/db/loki.js +78 -74
  52. package/test/group.js +161 -156
  53. package/test/index.js +20 -10
  54. package/test/node.js +461 -460
  55. package/test/routes.js +404 -399
  56. package/test/server/express.js +35 -31
  57. package/test/services.js +25 -18
  58. package/test/tools.js +8 -6
  59. package/test/utils.js +236 -234
  60. package/webpack.client.js +9 -7
  61. package/webpack.face.js +8 -6
  62. package/dist/face/fa-brands-400.eot +0 -0
  63. package/dist/face/fa-brands-400.svg +0 -3717
  64. package/dist/face/fa-brands-400.ttf +0 -0
  65. package/dist/face/fa-brands-400.woff +0 -0
  66. package/dist/face/fa-brands-400.woff2 +0 -0
  67. package/dist/face/fa-solid-900.eot +0 -0
  68. package/dist/face/fa-solid-900.svg +0 -5034
  69. package/dist/face/fa-solid-900.ttf +0 -0
  70. package/dist/face/fa-solid-900.woff +0 -0
  71. package/dist/face/fa-solid-900.woff2 +0 -0
  72. /package/dist/face/{open-sans.ttf → 17e98b9e5586529b13cc.ttf} +0 -0
  73. /package/dist/face/{proxima-nova.ttf → 326601dfabd91e3f016c.ttf} +0 -0
  74. /package/dist/face/{logo.svg → ee9c6af64aa224827cec.svg} +0 -0
package/src/node.js CHANGED
@@ -1,36 +1,42 @@
1
- const _ = require('lodash');
2
- const fs = require('fs');
3
- const path = require('path');
4
- const sharp = require('sharp');
5
- const fse = require('fs-extra');
6
- const qs = require('querystring');
7
- const SplayTree = require('splaytree');
8
- const DatabaseLokiMuseria = require('./db/transports/loki')();
9
- const ServerExpressMuseria = require('./server/transports/express')();
10
- const MusicCollection = require('./collection/transports/music')();
11
- const ApprovalCaptcha = require('spreadable/src/approval/transports/captcha')();
12
- const NodeMetastocle = require('metastocle/src/node')();
13
- const NodeStoracle = require('storacle/src/node')(NodeMetastocle);
14
- const schema = require('./schema');
15
- const utils = require('./utils');
16
- const errors = require('./errors');
17
- const pack = require('../package.json');
18
-
19
- module.exports = (Parent) => {
1
+ import { merge, omit, random, assign } from "lodash-es";
2
+ import path from "path";
3
+ import sharp from "sharp";
4
+ import fse from "fs-extra";
5
+ import qs from "querystring";
6
+ import SplayTree from "splaytree";
7
+ import databaseLokiMuseria from "./db/transports/loki/index.js";
8
+ import serverExpressMuseria from "./server/transports/express/index.js";
9
+ import musicCollection from "./collection/transports/music/index.js";
10
+ import approvalCaptcha from "spreadable/src/approval/transports/captcha/index.js";
11
+ import nodeMetastocle from "metastocle/src/node.js";
12
+ import nodeStoracle from "storacle/src/node.js";
13
+ import schema from "./schema.js";
14
+ import utils from "./utils.js";
15
+ import errors from "./errors.js";
16
+ import pack from "../package.json" with { type: "json" }
17
+
18
+ const DatabaseLokiMuseria = databaseLokiMuseria();
19
+ const ServerExpressMuseria = serverExpressMuseria();
20
+ const MusicCollection = musicCollection();
21
+ const ApprovalCaptcha = approvalCaptcha();
22
+ const NodeMetastocle = nodeMetastocle();
23
+ const NodeStoracle = nodeStoracle(NodeMetastocle);
24
+
25
+ export default (Parent) => {
20
26
  /**
21
27
  * Class to manage the node
22
28
  */
23
29
  return class NodeMuseria extends (Parent || NodeStoracle) {
24
- static get version () { return pack.version }
25
- static get codename () { return pack.name }
26
- static get DatabaseTransport () { return DatabaseLokiMuseria }
27
- static get ServerTransport () { return ServerExpressMuseria }
30
+ static get version() { return pack.version; }
31
+ static get codename() { return pack.name; }
32
+ static get DatabaseTransport() { return DatabaseLokiMuseria; }
33
+ static get ServerTransport() { return ServerExpressMuseria; }
28
34
 
29
35
  /**
30
36
  * @see NodeStoracle
31
37
  */
32
38
  constructor(options = {}) {
33
- options = _.merge({
39
+ options = merge({
34
40
  request: {
35
41
  fileStoringNodeTimeout: '10m'
36
42
  },
@@ -42,7 +48,7 @@ module.exports = (Parent) => {
42
48
  queue: true,
43
49
  loki: {
44
50
  unique: ['title', 'fileHash'],
45
- },
51
+ },
46
52
  limitationOrder: ['priority', '$accessedAt'],
47
53
  duplicationKey: 'fileHash',
48
54
  schema: schema.getMusicCollection()
@@ -57,12 +63,12 @@ module.exports = (Parent) => {
57
63
  relevanceTime: '14d',
58
64
  prepareTitle: true,
59
65
  prepareCover: true,
60
- coverQuality: 85,
66
+ coverQuality: 80,
61
67
  coverMinSize: 200,
62
68
  coverMaxSize: 500,
63
69
  coverMaxFileSize: '110kb'
64
70
  },
65
- storage: {
71
+ storage: {
66
72
  autoCleanSize: '30mb',
67
73
  tempLifetime: '10m',
68
74
  dataSize: '95% - 2gb',
@@ -74,14 +80,13 @@ module.exports = (Parent) => {
74
80
  mimeWhitelist: [
75
81
  'audio/mp3',
76
82
  'audio/mpeg',
77
- 'audio/mpeg3'
83
+ 'audio/mpeg3'
78
84
  ]
79
85
  },
80
86
  task: {
81
87
  cleanUpMusicInterval: '1m'
82
88
  }
83
89
  }, options);
84
-
85
90
  super(options);
86
91
  this.__addingFiles = {};
87
92
  }
@@ -90,51 +95,51 @@ module.exports = (Parent) => {
90
95
  * @see NodeStoracle.prototype.initBeforeSync
91
96
  */
92
97
  async initBeforeSync() {
93
- await super.initBeforeSync.apply(this, arguments);
98
+ await super.initBeforeSync.apply(this, arguments);
94
99
  await this.normalizeSongTitles();
95
100
  await this.cleanUpMusic();
96
- }
101
+ }
97
102
 
98
103
  /**
99
104
  * @see NodeStoracle.prototype.prepareServices
100
105
  */
101
106
  async prepareServices() {
102
107
  await super.prepareServices.apply(this, arguments);
103
- await this.addApproval('addSong', new ApprovalCaptcha({ period: this.options.request.fileStoringNodeTimeout }));
108
+ await this.addApproval('addSong', new ApprovalCaptcha({ period: this.options.request.fileStoringNodeTimeout }));
104
109
  await this.addCollection('music', new MusicCollection(this.options.collections.music));
105
110
  }
106
111
 
107
112
  /**
108
113
  * Prepare the task service
109
- *
114
+ *
110
115
  * @async
111
116
  */
112
- async prepareTask() {
117
+ async prepareTask() {
113
118
  await super.prepareTask.apply(this, arguments);
114
-
115
- if(!this.task) {
119
+
120
+ if (!this.task) {
116
121
  return;
117
122
  }
118
123
 
119
- if(this.options.task.cleanUpMusicInterval) {
124
+ if (this.options.task.cleanUpMusicInterval) {
120
125
  await this.task.add('cleanUpMusic', this.options.task.cleanUpMusicInterval, () => this.cleanUpMusic());
121
126
  }
122
127
  }
123
128
 
124
- /**
129
+ /**
125
130
  * Normalize the song titles
126
- *
131
+ *
127
132
  * @async
128
133
  */
129
134
  async normalizeSongTitles() {
130
135
  const docs = await this.db.getDocuments('music');
131
136
  const titles = {};
132
-
133
- for(let i = 0; i < docs.length; i++) {
137
+
138
+ for (let i = 0; i < docs.length; i++) {
134
139
  const doc = docs[i];
135
140
  const title = utils.beautifySongTitle(doc.title);
136
-
137
- if(titles[title] && titles[title] != doc.$loki) {
141
+
142
+ if (titles[title] && titles[title] != doc.$loki) {
138
143
  await this.db.deleteDocument(doc);
139
144
  continue;
140
145
  }
@@ -145,43 +150,38 @@ module.exports = (Parent) => {
145
150
  }
146
151
  }
147
152
 
148
- /**
153
+ /**
149
154
  * Clean up the music
150
- *
155
+ *
151
156
  * @async
152
157
  */
153
158
  async cleanUpMusic() {
154
159
  const docs = await this.db.getDocuments('music');
155
160
  const hashes = {};
156
161
 
157
- for(let i = 0; i < docs.length; i++) {
162
+ for (let i = 0; i < docs.length; i++) {
158
163
  const doc = docs[i];
159
164
 
160
- if(
161
- !doc.fileHash ||
165
+ if (!doc.fileHash ||
162
166
  typeof doc.fileHash != 'string' ||
163
- (
164
- !this.isFileAdding(doc.fileHash) &&
167
+ (!this.isFileAdding(doc.fileHash) &&
165
168
  !await this.hasFile(doc.fileHash) &&
166
- await this.db.getMusicByFileHash(doc.fileHash)
167
- )
168
- ) {
169
+ await this.db.getMusicByFileHash(doc.fileHash))) {
169
170
  await this.db.deleteDocument(doc);
170
171
  continue;
171
172
  }
172
173
 
173
174
  hashes[doc.fileHash] = true;
174
175
  }
175
-
176
- await this.iterateFiles(async filePath => {
176
+ await this.iterateFiles(async (filePath) => {
177
177
  try {
178
178
  const hash = path.basename(filePath);
179
-
180
- if(!hashes[hash] && !this.isFileAdding(hash) && !await this.db.getMusicByFileHash(hash)) {
179
+
180
+ if (!hashes[hash] && !this.isFileAdding(hash) && !await this.db.getMusicByFileHash(hash)) {
181
181
  await this.removeFileFromStorage(hash);
182
182
  }
183
183
  }
184
- catch(err) {
184
+ catch (err) {
185
185
  this.logger.warn(err.stack);
186
186
  }
187
187
  });
@@ -190,17 +190,17 @@ module.exports = (Parent) => {
190
190
  /**
191
191
  * @see NodeStoracle.prototype.calculateStorageInfo
192
192
  */
193
- async calculateStorageInfo() {
194
- let limit = this.options.collections.music.limit;
195
- await super.calculateStorageInfo.apply(this, arguments);
196
-
197
- if(limit != 'auto') {
193
+ async calculateStorageInfo() {
194
+ let limit = this.options.collections.music.limit;
195
+ await super.calculateStorageInfo.apply(this, arguments);
196
+
197
+ if (limit != 'auto') {
198
198
  return;
199
199
  }
200
-
200
+
201
201
  const filesTotalSize = await this.db.getData('filesTotalSize');
202
202
  const filesCount = await this.db.getData('filesCount');
203
- const avgSize = filesTotalSize && filesCount? filesTotalSize / filesCount: this.fileMaxSize;
203
+ const avgSize = filesTotalSize && filesCount ? filesTotalSize / filesCount : this.fileMaxSize;
204
204
  limit = Math.floor(this.storageDataSize / avgSize) - 1;
205
205
  limit < 1 && (limit = 1);
206
206
  const collection = await this.getCollection('music');
@@ -210,59 +210,59 @@ module.exports = (Parent) => {
210
210
  /**
211
211
  * @see NodeStoracle.prototype.getStatusInfo
212
212
  */
213
- async getStatusInfo(pretty = false) {
213
+ async getStatusInfo(pretty = false) {
214
214
  const collection = await this.getCollection('music');
215
- return _.merge(await super.getStatusInfo(pretty), { collectionLimit: collection.limit });
215
+ return merge(await super.getStatusInfo(pretty), { collectionLimit: collection.limit });
216
216
  }
217
217
 
218
218
  /**
219
219
  * @see NodeStoracle.prototype.getStorageCleaningUpTree
220
220
  */
221
- async getStorageCleaningUpTree() {
221
+ async getStorageCleaningUpTree() {
222
222
  const docs = await this.db.getDocuments('music');
223
223
  const hashes = {};
224
-
225
- for(let i = 0; i < docs.length; i++) {
224
+
225
+ for (let i = 0; i < docs.length; i++) {
226
226
  const doc = docs[i];
227
-
228
- if(!doc.fileHash || typeof doc.fileHash != 'string') {
227
+
228
+ if (!doc.fileHash || typeof doc.fileHash != 'string') {
229
229
  continue;
230
230
  }
231
231
 
232
232
  hashes[doc.fileHash] = doc;
233
233
  }
234
-
234
+
235
235
  const tree = new SplayTree((a, b) => {
236
- if(a.priority == b.priority) {
236
+ if (a.priority == b.priority) {
237
237
  return a.accessedAt - b.accessedAt;
238
238
  }
239
-
239
+
240
240
  return a.priority - b.priority;
241
241
  });
242
242
  await this.iterateFiles(async (filePath, stat) => {
243
243
  const hash = path.basename(filePath);
244
244
  const doc = hashes[hash] || await this.db.getMusicByFileHash(hash);
245
245
 
246
- if(!this.isFileAdding(hash)) {
247
- const accessedAt = doc? doc.$accessedAt: 0;
248
- const priority = doc? doc.priority: -1;
246
+ if (!this.isFileAdding(hash)) {
247
+ const accessedAt = doc ? doc.$accessedAt : 0;
248
+ const priority = doc ? doc.priority : -1;
249
249
  tree.insert({ accessedAt, priority }, { size: stat.size, path: filePath });
250
- }
250
+ }
251
251
  });
252
252
  return tree;
253
253
  }
254
-
254
+
255
255
  /**
256
256
  * Add the song
257
- *
257
+ *
258
258
  * @async
259
- * @param {string|Buffer|fs.ReadStream} file
259
+ * @param {string|Buffer|fse.ReadStream} file
260
260
  * @param {object} [options]
261
261
  * @returns {string}
262
262
  */
263
263
  async addSong(file, options = {}) {
264
264
  const destroyFileStream = () => utils.isFileReadStream(file) && file.destroy();
265
-
265
+
266
266
  try {
267
267
  options = Object.assign({
268
268
  priority: 0,
@@ -271,52 +271,48 @@ module.exports = (Parent) => {
271
271
  this.songPriorityTest(options);
272
272
  file = await this.prepareSongFileBeforeAddition(file);
273
273
  const timer = this.createRequestTimer(options.timeout);
274
- const collection = await this.getCollection('music');
274
+ const collection = await this.getCollection('music');
275
275
  const tags = await utils.getSongTags(file);
276
- this.songTitleTest(tags.fullTitle);
276
+ this.songTitleTest(tags.fullTitle);
277
277
  const fileInfo = await utils.getFileInfo(file);
278
278
  const info = { collection: 'music', pkValue: tags.fullTitle, fileInfo };
279
279
  const masterRequestTimeout = await this.getRequestMasterTimeout();
280
-
281
- if(typeof file == 'string') {
282
- file = fs.createReadStream(file);
280
+
281
+ if (typeof file == 'string') {
282
+ file = fse.createReadStream(file);
283
283
  }
284
284
 
285
285
  const results = await this.requestNetwork('get-document-addition-info', {
286
286
  body: { info },
287
- timeout: timer(
288
- [masterRequestTimeout, this.options.request.fileStoringNodeTimeout],
289
- { min: masterRequestTimeout, grabFree: true }
290
- ),
291
- responseSchema: schema.getDocumentAdditionInfoMasterResponse({
287
+ timeout: timer([masterRequestTimeout, this.options.request.fileStoringNodeTimeout], { min: masterRequestTimeout, grabFree: true }),
288
+ responseSchema: schema.getDocumentAdditionInfoMasterResponse({
292
289
  networkOptimum: await this.getNetworkOptimum(),
293
290
  schema: collection.schema
294
291
  })
295
292
  });
296
-
297
293
  const limit = await this.getDocumentDuplicatesCount(info);
298
294
  const filterOptions = Object.assign(await this.getDocumentAdditionInfoFilterOptions(info), { limit });
299
295
  const candidates = await this.filterCandidatesMatrix(results.map(r => r.candidates), filterOptions);
300
-
301
- if(!candidates.length) {
296
+
297
+ if (!candidates.length) {
302
298
  throw new errors.WorkError('Not found a suitable server to store the song', 'ERR_MUSERIA_NOT_FOUND_STORAGE');
303
299
  }
304
300
 
305
301
  const suspicious = candidates.filter(c => !c.existenceInfo)[0];
306
302
  suspicious && await this.db.addBehaviorCandidate('addSong', suspicious.address);
307
- const servers = candidates.map(c => c.address);
303
+ const servers = candidates.map(c => c.address);
308
304
  const dupOptions = Object.assign({}, options, { timeout: timer() });
309
- const dupInfo = Object.assign({ title: tags.fullTitle }, fileInfo);
305
+ const dupInfo = Object.assign({ title: tags.fullTitle }, fileInfo);
310
306
  const result = await this.duplicateSong(servers, file, dupInfo, dupOptions);
311
307
 
312
- if(!result) {
308
+ if (!result) {
313
309
  throw new errors.WorkError('Not found an available server to store the file', 'ERR_MUSERIA_NOT_FOUND_STORAGE');
314
310
  }
315
311
 
316
312
  destroyFileStream();
317
- return _.omit(result, ['address']);
313
+ return omit(result, ['address']);
318
314
  }
319
- catch(err) {
315
+ catch (err) {
320
316
  destroyFileStream();
321
317
  throw err;
322
318
  }
@@ -324,36 +320,36 @@ module.exports = (Parent) => {
324
320
 
325
321
  /**
326
322
  * Prepare the song file before the addition
327
- *
323
+ *
328
324
  * @async
329
- * @param {string|Buffer|fs.ReadStream} file
330
- * @returns {string|Buffer|fs.ReadStream}
325
+ * @param {string|Buffer|fse.ReadStream} file
326
+ * @returns {string|Buffer|fse.ReadStream}
331
327
  */
332
328
  async prepareSongFileBeforeAddition(file) {
333
329
  const tags = await utils.getSongTags(file);
334
330
  let changed = false;
335
-
336
- if(this.options.music.prepareTitle) {
331
+
332
+ if (this.options.music.prepareTitle) {
337
333
  this.songTitleTest(tags.fullTitle);
338
- tags.fullTitle = await this.prepareSongTitle(tags.fullTitle);
334
+ tags.fullTitle = await this.prepareSongTitle(tags.fullTitle);
339
335
  changed = true;
340
336
  }
341
337
 
342
- if(tags.APIC && this.options.music.prepareCover) {
338
+ if (tags.APIC && this.options.music.prepareCover) {
343
339
  tags.APIC = await this.prepareSongCover(tags.APIC);
344
340
  changed = true;
345
341
  }
346
342
 
347
- if(!changed) {
343
+ if (!changed) {
348
344
  return file;
349
345
  }
350
-
346
+
351
347
  return await utils.setSongTags(file, tags);
352
348
  }
353
349
 
354
350
  /**
355
351
  * Prepare the song cover
356
- *
352
+ *
357
353
  * @async
358
354
  * @param {Buffer} buffer
359
355
  * @returns {Buffer}
@@ -367,48 +363,51 @@ module.exports = (Parent) => {
367
363
  let width = metadata.width;
368
364
  let height = metadata.height;
369
365
 
370
- if(minSize && (width < minSize || height < minSize )) {
366
+ if (minSize && (width < minSize || height < minSize)) {
371
367
  throw new errors.WorkError(`Minimum size of a cover width or height is ${minSize}px`, 'ERR_MUSERIA_COVER_MIN_SIZE');
372
- }
368
+ }
373
369
 
374
- let dev;
370
+ let dev;
375
371
  let maxDev;
376
-
377
- if(width > maxSize) {
372
+
373
+ if (width > maxSize) {
378
374
  maxDev = height / maxSize;
379
375
  dev = width / maxSize;
380
376
  }
381
377
  else {
382
378
  maxDev = width / maxSize;
383
- dev = height / maxSize;
379
+ dev = height / maxSize;
384
380
  }
385
381
 
386
382
  dev > maxDev && (dev = maxDev);
387
383
  width = Math.floor(width / dev);
388
- height = Math.floor(height / dev);
389
- const size = width > height? height: width;
390
-
391
- const buff = await image
384
+ height = Math.floor(height / dev);
385
+ const size = width > height ? height : width;
386
+ let buff = await image
392
387
  .jpeg({ quality: this.options.music.coverQuality })
393
388
  .resize(width, height)
394
- .extract({
389
+ .extract({
395
390
  left: Math.floor((width - size) / 2),
396
391
  top: Math.floor((height - size) / 2),
397
392
  width: size,
398
393
  height: size
399
394
  })
400
395
  .toBuffer();
396
+
397
+ if (buff.byteLength > metadata.size) {
398
+ buff = buffer;
399
+ }
401
400
 
402
- if(buff.byteLength > maxFileSize) {
401
+ if (buff.byteLength > maxFileSize) {
403
402
  throw new errors.WorkError(`Maximum size of a cover file is ${maxFileSize} byte(s)`, 'ERR_MUSERIA_COVER_MAX_FILE_SIZE');
404
- }
403
+ }
405
404
 
406
405
  return buff;
407
406
  }
408
407
 
409
408
  /**
410
409
  * Prepare the song title
411
- *
410
+ *
412
411
  * @async
413
412
  * @param {string} title
414
413
  * @returns {string}
@@ -416,23 +415,23 @@ module.exports = (Parent) => {
416
415
  async prepareSongTitle(title) {
417
416
  return utils.beautifySongTitle(title);
418
417
  }
419
-
418
+
420
419
  /**
421
- * Get the song info
422
- *
420
+ * Get the song info
421
+ *
423
422
  * @async
424
423
  * @param {string} title
425
424
  * @param {object} [options]
426
425
  * @returns {object[]}
427
426
  */
428
- async getSongInfo(title, options = {}) {
427
+ async getSongInfo(title, options = {}) {
429
428
  title = utils.prepareComparisonSongTitle(title);
430
429
  const collection = await this.getCollection('music');
431
- const actions = utils.prepareDocumentGettingActions({
430
+ const actions = utils.prepareDocumentGettingActions({
432
431
  offset: 0,
433
432
  limit: 0,
434
433
  removeDuplicates: false,
435
- filter: {
434
+ filter: {
436
435
  compTitle: {
437
436
  $mus: {
438
437
  value: title,
@@ -442,13 +441,13 @@ module.exports = (Parent) => {
442
441
  }
443
442
  },
444
443
  sort: this.getFindingSongsSort()
445
- });
444
+ });
446
445
  await collection.actionsGettingTest(actions);
447
446
  const results = await this.requestNetwork('get-documents', {
448
447
  body: { actions, collection: 'music' },
449
448
  timeout: options.timeout,
450
449
  responseSchema: schema.getDocumentsMasterResponse({ schema: collection.schema })
451
- });
450
+ });
452
451
  results.forEach((result) => {
453
452
  result.documents.forEach(doc => {
454
453
  doc.main = 1;
@@ -456,51 +455,51 @@ module.exports = (Parent) => {
456
455
  doc.intScore = parseInt(doc.score * 100);
457
456
  doc.random = Math.random();
458
457
  });
459
- })
458
+ });
460
459
  const result = await this.handleDocumentsGettingForClient(collection, results, actions);
461
- const documents = result.documents.map(doc => _.omit(doc, ['main', 'address', 'random', 'intScore', 'compTitle']));
460
+ const documents = result.documents.map(doc => omit(doc, ['main', 'address', 'random', 'intScore', 'compTitle']));
462
461
  return documents;
463
462
  }
464
463
 
465
464
  /**
466
465
  * Find songs
467
- *
466
+ *
468
467
  * @async
469
468
  * @param {string} str
470
469
  * @param {object} [options]
471
470
  * @returns {object[]}
472
471
  */
473
- async findSongs(str, options = {}) {
472
+ async findSongs(str, options = {}) {
474
473
  const title = utils.prepareComparisonSongTitle(str);
475
474
  str = utils.prepareSongFindingString(str);
476
-
477
- if(str.length < this.options.music.findingStringMinLength) {
478
- const msg = `You have to pass at least "${ this.options.music.findingStringMinLength }" symbol(s)`;
475
+
476
+ if (str.length < this.options.music.findingStringMinLength) {
477
+ const msg = `You have to pass at least "${this.options.music.findingStringMinLength}" symbol(s)`;
479
478
  throw new errors.WorkError(msg, 'ERR_MUSERIA_FINDING_SONGS_STRING_LENGTH');
480
479
  }
481
-
482
- if(!str) {
480
+
481
+ if (!str) {
483
482
  return [];
484
483
  }
485
-
484
+
486
485
  const collection = await this.getCollection('music');
487
- let limit = options.limit === undefined? this.options.music.findingLimit: options.limit;
486
+ let limit = options.limit === undefined ? this.options.music.findingLimit : options.limit;
488
487
  limit > this.options.music.findingLimit && (limit = this.options.music.findingLimit);
489
- limit < 0 && (limit = 0);
490
- const actions = utils.prepareDocumentGettingActions({
488
+ limit < 0 && (limit = 0);
489
+ const actions = utils.prepareDocumentGettingActions({
491
490
  offset: 0,
492
491
  limit,
493
492
  removeDuplicates: true,
494
- filter: {
493
+ filter: {
495
494
  compTitle: {
496
495
  $or: [
497
496
  { $milk: str },
498
- {
497
+ {
499
498
  $mus: {
500
499
  value: title,
501
500
  similarity: this.options.music.similarity,
502
501
  beautify: false
503
- }
502
+ }
504
503
  }
505
504
  ]
506
505
  }
@@ -511,83 +510,82 @@ module.exports = (Parent) => {
511
510
  body: { actions, collection: 'music' },
512
511
  timeout: options.timeout,
513
512
  responseSchema: schema.getDocumentsMasterResponse({ schema: collection.schema })
514
- });
515
-
513
+ });
516
514
  const titles = {};
517
- let documents = results.reduce((p, c) => p.concat(c.documents), []);
515
+ let documents = results.reduce((p, c) => p.concat(c.documents), []);
518
516
  documents = this.uniqDocuments(collection, documents);
519
517
  documents.forEach((doc) => {
520
518
  doc.main = 0;
521
519
  doc.score = utils.getStringSimilarity(str, doc.compTitle, { ignoreOrder: true });
522
520
  doc.intScore = parseInt(doc.score * 100);
523
521
  doc.random = Math.random();
524
- titles[doc.title]? titles[doc.title].push(doc): titles[doc.title] = [doc];
522
+ titles[doc.title] ? titles[doc.title].push(doc) : titles[doc.title] = [doc];
525
523
  });
526
-
527
- for(let key in titles) {
524
+
525
+ for (let key in titles) {
528
526
  const docs = titles[key];
529
- docs[_.random(0, docs.length - 1)].main = 1;
527
+ docs[random(0, docs.length - 1)].main = 1;
530
528
  }
531
529
 
532
530
  actions.removeDuplicates = false;
533
531
  const result = await this.handleDocumentsGettingForClient(collection, [{ documents }], actions);
534
- documents = result.documents.map(doc => _.omit(doc, ['main', 'address', 'random', 'intScore', 'compTitle']));
532
+ documents = result.documents.map(doc => omit(doc, ['main', 'address', 'random', 'intScore', 'compTitle']));
535
533
  return documents;
536
534
  }
537
535
 
538
536
  /**
539
537
  * Find the artist songs
540
- *
538
+ *
541
539
  * @async
542
540
  * @param {string} artist
543
541
  * @param {object} [options]
544
542
  * @returns {object[]}
545
543
  */
546
- async findArtistSongs(artist, options = {}) {
547
- if(!artist || typeof artist != 'string') {
548
- return [];
549
- }
550
-
551
- artist = utils.prepareSongFindingString(artist);
552
- const collection = await this.getCollection('music');
553
- const actions = utils.prepareDocumentGettingActions({
554
- offset: 0,
555
- limit: 0,
556
- removeDuplicates: true,
557
- filter: {
558
- compTitle: {
559
- $art: artist
560
- }
561
- },
562
- sort: this.getFindingSongsSort()
563
- });
564
- const results = await this.requestNetwork('get-documents', {
565
- body: { actions, collection: 'music' },
566
- timeout: options.timeout,
567
- responseSchema: schema.getDocumentsMasterResponse({ schema: collection.schema })
568
- });
569
- const titles = {};
570
- let documents = results.reduce((p, c) => p.concat(c.documents), []);
571
- documents = this.uniqDocuments(collection, documents);
572
- documents.forEach((doc) => {
573
- doc.main = 0;
574
- titles[doc.title]? titles[doc.title].push(doc): titles[doc.title] = [doc];
575
- });
576
-
577
- for(let key in titles) {
578
- const docs = titles[key];
579
- docs[_.random(0, docs.length - 1)].main = 1;
580
- }
544
+ async findArtistSongs(artist, options = {}) {
545
+ if (!artist || typeof artist != 'string') {
546
+ return [];
547
+ }
581
548
 
582
- actions.removeDuplicates = false;
583
- const result = await this.handleDocumentsGettingForClient(collection, [{ documents }], actions);
584
- documents = result.documents.map(doc => _.omit(doc, ['main', 'address']));
585
- return documents;
549
+ artist = utils.prepareSongFindingString(artist);
550
+ const collection = await this.getCollection('music');
551
+ const actions = utils.prepareDocumentGettingActions({
552
+ offset: 0,
553
+ limit: 0,
554
+ removeDuplicates: true,
555
+ filter: {
556
+ compTitle: {
557
+ $art: artist
558
+ }
559
+ },
560
+ sort: this.getFindingSongsSort()
561
+ });
562
+ const results = await this.requestNetwork('get-documents', {
563
+ body: { actions, collection: 'music' },
564
+ timeout: options.timeout,
565
+ responseSchema: schema.getDocumentsMasterResponse({ schema: collection.schema })
566
+ });
567
+ const titles = {};
568
+ let documents = results.reduce((p, c) => p.concat(c.documents), []);
569
+ documents = this.uniqDocuments(collection, documents);
570
+ documents.forEach((doc) => {
571
+ doc.main = 0;
572
+ titles[doc.title] ? titles[doc.title].push(doc) : titles[doc.title] = [doc];
573
+ });
574
+
575
+ for (let key in titles) {
576
+ const docs = titles[key];
577
+ docs[random(0, docs.length - 1)].main = 1;
586
578
  }
587
579
 
580
+ actions.removeDuplicates = false;
581
+ const result = await this.handleDocumentsGettingForClient(collection, [{ documents }], actions);
582
+ documents = result.documents.map(doc => omit(doc, ['main', 'address']));
583
+ return documents;
584
+ }
585
+
588
586
  /**
589
587
  * Get the song link
590
- *
588
+ *
591
589
  * @async
592
590
  * @param {string} title
593
591
  * @param {string} type
@@ -595,53 +593,52 @@ module.exports = (Parent) => {
595
593
  * @returns {string}
596
594
  */
597
595
  async getSongLink(title, type, options = {}) {
598
- if(type != 'audio' && type != 'cover') {
596
+ if (type != 'audio' && type != 'cover') {
599
597
  throw new errors.WorkError(`Link type must be "audio" or "cover", not "${type}"`, 'ERR_MUSERIA_SONG_LINK_TYPE');
600
598
  }
601
599
 
602
600
  this.songTitleTest(title);
603
- options = _.merge({
601
+ options = merge({
604
602
  cache: true
605
603
  }, options);
606
- title = utils.beautifySongTitle(title);
607
-
608
- LOOKING_FOR_CACHE: if(this.cacheFile && options.cache) {
604
+ title = utils.beautifySongTitle(title);
605
+
606
+ LOOKING_FOR_CACHE: if (this.cacheFile && options.cache) {
609
607
  const cache = await this.cacheFile.get(title);
610
-
611
- if(!cache) {
608
+ if (!cache) {
612
609
  break LOOKING_FOR_CACHE;
613
610
  }
614
611
 
615
612
  const link = cache.value[`${type}Link`];
616
-
617
- if(await this.checkCacheLink(link)) {
613
+
614
+ if (await this.checkCacheLink(link)) {
618
615
  return link;
619
616
  }
620
617
 
621
- const obj = _.merge({}, cache.value, { [`${type}Link`]: '' });
618
+ const obj = merge({}, cache.value, { [`${type}Link`]: '' });
622
619
 
623
- if(!obj.audioLink && !obj.coverLink) {
620
+ if (!obj.audioLink && !obj.coverLink) {
624
621
  await this.cacheFile.remove(title);
625
622
  break LOOKING_FOR_CACHE;
626
623
  }
627
624
 
628
625
  await this.cacheFile.set(title, obj);
629
- }
626
+ }
630
627
 
631
- const info = (await this.getSongInfo(title, options)).filter(c => c[`${type}Link`]);
628
+ const info = (await this.getSongInfo(title, options)).filter(c => c[`${type}Link`]);
632
629
  const selected = info[0];
633
-
634
- if(options.cache && selected) {
630
+
631
+ if (options.cache && selected) {
635
632
  await this.updateSongCache(title, selected);
636
633
  selected.title != title && await this.updateSongCache(selected.title, selected);
637
634
  }
638
-
639
- return selected? selected[`${type}Link`]: '';
635
+
636
+ return selected ? selected[`${type}Link`] : '';
640
637
  }
641
638
 
642
639
  /**
643
640
  * Get the song audio link
644
- *
641
+ *
645
642
  * @see NodeMuseria.prototype.getSongAudioLink
646
643
  */
647
644
  async getSongAudioLink(title, options = {}) {
@@ -650,7 +647,7 @@ module.exports = (Parent) => {
650
647
 
651
648
  /**
652
649
  * Get the song cover link
653
- *
650
+ *
654
651
  * @see NodeMuseria.prototype.getSongCoverLink
655
652
  */
656
653
  async getSongCoverLink(title, options = {}) {
@@ -659,22 +656,22 @@ module.exports = (Parent) => {
659
656
 
660
657
  /**
661
658
  * Get the song info filter options
662
- *
659
+ *
663
660
  * @async
664
661
  * @returns {object}
665
662
  */
666
663
  async getSongInfoFilterOptions() {
667
664
  return {
668
665
  fnFilter: c => (
669
- utils.isValidSongAudioLink(c.audioLink) &&
666
+ utils.isValidSongAudioLink(c.audioLink) &&
670
667
  (!c.coverLink || utils.isValidSongCoverLink(c.coverLink))
671
668
  )
672
- }
669
+ };
673
670
  }
674
671
 
675
672
  /**
676
673
  * Remove the song
677
- *
674
+ *
678
675
  * @async
679
676
  * @param {string} title
680
677
  * @param {object} [options]
@@ -692,28 +689,28 @@ module.exports = (Parent) => {
692
689
 
693
690
  /**
694
691
  * Update the song cache
695
- *
692
+ *
696
693
  * @async
697
694
  * @param {string} title
698
695
  * @param {object} value
699
696
  * @param {string} value.audioLink
700
697
  * @param {string} [value.coverLink]
701
698
  */
702
- async updateSongCache(title, value) {
703
- if(!this.cacheFile) {
699
+ async updateSongCache(title, value) {
700
+ if (!this.cacheFile) {
704
701
  return;
705
702
  }
706
-
707
- const cache = await this.cacheFile.get(title);
708
- let obj = { audioLink: value.audioLink, coverLink: value.coverLink };
703
+
704
+ const cache = await this.cacheFile.get(title);
705
+ let obj = { audioLink: value.audioLink, coverLink: value.coverLink };
709
706
  !utils.isValidSongAudioLink(obj.audioLink) && delete obj.audioLink;
710
707
  !utils.isValidSongCoverLink(obj.coverLink) && delete obj.coverLink;
711
- obj = _.merge(cache? cache.value: {}, obj);
708
+ obj = merge(cache ? cache.value : {}, obj);
712
709
 
713
- if(!Object.keys(obj).length) {
710
+ if (!Object.keys(obj).length) {
714
711
  return;
715
712
  }
716
-
713
+
717
714
  await this.cacheFile.set(title, obj);
718
715
  }
719
716
 
@@ -722,30 +719,29 @@ module.exports = (Parent) => {
722
719
  */
723
720
  async duplicateSong(servers, file, info, options = {}) {
724
721
  const query = qs.stringify({ title: info.title });
725
- options = _.assign({}, {
722
+ options = assign({}, {
726
723
  cache: true,
727
- action: `add-song?${ query }`,
728
- formData: {
729
- exported: options.exported? '1': '',
730
- controlled: options.controlled? '1': '',
731
- priority: String(options.priority || 0),
732
- approvalInfo: options.approvalInfo? JSON.stringify(options.approvalInfo): ''
724
+ action: `add-song?${query}`,
725
+ formData: {
726
+ exported: options.exported ? '1' : '',
727
+ controlled: options.controlled ? '1' : '',
728
+ priority: String(options.priority || 0),
729
+ approvalInfo: options.approvalInfo ? JSON.stringify(options.approvalInfo) : ''
733
730
  },
734
731
  responseSchema: schema.getSongAdditionResponse()
735
732
  }, options);
736
-
737
- const result = await super.duplicateFile(servers, file, info, _.omit(options, ['priority']));
733
+ const result = await super.duplicateFile(servers, file, info, omit(options, ['priority']));
738
734
  result && options.cache && await this.updateSongCache(result.title, result);
739
735
  return result;
740
736
  }
741
737
 
742
738
  /**
743
739
  * Export all songs to another server
744
- *
740
+ *
745
741
  * @see NodeStoracle.prototype.exportFiles
746
742
  */
747
- async exportSongs(address, options = {}) {
748
- options = _.merge({
743
+ async exportSongs(address, options = {}) {
744
+ options = merge({
749
745
  strict: false
750
746
  }, options);
751
747
  let success = 0;
@@ -757,56 +753,55 @@ module.exports = (Parent) => {
757
753
  });
758
754
  const docs = await this.db.getDocuments('music');
759
755
  const hashes = {};
760
-
761
- for(let i = 0; i < docs.length; i++) {
756
+
757
+ for (let i = 0; i < docs.length; i++) {
762
758
  const doc = docs[i];
763
-
764
- if(!doc.fileHash || typeof doc.fileHash != 'string') {
759
+
760
+ if (!doc.fileHash || typeof doc.fileHash != 'string') {
765
761
  continue;
766
762
  }
767
-
768
763
  hashes[doc.fileHash] = doc;
769
764
  }
770
-
765
+
771
766
  await this.iterateFiles(async (filePath) => {
772
767
  const fileInfo = await utils.getFileInfo(filePath);
773
768
  const doc = hashes[fileInfo.hash];
774
769
 
775
- if(!doc) {
770
+ if (!doc) {
776
771
  return;
777
772
  }
778
773
 
779
774
  const info = Object.assign({ title: doc.title }, fileInfo);
780
775
  let file;
781
-
776
+
782
777
  try {
783
- file = fs.createReadStream(filePath);
784
- await this.duplicateSong([address], file, info, {
778
+ file = fse.createReadStream(filePath);
779
+ await this.duplicateSong([address], file, info, {
785
780
  exported: true,
786
781
  priority: doc.priority,
787
- timeout: timer()
782
+ timeout: timer()
788
783
  });
789
784
  success++;
790
785
  file.destroy();
791
786
  this.logger.info(`Song "${doc.title}" has been exported`);
792
787
  }
793
- catch(err) {
794
- file.destroy();
795
-
796
- if(options.strict) {
788
+ catch (err) {
789
+ file.destroy();
790
+
791
+ if (options.strict) {
797
792
  throw err;
798
793
  }
799
-
794
+
800
795
  fail++;
801
796
  this.logger.warn(err.stack);
802
797
  this.logger.info(`Song "${doc.title}" has been failed`);
803
798
  }
804
799
  });
805
800
 
806
- if(!success && !fail) {
801
+ if (!success && !fail) {
807
802
  this.logger.info(`There haven't been songs to export`);
808
803
  }
809
- else if(!fail) {
804
+ else if (!fail) {
810
805
  this.logger.info(`${success} song(s) have been exported`);
811
806
  }
812
807
  else {
@@ -818,7 +813,7 @@ module.exports = (Parent) => {
818
813
  * @see NodeMetastocle.prototype.getDocumentAdditionInfoFilterOptions
819
814
  */
820
815
  async getDocumentAdditionInfoFilterOptions() {
821
- return _.merge(await super.getDocumentAdditionInfoFilterOptions.apply(this, arguments), {
816
+ return merge(await super.getDocumentAdditionInfoFilterOptions.apply(this, arguments), {
822
817
  uniq: 'address',
823
818
  fnCompare: await this.createSongAdditionComparisonFunction(),
824
819
  fnFilter: c => c.isAvailable
@@ -829,10 +824,9 @@ module.exports = (Parent) => {
829
824
  * @see NodeMetastocle.prototype.getDocumentExistenceInfo
830
825
  */
831
826
  async getDocumentExistenceInfo(info) {
832
- if(info.collection == 'music') {
827
+ if (info.collection == 'music') {
833
828
  return await this.db.getMusicByPk(info.pkValue);
834
- }
835
-
829
+ }
836
830
  return await super.getDocumentExistenceInfo.apply(this, arguments);
837
831
  }
838
832
 
@@ -840,51 +834,50 @@ module.exports = (Parent) => {
840
834
  * @see NodeMetastocle.prototype.documentAvailabilityTest
841
835
  */
842
836
  async documentAvailabilityTest(info = {}) {
843
- if(info.collection == 'music') {
837
+ if (info.collection == 'music') {
844
838
  await this.fileAvailabilityTest(info.fileInfo);
845
839
  const existent = await this.db.getMusicByPk(info.pkValue);
846
-
847
- if(existent) {
840
+
841
+ if (existent) {
848
842
  return;
849
843
  }
850
844
  }
851
-
845
+
852
846
  return await super.documentAvailabilityTest.apply(this, arguments);
853
847
  }
854
848
 
855
849
  /**
856
850
  * Create a document addition comparison function
857
- *
851
+ *
858
852
  * @async
859
853
  * @returns {function}
860
854
  */
861
855
  async createSongAdditionComparisonFunction() {
862
856
  const obj = await this.prepareCandidateSuscpicionInfo('addSong');
863
857
  const fn = await this.createDocumentAdditionComparisonFunction();
864
-
865
858
  return (a, b) => {
866
- if(a.existenceInfo && !b.existenceInfo) {
859
+ if (a.existenceInfo && !b.existenceInfo) {
867
860
  return -1;
868
861
  }
869
862
 
870
- if(!a.existenceInfo && b.existenceInfo) {
863
+ if (!a.existenceInfo && b.existenceInfo) {
871
864
  return 1;
872
865
  }
873
866
 
874
867
  const suspicionLevelA = obj[a.address] || 0;
875
868
  const suspicionLevelB = obj[b.address] || 0;
876
869
 
877
- if(suspicionLevelA != suspicionLevelB) {
870
+ if (suspicionLevelA != suspicionLevelB) {
878
871
  return suspicionLevelA - suspicionLevelB;
879
872
  }
880
873
 
881
874
  return fn(a, b);
882
- }
875
+ };
883
876
  }
884
877
 
885
878
  /**
886
879
  * Create the song audio link
887
- *
880
+ *
888
881
  * @async
889
882
  * @param {object} document
890
883
  * @param {object} document.fileHash
@@ -898,7 +891,7 @@ module.exports = (Parent) => {
898
891
 
899
892
  /**
900
893
  * Create the song cover link
901
- *
894
+ *
902
895
  * @async
903
896
  * @param {object} document
904
897
  * @param {object} document.fileHash
@@ -910,27 +903,27 @@ module.exports = (Parent) => {
910
903
  const hash = document.fileHash;
911
904
  const filePath = this.getFilePath(hash);
912
905
  tags = tags || await utils.getSongTags(filePath);
913
-
914
- if(!tags.APIC) {
906
+
907
+ if (!tags.APIC) {
915
908
  return '';
916
909
  }
917
910
 
918
911
  const buff = await this.getSongCoverHeadersBuffer(tags.APIC);
919
912
  const info = await utils.getFileInfo(buff, { hash: false });
920
913
  const code = utils.encodeSongTitle(document.title);
921
- return `${this.getRequestProtocol()}://${this.address}/cover/${code}${info.ext? '.' + info.ext: ''}?f=${hash}`;
914
+ return `${this.getRequestProtocol()}://${this.address}/cover/${code}${info.ext ? '.' + info.ext : ''}?f=${hash}`;
922
915
  }
923
916
 
924
917
  /**
925
918
  * @see NodeStoracle.prototype.removeFileFromStorage
926
- *
919
+ *
927
920
  * @param [options]
928
921
  */
929
922
  async removeFileFromStorage(hash, options = {}) {
930
923
  await super.removeFileFromStorage.apply(this, arguments);
931
924
  !options.ignoreDocument && await this.db.removeMusicByFileHash(hash);
932
- }
933
-
925
+ }
926
+
934
927
  /**
935
928
  * @see NodeStoracle.prototype.emptyStorage
936
929
  */
@@ -941,7 +934,7 @@ module.exports = (Parent) => {
941
934
 
942
935
  /**
943
936
  * Check the song relevance
944
- *
937
+ *
945
938
  * @async
946
939
  * @param {string} filePathSource
947
940
  * @param {string} filePathTarget
@@ -950,32 +943,32 @@ module.exports = (Parent) => {
950
943
  async checkSongRelevance(filePathSource, filePathTarget) {
951
944
  const hashSource = path.basename(filePathSource);
952
945
  const hashTarget = path.basename(filePathTarget);
953
-
954
- if(!this.hasFile(hashSource)) {
946
+
947
+ if (!this.hasFile(hashSource)) {
955
948
  return false;
956
949
  }
957
950
 
958
- if(!await fse.pathExists(filePathTarget)) {
951
+ if (!await fse.pathExists(filePathTarget)) {
959
952
  return true;
960
953
  }
961
954
 
962
- if(hashSource == hashTarget) {
955
+ if (hashSource == hashTarget) {
963
956
  return true;
964
957
  }
965
958
 
966
959
  let time = this.options.music.relevanceTime;
967
-
968
- if(time <= 0) {
960
+
961
+ if (time <= 0) {
969
962
  return false;
970
963
  }
971
964
 
972
965
  let mdSource;
973
966
  let mdTarget;
974
-
967
+
975
968
  try {
976
969
  mdSource = await utils.getSongMetadata(filePathSource);
977
970
  }
978
- catch(err) {
971
+ catch (err) {
979
972
  this.logger.warn(err.stack);
980
973
  return false;
981
974
  }
@@ -983,13 +976,13 @@ module.exports = (Parent) => {
983
976
  try {
984
977
  mdTarget = await utils.getSongMetadata(filePathTarget);
985
978
  }
986
- catch(err) {
979
+ catch (err) {
987
980
  this.logger.warn(err.stack);
988
981
  return true;
989
982
  }
990
-
983
+
991
984
  const criterias = 2;
992
- const step = Math.round(time / criterias);
985
+ const step = Math.round(time / criterias);
993
986
  mdTarget.duration > mdSource.duration && (time -= step);
994
987
  mdTarget.sampleRate > mdSource.sampleRate && (time -= step / 2);
995
988
  mdTarget.bitrate > mdSource.bitrate && (time -= step / 2);
@@ -1008,17 +1001,17 @@ module.exports = (Parent) => {
1008
1001
  let res;
1009
1002
  let isError = false;
1010
1003
  this.__addingFiles[hash] = true;
1011
-
1004
+
1012
1005
  try {
1013
1006
  res = await fn();
1014
1007
  }
1015
- catch(err) {
1008
+ catch (err) {
1016
1009
  isError = true;
1017
1010
  }
1018
1011
 
1019
1012
  delete this.__addingFiles[hash];
1020
-
1021
- if(isError) {
1013
+
1014
+ if (isError) {
1022
1015
  throw res;
1023
1016
  }
1024
1017
 
@@ -1027,16 +1020,16 @@ module.exports = (Parent) => {
1027
1020
 
1028
1021
  /**
1029
1022
  * Get the song audio file headers buffer
1030
- *
1023
+ *
1031
1024
  * @see NodeMuseria.prototype.getSongHeadersBuffer
1032
1025
  */
1033
1026
  async getSongAudioHeadersBuffer(content) {
1034
1027
  return this.getSongHeadersBuffer(content, this.options.music.audioHeadersMaxSize);
1035
1028
  }
1036
-
1029
+
1037
1030
  /**
1038
1031
  * Get the song cover file headers buffer
1039
- *
1032
+ *
1040
1033
  * @see NodeMuseria.prototype.getSongHeadersBuffer
1041
1034
  */
1042
1035
  async getSongCoverHeadersBuffer(content) {
@@ -1045,17 +1038,17 @@ module.exports = (Parent) => {
1045
1038
 
1046
1039
  /**
1047
1040
  * Get the song file headers buffer
1048
- *
1041
+ *
1049
1042
  * @async
1050
- * @param {string|Buffer} content
1051
- * @param {number} limit
1043
+ * @param {string|Buffer} content
1044
+ * @param {number} limit
1052
1045
  * @returns {Buffer}
1053
1046
  */
1054
1047
  async getSongHeadersBuffer(content, limit) {
1055
- if(typeof content == 'string') {
1048
+ if (typeof content == 'string') {
1056
1049
  return new Promise((resolve, reject) => {
1057
1050
  const chunks = [];
1058
- fs.createReadStream(content, { start: 0, end: limit })
1051
+ fse.createReadStream(content, { start: 0, end: limit })
1059
1052
  .on('error', reject)
1060
1053
  .on('data', data => chunks.push(data))
1061
1054
  .on('end', () => resolve(Buffer.concat(chunks)));
@@ -1064,20 +1057,19 @@ module.exports = (Parent) => {
1064
1057
 
1065
1058
  return content.slice(0, limit);
1066
1059
  }
1067
-
1068
1060
  /**
1069
1061
  * Get finding songs sort
1070
- *
1062
+ *
1071
1063
  * @returns {array}
1072
1064
  */
1073
1065
  getFindingSongsSort() {
1074
- return [['main', 'desc'],['intScore', 'desc'], ['priority', 'desc'], ['random', 'asc']];
1066
+ return [['main', 'desc'], ['intScore', 'desc'], ['priority', 'desc'], ['random', 'asc']];
1075
1067
  }
1076
1068
 
1077
1069
  /**
1078
1070
  * Check the file is adding
1079
- *
1080
- * @param {string} hash
1071
+ *
1072
+ * @param {string} hash
1081
1073
  * @returns {boolean}
1082
1074
  */
1083
1075
  isFileAdding(hash) {
@@ -1086,42 +1078,42 @@ module.exports = (Parent) => {
1086
1078
 
1087
1079
  /**
1088
1080
  * Test the song title
1089
- *
1090
- * @param {string} title
1081
+ *
1082
+ * @param {string} title
1091
1083
  */
1092
1084
  songTitleTest(title) {
1093
- if(!utils.isSongTitle(title)) {
1085
+ if (!utils.isSongTitle(title)) {
1094
1086
  throw new errors.WorkError(`Wrong song title "${title}"`, 'ERR_MUSERIA_SONG_WRONG_TITLE');
1095
1087
  }
1096
1088
  }
1097
1089
 
1098
1090
  /**
1099
1091
  * Test the song title
1100
- *
1101
- * @param {object} info
1092
+ *
1093
+ * @param {object} info
1102
1094
  * @param {number} info.priority
1103
1095
  * @param {boolean} info.controlled
1104
1096
  * @param {boolean} [info.exported]
1105
1097
  */
1106
1098
  songPriorityTest({ priority, controlled, exported }) {
1107
- if(!utils.isValidSongPriority(priority)) {
1099
+ if (!utils.isValidSongPriority(priority)) {
1108
1100
  const msg = 'Song priority must be an integer from -1 to 1';
1109
1101
  throw new errors.WorkError(msg, 'ERR_MUSERIA_SONG_WRONG_PRIORITY');
1110
1102
  }
1111
1103
 
1112
- if(priority > 0 && !controlled && !exported) {
1104
+ if (priority > 0 && !controlled && !exported) {
1113
1105
  const msg = 'Priority 1 is possible only if "controlled" is true';
1114
1106
  throw new errors.WorkError(msg, 'ERR_MUSERIA_SONG_WRONG_PRIORITY_CONTROLLED');
1115
1107
  }
1116
1108
  }
1117
1109
 
1118
- /**
1110
+ /**
1119
1111
  * @see NodeStoracle.prototype.calculateTempFileMinSize
1120
1112
  */
1121
1113
  calculateTempFileMinSize(size) {
1122
1114
  return size * 2 + this.fileMaxSize;
1123
1115
  }
1124
-
1116
+
1125
1117
  /**
1126
1118
  * Prepare the options
1127
1119
  */
@@ -1130,7 +1122,7 @@ module.exports = (Parent) => {
1130
1122
  this.options.music.relevanceTime = utils.getMs(this.options.music.relevanceTime);
1131
1123
  this.options.music.audioHeadersMaxSize = utils.getBytes(this.options.music.audioHeadersMaxSize);
1132
1124
  this.options.music.coverHeadersMaxSize = utils.getBytes(this.options.music.coverHeadersMaxSize);
1133
- this.options.music.coverMaxFileSize = utils.getBytes(this.options.music.coverMaxFileSize);
1125
+ this.options.music.coverMaxFileSize = utils.getBytes(this.options.music.coverMaxFileSize);
1134
1126
  }
1135
- }
1136
- };
1127
+ };
1128
+ };