Initial commit: StreamFlow IPTV platform
This commit is contained in:
commit
73a8ae9ffd
1240 changed files with 278451 additions and 0 deletions
324
backend/utils/radioMetadata.js
Normal file
324
backend/utils/radioMetadata.js
Normal file
|
|
@ -0,0 +1,324 @@
|
|||
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
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue