incyclist-services 1.7.135 → 1.7.137

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/lib/cjs/i18n/countries/service.js +1 -17
  2. package/lib/cjs/ride/display/service.js +5 -4
  3. package/lib/cjs/ride/route/GpxDisplayService.js +3 -3
  4. package/lib/cjs/ride/route/RLVDisplayService.js +2 -2
  5. package/lib/cjs/routes/base/parsers/gpx.js +14 -3
  6. package/lib/cjs/routes/base/parsers/gpxFast.js +203 -0
  7. package/lib/cjs/routes/base/parsers/incyclist.js +2 -1
  8. package/lib/cjs/routes/base/parsers/utils.js +15 -10
  9. package/lib/cjs/routes/base/parsers/xml.js +16 -7
  10. package/lib/cjs/routes/library/service.js +11 -0
  11. package/lib/cjs/services/overpass/overpass.js +22 -29
  12. package/lib/cjs/video/resolvePlaybackUrl.js +1 -3
  13. package/lib/esm/i18n/countries/service.js +1 -17
  14. package/lib/esm/ride/display/service.js +5 -4
  15. package/lib/esm/ride/route/GpxDisplayService.js +3 -3
  16. package/lib/esm/ride/route/RLVDisplayService.js +2 -2
  17. package/lib/esm/routes/base/parsers/gpx.js +12 -1
  18. package/lib/esm/routes/base/parsers/gpxFast.js +199 -0
  19. package/lib/esm/routes/base/parsers/incyclist.js +2 -1
  20. package/lib/esm/routes/base/parsers/utils.js +15 -10
  21. package/lib/esm/routes/base/parsers/xml.js +16 -7
  22. package/lib/esm/routes/library/service.js +11 -0
  23. package/lib/esm/services/overpass/overpass.js +22 -26
  24. package/lib/esm/video/resolvePlaybackUrl.js +1 -3
  25. package/lib/types/routes/base/parsers/gpx.d.ts +3 -0
  26. package/lib/types/routes/base/parsers/gpxFast.d.ts +2 -0
  27. package/lib/types/routes/base/parsers/xml.d.ts +1 -0
  28. package/lib/types/routes/library/service.d.ts +1 -0
  29. package/lib/types/routes/library/types.d.ts +2 -0
  30. package/lib/types/video/resolvePlaybackUrl.d.ts +1 -1
  31. package/package.json +2 -2
