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/.github/workflows/build.yml +1 -1
- package/.github/workflows/publish.yml +1 -1
- package/README.md +4 -0
- package/dist/client/museria.client.js +7 -7
- package/dist/face/fa-brands-400.eot +0 -0
- package/dist/face/fa-brands-400.svg +41 -41
- 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 +2 -2
- 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/museria.face.js +9 -9
- package/dist/face/style.css +5 -5
- package/package.json +5 -4
- package/src/browser/face/controllers/app/app.html +7 -0
- package/src/browser/face/controllers/app/app.js +20 -1
- package/src/db/transports/loki/index.js +8 -7
- package/src/node.js +24 -3
- package/src/server/transports/express/index.js +1 -1
- package/src/utils.js +16 -5
- package/test/node.js +12 -0
- package/test/utils.js +6 -0
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "museria",
|
3
|
-
"version": "0.2.
|
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.
|
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.
|
89
|
-
"storacle": "^0.2.
|
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
|
-
|
26
|
-
|
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
|
-
|
39
|
+
return reader.stop();
|
39
40
|
}
|
40
41
|
|
41
42
|
if(!max || score > max.score) {
|
42
43
|
max = { score, doc };
|
43
|
-
|
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
|
});
|