1
0
Files
ws4kp-to-hls/index.js

207 lines
6.5 KiB
JavaScript

const express = require('express');
const { streamHandler } = require('./src/streamHandler');
const { geocodeCity } = require('./src/geocode');
const { getAllMusicFiles } = require('./src/musicPlaylist');
const app = express();
const PORT = process.env.PORT || 3000;
const WS4KP_PORT = process.env.WS4KP_PORT || 8080;
const MUSIC_PATH = process.env.MUSIC_PATH || '/music';
/**
* Build WS4KP weather URL with given coordinates and settings
*/
function buildWeatherUrl(latitude, longitude, settings) {
const {
city,
showHazards,
showCurrent,
showLatestObservations,
showHourly,
showHourlyGraph,
showTravel,
showRegionalForecast,
showLocalForecast,
showExtendedForecast,
showAlmanac,
showRadar,
showMarineForecast,
showAQI,
units,
timeFormat
} = settings;
const isMetric = units.toLowerCase() === 'metric';
const temperatureUnit = isMetric ? '1.00' : '2.00';
const windUnit = isMetric ? '1.00' : '2.00';
const distanceUnit = isMetric ? '1.00' : '2.00';
const pressureUnit = isMetric ? '1.00' : '2.00';
const hoursFormat = timeFormat === '12h' ? '1.00' : '2.00';
const ws4kpPort = process.env.WS4KP_PORT || 8080;
const ws4kpBaseUrl = process.env.WS4KP_URL || `http://localhost:${ws4kpPort}`;
const ws4kpParams = new URLSearchParams({
'hazards-checkbox': showHazards,
'current-weather-checkbox': showCurrent,
'latest-observations-checkbox': showLatestObservations,
'hourly-checkbox': showHourly,
'hourly-graph-checkbox': showHourlyGraph,
'travel-checkbox': showTravel,
'regional-forecast-checkbox': showRegionalForecast,
'local-forecast-checkbox': showLocalForecast,
'extended-forecast-checkbox': showExtendedForecast,
'almanac-checkbox': showAlmanac,
'radar-checkbox': showRadar,
'marine-forecast-checkbox': showMarineForecast,
'aqi-forecast-checkbox': showAQI,
'settings-experimentalFeatures-checkbox': 'false',
'settings-hideWebamp-checkbox': 'true',
'settings-kiosk-checkbox': 'false',
'settings-scanLines-checkbox': 'false',
'settings-wide-checkbox': 'true',
'chkAutoRefresh': 'true',
'settings-windUnits-select': windUnit,
'settings-marineWindUnits-select': '1.00',
'settings-marineWaveHeightUnits-select': '1.00',
'settings-temperatureUnits-select': temperatureUnit,
'settings-distanceUnits-select': distanceUnit,
'settings-pressureUnits-select': pressureUnit,
'settings-hoursFormat-select': hoursFormat,
'settings-speed-select': '1.00',
'latLonQuery': city,
'latLon': JSON.stringify({ lat: latitude, lon: longitude }),
'kiosk': 'true'
});
return `${ws4kpBaseUrl}/?${ws4kpParams.toString()}`;
}
// Basic stream endpoint (no music)
app.get('/stream', (req, res) => {
streamHandler(req, res, {
useMusic: false,
musicPath: MUSIC_PATH
});
});
// Weather endpoint (with music)
app.get('/weather', async (req, res) => {
const {
city = 'Toronto, ON, CAN',
width = 1920,
height = 1080,
fps = 30,
hideLogo = 'false',
units = 'metric',
timeFormat = '24h',
showHazards = 'true',
showCurrent = 'true',
showHourly = 'true',
showHourlyGraph = 'true',
showLocalForecast = 'true',
showExtendedForecast = 'true',
showRadar = 'true',
showAQI = 'true',
showAlmanac = 'true',
showLatestObservations = 'false',
showRegionalForecast = 'false',
showTravel = 'false',
showMarineForecast = 'false'
} = req.query;
const weatherSettings = {
city,
showHazards,
showCurrent,
showLatestObservations,
showHourly,
showHourlyGraph,
showTravel,
showRegionalForecast,
showLocalForecast,
showExtendedForecast,
showAlmanac,
showRadar,
showMarineForecast,
showAQI,
units,
timeFormat
};
let lateGeocodePromise = null;
let initialUrl = 'data:text/html,<html><body style="margin:0;padding:0;background:#000"></body></html>';
if (city && city !== 'Toronto, ON, CAN') {
// Try quick geocode first
const geocodePromise = Promise.race([
geocodeCity(city),
new Promise((_, reject) => setTimeout(() => reject(new Error('Geocoding timeout')), 1000))
]).then(geoResult => {
console.log(`Geocoded: ${city} -> ${geoResult.displayName}`);
const finalUrl = buildWeatherUrl(geoResult.lat, geoResult.lon, weatherSettings);
console.log(`URL: ${finalUrl}`);
return { url: finalUrl, lat: geoResult.lat, lon: geoResult.lon };
}).catch(error => {
// Continue geocoding in background
return geocodeCity(city).then(geoResult => {
const finalUrl = buildWeatherUrl(geoResult.lat, geoResult.lon, weatherSettings);
console.log(`Geocoding completed: ${geoResult.displayName} (${geoResult.lat}, ${geoResult.lon})`);
console.log(`Final URL: ${finalUrl}`);
return { url: finalUrl, lat: geoResult.lat, lon: geoResult.lon, isLate: true };
}).catch(err => {
console.warn(`Geocoding failed: ${err.message}`);
// Fallback to Toronto
const fallbackUrl = buildWeatherUrl(43.6532, -79.3832, weatherSettings);
return { url: fallbackUrl, lat: 43.6532, lon: -79.3832, isLate: true };
});
});
lateGeocodePromise = geocodePromise.then(result => result.url);
} else {
// Toronto default
initialUrl = buildWeatherUrl(43.6532, -79.3832, weatherSettings);
console.log(`URL: ${initialUrl}`);
}
console.log(`Stream starting: ${city}`);
// Update request query for stream handler
req.query.url = initialUrl;
req.query.width = width;
req.query.height = height;
req.query.fps = fps;
req.query.hideLogo = hideLogo;
// Call stream handler with music enabled
return streamHandler(req, res, {
useMusic: true,
musicPath: MUSIC_PATH,
lateGeocodePromise
});
});
// Health check endpoint
app.get('/health', (req, res) => {
res.send('OK');
});
// Start server
app.listen(PORT, () => {
console.log(`Webpage to HLS server running on port ${PORT}`);
console.log(`WS4KP weather service on port ${WS4KP_PORT}`);
console.log(`Usage: http://localhost:${PORT}/stream?url=http://example.com`);
console.log(`Weather: http://localhost:${PORT}/weather?city=YourCity`);
// Pre-validate music files on startup to cache results
if (MUSIC_PATH) {
console.log(`\n🎵 Pre-validating music library at ${MUSIC_PATH}...`);
const validFiles = getAllMusicFiles(MUSIC_PATH);
if (validFiles.length > 0) {
console.log(`✅ Music library ready: ${validFiles.length} valid tracks cached\n`);
} else {
console.log(`⚠️ No valid music files found in ${MUSIC_PATH}\n`);
}
}
});