From f9eede4fea238cdf42b4c9dcdfafed980ec8627a Mon Sep 17 00:00:00 2001 From: velvettear Date: Fri, 3 Jun 2022 17:04:44 +0200 Subject: [PATCH] heavily modified database interaction --- classes/Database.js | 8 ++-- classes/Queue.js | 17 +++---- classes/Watcher.js | 10 ++++- example_config.json | 6 +-- libs/constants.js | 4 +- models/Album.js | 33 ++++++++------ models/AlbumToArtist.js | 44 ++++++++++-------- models/Artist.js | 31 +++++++------ models/Base.js | 98 +++++++++++++++++++++++++++++++++++++++++ models/Environment.js | 37 ++++++++++++++++ models/Track.js | 48 +++++++++++--------- models/TrackToAlbum.js | 40 +++++++++-------- models/TrackToArtist.js | 42 ++++++++++-------- 13 files changed, 291 insertions(+), 127 deletions(-) create mode 100644 models/Base.js create mode 100644 models/Environment.js diff --git a/classes/Database.js b/classes/Database.js index 888f539..6c7a3bc 100644 --- a/classes/Database.js +++ b/classes/Database.js @@ -43,7 +43,7 @@ class Database { continue; } const model = require(modelFile); - if (model.createTable === undefined) { + if (model.prototype?.createTable === undefined || model.name === 'Base') { continue; } modelFile = path.basename(modelFile); @@ -62,10 +62,10 @@ class Database { const model = models[index]; try { const timestamp = new Date().getTime(); - await model.createTable(); - logger.debug('creation/alteration of table \'' + model.tableName + '\' finished after ' + (new Date().getTime() - timestamp) + 'ms'); + await model.prototype.createTable(); + logger.debug('creation/alteration of table \'' + model.name + '\' finished after ' + (new Date().getTime() - timestamp) + 'ms'); } catch (err) { - logger.error('an error occured creating table \'' + model.tableName + '\' > ' + err); + logger.error('an error occured creating table \'' + model.name + '\' > ' + err); } } diff --git a/classes/Queue.js b/classes/Queue.js index 71d1727..9ed2fc1 100644 --- a/classes/Queue.js +++ b/classes/Queue.js @@ -1,6 +1,8 @@ const metadata = require('../libs/metadata.js'); const { extname } = require('path'); +const Artist = require('../models/Artist.js'); + class Queue { constructor() { @@ -111,18 +113,9 @@ class Queue { artists.push(artist); } for (let index = 0; index < artists.length; index++) { - artist = artists[index].trim(); - try { - const [element, created] = await database.models.Artist.findOrCreate({ - where: { name: artist } - }); - if (created) { - logger.debug('created artist: ' + JSON.stringify(element)); - } - return element; - } catch (err) { - logger.error('error finding or creating artist \'' + JSON.stringify(tags) + '\' > ' + err); - } + artist = new Artist(artists[index]); + artist = await artist.save(); + return artist; } } diff --git a/classes/Watcher.js b/classes/Watcher.js index 80d106c..305ba25 100644 --- a/classes/Watcher.js +++ b/classes/Watcher.js @@ -2,6 +2,8 @@ const path = require('path'); const chokidar = require('chokidar'); const { open, writeFile } = require('fs/promises'); +const Environment = require('../models/Environment.js'); + class Watcher { constructor() { @@ -23,6 +25,12 @@ class Watcher { const directory = path.resolve(config.library.sources[index]); let ignoreInitial = !initialScan; if (ignoreInitial === false) { + let env = new Environment(constants.ENVIRONMENT_LASTSCAN); + await env.find('key'); + if (env === undefined) { + env = new Environment(constants.ENVIRONMENT_LASTSCAN, new Date().getTime().toString()); + env.save(); + } ignoreInitial = (new Date().getTime() - await this.#checkHiddenFile(directory)) < (config?.library?.initialscan?.maxage || 86400000); } logger.debug('watching directory \'' + directory + '\'...'); @@ -55,7 +63,7 @@ class Watcher { let filehandle; try { filehandle = await open(hiddenFile); - return (await filehandle.readFile()).toString(); + return (await filehandle.readFile()).toString(); } catch (error) { if (error.code === 'ENOENT') { return await this.#createHiddenFile(hiddenFile); diff --git a/example_config.json b/example_config.json index 8378aba..0ed3b36 100644 --- a/example_config.json +++ b/example_config.json @@ -17,14 +17,14 @@ "library": { "enabled": true, "sources": [ - "/home/velvettear/mounts/samsung/music" + "/home/velvettear/downloads" ], "formats": [ "mp3", "flac" ], "initialscan": { - "enabled": false, + "enabled": true, "maxage": 3600000 } }, @@ -41,6 +41,6 @@ "listen": "0.0.0.0", "port": 3001, "nodelay": false, - "threshold": 8 + "threshold": 10 } } \ No newline at end of file diff --git a/libs/constants.js b/libs/constants.js index f95c3eb..bc19a0d 100644 --- a/libs/constants.js +++ b/libs/constants.js @@ -19,5 +19,7 @@ module.exports = { API_PLAY: '/play', API_PAUSE: '/pause', API_RESUME: '/resume', - API_STOP: '/stop' + API_STOP: '/stop', + + ENVIRONMENT_LASTSCAN:'lastscan' } \ No newline at end of file diff --git a/models/Album.js b/models/Album.js index 33f612f..93d22c2 100644 --- a/models/Album.js +++ b/models/Album.js @@ -1,23 +1,28 @@ +const path = require('path'); const { DataTypes } = require('sequelize'); -const tableName = 'album'; +const Base = require('./Base.js'); -class Album { +const tableName = path.basename(__filename, '.js').toLowerCase(); - constructor() { +class Album extends Base { + constructor(name) { + super(); + this.tableName = tableName; + this.name = name; + } + + isValid() { + return this.name !== undefined; + } + + async getModel() { + return database.connection.define(tableName, { + name: DataTypes.TEXT + }); } } -async function createTable() { - (await getModel()).sync({ alter: true }); -} - -async function getModel() { - return database.connection.define(tableName, { - name: DataTypes.TEXT - }); -} - -module.exports = { Album, createTable, tableName }; \ No newline at end of file +module.exports = Album; \ No newline at end of file diff --git a/models/AlbumToArtist.js b/models/AlbumToArtist.js index 7070711..37e2c19 100644 --- a/models/AlbumToArtist.js +++ b/models/AlbumToArtist.js @@ -1,28 +1,34 @@ +const path = require('path'); const { DataTypes } = require('sequelize'); -const tableName = 'albumtoartist'; +const Base = require('./Base.js'); -class AlbumToArtist { +const tableName = path.basename(__filename, '.js').toLowerCase(); - constructor() { +class AlbumToArtist extends Base { + constructor(album, artist) { + super(); + this.tableName = tableName; + this.album = album; + this.artist = artist; + } + + isValid() { + return this.album !== undefined || this.artist !== undefined; + } + + async getModel() { + return database.connection.define(tableName, { + album: DataTypes.INTEGER, + artist: DataTypes.INTEGER + }, + { + freezeTableName: true + } + ); } } -async function createTable() { - (await getModel()).sync({ alter: true }); -} - -async function getModel() { - return database.connection.define(tableName, { - album: DataTypes.INTEGER, - artist: DataTypes.INTEGER - }, - { - freezeTableName: true - } - ); -} - -module.exports = { AlbumToArtist, createTable, tableName }; \ No newline at end of file +module.exports = AlbumToArtist; \ No newline at end of file diff --git a/models/Artist.js b/models/Artist.js index 2d20153..cba60e4 100644 --- a/models/Artist.js +++ b/models/Artist.js @@ -1,23 +1,26 @@ +const path = require('path'); const { DataTypes } = require('sequelize'); -const tableName = 'artist'; +const Base = require('./Base.js'); -class Artist { +const tableName = path.basename(__filename, '.js').toLowerCase(); - constructor() { +class Artist extends Base { + constructor(name) { + this.name = name; + } + + isValid() { + return this.name !== undefined; + } + + async getModel() { + return database.connection.define(tableName, { + name: DataTypes.TEXT + }); } } -async function createTable() { - (await getModel()).sync({ alter: true }); -} - -async function getModel() { - return database.connection.define(tableName, { - name: DataTypes.TEXT - }); -} - -module.exports = { Artist, createTable, tableName }; \ No newline at end of file +module.exports = Artist; \ No newline at end of file diff --git a/models/Base.js b/models/Base.js new file mode 100644 index 0000000..b935c84 --- /dev/null +++ b/models/Base.js @@ -0,0 +1,98 @@ +class Base { + + constructor() { + + } + + async save() { + if (!this.isValid()) { + return; + } + try { + const [element, created] = await (await this.getModel()).findOrCreate({ + where: await this.#createWhereClause() + }); + if (created) { + logger.debug('created \'' + this.tableName + '\' entry: ' + JSON.stringify(element)); + } + if (element?.id === undefined) { + return undefined; + } + return element; + } catch (error) { + logger.error('error finding or creating \'' + this.tableName + '\' entry: \'' + JSON.stringify(this) + '\' > ' + error); + } + } + + async find() { + if (!this.isValid()) { + return; + } + try { + const element = await (await this.getModel()).findOne({ + where: await this.#createWhereClause() + }); + if (element?.id === undefined) { + return undefined; + } + this.#fillThis(element); + } catch (error) { + logger.error('error finding \'' + this.tableName + '\' entry: \'' + JSON.stringify(this) + '\' > ' + error); + } + } + + async createTable() { + return await createTable(); + } + + async createTable() { + const model = await this.getModel(); + if (model === undefined) { + return; + } + await model.sync({ alter: true }); + } + + async getModel() { + return undefined; + } + + isValid() { + return false; + } + + #fillThis(data) { + if (data === undefined || data.dataValues === undefined || data.dataValues.id === undefined || data.dataValues.id < 0) { + return; + } + for (const key in data.dataValues) { + this[key] = data.dataValues[key]; + } + } + + async #createWhereClause(fields) { + const model = await this.getModel(); + if (model === undefined || model.tableAttributes === undefined) { + return undefined; + } + const where = {}; + for (const key in model.tableAttributes) { + if (key === 'id' || key === 'createdAt' || key === 'updatedAt' || fields !== undefined && (key !== fields || !fields.includes(key))) { + continue; + } + const value = this[key]; + if (value === undefined) { + continue; + } + where[key] = value; + } + return where; + } + +} + + + + + +module.exports = Base; \ No newline at end of file diff --git a/models/Environment.js b/models/Environment.js new file mode 100644 index 0000000..386de58 --- /dev/null +++ b/models/Environment.js @@ -0,0 +1,37 @@ +const path = require('path'); +const { DataTypes } = require('sequelize'); + +const Base = require('./Base.js'); + +const tableName = path.basename(__filename, '.js').toLowerCase(); + +class Environment extends Base { + + constructor(key, value) { + super(); + this.tableName = tableName; + this.key = key; + this.value = value; + } + + isValid() { + return this.key !== undefined; + } + + async getModel() { + if (this.model === undefined) { + this.model = await database.connection.define(tableName, { + key: DataTypes.TEXT, + value: DataTypes.TEXT + }, + { + freezeTableName: true + } + ); + } + return this.model; + } + +} + +module.exports = Environment; \ No newline at end of file diff --git a/models/Track.js b/models/Track.js index d653279..261dd4d 100644 --- a/models/Track.js +++ b/models/Track.js @@ -1,31 +1,35 @@ +const path = require('path'); const { DataTypes } = require('sequelize'); -const tableName = 'track'; +const Base = require('./Base.js'); -class Track { +const tableName = path.basename(__filename, '.js').toLowerCase(); + +class Track extends Base { constructor() { - + + } + + isValid() { + return this.title !== undefined && + this.file !== undefined; + } + + async getModel() { + return database.connection.define(tableName, { + title: DataTypes.TEXT, + year: DataTypes.INTEGER, + duration: DataTypes.FLOAT, + comment: DataTypes.TEXT, + diskno: DataTypes.INTEGER, + diskof: DataTypes.INTEGER, + trackno: DataTypes.INTEGER, + trackof: DataTypes.INTEGER, + file: DataTypes.TEXT + }); } } -async function createTable() { - (await getModel()).sync({ alter: true }); -} - -async function getModel() { - return database.connection.define(tableName, { - title: DataTypes.TEXT, - year: DataTypes.INTEGER, - duration: DataTypes.FLOAT, - comment: DataTypes.TEXT, - diskno: DataTypes.INTEGER, - diskof: DataTypes.INTEGER, - trackno: DataTypes.INTEGER, - trackof: DataTypes.INTEGER, - file: DataTypes.TEXT - }); -} - -module.exports = { Track, createTable, tableName }; \ No newline at end of file +module.exports = Track; \ No newline at end of file diff --git a/models/TrackToAlbum.js b/models/TrackToAlbum.js index cfba3b1..bb5a596 100644 --- a/models/TrackToAlbum.js +++ b/models/TrackToAlbum.js @@ -1,28 +1,32 @@ +const path = require('path'); const { DataTypes } = require('sequelize'); -const tableName = 'tracktoalbum'; +const Base = require('./Base.js'); + +const tableName = path.basename(__filename, '.js').toLowerCase(); class TrackToAlbum { - constructor() { + constructor(track, album) { + this.track = track; + this.album = album; + } + isValid() { + return this.track !== undefined || this.album !== undefined; + } + + async getModel() { + return database.connection.define(tableName, { + track: DataTypes.INTEGER, + album: DataTypes.INTEGER + }, + { + freezeTableName: true + } + ); } } -async function createTable() { - (await getModel()).sync({ alter: true }); -} - -async function getModel() { - return database.connection.define(tableName, { - track: DataTypes.INTEGER, - album: DataTypes.INTEGER - }, - { - freezeTableName: true - } - ); -} - -module.exports = { TrackToAlbum, createTable, tableName }; \ No newline at end of file +module.exports = TrackToAlbum; \ No newline at end of file diff --git a/models/TrackToArtist.js b/models/TrackToArtist.js index 706d43c..099e225 100644 --- a/models/TrackToArtist.js +++ b/models/TrackToArtist.js @@ -1,28 +1,32 @@ +const path = require('path'); const { DataTypes } = require('sequelize'); -const tableName = 'tracktoartist' +const Base = require('./Base.js'); -class TrackToArtist { +const tableName = path.basename(__filename, '.js').toLowerCase(); - constructor() { +class TrackToArtist { + constructor(track, artist) { + this.track = track; + this.artist = artist; + } + + isValid() { + return this.track !== undefined || this.artist !== undefined; + } + + async getModel() { + return database.connection.define(tableName, { + track: DataTypes.INTEGER, + artist: DataTypes.INTEGER + }, + { + freezeTableName: true + } + ); } } -async function createTable() { - (await getModel()).sync({ alter: true }); -} - -async function getModel() { - return database.connection.define(tableName, { - track: DataTypes.INTEGER, - artist: DataTypes.INTEGER - }, - { - freezeTableName: true - } - ); -} - -module.exports = { TrackToArtist, createTable, tableName }; \ No newline at end of file +module.exports = TrackToArtist; \ No newline at end of file