search field visibility changed, tournament import fixed
All checks were successful
Deploy tt-tagebuch / deploy (push) Successful in 5m4s

This commit is contained in:
Torsten Schulz (local)
2026-09-04 07:33:52 +02:00
parent 5ac126bedb
commit 6aeff309cd
2 changed files with 68 additions and 2 deletions

View File

@@ -8,6 +8,48 @@ const MY_TT_BASE_URL = 'https://www.mytischtennis.de';
const cleanText = (value = '') => String(value).replace(/<[^>]*>/g, ' ').replace(/&nbsp;/gi, ' ').replace(/&amp;/gi, '&').replace(/\s+/g, ' ').trim();
const absoluteUrl = (href) => href.startsWith('http') ? href : `${MY_TT_BASE_URL}${href.startsWith('/') ? '' : '/'}${href}`;
function extractJsonArray(html, propertyName) {
const propertyIndex = String(html).indexOf(`"${propertyName}":[`);
if (propertyIndex < 0) return [];
const start = String(html).indexOf('[', propertyIndex);
let depth = 0;
let quoted = false;
let escaped = false;
for (let index = start; index < String(html).length; index += 1) {
const character = String(html)[index];
if (quoted) {
if (escaped) escaped = false;
else if (character === '\\') escaped = true;
else if (character === '"') quoted = false;
continue;
}
if (character === '"') quoted = true;
else if (character === '[') depth += 1;
else if (character === ']') {
depth -= 1;
if (depth === 0) {
try { return JSON.parse(String(html).slice(start, index + 1)); }
catch (_error) { return []; }
}
}
}
return [];
}
function parseTournamentWorldCalendar(html, federation) {
const tournaments = extractJsonArray(html, 'tournaments');
return tournaments
.filter((tournament) => tournament?.id && tournament?.name)
.map((tournament) => ({
title: cleanText(tournament.name).slice(0, 255),
eventDate: tournament.formattedStartDate || null,
organizer: cleanText(tournament.host || '').slice(0, 255) || null,
location: cleanText([tournament.location_zip, tournament.location].filter(Boolean).join(' ')).slice(0, 255) || null,
sourceUrl: `${MY_TT_BASE_URL}/turnierwelt/turniere/${encodeURIComponent(tournament.id)}`,
federation: tournament.organization_short || federation,
}));
}
function parseCalendar(html, federation) {
const items = [];
const anchors = [...String(html).matchAll(/<a\b[^>]*href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi)];
@@ -31,9 +73,23 @@ class TournamentSuggestionService {
const club = await Club.findByPk(clubId);
if (!club) throw new Error('Verein nicht gefunden.');
const federation = String(club.myTischtennisFedNickname || 'HeTTV').trim();
const calendarUrl = `${MY_TT_BASE_URL}/click-tt/${encodeURIComponent(federation)}/turnierkalender`;
const startDate = new Date();
startDate.setHours(0, 0, 0, 0);
const endDate = new Date(startDate);
endDate.setMonth(endDate.getMonth() + 6);
const calendarParams = new URLSearchParams({
association: federation,
date_start: startDate.toISOString().slice(0, 10),
date_end: endDate.toISOString().slice(0, 10),
});
const calendarUrl = `${MY_TT_BASE_URL}/turnierwelt/kalender?${calendarParams.toString()}`;
const response = await axios.get(calendarUrl, { timeout: 30000, headers: { 'User-Agent': 'Trainingstagebuch/1.0 (+turnierkalender)' } });
const parsed = parseCalendar(response.data, federation);
let parsed = parseTournamentWorldCalendar(response.data, federation);
if (!parsed.length) {
const legacyCalendarUrl = `${MY_TT_BASE_URL}/click-tt/${encodeURIComponent(federation)}/turnierkalender`;
const legacyResponse = await axios.get(legacyCalendarUrl, { timeout: 30000, headers: { 'User-Agent': 'Trainingstagebuch/1.0 (+turnierkalender)' } });
parsed = parseCalendar(legacyResponse.data, federation);
}
if (!parsed.length) throw new Error('Im Turnierkalender konnten keine Turniere erkannt werden.');
let newCount = 0;