From 2cd90796923dd110d48945ad6de1f02400b660ca Mon Sep 17 00:00:00 2001 From: advplyr Date: Sat, 7 Jan 2023 13:05:33 -0600 Subject: [PATCH] Add MusicBrainz provider --- server/controllers/SearchController.js | 7 ++++ server/finders/MusicFinder.js | 12 ++++++ server/providers/MusicBrainz.js | 51 ++++++++++++++++++++++++++ server/routers/ApiRouter.js | 3 ++ 4 files changed, 73 insertions(+) create mode 100644 server/finders/MusicFinder.js create mode 100644 server/providers/MusicBrainz.js diff --git a/server/controllers/SearchController.js b/server/controllers/SearchController.js index 5ca217b3..cc803720 100644 --- a/server/controllers/SearchController.js +++ b/server/controllers/SearchController.js @@ -49,5 +49,12 @@ class SearchController { } res.json(chapterData) } + + async findMusicTrack(req, res) { + const tracks = await this.musicFinder.searchTrack(req.query || {}) + res.json({ + tracks + }) + } } module.exports = new SearchController() \ No newline at end of file diff --git a/server/finders/MusicFinder.js b/server/finders/MusicFinder.js new file mode 100644 index 00000000..938cae83 --- /dev/null +++ b/server/finders/MusicFinder.js @@ -0,0 +1,12 @@ +const MusicBrainz = require('../providers/MusicBrainz') + +class MusicFinder { + constructor() { + this.musicBrainz = new MusicBrainz() + } + + searchTrack(options) { + return this.musicBrainz.searchTrack(options) + } +} +module.exports = MusicFinder \ No newline at end of file diff --git a/server/providers/MusicBrainz.js b/server/providers/MusicBrainz.js new file mode 100644 index 00000000..6ae24596 --- /dev/null +++ b/server/providers/MusicBrainz.js @@ -0,0 +1,51 @@ +const axios = require('axios') +const packageJson = require('../../package.json') +const Logger = require('../Logger') +const { isNullOrNaN } = require('../utils/index') + +class MusicBrainz { + constructor() { } + + get userAgentString() { + return `audiobookshelf/${packageJson.version} (https://audiobookshelf.org)` + } + + // https://musicbrainz.org/doc/MusicBrainz_API/Search + searchTrack(options) { + let luceneParts = [] + if (options.artist) { + luceneParts.push(`artist:${options.artist}`) + } + if (options.isrc) { + luceneParts.push(`isrc:${options.isrc}`) + } + if (options.title) { + luceneParts.push(`recording:${options.title}`) + } + if (options.album) { + luceneParts.push(`release:${options.album}`) + } + if (!luceneParts.length) { + Logger.error(`[MusicBrainz] Invalid search options - must have at least one of artist, isrc, title, album`) + return [] + } + + const query = { + query: luceneParts.join(' AND '), + limit: isNullOrNaN(options.limit) ? 15 : Number(options.limit), + fmt: 'json' + } + const config = { + headers: { + 'User-Agent': this.userAgentString + } + } + return axios.get('https://musicbrainz.org/ws/2/recording', { params: query }, config).then((response) => { + return response.data.recordings || [] + }).catch((error) => { + Logger.error(`[MusicBrainz] search request error`, error) + return [] + }) + } +} +module.exports = MusicBrainz \ No newline at end of file diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js index e3455cfd..4ca1aef6 100644 --- a/server/routers/ApiRouter.js +++ b/server/routers/ApiRouter.js @@ -30,6 +30,7 @@ const MiscController = require('../controllers/MiscController') const BookFinder = require('../finders/BookFinder') const AuthorFinder = require('../finders/AuthorFinder') const PodcastFinder = require('../finders/PodcastFinder') +const MusicFinder = require('../finders/MusicFinder') const Author = require('../objects/entities/Author') const Series = require('../objects/entities/Series') @@ -56,6 +57,7 @@ class ApiRouter { this.bookFinder = new BookFinder() this.authorFinder = new AuthorFinder() this.podcastFinder = new PodcastFinder() + this.musicFinder = new MusicFinder() this.router = express() this.init() @@ -253,6 +255,7 @@ class ApiRouter { this.router.get('/search/podcast', SearchController.findPodcasts.bind(this)) this.router.get('/search/authors', SearchController.findAuthor.bind(this)) this.router.get('/search/chapters', SearchController.findChapters.bind(this)) + this.router.get('/search/tracks', SearchController.findMusicTrack.bind(this)) // // Cache Routes (Admin and up)