Files
KeySelector/server/routes/recognize.js
T

99 lines
2.7 KiB
JavaScript

const express = require('express');
const router = express.Router();
const multer = require('multer');
const { execFile } = require('child_process');
const fs = require('fs');
const os = require('os');
const path = require('path');
const axios = require('axios');
// Configure multer to save files to system temp directory
const upload = multer({ dest: os.tmpdir() });
router.post('/', upload.single('audio'), async (req, res) => {
if (!req.file) {
return res.status(400).json({ error: 'No audio file uploaded.' });
}
const filePath = req.file.path;
try {
// 1. Generate fingerprint using fpcalc
const { duration, fingerprint } = await new Promise((resolve, reject) => {
execFile('fpcalc', [filePath], (error, stdout, stderr) => {
if (error) {
return reject(new Error('fpcalc failed: ' + stderr));
}
let dur = null;
let fp = null;
stdout.split('\n').forEach(line => {
if (line.startsWith('DURATION=')) dur = parseInt(line.split('=')[1], 10);
if (line.startsWith('FINGERPRINT=')) fp = line.split('=')[1];
});
if (!dur || !fp) {
return reject(new Error('Invalid fpcalc output'));
}
resolve({ duration: dur, fingerprint: fp });
});
});
// 2. Query AcoustID API
const acoustidKey = process.env.ACOUSTID_API_KEY;
if (!acoustidKey) {
throw new Error('Missing ACOUSTID_API_KEY');
}
const response = await axios.get('https://api.acoustid.org/v2/lookup', {
params: {
client: acoustidKey,
meta: 'recordings',
duration: duration,
fingerprint: fingerprint
}
});
const results = response.data.results;
if (!results || results.length === 0) {
return res.json({ status: 'not_found' });
}
// Sort results by score (descending)
results.sort((a, b) => b.score - a.score);
const bestResult = results[0];
// Find a recording with title and artists
let title = 'Unknown';
let artist = 'Unknown';
if (bestResult.recordings && bestResult.recordings.length > 0) {
const rec = bestResult.recordings[0];
title = rec.title || 'Unknown';
if (rec.artists && rec.artists.length > 0) {
artist = rec.artists.map(a => a.name).join(' & ');
}
}
res.json({
status: 'success',
data: {
title: title,
artist: artist,
matchScore: Math.round(bestResult.score * 100) + '%'
}
});
} catch (err) {
console.error('Recognition error:', err.message);
res.status(500).json({ error: 'Failed to recognize audio' });
} finally {
// Clean up uploaded file
fs.unlink(filePath, () => {});
}
});
module.exports = router;