streamflow/backend/utils/radioMetadata.js

325 lines
9.4 KiB
JavaScript
Raw Permalink Normal View History

const axios = require('axios');
const logger = require('./logger');
/**
* Radio station metadata providers
* Maps station names/URLs to their API endpoints or scraping methods
*/
// Europa FM API
async function getEuropaFMMetadata() {
try {
const response = await axios.get('https://www.europafm.ro/now-playing/', {
timeout: 5000,
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
});
// Parse the HTML or JSON response
const html = response.data;
// Look for common patterns in Europa FM's page
const titleMatch = html.match(/<div class="now-playing-title">([^<]+)<\/div>/i) ||
html.match(/<span class="track-title">([^<]+)<\/span>/i) ||
html.match(/"title":"([^"]+)"/);
const artistMatch = html.match(/<div class="now-playing-artist">([^<]+)<\/div>/i) ||
html.match(/<span class="track-artist">([^<]+)<\/span>/i) ||
html.match(/"artist":"([^"]+)"/);
if (titleMatch || artistMatch) {
return {
title: titleMatch ? titleMatch[1].trim() : null,
artist: artistMatch ? artistMatch[1].trim() : null,
source: 'Europa FM Website'
};
}
// Try API endpoint
try {
const apiResponse = await axios.get('https://www.europafm.ro/api/now-playing', {
timeout: 3000,
headers: { 'User-Agent': 'Mozilla/5.0' }
});
if (apiResponse.data && apiResponse.data.title) {
return {
title: apiResponse.data.title,
artist: apiResponse.data.artist || null,
source: 'Europa FM API'
};
}
} catch (apiError) {
// API might not exist, continue
}
return null;
} catch (error) {
logger.error('Europa FM metadata fetch error:', error.message);
return null;
}
}
// Radio Romania (various stations)
async function getRadioRomaniaMetadata(stationId = 'actualitati') {
try {
const response = await axios.get(`https://www.radioromania.ro/live/${stationId}/`, {
timeout: 5000,
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
});
const html = response.data;
const titleMatch = html.match(/"currentSong":"([^"]+)"/);
const artistMatch = html.match(/"currentArtist":"([^"]+)"/);
if (titleMatch || artistMatch) {
return {
title: titleMatch ? titleMatch[1].trim() : null,
artist: artistMatch ? artistMatch[1].trim() : null,
source: 'Radio Romania'
};
}
return null;
} catch (error) {
logger.error('Radio Romania metadata fetch error:', error.message);
return null;
}
}
// Magic FM
async function getMagicFMMetadata() {
try {
const response = await axios.get('https://www.magicfm.ro/now-playing/', {
timeout: 5000,
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
});
const html = response.data;
const titleMatch = html.match(/"title":"([^"]+)"/);
const artistMatch = html.match(/"artist":"([^"]+)"/);
if (titleMatch || artistMatch) {
return {
title: titleMatch ? titleMatch[1].trim() : null,
artist: artistMatch ? artistMatch[1].trim() : null,
source: 'Magic FM'
};
}
return null;
} catch (error) {
logger.error('Magic FM metadata fetch error:', error.message);
return null;
}
}
// Kiss FM
async function getKissFMMetadata() {
try {
const response = await axios.get('https://www.kissfm.ro/now-playing/', {
timeout: 5000,
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
});
const html = response.data;
const titleMatch = html.match(/"title":"([^"]+)"/);
const artistMatch = html.match(/"artist":"([^"]+)"/);
if (titleMatch || artistMatch) {
return {
title: titleMatch ? titleMatch[1].trim() : null,
artist: artistMatch ? artistMatch[1].trim() : null,
source: 'Kiss FM'
};
}
return null;
} catch (error) {
logger.error('Kiss FM metadata fetch error:', error.message);
return null;
}
}
// Pro FM
async function getProFMMetadata() {
try {
const response = await axios.get('https://www.profm.ro/now-playing/', {
timeout: 5000,
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
});
const html = response.data;
const titleMatch = html.match(/"title":"([^"]+)"/);
const artistMatch = html.match(/"artist":"([^"]+)"/);
if (titleMatch || artistMatch) {
return {
title: titleMatch ? titleMatch[1].trim() : null,
artist: artistMatch ? artistMatch[1].trim() : null,
source: 'Pro FM'
};
}
return null;
} catch (error) {
logger.error('Pro FM metadata fetch error:', error.message);
return null;
}
}
// Generic RadioBrowser API fallback
async function getRadioBrowserMetadata(stationName) {
try {
// Search for station
const searchResponse = await axios.get('https://de1.api.radio-browser.info/json/stations/search', {
params: {
name: stationName,
limit: 1
},
timeout: 5000,
headers: {
'User-Agent': 'StreamFlow/1.0'
}
});
if (searchResponse.data && searchResponse.data.length > 0) {
const station = searchResponse.data[0];
// Radio Browser doesn't provide real-time metadata, but we can try the station's homepage
if (station.homepage) {
try {
const homepageResponse = await axios.get(station.homepage, {
timeout: 3000,
headers: { 'User-Agent': 'Mozilla/5.0' }
});
const html = homepageResponse.data;
// Try common patterns
const patterns = [
/"nowPlaying":"([^"]+)"/,
/"current_track":"([^"]+)"/,
/<div[^>]*class="[^"]*now-playing[^"]*"[^>]*>([^<]+)</i,
/"title":"([^"]+)"/
];
for (const pattern of patterns) {
const match = html.match(pattern);
if (match && match[1]) {
const parts = match[1].split(' - ');
if (parts.length >= 2) {
return {
artist: parts[0].trim(),
title: parts.slice(1).join(' - ').trim(),
source: 'Radio Browser + Website'
};
}
return {
title: match[1].trim(),
artist: null,
source: 'Radio Browser + Website'
};
}
}
} catch (homepageError) {
// Homepage fetch failed, ignore
}
}
}
return null;
} catch (error) {
logger.error('Radio Browser metadata fetch error:', error.message);
return null;
}
}
/**
* Main function to get metadata for a radio station
* Tries to identify the station and use the appropriate provider
*/
async function getRadioStationMetadata(channelName, channelUrl) {
const nameLower = channelName.toLowerCase();
const urlLower = channelUrl ? channelUrl.toLowerCase() : '';
logger.info(`[RadioMetadata] Fetching metadata for: ${channelName}`);
// Try specific providers based on station name or URL
try {
// Europa FM
if (nameLower.includes('europa') && nameLower.includes('fm')) {
const metadata = await getEuropaFMMetadata();
if (metadata) {
logger.info(`[RadioMetadata] Found metadata from ${metadata.source}`);
return metadata;
}
}
// Radio Romania
if (nameLower.includes('radio') && nameLower.includes('romania')) {
let stationId = 'actualitati';
if (nameLower.includes('muzical')) stationId = 'muzical';
if (nameLower.includes('cultural')) stationId = 'cultural';
if (nameLower.includes('Cluj')) stationId = 'cluj';
const metadata = await getRadioRomaniaMetadata(stationId);
if (metadata) {
logger.info(`[RadioMetadata] Found metadata from ${metadata.source}`);
return metadata;
}
}
// Magic FM
if (nameLower.includes('magic') && nameLower.includes('fm')) {
const metadata = await getMagicFMMetadata();
if (metadata) {
logger.info(`[RadioMetadata] Found metadata from ${metadata.source}`);
return metadata;
}
}
// Kiss FM
if (nameLower.includes('kiss') && nameLower.includes('fm')) {
const metadata = await getKissFMMetadata();
if (metadata) {
logger.info(`[RadioMetadata] Found metadata from ${metadata.source}`);
return metadata;
}
}
// Pro FM
if (nameLower.includes('pro') && nameLower.includes('fm')) {
const metadata = await getProFMMetadata();
if (metadata) {
logger.info(`[RadioMetadata] Found metadata from ${metadata.source}`);
return metadata;
}
}
// Fallback to RadioBrowser
const metadata = await getRadioBrowserMetadata(channelName);
if (metadata) {
logger.info(`[RadioMetadata] Found metadata from ${metadata.source}`);
return metadata;
}
} catch (error) {
logger.error(`[RadioMetadata] Error fetching metadata: ${error.message}`);
}
logger.info(`[RadioMetadata] No external metadata found for ${channelName}`);
return null;
}
module.exports = {
getRadioStationMetadata
};