116 lines
3.6 KiB
JavaScript
116 lines
3.6 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const axios = require('axios');
|
|
|
|
const reverseMap = {
|
|
'c': '1B', 'a minor': '1A', 'am': '1A',
|
|
'g': '2B', 'e minor': '2A', 'em': '2A',
|
|
'd': '3B', 'b minor': '3A', 'bm': '3A',
|
|
'a': '4B', 'f# minor': '4A', 'f#m': '4A',
|
|
'e': '5B', 'c# minor': '5A', 'c#m': '5A',
|
|
'b': '6B', 'g# minor': '6A', 'g#m': '6A',
|
|
'f#': '7B', 'd# minor': '7A', 'd#m': '7A',
|
|
'db': '8B', 'c#': '8B', 'bb minor': '8A', 'a# minor': '8A', 'bbm': '8A',
|
|
'ab': '9B', 'g#': '9B', 'f minor': '9A', 'fm': '9A',
|
|
'eb': '10B', 'd#': '10B', 'c minor': '10A', 'cm': '10A',
|
|
'bb': '11B', 'a#': '11B', 'g minor': '11A', 'gm': '11A',
|
|
'f': '12B', 'd minor': '12A', 'dm': '12A',
|
|
'c major': '1B', 'g major': '2B', 'd major': '3B', 'a major': '4B',
|
|
'e major': '5B', 'b major': '6B', 'f# major': '7B', 'db major': '8B',
|
|
'ab major': '9B', 'eb major': '10B', 'bb major': '11B', 'f major': '12B'
|
|
};
|
|
|
|
function standardizeKey(keyString) {
|
|
if (!keyString) return null;
|
|
const str = keyString.toLowerCase().trim();
|
|
return reverseMap[str] || null;
|
|
}
|
|
|
|
router.get('/', async (req, res) => {
|
|
const track = req.query.track;
|
|
const artist = req.query.artist;
|
|
|
|
if (!track || !artist) {
|
|
return res.status(400).json({ error: 'Missing track or artist' });
|
|
}
|
|
|
|
let bpm = null;
|
|
let camelotKey = null;
|
|
let tags = [];
|
|
|
|
try {
|
|
// 1. GetSongBPM API
|
|
if (process.env.GETSONGBPM_API_KEY) {
|
|
const gsbRes = await axios.get('https://api.getsong.co/search/', {
|
|
params: {
|
|
api_key: process.env.GETSONGBPM_API_KEY,
|
|
type: 'both',
|
|
lookup: `song:${track} artist:${artist}`
|
|
}
|
|
}).catch(err => {
|
|
console.error('GetSongBPM Error:', err.message);
|
|
return { data: { search: [] } };
|
|
});
|
|
|
|
const searchResults = gsbRes.data.search;
|
|
if (searchResults && searchResults.length > 0) {
|
|
// get song id
|
|
const songId = searchResults[0].song_id;
|
|
if (songId) {
|
|
const songRes = await axios.get('https://api.getsong.co/song/', {
|
|
params: { api_key: process.env.GETSONGBPM_API_KEY, id: songId }
|
|
}).catch(() => ({ data: { song: {} } }));
|
|
|
|
if (songRes.data.song) {
|
|
bpm = songRes.data.song.tempo;
|
|
const keyStr = songRes.data.song.key_of;
|
|
camelotKey = standardizeKey(keyStr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. Last.fm API
|
|
if (process.env.LASTFM_API_KEY) {
|
|
const lfmRes = await axios.get('https://ws.audioscrobbler.com/2.0/', {
|
|
params: {
|
|
method: 'track.getInfo',
|
|
api_key: process.env.LASTFM_API_KEY,
|
|
artist: artist,
|
|
track: track,
|
|
format: 'json'
|
|
}
|
|
}).catch(err => {
|
|
console.error('Last.fm Error:', err.message);
|
|
return { data: {} };
|
|
});
|
|
|
|
if (lfmRes.data && lfmRes.data.track && lfmRes.data.track.toptags && lfmRes.data.track.toptags.tag) {
|
|
tags = lfmRes.data.track.toptags.tag
|
|
.slice(0, 4) // top 4 tags
|
|
.map(t => t.name.toLowerCase());
|
|
}
|
|
}
|
|
|
|
// Fallbacks if APIs failed or didn't find anything
|
|
if (!bpm) bpm = 120;
|
|
if (!camelotKey) camelotKey = '8B'; // random default
|
|
if (tags.length === 0) tags = ['electronic'];
|
|
|
|
res.json({
|
|
status: 'success',
|
|
data: {
|
|
key: camelotKey,
|
|
bpm: Math.round(bpm),
|
|
tags: tags
|
|
}
|
|
});
|
|
|
|
} catch (err) {
|
|
console.error('Metadata error:', err.message);
|
|
res.status(500).json({ error: 'Failed to fetch metadata' });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|