Files
KeySelector/server/routes/metadata.js
T

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;