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

217 lines
6.6 KiB
JavaScript

const express = require('express');
const { streamHandler } = require('./src/streamHandler');
const { geocodeCity } = require('./src/geocode');
const { getAllMusicFiles, initializeSharedPlaylist } = 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 (with optional music parameter)
app.get('/stream', (req, res) => {
const { music = 'false' } = req.query;
const useMusic = music === 'true';
const startTime = Date.now();
streamHandler(req, res, {
useMusic,
musicPath: MUSIC_PATH,
startTime
});
});
// 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') {
// Start geocoding (only call once)
const geocodePromise = geocodeCity(city);
// Try to use quick result if available within 1 second
const quickResult = Promise.race([
geocodePromise,
new Promise((_, reject) => setTimeout(() => reject(new Error('Geocoding timeout')), 1000))
]).catch(() => null); // Timeout = null, will use late result
// Build URL from quick result if available
const urlPromise = quickResult.then(geoResult => {
if (geoResult) {
// Got quick result
return buildWeatherUrl(geoResult.lat, geoResult.lon, weatherSettings);
}
return null; // Will use initial black screen
});
// Late geocode promise (reuses the same geocode call)
lateGeocodePromise = geocodePromise.then(geoResult => {
return buildWeatherUrl(geoResult.lat, geoResult.lon, weatherSettings);
}).catch(err => {
console.warn(`Geocoding failed for ${city}, using fallback`);
// Fallback to Toronto
return buildWeatherUrl(43.6532, -79.3832, weatherSettings);
});
} else {
// Toronto default
initialUrl = buildWeatherUrl(43.6532, -79.3832, weatherSettings);
}
const startTime = Date.now();
// 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,
startTime
});
});
// 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}`);
if (process.env.WS4KP_EXTERNAL_PORT) {
console.log(`WS4KP weather service on port ${process.env.WS4KP_EXTERNAL_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(`\nInitializing music library at ${MUSIC_PATH}...`);
const validFiles = getAllMusicFiles(MUSIC_PATH);
if (validFiles.length > 0) {
console.log(`Music library ready: ${validFiles.length} tracks validated`);
// Initialize shared playlist at startup
initializeSharedPlaylist(MUSIC_PATH);
console.log('');
} else {
console.log(`Warning: No valid music files found in ${MUSIC_PATH}\n`);
}
}
});