diff --git a/.vscode/launch.json b/.vscode/launch.json index e43147e..2900e70 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,26 +5,13 @@ "type": "pwa-node", "runtimeVersion": "18", "request": "launch", - "name": "kannon-server", + "name": "kannon", "skipFiles": [ "/**" ], "program": "${workspaceFolder}/kannon.js", "args": [ - "${workspaceFolder}/example_config_server.json" - ] - }, - { - "type": "pwa-node", - "runtimeVersion": "17", - "request": "launch", - "name": "kannon-library", - "skipFiles": [ - "/**" - ], - "program": "${workspaceFolder}/kannon.js", - "args": [ - "${workspaceFolder}/example_config_library.json" + "${workspaceFolder}/example_config.json" ] } ] diff --git a/classes/Queue.js b/classes/Queue.js index a173ab0..b8eaad8 100644 --- a/classes/Queue.js +++ b/classes/Queue.js @@ -74,9 +74,12 @@ class Queue { return; } const tags = await metadata.parseFile(file); - const artists = await this.addArtist(tags); + const artist = await this.addArtist(tags); const album = await this.addAlbum(tags); const track = await this.addTrack(tags, file); + this.linkTrackToArtist(track, artist); + this.linkTrackToAlbum(track, album); + this.linkAlbumToArtist(album, artist); } async eventUnlink(file) { @@ -113,10 +116,10 @@ class Queue { const [element, created] = await database.models.Artist.findOrCreate({ where: { name: artist } }); - if (!created) { - continue; + if (created) { + logger.debug('created artist: ' + JSON.stringify(element)); } - logger.debug('created artist ' + JSON.stringify(element)); + return element; } catch (err) { logger.error('error finding or creating artist \'' + JSON.stringify(tags) + '\' > ' + err); } @@ -133,10 +136,10 @@ class Queue { name: tags.common.album } }); - if (!created) { - return; + if (created) { + logger.debug('created album: ' + JSON.stringify(element)); } - logger.debug('created album ' + JSON.stringify(element)); + return element; } catch (err) { logger.error('error finding or creating album \'' + JSON.stringify(tags) + '\' > ' + err); } @@ -186,15 +189,75 @@ class Queue { const [element, created] = await database.models.Track.findOrCreate({ where: where }); - if (!created) { - return; + if (created) { + logger.debug('created track: ' + JSON.stringify(element)); } - logger.debug('created track ' + JSON.stringify(element)); + return element; } catch (err) { logger.error('error finding or creating track \'' + JSON.stringify(tags) + '\' > ' + err); } } + 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); + } + } + getFilter() { let filter = config?.library?.formats || ['.mp3']; for (let index = 0; index < filter.length; index++) { diff --git a/docker/.dockerignore b/docker/.dockerignore new file mode 100644 index 0000000..412c257 --- /dev/null +++ b/docker/.dockerignore @@ -0,0 +1 @@ +docker-compose.yml \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..80ccb09 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,22 @@ +FROM node:18-alpine3.15 + +LABEL version="1.0.0" \ + author="Daniel Sommer + +ENV LANG=C.UTF-8 + +RUN apk update --no-cache --progress \ + && apk upgrade --no-cache --progress \ + && apk add --no-cache --progress \ + tzdata \ + git \ + && ln -sf /usr/share/zoneinfo/Europe/Berlin /etc/localtime \ + && echo "Europe/Berlin" > /etc/timezone \ + && git clone https://git.velvettear.de/velvettear/kannon.git /opt/kannon \ + && cd /opt/kannon \ + && apk del --no-cache tzdata git + + ENTRYPOINT ["node /opt/kannon/kannon.js /opt/kannon/docker/docker_config.json"] \ No newline at end of file diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..731f0b2 --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,21 @@ +version: "3.9" + +services: + postgres: + image: postgres:alpine + container_name: kannon-postgres + restart: unless-stopped + environment: + POSTGRES_PASSWORD: kannon + POSTGRES_USER: kannon + POSTGRES_DB: kannon + ports: + - 5432:5432 + kannon: + build: . + container_name: kannon + restart: unless-stopped + ports: + - 3000:3000 + - 3001:3001 + - 3002:3002 \ No newline at end of file diff --git a/docker/docker_config.json b/docker/docker_config.json new file mode 100644 index 0000000..fffec33 --- /dev/null +++ b/docker/docker_config.json @@ -0,0 +1,41 @@ +{ + "server": { + "enabled": true, + "listen": "0.0.0.0", + "port": 3000, + "heartbeat": 10000 + }, + "api": { + "enabled": true, + "listen": "0.0.0.0", + "port": 3002 + }, + "log": { + "level": "debug", + "timestamp": "DD.MM.YYYY HH:mm:ss:SS" + }, + "library": { + "enabled": true, + "sources": [ + "/mnt/library" + ], + "formats": [ + "mp3", + "flac" + ] + }, + "database": { + "dialect": "postgres", + "host": "kannon-postgres", + "port": 5432, + "database": "kannon", + "username": "kannon", + "password": "kannon" + }, + "audio": { + "listen": "0.0.0.0", + "port": 3001, + "nodelay": false, + "threshold": 10 + } + } \ No newline at end of file diff --git a/example_config_server.json b/example_config.json similarity index 78% rename from example_config_server.json rename to example_config.json index b40494e..8095187 100644 --- a/example_config_server.json +++ b/example_config.json @@ -15,13 +15,20 @@ "timestamp": "DD.MM.YYYY HH:mm:ss:SS" }, "library": { - "enabled": false + "enabled": true, + "sources": [ + "/home/velvettear/mounts/samsung/music" + ], + "formats": [ + "mp3", + "flac" + ] }, "database": { "dialect": "postgres", "storage": "/tmp/kannon.sqlite", "host": "192.168.104.136", - "port": 5432, + "port": 5000, "database": "kannon", "username": "kannon", "password": "kannon" diff --git a/example_config_library.json b/example_config_library.json deleted file mode 100644 index 5e54c6a..0000000 --- a/example_config_library.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "server": { - "enabled": false - }, - "log": { - "level": "debug", - "timestamp": "DD.MM.YYYY HH:mm:ss:SS" - }, - "library": { - "enabled": true, - "sources": [ - "/home/velvettear/downloads" - ], - "formats": [ - "mp3", - "flac" - ] - }, - "database": { - "dialect": "postgres", - "storage": "/tmp/kannon.sqlite", - "host": "192.168.104.136", - "port": 5432, - "database": "kannon", - "username": "postgres", - "password": "$Velvet90" - } -} \ No newline at end of file diff --git a/models/AlbumToArtist.js b/models/AlbumToArtist.js new file mode 100644 index 0000000..9b25a96 --- /dev/null +++ b/models/AlbumToArtist.js @@ -0,0 +1,12 @@ +const { DataTypes } = require('sequelize'); + +const AlbumToArtist = database.connection.define("albumtoartist", { + album: DataTypes.INTEGER, + artist: DataTypes.INTEGER +}, + { + freezeTableName: true + } +); + +module.exports = AlbumToArtist; \ No newline at end of file diff --git a/models/TrackToAlbum.js b/models/TrackToAlbum.js new file mode 100644 index 0000000..450e8ec --- /dev/null +++ b/models/TrackToAlbum.js @@ -0,0 +1,12 @@ +const { DataTypes } = require('sequelize'); + +const TrackToAlbum = database.connection.define("tracktoalbum", { + track: DataTypes.INTEGER, + album: DataTypes.INTEGER +}, + { + freezeTableName: true + } +); + +module.exports = TrackToAlbum; \ No newline at end of file diff --git a/models/TrackToArtist.js b/models/TrackToArtist.js new file mode 100644 index 0000000..544dc06 --- /dev/null +++ b/models/TrackToArtist.js @@ -0,0 +1,12 @@ +const { DataTypes } = require('sequelize'); + +const TrackToArtist = database.connection.define("tracktoartist", { + track: DataTypes.INTEGER, + artist: DataTypes.INTEGER +}, + { + freezeTableName: true + } +); + +module.exports = TrackToArtist; \ No newline at end of file