Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package kitinerary for openSUSE:Factory 
checked in at 2025-06-06 22:33:26
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/kitinerary (Old)
 and      /work/SRC/openSUSE:Factory/.kitinerary.new.19631 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "kitinerary"

Fri Jun  6 22:33:26 2025 rev:86 rq:1283172 version:25.04.2

Changes:
--------
--- /work/SRC/openSUSE:Factory/kitinerary/kitinerary.changes    2025-05-09 
18:44:24.419661066 +0200
+++ /work/SRC/openSUSE:Factory/.kitinerary.new.19631/kitinerary.changes 
2025-06-06 22:33:37.025371750 +0200
@@ -1,0 +2,23 @@
+Tue Jun  3 21:12:27 UTC 2025 - Christophe Marin <christo...@krop.fr>
+
+- Update to 25.04.2
+  * New bugfix release
+  * For more details please see:
+  * https://kde.org/announcements/gear/25.04.2/
+- Changes since 25.04.1:
+  * Fix Eurostar ticket extraction for French language tickets
+  * Handle checkout time ranges in German booking.com emails
+  * Add booking.com extractor for PDF confirmations
+  * Add copyright/licensing information
+  * Add Stena Line extractor
+  * Add Viking Line extractor
+  * Add IHG extractor
+  * Fix Tallink extractor for English language
+  * Don't require an arrival time for boat reservations
+  * Improve Flixbus PDF ticket extractor
+  * Support extracting an alternate Flixbus pkpass variant
+  * Extend pkpass preprocessing for bus tickets
+  * Update static extractor build documentation
+  * Give published static extractor builds the release service version
+
+-------------------------------------------------------------------

Old:
----
  kitinerary-25.04.1.tar.xz
  kitinerary-25.04.1.tar.xz.sig

New:
----
  kitinerary-25.04.2.tar.xz
  kitinerary-25.04.2.tar.xz.sig

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ kitinerary.spec ++++++
--- /var/tmp/diff_new_pack.QBN0wm/_old  2025-06-06 22:33:37.501391380 +0200
+++ /var/tmp/diff_new_pack.QBN0wm/_new  2025-06-06 22:33:37.505391545 +0200
@@ -18,11 +18,11 @@
 
 %define kf6_version 6.6.0
 %define qt6_version 6.6.0
-%define kpim6_version 6.4.1
+%define kpim6_version 6.4.2
 
 %bcond_without released
 Name:           kitinerary
-Version:        25.04.1
+Version:        25.04.2
 Release:        0
 Summary:        Data model and extraction system for travel reservations
 License:        LGPL-2.1-or-later


++++++ kitinerary-25.04.1.tar.xz -> kitinerary-25.04.2.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-25.04.1/CMakeLists.txt 
new/kitinerary-25.04.2/CMakeLists.txt
--- old/kitinerary-25.04.1/CMakeLists.txt       2025-05-03 00:09:58.000000000 
+0200
+++ new/kitinerary-25.04.2/CMakeLists.txt       2025-06-02 23:33:53.000000000 
+0200
@@ -3,7 +3,14 @@
 # SPDX-License-Identifier: BSD-3-Clause
 
 cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