@@ -96,23 +96,7 @@ let Countries = (() => {
96
96
  return countries.map(c => this.getIsoFromCountry(c, locale));
97
97
  }
98
98
  async getIsoFromLatLng(point) {
99
- const { lat, lng } = point;
100
- const query = `[out:json][timeout:10];is_in(${lat},${lng})->.a;rel(pivot.a)['admin_level'='2']['boundary'='administrative'];out tags;`;
101
- try {
102
- const result = await this.getOverpassApi().query(query);
103
- if (!result?.elements)
104
- return undefined;
105
- const isos = result.elements
106
- .map(el => el.tags['ISO3166-1:alpha2'])
107
- .filter((iso) => !!iso)
108
- .map(iso => iso.toUpperCase());
109
- if (isos.length < 2)
110
- return isos[0];
111
- return isos.find(iso => this.getList().countries[iso] !== undefined);
112
- }
113
- catch {
114
- return undefined;
115
- }
99
+ return;
116
100
  }
117
101
  getOverpassApi() {
118
102
  return (0, services_1.useOverpassApi)();
@@ -443,10 +443,11 @@ let RideDisplayService = (() => {
443
443
  }
444
444
  async stopRide(props = {}) {
445
445
  try {
446
- if (this.state !== 'Starting' && this.state !== 'Idle') {
447
- this.logEvent({ message: "activity stopped", activity: this.activity?.id, lastState: this.state });
448
- }
449
446
  const prevState = this.state;
447
+ const isRedundantCall = prevState === 'Finished' || prevState === 'Idle' || prevState === 'Closing';
448
+ if (!isRedundantCall && this.activity) {
449
+ this.logEvent({ message: "activity stopped", activity: this.activity?.id, lastState: prevState });
450
+ }
450
451
  this.state = prevState === 'Starting' ? 'Closing' : 'Finished';
451
452
  this.observer?.stop({ immediately: props.noStateUpdates });
452
453
  if (props.noStateUpdates)
@@ -456,7 +457,7 @@ let RideDisplayService = (() => {
456
457
  (0, utils_1.waitNextTick)().then(() => { if (this.observer === observerToClear)
457
458
  delete this.observer; });
458
459
  }
459
- if (prevState === 'Finished' || prevState === 'Idle' || prevState === 'Closing') {
460
+ if (isRedundantCall) {
460
461
  this.state = 'Idle';
461
462
  return;
462
463
  }
@@ -140,10 +140,10 @@ let GpxDisplayService = (() => {
140
140
  }
141
141
  }
142
142
  waitsForStreetView() {
143
- return this.isMobile() && this.getRideSettingsDisplay().getRideView() === 'sv';
143
+ return this.getRideSettingsDisplay().getRideView() === 'sv';
144
144
  }
145
145
  waitsForSatelliteView() {
146
- return this.isMobile() && this.getRideSettingsDisplay().getRideView() === 'sat';
146
+ return this.getRideSettingsDisplay().getRideView() === 'sat';
147
147
  }
148
148
  getSatelliteViewProps() {
149
149
  return {
@@ -217,8 +217,8 @@ let GpxDisplayService = (() => {
217
217
  this.clearSatelliteViewStartTimeout();
218
218
  }
219
219
  else if (state === 'Error') {
220
- this.logEvent({ message: 'sat view error', error: this.mapError });
221
220
  this.mapError = error;
221
+ this.logEvent({ message: 'sat view error', error: this.mapError });
222
222
  }
223
223
  this.emit('state-update');
224
224
  }
@@ -131,7 +131,7 @@ let RLVDisplayService = (() => {
131
131
  }
132
132
  await this.checkForAdditionalVideos(videoRoute);
133
133
  const nextId = (0, routes_1.getNextVideoId)(videoRoute);
134
- this.logEvent({ message: 'video added', id: videoRoute.description.id, title: videoRoute.description.title, segmentDistance: videoRoute.description.distance, next: nextId });
134
+ this.logEvent({ message: 'video added', id: videoRoute.description.id, title: videoRoute.description.title, segmentDistance: videoRoute.description.distance, next: nextId, src: video?.source });
135
135
  }
136
136
  catch (err) {
137
137
  this.logError(err, 'addVideo');
@@ -524,7 +524,7 @@ let RLVDisplayService = (() => {
524
524
  video.source.getObserver().on('convert-error', (message) => this.onConvertError(message, video));
525
525
  }
526
526
  cleanupUrl(url) {
527
- return (0, video_1.resolvePlaybackUrl)(url, this.isMobile());
527
+ return (0, video_1.resolvePlaybackUrl)(url);
528
528
  }
529
529
  isLoopEnabled() {
530
530
  return this.isLoop() && !this.startSettings?.loopOverwrite;
@@ -4,16 +4,27 @@ exports.GPXParser = void 0;
4
4
  const utils_1 = require("../../../utils");
5
5
  const math_1 = require("../../../utils/math");
6
6
  const valid_1 = require("../../../utils/valid");
7
+ const xml_1 = require("../../../utils/xml");
7
8
  const route_1 = require("../utils/route");
8
- const xml_1 = require("./xml");
9
+ const gpxFast_1 = require("./gpxFast");
10
+ const xml_2 = require("./xml");
9
11
  const MIN_DISTANCE = 1;
10
- class GPXParser extends xml_1.XMLParser {
12
+ class GPXParser extends xml_2.XMLParser {
11
13
  static SCHEME = 'gpx';
12
14
  props;
13
15
  constructor(props = {}) {
14
16
  super();
15
17
  this.props = props;
16
18
  }
19
+ async getData(file, data) {
20
+ if (data)
21
+ return data;
22
+ const resData = await this.readAndDecode(file);
23
+ const fast = (0, gpxFast_1.tryParseGpxRaw)(resData);
24
+ if (fast)
25
+ return new xml_1.XmlJSON(fast, 'gpx');
26
+ return await (0, xml_1.parseXml)(resData);
27
+ }
17
28
  getPrimaryExtension() {
18
29
  return 'gpx';
19
30
  }
@@ -125,7 +136,7 @@ class GPXParser extends xml_1.XMLParser {
125
136
  original: gpxPt,
126
137
  parsed: point
127
138
  };
128
- this.logger.logEvent({ message: 'error', fn: 'loadPoints', reason: 'unvalid point', info });
139
+ this.getLogger()?.logEvent({ message: 'error', fn: 'loadPoints', reason: 'unvalid point', info });
129
140
  return;
130
141
  }
131
142
  if (this.props.addTime) {
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tryParseGpxRaw = void 0;
4
+ const decodeEntities = (s) => s.replaceAll('&lt;', '<').replaceAll('&gt;', '>').replaceAll('&quot;', '"').replaceAll('&apos;', "'").replaceAll('&amp;', '&');
5
+ const isTagWhitespace = (c) => c === ' ' || c === '\t' || c === '\n' || c === '\r';
6
+ const isAttrBoundary = (c) => c === '=' || c === '>' || c === '/' || isTagWhitespace(c);
7
+ const skipBoundary = (s, i) => {
8
+ while (i < s.length && (s[i] === '<' || isAttrBoundary(s[i])))
9
+ i++;
10
+ return i;
11
+ };
12
+ const skipWhitespace = (s, i) => {
13
+ while (i < s.length && isTagWhitespace(s[i]))
14
+ i++;
15
+ return i;
16
+ };
17
+ const readToken = (s, i) => {
18
+ const start = i;
19
+ while (i < s.length && !isAttrBoundary(s[i]))
20
+ i++;
21
+ return { token: s.slice(start, i), next: i };
22
+ };
23
+ const readAttr = (s, i) => {
24
+ const { token: name, next: afterName } = readToken(s, i);
25
+ let j = skipWhitespace(s, afterName);
26
+ if (s[j] !== '=')
27
+ return { name, next: j };
28
+ j = skipWhitespace(s, j + 1);
29
+ const quote = s[j];
30
+ if (quote !== '"' && quote !== "'")
31
+ return { name, next: j };
32
+ const valueStart = j + 1;
33
+ const closeIdx = s.indexOf(quote, valueStart);
34
+ if (closeIdx === -1)
35
+ return { name, next: s.length };
36
+ return { name, value: s.slice(valueStart, closeIdx), next: closeIdx + 1 };
37
+ };
38
+ const parseAttrs = (tagText) => {
39
+ const attrs = {};
40
+ let i = 0;
41
+ while (i < tagText.length) {
42
+ i = skipBoundary(tagText, i);
43
+ if (i >= tagText.length)
44
+ break;
45
+ const { name, value, next } = readAttr(tagText, i);
46
+ if (name && value !== undefined)
47
+ attrs[name] = value;
48
+ i = next;
49
+ }
50
+ return attrs;
51
+ };
52
+ const CDATA_RE = /^<!\[CDATA\[([\s\S]*)\]\]>$/;
53
+ const extractLeaf = (block, tag) => {
54
+ const openTag = `<${tag}`;
55
+ const start = block.indexOf(openTag);
56
+ if (start === -1)
57
+ return undefined;
58
+ const afterName = block[start + 1 + tag.length];
59
+ if (afterName !== undefined && !/[\s>/]/.test(afterName))
60
+ return undefined;
61
+ const tagEnd = block.indexOf('>', start);
62
+ if (tagEnd === -1)
63
+ return undefined;
64
+ if (block[tagEnd - 1] === '/')
65
+ return '';
66
+ const closeTag = `</${tag}>`;
67
+ const closeIdx = block.indexOf(closeTag, tagEnd);
68
+ if (closeIdx === -1)
69
+ return undefined;
70
+ const raw = block.slice(tagEnd + 1, closeIdx).trim();
71
+ const cdata = CDATA_RE.exec(raw);
72
+ return cdata ? cdata[1] : decodeEntities(raw);
73
+ };
74
+ const extractBlocks = (text, tag) => {
75
+ const blocks = [];
76
+ let idx = 0;
77
+ while (true) {
78
+ const start = text.indexOf(`<${tag}`, idx);
79
+ if (start === -1)
80
+ break;
81
+ const afterName = text[start + 1 + tag.length];
82
+ if (afterName !== undefined && !/[\s>/]/.test(afterName)) {
83
+ idx = start + 1;
84
+ continue;
85
+ }
86
+ const tagEnd = text.indexOf('>', start);
87
+ if (tagEnd === -1)
88
+ return undefined;
89
+ if (text[tagEnd - 1] === '/') {
90
+ blocks.push({ body: '' });
91
+ idx = tagEnd + 1;
92
+ continue;
93
+ }
94
+ const closeTag = `</${tag}>`;
95
+ const closeIdx = text.indexOf(closeTag, tagEnd);
96
+ if (closeIdx === -1)
97
+ return undefined;
98
+ blocks.push({ body: text.slice(tagEnd + 1, closeIdx) });
99
+ idx = closeIdx + closeTag.length;
100
+ }
101
+ return blocks;
102
+ };
103
+ const parseOneTrkpt = (segBody, start) => {
104
+ const tagEnd = segBody.indexOf('>', start);
105
+ if (tagEnd === -1)
106
+ return undefined;
107
+ const attrs = parseAttrs(segBody.slice(start, tagEnd + 1));
108
+ const point = { $: { lat: attrs.lat, lon: attrs.lon } };
109
+ if (segBody[tagEnd - 1] === '/')
110
+ return { point, nextIdx: tagEnd + 1 };
111
+ const closeIdx = segBody.indexOf('</trkpt>', tagEnd);
112
+ if (closeIdx === -1)
113
+ return undefined;
114
+ const body = segBody.slice(tagEnd + 1, closeIdx);
115
+ const ele = extractLeaf(body, 'ele');
116
+ if (ele !== undefined)
117
+ point.ele = [ele];
118
+ const time = extractLeaf(body, 'time');
119
+ if (time !== undefined)
120
+ point.time = [time];
121
+ return { point, nextIdx: closeIdx + '</trkpt>'.length };
122
+ };
123
+ const extractTrkpts = (segBody) => {
124
+ const points = [];
125
+ let idx = 0;
126
+ while (true) {
127
+ const start = segBody.indexOf('<trkpt', idx);
128
+ if (start === -1)
129
+ break;
130
+ const afterName = segBody[start + 6];
131
+ if (afterName !== undefined && !/[\s>/]/.test(afterName)) {
132
+ idx = start + 1;
133
+ continue;
134
+ }
135
+ const parsed = parseOneTrkpt(segBody, start);
136
+ if (!parsed)
137
+ return undefined;
138
+ points.push(parsed.point);
139
+ idx = parsed.nextIdx;
140
+ }
141
+ return points;
142
+ };
143
+ const buildTrk = (body) => {
144
+ const segStart = body.search(/<trkseg[\s>]/);
145
+ const header = segStart === -1 ? body : body.slice(0, segStart);
146
+ const segBlocks = extractBlocks(body, 'trkseg');
147
+ if (!segBlocks)
148
+ return undefined;
149
+ const trkseg = [];
150
+ for (const seg of segBlocks) {
151
+ const points = extractTrkpts(seg.body);
152
+ if (!points)
153
+ return undefined;
154
+ trkseg.push({ trkpt: points });
155
+ }
156
+ const trkObj = { trkseg };
157
+ const name = extractLeaf(header, 'name');
158
+ if (name !== undefined)
159
+ trkObj.name = [name];
160
+ const desc = extractLeaf(header, 'desc');
161
+ if (desc !== undefined)
162
+ trkObj.desc = [desc];
163
+ return trkObj;
164
+ };
165
+ const buildMetadata = (xmlText) => {
166
+ const metaMatch = /<metadata[\s>][\s\S]*?<\/metadata>/.exec(xmlText);
167
+ if (!metaMatch)
168
+ return undefined;
169
+ const openEnd = metaMatch[0].indexOf('>');
170
+ const closeStart = metaMatch[0].lastIndexOf('</metadata>');
171
+ const body = metaMatch[0].slice(openEnd + 1, closeStart).replace(/<author[\s>][\s\S]*?<\/author>/, '');
172
+ const name = extractLeaf(body, 'name');
173
+ return name !== undefined ? { name: [name] } : undefined;
174
+ };
175
+ const countPoints = (trk) => trk.reduce((sum, t) => sum + t.trkseg.reduce((s, seg) => s + seg.trkpt.length, 0), 0);
176
+ const tryParseGpxRaw = (xmlText) => {
177
+ try {
178
+ if (!/<gpx[\s>]/.test(xmlText))
179
+ return undefined;
180
+ const trkBlocks = extractBlocks(xmlText, 'trk');
181
+ if (!trkBlocks || trkBlocks.length === 0)
182
+ return undefined;
183
+ const trk = [];
184
+ for (const { body } of trkBlocks) {
185
+ const trkObj = buildTrk(body);
186
+ if (!trkObj)
187
+ return undefined;
188
+ trk.push(trkObj);
189
+ }
190
+ const gpx = { trk };
191
+ const metadata = buildMetadata(xmlText);
192
+ if (metadata)
193
+ gpx.metadata = [metadata];
194
+ const literalCount = (xmlText.match(/<trkpt[\s>]/g) ?? []).length;
195
+ if (countPoints(trk) !== literalCount)
196
+ return undefined;
197
+ return { gpx };
198
+ }
199
+ catch {
200
+ return undefined;
201
+ }
202
+ };
203
+ exports.tryParseGpxRaw = tryParseGpxRaw;
@@ -49,7 +49,8 @@ class IncyclistXMLParser extends xml_1.XMLParser {
49
49
  gpxFile.url = gpxFile.url.replace(xmlBase, fileName);
50
50
  }
51
51
  else {
52
- gpxFile.filename = gpxFile.filename.replace(xmlBase, fileName);
52
+ const encodedFileName = fileInfo.delimiter === '%2F' ? encodeURIComponent(fileName) : fileName;
53
+ gpxFile.filename = `${fileInfo.dir}${fileInfo.delimiter}${encodedFileName}`;
53
54
  }
54
55
  try {
55
56
  let points;
@@ -142,8 +142,8 @@ const safeEncode = (path) => {
142
142
  };
143
143
  exports.safeEncode = safeEncode;
144
144
  const handleFileUrlPath = (fileName) => {
145
- const cleanPath = (0, exports.safeDecode)(fileName.replace(/^file:\/\/\/?/, ''));
146
- return `file://${encodeURI(cleanPath)}`;
145
+ const cleanPath = (0, exports.safeDecode)(fileName.replace(/^file:\/\//, ''));
146
+ return (0, exports.buildFileUrl)(cleanPath);
147
147
  };
148
148
  const buildFileUrl = (normalizedPath) => {
149
149
  if (isAbsolutePath(normalizedPath)) {
@@ -231,26 +231,31 @@ const decodeUtf16Be = (data) => {
231
231
  return Buffer.from(swapped.toString('utf16le')).toString('utf-8');
232
232
  };
233
233
  const getUtf8Data = (res) => {
234
- const buf = Buffer.isBuffer(res) ? res : Buffer.from(res, 'binary');
235
- if (buf[0] === 0xFE && buf[1] === 0xFF) {
234
+ const prefix = Buffer.isBuffer(res) ? res.subarray(0, 4) : Buffer.from(res.slice(0, 4), 'binary');
235
+ if (prefix[0] === 0xFE && prefix[1] === 0xFF) {
236
+ const buf = Buffer.isBuffer(res) ? res : Buffer.from(res, 'binary');
236
237
  return decodeUtf16Be(buf.subarray(2));
237
238
  }
238
- if (buf[0] === 0xFF && buf[1] === 0xFE) {
239
+ if (prefix[0] === 0xFF && prefix[1] === 0xFE) {
240
+ const buf = Buffer.isBuffer(res) ? res : Buffer.from(res, 'binary');
239
241
  return Buffer.from(buf.subarray(2).toString('utf16le')).toString('utf-8');
240
242
  }
241
- if (buf[0] === 0xFD && buf[1] === 0xFD) {
242
- if (buf[2] === 0x00 && buf[3] === 0x3C) {
243
+ if (prefix[0] === 0xFD && prefix[1] === 0xFD) {
244
+ const buf = Buffer.isBuffer(res) ? res : Buffer.from(res, 'binary');
245
+ if (prefix[2] === 0x00 && prefix[3] === 0x3C) {
243
246
  return decodeUtf16Be(buf.subarray(2));
244
247
  }
245
- if (buf[2] === 0x3C && buf[3] === 0x00) {
248
+ if (prefix[2] === 0x3C && prefix[3] === 0x00) {
246
249
  return Buffer.from(buf.subarray(2).toString('utf16le')).toString('utf-8');
247
250
  }
248
251
  }
249
- const str = typeof res === 'string' ? res : buf.toString('utf-8');
252
+ const str = typeof res === 'string' ? res : res.toString('utf-8');
250
253
  if (str.codePointAt(0) === 0xFEFF)
251
254
  return str.slice(1);
252
- if (buf[0] === 0xEF && buf[1] === 0xBB && buf[2] === 0xBF)
255
+ if (prefix[0] === 0xEF && prefix[1] === 0xBB && prefix[2] === 0xBF) {
256
+ const buf = Buffer.isBuffer(res) ? res : Buffer.from(res, 'binary');
253
257
  return buf.subarray(3).toString('utf-8');
258
+ }
254
259
  return str;
255
260
  };
256
261
  exports.getUtf8Data = getUtf8Data;
@@ -35,6 +35,10 @@ class XMLParser {
35
35
  async getData(file, data) {
36
36
  if (data)
37
37
  return data;
38
+ const resData = await this.readAndDecode(file);
39
+ return await (0, xml_1.parseXml)(resData);
40
+ }
41
+ async readAndDecode(file) {
38
42
  const onError = () => {
39
43
  throw new Error('Could not open file: ' + (0, utils_2.getFileName)(file));
40
44
  };
@@ -45,9 +49,7 @@ class XMLParser {
45
49
  this.getLogger().logEvent({ message: '[Parser] getData error', error: res.error });
46
50
  onError();
47
51
  }
48
- const resData = (0, utils_1.getUtf8Data)(res.data);
49
- const xml = await (0, xml_1.parseXml)(resData);
50
- return xml;
52
+ return (0, utils_1.getUtf8Data)(res.data);
51
53
  }
52
54
  catch (err) {
53
55
  this.getLogger().logEvent({ message: '[Parser] getData error', error: err.message });
@@ -278,6 +280,14 @@ class XMLParser {
278
280
  let prevDistance = 0;
279
281
  route.elevation = 0;
280
282
  const points = [];
283
+ const altitudeByDistance = new Map();
284
+ if (Array.isArray(altitudes)) {
285
+ altitudes.forEach(a => {
286
+ const d = Number.parseInt(a.distance);
287
+ if (!altitudeByDistance.has(d))
288
+ altitudeByDistance.set(d, a.height);
289
+ });
290
+ }
281
291
  positions.forEach((pos, i) => {
282
292
  if (i > 0) {
283
293
  const prev = positions[i - 1];
@@ -285,7 +295,7 @@ class XMLParser {
285
295
  return;
286
296
  }
287
297
  }
288
- const altitude = getAltitude(altitudes, positions, i, prevAltitude);
298
+ const altitude = getAltitude(altitudes, altitudeByDistance, positions, i, prevAltitude);
289
299
  const elevationGain = altitude - prevAltitude;
290
300
  const pi = createPoint(pos, altitude, prevDistance);
291
301
  pi.point.cnt = i;
@@ -342,7 +352,7 @@ function createPoint(pos, altitude, prevDistance) {
342
352
  }
343
353
  return { point, prevDistance };
344
354
  }
345
- function getAltitude(altitudes, positions, i, prevAltitude) {
355
+ function getAltitude(altitudes, altitudeByDistance, positions, i, prevAltitude) {
346
356
  const pos = positions[i];
347
357
  let height = altitudes[i]?.height;
348
358
  if (altitudes && Array.isArray(altitudes)) {
@@ -350,8 +360,7 @@ function getAltitude(altitudes, positions, i, prevAltitude) {
350
360
  return height !== undefined ? Number(height) : prevAltitude;
351
361
  }
352
362
  else {
353
- const altFound = altitudes.find(a => Number.parseInt(a.distance) === Number.parseInt(pos.distance));
354
- height = altFound?.height;
363
+ height = altitudeByDistance.get(Number.parseInt(pos.distance));
355
364
  return height !== undefined ? Number(height) : prevAltitude;
356
365
  }
357
366
  }
@@ -371,6 +371,7 @@ let RouteLibraryScannerService = (() => {
371
371
  const file = this.buildFileInfo(target.controlFileUri, target.format);
372
372
  this.importProps.routes.push({
373
373
  format: target.format,
374
+ parseState: 'waiting',
374
375
  importable: false,
375
376
  label: file.base,
376
377
  id: target.controlFileUri,
@@ -401,8 +402,12 @@ let RouteLibraryScannerService = (() => {
401
402
  async _parseTarget(target, service, observer) {
402
403
  let result;
403
404
  const file = this.buildFileInfo(target.controlFileUri, target.format);
405
+ const importProps = this.importProps.routes.find(r => r.id === target.controlFileUri) ?? {};
404
406
  try {
407
+ importProps.parseState = 'parsing';
408
+ observer.emit('updated', importProps);
405
409
  result = await parsers_1.RouteParser.parse(file);
410
+ importProps.parseState = 'parsed';
406
411
  const route = new route_1.Route(result.data, result.details);
407
412
  const existing = this.importProps.routes.find(ri => ri.id === route.description.id);
408
413
  if (existing) {
@@ -425,6 +430,7 @@ let RouteLibraryScannerService = (() => {
425
430
  observer.emit('parse-result', parsed);
426
431
  }
427
432
  catch (err) {
433
+ importProps.parseState = 'parsed';
428
434
  const parsed = {
429
435
  alreadyImported: false,
430
436
  route: result ? new route_1.Route(result.data, result.details) : undefined,
@@ -581,6 +587,9 @@ let RouteLibraryScannerService = (() => {
581
587
  delimiter: uri?.startsWith('content://') ? '%2F' : '/'
582
588
  };
583
589
  }
590
+ getImportProps(parsed) {
591
+ return this.importProps.routes.find(r => r.id === parsed.controlFileUri);
592
+ }
584
593
  buildRouteDisplayItem(parsed, observer) {
585
594
  const { route, alreadyImported, parseError, format, controlFileUri } = parsed;
586
595
  const descr = route?.description ?? {};
@@ -591,11 +600,13 @@ let RouteLibraryScannerService = (() => {
591
600
  };
592
601
  const path = this.getBindings().path;
593
602
  const info = path.parse(controlFileUri);
603
+ const importProps = this.getImportProps(parsed);
594
604
  return {
595
605
  id: route?.description?.id ?? info?.base,
596
606
  distance,
597
607
  label: route?.title ?? info?.base,
598
608
  alreadyImported,
609
+ parseState: importProps?.parseState ?? 'waiting',
599
610
  importable: parseError == null,
600
611
  format,
601
612
  errorReason: parseError,
@@ -33,12 +33,8 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
33
33
  if (target) Object.defineProperty(target, contextIn.name, descriptor);
34
34
  done = true;
35
35
  };
36
- var __importDefault = (this && this.__importDefault) || function (mod) {
37
- return (mod && mod.__esModule) ? mod : { "default": mod };
38
- };
39
36
  Object.defineProperty(exports, "__esModule", { value: true });
40
37
  exports.useOverpassApi = exports.OverpassApi = exports.OVERPASS_URL_DEFAULT = void 0;
41
- const promise_any_1 = __importDefault(require("promise.any"));
42
38
  const types_1 = require("../../base/types");
43
39
  const sleep_1 = require("../../utils/sleep");
44
40
  const base_1 = require("../../apps/base/api/base");
@@ -47,9 +43,10 @@ const decorators_1 = require("../../base/decorators");
47
43
  const api_1 = require("../../api");
48
44
  const settings_1 = require("../../settings");
49
45
  const OVERPASS_URL_PRIMARY = 'https://overpass-api.de/api/interpreter';
50
- const OVERPASS_URL_ALT1 = 'https://overpass.kumi.systems/api/interpreter';
51
- const OVERPASS_URL_ALT2 = 'https://overpass.openstreetmap.fr/api/interpreter';
46
+ const OVERPASS_URL_ALT2 = 'https://lz4.overpass-api.de/api/interpreter';
47
+ const OVERPASS_URL_ALT3 = 'https://maps.mail.ru/osm/tools/overpass/api/interpreter';
52
48
  exports.OVERPASS_URL_DEFAULT = OVERPASS_URL_PRIMARY;
49
+ const DEFAULT_MIRRORS = [OVERPASS_URL_PRIMARY, OVERPASS_URL_ALT2, OVERPASS_URL_ALT3];
53
50
  let OverpassApi = (() => {
54
51
  let _classDecorators = [types_1.Singleton];
55
52
  let _classDescriptor;
@@ -77,7 +74,7 @@ let OverpassApi = (() => {
77
74
  logger;
78
75
  constructor(props) {
79
76
  super();
80
- this.mirrors = [OVERPASS_URL_PRIMARY, OVERPASS_URL_ALT1, OVERPASS_URL_ALT2];
77
+ this.mirrors = DEFAULT_MIRRORS;
81
78
  this.url = props?.url ?? this.mirrors[0];
82
79
  this.logger = new gd_eventlog_1.EventLogger('Overpass');
83
80
  if (props?.url) {
@@ -128,25 +125,19 @@ let OverpassApi = (() => {
128
125
  async postViaFetchBinding(fetchBinding, url, data) {
129
126
  const headers = this.getOverpassHeaders();
130
127
  const body = typeof data === 'string' ? data : JSON.stringify(data);
131
- try {
132
- const res = await fetchBinding.fetch(url, {
133
- method: 'POST',
134
- headers,
135
- body,
136
- referrerPolicy: 'unsafe-url'
137
- });
138
- return {
139
- data: res.data,
140
- status: res.status,
141
- statusText: res.statusText,
142
- headers: res.headers,
143
- config: {}
144
- };
145
- }
146
- catch (err) {
147
- this.logger.logEvent({ message: 'fetch error', url, data, error: err.message });
148
- throw err;
149
- }
128
+ const res = await fetchBinding.fetch(url, {
129
+ method: 'POST',
130
+ headers,
131
+ body,
132
+ referrerPolicy: 'unsafe-url'
133
+ });
134
+ return {
135
+ data: res.data,
136
+ status: res.status,
137
+ statusText: res.statusText,
138
+ headers: res.headers,
139
+ config: {}
140
+ };
150
141
  }
151
142
  async postViaNodeHttps(https, url, data) {
152
143
  return new Promise((resolve, reject) => {
@@ -245,10 +236,12 @@ let OverpassApi = (() => {
245
236
  }));
246
237
  });
247
238
  if (timeout !== undefined && timeout !== null) {
248
- promises.push((0, sleep_1.sleep)(timeout).then(() => 'timeout'));
239
+ promises.push((0, sleep_1.sleep)(timeout).then(() => {
240
+ return 'timeout';
241
+ }));
249
242
  }
250
243
  try {
251
- const res = await (0, promise_any_1.default)(promises);
244
+ const res = await Promise.any(promises);
252
245
  if (res === 'timeout') {
253
246
  return undefined;
254
247
  }
@@ -289,7 +282,7 @@ let OverpassApi = (() => {
289
282
  throw lastError;
290
283
  }
291
284
  reset() {
292
- this.mirrors = [OVERPASS_URL_PRIMARY, OVERPASS_URL_ALT1, OVERPASS_URL_ALT2];
285
+ this.mirrors = DEFAULT_MIRRORS;
293
286
  this.url = this.mirrors[0];
294
287
  }
295
288
  getAppVersion() {
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resolvePlaybackUrl = void 0;
4
- const resolvePlaybackUrl = (url, isMobile = false) => {
4
+ const resolvePlaybackUrl = (url) => {
5
5
  if (!url)
6
6
  return undefined;
7
7
  let fileName = url;
@@ -10,8 +10,6 @@ const resolvePlaybackUrl = (url, isMobile = false) => {
10
10
  fileName = fileName.replace('incyclist:', 'file:');
11
11
  if (fileName.startsWith('video:') && !lc.endsWith('.avi'))
12
12
  return fileName.replace('video:', 'file:');
13
- if (fileName.startsWith('file:') && !lc.endsWith('.avi') && !isMobile)
14
- return fileName.replace('file:', 'video:');
15
13
  if (fileName.startsWith('video:') && lc.endsWith('.avi'))
16
14
  return fileName;
17
15
  if (fileName.startsWith('file:') || fileName.startsWith('http:') || fileName.startsWith('https:') || fileName.startsWith('/'))
@@ -90,23 +90,7 @@ let Countries = (() => {
90
90
  return countries.map(c => this.getIsoFromCountry(c, locale));
91
91
  }
92
92
  async getIsoFromLatLng(point) {
93
- const { lat, lng } = point;
94
- const query = `[out:json][timeout:10];is_in(${lat},${lng})->.a;rel(pivot.a)['admin_level'='2']['boundary'='administrative'];out tags;`;
95
- try {
96
- const result = await this.getOverpassApi().query(query);
97
- if (!result?.elements)
98
- return undefined;
99
- const isos = result.elements
100
- .map(el => el.tags['ISO3166-1:alpha2'])
101
- .filter((iso) => !!iso)
102
- .map(iso => iso.toUpperCase());
103
- if (isos.length < 2)
104
- return isos[0];
105
- return isos.find(iso => this.getList().countries[iso] !== undefined);
106
- }
107
- catch {
108
- return undefined;
109
- }
93
+ return;
110
94
  }
111
95
  getOverpassApi() {
112
96
  return useOverpassApi();