incyclist-services 1.7.44 → 1.7.46
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/activities/ride/service.js +1 -0
- package/lib/cjs/routes/download/service.js +51 -20
- package/lib/cjs/routes/list/cards/RouteCard.js +44 -13
- package/lib/cjs/routes/page/service.js +46 -30
- package/lib/esm/activities/ride/service.js +1 -0
- package/lib/esm/routes/download/service.js +52 -21
- package/lib/esm/routes/list/cards/RouteCard.js +45 -14
- package/lib/esm/routes/page/service.js +46 -30
- package/lib/types/api/download/index.d.ts +1 -0
- package/lib/types/routes/download/service.d.ts +4 -2
- package/lib/types/routes/list/cards/RouteCard.d.ts +9 -7
- package/lib/types/routes/list/cards/types.d.ts +4 -2
- package/lib/types/routes/page/service.d.ts +5 -2
- package/lib/types/routes/page/types.d.ts +2 -2
- package/package.json +1 -1
|
@@ -464,6 +464,7 @@ let ActivityRideService = (() => {
|
|
|
464
464
|
this.saveObserver.emit(event, ...args);
|
|
465
465
|
};
|
|
466
466
|
const run = async () => {
|
|
467
|
+
await (0, sleep_1.sleep)(5);
|
|
467
468
|
let cntCompleted = 0;
|
|
468
469
|
let success = false;
|
|
469
470
|
const localEmitter = new node_events_1.EventEmitter();
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
3
|
+
var useValue = arguments.length > 2;
|
|
4
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
5
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
6
|
+
}
|
|
7
|
+
return useValue ? value : void 0;
|
|
8
|
+
};
|
|
2
9
|
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
3
10
|
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
4
11
|
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
@@ -26,13 +33,6 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
|
|
|
26
33
|
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
27
34
|
done = true;
|
|
28
35
|
};
|
|
29
|
-
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
30
|
-
var useValue = arguments.length > 2;
|
|
31
|
-
for (var i = 0; i < initializers.length; i++) {
|
|
32
|
-
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
33
|
-
}
|
|
34
|
-
return useValue ? value : void 0;
|
|
35
|
-
};
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
37
|
exports.useRouteDownload = exports.RouteDownloadService = void 0;
|
|
38
38
|
const api_1 = require("../../api");
|
|
@@ -49,16 +49,20 @@ let RouteDownloadService = (() => {
|
|
|
49
49
|
let _classExtraInitializers = [];
|
|
50
50
|
let _classThis;
|
|
51
51
|
let _classSuper = service_1.IncyclistService;
|
|
52
|
+
let _instanceExtraInitializers = [];
|
|
53
|
+
let _getBindings_decorators;
|
|
52
54
|
var RouteDownloadService = class extends _classSuper {
|
|
53
55
|
static { _classThis = this; }
|
|
54
56
|
static {
|
|
55
57
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
58
|
+
_getBindings_decorators = [decorators_1.Injectable];
|
|
59
|
+
__esDecorate(this, null, _getBindings_decorators, { kind: "method", name: "getBindings", static: false, private: false, access: { has: obj => "getBindings" in obj, get: obj => obj.getBindings }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
56
60
|
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
57
61
|
RouteDownloadService = _classThis = _classDescriptor.value;
|
|
58
62
|
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
59
63
|
__runInitializers(_classThis, _classExtraInitializers);
|
|
60
64
|
}
|
|
61
|
-
downloads;
|
|
65
|
+
downloads = __runInitializers(this, _instanceExtraInitializers);
|
|
62
66
|
progressLogTs;
|
|
63
67
|
constructor() {
|
|
64
68
|
super('Routes');
|
|
@@ -97,7 +101,7 @@ let RouteDownloadService = (() => {
|
|
|
97
101
|
if (file) {
|
|
98
102
|
try {
|
|
99
103
|
const fs = (0, api_1.getBindings)().fs;
|
|
100
|
-
fs
|
|
104
|
+
fs?.unlink(file);
|
|
101
105
|
}
|
|
102
106
|
catch (err) {
|
|
103
107
|
this.logError(err, 'deleteIncompleteFile');
|
|
@@ -111,18 +115,35 @@ let RouteDownloadService = (() => {
|
|
|
111
115
|
async _download(route) {
|
|
112
116
|
await (0, utils_1.waitNextTick)();
|
|
113
117
|
const observer = this.getObserver(route);
|
|
118
|
+
if (!observer)
|
|
119
|
+
return;
|
|
114
120
|
try {
|
|
115
121
|
const videoDir = await this.waitForVideoDir(observer);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
122
|
+
if (videoDir) {
|
|
123
|
+
observer.once('stopped', () => {
|
|
124
|
+
this.deleteIncompleteFile(route, videoDir);
|
|
125
|
+
});
|
|
126
|
+
this.downloadRoute(route, videoDir, observer);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
this.logEvent({ message: 'could not start download', reason: 'video dir not specified' });
|
|
130
|
+
}
|
|
120
131
|
}
|
|
121
132
|
catch (err) {
|
|
122
133
|
observer.emit('error', err);
|
|
123
134
|
}
|
|
124
135
|
}
|
|
125
136
|
waitForVideoDir(observer) {
|
|
137
|
+
if (this.isMobile()) {
|
|
138
|
+
try {
|
|
139
|
+
const getVideoDir = this.getBindings()?.downloadManager?.getVideoDir;
|
|
140
|
+
if (getVideoDir) {
|
|
141
|
+
const videoDir = getVideoDir();
|
|
142
|
+
return Promise.resolve(videoDir);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
catch { }
|
|
146
|
+
}
|
|
126
147
|
const settings = (0, settings_1.useUserSettings)();
|
|
127
148
|
const videoDir = settings.get('videos.directory', null);
|
|
128
149
|
if (!videoDir) {
|
|
@@ -169,12 +190,12 @@ let RouteDownloadService = (() => {
|
|
|
169
190
|
try {
|
|
170
191
|
const { file, url, error } = await this.getDownloadFileName(route, targetDir);
|
|
171
192
|
urlLog = url;
|
|
172
|
-
if (!file || error) {
|
|
193
|
+
if (!file || error || !url) {
|
|
173
194
|
observer.emit('error', new Error(error ?? 'download not supported'));
|
|
174
195
|
return;
|
|
175
196
|
}
|
|
176
197
|
const { id, title } = route.description;
|
|
177
|
-
const { downloadManager } =
|
|
198
|
+
const { downloadManager } = this.getBindings();
|
|
178
199
|
if (!downloadManager) {
|
|
179
200
|
observer.emit('error', new Error('download not supported'));
|
|
180
201
|
return;
|
|
@@ -207,20 +228,30 @@ let RouteDownloadService = (() => {
|
|
|
207
228
|
}
|
|
208
229
|
}
|
|
209
230
|
async restoreFromRepo(route) {
|
|
231
|
+
if (!route?.description?.id)
|
|
232
|
+
return;
|
|
210
233
|
await this.getRoutesApi().loadDetails([{ route, added: false }], true);
|
|
211
234
|
await (0, utils_1.waitNextTick)();
|
|
212
235
|
const descr = this.getRepo().getDescription(route.description.id);
|
|
213
236
|
return descr?.downloadUrl || descr?.videoUrl;
|
|
214
237
|
}
|
|
215
238
|
onDownloadProgress(id, observer, pct, speed, downloaded) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
239
|
+
if (!this.isMobile()) {
|
|
240
|
+
const prev = this.progressLogTs[id] || 0;
|
|
241
|
+
const ts = Date.now();
|
|
242
|
+
if (ts - prev > 5000) {
|
|
243
|
+
this.logEvent({ message: 'download progress', pctComplete: pct, downloaded, downloadSpeed: speed });
|
|
244
|
+
this.progressLogTs[id] = ts;
|
|
245
|
+
}
|
|
221
246
|
}
|
|
222
247
|
observer.emit('progress', Number(pct));
|
|
223
248
|
}
|
|
249
|
+
isMobile() {
|
|
250
|
+
return this.getBindings()?.appInfo?.getChannel() === 'mobile';
|
|
251
|
+
}
|
|
252
|
+
getBindings() {
|
|
253
|
+
return (0, api_1.getBindings)();
|
|
254
|
+
}
|
|
224
255
|
};
|
|
225
256
|
return RouteDownloadService = _classThis;
|
|
226
257
|
})();
|
|
@@ -98,7 +98,7 @@ let RouteCard = (() => {
|
|
|
98
98
|
deleteObserver;
|
|
99
99
|
ready;
|
|
100
100
|
logger;
|
|
101
|
-
cntActive;
|
|
101
|
+
cntActive = 0;
|
|
102
102
|
constructor(route, props) {
|
|
103
103
|
super();
|
|
104
104
|
const { list } = props || {};
|
|
@@ -114,9 +114,11 @@ let RouteCard = (() => {
|
|
|
114
114
|
}
|
|
115
115
|
canStart(status) {
|
|
116
116
|
try {
|
|
117
|
-
const { isOnline } = status;
|
|
118
|
-
const route = this.route
|
|
119
|
-
if (!route
|
|
117
|
+
const { isOnline } = status ?? {};
|
|
118
|
+
const route = this.route?.description;
|
|
119
|
+
if (!route)
|
|
120
|
+
return false;
|
|
121
|
+
if (!route.hasVideo || !(route.isLocal || route.isDownloaded) || route.videoUrl?.startsWith('http'))
|
|
120
122
|
return isOnline;
|
|
121
123
|
if (route.requiresDownload)
|
|
122
124
|
return isOnline;
|
|
@@ -285,6 +287,7 @@ let RouteCard = (() => {
|
|
|
285
287
|
}
|
|
286
288
|
catch (err) {
|
|
287
289
|
this.logError(err, 'getDisplayProperties');
|
|
290
|
+
return null;
|
|
288
291
|
}
|
|
289
292
|
}
|
|
290
293
|
getMarkers(settings) {
|
|
@@ -294,7 +297,8 @@ let RouteCard = (() => {
|
|
|
294
297
|
this.adjustStartPosAvi(startSettings);
|
|
295
298
|
const startDistance = startSettings.startPos ?? 0;
|
|
296
299
|
const startPos = (0, route_1.getPosition)(this.route, { distance: startDistance, nearest: true });
|
|
297
|
-
|
|
300
|
+
if (startPos)
|
|
301
|
+
markers.push(startPos);
|
|
298
302
|
}
|
|
299
303
|
catch (err) {
|
|
300
304
|
this.logError(err, 'getMarkers');
|
|
@@ -308,7 +312,9 @@ let RouteCard = (() => {
|
|
|
308
312
|
if (startPos !== undefined && startPos !== null) {
|
|
309
313
|
const startDistance = C(startPos.value, 'distance', { from: startPos.unit, to: 'm' });
|
|
310
314
|
const position = (0, route_1.getPosition)(this.route, { distance: startDistance, nearest: true });
|
|
311
|
-
|
|
315
|
+
if (position) {
|
|
316
|
+
markers.push(position);
|
|
317
|
+
}
|
|
312
318
|
}
|
|
313
319
|
}
|
|
314
320
|
catch (err) {
|
|
@@ -517,7 +523,7 @@ let RouteCard = (() => {
|
|
|
517
523
|
deleted = false;
|
|
518
524
|
}
|
|
519
525
|
finally {
|
|
520
|
-
this.deleteObserver
|
|
526
|
+
this.deleteObserver?.emit('done', deleted);
|
|
521
527
|
(0, utils_1.waitNextTick)().then(() => {
|
|
522
528
|
delete this.deleteObserver;
|
|
523
529
|
this.emitUpdate();
|
|
@@ -609,7 +615,7 @@ let RouteCard = (() => {
|
|
|
609
615
|
return v;
|
|
610
616
|
};
|
|
611
617
|
(0, service_1.getRouteList)().logEvent({ message: 'download started', route: routeDescr?.title, videoUrl: lv(routeDescr?.videoUrl), downloadUrl: lv(routeDescr?.downloadUrl) });
|
|
612
|
-
const dl =
|
|
618
|
+
const dl = this.getRouteDownload();
|
|
613
619
|
this.downloadObserver = dl.download(this.route);
|
|
614
620
|
this.downloadObserver
|
|
615
621
|
.on('done', this.onDownloadCompleted.bind(this))
|
|
@@ -623,19 +629,41 @@ let RouteCard = (() => {
|
|
|
623
629
|
}
|
|
624
630
|
stopDownload(immediate = false) {
|
|
625
631
|
try {
|
|
626
|
-
(0, service_1.getRouteList)().logEvent({ message: 'download stopped', route: this.route?.description?.title });
|
|
627
|
-
this.
|
|
632
|
+
(0, service_1.getRouteList)().logEvent({ message: 'download stopped', route: this.route?.description?.title, });
|
|
633
|
+
this.getRouteDownload().stopDownload(this.route);
|
|
628
634
|
if (immediate) {
|
|
629
635
|
delete this.downloadObserver;
|
|
630
636
|
}
|
|
631
637
|
else {
|
|
632
|
-
(0,
|
|
638
|
+
(0, sleep_1.sleep)(5).then(() => { delete this.downloadObserver; });
|
|
633
639
|
}
|
|
634
640
|
}
|
|
635
641
|
catch (err) {
|
|
636
642
|
this.logError(err, 'stopDownload');
|
|
637
643
|
}
|
|
638
644
|
}
|
|
645
|
+
async deleteDownload() {
|
|
646
|
+
try {
|
|
647
|
+
const descr = this.getRouteDescription();
|
|
648
|
+
const videoUrl = descr.videoUrl;
|
|
649
|
+
if (videoUrl?.startsWith('video:///') || videoUrl?.startsWith('file:///')) {
|
|
650
|
+
const filePath = videoUrl.startsWith('video:///')
|
|
651
|
+
? videoUrl.replace('video:///', '')
|
|
652
|
+
: videoUrl.replace('file:///', '');
|
|
653
|
+
const fs = (0, api_1.getBindings)().fs;
|
|
654
|
+
if (fs) {
|
|
655
|
+
const exists = await this.fileExists(filePath);
|
|
656
|
+
if (exists) {
|
|
657
|
+
await fs.unlink(filePath);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
await this.resetDownload();
|
|
662
|
+
}
|
|
663
|
+
catch (err) {
|
|
664
|
+
this.logError(err, 'deleteDownload');
|
|
665
|
+
}
|
|
666
|
+
}
|
|
639
667
|
async onDownloadCompleted(url) {
|
|
640
668
|
try {
|
|
641
669
|
(0, service_1.getRouteList)().logEvent({ message: 'download completed', route: this.route?.description?.title });
|
|
@@ -647,7 +675,7 @@ let RouteCard = (() => {
|
|
|
647
675
|
this.route.details.video.file = undefined;
|
|
648
676
|
this.route.details.video.url = url;
|
|
649
677
|
this.updateRoute(this.route);
|
|
650
|
-
(0,
|
|
678
|
+
(0, sleep_1.sleep)(5).then(() => {
|
|
651
679
|
this.downloadObserver.reset();
|
|
652
680
|
delete this.downloadObserver;
|
|
653
681
|
});
|
|
@@ -677,7 +705,7 @@ let RouteCard = (() => {
|
|
|
677
705
|
try {
|
|
678
706
|
(0, service_1.getRouteList)().logEvent({ message: 'download failed', reason: err.message, route: this.route?.description?.title });
|
|
679
707
|
this.downloadObserver.stop();
|
|
680
|
-
(0,
|
|
708
|
+
(0, sleep_1.sleep)(5).then(() => {
|
|
681
709
|
this.downloadObserver.reset();
|
|
682
710
|
delete this.downloadObserver;
|
|
683
711
|
});
|
|
@@ -882,6 +910,9 @@ let RouteCard = (() => {
|
|
|
882
910
|
getOnlineStatusMonitoring() {
|
|
883
911
|
return (0, monitoring_1.useOnlineStatusMonitoring)();
|
|
884
912
|
}
|
|
913
|
+
getRouteDownload() {
|
|
914
|
+
return (0, service_2.useRouteDownload)();
|
|
915
|
+
}
|
|
885
916
|
};
|
|
886
917
|
})();
|
|
887
918
|
exports.RouteCard = RouteCard;
|
|
@@ -84,6 +84,7 @@ let RoutesPageService = (() => {
|
|
|
84
84
|
importProps;
|
|
85
85
|
downloadCache;
|
|
86
86
|
downloadHandlers;
|
|
87
|
+
downloadObserver = new types_1.Observer();
|
|
87
88
|
constructor() {
|
|
88
89
|
super('RoutesPage');
|
|
89
90
|
this.downloadCache = new Map();
|
|
@@ -118,11 +119,11 @@ let RoutesPageService = (() => {
|
|
|
118
119
|
catch (err) {
|
|
119
120
|
this.logError(err, 'openPage');
|
|
120
121
|
}
|
|
121
|
-
return this.getPageObserver();
|
|
122
122
|
}
|
|
123
123
|
catch (err) {
|
|
124
124
|
this.logError(err, 'openPage');
|
|
125
125
|
}
|
|
126
|
+
return this.getPageObserver();
|
|
126
127
|
}
|
|
127
128
|
closePage() {
|
|
128
129
|
try {
|
|
@@ -130,13 +131,14 @@ let RoutesPageService = (() => {
|
|
|
130
131
|
this.logEvent({ message: 'page closed', page: 'Routes' });
|
|
131
132
|
this.stopEventListener();
|
|
132
133
|
this.downloadCache.clear();
|
|
134
|
+
this.downloadObserver.stop();
|
|
133
135
|
super.closePage();
|
|
134
136
|
}
|
|
135
137
|
catch (err) {
|
|
136
138
|
this.logError(err, 'closePage');
|
|
137
139
|
}
|
|
138
140
|
}
|
|
139
|
-
pausePage() {
|
|
141
|
+
async pausePage() {
|
|
140
142
|
try {
|
|
141
143
|
this.stopEventListener();
|
|
142
144
|
return super.pausePage();
|
|
@@ -145,7 +147,7 @@ let RoutesPageService = (() => {
|
|
|
145
147
|
this.logError(err, 'pausePage');
|
|
146
148
|
}
|
|
147
149
|
}
|
|
148
|
-
resumePage() {
|
|
150
|
+
async resumePage() {
|
|
149
151
|
try {
|
|
150
152
|
this.startEventListener();
|
|
151
153
|
return super.resumePage();
|
|
@@ -164,7 +166,7 @@ let RoutesPageService = (() => {
|
|
|
164
166
|
if (loading) {
|
|
165
167
|
return { loading, synchronizing: false, routes: [], displayType,
|
|
166
168
|
filters, filterVisible,
|
|
167
|
-
|
|
169
|
+
downloadObserver: this.downloadObserver,
|
|
168
170
|
showImportDialog: false };
|
|
169
171
|
}
|
|
170
172
|
else {
|
|
@@ -176,7 +178,7 @@ let RoutesPageService = (() => {
|
|
|
176
178
|
return { loading, synchronizing, routes, displayType,
|
|
177
179
|
filters, filterVisible, filterOptions,
|
|
178
180
|
detailRouteId,
|
|
179
|
-
|
|
181
|
+
downloadObserver: this.downloadObserver,
|
|
180
182
|
showImportDialog: this.showImportDialog };
|
|
181
183
|
}
|
|
182
184
|
}
|
|
@@ -235,7 +237,8 @@ let RoutesPageService = (() => {
|
|
|
235
237
|
try {
|
|
236
238
|
const service = this.getRouteList();
|
|
237
239
|
const pairing = this.getDevicePairing();
|
|
238
|
-
const
|
|
240
|
+
const setttings = service.getStartSettings() ?? {};
|
|
241
|
+
const { id, title, videoUrl } = setttings;
|
|
239
242
|
this.logEvent({ message: 'Attempting to start a ride', id, title, videoUrl, readyToStart: pairing.isReadyToStart(), });
|
|
240
243
|
service.close();
|
|
241
244
|
const next = pairing.isReadyToStart() ? '/rideDeviceOK' : '/pairingStart';
|
|
@@ -255,11 +258,11 @@ let RoutesPageService = (() => {
|
|
|
255
258
|
imports.forEach((i) => {
|
|
256
259
|
this.prepareSingleImport(i);
|
|
257
260
|
});
|
|
258
|
-
return this.importObserver;
|
|
259
261
|
}
|
|
260
262
|
catch (err) {
|
|
261
263
|
this.logError(err, 'start');
|
|
262
264
|
}
|
|
265
|
+
return this.importObserver;
|
|
263
266
|
}
|
|
264
267
|
onImportClosed() {
|
|
265
268
|
try {
|
|
@@ -276,12 +279,7 @@ let RoutesPageService = (() => {
|
|
|
276
279
|
}
|
|
277
280
|
}
|
|
278
281
|
getImportDisplayProps() {
|
|
279
|
-
|
|
280
|
-
return this.importProps;
|
|
281
|
-
}
|
|
282
|
-
catch (err) {
|
|
283
|
-
this.logError(err, 'getImportDisplayProps');
|
|
284
|
-
}
|
|
282
|
+
return this.importProps;
|
|
285
283
|
}
|
|
286
284
|
getDownloadDisplayProps() {
|
|
287
285
|
return Array.from(this.downloadCache.values());
|
|
@@ -290,7 +288,7 @@ let RoutesPageService = (() => {
|
|
|
290
288
|
this.getPageObserver()?.emit('page-update');
|
|
291
289
|
}
|
|
292
290
|
getRoutesDisplayProps() {
|
|
293
|
-
const { routes } = this.serviceState ?? {};
|
|
291
|
+
const { routes = [] } = this.serviceState ?? {};
|
|
294
292
|
const getRouteProps = (routeProps) => {
|
|
295
293
|
return {
|
|
296
294
|
...routeProps,
|
|
@@ -302,7 +300,7 @@ let RoutesPageService = (() => {
|
|
|
302
300
|
return this.getRouteList().getFilters();
|
|
303
301
|
}
|
|
304
302
|
startEventListener() {
|
|
305
|
-
const { observer } = this.serviceState;
|
|
303
|
+
const { observer } = this.serviceState ?? {};
|
|
306
304
|
if (!observer)
|
|
307
305
|
return;
|
|
308
306
|
observer.on('updated', this.updateStateHandler);
|
|
@@ -314,7 +312,7 @@ let RoutesPageService = (() => {
|
|
|
314
312
|
this.getRouteDownload().on('download-started', this.downloadStartedHandler);
|
|
315
313
|
}
|
|
316
314
|
stopEventListener(final) {
|
|
317
|
-
const { observer } = this.serviceState;
|
|
315
|
+
const { observer } = this.serviceState ?? {};
|
|
318
316
|
if (!observer)
|
|
319
317
|
return;
|
|
320
318
|
if (final)
|
|
@@ -328,29 +326,37 @@ let RoutesPageService = (() => {
|
|
|
328
326
|
this.getRouteDownload().off('download-started', this.downloadStartedHandler);
|
|
329
327
|
}
|
|
330
328
|
subscribeToActiveDownload(route, observer) {
|
|
331
|
-
const routeId = route
|
|
332
|
-
const title = route
|
|
329
|
+
const routeId = route?.description?.id;
|
|
330
|
+
const title = route?.description?.title ?? '';
|
|
331
|
+
if (!routeId)
|
|
332
|
+
return;
|
|
333
333
|
if (this.downloadHandlers.has(routeId))
|
|
334
334
|
return;
|
|
335
335
|
const onProgress = (pct) => {
|
|
336
336
|
this.downloadCache.set(routeId, { routeId, title, status: 'downloading', pct });
|
|
337
|
-
this.
|
|
337
|
+
this.emitDownloadUpdate();
|
|
338
338
|
};
|
|
339
339
|
const onDone = () => {
|
|
340
340
|
this.downloadCache.set(routeId, { routeId, title, status: 'done' });
|
|
341
341
|
this.downloadHandlers.delete(routeId);
|
|
342
|
-
this.
|
|
342
|
+
this.emitDownloadUpdate();
|
|
343
343
|
};
|
|
344
344
|
const onError = () => {
|
|
345
345
|
this.downloadCache.set(routeId, { routeId, title, status: 'failed' });
|
|
346
346
|
this.downloadHandlers.delete(routeId);
|
|
347
|
-
this.
|
|
347
|
+
this.emitDownloadUpdate();
|
|
348
|
+
};
|
|
349
|
+
const onStopped = () => {
|
|
350
|
+
this.downloadCache.delete(routeId);
|
|
351
|
+
this.downloadHandlers.delete(routeId);
|
|
352
|
+
this.emitDownloadUpdate();
|
|
348
353
|
};
|
|
349
|
-
this.downloadHandlers.set(routeId, { onProgress, onDone, onError });
|
|
354
|
+
this.downloadHandlers.set(routeId, { onProgress, onDone, onError, onStopped });
|
|
350
355
|
this.downloadCache.set(routeId, { routeId, title, status: 'downloading' });
|
|
351
356
|
observer.on('progress', onProgress);
|
|
352
357
|
observer.on('done', onDone);
|
|
353
358
|
observer.on('error', onError);
|
|
359
|
+
observer.on('stopped', onStopped);
|
|
354
360
|
this.updatePageDisplay();
|
|
355
361
|
}
|
|
356
362
|
subscribeAllActiveDownloads() {
|
|
@@ -361,12 +367,15 @@ let RoutesPageService = (() => {
|
|
|
361
367
|
unsubscribeAllActiveDownloads() {
|
|
362
368
|
const active = this.getRouteDownload().getActiveDownloads();
|
|
363
369
|
for (const { route, observer } of active) {
|
|
364
|
-
const routeId = route
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
+
const routeId = route?.description?.id;
|
|
371
|
+
if (routeId) {
|
|
372
|
+
const handlers = this.downloadHandlers.get(routeId);
|
|
373
|
+
if (handlers) {
|
|
374
|
+
observer.off('progress', handlers.onProgress);
|
|
375
|
+
observer.off('done', handlers.onDone);
|
|
376
|
+
observer.off('error', handlers.onError);
|
|
377
|
+
observer.off('stopped', handlers.onStopped);
|
|
378
|
+
}
|
|
370
379
|
}
|
|
371
380
|
}
|
|
372
381
|
this.downloadHandlers.clear();
|
|
@@ -374,6 +383,13 @@ let RoutesPageService = (() => {
|
|
|
374
383
|
downloadStartedHandler = (route, observer) => {
|
|
375
384
|
this.subscribeToActiveDownload(route, observer);
|
|
376
385
|
};
|
|
386
|
+
emitDownloadUpdate() {
|
|
387
|
+
this.downloadObserver.emit('download-update', {
|
|
388
|
+
rows: this.getDownloadDisplayProps(),
|
|
389
|
+
count: Array.from(this.downloadCache.values())
|
|
390
|
+
.filter(r => r.status === 'downloading').length
|
|
391
|
+
});
|
|
392
|
+
}
|
|
377
393
|
onSycncStart() {
|
|
378
394
|
this.updatePageDisplay();
|
|
379
395
|
}
|
|
@@ -384,8 +400,7 @@ let RoutesPageService = (() => {
|
|
|
384
400
|
this.updatePageDisplay();
|
|
385
401
|
}
|
|
386
402
|
onDialogClosed() {
|
|
387
|
-
|
|
388
|
-
this.detailRouteId = null;
|
|
403
|
+
this.detailRouteId = undefined;
|
|
389
404
|
this.updatePageDisplay();
|
|
390
405
|
}
|
|
391
406
|
prepareSingleImport(file) {
|
|
@@ -395,6 +410,7 @@ let RoutesPageService = (() => {
|
|
|
395
410
|
status: 'idle',
|
|
396
411
|
fileName: file.name
|
|
397
412
|
};
|
|
413
|
+
this.importProps = this.importProps ?? [];
|
|
398
414
|
this.importProps.push(props);
|
|
399
415
|
observer.once('success', () => {
|
|
400
416
|
props.status = 'success';
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
2
|
+
var useValue = arguments.length > 2;
|
|
3
|
+
for (var i = 0; i < initializers.length; i++) {
|
|
4
|
+
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
5
|
+
}
|
|
6
|
+
return useValue ? value : void 0;
|
|
7
|
+
};
|
|
1
8
|
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
|
|
2
9
|
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
|
|
3
10
|
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
|
|
@@ -25,15 +32,8 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
|
|
|
25
32
|
if (target) Object.defineProperty(target, contextIn.name, descriptor);
|
|
26
33
|
done = true;
|
|
27
34
|
};
|
|
28
|
-
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
|
|
29
|
-
var useValue = arguments.length > 2;
|
|
30
|
-
for (var i = 0; i < initializers.length; i++) {
|
|
31
|
-
value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
|
|
32
|
-
}
|
|
33
|
-
return useValue ? value : void 0;
|
|
34
|
-
};
|
|
35
35
|
import { getBindings } from "../../api";
|
|
36
|
-
import { Singleton } from "../../base/decorators";
|
|
36
|
+
import { Injectable, Singleton } from "../../base/decorators";
|
|
37
37
|
import { IncyclistService } from "../../base/service";
|
|
38
38
|
import { useUserSettings } from "../../settings";
|
|
39
39
|
import { waitNextTick } from "../../utils";
|
|
@@ -46,16 +46,20 @@ let RouteDownloadService = (() => {
|
|
|
46
46
|
let _classExtraInitializers = [];
|
|
47
47
|
let _classThis;
|
|
48
48
|
let _classSuper = IncyclistService;
|
|
49
|
+
let _instanceExtraInitializers = [];
|
|
50
|
+
let _getBindings_decorators;
|
|
49
51
|
var RouteDownloadService = class extends _classSuper {
|
|
50
52
|
static { _classThis = this; }
|
|
51
53
|
static {
|
|
52
54
|
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
55
|
+
_getBindings_decorators = [Injectable];
|
|
56
|
+
__esDecorate(this, null, _getBindings_decorators, { kind: "method", name: "getBindings", static: false, private: false, access: { has: obj => "getBindings" in obj, get: obj => obj.getBindings }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
53
57
|
__esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
|
|
54
58
|
RouteDownloadService = _classThis = _classDescriptor.value;
|
|
55
59
|
if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
56
60
|
__runInitializers(_classThis, _classExtraInitializers);
|
|
57
61
|
}
|
|
58
|
-
downloads;
|
|
62
|
+
downloads = __runInitializers(this, _instanceExtraInitializers);
|
|
59
63
|
progressLogTs;
|
|
60
64
|
constructor() {
|
|
61
65
|
super('Routes');
|
|
@@ -94,7 +98,7 @@ let RouteDownloadService = (() => {
|
|
|
94
98
|
if (file) {
|
|
95
99
|
try {
|
|
96
100
|
const fs = getBindings().fs;
|
|
97
|
-
fs
|
|
101
|
+
fs?.unlink(file);
|
|
98
102
|
}
|
|
99
103
|
catch (err) {
|
|
100
104
|
this.logError(err, 'deleteIncompleteFile');
|
|
@@ -108,18 +112,35 @@ let RouteDownloadService = (() => {
|
|
|
108
112
|
async _download(route) {
|
|
109
113
|
await waitNextTick();
|
|
110
114
|
const observer = this.getObserver(route);
|
|
115
|
+
if (!observer)
|
|
116
|
+
return;
|
|
111
117
|
try {
|
|
112
118
|
const videoDir = await this.waitForVideoDir(observer);
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
119
|
+
if (videoDir) {
|
|
120
|
+
observer.once('stopped', () => {
|
|
121
|
+
this.deleteIncompleteFile(route, videoDir);
|
|
122
|
+
});
|
|
123
|
+
this.downloadRoute(route, videoDir, observer);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
this.logEvent({ message: 'could not start download', reason: 'video dir not specified' });
|
|
127
|
+
}
|
|
117
128
|
}
|
|
118
129
|
catch (err) {
|
|
119
130
|
observer.emit('error', err);
|
|
120
131
|
}
|
|
121
132
|
}
|
|
122
133
|
waitForVideoDir(observer) {
|
|
134
|
+
if (this.isMobile()) {
|
|
135
|
+
try {
|
|
136
|
+
const getVideoDir = this.getBindings()?.downloadManager?.getVideoDir;
|
|
137
|
+
if (getVideoDir) {
|
|
138
|
+
const videoDir = getVideoDir();
|
|
139
|
+
return Promise.resolve(videoDir);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch { }
|
|
143
|
+
}
|
|
123
144
|
const settings = useUserSettings();
|
|
124
145
|
const videoDir = settings.get('videos.directory', null);
|
|
125
146
|
if (!videoDir) {
|
|
@@ -166,12 +187,12 @@ let RouteDownloadService = (() => {
|
|
|
166
187
|
try {
|
|
167
188
|
const { file, url, error } = await this.getDownloadFileName(route, targetDir);
|
|
168
189
|
urlLog = url;
|
|
169
|
-
if (!file || error) {
|
|
190
|
+
if (!file || error || !url) {
|
|
170
191
|
observer.emit('error', new Error(error ?? 'download not supported'));
|
|
171
192
|
return;
|
|
172
193
|
}
|
|
173
194
|
const { id, title } = route.description;
|
|
174
|
-
const { downloadManager } = getBindings();
|
|
195
|
+
const { downloadManager } = this.getBindings();
|
|
175
196
|
if (!downloadManager) {
|
|
176
197
|
observer.emit('error', new Error('download not supported'));
|
|
177
198
|
return;
|
|
@@ -204,20 +225,30 @@ let RouteDownloadService = (() => {
|
|
|
204
225
|
}
|
|
205
226
|
}
|
|
206
227
|
async restoreFromRepo(route) {
|
|
228
|
+
if (!route?.description?.id)
|
|
229
|
+
return;
|
|
207
230
|
await this.getRoutesApi().loadDetails([{ route, added: false }], true);
|
|
208
231
|
await waitNextTick();
|
|
209
232
|
const descr = this.getRepo().getDescription(route.description.id);
|
|
210
233
|
return descr?.downloadUrl || descr?.videoUrl;
|
|
211
234
|
}
|
|
212
235
|
onDownloadProgress(id, observer, pct, speed, downloaded) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
236
|
+
if (!this.isMobile()) {
|
|
237
|
+
const prev = this.progressLogTs[id] || 0;
|
|
238
|
+
const ts = Date.now();
|
|
239
|
+
if (ts - prev > 5000) {
|
|
240
|
+
this.logEvent({ message: 'download progress', pctComplete: pct, downloaded, downloadSpeed: speed });
|
|
241
|
+
this.progressLogTs[id] = ts;
|
|
242
|
+
}
|
|
218
243
|
}
|
|
219
244
|
observer.emit('progress', Number(pct));
|
|
220
245
|
}
|
|
246
|
+
isMobile() {
|
|
247
|
+
return this.getBindings()?.appInfo?.getChannel() === 'mobile';
|
|
248
|
+
}
|
|
249
|
+
getBindings() {
|
|
250
|
+
return getBindings();
|
|
251
|
+
}
|
|
221
252
|
};
|
|
222
253
|
return RouteDownloadService = _classThis;
|
|
223
254
|
})();
|
|
@@ -41,7 +41,7 @@ import { getRouteList, useRouteList } from "../service";
|
|
|
41
41
|
import { RoutesDbLoader } from "../loaders/db";
|
|
42
42
|
import { valid } from "../../../utils/valid";
|
|
43
43
|
import { waitNextTick } from "../../../utils";
|
|
44
|
-
import {
|
|
44
|
+
import { useRouteDownload } from "../../download/service";
|
|
45
45
|
import { EventLogger } from "gd-eventlog";
|
|
46
46
|
import { checkIsLoop, getNextVideoId, getPosition, updateSlopes } from "../../base/utils/route";
|
|
47
47
|
import { getWorkoutList } from "../../../workouts";
|
|
@@ -95,7 +95,7 @@ let RouteCard = (() => {
|
|
|
95
95
|
deleteObserver;
|
|
96
96
|
ready;
|
|
97
97
|
logger;
|
|
98
|
-
cntActive;
|
|
98
|
+
cntActive = 0;
|
|
99
99
|
constructor(route, props) {
|
|
100
100
|
super();
|
|
101
101
|
const { list } = props || {};
|
|
@@ -111,9 +111,11 @@ let RouteCard = (() => {
|
|
|
111
111
|
}
|
|
112
112
|
canStart(status) {
|
|
113
113
|
try {
|
|
114
|
-
const { isOnline } = status;
|
|
115
|
-
const route = this.route
|
|
116
|
-
if (!route
|
|
114
|
+
const { isOnline } = status ?? {};
|
|
115
|
+
const route = this.route?.description;
|
|
116
|
+
if (!route)
|
|
117
|
+
return false;
|
|
118
|
+
if (!route.hasVideo || !(route.isLocal || route.isDownloaded) || route.videoUrl?.startsWith('http'))
|
|
117
119
|
return isOnline;
|
|
118
120
|
if (route.requiresDownload)
|
|
119
121
|
return isOnline;
|
|
@@ -282,6 +284,7 @@ let RouteCard = (() => {
|
|
|
282
284
|
}
|
|
283
285
|
catch (err) {
|
|
284
286
|
this.logError(err, 'getDisplayProperties');
|
|
287
|
+
return null;
|
|
285
288
|
}
|
|
286
289
|
}
|
|
287
290
|
getMarkers(settings) {
|
|
@@ -291,7 +294,8 @@ let RouteCard = (() => {
|
|
|
291
294
|
this.adjustStartPosAvi(startSettings);
|
|
292
295
|
const startDistance = startSettings.startPos ?? 0;
|
|
293
296
|
const startPos = getPosition(this.route, { distance: startDistance, nearest: true });
|
|
294
|
-
|
|
297
|
+
if (startPos)
|
|
298
|
+
markers.push(startPos);
|
|
295
299
|
}
|
|
296
300
|
catch (err) {
|
|
297
301
|
this.logError(err, 'getMarkers');
|
|
@@ -305,7 +309,9 @@ let RouteCard = (() => {
|
|
|
305
309
|
if (startPos !== undefined && startPos !== null) {
|
|
306
310
|
const startDistance = C(startPos.value, 'distance', { from: startPos.unit, to: 'm' });
|
|
307
311
|
const position = getPosition(this.route, { distance: startDistance, nearest: true });
|
|
308
|
-
|
|
312
|
+
if (position) {
|
|
313
|
+
markers.push(position);
|
|
314
|
+
}
|
|
309
315
|
}
|
|
310
316
|
}
|
|
311
317
|
catch (err) {
|
|
@@ -514,7 +520,7 @@ let RouteCard = (() => {
|
|
|
514
520
|
deleted = false;
|
|
515
521
|
}
|
|
516
522
|
finally {
|
|
517
|
-
this.deleteObserver
|
|
523
|
+
this.deleteObserver?.emit('done', deleted);
|
|
518
524
|
waitNextTick().then(() => {
|
|
519
525
|
delete this.deleteObserver;
|
|
520
526
|
this.emitUpdate();
|
|
@@ -606,7 +612,7 @@ let RouteCard = (() => {
|
|
|
606
612
|
return v;
|
|
607
613
|
};
|
|
608
614
|
getRouteList().logEvent({ message: 'download started', route: routeDescr?.title, videoUrl: lv(routeDescr?.videoUrl), downloadUrl: lv(routeDescr?.downloadUrl) });
|
|
609
|
-
const dl =
|
|
615
|
+
const dl = this.getRouteDownload();
|
|
610
616
|
this.downloadObserver = dl.download(this.route);
|
|
611
617
|
this.downloadObserver
|
|
612
618
|
.on('done', this.onDownloadCompleted.bind(this))
|
|
@@ -620,19 +626,41 @@ let RouteCard = (() => {
|
|
|
620
626
|
}
|
|
621
627
|
stopDownload(immediate = false) {
|
|
622
628
|
try {
|
|
623
|
-
getRouteList().logEvent({ message: 'download stopped', route: this.route?.description?.title });
|
|
624
|
-
this.
|
|
629
|
+
getRouteList().logEvent({ message: 'download stopped', route: this.route?.description?.title, });
|
|
630
|
+
this.getRouteDownload().stopDownload(this.route);
|
|
625
631
|
if (immediate) {
|
|
626
632
|
delete this.downloadObserver;
|
|
627
633
|
}
|
|
628
634
|
else {
|
|
629
|
-
|
|
635
|
+
sleep(5).then(() => { delete this.downloadObserver; });
|
|
630
636
|
}
|
|
631
637
|
}
|
|
632
638
|
catch (err) {
|
|
633
639
|
this.logError(err, 'stopDownload');
|
|
634
640
|
}
|
|
635
641
|
}
|
|
642
|
+
async deleteDownload() {
|
|
643
|
+
try {
|
|
644
|
+
const descr = this.getRouteDescription();
|
|
645
|
+
const videoUrl = descr.videoUrl;
|
|
646
|
+
if (videoUrl?.startsWith('video:///') || videoUrl?.startsWith('file:///')) {
|
|
647
|
+
const filePath = videoUrl.startsWith('video:///')
|
|
648
|
+
? videoUrl.replace('video:///', '')
|
|
649
|
+
: videoUrl.replace('file:///', '');
|
|
650
|
+
const fs = getBindings().fs;
|
|
651
|
+
if (fs) {
|
|
652
|
+
const exists = await this.fileExists(filePath);
|
|
653
|
+
if (exists) {
|
|
654
|
+
await fs.unlink(filePath);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
await this.resetDownload();
|
|
659
|
+
}
|
|
660
|
+
catch (err) {
|
|
661
|
+
this.logError(err, 'deleteDownload');
|
|
662
|
+
}
|
|
663
|
+
}
|
|
636
664
|
async onDownloadCompleted(url) {
|
|
637
665
|
try {
|
|
638
666
|
getRouteList().logEvent({ message: 'download completed', route: this.route?.description?.title });
|
|
@@ -644,7 +672,7 @@ let RouteCard = (() => {
|
|
|
644
672
|
this.route.details.video.file = undefined;
|
|
645
673
|
this.route.details.video.url = url;
|
|
646
674
|
this.updateRoute(this.route);
|
|
647
|
-
|
|
675
|
+
sleep(5).then(() => {
|
|
648
676
|
this.downloadObserver.reset();
|
|
649
677
|
delete this.downloadObserver;
|
|
650
678
|
});
|
|
@@ -674,7 +702,7 @@ let RouteCard = (() => {
|
|
|
674
702
|
try {
|
|
675
703
|
getRouteList().logEvent({ message: 'download failed', reason: err.message, route: this.route?.description?.title });
|
|
676
704
|
this.downloadObserver.stop();
|
|
677
|
-
|
|
705
|
+
sleep(5).then(() => {
|
|
678
706
|
this.downloadObserver.reset();
|
|
679
707
|
delete this.downloadObserver;
|
|
680
708
|
});
|
|
@@ -879,6 +907,9 @@ let RouteCard = (() => {
|
|
|
879
907
|
getOnlineStatusMonitoring() {
|
|
880
908
|
return useOnlineStatusMonitoring();
|
|
881
909
|
}
|
|
910
|
+
getRouteDownload() {
|
|
911
|
+
return useRouteDownload();
|
|
912
|
+
}
|
|
882
913
|
};
|
|
883
914
|
})();
|
|
884
915
|
export { RouteCard };
|
|
@@ -81,6 +81,7 @@ let RoutesPageService = (() => {
|
|
|
81
81
|
importProps;
|
|
82
82
|
downloadCache;
|
|
83
83
|
downloadHandlers;
|
|
84
|
+
downloadObserver = new Observer();
|
|
84
85
|
constructor() {
|
|
85
86
|
super('RoutesPage');
|
|
86
87
|
this.downloadCache = new Map();
|
|
@@ -115,11 +116,11 @@ let RoutesPageService = (() => {
|
|
|
115
116
|
catch (err) {
|
|
116
117
|
this.logError(err, 'openPage');
|
|
117
118
|
}
|
|
118
|
-
return this.getPageObserver();
|
|
119
119
|
}
|
|
120
120
|
catch (err) {
|
|
121
121
|
this.logError(err, 'openPage');
|
|
122
122
|
}
|
|
123
|
+
return this.getPageObserver();
|
|
123
124
|
}
|
|
124
125
|
closePage() {
|
|
125
126
|
try {
|
|
@@ -127,13 +128,14 @@ let RoutesPageService = (() => {
|
|
|
127
128
|
this.logEvent({ message: 'page closed', page: 'Routes' });
|
|
128
129
|
this.stopEventListener();
|
|
129
130
|
this.downloadCache.clear();
|
|
131
|
+
this.downloadObserver.stop();
|
|
130
132
|
super.closePage();
|
|
131
133
|
}
|
|
132
134
|
catch (err) {
|
|
133
135
|
this.logError(err, 'closePage');
|
|
134
136
|
}
|
|
135
137
|
}
|
|
136
|
-
pausePage() {
|
|
138
|
+
async pausePage() {
|
|
137
139
|
try {
|
|
138
140
|
this.stopEventListener();
|
|
139
141
|
return super.pausePage();
|
|
@@ -142,7 +144,7 @@ let RoutesPageService = (() => {
|
|
|
142
144
|
this.logError(err, 'pausePage');
|
|
143
145
|
}
|
|
144
146
|
}
|
|
145
|
-
resumePage() {
|
|
147
|
+
async resumePage() {
|
|
146
148
|
try {
|
|
147
149
|
this.startEventListener();
|
|
148
150
|
return super.resumePage();
|
|
@@ -161,7 +163,7 @@ let RoutesPageService = (() => {
|
|
|
161
163
|
if (loading) {
|
|
162
164
|
return { loading, synchronizing: false, routes: [], displayType,
|
|
163
165
|
filters, filterVisible,
|
|
164
|
-
|
|
166
|
+
downloadObserver: this.downloadObserver,
|
|
165
167
|
showImportDialog: false };
|
|
166
168
|
}
|
|
167
169
|
else {
|
|
@@ -173,7 +175,7 @@ let RoutesPageService = (() => {
|
|
|
173
175
|
return { loading, synchronizing, routes, displayType,
|
|
174
176
|
filters, filterVisible, filterOptions,
|
|
175
177
|
detailRouteId,
|
|
176
|
-
|
|
178
|
+
downloadObserver: this.downloadObserver,
|
|
177
179
|
showImportDialog: this.showImportDialog };
|
|
178
180
|
}
|
|
179
181
|
}
|
|
@@ -232,7 +234,8 @@ let RoutesPageService = (() => {
|
|
|
232
234
|
try {
|
|
233
235
|
const service = this.getRouteList();
|
|
234
236
|
const pairing = this.getDevicePairing();
|
|
235
|
-
const
|
|
237
|
+
const setttings = service.getStartSettings() ?? {};
|
|
238
|
+
const { id, title, videoUrl } = setttings;
|
|
236
239
|
this.logEvent({ message: 'Attempting to start a ride', id, title, videoUrl, readyToStart: pairing.isReadyToStart(), });
|
|
237
240
|
service.close();
|
|
238
241
|
const next = pairing.isReadyToStart() ? '/rideDeviceOK' : '/pairingStart';
|
|
@@ -252,11 +255,11 @@ let RoutesPageService = (() => {
|
|
|
252
255
|
imports.forEach((i) => {
|
|
253
256
|
this.prepareSingleImport(i);
|
|
254
257
|
});
|
|
255
|
-
return this.importObserver;
|
|
256
258
|
}
|
|
257
259
|
catch (err) {
|
|
258
260
|
this.logError(err, 'start');
|
|
259
261
|
}
|
|
262
|
+
return this.importObserver;
|
|
260
263
|
}
|
|
261
264
|
onImportClosed() {
|
|
262
265
|
try {
|
|
@@ -273,12 +276,7 @@ let RoutesPageService = (() => {
|
|
|
273
276
|
}
|
|
274
277
|
}
|
|
275
278
|
getImportDisplayProps() {
|
|
276
|
-
|
|
277
|
-
return this.importProps;
|
|
278
|
-
}
|
|
279
|
-
catch (err) {
|
|
280
|
-
this.logError(err, 'getImportDisplayProps');
|
|
281
|
-
}
|
|
279
|
+
return this.importProps;
|
|
282
280
|
}
|
|
283
281
|
getDownloadDisplayProps() {
|
|
284
282
|
return Array.from(this.downloadCache.values());
|
|
@@ -287,7 +285,7 @@ let RoutesPageService = (() => {
|
|
|
287
285
|
this.getPageObserver()?.emit('page-update');
|
|
288
286
|
}
|
|
289
287
|
getRoutesDisplayProps() {
|
|
290
|
-
const { routes } = this.serviceState ?? {};
|
|
288
|
+
const { routes = [] } = this.serviceState ?? {};
|
|
291
289
|
const getRouteProps = (routeProps) => {
|
|
292
290
|
return {
|
|
293
291
|
...routeProps,
|
|
@@ -299,7 +297,7 @@ let RoutesPageService = (() => {
|
|
|
299
297
|
return this.getRouteList().getFilters();
|
|
300
298
|
}
|
|
301
299
|
startEventListener() {
|
|
302
|
-
const { observer } = this.serviceState;
|
|
300
|
+
const { observer } = this.serviceState ?? {};
|
|
303
301
|
if (!observer)
|
|
304
302
|
return;
|
|
305
303
|
observer.on('updated', this.updateStateHandler);
|
|
@@ -311,7 +309,7 @@ let RoutesPageService = (() => {
|
|
|
311
309
|
this.getRouteDownload().on('download-started', this.downloadStartedHandler);
|
|
312
310
|
}
|
|
313
311
|
stopEventListener(final) {
|
|
314
|
-
const { observer } = this.serviceState;
|
|
312
|
+
const { observer } = this.serviceState ?? {};
|
|
315
313
|
if (!observer)
|
|
316
314
|
return;
|
|
317
315
|
if (final)
|
|
@@ -325,29 +323,37 @@ let RoutesPageService = (() => {
|
|
|
325
323
|
this.getRouteDownload().off('download-started', this.downloadStartedHandler);
|
|
326
324
|
}
|
|
327
325
|
subscribeToActiveDownload(route, observer) {
|
|
328
|
-
const routeId = route
|
|
329
|
-
const title = route
|
|
326
|
+
const routeId = route?.description?.id;
|
|
327
|
+
const title = route?.description?.title ?? '';
|
|
328
|
+
if (!routeId)
|
|
329
|
+
return;
|
|
330
330
|
if (this.downloadHandlers.has(routeId))
|
|
331
331
|
return;
|
|
332
332
|
const onProgress = (pct) => {
|
|
333
333
|
this.downloadCache.set(routeId, { routeId, title, status: 'downloading', pct });
|
|
334
|
-
this.
|
|
334
|
+
this.emitDownloadUpdate();
|
|
335
335
|
};
|
|
336
336
|
const onDone = () => {
|
|
337
337
|
this.downloadCache.set(routeId, { routeId, title, status: 'done' });
|
|
338
338
|
this.downloadHandlers.delete(routeId);
|
|
339
|
-
this.
|
|
339
|
+
this.emitDownloadUpdate();
|
|
340
340
|
};
|
|
341
341
|
const onError = () => {
|
|
342
342
|
this.downloadCache.set(routeId, { routeId, title, status: 'failed' });
|
|
343
343
|
this.downloadHandlers.delete(routeId);
|
|
344
|
-
this.
|
|
344
|
+
this.emitDownloadUpdate();
|
|
345
|
+
};
|
|
346
|
+
const onStopped = () => {
|
|
347
|
+
this.downloadCache.delete(routeId);
|
|
348
|
+
this.downloadHandlers.delete(routeId);
|
|
349
|
+
this.emitDownloadUpdate();
|
|
345
350
|
};
|
|
346
|
-
this.downloadHandlers.set(routeId, { onProgress, onDone, onError });
|
|
351
|
+
this.downloadHandlers.set(routeId, { onProgress, onDone, onError, onStopped });
|
|
347
352
|
this.downloadCache.set(routeId, { routeId, title, status: 'downloading' });
|
|
348
353
|
observer.on('progress', onProgress);
|
|
349
354
|
observer.on('done', onDone);
|
|
350
355
|
observer.on('error', onError);
|
|
356
|
+
observer.on('stopped', onStopped);
|
|
351
357
|
this.updatePageDisplay();
|
|
352
358
|
}
|
|
353
359
|
subscribeAllActiveDownloads() {
|
|
@@ -358,12 +364,15 @@ let RoutesPageService = (() => {
|
|
|
358
364
|
unsubscribeAllActiveDownloads() {
|
|
359
365
|
const active = this.getRouteDownload().getActiveDownloads();
|
|
360
366
|
for (const { route, observer } of active) {
|
|
361
|
-
const routeId = route
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
+
const routeId = route?.description?.id;
|
|
368
|
+
if (routeId) {
|
|
369
|
+
const handlers = this.downloadHandlers.get(routeId);
|
|
370
|
+
if (handlers) {
|
|
371
|
+
observer.off('progress', handlers.onProgress);
|
|
372
|
+
observer.off('done', handlers.onDone);
|
|
373
|
+
observer.off('error', handlers.onError);
|
|
374
|
+
observer.off('stopped', handlers.onStopped);
|
|
375
|
+
}
|
|
367
376
|
}
|
|
368
377
|
}
|
|
369
378
|
this.downloadHandlers.clear();
|
|
@@ -371,6 +380,13 @@ let RoutesPageService = (() => {
|
|
|
371
380
|
downloadStartedHandler = (route, observer) => {
|
|
372
381
|
this.subscribeToActiveDownload(route, observer);
|
|
373
382
|
};
|
|
383
|
+
emitDownloadUpdate() {
|
|
384
|
+
this.downloadObserver.emit('download-update', {
|
|
385
|
+
rows: this.getDownloadDisplayProps(),
|
|
386
|
+
count: Array.from(this.downloadCache.values())
|
|
387
|
+
.filter(r => r.status === 'downloading').length
|
|
388
|
+
});
|
|
389
|
+
}
|
|
374
390
|
onSycncStart() {
|
|
375
391
|
this.updatePageDisplay();
|
|
376
392
|
}
|
|
@@ -381,8 +397,7 @@ let RoutesPageService = (() => {
|
|
|
381
397
|
this.updatePageDisplay();
|
|
382
398
|
}
|
|
383
399
|
onDialogClosed() {
|
|
384
|
-
|
|
385
|
-
this.detailRouteId = null;
|
|
400
|
+
this.detailRouteId = undefined;
|
|
386
401
|
this.updatePageDisplay();
|
|
387
402
|
}
|
|
388
403
|
prepareSingleImport(file) {
|
|
@@ -392,6 +407,7 @@ let RoutesPageService = (() => {
|
|
|
392
407
|
status: 'idle',
|
|
393
408
|
fileName: file.name
|
|
394
409
|
};
|
|
410
|
+
this.importProps = this.importProps ?? [];
|
|
395
411
|
this.importProps.push(props);
|
|
396
412
|
observer.once('success', () => {
|
|
397
413
|
props.status = 'success';
|
|
@@ -21,7 +21,7 @@ export declare class RouteDownloadService extends IncyclistService {
|
|
|
21
21
|
protected deleteIncompleteFile(route: Route, videoDir: string): Promise<void>;
|
|
22
22
|
protected getObserver(route: Route): DownloadObserver;
|
|
23
23
|
protected _download(route: Route): Promise<void>;
|
|
24
|
-
protected waitForVideoDir(observer: DownloadObserver):
|
|
24
|
+
protected waitForVideoDir(observer: DownloadObserver): Promise<string | undefined>;
|
|
25
25
|
protected getDownloadFileName(route: Route, targetDir: string): Promise<{
|
|
26
26
|
file?: string;
|
|
27
27
|
url?: string;
|
|
@@ -29,6 +29,8 @@ export declare class RouteDownloadService extends IncyclistService {
|
|
|
29
29
|
}>;
|
|
30
30
|
protected downloadRoute(route: Route, targetDir: string, observer: DownloadObserver): Promise<void>;
|
|
31
31
|
protected restoreFromRepo(route: Route): Promise<string>;
|
|
32
|
-
protected onDownloadProgress(id: string, observer: DownloadObserver, pct:
|
|
32
|
+
protected onDownloadProgress(id: string, observer: DownloadObserver, pct: number | string, speed: number | string, downloaded: number | string): void;
|
|
33
|
+
protected isMobile(): boolean;
|
|
34
|
+
protected getBindings(): import("../../api").IncyclistBindings;
|
|
33
35
|
}
|
|
34
36
|
export declare const useRouteDownload: () => RouteDownloadService;
|
|
@@ -16,20 +16,20 @@ export declare const DEFAULT_FILTERS: {
|
|
|
16
16
|
extensions: string[];
|
|
17
17
|
}[];
|
|
18
18
|
declare class ConvertObserver extends Observer {
|
|
19
|
-
protected conversion
|
|
19
|
+
protected conversion?: Observer;
|
|
20
20
|
constructor();
|
|
21
21
|
setConversion(conversion: Observer): void;
|
|
22
22
|
stop(): void;
|
|
23
23
|
}
|
|
24
24
|
export declare class RouteCard extends BaseCard implements Card<Route> {
|
|
25
|
-
protected downloadObserver
|
|
26
|
-
protected convertObserver
|
|
25
|
+
protected downloadObserver?: DownloadObserver;
|
|
26
|
+
protected convertObserver?: ConvertObserver;
|
|
27
27
|
protected route: Route;
|
|
28
|
-
protected list
|
|
28
|
+
protected list?: CardList<Route>;
|
|
29
29
|
protected deleteable: boolean;
|
|
30
|
-
protected startSettings
|
|
30
|
+
protected startSettings?: RouteSettings;
|
|
31
31
|
protected cardObserver: Observer;
|
|
32
|
-
protected deleteObserver
|
|
32
|
+
protected deleteObserver?: PromiseObserver<boolean>;
|
|
33
33
|
protected ready: boolean;
|
|
34
34
|
protected logger: EventLogger;
|
|
35
35
|
protected cntActive: number;
|
|
@@ -42,7 +42,7 @@ export declare class RouteCard extends BaseCard implements Card<Route> {
|
|
|
42
42
|
verify(): void;
|
|
43
43
|
protected isVideoMissing(): Promise<boolean>;
|
|
44
44
|
videoExists(): Promise<boolean>;
|
|
45
|
-
protected fileExists(path:
|
|
45
|
+
protected fileExists(path: string): Promise<boolean>;
|
|
46
46
|
cleanupEncoding(path: string): string;
|
|
47
47
|
previewMissing(): boolean;
|
|
48
48
|
setInitialized(init: boolean): void;
|
|
@@ -84,6 +84,7 @@ export declare class RouteCard extends BaseCard implements Card<Route> {
|
|
|
84
84
|
onVideoSelected(info: FileInfo): Promise<"Could not open file" | "Unsupported video format - Please select MP4 or AVI">;
|
|
85
85
|
download(): Observer;
|
|
86
86
|
stopDownload(immediate?: boolean): void;
|
|
87
|
+
deleteDownload(): Promise<void>;
|
|
87
88
|
protected onDownloadCompleted(url: string): Promise<void>;
|
|
88
89
|
private saveOriginalVideoUrl;
|
|
89
90
|
private restoreVideoUrl;
|
|
@@ -113,5 +114,6 @@ export declare class RouteCard extends BaseCard implements Card<Route> {
|
|
|
113
114
|
protected isMobile(): boolean;
|
|
114
115
|
protected getBindings(): import("../../../api").IncyclistBindings;
|
|
115
116
|
protected getOnlineStatusMonitoring(): import("../../../monitoring").OnlineStateMonitoringService;
|
|
117
|
+
protected getRouteDownload(): import("../../download/service").RouteDownloadService;
|
|
116
118
|
}
|
|
117
119
|
export {};
|
|
@@ -2,6 +2,7 @@ import { ImportFilter } from "../../../base/cardlist/types";
|
|
|
2
2
|
import { Observer } from "../../../base/types/observer";
|
|
3
3
|
import { Unit } from "../../../i18n";
|
|
4
4
|
import { RouteInfo } from "../../base/types";
|
|
5
|
+
import { DownloadObserver } from "../../download/types";
|
|
5
6
|
import { RouteStartSettings } from "../types";
|
|
6
7
|
export type RouteCardType = 'Import' | 'Route' | 'Free-Ride' | 'ActiveImport';
|
|
7
8
|
export interface RouteImportProps {
|
|
@@ -26,14 +27,15 @@ export interface SummaryCardDisplayProps extends RouteInfo {
|
|
|
26
27
|
loading?: boolean;
|
|
27
28
|
isNew?: boolean;
|
|
28
29
|
cntActive?: number;
|
|
29
|
-
totalDistance
|
|
30
|
+
totalDistance?: {
|
|
30
31
|
value: number;
|
|
31
32
|
unit: Unit;
|
|
32
33
|
};
|
|
33
|
-
totalElevation
|
|
34
|
+
totalElevation?: {
|
|
34
35
|
value: number;
|
|
35
36
|
unit: Unit;
|
|
36
37
|
};
|
|
38
|
+
downloadObserver?: DownloadObserver;
|
|
37
39
|
}
|
|
38
40
|
export interface DetailCardDisplayProps {
|
|
39
41
|
}
|
|
@@ -6,7 +6,7 @@ import { Observer } from "../../base/types";
|
|
|
6
6
|
import { Route } from "../base/model/route";
|
|
7
7
|
import { DownloadObserver } from "../download/types";
|
|
8
8
|
export declare class RoutesPageService extends IncyclistPageService implements IRoutePageService {
|
|
9
|
-
protected serviceState: SearchState;
|
|
9
|
+
protected serviceState: SearchState | undefined;
|
|
10
10
|
protected detailRouteId: string | undefined;
|
|
11
11
|
protected updateStateHandler: any;
|
|
12
12
|
protected syncStartHandler: any;
|
|
@@ -14,13 +14,15 @@ export declare class RoutesPageService extends IncyclistPageService implements I
|
|
|
14
14
|
protected updateSelectStateHandler: any;
|
|
15
15
|
protected showImportDialog: boolean;
|
|
16
16
|
protected importObserver: Observer | undefined;
|
|
17
|
-
protected importProps: Array<RouteImportDisplayProps
|
|
17
|
+
protected importProps: Array<RouteImportDisplayProps> | undefined;
|
|
18
18
|
protected downloadCache: Map<string, DownloadRowDisplayProps>;
|
|
19
19
|
protected downloadHandlers: Map<string, {
|
|
20
20
|
onProgress: (pct: number) => void;
|
|
21
21
|
onDone: () => void;
|
|
22
22
|
onError: () => void;
|
|
23
|
+
onStopped: () => void;
|
|
23
24
|
}>;
|
|
25
|
+
protected downloadObserver: Observer;
|
|
24
26
|
constructor();
|
|
25
27
|
openPage(): IObserver;
|
|
26
28
|
closePage(): void;
|
|
@@ -46,6 +48,7 @@ export declare class RoutesPageService extends IncyclistPageService implements I
|
|
|
46
48
|
protected subscribeAllActiveDownloads(): void;
|
|
47
49
|
protected unsubscribeAllActiveDownloads(): void;
|
|
48
50
|
protected downloadStartedHandler: (route: Route, observer: DownloadObserver) => void;
|
|
51
|
+
protected emitDownloadUpdate(): void;
|
|
49
52
|
protected onSycncStart(): void;
|
|
50
53
|
protected onSyncStop(): void;
|
|
51
54
|
protected onStateUpdate(): void;
|
|
@@ -13,13 +13,13 @@ export interface RoutePageDisplayProps {
|
|
|
13
13
|
filterOptions?: SearchFilterOptions;
|
|
14
14
|
routes?: Array<RouteItemProps>;
|
|
15
15
|
detailRouteId?: string;
|
|
16
|
-
|
|
16
|
+
downloadObserver?: IObserver;
|
|
17
17
|
showImportDialog?: boolean;
|
|
18
18
|
}
|
|
19
19
|
export interface RouteItemProps extends SummaryCardDisplayProps {
|
|
20
20
|
}
|
|
21
21
|
export interface IPageCallBacks {
|
|
22
|
-
onFilterChanged(filters: SearchFilter):
|
|
22
|
+
onFilterChanged(filters: SearchFilter): void;
|
|
23
23
|
onImportClicked(): void;
|
|
24
24
|
onFilterVisibleChange(visible: boolean): void;
|
|
25
25
|
}
|