-set(PIM_VERSION "6.4.1")
+
+# KDE Application Version, managed by release script
+set (RELEASE_SERVICE_VERSION_MAJOR "25")
+set (RELEASE_SERVICE_VERSION_MINOR "04")
+set (RELEASE_SERVICE_VERSION_MICRO "2")
+set (RELEASE_SERVICE_VERSION 
"${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
+
+set(PIM_VERSION "6.4.2")
 project(KItinerary VERSION ${PIM_VERSION})
 
 set(QT_REQUIRED_VERSION "6.7.0")
@@ -42,8 +49,8 @@
     find_package(SharedMimeInfo 1.3 REQUIRED)
 endif()
 
-set(KMIME_VERSION "6.4.1")
-set(PIM_PKPASS "6.4.1")
+set(KMIME_VERSION "6.4.2")
+set(PIM_PKPASS "6.4.2")
 
 find_package(KPim6Mime ${KMIME_VERSION} CONFIG REQUIRED)
 find_package(KPim6PkPass ${PIM_PKPASS} CONFIG REQUIRED)
@@ -143,3 +150,5 @@
 
 ki18n_install(po)
 
+# for static extractor build packaging
+file(WRITE ${CMAKE_BINARY_DIR}/version.txt ${RELEASE_SERVICE_VERSION})
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-25.04.1/po/es/kitinerary6.po 
new/kitinerary-25.04.2/po/es/kitinerary6.po
--- old/kitinerary-25.04.1/po/es/kitinerary6.po 2025-05-03 00:09:58.000000000 
+0200
+++ new/kitinerary-25.04.2/po/es/kitinerary6.po 2025-06-02 23:33:53.000000000 
+0200
@@ -1,14 +1,15 @@
-# Copyright (C) YEAR This_file_is_part_of_KDE
-# This file is distributed under the same license as the PACKAGE package.
+# Spanish translations for kitinerary6.po package.
+# Copyright (C) 2018-2025 This file is copyright:
+# This file is distributed under the same license as the kitinerary package.
 #
-# Javier Vinal <fjvi...@gmail.com>, 2018, 2019.
-# Eloy Cuadra <ecua...@eloihr.net>, 2020, 2021, 2022.
+# SPDX-FileCopyrightText: 2018, 2019 Javier Vinal <fjvi...@gmail.com>
+# SPDX-FileCopyrightText: 2020, 2021, 2022, 2025 Eloy Cuadra 
<ecua...@eloihr.net>
 msgid ""
 msgstr ""
-"Project-Id-Version: \n"
+"Project-Id-Version: kitinerary6\n"
 "Report-Msgid-Bugs-To: https://bugs.kde.org\n";
 "POT-Creation-Date: 2024-01-15 00:38+0000\n"
-"PO-Revision-Date: 2022-07-21 18:21+0200\n"
+"PO-Revision-Date: 2025-05-18 12:55+0100\n"
 "Last-Translator: Eloy Cuadra <ecua...@eloihr.net>\n"
 "Language-Team: Spanish <kde-l10n...@kde.org>\n"
 "Language: es\n"
@@ -185,53 +186,3 @@
 "Referencia de la reserva: %1\n"
 "Bajo el nombre: %2\n"
 "Lugar de recogida: %3"
-
-#~ msgid "Reservation reference: %1"
-#~ msgstr "Referencia de la reserva: %1"
-
-#~| msgid ""
-#~| "Reservation reference: %1\n"
-#~| "Under name: %2\n"
-#~| "\n"
-#~| "PickUp location: %3\n"
-#~| "\n"
-#~| "Dropoff Location: %4"
-#~ msgid ""
-#~ "Reservation reference: %1\n"
-#~ "Under name: %2\n"
-#~ "\n"
-#~ "Pickup location: %3\n"
-#~ "\n"
-#~ "Dropoff location: %4"
-#~ msgstr ""
-#~ "Referencia de la reserva: %1\n"
-#~ "Bajo el nombre: %2\n"
-#~ "\n"
-#~ "Lugar de recogida: %3\n"
-#~ "\n"
-#~ "Lugar de entrega: %4"
-
-#~ msgid "Rent car reservation: %1"
-#~ msgstr "Reserva de coche de alquiler: %1"
-
-#~ msgid ""
-#~ "Check-in: %1\n"
-#~ "Check-out: %2\n"
-#~ "Booking reference: %3"
-#~ msgstr ""
-#~ "Entrada: %1\n"
-#~ "Salida: %2\n"
-#~ "Referencia de la reserva: %3"
-
-#~ msgid ""
-#~ "Number Of People: %1\n"
-#~ "Reservation reference: %2\n"
-#~ "Under name: %3"
-#~ msgstr ""
-#~ "Número de personas: %1\n"
-#~ "Referencia de la reserva: %2\n"
-#~ "A nombre de: %3"
-
-#~ msgctxt "<street>, <postal code> <city>, <country>"
-#~ msgid "%1, %2 %3, %4"
-#~ msgstr "%1, %2 %3, %4"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-25.04.1/po/pl/kitinerary6.po 
new/kitinerary-25.04.2/po/pl/kitinerary6.po
--- old/kitinerary-25.04.1/po/pl/kitinerary6.po 2025-05-03 00:09:58.000000000 
+0200
+++ new/kitinerary-25.04.2/po/pl/kitinerary6.po 2025-06-02 23:33:53.000000000 
+0200
@@ -16,7 +16,6 @@
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
 "|| n%100>=20) ? 1 : 2);\n"
-"X-Generator: Lokalize 23.08.5\n"
 
 #: calendarhandler.cpp:171
 #, kde-format
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-25.04.1/scripts/README.md 
new/kitinerary-25.04.2/scripts/README.md
--- old/kitinerary-25.04.1/scripts/README.md    2025-05-03 00:09:58.000000000 
+0200
+++ new/kitinerary-25.04.2/scripts/README.md    2025-06-02 23:33:53.000000000 
+0200
@@ -10,18 +10,20 @@
 
 ## Deployment
 
-The extractor needs external data in the form of translation catalogs and 
iso-codes files. Those are provided
+When using the iCal output format, the extractor needs external translation 
catalogs. Those are provided
 in the same archive as the binary, and need to be placed in the same relative 
location to the binary to be found.
 
 Alternatively, they can be placed in a path listed in the `XDG_DATA_DIRS` 
environment variable.
 
-## Translations
-
-When using the iCal output format, translations are relevant. This is done 
using Gettext and thus follows the
+Translations are done using Gettext and thus follows the
 environment variables that influence that (`LANG`, `LANGUAGE`, `LC_ALL`, etc).
 
 This also implies that Glibc locale data has to be installed on the system, 
Gettext will not work without those.
 
+## CD builds
+
+CD builds are published to https://cdn.kde.org/ci-builds/pim/kitinerary/.
+
 ## Local builds
 
 If you want to locally reproduce the same build, this can be done by running 
the commands from the build scripts in the
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-25.04.1/scripts/package.sh 
new/kitinerary-25.04.2/scripts/package.sh
--- old/kitinerary-25.04.1/scripts/package.sh   2025-05-03 00:09:58.000000000 
+0200
+++ new/kitinerary-25.04.2/scripts/package.sh   2025-06-02 23:33:53.000000000 
+0200
@@ -27,7 +27,7 @@
 
 # determing version
 # VERSION=$CI_COMMIT_REF_SLUG
-VERSION=`cat $BUILD_ROOT/$CI_PROJECT_PATH/CMakeLists.txt | grep 
"set(PIM_VERSION" | sed -e 's/")//' | sed -e 's/.*"//'`
+VERSION=`cat $BUILD_ROOT/$CI_PROJECT_PATH/build/version.txt`
 
 # prepare output for publishing
 PACKAGE_ROOT=$CI_PROJECT_DIR/kde-ci-packages/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-25.04.1/src/cli/org.kde.kitinerary-extractor.appdata.xml 
new/kitinerary-25.04.2/src/cli/org.kde.kitinerary-extractor.appdata.xml
--- old/kitinerary-25.04.1/src/cli/org.kde.kitinerary-extractor.appdata.xml     
2025-05-03 00:09:58.000000000 +0200
+++ new/kitinerary-25.04.2/src/cli/org.kde.kitinerary-extractor.appdata.xml     
2025-06-02 23:33:53.000000000 +0200
@@ -133,6 +133,7 @@
   </categories>
   <launchable 
type="desktop-id">org.kde.kitinerary-extractor.desktop</launchable>
   <releases>
+    <release version="6.4.2" date="2025-06-05"/>
     <release version="6.4.1" date="2025-05-08"/>
     <release version="6.4.0" date="2025-04-17"/>
     <release version="6.3.3" date="2025-03-06"/>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-25.04.1/src/lib/extractorvalidator.cpp 
new/kitinerary-25.04.2/src/lib/extractorvalidator.cpp
--- old/kitinerary-25.04.1/src/lib/extractorvalidator.cpp       2025-05-03 
00:09:58.000000000 +0200
+++ new/kitinerary-25.04.2/src/lib/extractorvalidator.cpp       2025-06-02 
23:33:53.000000000 +0200
@@ -110,8 +110,7 @@
 {
     return filterPlace(trip.departureBoatTerminal())
         && filterPlace(trip.arrivalBoatTerminal())
-        && trip.departureTime().isValid()
-        && trip.arrivalTime().isValid();
+        && trip.departureTime().isValid();
 }
 
 bool ExtractorValidatorPrivate::filterEvent(const Event &event) const
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/kitinerary-25.04.1/src/lib/processors/pkpassdocumentprocessor.cpp 
new/kitinerary-25.04.2/src/lib/processors/pkpassdocumentprocessor.cpp
--- old/kitinerary-25.04.1/src/lib/processors/pkpassdocumentprocessor.cpp       
2025-05-03 00:09:58.000000000 +0200
+++ new/kitinerary-25.04.2/src/lib/processors/pkpassdocumentprocessor.cpp       
2025-06-02 23:33:53.000000000 +0200
@@ -6,6 +6,7 @@
 
 #include "pkpassdocumentprocessor.h"
 
+#include <KItinerary/BusTrip>
 #include <KItinerary/DocumentUtil>
 #include <KItinerary/Event>
 #include <KItinerary/ExtractorDocumentNodeFactory>
@@ -316,6 +317,38 @@
     return res;
 }
 
