2022-04-14 14:23:41 +02:00
|
|
|
const metadata = require('../libs/metadata.js');
|
2022-06-02 15:27:43 +02:00
|
|
|
const { extname } = require('path');
|
2022-04-14 14:23:41 +02:00
|
|
|
|
2022-06-03 17:04:44 +02:00
|
|
|
const Artist = require('../models/Artist.js');
|
|
|
|
|
2022-04-14 14:23:41 +02:00
|
|
|
class Queue {
|
|
|
|
|
|
|
|
constructor() {
|
|
|
|
this.queue = [];
|
|
|
|
this.filter = this.getFilter();
|
|
|
|
this.handleQueue();
|
|
|
|
}
|
|
|
|
|
|
|
|
add(event, file, stats) {
|
2022-06-02 15:27:43 +02:00
|
|
|
if (file === undefined || !this.filter.includes(extname(file))) {
|
2022-04-14 14:23:41 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
const element = { file: file };
|
|
|
|
switch (event) {
|
2022-04-21 13:43:33 +02:00
|
|
|
case constants.FS_EVENT_ADD:
|
|
|
|
element.event = constants.FS_EVENT_ADD;
|
2022-04-14 14:23:41 +02:00
|
|
|
break;
|
2022-04-21 13:43:33 +02:00
|
|
|
case constants.FS_EVENT_UNLINK:
|
|
|
|
element.event = constants.FS_EVENT_UNLINK;
|
2022-04-14 14:23:41 +02:00
|
|
|
break;
|
2022-04-21 13:43:33 +02:00
|
|
|
case constants.FS_EVENT_CHANGE:
|
|
|
|
element.event = constants.FS_EVENT_CHANGE;
|
2022-04-14 14:23:41 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.queue.push(element);
|
|
|
|
}
|
|
|
|
|
|
|
|
async handleQueue() {
|
|
|
|
if (this.queue.length === 0) {
|
|
|
|
if (this.timeout === undefined) {
|
|
|
|
this.timeout = 10;
|
|
|
|
} else {
|
|
|
|
if (this.timeout > 60000) {
|
|
|
|
this.timeout = 60000;
|
|
|
|
} else {
|
|
|
|
this.timeout += 10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
logger.debug('queue is currently empty - sleeping for ' + this.timeout + 'ms...');
|
|
|
|
setTimeout(() => {
|
|
|
|
this.handleQueue()
|
|
|
|
}, this.timeout);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (this.timeout !== undefined) {
|
|
|
|
this.timeout = undefined;
|
|
|
|
}
|
|
|
|
const element = this.queue[0];
|
|
|
|
this.queue.shift();
|
|
|
|
const timestamp = new Date().getTime();
|
|
|
|
logger.debug('handling event \'' + element.event + '\' for queued file \'' + element.file + '\'...');
|
|
|
|
switch (element.event) {
|
2022-04-21 13:43:33 +02:00
|
|
|
case constants.FS_EVENT_ADD:
|
2022-04-14 14:23:41 +02:00
|
|
|
await this.eventAdd(element.file);
|
|
|
|
break;
|
2022-04-21 13:43:33 +02:00
|
|
|
case constants.FS_EVENT_UNLINK:
|
2022-04-14 14:23:41 +02:00
|
|
|
await this.eventUnlink(element.file);
|
|
|
|
break;
|
2022-04-21 13:43:33 +02:00
|
|
|
case constants.FS_EVENT_CHANGE:
|
2022-04-14 14:23:41 +02:00
|
|
|
await this.eventChange(element.file);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
logger.debug('event \'' + element.event + '\' for file \'' + element.file + '\' handled after ' + (new Date().getTime() - timestamp) + 'ms');
|
|
|
|
this.handleQueue();
|
|
|
|
}
|
|
|
|
|
|
|
|
async eventAdd(file) {
|
|
|
|
if (file === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const tags = await metadata.parseFile(file);
|
2022-06-01 16:20:25 +02:00
|
|
|
const artist = await this.addArtist(tags);
|
2022-04-14 14:23:41 +02:00
|
|
|
const album = await this.addAlbum(tags);
|
2022-06-02 15:53:49 +02:00
|
|
|
const track = await this.addTrack(tags, file);
|
2022-06-01 16:20:25 +02:00
|
|
|
this.linkTrackToArtist(track, artist);
|
|
|
|
this.linkTrackToAlbum(track, album);
|
|
|
|
this.linkAlbumToArtist(album, artist);
|
2022-04-14 14:23:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
async eventUnlink(file) {
|
|
|
|
if (file === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
await database.models.Track.destroy({
|
|
|
|
where: { file: file }
|
|
|
|
});
|
|
|
|
} catch (err) {
|
|
|
|
logger.error(err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async eventChange(file) {
|
|
|
|
if (file === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async addArtist(tags) {
|
|
|
|
if (tags?.common?.artist === undefined && tags?.common?.artists === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let artist = tags.common.artist;
|
|
|
|
const artists = tags.common.artists || [];
|
|
|
|
if (artist !== undefined && !artists.includes(artist)) {
|
|
|
|
artists.push(artist);
|
|
|
|
}
|
|
|
|
for (let index = 0; index < artists.length; index++) {
|
2022-06-03 17:04:44 +02:00
|
|
|
artist = new Artist(artists[index]);
|
|
|
|
artist = await artist.save();
|
|
|
|
return artist;
|
2022-04-14 14:23:41 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async addAlbum(tags) {
|
|
|
|
if (tags?.common?.album === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
const [element, created] = await database.models.Album.findOrCreate({
|
|
|
|
where: {
|
|
|
|
name: tags.common.album
|
|
|
|
}
|
|
|
|
});
|
2022-06-01 16:20:25 +02:00
|
|
|
if (created) {
|
|
|
|
logger.debug('created album: ' + JSON.stringify(element));
|
2022-04-14 14:23:41 +02:00
|
|
|
}
|
2022-06-01 16:20:25 +02:00
|
|
|
return element;
|
2022-04-14 14:23:41 +02:00
|
|
|
} catch (err) {
|
|
|
|
logger.error('error finding or creating album \'' + JSON.stringify(tags) + '\' > ' + err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-02 15:53:49 +02:00
|
|
|
async addTrack(tags, file) {
|
2022-04-14 14:23:41 +02:00
|
|
|
if (tags?.common?.title === undefined || file === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const where = {
|
2022-06-02 15:27:43 +02:00
|
|
|
file: file
|
2022-04-14 14:23:41 +02:00
|
|
|
};
|
2022-06-02 15:27:43 +02:00
|
|
|
if (tags?.common?.title !== undefined) {
|
|
|
|
where.title = tags.common.title;
|
|
|
|
}
|
2022-04-14 14:23:41 +02:00
|
|
|
if (tags?.common?.year !== undefined) {
|
|
|
|
where.year = tags.common.year;
|
|
|
|
}
|
|
|
|
if (tags?.common?.duration !== undefined) {
|
|
|
|
where.duration = tags.common.duration;
|
|
|
|
}
|
|
|
|
if (tags?.common?.comment !== undefined) {
|
|
|
|
let comment = '';
|
|
|
|
if (Array.isArray(tags.common.comment)) {
|
|
|
|
for (let index = 0; index < tags.common.comment.length; index++) {
|
|
|
|
if (comment.length > 0) {
|
|
|
|
comment += '\n';
|
|
|
|
}
|
|
|
|
comment += tags.common.comment[index];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
comment = tags.common.comment;
|
|
|
|
}
|
|
|
|
where.comment = comment;
|
|
|
|
}
|
|
|
|
if (tags?.common?.disk?.no !== undefined) {
|
|
|
|
where.diskno = tags.common.disk.no;
|
|
|
|
}
|
|
|
|
if (tags?.common?.disk?.of !== undefined) {
|
|
|
|
where.diskof = tags.common.disk.of;
|
|
|
|
}
|
|
|
|
if (tags?.common?.track?.no !== undefined) {
|
|
|
|
where.trackno = tags.common.track.no;
|
|
|
|
}
|
|
|
|
if (tags?.common?.track?.of !== undefined) {
|
|
|
|
where.trackof = tags.common.track.of;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
const [element, created] = await database.models.Track.findOrCreate({
|
|
|
|
where: where
|
|
|
|
});
|
2022-06-01 16:20:25 +02:00
|
|
|
if (created) {
|
|
|
|
logger.debug('created track: ' + JSON.stringify(element));
|
2022-04-14 14:23:41 +02:00
|
|
|
}
|
2022-06-01 16:20:25 +02:00
|
|
|
return element;
|
2022-04-14 14:23:41 +02:00
|
|
|
} catch (err) {
|
|
|
|
logger.error('error finding or creating track \'' + JSON.stringify(tags) + '\' > ' + err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-01 16:20:25 +02:00
|
|
|
async linkTrackToArtist(track, artist) {
|
|
|
|
if (track === undefined || artist === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
const [element, created] = await database.models.TrackToArtist.findOrCreate({
|
|
|
|
where: {
|
|
|
|
track: track.id,
|
|
|
|
artist: artist.id
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (created) {
|
|
|
|
logger.debug('linked track \'' + track.id + '\' to artist \'' + artist.id + '\': ' + JSON.stringify(element));
|
|
|
|
}
|
|
|
|
return element;
|
|
|
|
} catch (err) {
|
|
|
|
logger.error('error finding or creating tracktoartist entry for track \'' + track.id + '\' and album \'' + artist.id + '\' > ' + err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async linkTrackToAlbum(track, album) {
|
|
|
|
if (track === undefined || album === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
const [element, created] = await database.models.TrackToAlbum.findOrCreate({
|
|
|
|
where: {
|
|
|
|
track: track.id,
|
|
|
|
album: album.id
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (created) {
|
|
|
|
logger.debug('linked track \'' + track.id + '\' to album \'' + album.id + '\': ' + JSON.stringify(element));
|
|
|
|
}
|
|
|
|
return element;
|
|
|
|
} catch (err) {
|
|
|
|
logger.error('error finding or creating tracktoalbum entry for track \'' + track.id + '\' and album \'' + album.id + '\' > ' + err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async linkAlbumToArtist(album, artist) {
|
|
|
|
if (album === undefined || artist === undefined) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
const [element, created] = await database.models.AlbumToArtist.findOrCreate({
|
|
|
|
where: {
|
|
|
|
album: album.id,
|
|
|
|
artist: artist.id
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (created) {
|
|
|
|
logger.debug('linked album \'' + album.id + '\' to artist \'' + artist.id + '\': ' + JSON.stringify(element));
|
|
|
|
}
|
|
|
|
return element;
|
|
|
|
} catch (err) {
|
|
|
|
logger.error('error finding or creating albumtoartist entry for album \'' + album.id + '\' and artist \'' + artist.id + '\' > ' + err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-14 14:23:41 +02:00
|
|
|
getFilter() {
|
|
|
|
let filter = config?.library?.formats || ['.mp3'];
|
|
|
|
for (let index = 0; index < filter.length; index++) {
|
|
|
|
if (filter[index].startsWith(".")) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
filter[index] = '.' + filter[index];
|
|
|
|
}
|
|
|
|
return filter;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = Queue;
|