'use strict'; const { parseChannelName, normalizeText } = require('./channel-name'); const { liveUrlCandidates } = require('./xtream-client'); function defaultUrlBuilder(streamId) { return liveUrlCandidates(streamId); } function compareEvents(a, b) { const byStart = a.startAt.getTime() - b.startAt.getTime(); if (byStart !== 0) return byStart; const bySeq = (a.firstSeenSeq ?? 0) - (b.firstSeenSeq ?? 0); if (bySeq !== 0) return bySeq; return a.key.localeCompare(b.key); } function isEligible(event, now, { startGraceMs }) { return now >= event.startAt.getTime() - startGraceMs && now < event.endAt.getTime(); } function buildEvents(streams, options) { const { searchTerm, language, timezone, eventDurationMs, urlBuilder = defaultUrlBuilder, } = options; const term = normalizeText(searchTerm); const events = new Map(); const warnings = []; for (const stream of streams || []) { const rawName = String(stream && stream.name ? stream.name : ''); if (!rawName) continue; if (!term || !normalizeText(rawName).includes(term)) continue; const parsed = parseChannelName(rawName, { timezone }); if (!parsed) { warnings.push({ streamId: stream.stream_id, name: rawName, reason: 'unparseable_name' }); continue; } if (parsed.language && parsed.language !== language) continue; const key = `${normalizeText(parsed.title)}|${parsed.startAt.toISOString()}`; let event = events.get(key); if (!event) { event = { key, title: parsed.title, league: parsed.league, language: parsed.language || language, startAt: parsed.startAt, endAt: new Date(parsed.startAt.getTime() + eventDurationMs), sources: [], }; events.set(key, event); } const streamId = String(stream.stream_id); if (!event.sources.some((source) => source.streamId === streamId)) { event.sources.push({ streamId, name: rawName, candidates: urlBuilder(stream.stream_id).slice(), }); } } return { events, warnings }; } module.exports = { buildEvents, isEligible, compareEvents };