+[[nodiscard]] static BusReservation extractBusTicket(KPkPass::Pass *pass, 
BusReservation res)
+{
+    auto trip = res.reservationFor().value<BusTrip>();
+
+    // "relevantDate" is the best guess for the departure time if we didn't 
find an explicit field for it
+    if (pass->relevantDate().isValid() && !trip.departureTime().isValid()) {
+        // TODO try to recover timezone?
+        trip.setDepartureTime(pass->relevantDate());
+    }
+
+    // location is the best guess for the departure station geo coordinates
+    auto depStation = trip.departureBusStop();
+    auto depGeo = depStation.geo();
+    if (pass->locations().size() == 1 && !depGeo.isValid()) {
+        const auto loc = pass->locations().at(0);
+        depGeo.setLatitude(loc.latitude());
+        depGeo.setLongitude(loc.longitude());
+        depStation.setGeo(depGeo);
+        trip.setDepartureBusStop(depStation);
+    }
+
+    // organizationName is the best guess for the provider
+    auto provider = trip.provider();
+    if (provider.name().isEmpty()) {
+        provider.setName(pass->organizationName());
+        trip.setProvider(provider);
+    }
+
+    res.setReservationFor(trip);
+    return res;
+}
+
 static void extractEventTicketPass(KPkPass::Pass *pass, EventReservation 
&eventRes)
 {
     auto event = eventRes.reservationFor().value<Event>();
@@ -480,6 +513,14 @@
                         res = trainRes;
                         break;
                     }
