'use strict'; function readNumber(value, fallback, min, max) { const n = Number(value); if (value === undefined || value === null || value === '' || !Number.isFinite(n)) { return fallback; } return Math.min(Math.max(n, min), max); } const settings = { xtreamUrl: (process.env.XTREAM_URL || '').trim().replace(/\/+$/, ''), xtreamUsername: process.env.XTREAM_USERNAME || '', xtreamPassword: process.env.XTREAM_PASSWORD || '', searchTerm: (process.env.SEARCH_TERM || '').trim(), maxChannels: Math.round(readNumber(process.env.MAX_CHANNELS, 3, 1, 10)), language: (process.env.LANGUAGE || 'nl').trim().toLowerCase(), eventDurationMin: readNumber(process.env.EVENT_DURATION_MIN, 150, 1, 1440), startGraceMin: readNumber(process.env.START_GRACE_MIN, 10, 0, 1440), refreshIntervalSec: readNumber(process.env.REFRESH_INTERVAL_SEC, 60, 10, 3600), sourceTimezone: (process.env.SOURCE_TIMEZONE || 'UTC').trim() || 'UTC', timezone: (process.env.TIMEZONE || 'Europe/Brussels').trim() || 'Europe/Brussels', listenPort: Math.round(readNumber(process.env.LISTEN_PORT, 8080, 1, 65535)), upstreamTimeoutMs: readNumber(process.env.UPSTREAM_TIMEOUT_MS, 15000, 1000, 120000), rejectUnauthorized: process.env.PROXY_INSECURE_TLS !== '1', logLevel: (process.env.LOG_LEVEL || 'info').trim().toLowerCase(), userAgent: process.env.USER_AGENT || 'xtream-ppv-bundler/1.0', healthFailThreshold: 3, eventDurationMs: 0, startGraceMs: 0, }; settings.eventDurationMs = settings.eventDurationMin * 60 * 1000; settings.startGraceMs = settings.startGraceMin * 60 * 1000; function validateTimezone(timezone, envName) { try { new Intl.DateTimeFormat('en-US', { timeZone: timezone }); } catch { throw new Error(`Invalid ${envName}: ${timezone}`); } } function validate() { const missing = []; if (!settings.xtreamUrl) missing.push('XTREAM_URL'); if (!settings.xtreamUsername) missing.push('XTREAM_USERNAME'); if (!settings.xtreamPassword) missing.push('XTREAM_PASSWORD'); if (!settings.searchTerm) missing.push('SEARCH_TERM'); if (missing.length) { throw new Error(`Missing required environment variables: ${missing.join(', ')}`); } let parsed; try { parsed = new URL(settings.xtreamUrl); } catch { throw new Error(`XTREAM_URL is not a valid URL: ${settings.xtreamUrl}`); } if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') { throw new Error('XTREAM_URL must use http or https'); } validateTimezone(settings.sourceTimezone, 'SOURCE_TIMEZONE'); validateTimezone(settings.timezone, 'TIMEZONE'); if (!settings.language) { throw new Error('LANGUAGE must not be empty'); } } module.exports = { settings, validate };