Files

117 lines
3.9 KiB
JavaScript

const express = require('express');
const router = express.Router();
const axios = require('axios');
const db = require('../db');
// GET /api/search?q=query
router.get('/', async (req, res) => {
const query = req.query.q;
if (!query || !query.trim()) {
return res.json({ status: 'success', data: [] });
}
const cleanQuery = query.trim();
try {
// 1. Search local PostgreSQL database first
const localTracks = await db.searchTracks(cleanQuery, 10);
const localMap = new Map();
localTracks.forEach(t => {
const key = `${t.artist.toLowerCase()} - ${t.title.toLowerCase()}`;
localMap.set(key, t);
});
// 2. Query Deezer public search API (no API key needed, high quality covers & metadata)
let onlineTracks = [];
try {
const deezerRes = await axios.get(`https://api.deezer.com/search?q=${encodeURIComponent(cleanQuery)}&limit=15`, {
timeout: 4000
});
if (deezerRes.data && Array.isArray(deezerRes.data.data)) {
onlineTracks = deezerRes.data.data.map(item => ({
title: item.title,
artist: item.artist ? item.artist.name : 'Unknown Artist',
cover_url: item.album && item.album.cover_medium ? item.album.cover_medium : (item.album ? item.album.cover : null),
preview_url: item.preview || null,
duration_sec: item.duration || 210,
deezer_id: item.id,
deezer_link: item.link || (item.id ? `https://www.deezer.com/track/${item.id}` : null)
}));
}
} catch (e) {
console.warn('[Search] Deezer search error:', e.message);
}
// 3. Merge results: Local tracks first, then online tracks not yet in local
const merged = [];
const seen = new Set();
// Add local tracks first
for (const lt of localTracks) {
const key = `${lt.artist.toLowerCase()} - ${lt.title.toLowerCase()}`;
seen.add(key);
merged.push({
id: lt.id,
title: lt.title,
artist: lt.artist,
camelot_key: lt.camelot_key,
bpm: lt.bpm,
music_key: lt.music_key,
cover_url: lt.cover_url,
preview_url: lt.preview_url,
duration_sec: lt.duration_sec,
energy: lt.energy,
danceability: lt.danceability,
happiness: lt.happiness,
deezer_id: lt.deezer_id || null,
deezer_link: lt.deezer_link || (lt.deezer_id ? `https://www.deezer.com/track/${lt.deezer_id}` : null),
is_local: true,
is_corrected: lt.is_corrected
});
}
// Add online tracks, enriching with local data if available
for (const ot of onlineTracks) {
const key = `${ot.artist.toLowerCase()} - ${ot.title.toLowerCase()}`;
if (seen.has(key)) continue;
seen.add(key);
let existingLocal = localMap.get(key);
if (!existingLocal) {
existingLocal = await db.findTrack(ot.artist, ot.title);
}
merged.push({
title: ot.title,
artist: ot.artist,
cover_url: ot.cover_url,
preview_url: ot.preview_url,
duration_sec: ot.duration_sec,
deezer_id: ot.deezer_id,
deezer_link: ot.deezer_link,
camelot_key: existingLocal ? existingLocal.camelot_key : null,
bpm: existingLocal ? existingLocal.bpm : null,
music_key: existingLocal ? existingLocal.music_key : null,
energy: existingLocal ? existingLocal.energy : null,
danceability: existingLocal ? existingLocal.danceability : null,
happiness: existingLocal ? existingLocal.happiness : null,
is_local: !!existingLocal,
is_corrected: existingLocal ? existingLocal.is_corrected : false
});
if (merged.length >= 15) break;
}
res.json({
status: 'success',
data: merged
});
} catch (err) {
console.error('[Search] General search error:', err.message);
res.status(500).json({ error: 'Search failed' });
}
});
module.exports = router;