+                    case KPkPass::BoardingPass::Bus:
+                    {
+                        auto busRes = res.value<BusReservation>();
+                        busRes = extractBusTicket(pass, busRes);
+                        busRes.setUnderName(extractPerson(pass, 
busRes.underName().value<Person>()));
+                        res = busRes;
+                        break;
+                    }
                     default:
                         if (!node.result().isEmpty()) { // don't overwrite 
better results from child nodes
                             return;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-25.04.1/src/lib/scripts/booking.js 
new/kitinerary-25.04.2/src/lib/scripts/booking.js
--- old/kitinerary-25.04.1/src/lib/scripts/booking.js   2025-05-03 
00:09:58.000000000 +0200
+++ new/kitinerary-25.04.2/src/lib/scripts/booking.js   2025-06-02 
23:33:53.000000000 +0200
@@ -28,10 +28,10 @@
 regExMap['de'] = {
     bookingRef: /(?:Buchungsnummer|Bestätigungsnummer): ([0-9]*)\s+/,
     // 1: hotel name, 2: adress, 3: city, 4:postal code, 5: country, 6: phone
-    hotelInformation: /(?:Lage )?(.+), (.+), (.+), (.+) ?-?\s+Telefon:? 
(\+[0-9 \-]+)\n/,
-    hotelName: [/\[checkmark.png\] Die Unterkunft (.*)\s+erwartet Sie/, 
/\n\n\s*(\S.*\S)\n\n\s*\[\S/],
+    hotelInformation: /(?:Lage )?(.+?), ([^\n,]+)(?:, ([^\n,]+))?, ([^\n,]+?) 
?-?\s+Telefon:? (\+[0-9 \-]+)\n/,
+    hotelName: [/Die Unterkunft (.*)\s+erwartet Sie/, 
/\n\n\s*(\S.*\S)\n\n\s*\[\S/],
     arrivalDate: /Anreise ([A-Z][a-z]+, [0-9]{1,2}\. \S+ [0-9]{4}) \((?:ab 
)?([0-9]{1,2}:[0-9]{2}).*\)/,
-    departureDate: /Abreise ([A-Z][a-z]+, [0-9]{1,2}\. \S+ [0-9]{4}) \(bis 
([0-9]{1,2}:[0-9]{2})\)/,
+    departureDate: /Abreise ([A-Z][a-z]+, [0-9]{1,2}\. \S+ [0-9]{4}) 
\(.*?([0-9]{1,2}:[0-9]{2})\)/,
     person: /Name des Gastes[\n\s]+(.*?)(?:\n| Name des Gastes bearbeiten)/,
 }
 
@@ -85,9 +85,9 @@
 
         const hotel = text.match(regExMap[locale]['hotelInformation']);
         res.reservationFor.address.streetAddress = hotel[1];
-        res.reservationFor.address.postalCode = hotel[3];
+        res.reservationFor.address.postalCode = hotel[4] ? hotel[3] : "";
         res.reservationFor.address.addressLocality = hotel[2];
-        res.reservationFor.address.addressCountry = hotel[4];
+        res.reservationFor.address.addressCountry = hotel[4] ? hotel[4] : 
hotel[3];
         res.reservationFor.telephone = hotel[5];
 
         const arrivalDate = text.match(regExMap[locale]['arrivalDate']);
@@ -201,3 +201,44 @@
     res.reservationFor.address.addressCountry = addrElems[addrElems.length - 
1];
     return parseHtmlCommon(doc, node, res);
 }
+
+function parsePdf(pdf, node)
+{
+    var pdfText = pdf.text;
+
+    var res = JsonLd.newLodgingReservation();
+
+    var num = pdfText.match(/CONFIRMATION NUMBER: (.+)/)[1];
+    res.reservationNumber = num.split(".").join("");
+
+    var info = 
pdfText.match(/CHECK-OUT\s+ROOMS\s+NIGHTS\n\s+(.+)\n(.+)\n\s+(\d+)\s+(\d+)\s+\d+\s*\/\d+\n\s+Address:\s+(.+)\s+([A-Z]+)\s+([A-Z]+)\n\s+(\d+)\s+(.+)\n\s+.*\n\s+Phone:
 (.+)\n\s+from (\d\d:\d\d)\s+until (\d\d:\d\d)\n\s+GPS coordinates: (.+)/);
+
+    res.reservationFor.name = info[1] + info[2];
+    res.reservationFor.address.streetAddress = info[5];
+    res.reservationFor.address.postalCode = info[8];
+    res.reservationFor.address.addressLocality = info[9];
+    res.reservationFor.telephone = info[10];
+
+    var coords = parseCoordinates(info[13]);
+    res.reservationFor.geo.latitude = coords[0];
+    res.reservationFor.geo.longitude = coords[1];
+
+    var checkin = info[3] + " " + info[6] + " " + info[11];
+    var checkout = info[4] + " " + info[7] + " " + info[12];
+    res.checkinTime = JsonLd.toDateTime(checkin, "d MMMM hh:mm", 'en');
+    res.checkoutTime = JsonLd.toDateTime(checkout, "d MMMM hh:mm", 'en');
+
+    return res;
+}
+
+function parseCoordinates(input) {
+    const regex = 
/([NS])\s*(\d{1,3})[°º]?\s*(\d+(?:\.\d+)?)[,\s]+([EW])\s*(\d{1,3})[°º]?\s*(\d+(?:\.\d+)?)/i;
+    const match = input.match(regex);
+
+    const [, latHem, latDeg, latMin, lonHem, lonDeg, lonMin] = match;
+
+    const latitude = (parseInt(latDeg, 10) + parseFloat(latMin) / 60) * 
(latHem.toUpperCase() === 'S' ? -1 : 1);
+    const longitude = (parseInt(lonDeg, 10) + parseFloat(lonMin) / 60) * 
(lonHem.toUpperCase() === 'W' ? -1 : 1);
+
+    return [latitude, longitude];
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-25.04.1/src/lib/scripts/booking.json 
new/kitinerary-25.04.2/src/lib/scripts/booking.json
--- old/kitinerary-25.04.1/src/lib/scripts/booking.json 2025-05-03 
00:09:58.000000000 +0200
+++ new/kitinerary-25.04.2/src/lib/scripts/booking.json 2025-06-02 
23:33:53.000000000 +0200
@@ -15,5 +15,11 @@
         "filter": [ { "field": "From", "match": "@booking.com", "mimeType": 
"message/rfc822", "scope": "Ancestors" } ],
         "script": "booking.js",
         "function": "parseHtmlAlternative"
+    },
+    {
+        "mimeType": "application/pdf",
+        "filter": [ { "mimeType": "text/plain", "match": "This print version 
of your confirmation contains the most important information about your 
booking. It can be used to check in", "scope": "Descendants" } ],
+        "script": "booking.js",
+        "function": "parsePdf"
     }
 ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-25.04.1/src/lib/scripts/flixbus.js 
new/kitinerary-25.04.2/src/lib/scripts/flixbus.js
--- old/kitinerary-25.04.1/src/lib/scripts/flixbus.js   2025-05-03 
00:09:58.000000000 +0200
+++ new/kitinerary-25.04.2/src/lib/scripts/flixbus.js   2025-06-02 
23:33:53.000000000 +0200
@@ -44,7 +44,7 @@
     const idx = addr1.lastIndexOf(',');
     place.address.streetAddress = addr1.substring(0, idx);
     place.address.addressLocality = addr1.substr(idx + 1);
-    place.geo = JsonLd.toGeoCoordinates(links.shift().url);
+    place.geo = JsonLd.toGeoCoordinates(links.shift()?.url);
 }
 
 function parsePdfTicket(pdf, node, triggerNode)
@@ -63,7 +63,7 @@
     let reservations = [];
     while (true) {
         const times = 
timeColumn.substr(idxTime).match(/(\d\d:\d\d)\n([^:]*?\n)?([^:]*?\n)?(\d\d:\d\d)/);
-        const stations = stationColumn.substr(idxStations).match(/(.*)\n[ 
]+(.*)(?:\n|,\n  +(.*)\n)(?:.*(?:NOTE:|Wagennummerierung|platform|The 
regional part).(?:.*\n)+)?.*(?:Bus|Autobus|Zug|Strecke|Route|Tratta) 
+(.*)\n.*(?:Direction|à destination de|Kierunek|richting|Richtung|Direzione) 
(.*)\n(?:.*(?:Operated|Betrieben|Uitgevoerd|Effettuata).*\n)?(.*)\n(?:[ 
]+(.*?)(?:\n|,\n +(.*)\n))?/);
+        const stations = 
stationColumn.substr(idxStations).match(/\b([^].*)\n[ ]+(.*)(?:\n|,\n  
+(.*)\n)(?:.*(?:NOTE:|Wagennummerierung|platform|The regional part|Die 
Bussteignummer).(?:.*\n)+?)?.*(?:Bus|Autobus|Zug|Strecke|Route|Tratta)\s+(\S.+)\n.*(?:Direction|à
 destination de|Kierunek|richting|Richtung|Direzione) 
(.*)\n(?:.*(?:Operated|Betrieben|Uitgevoerd|Effettuata).*\n)?(.*)\n(?:[ 
]+(.*?)(?:\n|,\n +(.*)\n))?/);
         if (!times || !stations) {
             break;
         }
@@ -130,4 +130,27 @@
     //TODO: Passangier List, Seating
 
     return res
-}
\ No newline at end of file
+}
+
+function parsePkPass2(pass, node) {
+    let res = Object.assign(JsonLd.newBusReservation(), node.result[0]);
+
+    res.reservationFor.departureBusStop.name = pass.primaryFields[0].label;
+    res.reservationFor.departureTime = pass.primaryFields[0].value
+    res.reservationFor.arrivalBusStop = {
+        '@type': 'BusStation',
+        name: pass.primaryFields[1].label
+    };
+    res.reservationFor.arrivalTime = pass.primaryFields[1].value
+
+    res.reservationFor.busNumber = pass.field["transport-type"].value.split(" 
")[0]
+    res.reservationFor.busName = pass.field["transport-type"].value
+
+    res.reservationNumber = pass.field["booking-number"].value
+    res.underName.name = pass.field["passenger"].value;
+    res.reservedTicket.ticketedSeat = {
+        '@type': 'Seat',
+        seatNumber: pass.field["seat"].value
+    };
+    return res
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-25.04.1/src/lib/scripts/flixbus.json 
new/kitinerary-25.04.2/src/lib/scripts/flixbus.json
--- old/kitinerary-25.04.1/src/lib/scripts/flixbus.json 2025-05-03 
00:09:58.000000000 +0200
+++ new/kitinerary-25.04.2/src/lib/scripts/flixbus.json 2025-06-02 
23:33:53.000000000 +0200
@@ -35,5 +35,18 @@
         ],
         "script": "flixbus.js",
         "function": "parsePkPass"
+    },
+    {
+        "mimeType": "application/vnd.apple.pkpass",
+        "filter": [
+            {
+                "field": "passTypeIdentifier",
+                "match": "pass.de.flixbus",
+                "mimeType": "application/vnd.apple.pkpass",
+                "scope": "Current"
+            }
+        ],
+        "script": "flixbus.js",
+        "function": "parsePkPass2"
     }
 ]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-25.04.1/src/lib/scripts/ihg.js 
new/kitinerary-25.04.2/src/lib/scripts/ihg.js
--- old/kitinerary-25.04.1/src/lib/scripts/ihg.js       1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-25.04.2/src/lib/scripts/ihg.js       2025-06-02 
23:33:53.000000000 +0200
@@ -0,0 +1,31 @@
+/*
+    SPDX-FileCopyrightText: 2025 Johannes Krattenmacher 
<git.nore...@krateng.ch>
+    SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+function parseConfirmation(html) {
+
+    var res = JsonLd.newLodgingReservation();
+
+    const content = html.root.recursiveContent;
+
+    const formats = ["d MMM yyyy h a"];
+
+    // Supporting English and German
+    let info = 
content.match(/(?:Confirmation|Buchungsnummer):?\s*([0-9#]+)\n(.+)\n[\s\S]*?(.+)\n(?:Address|Adresse):\n([\s\S]*?)\n(?:Front
 
Desk|Rezeption):\n(\d{4,25})\n(?:Email|E-Mail):\n(.*)\n(?:Dates|Daten)\n(\d{1,2}
 \w{3} \d{4})[\s\S]*?(\d{1,2} \w{3} \d{4})\nCheck in ‌(\d{1,2} [ap]m).* \/ 
Check out[\s\S]*?(\d{1,2} [ap]m)[\s\S]*?(?:Reservation|Reservierung)/)
+
+    // sometimes includes #, sometimes not - not sure if hotel specific
+    res.reservationNumber = info[1];
+    res.reservationFor.name = info[2] + info[3];
+    res.reservationFor.address.streetAddress = info[4];
+    res.reservationFor.telephone = info[5];
+    res.reservationFor.email = info[6];
+
+    let checkin = info[7] + " " + info[9];
+    let checkout = info[8] + " " + info[10];
+
+    res.checkinTime = JsonLd.toDateTime(checkin, formats, 'en');
+    res.checkoutTime = JsonLd.toDateTime(checkout, formats, 'en');
+
+    return res;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-25.04.1/src/lib/scripts/ihg.json 
new/kitinerary-25.04.2/src/lib/scripts/ihg.json
--- old/kitinerary-25.04.1/src/lib/scripts/ihg.json     1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-25.04.2/src/lib/scripts/ihg.json     2025-06-02 
23:33:53.000000000 +0200
@@ -0,0 +1,13 @@
+{
+    "filter": [
+        {
+            "field": "From",
+            "match": "@tx.ihg.com",
+            "mimeType": "message/rfc822",
+            "scope": "Ancestors"
+        }
+    ],
+    "function": "parseConfirmation",
+    "mimeType": "text/html",
+    "script": "ihg.js"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-25.04.1/src/lib/scripts/stena.js 
new/kitinerary-25.04.2/src/lib/scripts/stena.js
--- old/kitinerary-25.04.1/src/lib/scripts/stena.js     1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-25.04.2/src/lib/scripts/stena.js     2025-06-02 
23:33:53.000000000 +0200
@@ -0,0 +1,65 @@
+/*
+    SPDX-FileCopyrightText: 2025 Johannes Krattenmacher 
<git.nore...@krateng.ch>
+    SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+function parseConfirmation(html) {
+    const content = html.root.recursiveContent;
+    let res = JsonLd.newBoatReservation();
+    res.reservationNumber = content.match(/BOOKING REFERENCE:.*\n.*?(\d+)/)[1];
+
+    var timeinfo = 
content.match(/Departs\n.*(\d\d\/\d\d\/\d\d\d\d).*\n.*(\d\d:\d\d)\n.*Arrives\n.*(\d\d\/\d\d\/\d\d\d\d).*\n.*(\d\d:\d\d)/);
+
+    res.reservationFor.departureTime = JsonLd.toDateTime(timeinfo[1] + " " + 
timeinfo[2], "dd/MM/yyyy hh:mm", "en");
+    res.reservationFor.arrivalTime = JsonLd.toDateTime(timeinfo[3] + " " + 
timeinfo[4], "dd/MM/yyyy hh:mm", "en");
+
+    var routeinfo = content.match(/Route Information\n(.+)-(.+)/);
+    var departPort = routeinfo[1];
+    var arrivePort = routeinfo[2];
+
+    // hardcode ports that Stena Line uses?
+    // all but liepaja and travemunde are NOT confirmed to use this exact 
string on the ticket
+    var ports = {
+        'Liepaja': [56.529230, 20.998445, "14A Brīvostas Street", 3405, 
"Liepāja", "Stena Line Liepāja Ferry Terminal"],
+        'Travemünde': [53.940075, 10.852651, "Zum Hafenplatz 1", 23570, 
"Lübeck-Travemünde", "Skandinavienkai"],
+        'Frederikshavn': [57.43459, 10.54365, "Færgehavnsvej 10", 9900, 
"Frederikshavn", "Stena Line Denmark A/S"],
+        'Gothenburg': [57.701195, 11.947163, "Emigrantvägen 20", 41327, 
"Gothenburg", "Denmark Terminal"],
+        'Hook of Holland': [51.974122, 4.128671, "Stationsweg 10", 3151, "Hook 
of Holland", "Stena Line BV"],
+        'Harwich': [51.946494, 1.252731, "Parkeston Quay", "CO12 4SR", 
"Harwich", "Harwich International Port"],
+        'Ventspils': [57.398297, 21.569751, "Dārzu iela 6", 3601, "Ventspils", 
"Pramju Terminalis"],
+        'Nynäshamn': [58.93915, 17.97527, "Norvikvägen 26", 14945, 
"Nynäshamn", "Norviks färjeterminal"],
+        'Rostock': [54.141775, 12.104687, "Zum Fährterminal 1", 18147, 
"Rostock", "Fährterminal 1"],
+        'Trelleborg': [55.373026, 13.154049, "Norra Nyhamnsgatan 1A", 23161, 
"Trelleborg", "Norra Nyhamnsgatan 1A"],
+        'Gdynia': [54.533212, 18.544250, "ul. Polska 4", 81339, "Gdynia", 
"Gdynia Port"],
+        'Karlskrona': [56.1646, 15.6308, "Verkövägen 101", 37165, "Lyckeby, 
Karlskrona", "Stena Line Scandinavia AB"],
+        // more
+    }
+
+    if (departPort in ports) {
+        res.reservationFor.departureBoatTerminal.geo.latitude = 
ports[departPort][0];
+        res.reservationFor.departureBoatTerminal.geo.longitude = 
ports[departPort][1];
+        res.reservationFor.departureBoatTerminal.address.streetAddress = 
ports[departPort][2];
+        res.reservationFor.departureBoatTerminal.address.postalCode = 
ports[departPort][3];
+        res.reservationFor.departureBoatTerminal.address.addressLocality = 
ports[departPort][4];
+        res.reservationFor.departureBoatTerminal.name = ports[departPort][5];
+    }
+    else {
+        res.reservationFor.departureBoatTerminal.name = departPort;
+        res.reservationFor.departureBoatTerminal.address.addressLocality = 
departPort;
+    }
+
+    if (arrivePort in ports) {
+        res.reservationFor.arrivalBoatTerminal.geo.latitude = 
ports[arrivePort][0];
+        res.reservationFor.arrivalBoatTerminal.geo.longitude = 
ports[arrivePort][1];
+        res.reservationFor.arrivalBoatTerminal.address.streetAddress = 
ports[arrivePort][2];
+        res.reservationFor.arrivalBoatTerminal.address.postalCode = 
ports[arrivePort][3];
+        res.reservationFor.arrivalBoatTerminal.address.addressLocality = 
ports[arrivePort][4];
+        res.reservationFor.arrivalBoatTerminal.name = ports[arrivePort][5];
+    }
+    else {
+        res.reservationFor.arrivalBoatTerminal.name = arrivePort;
+        res.reservationFor.arrivalBoatTerminal.address.addressLocality = 
arrivePort;
+    }
+
+    return res;
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-25.04.1/src/lib/scripts/stena.json 
new/kitinerary-25.04.2/src/lib/scripts/stena.json
--- old/kitinerary-25.04.1/src/lib/scripts/stena.json   1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-25.04.2/src/lib/scripts/stena.json   2025-06-02 
23:33:53.000000000 +0200
@@ -0,0 +1,13 @@
+{
+    "filter": [
+        {
+            "field": "From",
+            "match": "@stenaline.com",
+            "mimeType": "message/rfc822",
+            "scope": "Ancestors"
+        }
+    ],
+    "function": "parseConfirmation",
+    "mimeType": "text/html",
+    "script": "stena.js"
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-25.04.1/src/lib/scripts/tallink.js 
new/kitinerary-25.04.2/src/lib/scripts/tallink.js
--- old/kitinerary-25.04.1/src/lib/scripts/tallink.js   2025-05-03 
00:09:58.000000000 +0200
+++ new/kitinerary-25.04.2/src/lib/scripts/tallink.js   2025-06-02 
23:33:53.000000000 +0200
@@ -7,17 +7,19 @@
     let res = JsonLd.newBoatReservation();
     res.reservationNumber = triggerNode.content.match(/;(\d+)-/)[1];
     const text = pdf.pages[triggerNode.location].textInRect(0.0, 0.0, 0.68, 
1.0);
-    const dep = text.match(/Abfahrt Von (\S.*?)  +\S.*(\d\d\.\d\d.\d{4}) 
+(.*)\n.*(\d\d:\d\d)  +(\S.*)/);
+    const dep = text.match(/(?:Abfahrt Von|Departure from) (\S.*?)  
+\S.*(\d\d\.\d\d.\d{4}) +(.*)\n.*(\d\d:\d\d)  +(\S.*)/);
     res.reservationFor.departureBoatTerminal.name = dep[3];
     res.reservationFor.departureBoatTerminal.address.addressLocality = dep[1];
     res.reservationFor.departureBoatTerminal.address.streetAddress = dep[5];
     res.reservationFor.departureTime = JsonLd.toDateTime(dep[2] + ' ' + 
dep[4], 'dd.MM.yyyy hh:mm', 'de');
 
-    const arr = text.match(/Ankunft in (\S.*?)  +\S.*(\d\d\.\d\d.\d{4}) 
+(.*)\n.*(\d\d:\d\d)  +(\S.*)/);
+    const arr = text.match(/(?:Ankunft in|Arrives in) (\S.*?)  
+\S.*(\d\d\.\d\d.\d{4}) +(.*)\n.*(\d\d:\d\d)  +(\S.*)/);
     res.reservationFor.arrivalBoatTerminal.name = arr[3];
     res.reservationFor.arrivalBoatTerminal.address.addressLocality = arr[1];
     res.reservationFor.arrivalBoatTerminal.address.streetAddress = arr[5];
     res.reservationFor.arrivalTime = JsonLd.toDateTime(arr[2] + ' ' + arr[4], 
'dd.MM.yyyy hh:mm', 'de');
 
+    // TODO security code? is there any field for that?
+
     return res;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-25.04.1/src/lib/scripts/vikingline.js 
new/kitinerary-25.04.2/src/lib/scripts/vikingline.js
--- old/kitinerary-25.04.1/src/lib/scripts/vikingline.js        1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-25.04.2/src/lib/scripts/vikingline.js        2025-06-02 
23:33:53.000000000 +0200
@@ -0,0 +1,35 @@
+/*
+    SPDX-FileCopyrightText: 2025 Johannes Krattenmacher 
<git.nore...@krateng.ch>
+    SPDX-License-Identifier: LGPL-2.0-or-later
+*/
+
+function parsePdf(pdf, node) {
+    var res = JsonLd.newBoatReservation();
+    var pdfText = pdf.text;
+    res.reservationNumber = pdfText.match(/Booking number (\d{5,10})/)[1];
+
+    // use the more specific info for departure
+    var info = pdfText.match(/Port information and check-in 
times\n(.*):(.*)\nAddress\n(.*), (\d+) (.*)\nGPS: 
N\/lat\s*(\d{1,3})°\s*(\d{1,2}\.\d+)',\s*E\/lon\s*(\d{1,3})°\s*(\d{1,2}\.\d+)'/);
+    // viking lines is always N E
+
+    res.reservationFor.departureBoatTerminal.name = info[2];
+    res.reservationFor.departureBoatTerminal.address.streetAddress = info[3];
+    res.reservationFor.departureBoatTerminal.address.addressLocality = info[5];
+    res.reservationFor.departureBoatTerminal.address.postalCode = info[4];
+    res.reservationFor.departureBoatTerminal.geo.latitude = parseInt(info[6], 
10) + (parseFloat(info[7])/60);
+    res.reservationFor.departureBoatTerminal.geo.longitude = parseInt(info[8], 
10) + (parseFloat(info[9])/60);
+
+    var shortinfo = pdfText.match(/Departure[^\S\r\n]*(.*) (.{3}) (\d{1,2}) 
(.+) (\d{4}) at (\d{1,2}):(\d{2})\nArrival[^\S\r\n]*(.*) (.{3}) (\d{1,2}) (.+) 
(\d{4}) at (\d{1,2}):(\d{2})/);
+    var depart = shortinfo[3] + " " + shortinfo[4] + " " + shortinfo[5] + " " 
+ shortinfo[6] + ":" + shortinfo[7];
+    var arrive = shortinfo[10] + " " + shortinfo[11] + " " + shortinfo[12] + " 
" + shortinfo[13] + ":" + shortinfo[14];
+
+    res.reservationFor.arrivalBoatTerminal.address.addressLocality = 
shortinfo[8];
+    // terminal name needed for validation? this just uses the city for now
+    res.reservationFor.arrivalBoatTerminal.name = shortinfo[8];
+    res.reservationFor.departureTime = JsonLd.toDateTime(depart, 'd MMMM yyyy 
HH:mm', 'en');
+    res.reservationFor.arrivalTime = JsonLd.toDateTime(arrive, 'd MMMM yyyy 
HH:mm', 'en');
+
+    return res;
+}
+
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/kitinerary-25.04.1/src/lib/scripts/vikingline.json 
new/kitinerary-25.04.2/src/lib/scripts/vikingline.json
--- old/kitinerary-25.04.1/src/lib/scripts/vikingline.json      1970-01-01 
01:00:00.000000000 +0100
+++ new/kitinerary-25.04.2/src/lib/scripts/vikingline.json      2025-06-02 
23:33:53.000000000 +0200
@@ -0,0 +1,8 @@
+{
+    "filter": [
+        { "mimeType": "text/plain", "match": "sales.vikingline.com", "scope": 
"Descendants" }
+    ],
+    "function": "parsePdf",
+    "mimeType": "application/pdf",
+    "script": "vikingline.js"
+}

Reply via email to