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;