incyclist-services 1.5.71 → 1.6.2

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.
@@ -79,7 +79,7 @@ let ActivityListService = (() => {
79
79
  preload(props) {
80
80
  let preloadObserver = this.getPreloadObserver();
81
81
  try {
82
- if (!preloadObserver) {
82
+ if (preloadObserver === undefined || preloadObserver === null) {
83
83
  this.logEvent({ message: 'preload activity list' });
84
84
  const promise = this.loadActivities();
85
85
  preloadObserver = this.observers.preload = new observer_1.PromiseObserver(promise);
@@ -247,10 +247,17 @@ let ActivityListService = (() => {
247
247
  if (!activity) {
248
248
  return false;
249
249
  }
250
- const emitUpdate = () => { this.emitSelected('updated'); };
251
- const observer = new observer_1.Observer();
252
- observer.on('export', emitUpdate);
253
- return yield activity.export(format, observer);
250
+ return new Promise(done => {
251
+ const emitUpdate = (update) => __awaiter(this, void 0, void 0, function* () {
252
+ this.exports = yield this.selected.getExports();
253
+ this.emitSelected('updated');
254
+ if (update.status === 'done')
255
+ done(update.success);
256
+ });
257
+ const observer = new observer_1.Observer();
258
+ observer.on('export', emitUpdate);
259
+ activity.export(format, observer);
260
+ });
254
261
  });
255
262
  }
256
263
  upload(connectedApp) {
@@ -259,11 +266,24 @@ let ActivityListService = (() => {
259
266
  if (!activity) {
260
267
  return false;
261
268
  }
262
- const emitUpdate = () => { this.emitSelected('updated'); };
263
- const observer = new observer_1.Observer();
264
- observer.on('upload', emitUpdate);
265
- observer.on('export', emitUpdate);
266
- return yield activity.upload(connectedApp, observer);
269
+ return new Promise(done => {
270
+ const emitUploadUpdate = (update) => {
271
+ if (connectedApp === update.connectedApp) {
272
+ this.emitSelected('updated');
273
+ if (update.status === 'done') {
274
+ done(update.success);
275
+ }
276
+ }
277
+ };
278
+ const emitExportUpdate = (update) => __awaiter(this, void 0, void 0, function* () {
279
+ this.exports = yield this.selected.getExports();
280
+ this.emitSelected('updated');
281
+ });
282
+ const observer = new observer_1.Observer();
283
+ observer.on('upload', emitUploadUpdate);
284
+ observer.on('export', emitExportUpdate);
285
+ activity.upload(connectedApp, observer);
286
+ });
267
287
  });
268
288
  }
269
289
  openRoute() {
@@ -28,6 +28,7 @@ export declare class StravaApi extends AppApiBase {
28
28
  protected verifyToken(): Promise<void>;
29
29
  protected isTokenStillValid(): boolean;
30
30
  protected verifyAuthentication(): Promise<void>;
31
+ protected waitForReady(): Promise<void>;
31
32
  protected waitForUploadResponse(response: StravaUploadResponse): Promise<StravaUploadResult>;
32
33
  protected createUpload(request: StravaUploadRequest): Promise<StravaUploadResponse>;
33
34
  protected getUploadById(id: string): Promise<StravaUploadResponse>;
@@ -125,11 +125,16 @@ class StravaApi extends base_1.AppApiBase {
125
125
  yield this.verifyToken();
126
126
  });
127
127
  }
128
+ waitForReady() {
129
+ return __awaiter(this, void 0, void 0, function* () {
130
+ yield (0, utils_1.sleep)(1000);
131
+ });
132
+ }
128
133
  waitForUploadResponse(response) {
129
134
  return __awaiter(this, void 0, void 0, function* () {
130
135
  let data = response;
131
136
  while (data.status !== 'Your activity is ready.' && !data.error) {
132
- yield (0, utils_1.sleep)(1000);
137
+ yield this.waitForReady();
133
138
  data = yield this.getUploadById(data.id_str);
134
139
  }
135
140
  if (data.error) {
@@ -1 +1,2 @@
1
1
  export * from './service';
2
+ export * from './types';
@@ -15,3 +15,4 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./service"), exports);
18
+ __exportStar(require("./types"), exports);
@@ -1,7 +1,9 @@
1
1
  import { IncyclistService } from "../base/service";
2
+ import { AppFeature } from "./types";
2
3
  export declare class AppStateService extends IncyclistService {
3
4
  protected state: Record<string, any>;
4
5
  constructor();
6
+ hasFeature(feature: AppFeature): any;
5
7
  getPersistedState(key: string): any;
6
8
  setPersistedState(key: string, value: any): void;
7
9
  getState(key: string): any;
@@ -56,6 +56,9 @@ let AppStateService = (() => {
56
56
  super('AppState');
57
57
  this.state = (__runInitializers(this, _instanceExtraInitializers), {});
58
58
  }
59
+ hasFeature(feature) {
60
+ return this.getSetting(feature, false);
61
+ }
59
62
  getPersistedState(key) {
60
63
  return this.getSetting(`state.${key}`, undefined);
61
64
  }
@@ -391,7 +391,7 @@ let DeviceConfigurationService = (() => {
391
391
  if (adapter.getSupportedCyclingModes) {
392
392
  modes = adapter.getSupportedCyclingModes();
393
393
  }
394
- const options = modes.map(M => new M(adapter));
394
+ const options = modes.map(M => adapter.createMode(M));
395
395
  mode = requestedMode || device.mode;
396
396
  if (!mode) {
397
397
  modeObj = adapter.getDefaultCyclingMode();
@@ -99,21 +99,14 @@ let RideDisplayService = (() => {
99
99
  }
100
100
  init() {
101
101
  return __awaiter(this, void 0, void 0, function* () {
102
- var _a, _b, _c;
103
102
  yield this.closePrevRide();
104
103
  this.observer = new types_1.Observer();
105
104
  try {
106
105
  this.displayService = this.getRideModeService(true);
107
- const ftNewUI = this.getUserSettings().get('NEW_GPX_UI', false);
108
- const isVideo = this.getRideType() === 'Video';
109
- const isMp4 = isVideo && ((_c = (_b = (_a = this.route) === null || _a === void 0 ? void 0 : _a.description) === null || _b === void 0 ? void 0 : _b.videoFormat) === null || _c === void 0 ? void 0 : _c.toLowerCase()) === 'mp4';
110
- const showLegacy = isVideo && !isMp4 && !ftNewUI;
111
- if (!showLegacy) {
112
- this.displayService.init(this);
113
- this.displayService.on('lap-completed', this.onLapCompleted.bind(this));
114
- this.displayService.on('route-completed', this.onRouteCompleted.bind(this));
115
- this.hideAll = false;
116
- }
106
+ this.displayService.init(this);
107
+ this.displayService.on('lap-completed', this.onLapCompleted.bind(this));
108
+ this.displayService.on('route-completed', this.onRouteCompleted.bind(this));
109
+ this.hideAll = false;
117
110
  }
118
111
  catch (err) {
119
112
  this.logError(err, 'init');
@@ -180,6 +173,9 @@ let RideDisplayService = (() => {
180
173
  }
181
174
  pause(requester = 'user') {
182
175
  var _a, _b;
176
+ if (requester === 'device' && this.isResuming) {
177
+ return;
178
+ }
183
179
  try {
184
180
  if (this.state !== 'Active')
185
181
  return;
@@ -205,10 +201,8 @@ let RideDisplayService = (() => {
205
201
  this.state = 'Active';
206
202
  this.getActivityRide().resume();
207
203
  this.getWorkoutRide().resume();
208
- this.displayService.resume();
204
+ this.pauseReason = 'device';
209
205
  (_b = this.observer) === null || _b === void 0 ? void 0 : _b.emit('state-update', this.state);
210
- (0, utils_1.waitNextTick)().then(() => this.isResuming = false);
211
- delete this.pauseReason;
212
206
  }
213
207
  catch (err) {
214
208
  this.logError(err, 'resume');
@@ -552,10 +546,15 @@ let RideDisplayService = (() => {
552
546
  const currentValues = this.getActivityRide().getCurrentValues();
553
547
  if (!currentValues)
554
548
  return;
555
- if (currentValues.speed === 0) {
549
+ if (currentValues.speed === 0 && !this.isResuming) {
556
550
  this.pause('device');
557
551
  return;
558
552
  }
553
+ if (currentValues.speed > 0 && this.isResuming) {
554
+ this.displayService.resume();
555
+ this.isResuming = false;
556
+ delete this.pauseReason;
557
+ }
559
558
  this.getRideModeService().onActivityUpdate(data, currentValues);
560
559
  this.observer.emit('data-update', data, currentValues);
561
560
  }
@@ -598,7 +597,6 @@ let RideDisplayService = (() => {
598
597
  }
599
598
  }
600
599
  onLapCompleted(oldLap, newLap) {
601
- console.log('# lap completed', oldLap, newLap);
602
600
  }
603
601
  onRouteUpdated(route) {
604
602
  this.getActivityRide().onRouteUpdate(route.points);
@@ -898,7 +896,7 @@ let RideDisplayService = (() => {
898
896
  udid: d.udid,
899
897
  isControl: d.isControl,
900
898
  capabilities: d.capabilities,
901
- status: d.isStarted ? 'Started' : 'Starting'
899
+ status: 'Starting'
902
900
  }));
903
901
  this.updateStartOverlay();
904
902
  }
@@ -981,6 +979,7 @@ let RideDisplayService = (() => {
981
979
  const pct = Math.round(completed / total * 100);
982
980
  this.logEvent({ message: 'onDeviceStartStatusUpdate', pct });
983
981
  startDevice.stateText = `uploading (${pct}%)`;
982
+ this.checkStartStatus();
984
983
  }
985
984
  onDeviceData(data, udid) {
986
985
  this.deviceData = data;
@@ -35,11 +35,13 @@ export declare class RLVDisplayService extends RouteDisplayService {
35
35
  protected infotext?: InfotextDisplayProps;
36
36
  protected videosInitialized: boolean;
37
37
  protected startTime: number;
38
+ protected isVideoPaused: boolean;
38
39
  constructor();
39
40
  initView(): void;
40
41
  getOverlayProps(overlay: any, props: CurrentRideDisplayProps): OverlayDisplayProps;
41
42
  protected addVideo(route: Route, isCurrent?: boolean, parent?: VideoState): Promise<void>;
42
43
  pause(): void;
44
+ resume(): void;
43
45
  getDisplayProperties(props: CurrentRideDisplayProps): RLVDisplayProps;
44
46
  protected getInfotextDisplayProps(): InfotextDisplayProps;
45
47
  protected checkFinishOptions(position: CurrentPosition): boolean;
@@ -62,6 +62,7 @@ let RLVDisplayService = (() => {
62
62
  super();
63
63
  this.currentVideo = __runInitializers(this, _instanceExtraInitializers);
64
64
  this.videosInitialized = false;
65
+ this.isVideoPaused = false;
65
66
  this.isInitialized = false;
66
67
  }
67
68
  initView() {
@@ -130,6 +131,12 @@ let RLVDisplayService = (() => {
130
131
  pause() {
131
132
  super.pause();
132
133
  this.currentVideo.syncHelper.pause();
134
+ this.isVideoPaused = true;
135
+ }
136
+ resume() {
137
+ super.resume();
138
+ this.currentVideo.syncHelper.resume();
139
+ this.isVideoPaused = false;
133
140
  }
134
141
  getDisplayProperties(props) {
135
142
  var _b, _c, _d, _e;
@@ -380,7 +387,9 @@ let RLVDisplayService = (() => {
380
387
  super.onActivityUpdate(activityPos, data);
381
388
  const offset = (_b = this.offset) !== null && _b !== void 0 ? _b : 0;
382
389
  const { routeDistance, speed } = activityPos;
383
- this.currentVideo.syncHelper.onActivityUpdate(routeDistance - offset, speed);
390
+ if (!this.isVideoPaused) {
391
+ this.currentVideo.syncHelper.onActivityUpdate(routeDistance - offset, speed);
392
+ }
384
393
  }
385
394
  initVideoSource(video) {
386
395
  var _b, _c;
@@ -31,9 +31,9 @@ export interface GetPositionProps {
31
31
  nearest?: boolean;
32
32
  latlng?: LatLng;
33
33
  }
34
- export declare const getPosition: (route: Route, props: GetPositionProps) => RoutePoint;
34
+ export declare const getPosition: (src: Route | Array<RoutePoint>, props: GetPositionProps) => RoutePoint;
35
35
  export declare const getHeading: (route: Route, point: RoutePoint) => number;
36
- export declare const getPointAtDistance: (route: Route | RouteApiDetail, routeDistance: number, nearest?: boolean) => RoutePoint;
36
+ export declare const getPointAtDistance: (src: Route | RouteApiDetail | Array<RoutePoint>, routeDistance: number, nearest?: boolean) => RoutePoint;
37
37
  interface LegacyRouteApiDetail extends RouteApiDetail {
38
38
  decoded?: Array<RoutePoint>;
39
39
  }
@@ -359,18 +359,26 @@ const getNextPosition = (route, props) => {
359
359
  return lapPoint;
360
360
  };
361
361
  exports.getNextPosition = getNextPosition;
362
- const getPosition = (route, props) => {
363
- if (props === undefined || route.points === undefined)
362
+ const getPosition = (src, props) => {
363
+ let route;
364
+ let points;
365
+ if (Array.isArray(src)) {
366
+ points = src;
367
+ }
368
+ else {
369
+ route = src;
370
+ }
371
+ if (props === undefined || ((route === null || route === void 0 ? void 0 : route.points) === undefined && !(points === null || points === void 0 ? void 0 : points.length)))
364
372
  return;
365
373
  const { cnt, distance, nearest = true, latlng } = props;
366
374
  if (props.cnt !== undefined) {
367
- return getPointAtIndex(route, cnt);
375
+ return getPointAtIndex(src, cnt);
368
376
  }
369
377
  if (props.distance !== undefined) {
370
- return (0, exports.getPointAtDistance)(route, distance, nearest);
378
+ return (0, exports.getPointAtDistance)(src, distance, nearest);
371
379
  }
372
380
  if (props.latlng !== undefined) {
373
- return getPointAtLatLng(route, latlng, nearest);
381
+ return getPointAtLatLng(src, latlng, nearest);
374
382
  }
375
383
  };
376
384
  exports.getPosition = getPosition;
@@ -394,16 +402,16 @@ const getHeading = (route, point) => {
394
402
  return (0, geo_1.calculateHeaderFromPoints)(p1, p2);
395
403
  };
396
404
  exports.getHeading = getHeading;
397
- const getPointAtIndex = (route, idx) => {
398
- const points = route.points;
405
+ const getPointAtIndex = (src, idx) => {
406
+ const points = Array.isArray(src) ? src : src.points;
399
407
  if (idx >= points.length || idx < 0)
400
408
  return;
401
409
  const point = Object.assign({}, points[idx]);
402
410
  point.cnt = idx;
403
411
  return point;
404
412
  };
405
- const getPointAtDistance = (route, routeDistance, nearest = true) => {
406
- const points = route.points;
413
+ const getPointAtDistance = (src, routeDistance, nearest = true) => {
414
+ const points = Array.isArray(src) ? src : src.points;
407
415
  if (!points)
408
416
  return;
409
417
  const exact = !nearest;
@@ -432,9 +440,9 @@ const getPointAtDistance = (route, routeDistance, nearest = true) => {
432
440
  return point;
433
441
  };
434
442
  exports.getPointAtDistance = getPointAtDistance;
435
- const getPointAtLatLng = (route, latlng, nearest = true) => {
436
- const points = route.points;
437
- if (!points || !route.description.hasGpx)
443
+ const getPointAtLatLng = (src, latlng, nearest = true) => {
444
+ const points = Array.isArray(src) ? src : src.points;
445
+ if (!points)
438
446
  return;
439
447
  const exact = !nearest;
440
448
  let cnt = 0;
@@ -176,6 +176,7 @@ export declare class RouteListService extends IncyclistService implements IRoute
176
176
  private selectListForSource;
177
177
  private addCustomList;
178
178
  createPreview(descr: RouteInfo): Promise<unknown>;
179
+ protected checkExistingPreviewFiles(descr: RouteInfo): Promise<string | undefined>;
179
180
  protected doCreatePreview(descr: RouteInfo): Promise<string>;
180
181
  protected processPreviewQueue(): PromiseObserver<void>;
181
182
  protected findCard(target: Route | string): {
@@ -993,6 +993,45 @@ let RouteListService = (() => {
993
993
  });
994
994
  });
995
995
  }
996
+ checkExistingPreviewFiles(descr) {
997
+ return __awaiter(this, void 0, void 0, function* () {
998
+ var _a;
999
+ const videoUrl = descr.videoUrl || descr.downloadUrl;
1000
+ const path = (0, api_1.getBindings)().path;
1001
+ const fs = (0, api_1.getBindings)().fs;
1002
+ let existingPreview;
1003
+ let fileUrl = false;
1004
+ try {
1005
+ let { dir, name } = (_a = path.parse(videoUrl)) !== null && _a !== void 0 ? _a : {};
1006
+ if (!dir.startsWith('htttp')) {
1007
+ if (dir.startsWith('video:')) {
1008
+ dir = dir.replace('video:', 'file:');
1009
+ }
1010
+ if (dir.startsWith('file:///')) {
1011
+ dir = dir.replace('file:///', '');
1012
+ fileUrl = true;
1013
+ }
1014
+ const check = (name) => __awaiter(this, void 0, void 0, function* () {
1015
+ if (existingPreview)
1016
+ return;
1017
+ if (yield fs.existsFile(name))
1018
+ existingPreview = name;
1019
+ });
1020
+ yield check(path.join(dir, 'preview.png'));
1021
+ yield check(path.join(dir, 'preview.jpg'));
1022
+ yield check(path.join(dir, `${name}_preview.png`));
1023
+ yield check(path.join(dir, `${name}_preview.jpg`));
1024
+ if (existingPreview) {
1025
+ this.logEvent({ message: 'found preview', title: descr.title, id: descr.id, video: videoUrl, existingPreview });
1026
+ descr.previewUrl = fileUrl ? `file:///${existingPreview}` : existingPreview;
1027
+ return descr.previewUrl;
1028
+ }
1029
+ }
1030
+ }
1031
+ catch (_b) {
1032
+ }
1033
+ });
1034
+ }
996
1035
  doCreatePreview(descr) {
997
1036
  return __awaiter(this, void 0, void 0, function* () {
998
1037
  let props;
@@ -1005,6 +1044,9 @@ let RouteListService = (() => {
1005
1044
  const fs = (0, api_1.getBindings)().fs;
1006
1045
  const outDir = path.join(appInfo.getAppDir(), 'previewImg');
1007
1046
  yield fs.ensureDir(outDir);
1047
+ const existing = yield this.checkExistingPreviewFiles(descr);
1048
+ if (existing)
1049
+ return existing;
1008
1050
  if (!videoUrl)
1009
1051
  return;
1010
1052
  if (videoUrl.startsWith('http')) {
@@ -24,12 +24,16 @@ class VideoSyncHelper extends service_1.IncyclistService {
24
24
  this.logPlaybackSummary();
25
25
  this.isPaused = true;
26
26
  this.send('rate-update', 0);
27
+ this.activityStatus.ts = Date.now();
28
+ this.rlvStatus.ts = Date.now();
27
29
  }
28
30
  resume() {
29
31
  if (this.isStopped)
30
32
  return;
31
33
  this.logEvent({ message: 'video resumed', route: this.route.details.title });
32
34
  this.isPaused = false;
35
+ this.activityStatus.ts = Date.now();
36
+ this.rlvStatus.ts = Date.now();
33
37
  }
34
38
  reset() {
35
39
  this.logEvent({ message: 'video reset', route: this.route.details.title });
@@ -64,7 +68,7 @@ class VideoSyncHelper extends service_1.IncyclistService {
64
68
  try {
65
69
  this.endTime = (_a = this.endTime) !== null && _a !== void 0 ? _a : this.getVideoTimeByPosition((_b = this.route.description) === null || _b === void 0 ? void 0 : _b.distance);
66
70
  const endTime = this.endTime;
67
- if (this.isStopped)
71
+ if (this.isStopped || this.isPaused)
68
72
  return;
69
73
  if (time > endTime - 0.3 && !this.loopMode && !this.rlvStatus.timeRequested) {
70
74
  this.updateRate(0);
@@ -305,6 +309,10 @@ class VideoSyncHelper extends service_1.IncyclistService {
305
309
  if (this.isPaused && rate > 0) {
306
310
  this.resume();
307
311
  }
312
+ else if (this.isPaused && rate === 0) {
313
+ delete this.rlvStatus.rateRequested;
314
+ return;
315
+ }
308
316
  this.rlvStatus.rateRequested = rate;
309
317
  this.send('rate-update', rate);
310
318
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "incyclist-services",
3
- "version": "1.5.71",
3
+ "version": "1.6.2",
4
4
  "peerDependencies": {
5
5
  "gd-eventlog": "^0.1.26"
6
6
  },
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "axios": "^1.8.2",
45
- "incyclist-devices": "^2.4.6-beta.3",
45
+ "incyclist-devices": "^2.4.7",
46
46
  "promise.any": "^2.0.6",
47
47
  "semver": "^7.6.3",
48
48
  "tcx-builder": "^1.1.1",