소스 검색

feature: add job/action to fetch MusicBrainz data for an artist

Kristian Vos 1 개월 전
부모
커밋
c05f64fcbb
2개의 변경된 파일119개의 추가작업 그리고 1개의 파일을 삭제
  1. 30 0
      backend/logic/actions/albums.js
  2. 89 1
      backend/logic/musicbrainz.js

+ 30 - 0
backend/logic/actions/albums.js

@@ -7,6 +7,7 @@ import moduleManager from "../../index";
 
 const DBModule = moduleManager.modules.db;
 const UtilsModule = moduleManager.modules.utils;
+const MusicBrainzModule = moduleManager.modules.musicbrainz;
 
 export default {
 	/**
@@ -111,5 +112,34 @@ export default {
 				}
 			);
 		}
+	),
+
+	/**
+	 * Gets MusicBrainz recordings release release groups data
+	 * @param session
+	 * @param artistId - the MusicBrainz artist id
+	 * @param {Function} cb
+	 */
+	getMusicBrainzRecordingsReleasesReleaseGroups: useHasPermission(
+		"apis.searchDiscogs",
+		async function getMusicBrainzRecordingsReleasesReleaseGroups(session, artistId, cb) {
+			// TODO update permission
+			const response = await MusicBrainzModule.runJob(
+				"GET_RECORDINGS_RELEASES_RELEASE_GROUPS",
+				{ artistId },
+				this
+			);
+
+			this.log(
+				"SUCCESS",
+				"ALBUMS_GET_MUSICBRAINZ_RECORDINGS_RELEASES_RELEASE_GROUPS",
+				`User "${session.userId}" got MusicBrainz recordings releases release groups for artist "${artistId}".`
+			);
+
+			return cb({
+				status: "success",
+				data: response
+			});
+		}
 	)
 };

+ 89 - 1
backend/logic/musicbrainz.js

@@ -34,12 +34,13 @@ class RateLimitter {
 
 let MusicBrainzModule;
 let DBModule;
+let CacheModule;
 
 class _MusicBrainzModule extends CoreClass {
 	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		super("musicbrainz", {
-			concurrency: 10
+			concurrency: 1
 		});
 
 		MusicBrainzModule = this;
@@ -51,6 +52,7 @@ class _MusicBrainzModule extends CoreClass {
 	 */
 	async initialize() {
 		DBModule = this.moduleManager.modules.db;
+		CacheModule = this.moduleManager.modules.cache;
 
 		this.genericApiRequestModel = this.GenericApiRequestModel = await DBModule.runJob("GET_MODEL", {
 			modelName: "genericApiRequest"
@@ -85,6 +87,10 @@ class _MusicBrainzModule extends CoreClass {
 		MusicBrainzModule.rateLimiter.restart();
 
 		const responseData = await new Promise((resolve, reject) => {
+			console.log(
+				"INFO",
+				`Making MusicBrainz API request to ${url} with ${new URLSearchParams(params).toString()}`
+			);
 			MusicBrainzModule.axios
 				.get(url, {
 					params,
@@ -117,6 +123,88 @@ class _MusicBrainzModule extends CoreClass {
 
 		return responseData;
 	}
+
+	/**
+	 * Gets a list of recordings, releases and release groups for a given artist
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.artistId - MusicBrainz artist id
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
+	async GET_RECORDINGS_RELEASES_RELEASE_GROUPS(payload) {
+		const { artistId } = payload;
+		// TODO this job caches a response to prevent overloading the API, but doesn't do anything to prevent the same job for the same artistId from running more than once at the same time
+
+		const existingResponse = await CacheModule.runJob(
+			"HGET",
+			{
+				table: "musicbrainzRecordingsReleasesReleaseGroups",
+				key: artistId
+			},
+			this
+		);
+		if (existingResponse) return existingResponse;
+
+		const fetchDate = new Date();
+		let maxReleases = 0;
+		let releases = [];
+		do {
+			const offset = releases.length;
+			// eslint-disable-next-line no-await-in-loop
+			const response = await MusicBrainzModule.runJob(
+				"GET_RECORDINGS_RELEASES_RELEASE_GROUPS_PAGE",
+				{ artistId, offset },
+				this
+			);
+			maxReleases = response["release-count"];
+			releases = [...releases, ...response.releases];
+			if (response.releases.length === 0) break;
+		} while (maxReleases >= releases.length);
+
+		const response = {
+			releases,
+			fetchDate
+		};
+
+		await CacheModule.runJob(
+			"HSET",
+			{
+				table: "musicbrainzRecordingsReleasesReleaseGroups",
+				key: artistId,
+				value: JSON.stringify(response)
+			},
+			this
+		);
+
+		return response;
+	}
+
+	/**
+	 * Gets a list of recordings, releases and release groups for a given artist, for a given page
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.artistId - MusicBrainz artist id
+	 * @param {string} payload.offset - offset by how much
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
+	async GET_RECORDINGS_RELEASES_RELEASE_GROUPS_PAGE(payload) {
+		const { artistId, offset } = payload;
+
+		const response = await MusicBrainzModule.runJob(
+			"API_CALL",
+			{
+				url: `https://musicbrainz.org/ws/2/release`,
+				params: {
+					fmt: "json",
+					artist: artistId,
+					inc: "release-groups+recordings",
+					limit: 100,
+					offset
+				}
+			},
+			this
+		);
+
+		return response;
+	}
 }
 
 export default new _MusicBrainzModule();