324 lines
9.4 KiB
JavaScript
324 lines
9.4 KiB
JavaScript
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
|
|
};
|