incyclist-services 1.7.55 → 1.7.56
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.
- package/lib/cjs/devices/page/service.js +13 -1
- package/lib/cjs/ride/route/RLVDisplayService.js +2 -0
- package/lib/cjs/routes/base/model/route.js +1 -4
- package/lib/cjs/routes/base/parsers/epm.js +1 -0
- package/lib/cjs/routes/base/parsers/geometry.js +3 -4
- package/lib/cjs/routes/base/parsers/index.js +10 -2
- package/lib/cjs/routes/base/parsers/multixml.js +3 -1
- package/lib/cjs/routes/base/parsers/tacx/TacxParser.js +11 -15
- package/lib/cjs/routes/base/parsers/utils.js +43 -1
- package/lib/cjs/routes/base/parsers/xml.js +4 -1
- package/lib/cjs/routes/library/service.js +65 -28
- package/lib/cjs/routes/list/service.js +41 -9
- package/lib/cjs/routes/page/service.js +5 -1
- package/lib/cjs/video/VideoSyncHelper.js +14 -3
- package/lib/cjs/workouts/base/parsers/incyclist/Json.js +3 -1
- package/lib/cjs/workouts/base/parsers/intervals/parser.js +3 -1
- package/lib/cjs/workouts/base/parsers/zwo/zwo.js +3 -1
- package/lib/esm/devices/page/service.js +13 -1
- package/lib/esm/ride/route/RLVDisplayService.js +2 -0
- package/lib/esm/routes/base/model/route.js +1 -4
- package/lib/esm/routes/base/parsers/epm.js +1 -0
- package/lib/esm/routes/base/parsers/geometry.js +3 -4
- package/lib/esm/routes/base/parsers/index.js +10 -2
- package/lib/esm/routes/base/parsers/multixml.js +3 -1
- package/lib/esm/routes/base/parsers/tacx/TacxParser.js +11 -15
- package/lib/esm/routes/base/parsers/utils.js +40 -0
- package/lib/esm/routes/base/parsers/xml.js +5 -2
- package/lib/esm/routes/library/service.js +65 -28
- package/lib/esm/routes/list/service.js +41 -9
- package/lib/esm/routes/page/service.js +5 -1
- package/lib/esm/video/VideoSyncHelper.js +14 -3
- package/lib/esm/workouts/base/parsers/incyclist/Json.js +3 -1
- package/lib/esm/workouts/base/parsers/intervals/parser.js +3 -1
- package/lib/esm/workouts/base/parsers/zwo/zwo.js +3 -1
- package/lib/types/api/fs/index.d.ts +1 -1
- package/lib/types/api/repository/types.d.ts +1 -2
- package/lib/types/devices/page/service.d.ts +1 -0
- package/lib/types/routes/base/parsers/utils.d.ts +2 -0
- package/lib/types/routes/library/service.d.ts +1 -0
- package/lib/types/routes/library/types.d.ts +1 -1
- package/lib/types/routes/list/service.d.ts +4 -0
- package/lib/types/routes/page/service.d.ts +1 -0
- package/package.json +1 -1
|
@@ -45,6 +45,7 @@ const access_1 = require("../access");
|
|
|
45
45
|
const configuration_1 = require("../configuration");
|
|
46
46
|
const ui_1 = require("../../ui");
|
|
47
47
|
const types_1 = require("../../base/types");
|
|
48
|
+
const ride_1 = require("../ride");
|
|
48
49
|
let DevicesPageService = (() => {
|
|
49
50
|
let _classDecorators = [decorators_1.Singleton];
|
|
50
51
|
let _classDescriptor;
|
|
@@ -53,6 +54,7 @@ let DevicesPageService = (() => {
|
|
|
53
54
|
let _classSuper = pages_1.IncyclistPageService;
|
|
54
55
|
let _instanceExtraInitializers = [];
|
|
55
56
|
let _getDevicePairing_decorators;
|
|
57
|
+
let _getDeviceRide_decorators;
|
|
56
58
|
let _getDeviceConfiguration_decorators;
|
|
57
59
|
let _getIncyclist_decorators;
|
|
58
60
|
var DevicesPageService = class extends _classSuper {
|
|
@@ -60,9 +62,11 @@ let DevicesPageService = (() => {
|
|
|
60
62
|
static {
|
|
61
63
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
62
64
|
_getDevicePairing_decorators = [decorators_1.Injectable];
|
|
65
|
+
_getDeviceRide_decorators = [decorators_1.Injectable];
|
|
63
66
|
_getDeviceConfiguration_decorators = [decorators_1.Injectable];
|
|
64
67
|
_getIncyclist_decorators = [decorators_1.Injectable];
|
|
65
68
|
__esDecorate(this, null, _getDevicePairing_decorators, { kind: "method", name: "getDevicePairing", static: false, private: false, access: { has: obj => "getDevicePairing" in obj, get: obj => obj.getDevicePairing }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
69
|
+
__esDecorate(this, null, _getDeviceRide_decorators, { kind: "method", name: "getDeviceRide", static: false, private: false, access: { has: obj => "getDeviceRide" in obj, get: obj => obj.getDeviceRide }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
66
70
|
__esDecorate(this, null, _getDeviceConfiguration_decorators, { kind: "method", name: "getDeviceConfiguration", static: false, private: false, access: { has: obj => "getDeviceConfiguration" in obj, get: obj => obj.getDeviceConfiguration }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
67
71
|
__esDecorate(this, null, _getIncyclist_decorators, { kind: "method", name: "getIncyclist", static: false, private: false, access: { has: obj => "getIncyclist" in obj, get: obj => obj.getIncyclist }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
68
72
|
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
@@ -325,6 +329,12 @@ let DevicesPageService = (() => {
|
|
|
325
329
|
return [
|
|
326
330
|
{ label: 'OK', primary: true, onClick: this.onOK.bind(this) }
|
|
327
331
|
];
|
|
332
|
+
if (this.getDeviceRide().canEnforceSimulator()) {
|
|
333
|
+
return [
|
|
334
|
+
{ label: 'Simulate', primary: true, onClick: this.onSimulate.bind(this) },
|
|
335
|
+
{ label: 'Skip', primary: false, onClick: this.onSkip.bind(this) }
|
|
336
|
+
];
|
|
337
|
+
}
|
|
328
338
|
return [
|
|
329
339
|
{ label: 'Skip', primary: true, onClick: this.onSkip.bind(this) }
|
|
330
340
|
];
|
|
@@ -375,7 +385,6 @@ let DevicesPageService = (() => {
|
|
|
375
385
|
const simulator = this.getDeviceConfiguration().getSimulatorAdapterId();
|
|
376
386
|
this.getDevicePairing().prepareStart([simulator]);
|
|
377
387
|
const prevContentPage = this.getPrevContentPage();
|
|
378
|
-
const prevPage = this.getAppState().getState('prevPage');
|
|
379
388
|
if (this.isPairingForRide)
|
|
380
389
|
this.moveTo('/rideSimulate');
|
|
381
390
|
else
|
|
@@ -400,6 +409,9 @@ let DevicesPageService = (() => {
|
|
|
400
409
|
getDevicePairing() {
|
|
401
410
|
return (0, pairing_1.useDevicePairing)();
|
|
402
411
|
}
|
|
412
|
+
getDeviceRide() {
|
|
413
|
+
return (0, ride_1.useDeviceRide)();
|
|
414
|
+
}
|
|
403
415
|
getDeviceConfiguration() {
|
|
404
416
|
return (0, configuration_1.useDeviceConfiguration)();
|
|
405
417
|
}
|
|
@@ -521,6 +521,8 @@ let RLVDisplayService = (() => {
|
|
|
521
521
|
return fileName;
|
|
522
522
|
if (fileName.startsWith('file:') || fileName.startsWith('http:') || fileName.startsWith('https:') || fileName.startsWith('/'))
|
|
523
523
|
return fileName;
|
|
524
|
+
if (fileName.startsWith('content:'))
|
|
525
|
+
return fileName;
|
|
524
526
|
return `./${fileName}`;
|
|
525
527
|
}
|
|
526
528
|
isLoopEnabled() {
|
|
@@ -90,10 +90,7 @@ let Route = (() => {
|
|
|
90
90
|
return this.getLocalizedTitle(language);
|
|
91
91
|
}
|
|
92
92
|
getLocalizedTitle(language) {
|
|
93
|
-
|
|
94
|
-
return (0, localization_1.getLocalizedText)(this._details.localizedTitle, language) ?? this._details.title;
|
|
95
|
-
else
|
|
96
|
-
return (0, localization_1.getLocalizedText)(this._description.localizedTitle, language) ?? this._description.title;
|
|
93
|
+
return (0, localization_1.getLocalizedText)(this._description.localizedTitle, language) ?? this._description.title;
|
|
97
94
|
}
|
|
98
95
|
clone() {
|
|
99
96
|
const description = (0, clone_1.default)(this._description);
|
|
@@ -38,6 +38,7 @@ exports.GeometryParser = void 0;
|
|
|
38
38
|
const api_1 = require("../../../api");
|
|
39
39
|
const decorators_1 = require("../../../base/decorators");
|
|
40
40
|
const utils_1 = require("../../../utils");
|
|
41
|
+
const utils_2 = require("./utils");
|
|
41
42
|
let GeometryParser = (() => {
|
|
42
43
|
let _instanceExtraInitializers = [];
|
|
43
44
|
let _getLoader_decorators;
|
|
@@ -75,10 +76,8 @@ let GeometryParser = (() => {
|
|
|
75
76
|
if (res.error) {
|
|
76
77
|
onError();
|
|
77
78
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
return res.data;
|
|
79
|
+
const cleaned = (0, utils_2.getUtf8Data)(res.data);
|
|
80
|
+
return JSON.parse(cleaned);
|
|
82
81
|
}
|
|
83
82
|
catch {
|
|
84
83
|
onError();
|
|
@@ -24,6 +24,8 @@ const multixml_1 = require("./multixml");
|
|
|
24
24
|
const bikelab_1 = require("./bikelab");
|
|
25
25
|
const epm_1 = require("./epm");
|
|
26
26
|
const TacxParser_1 = require("./tacx/TacxParser");
|
|
27
|
+
const utils_1 = require("./utils");
|
|
28
|
+
const gd_eventlog_1 = require("gd-eventlog");
|
|
27
29
|
const useParsers = () => {
|
|
28
30
|
const parsers = factory_1.ParserFactory.getInstance();
|
|
29
31
|
if (!parsers.isInitialized()) {
|
|
@@ -38,6 +40,8 @@ const useParsers = () => {
|
|
|
38
40
|
exports.useParsers = useParsers;
|
|
39
41
|
class RouteParser {
|
|
40
42
|
static async parse(info) {
|
|
43
|
+
const logger = new gd_eventlog_1.EventLogger('RouteParser');
|
|
44
|
+
(0, utils_1.fixIncorrectFileInfo)(info);
|
|
41
45
|
const parsers = (0, exports.useParsers)();
|
|
42
46
|
const formatParsers = parsers.suppertsExtension(info.ext);
|
|
43
47
|
const promises = [];
|
|
@@ -51,8 +55,12 @@ class RouteParser {
|
|
|
51
55
|
});
|
|
52
56
|
const res = await Promise.allSettled(promises);
|
|
53
57
|
const matching = res.map(promise => promise.status === 'fulfilled' ? promise.value : undefined).find(p => p !== undefined);
|
|
54
|
-
if (matching)
|
|
55
|
-
|
|
58
|
+
if (matching) {
|
|
59
|
+
logger.logEvent({ message: 'before import' });
|
|
60
|
+
const res = await matching.parser.import(info, matching.data);
|
|
61
|
+
logger.logEvent({ message: 'after import' });
|
|
62
|
+
return res;
|
|
63
|
+
}
|
|
56
64
|
if (formatParsers.length === 0)
|
|
57
65
|
throw new Error('no matching parser found');
|
|
58
66
|
else {
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.MultipleXMLParser = void 0;
|
|
4
4
|
const api_1 = require("../../../api");
|
|
5
5
|
const xml_1 = require("../../../utils/xml");
|
|
6
|
+
const utils_1 = require("./utils");
|
|
6
7
|
class MultipleXMLParser {
|
|
7
8
|
parsers;
|
|
8
9
|
constructor(classes) {
|
|
@@ -37,7 +38,8 @@ class MultipleXMLParser {
|
|
|
37
38
|
if (res.error) {
|
|
38
39
|
throw new Error('Could not open file');
|
|
39
40
|
}
|
|
40
|
-
const
|
|
41
|
+
const cleaned = (0, utils_1.getUtf8Data)(res.data);
|
|
42
|
+
const xml = await (0, xml_1.parseXml)(cleaned);
|
|
41
43
|
return xml;
|
|
42
44
|
}
|
|
43
45
|
}
|
|
@@ -30,7 +30,9 @@ class TacxParser {
|
|
|
30
30
|
if (res.error) {
|
|
31
31
|
onError();
|
|
32
32
|
}
|
|
33
|
-
|
|
33
|
+
const buf = Buffer.isBuffer(res.data) ? res.data : Buffer.from(res.data, 'binary');
|
|
34
|
+
const data = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
|
|
35
|
+
return data;
|
|
34
36
|
}
|
|
35
37
|
catch {
|
|
36
38
|
onError();
|
|
@@ -50,24 +52,18 @@ class TacxParser {
|
|
|
50
52
|
if (file.ext === 'rlv') {
|
|
51
53
|
const pgmfFile = (0, clone_1.default)(file);
|
|
52
54
|
pgmfFile.ext = 'pgmf';
|
|
53
|
-
pgmfFile.
|
|
54
|
-
pgmfFile.filename = `${dir}${d}${pgmfFile.
|
|
55
|
-
pgmfFile.url = `file:///${dir}${d}${pgmfFile.
|
|
56
|
-
return {
|
|
57
|
-
rlvFile: file,
|
|
58
|
-
pgmfFile
|
|
59
|
-
};
|
|
55
|
+
pgmfFile.base = pgmfFile.base.replace('.rlv', '.pgmf');
|
|
56
|
+
pgmfFile.filename = `${dir}${d}${pgmfFile.base}`;
|
|
57
|
+
pgmfFile.url = `file:///${dir}${d}${pgmfFile.base}`;
|
|
58
|
+
return { rlvFile: file, pgmfFile };
|
|
60
59
|
}
|
|
61
60
|
else if (file.ext === 'pgmf') {
|
|
62
61
|
const rlvFile = (0, clone_1.default)(file);
|
|
63
62
|
rlvFile.ext = 'rlv';
|
|
64
|
-
rlvFile.
|
|
65
|
-
rlvFile.filename = `${dir}${d}${rlvFile.
|
|
66
|
-
rlvFile.url = `file:///${dir}${d}${rlvFile.
|
|
67
|
-
return {
|
|
68
|
-
rlvFile,
|
|
69
|
-
pgmfFile: file
|
|
70
|
-
};
|
|
63
|
+
rlvFile.base = rlvFile.base.replace('.pgmf', '.rlv');
|
|
64
|
+
rlvFile.filename = `${dir}${d}${rlvFile.base}`;
|
|
65
|
+
rlvFile.url = `file:///${dir}${d}${rlvFile.base}`;
|
|
66
|
+
return { rlvFile, pgmfFile: file };
|
|
71
67
|
}
|
|
72
68
|
else {
|
|
73
69
|
throw new Error(`Unsupported file type ${file.ext}`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseInformations = exports.getReferencedFileInfo = exports.BinaryReader = void 0;
|
|
3
|
+
exports.getUtf8Data = exports.fixIncorrectFileInfo = exports.parseInformations = exports.getReferencedFileInfo = exports.BinaryReader = void 0;
|
|
4
4
|
const api_1 = require("../../../api");
|
|
5
5
|
class BinaryReader {
|
|
6
6
|
pos;
|
|
@@ -102,6 +102,9 @@ const getReferencedFileInfo = (info, referenced, scheme = 'file') => {
|
|
|
102
102
|
exports.getReferencedFileInfo = getReferencedFileInfo;
|
|
103
103
|
const buildFromFile = (info, referenced) => {
|
|
104
104
|
if (referenced.file) {
|
|
105
|
+
if (info.filename?.startsWith('content://')) {
|
|
106
|
+
return `${info.dir}${info.delimiter}${referenced.file}`;
|
|
107
|
+
}
|
|
105
108
|
const fileName = info.filename?.replace(info.base, referenced.file);
|
|
106
109
|
return `file:///${fileName}`;
|
|
107
110
|
}
|
|
@@ -136,3 +139,42 @@ const parseInformations = (informations) => {
|
|
|
136
139
|
});
|
|
137
140
|
};
|
|
138
141
|
exports.parseInformations = parseInformations;
|
|
142
|
+
const fixIncorrectFileInfo = (file) => {
|
|
143
|
+
if (!file.base) {
|
|
144
|
+
file.base = file.name;
|
|
145
|
+
file.name = file.base.replace(`.${file.ext}`, '');
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
exports.fixIncorrectFileInfo = fixIncorrectFileInfo;
|
|
149
|
+
const decodeUtf16Be = (data) => {
|
|
150
|
+
const swapped = Buffer.alloc(data.length);
|
|
151
|
+
for (let i = 0; i + 1 < data.length; i += 2) {
|
|
152
|
+
swapped[i] = data[i + 1];
|
|
153
|
+
swapped[i + 1] = data[i];
|
|
154
|
+
}
|
|
155
|
+
return Buffer.from(swapped.toString('utf16le')).toString('utf-8');
|
|
156
|
+
};
|
|
157
|
+
const getUtf8Data = (res) => {
|
|
158
|
+
const buf = Buffer.isBuffer(res) ? res : Buffer.from(res, 'binary');
|
|
159
|
+
if (buf[0] === 0xFE && buf[1] === 0xFF) {
|
|
160
|
+
return decodeUtf16Be(buf.subarray(2));
|
|
161
|
+
}
|
|
162
|
+
if (buf[0] === 0xFF && buf[1] === 0xFE) {
|
|
163
|
+
return Buffer.from(buf.subarray(2).toString('utf16le')).toString('utf-8');
|
|
164
|
+
}
|
|
165
|
+
if (buf[0] === 0xFD && buf[1] === 0xFD) {
|
|
166
|
+
if (buf[2] === 0x00 && buf[3] === 0x3C) {
|
|
167
|
+
return decodeUtf16Be(buf.subarray(2));
|
|
168
|
+
}
|
|
169
|
+
if (buf[2] === 0x3C && buf[3] === 0x00) {
|
|
170
|
+
return Buffer.from(buf.subarray(2).toString('utf16le')).toString('utf-8');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const str = typeof res === 'string' ? res : buf.toString('utf-8');
|
|
174
|
+
if (str.charCodeAt(0) === 0xFEFF)
|
|
175
|
+
return str.slice(1);
|
|
176
|
+
if (buf[0] === 0xEF && buf[1] === 0xBB && buf[2] === 0xBF)
|
|
177
|
+
return buf.subarray(3).toString('utf-8');
|
|
178
|
+
return str;
|
|
179
|
+
};
|
|
180
|
+
exports.getUtf8Data = getUtf8Data;
|
|
@@ -38,16 +38,19 @@ class XMLParser {
|
|
|
38
38
|
const onError = () => {
|
|
39
39
|
throw new Error('Could not open file: ' + (0, utils_2.getFileName)(file));
|
|
40
40
|
};
|
|
41
|
+
let cleaned;
|
|
41
42
|
const loader = (0, api_1.getBindings)().loader;
|
|
42
43
|
try {
|
|
43
44
|
const res = await loader.open(file);
|
|
44
45
|
if (res.error) {
|
|
45
46
|
onError();
|
|
46
47
|
}
|
|
47
|
-
const
|
|
48
|
+
const resData = (0, utils_1.getUtf8Data)(res.data);
|
|
49
|
+
const xml = await (0, xml_1.parseXml)(resData);
|
|
48
50
|
return xml;
|
|
49
51
|
}
|
|
50
52
|
catch {
|
|
53
|
+
console.log('# failed', Buffer.from(cleaned ?? '').toString('hex'));
|
|
51
54
|
onError();
|
|
52
55
|
}
|
|
53
56
|
}
|
|
@@ -93,6 +93,7 @@ let RouteLibraryScannerService = (() => {
|
|
|
93
93
|
}
|
|
94
94
|
done() {
|
|
95
95
|
this.importProps = undefined;
|
|
96
|
+
this.scanResult = [];
|
|
96
97
|
}
|
|
97
98
|
getDisplayProps() {
|
|
98
99
|
return { ...this.importProps };
|
|
@@ -130,7 +131,6 @@ let RouteLibraryScannerService = (() => {
|
|
|
130
131
|
this.importProps.scanProgress = progress;
|
|
131
132
|
})
|
|
132
133
|
.on('scan-complete', () => {
|
|
133
|
-
console.log('# scan-complete');
|
|
134
134
|
this.importProps.phase = 'parsing';
|
|
135
135
|
});
|
|
136
136
|
return observer;
|
|
@@ -159,7 +159,6 @@ let RouteLibraryScannerService = (() => {
|
|
|
159
159
|
}
|
|
160
160
|
});
|
|
161
161
|
observer.on('parse-complete', () => {
|
|
162
|
-
console.log('# parse-complete');
|
|
163
162
|
this.importProps.phase = 'selecting';
|
|
164
163
|
});
|
|
165
164
|
return observer;
|
|
@@ -193,7 +192,8 @@ let RouteLibraryScannerService = (() => {
|
|
|
193
192
|
}
|
|
194
193
|
cancel() {
|
|
195
194
|
this.isCancelled = true;
|
|
196
|
-
this.
|
|
195
|
+
this.done();
|
|
196
|
+
this.prepare();
|
|
197
197
|
}
|
|
198
198
|
async importRoute(fileInfo, observer) {
|
|
199
199
|
await (0, sleep_1.sleep)(0);
|
|
@@ -313,7 +313,6 @@ let RouteLibraryScannerService = (() => {
|
|
|
313
313
|
if (!this.isCancelled) {
|
|
314
314
|
const routeAnnouncement = await this.buildDiscoveredRoute(file, files, uri, folderName, parsers);
|
|
315
315
|
discoveredCount.value++;
|
|
316
|
-
console.log('added', routeAnnouncement, this.scanResult.length);
|
|
317
316
|
observer.emit('scan-result', routeAnnouncement);
|
|
318
317
|
this.scanResult.push(routeAnnouncement);
|
|
319
318
|
}
|
|
@@ -373,57 +372,95 @@ let RouteLibraryScannerService = (() => {
|
|
|
373
372
|
observer.emit('parse-complete');
|
|
374
373
|
}
|
|
375
374
|
async _parseTarget(target, service, observer, idx) {
|
|
376
|
-
|
|
377
|
-
observer.emit('parse-result', {
|
|
378
|
-
alreadyImported: true,
|
|
379
|
-
route: service.getBySourceUri(target.controlFileUri),
|
|
380
|
-
folderUri: target.folderUri,
|
|
381
|
-
controlFileUri: target.controlFileUri,
|
|
382
|
-
format: target.format
|
|
383
|
-
});
|
|
384
|
-
return;
|
|
385
|
-
}
|
|
375
|
+
this.logEvent({ message: '_parseTarget', target: target.controlFileUri });
|
|
386
376
|
let result;
|
|
387
377
|
const file = this.buildFileInfo(target.controlFileUri, target.format);
|
|
388
378
|
try {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
|
|
379
|
+
this.logEvent({ message: '_parseTarget:parse', target: target.controlFileUri });
|
|
380
|
+
result = await parsers_1.RouteParser.parse(file);
|
|
381
|
+
this.logEvent({ message: '_parseTarget:parse done', target: target.controlFileUri });
|
|
382
|
+
const route = new route_1.Route(result.data, result.details);
|
|
383
|
+
const existing = this.importProps.routes.find(ri => ri.id === route.description.id);
|
|
384
|
+
if (existing) {
|
|
385
|
+
result = undefined;
|
|
386
|
+
throw new Error(`Duplicate of ${existing.label}`);
|
|
394
387
|
}
|
|
395
388
|
if (result.data.hasVideo) {
|
|
396
|
-
this.validateVideoUrl(
|
|
389
|
+
this.validateVideoUrl(route, target.folderUri, target.files);
|
|
397
390
|
}
|
|
398
|
-
|
|
391
|
+
const parsed = {
|
|
399
392
|
alreadyImported: false,
|
|
400
|
-
route
|
|
393
|
+
route,
|
|
401
394
|
folderUri: target.folderUri,
|
|
402
395
|
controlFileUri: target.controlFileUri,
|
|
403
396
|
format: target.format
|
|
404
|
-
}
|
|
397
|
+
};
|
|
398
|
+
if (service.getRoute(route.description.id)) {
|
|
399
|
+
parsed.alreadyImported = true;
|
|
400
|
+
}
|
|
401
|
+
observer.emit('parse-result', parsed);
|
|
405
402
|
}
|
|
406
403
|
catch (err) {
|
|
407
|
-
|
|
404
|
+
const parsed = {
|
|
408
405
|
alreadyImported: false,
|
|
409
406
|
route: result ? new route_1.Route(result.data, result.details) : undefined,
|
|
410
407
|
folderUri: target.folderUri,
|
|
411
408
|
controlFileUri: target.controlFileUri,
|
|
412
409
|
format: target.format,
|
|
413
410
|
parseError: err?.message ?? String(err)
|
|
414
|
-
}
|
|
411
|
+
};
|
|
412
|
+
this.logEvent({ message: 'could not parse route file', file: file.base, reason: err.message, stack: err.stack });
|
|
413
|
+
observer.emit('parse-result', parsed);
|
|
415
414
|
}
|
|
416
415
|
}
|
|
417
|
-
validateVideoUrl(
|
|
416
|
+
validateVideoUrl(route, folderUri, folderFiles) {
|
|
417
|
+
const routeDetail = route.details;
|
|
418
|
+
const routeDescr = route.description;
|
|
418
419
|
if (this.isMobile()) {
|
|
419
420
|
if (routeDetail.video.format === 'avi') {
|
|
420
|
-
|
|
421
|
+
const hasUrl = routeDetail.video.url != null;
|
|
422
|
+
const url = routeDetail.video.url ?? routeDetail.video.file;
|
|
423
|
+
this.logEvent({ message: '# here', url });
|
|
424
|
+
try {
|
|
425
|
+
if (url) {
|
|
426
|
+
const mp4Url = this.findMatchingMp4(url, folderFiles);
|
|
427
|
+
if (mp4Url) {
|
|
428
|
+
routeDetail.video.format = 'mp4';
|
|
429
|
+
if (hasUrl)
|
|
430
|
+
routeDetail.video.url = mp4Url;
|
|
431
|
+
else
|
|
432
|
+
routeDetail.video.file = mp4Url;
|
|
433
|
+
routeDescr.videoFormat = 'mp4';
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
throw new Error('AVI video not supported');
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
this.logEvent({ message: 'video file not found', video: routeDetail.video });
|
|
441
|
+
throw new Error('no video found');
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
catch (err) {
|
|
445
|
+
this.logEvent({ message: 'video check failed', url });
|
|
446
|
+
throw err;
|
|
447
|
+
}
|
|
421
448
|
}
|
|
422
449
|
}
|
|
423
450
|
if (routeDetail.video.file) {
|
|
424
451
|
routeDetail.video.file = this.resolveVideoUri(routeDetail.video.file, folderUri, folderFiles);
|
|
425
452
|
}
|
|
426
453
|
}
|
|
454
|
+
findMatchingMp4(videoUrl, folderFiles) {
|
|
455
|
+
const path = this.getBindings().path;
|
|
456
|
+
const fileName = path.parse(videoUrl)?.base;
|
|
457
|
+
const target = fileName.replace('.avi', '.mp4');
|
|
458
|
+
const folderFile = folderFiles.find(file => file.name === target);
|
|
459
|
+
if (!folderFile)
|
|
460
|
+
return;
|
|
461
|
+
const info = path.parse(videoUrl);
|
|
462
|
+
return videoUrl.replace(info.base, folderFile.name);
|
|
463
|
+
}
|
|
427
464
|
async _ingest(routes, observer) {
|
|
428
465
|
await (0, utils_1.waitNextTick)();
|
|
429
466
|
const service = this.getRouteList();
|
|
@@ -501,7 +538,7 @@ let RouteLibraryScannerService = (() => {
|
|
|
501
538
|
name,
|
|
502
539
|
dir,
|
|
503
540
|
ext,
|
|
504
|
-
delimiter: uri
|
|
541
|
+
delimiter: uri?.startsWith('content://') ? '%2F' : '/'
|
|
505
542
|
};
|
|
506
543
|
}
|
|
507
544
|
buildRouteDisplayItem(parsed, observer) {
|
|
@@ -127,6 +127,7 @@ let RouteListService = (() => {
|
|
|
127
127
|
currentView;
|
|
128
128
|
stats;
|
|
129
129
|
isListUpdatePaused = false;
|
|
130
|
+
cardLookup = {};
|
|
130
131
|
constructor() {
|
|
131
132
|
super('RouteList');
|
|
132
133
|
this.myRoutes = new myroutes_1.MyRoutes('myRoutes', 'My Routes');
|
|
@@ -324,15 +325,17 @@ let RouteListService = (() => {
|
|
|
324
325
|
}
|
|
325
326
|
try {
|
|
326
327
|
const filters = requestedFilters ?? this.filters;
|
|
327
|
-
|
|
328
|
+
const allCards = this.getAllSearchCards();
|
|
329
|
+
let routes = allCards.map(c => c.getDisplayProperties());
|
|
328
330
|
routes.sort((a, b) => a.title > b.title ? 1 : -1);
|
|
329
331
|
const units = this.getUnitConverter().getDefaultUnits();
|
|
330
332
|
if (!filters) {
|
|
331
333
|
const cards = routes.map(r => this.getCard(r.id));
|
|
332
334
|
return { routes, cards, filters, observer: this.observer, units };
|
|
333
335
|
}
|
|
334
|
-
if (!filters.includeDeleted)
|
|
336
|
+
if (!filters.includeDeleted) {
|
|
335
337
|
routes = routes.filter(r => !r?.isDeleted);
|
|
338
|
+
}
|
|
336
339
|
routes = this.applyTitleFilter(filters, routes);
|
|
337
340
|
routes = this.applyDistanceFilter(filters, routes);
|
|
338
341
|
routes = this.applyElevationFilter(filters, routes);
|
|
@@ -340,7 +343,8 @@ let RouteListService = (() => {
|
|
|
340
343
|
routes = this.applyContentTypeFilter(filters, routes);
|
|
341
344
|
routes = this.applyRouteTypeFilter(filters, routes);
|
|
342
345
|
routes = this.applySourceFilter(filters, routes);
|
|
343
|
-
const
|
|
346
|
+
const routeIdSet = new Set(routes.map(r => r.id));
|
|
347
|
+
const cards = allCards.filter(c => routeIdSet.has(c.getId()));
|
|
344
348
|
this.setListTop('list', 0);
|
|
345
349
|
this.setListTop('tiles', 0);
|
|
346
350
|
return { routes, cards, filters, observer: this.observer, units };
|
|
@@ -690,6 +694,7 @@ let RouteListService = (() => {
|
|
|
690
694
|
card.save();
|
|
691
695
|
card.enableDelete();
|
|
692
696
|
this.myRoutes.add(card, true);
|
|
697
|
+
this.cardLookup[route.description.id] = { card, list: this.myRoutes };
|
|
693
698
|
if (importCard) {
|
|
694
699
|
this.myRoutes.remove(importCard);
|
|
695
700
|
}
|
|
@@ -820,6 +825,9 @@ let RouteListService = (() => {
|
|
|
820
825
|
}
|
|
821
826
|
}
|
|
822
827
|
addRoute(route, source = 'system') {
|
|
828
|
+
if (this.getRoute(route.description.id)) {
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
823
831
|
this.routes.push(route);
|
|
824
832
|
if (route.description?.isDeleted) {
|
|
825
833
|
return;
|
|
@@ -831,12 +839,14 @@ let RouteListService = (() => {
|
|
|
831
839
|
if (!route.description.country) {
|
|
832
840
|
route.updateCountryFromPoints()
|
|
833
841
|
.then(() => {
|
|
842
|
+
this.logEvent({ message: 'route updated (country)', route: route.title });
|
|
834
843
|
this.db.save(route, false);
|
|
835
844
|
});
|
|
836
845
|
}
|
|
837
846
|
}
|
|
838
847
|
const list = this.selectList(route);
|
|
839
848
|
const card = new RouteCard_1.RouteCard(route, { list });
|
|
849
|
+
this.cardLookup[route.description.id] = { card, list };
|
|
840
850
|
card.verify();
|
|
841
851
|
list.add(card);
|
|
842
852
|
if (list.getId() === 'myRoutes')
|
|
@@ -850,10 +860,9 @@ let RouteListService = (() => {
|
|
|
850
860
|
}
|
|
851
861
|
}
|
|
852
862
|
async addFromApi(route) {
|
|
853
|
-
|
|
854
|
-
if (existing)
|
|
855
|
-
return;
|
|
863
|
+
this.logEvent({ message: 'add route from Api', route: route.title });
|
|
856
864
|
this.addRoute(route, 'system');
|
|
865
|
+
this.logEvent({ message: 'add route from Api done', route: route.title });
|
|
857
866
|
}
|
|
858
867
|
async update(route, source = 'user') {
|
|
859
868
|
const existing = this.findCard(route);
|
|
@@ -870,16 +879,21 @@ let RouteListService = (() => {
|
|
|
870
879
|
this.startSync();
|
|
871
880
|
}
|
|
872
881
|
async preloadDetails() {
|
|
882
|
+
this.logEvent({ message: 'preload route details: searchRepo' });
|
|
873
883
|
const { cards = [] } = this.searchRepo();
|
|
884
|
+
this.logEvent({ message: 'preload route details: searchRepo done' });
|
|
874
885
|
const promises = [];
|
|
875
886
|
const loadDetails = (card) => {
|
|
887
|
+
this.logEvent({ message: 'preload route details', route: card?.getData()?.title });
|
|
876
888
|
return this.db.getDetails(card.getId())
|
|
877
889
|
.then(details => {
|
|
890
|
+
this.logEvent({ message: 'preload route details done', route: card?.getData()?.title });
|
|
878
891
|
card.setRouteData(details);
|
|
879
892
|
const route = card.getData();
|
|
880
893
|
if (!route.description.country) {
|
|
881
894
|
route.updateCountryFromPoints()
|
|
882
895
|
.then(() => {
|
|
896
|
+
this.logEvent({ message: 'preload route updated (country)', route: card?.getData()?.title });
|
|
883
897
|
this.db.save(route, false);
|
|
884
898
|
});
|
|
885
899
|
}
|
|
@@ -981,6 +995,9 @@ let RouteListService = (() => {
|
|
|
981
995
|
});
|
|
982
996
|
}
|
|
983
997
|
async checkUIUpdateWithNoRepoStats() {
|
|
998
|
+
if (this.isMobile())
|
|
999
|
+
return;
|
|
1000
|
+
this.logEvent({ message: 'checkUIUpdateWithNoRepoStats' });
|
|
984
1001
|
const repoUpdate = this.getRepoUpdates();
|
|
985
1002
|
if (this.routes.length > 0 && repoUpdate.initial === undefined) {
|
|
986
1003
|
const ts = Date.now() - 60000;
|
|
@@ -991,6 +1008,7 @@ let RouteListService = (() => {
|
|
|
991
1008
|
this.updateRepoStats(ts);
|
|
992
1009
|
await (0, sleep_1.sleep)(5);
|
|
993
1010
|
}
|
|
1011
|
+
this.logEvent({ message: 'checkUIUpdateWithNoRepoStats done' });
|
|
994
1012
|
}
|
|
995
1013
|
getRepoUpdates() {
|
|
996
1014
|
try {
|
|
@@ -1005,23 +1023,33 @@ let RouteListService = (() => {
|
|
|
1005
1023
|
}
|
|
1006
1024
|
async loadRoutesFromRepo() {
|
|
1007
1025
|
return new Promise(done => {
|
|
1026
|
+
this.logEvent({ message: 'loadRoutesFromRepo start' });
|
|
1008
1027
|
const observer = this.db.load();
|
|
1009
1028
|
const add = this.addRoute.bind(this);
|
|
1010
1029
|
const update = this.update.bind(this);
|
|
1030
|
+
const completed = () => {
|
|
1031
|
+
this.logEvent({ message: 'loadRoutesFromRepo done' });
|
|
1032
|
+
done();
|
|
1033
|
+
};
|
|
1011
1034
|
observer.on('route.added', add);
|
|
1012
1035
|
observer.on('route.updated', update);
|
|
1013
|
-
observer.on('done',
|
|
1036
|
+
observer.on('done', completed);
|
|
1014
1037
|
});
|
|
1015
1038
|
}
|
|
1016
1039
|
async loadRoutesFromApi() {
|
|
1017
1040
|
return new Promise(done => {
|
|
1041
|
+
this.logEvent({ message: 'loadRoutesFromApi start' });
|
|
1018
1042
|
const observer = this.api.load();
|
|
1019
1043
|
const add = this.addFromApi.bind(this);
|
|
1020
1044
|
const update = this.update.bind(this);
|
|
1045
|
+
const completed = () => {
|
|
1046
|
+
this.logEvent({ message: 'loadRoutesFromApi done' });
|
|
1047
|
+
done();
|
|
1048
|
+
};
|
|
1021
1049
|
observer.on('route.added', add);
|
|
1022
1050
|
observer.on('route.updated', update);
|
|
1023
|
-
observer.on('loaded',
|
|
1024
|
-
observer.on('done',
|
|
1051
|
+
observer.on('loaded', completed);
|
|
1052
|
+
observer.on('done', completed);
|
|
1025
1053
|
});
|
|
1026
1054
|
}
|
|
1027
1055
|
async loadRouteDetails(route, id) {
|
|
@@ -1232,6 +1260,10 @@ let RouteListService = (() => {
|
|
|
1232
1260
|
if (!id && !legacyId) {
|
|
1233
1261
|
return;
|
|
1234
1262
|
}
|
|
1263
|
+
const res = this.cardLookup[id];
|
|
1264
|
+
if (res?.card) {
|
|
1265
|
+
return res;
|
|
1266
|
+
}
|
|
1235
1267
|
let card = this.myRoutes.getCards().find(c => c.getData()?.description?.id === id || c.getData()?.description?.legacyId === id);
|
|
1236
1268
|
if (card)
|
|
1237
1269
|
return { card, list: this.myRoutes };
|
|
@@ -262,6 +262,9 @@ let RoutesPageService = (() => {
|
|
|
262
262
|
getImportDisplayProps() {
|
|
263
263
|
return this.getRouteLibraryScanner().getDisplayProps();
|
|
264
264
|
}
|
|
265
|
+
onImportCancelled() {
|
|
266
|
+
this.getRouteLibraryScanner().done();
|
|
267
|
+
}
|
|
265
268
|
onImportClosed() {
|
|
266
269
|
try {
|
|
267
270
|
this.showImportDialog = false;
|
|
@@ -290,8 +293,9 @@ let RoutesPageService = (() => {
|
|
|
290
293
|
try {
|
|
291
294
|
const service = this.getRouteList();
|
|
292
295
|
const card = service.getCard(id);
|
|
293
|
-
if (card)
|
|
296
|
+
if (card) {
|
|
294
297
|
card.delete();
|
|
298
|
+
}
|
|
295
299
|
}
|
|
296
300
|
catch (err) {
|
|
297
301
|
this.logError(err, 'onDelete');
|