Some checks failed
Code Analysis and Production Deploy / analyze (push) Has been skipped
Code Analysis and Production Deploy / deploy-production (push) Has been skipped
Code Analysis and Production Deploy / deploy-test (push) Successful in 2m2s
Code Analysis and Production Deploy / analyze (pull_request) Failing after 33s
Code Analysis and Production Deploy / deploy-production (pull_request) Has been skipped
Code Analysis and Production Deploy / deploy-test (pull_request) Has been skipped
Require Package Version Change / check (pull_request) Failing after 10s
- Created `import-spielplan.js` to fetch and parse the match schedule from the specified URL, saving the output as JSON. - Added `run-spielplan-import.sh` to automate the execution of the import script and log output. - Introduced `spielplan.html` file to store the downloaded HTML content for further processing.
108 lines
2.8 KiB
JavaScript
108 lines
2.8 KiB
JavaScript
import { importSpielplan } from '../utils/spielplan-import.js'
|
|
|
|
const TIME_ZONE = 'Europe/Berlin'
|
|
const RUN_HOUR = 7
|
|
const RUN_MINUTE = 0
|
|
const MAX_TIMEOUT = 2_147_483_647
|
|
|
|
let timer = null
|
|
let running = false
|
|
|
|
function getTimeParts(date) {
|
|
const parts = new Intl.DateTimeFormat('en-CA', {
|
|
timeZone: TIME_ZONE,
|
|
year: 'numeric',
|
|
month: '2-digit',
|
|
day: '2-digit',
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
second: '2-digit',
|
|
hour12: false
|
|
}).formatToParts(date)
|
|
|
|
return Object.fromEntries(parts.map((part) => [part.type, part.value]))
|
|
}
|
|
|
|
function getTimeZoneOffset(date) {
|
|
const parts = getTimeParts(date)
|
|
const zonedAsUtc = Date.UTC(
|
|
Number(parts.year),
|
|
Number(parts.month) - 1,
|
|
Number(parts.day),
|
|
Number(parts.hour),
|
|
Number(parts.minute),
|
|
Number(parts.second)
|
|
)
|
|
|
|
return zonedAsUtc - date.getTime()
|
|
}
|
|
|
|
function zonedDateToUtc(year, month, day, hour, minute) {
|
|
const utcGuess = new Date(Date.UTC(year, month - 1, day, hour, minute, 0))
|
|
const offset = getTimeZoneOffset(utcGuess)
|
|
return new Date(utcGuess.getTime() - offset)
|
|
}
|
|
|
|
function nextRunAt(now = new Date()) {
|
|
const parts = getTimeParts(now)
|
|
let year = Number(parts.year)
|
|
let month = Number(parts.month)
|
|
let day = Number(parts.day)
|
|
let candidate = zonedDateToUtc(year, month, day, RUN_HOUR, RUN_MINUTE)
|
|
|
|
if (candidate <= now) {
|
|
const nextDay = zonedDateToUtc(year, month, day + 1, 12, 0)
|
|
const nextParts = getTimeParts(nextDay)
|
|
year = Number(nextParts.year)
|
|
month = Number(nextParts.month)
|
|
day = Number(nextParts.day)
|
|
candidate = zonedDateToUtc(year, month, day, RUN_HOUR, RUN_MINUTE)
|
|
}
|
|
|
|
return candidate
|
|
}
|
|
|
|
async function runImport(reason) {
|
|
if (running) return
|
|
|
|
running = true
|
|
try {
|
|
const result = await importSpielplan()
|
|
console.log(`[spielplan-import] ${reason}: ${result.matchCount} Spiele importiert (${result.source.season.dateStart} bis ${result.source.season.dateEnd})`)
|
|
} catch (error) {
|
|
console.error('[spielplan-import] Import fehlgeschlagen:', error)
|
|
} finally {
|
|
running = false
|
|
}
|
|
}
|
|
|
|
function scheduleNext() {
|
|
const runAt = nextRunAt()
|
|
const delay = Math.min(Math.max(runAt.getTime() - Date.now(), 1_000), MAX_TIMEOUT)
|
|
|
|
timer = setTimeout(async () => {
|
|
await runImport('taeglicher Lauf')
|
|
scheduleNext()
|
|
}, delay)
|
|
|
|
timer.unref?.()
|
|
console.log(`[spielplan-import] Naechster Lauf: ${runAt.toISOString()} (${TIME_ZONE} ${String(RUN_HOUR).padStart(2, '0')}:${String(RUN_MINUTE).padStart(2, '0')})`)
|
|
}
|
|
|
|
export default defineNitroPlugin((nitroApp) => {
|
|
if (process.env.SPIELPLAN_IMPORT_DISABLED === 'true') {
|
|
console.log('[spielplan-import] Scheduler deaktiviert')
|
|
return
|
|
}
|
|
|
|
scheduleNext()
|
|
|
|
if (process.env.SPIELPLAN_IMPORT_RUN_ON_START === 'true') {
|
|
runImport('Startlauf')
|
|
}
|
|
|
|
nitroApp.hooks.hookOnce('close', () => {
|
|
if (timer) clearTimeout(timer)
|
|
})
|
|
})
|