museria 0.2.32 → 0.2.36

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "museria",
3
- "version": "0.2.32",
3
+ "version": "0.2.36",
4
4
  "description": "Decentralized music storage",
5
5
  "main": "./src/index.js",
6
6
  "bin": {
@@ -69,6 +69,7 @@
69
69
  "dependencies": {
70
70
  "@fortawesome/fontawesome-free": "^5.15.0",
71
71
  "akili": "^1.2.17",
72
+ "array-chunk-reader": "^0.1.15",
72
73
  "base64url": "^3.0.1",
73
74
  "bootstrap": "^4.5.3",
74
75
  "chalk": "^3.0.0",
@@ -76,7 +77,7 @@
76
77
  "express": "^4.17.1",
77
78
  "fs-extra": "^9.0.1",
78
79
  "lodash": "^4.17.20",
79
- "metastocle": "^0.2.20",
80
+ "metastocle": "^0.2.23",
80
81
  "music-metadata": "^6.4.0",
81
82
  "node-fetch": "^2.6.1",
82
83
  "node-id3": "^0.2.2",
@@ -85,8 +86,8 @@
85
86
  "serve-favicon": "^2.5.0",
86
87
  "sharp": "^0.25.2",
87
88
  "splaytree": "^3.1.0",
88
- "spreadable": "^0.2.16",
89
- "storacle": "^0.2.16",
89
+ "spreadable": "^0.2.19",
90
+ "storacle": "^0.2.19",
90
91
  "transliteration": "^2.2.0"
91
92
  },
92
93
  "repository": {
@@ -238,6 +238,13 @@
238
238
  Once you are here, then the network is public or you have access.
239
239
  You can try to find a song in the storage or add your own there.
240
240
  </div>
241
+ </li>
242
+ <li>
243
+ <div>How many songs are in this node?</div>
244
+ <div>
245
+ There are <b class="text-warning">${ this.songsCount }</b> song(s) at the moment. You also can find more info about the node status
246
+ <a href="/status?pretty" target="_blank">here</a>.
247
+ </div>
241
248
  </li>
242
249
  <li>
243
250
  <div>How to learn more about all this?</div>
@@ -1,6 +1,7 @@
1
1
  import './app.scss';
2
2
  import Akili from 'akili';
3
3
  import router from 'akili/src/services/router';
4
+ import request from 'akili/src/services/request';
4
5
  import client from '../../client';
5
6
 
6
7
  export default class App extends Akili.Component {
@@ -18,6 +19,9 @@ export default class App extends Akili.Component {
18
19
  created() {
19
20
  this.captchaWidth = 240;
20
21
  this.findingSongsLimit = 10;
22
+ this.songsCountInterval = 10 * 1000;
23
+ this.songsCountIntervalObj = null;
24
+ this.scope.songsCount = 0;
21
25
  this.scope.searchInputValue = this.transition.query.f;
22
26
  this.scope.showCaptcha = false;
23
27
  this.scope.isUploading = false;
@@ -39,11 +43,16 @@ export default class App extends Akili.Component {
39
43
  this.scope.checkUploadSongTitle = this.checkUploadSongTitle.bind(this);
40
44
  this.resetSearchEvent();
41
45
  this.resetUploadEvent();
42
- this.resetSongUploadInfo();
46
+ this.resetSongUploadInfo();
43
47
  }
44
48
 
45
49
  async compiled() {
46
50
  this.scope.searchInputValue && await this.findSongs();
51
+ await this.enableSongsCountCounter();
52
+ }
53
+
54
+ removed() {
55
+ clearInterval(this.songsCountIntervalObj);
47
56
  }
48
57
 
49
58
  resetSearchEvent() {
@@ -67,6 +76,16 @@ export default class App extends Akili.Component {
67
76
  };
68
77
  }
69
78
 
79
+ async enableSongsCountCounter() {
80
+ this.songsCountIntervalObj = setInterval(() => this.setSongsCount(), this.songsCountInterval);
81
+ await this.setSongsCount();
82
+ }
83
+
84
+ async setSongsCount() {
85
+ const data = (await request.get('/status', { json: true })).data;
86
+ this.scope.songsCount = data.filesCount;
87
+ }
88
+
70
89
  setFindingValue(val) {
71
90
  this.scope.searchInputValue = val;
72
91
  router.reload({}, { f: this.scope.searchInputValue || null }, undefined, { reload: false, saveScrollPosition: true });
@@ -2,6 +2,7 @@ const DatabaseMuseria = require('../database')();
2
2
  const DatabaseLoki = require('spreadable/src/db/transports/loki')(DatabaseMuseria);
3
3
  const DatabaseLokiMetastocle = require('metastocle/src/db/transports/loki')(DatabaseLoki);
4
4
  const DatabaseLokiStoracle = require('storacle/src/db/transports/loki')(DatabaseLokiMetastocle);
5
+ const ArrayChunkReader = require('array-chunk-reader');
5
6
  const utils = require('../../../utils');
6
7
 
7
8
  module.exports = (Parent) => {
@@ -18,13 +19,13 @@ module.exports = (Parent) => {
18
19
  similarity: this.node.options.music.similarity
19
20
  }, options);
20
21
  const fullName = this.createCollectionName('music');
21
- const collection = await this.node.getCollection('music');
22
+ const collection = await this.node.getCollection('music');
22
23
  const documents = this.col[fullName].find();
24
+ const reader = new ArrayChunkReader(documents, { limit: 1000, log: false });
23
25
  let max = null;
24
26
 
25
- for(let i = 0; i < documents.length; i++) {
26
- const doc = documents[i];
27
- let score = doc[collection.pk] === title? 1: 0;
27
+ await reader.start((doc) => {
28
+ let score = doc[collection.pk] === title? 1: 0;
28
29
 
29
30
  if(!score) {
30
31
  score = utils.getSongSimilarity(doc[collection.pk], title, {
@@ -35,18 +36,18 @@ module.exports = (Parent) => {
35
36
 
36
37
  if(score === 1) {
37
38
  max = { score, doc };
38
- break;
39
+ return reader.stop();
39
40
  }
40
41
 
41
42
  if(!max || score > max.score) {
42
43
  max = { score, doc };
43
- continue;
44
+ return;
44
45
  }
45
46
 
46
47
  if(score == max.score && Math.random() > 0.5) {
47
48
  max = { score, doc };
48
49
  }
49
- }
50
+ });
50
51
 
51
52
  if(max && max.score >= options.similarity) {
52
53
  return this.prepareDocumentToGet(max.doc);
package/src/node.js CHANGED
@@ -5,6 +5,7 @@ const sharp = require('sharp');
5
5
  const fse = require('fs-extra');
6
6
  const qs = require('querystring');
7
7
  const SplayTree = require('splaytree');
8
+ const ArrayChunkReader = require('array-chunk-reader');
8
9
  const DatabaseLokiMuseria = require('./db/transports/loki')();
9
10
  const ServerExpressMuseria = require('./server/transports/express')();
10
11
  const MusicCollection = require('./collection/transports/music')();
@@ -78,7 +79,8 @@ module.exports = (Parent) => {
78
79
  ]
79
80
  },
80
81
  task: {
81
- cleanUpMusicInterval: '30s'
82
+ cleanUpMusicInterval: '30s',
83
+ beautifySongTitlesInterval: '10m',
82
84
  }
83
85
  }, options);
84
86
 
@@ -110,10 +112,28 @@ module.exports = (Parent) => {
110
112
  if(this.options.task.cleanUpMusicInterval) {
111
113
  await this.task.add('cleanUpMusic', this.options.task.cleanUpMusicInterval, () => this.cleanUpMusic());
112
114
  }
115
+
116
+ if(this.options.task.beautifySongTitlesInterval) {
117
+ await this.task.add('beautifySongTitles', this.options.task.beautifySongTitlesInterval, () => this.beautifySongTitles());
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Beautify the song titles
123
+ *
124
+ * @async
125
+ */
126
+ async beautifySongTitles() {
127
+ const docs = await this.db.getDocuments('music');
128
+ const reader = new ArrayChunkReader(docs, { size: 1000, log: false });
129
+ await reader.start(async (doc) => {
130
+ doc.title = utils.beautifySongTitle(doc.title);
131
+ await this.db.updateDocument(doc);
132
+ });
113
133
  }
114
134
 
115
135
  /**
116
- * Clean up the music
136
+ * Clean up the music
117
137
  *
118
138
  * @async
119
139
  */
@@ -124,7 +144,7 @@ module.exports = (Parent) => {
124
144
  for(let i = 0; i < docs.length; i++) {
125
145
  const doc = docs[i];
126
146
 
127
- if(
147
+ if(
128
148
  !doc.fileHash ||
129
149
  typeof doc.fileHash != 'string' ||
130
150
  (
@@ -516,6 +536,7 @@ module.exports = (Parent) => {
516
536
  return [];
517
537
  }
518
538
 
539
+ artist = utils.prepareSongFindingString(artist);
519
540
  const collection = await this.getCollection('music');
520
541
  const actions = utils.prepareDocumentGettingActions({
521
542
  offset: 0,
@@ -29,7 +29,7 @@ module.exports = (Parent) => {
29
29
  const remove = [
30
30
  'addDocument', 'updateDocuments',
31
31
  'deleteDocuments', 'getDocumentsCount',
32
- 'getDocumentByPk'
32
+ 'getDocumentByPk', 'getDocuments'
33
33
  ];
34
34
  return super.getClientRoutes().filter(r => !remove.includes(r.name)).concat(routesClient);
35
35
  }
package/src/utils.js CHANGED
@@ -85,14 +85,13 @@ utils.prepareSongFindingString = function (str) {
85
85
  return '';
86
86
  }
87
87
 
88
- str = str
88
+ str = this.normalizeString(str)
89
89
  .trim()
90
90
  .replace(/[–—]+/g, '-')
91
91
  .replace(/[\sᅠ]+/g, ' ');
92
92
  return str;
93
93
  }
94
94
 
95
-
96
95
  /**
97
96
  * Split the song title
98
97
  *
@@ -120,7 +119,7 @@ utils.beautifySongTitle = function (title) {
120
119
  return '';
121
120
  }
122
121
 
123
- title = emojiStrip(title)
122
+ title = emojiStrip(this.normalizeString(title))
124
123
  .replace(/[–—]+/g, '-')
125
124
  .replace(this.regexSongLinks, '')
126
125
  .replace(/[\sᅠ]+/g, ' ')
@@ -568,10 +567,12 @@ utils.isValidSongPriority = function (value) {
568
567
  * @param {string} second
569
568
  * @param {object} [options]
570
569
  * @param {number} [options.min]
570
+ * @param {boolean} [options.ignoreOrder]
571
+ *
571
572
  * @returns {number}
572
573
  */
573
574
  utils.getStringSimilarity = function(first, second, options = {}) {
574
- const min = options.min || 0;
575
+ const min = options.min || 0;
575
576
  let short = first;
576
577
  let long = second;
577
578
 
@@ -579,7 +580,7 @@ utils.getStringSimilarity = function(first, second, options = {}) {
579
580
  short = second;
580
581
  long = first;
581
582
  }
582
-
583
+
583
584
  long = long.toLowerCase().split('');
584
585
  short = short.toLowerCase().split('');
585
586
  const coef = Math.sqrt(short.length * long.length);
@@ -619,4 +620,14 @@ utils.getStringSimilarity = function(first, second, options = {}) {
619
620
  return matches / coef;
620
621
  }
621
622
 
623
+ /**
624
+ * Normalize the string
625
+ *
626
+ * @param {string} str
627
+ * @returns {string}
628
+ */
629
+ utils.normalizeString = function (str) {
630
+ return str.normalize("NFD").replace(/(?!\^)\p{Diacritic}/gu, "");
631
+ }
632
+
622
633
  module.exports = utils;
package/test/node.js CHANGED
@@ -366,6 +366,18 @@ describe('Node', () => {
366
366
  });
367
367
  });
368
368
 
369
+ describe('.beautifySongTitles()', () => {
370
+ it('should beautify song titles', async () => {
371
+ const title = 'ARTIST - test';
372
+ await node.db.addDocument('music', { title });
373
+ await node.beautifySongTitles();
374
+ const beauty = utils.beautifySongTitle(title);
375
+ const docs = await node.db.getDocuments('music');
376
+ assert.notEqual(title, beauty, 'check the title');
377
+ assert.equal(docs[docs.length - 1].title, beauty, 'check the doc');
378
+ });
379
+ });
380
+
369
381
  describe('.getStorageCleaningUpTree()', () => {
370
382
  it('should get right order of priority', async () => {
371
383
  for(let i = 0; i < 3; i++) {
package/test/utils.js CHANGED
@@ -262,4 +262,10 @@ describe('utils', () => {
262
262
  assert.containsAllKeys(res, ['bitrate', 'duration', 'sampleRate']);
263
263
  });
264
264
  });
265
+
266
+ describe('.normalizeString()', () => {
267
+ it('should remove accents', () => {
268
+ assert.equal(utils.normalizeString('Mylène Farmer'), 'Mylene Farmer');
269
+ });
270
+ });
265
271
  });