diff --git a/README.md b/README.md index 6d2630b38..596f74ac3 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,9 @@ Use the provided URLs to sync with your Google Calendar instead of downloading t - **Customizable**: Select specific sports or teams you're interested in. - **Easy Integration**: Sync directly with Google Calendar using provided URLs. +## Translation +The translations have been generated automatically. If you notice any incorrect translations, please accept my apologies and feel free to open an issue with the correct translation. + ## Contribution Contributions are welcome! Feel free to open issues or submit pull requests. diff --git a/src/index.ts b/src/index.ts index 616cd2a6d..f6b9c7183 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,420 +1,6 @@ - -import cheerio from "cheerio"; -import Debug from "debug"; -import fs from "fs"; -import autoprefixer from "autoprefixer"; -import postcss from "postcss"; -import tailwindcss from "tailwindcss"; - -import { Event, Sport } from "./types"; - -import { getSportIcon } from "./sports"; -import { isValidNOC, getNOCName, getNOCFlag } from "./nocs"; -import { generateICS } from "./ics"; import { Calendar } from "./calendar"; -const debug = Debug("paris2024:index"); - -const downloadSchedule = async (sportKey: string) => { - debug(`Checking schedule for ${sportKey}`); - const cacheFile = `${__dirname}/../cache/${sportKey}.html`; - - if (!fs.existsSync(cacheFile)) { - debug(`Downloading schedule for ${sportKey} https://olympics.com/en/paris-2024/schedule/${sportKey}`); - const response = await fetch(`https://olympics.com/en/paris-2024/schedule/${sportKey}`); - const content = await response.text(); - fs.writeFileSync(cacheFile, content); - } - - const html = fs.readFileSync(cacheFile, "utf-8"); - const $ = cheerio.load(html); - return JSON.parse($("#__NEXT_DATA__").text()); -}; - -const EVENTS: Event[] = []; -const NOCS: string[] = []; -const SPORTS: Sport[] = []; - -const addNOC = (noc: string) => { - debug(`Adding NOC ${noc}`); - if (!NOCS.includes(noc)) { - NOCS.push(noc); - } -}; - -const addSport = (sportKey: string, sportName: string) => { - debug(`Adding sport ${sportKey}`); - if (!SPORTS.find((sport) => sport.key === sportKey)) { - SPORTS.push({ key: sportKey, name: sportName, NOCS: [] }); - } -}; - -const addSportNOC = (sportKey: string, sportName: string, noc: string) => { - debug(`Adding NOC ${noc} to sport ${sportKey}`); - addSport(sportKey, sportName); - const sport = SPORTS.find((sport) => sport.key === sportKey)!; - if (!sport.NOCS.includes(noc)) { - sport.NOCS.push(noc); - } -}; - -const generateCalendars = () => { - SPORTS - .sort((a, b) => a.name > b.name ? 1 : -1) - .forEach((sport) => { - let events = EVENTS - .filter((event) => event._SPORT === sport.key) - .sort((a, b) => a.UID > b.UID ? 1 : -1); - let key = `${sport.key}/general`; - let title = `${getSportIcon(sport.key)} ${sport.name} | Paris 2024`; - generateICS(title, key, events); - - events = EVENTS - .filter((event) => event._SPORT === sport.key && event._MEDAL) - .sort((a, b) => a.UID > b.UID ? 1 : -1); - key = `${sport.key}/medals`; - title = `${getSportIcon(sport.key)} ${sport.name} 🏅 | Paris 2024`; - generateICS(title, key, events); - - sport.NOCS.forEach((noc) => { - events = EVENTS - .filter((event) => event._SPORT === sport.key && event._NOCS.includes(noc)) - .sort((a, b) => a.UID > b.UID ? 1 : -1); - key = `${sport.key}/${noc}`; - title = `${getNOCFlag(noc)} ${getNOCName(noc)} ${sport.name} | Paris 2024`; - generateICS(title, key, events); - }); - }); - - NOCS.sort() - .forEach((noc) => { - let events = EVENTS - .filter((event) => event._NOCS.includes(noc)) - .sort((a, b) => a.UID > b.UID ? 1 : -1); - let key = `general/${noc}`; - let title = `${getNOCFlag(noc)} ${getNOCName(noc)} | Paris 2024`; - generateICS(title, key, events); - - events = EVENTS - .filter((event) => event._NOCS.includes(noc) && event._MEDAL) - .sort((a, b) => a.UID > b.UID ? 1 : -1); - if (events.length) { - key = `medals/${noc}`; - title = `${getNOCFlag(noc)} ${getNOCName(noc)} 🏅 | Paris 2024`; - generateICS(title, key, events); - } - }); - - const events = EVENTS - .sort((a, b) => a.UID > b.UID ? 1 : -1); - const key = "general/general"; - const title = "Paris 2024"; - generateICS(title, key, events); - - const medalEvents = EVENTS - .filter((event) => event._MEDAL) - .sort((a, b) => a.UID > b.UID ? 1 : -1); - const medalKey = "medals/general"; - const medalTitle = "🏅 Paris 2024"; - if (medalEvents.length) { - generateICS(medalTitle, medalKey, medalEvents); - } -}; - -const slugify = (text: string) => text.toLowerCase().replace(/\s/g, "-") - .replace(/[^a-z0-9-]/g, "") - .replace(/-+/g, "-"); - -const extractSportCalendar = async (sportKey: string) => { - const data = await downloadSchedule(sportKey); - const sportName = data.query.pDisciplineLabel; - const sportIcon = getSportIcon(sportKey); - addSport(sportKey, sportName); - - data.props.pageProps.scheduleDataSource.initialSchedule.units.forEach((unit: any) => { - unit.startDateTimeUtc = new Date(unit.startDate).toISOString().replace(".000", ""); - unit.endDateTimeUtc = new Date(unit.endDate).toISOString().replace(".000", ""); - - const event: Event = { - UID: `${unit.startDateTimeUtc.replace(/[:-]/g, "")}-${sportKey}-${slugify(unit.eventUnitName).toUpperCase()}`, - DTSTAMP: unit.startDateTimeUtc.replace(/[:-]/g, ""), - DTSTART: unit.startDateTimeUtc.replace(/[:-]/g, ""), - DTEND: unit.endDateTimeUtc.replace(/[:-]/g, ""), - DESCRIPTION: `${sportName} - ${unit.eventUnitName}`, - SUMMARY: `${sportIcon} ${unit.eventUnitName}`.trim(), - LOCATION: unit.venueDescription, - _SPORT: sportKey, - _NOCS: [], - _COMPETITORS: [], - _UNITNAME: unit.eventUnitName, - _MEDAL: !!unit.medalFlag, - _GENDER: unit.genderCode, - }; - - if (unit.competitors) { - const competitors = unit.competitors - .filter((competitor: any) => competitor.noc && isValidNOC(competitor.noc)) - .sort((a: any, b: any) => a.order > b.order ? 1 : -1); - event._NOCS = competitors.map((competitor: any) => { - addSportNOC(sportKey, sportName, competitor.noc); - addNOC(competitor.noc); - return competitor.noc; - }); - - // two competitors, we put them in the summary - if (competitors.length === 2) { - const competitor1 = competitors.shift(); - const competitor2 = competitors.shift(); - - event.UID += `-${competitor1.noc}-${competitor2.noc}`; - if (competitor1.name !== getNOCName(competitor1.noc)) { - event.SUMMARY = `${sportIcon} ${competitor1.name} ${getNOCFlag(competitor1.noc)} - ${getNOCFlag(competitor2.noc)} ${competitor2.name}`; - } else { - event.SUMMARY = `${sportIcon} ${competitor1.noc} ${getNOCFlag(competitor1.noc)} - ${getNOCFlag(competitor2.noc)} ${competitor2.noc}`; - } - } else if (competitors.length !== 0) { - // more than two, we put them in the description - competitors - .sort((a: any, b: any) => a.name > b.name ? 1 : -1) - .forEach((competitor: any) => { - if (competitor.name !== getNOCName(competitor.noc)) { - event.DESCRIPTION += `\\n${getNOCFlag(competitor.noc)} ${competitor.name}`; - event._COMPETITORS.push({ noc: competitor.noc, name: `${getNOCFlag(competitor.noc)} ${competitor.name}` }); - } else { - event.DESCRIPTION += `\\n${getNOCFlag(competitor.noc)} ${competitor.noc}`; - } - }); - } - } - EVENTS.push(event); - }); -}; - -const generateCeremoniesEvents = () => { - let startDateUtc = new Date("2024-07-26T17:30:00Z").toISOString().replace(".000", ""); - let endDateUtc = new Date("2024-07-26T21:00:00Z").toISOString().replace(".000", ""); - - const openingCeremony: Event = { - UID: `${startDateUtc.replace(/[:-]/g, "")}-opening-ceremony`, - DTSTAMP: startDateUtc.replace(/[:-]/g, ""), - DTSTART: startDateUtc.replace(/[:-]/g, ""), - DTEND: endDateUtc.replace(/[:-]/g, ""), - DESCRIPTION: "Paris 2024 - Opening ceremony", - SUMMARY: "Paris 2024 - Opening ceremony", - LOCATION: "Paris", - _NOCS: NOCS, - _MEDAL: false, - _COMPETITORS: [], - _GENDER: "", - _SPORT: "", - _UNITNAME: "", - }; - - startDateUtc = new Date("2024-08-11T19:00:00Z").toISOString().replace(".000", ""); - endDateUtc = new Date("2024-08-11T21:15:00Z").toISOString().replace(".000", ""); - - const closingCeremony: Event = { - UID: `${startDateUtc.replace(/[:-]/g, "")}-closing-ceremony`, - DTSTAMP: startDateUtc.replace(/[:-]/g, ""), - DTSTART: startDateUtc.replace(/[:-]/g, ""), - DTEND: endDateUtc.replace(/[:-]/g, ""), - DESCRIPTION: "Paris 2024 - Closing ceremony", - SUMMARY: "Paris 2024 - Closing ceremony", - LOCATION: "Stade de France, Saint-Denis", - _NOCS: NOCS, - _MEDAL: false, - _COMPETITORS: [], - _GENDER: "", - _SPORT: "", - _UNITNAME: "", - }; - - EVENTS.push(openingCeremony); - EVENTS.push(closingCeremony); -}; - -const generateOutputPage = () => { - const html = []; - - const linkClass = "inline-block bg-slate-400 hover:bg-blue-400 text-white px-2 py-1 my-px whitespace-nowrap rounded-lg text-base"; - - html.push("
| All sports"); - html.push(" | ");
- html.push(`Full schedule`);
- if (fs.existsSync(`${__dirname}/../docs/medals/general.ics`)) {
- html.push(` 🏅 Medal events`); - } - html.push(" | ");
- html.push(""); - NOCS.sort().forEach((noc) => { - html.push(`${getNOCFlag(noc)} ${noc}`); - }); - html.push(" | "); - html.push("
|---|---|---|
| 🏅 Medal events"); - html.push(" | "); - html.push(`Full schedule`); - html.push(" | "); - html.push(""); - fs.readdirSync(`${__dirname}/../docs/medals`) - .filter((ics) => ics !== "general.ics") - .forEach((ics) => { - const noc = ics.replace(".ics", ""); - html.push(`${getNOCFlag(noc)} ${noc}`); - }); - html.push(" | "); - html.push("
| ${getSportIcon(sport.key)} ${sport.name}`); - html.push(" | ");
- html.push(`Full schedule`);
- if (fs.existsSync(`${__dirname}/../docs/${sport.key}/medals.ics`)) {
- html.push(` 🏅 Medal events`); - } - html.push(" | ");
- html.push(""); - sport.NOCS.sort().forEach((noc) => { - html.push(`${getNOCFlag(noc)} ${noc}`); - }); - html.push(" | "); - html.push("