99 lines
2.7 KiB
JavaScript
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;
|