mirror of
https://github.com/xtrll/MusicMetaFinder.git
synced 2026-04-17 21:54:09 +02:00
feat: add image retrieval functionality using coverartarchive.org API
This commit is contained in:
1
app.js
1
app.js
@@ -24,6 +24,7 @@ async function main() {
|
||||
const recordingIds = await recognizeAudioFiles(audioFiles);
|
||||
// Fetch the audio metadata from Spotify using the recognized track IDs
|
||||
const audioMetadata = await retrieveMetadata(recordingIds);
|
||||
console.log(audioMetadata);
|
||||
// Write the fetched metadata into the audio file
|
||||
// const processedAudioFiles = await fileController.writeMetadata(audioMetadata, audioFiles);
|
||||
} catch (e) {
|
||||
|
||||
33
src/api/imageRetrieval.js
Normal file
33
src/api/imageRetrieval.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import axios from 'axios';
|
||||
|
||||
/**
|
||||
* Retrieves the album art image URL for a given album ID.
|
||||
* It attempts to get the image from the `coverartarchive.org` service.
|
||||
*
|
||||
* @param {string} albumId - The unique identifier for the album whose art is being retrieved.
|
||||
* @returns {Promise<string|null>} - A promise that resolves to the image URL or null if not found or in case of an error.
|
||||
*/
|
||||
export const getAlbumArt = (albumId) => {
|
||||
const endpoint = `http://coverartarchive.org/release/${albumId}/front`;
|
||||
|
||||
return axios.get(endpoint, { maxRedirects: 0 })
|
||||
.then((response) => {
|
||||
// If the status code is 200, the image URL should be in the responseURL
|
||||
if (response.status === 200) {
|
||||
return response.request.responseURL;
|
||||
} if (response.status === 307) {
|
||||
// If it's a 307 Temporary Redirect, the image URL will be in the headers 'location'
|
||||
return response.headers.location;
|
||||
}
|
||||
// If the response status is not 200 or 307, which we don't handle, resolve as null
|
||||
return null;
|
||||
})
|
||||
.catch((error) => {
|
||||
if (error.response && error.response.status === 307) {
|
||||
// In case of a 307 redirect status received in the error, return the 'location' header.
|
||||
return error.response.headers.location;
|
||||
}
|
||||
console.error('Error retrieving cover art:', error);
|
||||
return null;
|
||||
});
|
||||
};
|
||||
26
src/api/lyricRetrieval.js
Normal file
26
src/api/lyricRetrieval.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import axiosRetry from '../services/retryAxios.js';
|
||||
import { handleError } from '../errors/generalApiErrorHandler.js';
|
||||
|
||||
/**
|
||||
* Fetches lyrics for a specific song using the provided artist name and title.
|
||||
* Makes a call to the `lyrics.ovh` API and returns the lyrics found, if any.
|
||||
*
|
||||
* @param {string} artist - The name of the artist.
|
||||
* @param {string} title - The title of the song.
|
||||
* @returns {Promise<string|null>} Promise object representing the lyrics for the song or null if not found or in case of an error.
|
||||
*/
|
||||
export default function fetchLyrics(artist, title) {
|
||||
const endpoint = `https://api.lyrics.ovh/v1/${encodeURIComponent(artist)}/${encodeURIComponent(title)}`;
|
||||
|
||||
return axiosRetry.get(endpoint)
|
||||
.then((response) =>
|
||||
// Access the lyrics from the response data object
|
||||
response.data.lyrics || null, // If no lyrics are found, return null
|
||||
)
|
||||
.catch((error) => {
|
||||
// Use the general API error handler to handle the error
|
||||
const errorMessage = handleError(error, `${artist} - ${title}`);
|
||||
console.error(errorMessage);
|
||||
return null; // Return null to indicate failure to retrieve lyrics
|
||||
});
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import axios from 'axios';
|
||||
|
||||
export default async function (artist, title) {
|
||||
const endpoint = `https://api.lyrics.ovh/v1/${artist}/${title}`;
|
||||
|
||||
try {
|
||||
const response = await axios.get(endpoint);
|
||||
return response.data.lyrics;
|
||||
} catch (error) {
|
||||
console.error('Error fetching lyrics: ', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import axiosRetry from '../services/retryAxios.js';
|
||||
import { handleError } from '../errors/musicBrainzApiErrorHandler.js';
|
||||
// import { handleError } from '../errors/musicBrainzApiErrorHandler.js';
|
||||
import { handleError } from '../errors/generalApiErrorHandler.js';
|
||||
|
||||
/**
|
||||
* Retrieves the metadata for a recording from MusicBrainz.
|
||||
@@ -30,6 +28,7 @@ export default async function getAudioMetadata(recordingId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log('Metadata retrieval successful for:', recordingId);
|
||||
return data;
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import getTrackMetadata from '../api/metadataRetrieval.js';
|
||||
import getLyrics from '../api/lyricRetrieval.js';
|
||||
import {getAlbumArt} from "../api/imageRetrieval.js";
|
||||
|
||||
/**
|
||||
* Controller to handle retrieval of metadata for an array of MusicBrainz recording IDs.
|
||||
@@ -11,10 +13,29 @@ export async function retrieveMetadata(recordingIds) {
|
||||
if (!Array.isArray(recordingIds)) {
|
||||
throw new Error('MetadataController expects an array of recordingIds');
|
||||
}
|
||||
const metadata = recordingIds.map((recordingId) => getTrackMetadata(recordingId));
|
||||
return metadata;
|
||||
|
||||
// Prepare an array to hold the metadata results initialized as an array of Promises
|
||||
const metadataPromises = recordingIds.map(async (recordingId) => {
|
||||
const trackMetadata = await getTrackMetadata(recordingId);
|
||||
const artist = trackMetadata['artist-credit']?.[0]?.name;
|
||||
const title = trackMetadata.title;
|
||||
const albumId = trackMetadata.releases[0].id
|
||||
|
||||
// Assume getLyrics requires artist name and track name, which are to be obtained from the trackMetadata
|
||||
const lyrics = await getLyrics(artist, title);
|
||||
const albumArt = await getAlbumArt(albumId);
|
||||
|
||||
// Combine the track metadata with the lyrics
|
||||
return {
|
||||
...trackMetadata,
|
||||
albumArt,
|
||||
lyrics
|
||||
};
|
||||
});
|
||||
|
||||
return await Promise.all(metadataPromises);
|
||||
} catch (e) {
|
||||
console.error('Error retrieving metadata from MusicBrainz:', e);
|
||||
console.error('Error retrieving metadata from MusicBrainz or lyrics:', e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
export function handleError(error, recordingId) {
|
||||
export function handleError(error, identifier) {
|
||||
let message = '';
|
||||
|
||||
// Handler when the API responds with an error status.
|
||||
if (error.response) {
|
||||
const { status } = error.response;
|
||||
console.error(`Error with recording ID: ${recordingId}`);
|
||||
console.error(`Request failed with status: ${status}`);
|
||||
console.error('Headers:', error.response.headers);
|
||||
console.error('Data:', error.response.data);
|
||||
|
||||
let message = `API responded with an error: ${error.response.data.message || status}`;
|
||||
message = `API responded with an error for ${identifier}: ${error.response.data.message || status}`;
|
||||
|
||||
switch (status) {
|
||||
case 400:
|
||||
@@ -20,25 +21,27 @@ export function handleError(error, recordingId) {
|
||||
message += ' - Forbidden: You do not have access to this resource.';
|
||||
break;
|
||||
case 404:
|
||||
message += ' - Not Found: The requested resource was not found on the server.';
|
||||
message += ' - Not Found: The requested resource or endpoint was not found on the server.';
|
||||
break;
|
||||
case 503:
|
||||
message += ' - Service Unavailable: The server is not ready to handle the request. Common causes include server overload or server taken down for maintenance.';
|
||||
message += ' - Service Unavailable: The server is not ready to handle the request.';
|
||||
break;
|
||||
default:
|
||||
message += ' - An unexpected error occurred.';
|
||||
}
|
||||
return message;
|
||||
|
||||
} else if (error.request) {
|
||||
// Case when the request was made but no response was received.
|
||||
} if (error.request) {
|
||||
console.error(`No response received for recording ID: ${recordingId}`);
|
||||
console.error(`No response received for ${identifier}`);
|
||||
console.error('Error request:', error.request);
|
||||
return `No response from API for recording ID: ${recordingId}`;
|
||||
|
||||
// Errors that occur before the request is made, during setup.
|
||||
message = `No response from API for ${identifier}`;
|
||||
} else {
|
||||
// Errors that occur before the request is made, during setup.
|
||||
console.error(`Error setting up the request for ${identifier}`);
|
||||
console.error('Error message:', error.message);
|
||||
message = `Problem with setting up the request for ${identifier} - ${error.message}`;
|
||||
}
|
||||
console.error(`Error setting up the request for recording ID: ${recordingId}`);
|
||||
console.error('Error message:', error.message);
|
||||
return `Problem with setting up the request for recording ID: ${recordingId} - ${error.message}`;
|
||||
|
||||
// Log the message and return it
|
||||
console.error(message);
|
||||
return message;
|
||||
}
|
||||
Reference in New Issue
Block a user