@audiowalk/sdk 1.5.2 → 1.5.4

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.
@@ -0,0 +1,2 @@
1
+ export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
2
+ //# sourceMappingURL=objects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"objects.d.ts","sourceRoot":"","sources":["../../src/helpers/objects.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -34,7 +34,7 @@ export class MediaControlsController {
34
34
  .pipe(takeUntil(this.playerDetachEvent))
35
35
  .subscribe(([currentTime, totalTime]) => {
36
36
  navigator.mediaSession.setPositionState({
37
- duration: Number.isNaN(totalTime) ? 0 : totalTime,
37
+ duration: totalTime || 0,
38
38
  position: currentTime,
39
39
  });
40
40
  });
@@ -1,12 +1,13 @@
1
1
  import { BehaviorSubject, Subject } from "rxjs";
2
+ import { PartialBy } from "../helpers/objects";
2
3
  export interface PlayerControllerOptions {
3
4
  autoSave?: boolean;
4
5
  audioElement?: HTMLAudioElement;
5
6
  loop?: boolean;
6
- fadeIn?: boolean;
7
- fadeOut?: boolean;
8
- fadeInterval?: number;
7
+ crossfade?: boolean;
8
+ crossfadeTime: number;
9
9
  }
10
+ export type PlayerControllerParams = PartialBy<PlayerControllerOptions, "crossfadeTime">;
10
11
  export declare enum PlayerStatus {
11
12
  "playing" = "playing",
12
13
  "paused" = "paused",
@@ -17,8 +18,8 @@ export declare class PlayerController {
17
18
  readonly onPause: Subject<void>;
18
19
  readonly onStop: Subject<void>;
19
20
  readonly currentTime: BehaviorSubject<number>;
20
- readonly totalTime: BehaviorSubject<number>;
21
- readonly progress: import("rxjs").Observable<number>;
21
+ readonly totalTime: BehaviorSubject<number | null>;
22
+ readonly progress: import("rxjs").Observable<number | null>;
22
23
  private trackId;
23
24
  readonly status: BehaviorSubject<PlayerStatus | null>;
24
25
  readonly playing: import("rxjs").Observable<boolean>;
@@ -27,21 +28,23 @@ export declare class PlayerController {
27
28
  private options;
28
29
  private defaultOptions;
29
30
  private destroyEvent;
30
- private fadeIntervalSubscription?;
31
- private fadePromiseResolve?;
32
- constructor(trackId: string, trackUrl: string, options?: PlayerControllerOptions);
31
+ private volume;
32
+ private fadeCancelEvent;
33
+ constructor(trackId: string, trackUrl: string, options?: PlayerControllerParams);
33
34
  open(id: string, file: string): Promise<void>;
34
- destroy(): Promise<void>;
35
- play(options?: {
36
- fadeIn?: boolean;
35
+ destroy(now?: boolean): Promise<void>;
36
+ play(params?: {
37
+ fade?: boolean;
37
38
  }): Promise<void>;
38
- fadeIn(): Promise<void>;
39
- fadeOut(): Promise<void>;
40
- private clearFade;
41
- pause(options?: {
42
- fadeIn?: boolean;
39
+ pause(): Promise<void>;
40
+ stop(params?: {
41
+ fade?: boolean;
43
42
  }): Promise<void>;
44
43
  seekTo(seconds: number): void;
44
+ setVolume(volume: number, params?: {
45
+ fade?: boolean;
46
+ }): void;
47
+ fadeToVolume(volume: number): Promise<void>;
45
48
  back(seconds?: number): void;
46
49
  forward(seconds?: number): void;
47
50
  private savePosition;
@@ -1 +1 @@
1
- {"version":3,"file":"player-controller.d.ts","sourceRoot":"","sources":["../../src/player/player-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAsB,OAAO,EAA8B,MAAM,MAAM,CAAC;AAGhG,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,oBAAY,YAAY;IACtB,SAAS,YAAY;IACrB,QAAQ,WAAW;IACnB,OAAO,UAAU;CAClB;AAED,qBAAa,gBAAgB;IAC3B,SAAgB,MAAM,gBAAuB;IAC7C,SAAgB,OAAO,gBAAuB;IAC9C,SAAgB,MAAM,gBAAuB;IAE7C,SAAgB,WAAW,0BAAkC;IAC7D,SAAgB,SAAS,0BAAkC;IAC3D,SAAgB,QAAQ,oCAEtB;IAEF,OAAO,CAAC,OAAO,CAAuB;IAEtC,SAAgB,MAAM,uCAAkD;IACxE,SAAgB,OAAO,qCAA2D;IAElF,OAAO,CAAC,aAAa,CAAmB;IAExC,OAAO,CAAC,YAAY,CAA0C;IAE9D,OAAO,CAAC,OAAO,CAAoF;IACnG,OAAO,CAAC,cAAc,CAEpB;IAEF,OAAO,CAAC,YAAY,CAAuB;IAE3C,OAAO,CAAC,wBAAwB,CAAC,CAAS;IAC1C,OAAO,CAAC,kBAAkB,CAAC,CAAa;gBAE5B,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,uBAA4B;IA+D9E,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAQ7B,OAAO;IAQP,IAAI,CAAC,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAO;IASvC,MAAM;IAiBN,OAAO;IAgBb,OAAO,CAAC,SAAS;IAKX,KAAK,CAAC,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAO;IAQ9C,MAAM,CAAC,OAAO,EAAE,MAAM;IAOtB,IAAI,CAAC,OAAO,GAAE,MAAW;IAOzB,OAAO,CAAC,OAAO,GAAE,MAAW;YAQd,YAAY;IAK1B,OAAO,CAAC,GAAG;CAOZ"}
1
+ {"version":3,"file":"player-controller.d.ts","sourceRoot":"","sources":["../../src/player/player-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAwC,OAAO,EAAwB,MAAM,MAAM,CAAC;AAC5G,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAG/C,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,sBAAsB,GAAG,SAAS,CAAC,uBAAuB,EAAE,eAAe,CAAC,CAAC;AAEzF,oBAAY,YAAY;IACtB,SAAS,YAAY;IACrB,QAAQ,WAAW;IACnB,OAAO,UAAU;CAClB;AAED,qBAAa,gBAAgB;IAC3B,SAAgB,MAAM,gBAAuB;IAC7C,SAAgB,OAAO,gBAAuB;IAC9C,SAAgB,MAAM,gBAAuB;IAE7C,SAAgB,WAAW,0BAAkC;IAC7D,SAAgB,SAAS,iCAA4C;IACrE,SAAgB,QAAQ,2CAEtB;IAEF,OAAO,CAAC,OAAO,CAAuB;IAEtC,SAAgB,MAAM,uCAAkD;IACxE,SAAgB,OAAO,qCAA2D;IAElF,OAAO,CAAC,aAAa,CAAmB;IAExC,OAAO,CAAC,YAAY,CAA0C;IAE9D,OAAO,CAAC,OAAO,CAA0B;IAEzC,OAAO,CAAC,cAAc,CAEpB;IAEF,OAAO,CAAC,YAAY,CAAuB;IAE3C,OAAO,CAAC,MAAM,CAAa;IAE3B,OAAO,CAAC,eAAe,CAAuB;gBAElC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,sBAA2B;IAiE7E,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAQ7B,OAAO,CAAC,GAAG,GAAE,OAAe;IAa5B,IAAI,CAAC,MAAM,GAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAqC;IAiBlE,KAAK;IAQL,IAAI,CAAC,MAAM,GAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAqC;IAexE,MAAM,CAAC,OAAO,EAAE,MAAM;IAQtB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAO;IAYnD,YAAY,CAAC,MAAM,EAAE,MAAM;IA4BjC,IAAI,CAAC,OAAO,GAAE,MAAW;IAKzB,OAAO,CAAC,OAAO,GAAE,MAAW;YAMd,YAAY;IAK1B,OAAO,CAAC,GAAG;CAOZ"}
@@ -1,4 +1,4 @@
1
- import { BehaviorSubject, combineLatest, map, Subject, take, takeUntil, takeWhile } from "rxjs";
1
+ import { BehaviorSubject, combineLatest, filter, interval, map, Subject, takeUntil, takeWhile } from "rxjs";
2
2
  import { LocalStorage } from "../storage/local-storage";
3
3
  export var PlayerStatus;
4
4
  (function (PlayerStatus) {
@@ -11,8 +11,8 @@ export class PlayerController {
11
11
  onPause = new Subject();
12
12
  onStop = new Subject();
13
13
  currentTime = new BehaviorSubject(0);
14
- totalTime = new BehaviorSubject(0);
15
- progress = combineLatest([this.currentTime, this.totalTime]).pipe(map(([currentTime, totalTime]) => currentTime / totalTime));
14
+ totalTime = new BehaviorSubject(null);
15
+ progress = combineLatest([this.currentTime, this.totalTime]).pipe(map(([currentTime, totalTime]) => (totalTime ? currentTime / totalTime : null)));
16
16
  trackId = null;
17
17
  status = new BehaviorSubject(null);
18
18
  playing = this.status.pipe(map((status) => status === "playing"));
@@ -20,14 +20,15 @@ export class PlayerController {
20
20
  localStorage = new LocalStorage({ prefix: "player" });
21
21
  options;
22
22
  defaultOptions = {
23
- fadeInterval: 2000,
23
+ crossfadeTime: 2000,
24
24
  };
25
25
  destroyEvent = new Subject();
26
- fadeIntervalSubscription;
27
- fadePromiseResolve;
26
+ volume = 1;
27
+ fadeCancelEvent = new Subject();
28
28
  constructor(trackId, trackUrl, options = {}) {
29
29
  this.options = { ...this.defaultOptions, ...options };
30
30
  this.playerElement = this.options.audioElement ?? new Audio();
31
+ this.playerElement.volume = this.volume;
31
32
  this.playerElement.addEventListener("play", () => {
32
33
  this.onPlay.next();
33
34
  this.status.next(PlayerStatus.playing);
@@ -37,8 +38,10 @@ export class PlayerController {
37
38
  this.status.next(PlayerStatus.paused);
38
39
  });
39
40
  this.playerElement.addEventListener("ended", () => {
40
- this.onStop.next();
41
- this.status.next(PlayerStatus.ended);
41
+ if (!this.options.crossfade && !this.options.loop) {
42
+ this.onStop.next();
43
+ this.status.next(PlayerStatus.ended);
44
+ }
42
45
  });
43
46
  this.playerElement.addEventListener("loadedmetadata", (event) => {
44
47
  if (this.playerElement.duration) {
@@ -54,13 +57,14 @@ export class PlayerController {
54
57
  if (this.options.autoSave) {
55
58
  this.currentTime.pipe(takeUntil(this.destroyEvent)).subscribe((currentTime) => this.savePosition(currentTime));
56
59
  }
57
- if (this.options.fadeOut && !this.options.loop) {
60
+ if (this.options.crossfade && !this.options.loop) {
58
61
  combineLatest([this.currentTime, this.totalTime])
59
62
  .pipe(takeUntil(this.destroyEvent))
60
- .pipe(takeWhile(([currentTime, totalTime]) => totalTime - currentTime < this.options.fadeInterval), take(1))
61
- .subscribe(async (currentTime) => {
62
- this.onStop.next();
63
- this.fadeOut();
63
+ .pipe(filter(([currentTime, totalTime]) => !!totalTime)) // track is loaded
64
+ .pipe(filter(([currentTime, totalTime]) => currentTime >= totalTime - this.options.crossfadeTime / 1000)) // crossfading should start
65
+ .subscribe(([currentTime, totalTime]) => {
66
+ if (this.status.value !== PlayerStatus.ended)
67
+ this.stop();
64
68
  });
65
69
  }
66
70
  if (this.options.loop) {
@@ -70,7 +74,7 @@ export class PlayerController {
70
74
  });
71
75
  }
72
76
  this.open(trackId, trackUrl);
73
- this.log("Initialized player", this.playerElement);
77
+ this.log("Initialized player", trackId);
74
78
  }
75
79
  async open(id, file) {
76
80
  this.trackId = id;
@@ -79,74 +83,96 @@ export class PlayerController {
79
83
  if (position && this.options.autoSave)
80
84
  this.playerElement.currentTime = position;
81
85
  }
82
- async destroy() {
86
+ async destroy(now = false) {
87
+ this.log("Called destroy", now ? "now" : "");
88
+ if (this.options.crossfade && now !== true) {
89
+ setTimeout(() => this.destroy(true), this.options.crossfadeTime);
90
+ return;
91
+ }
92
+ this.log("Destroying player");
83
93
  this.destroyEvent.next();
84
- await this.pause();
85
94
  this.playerElement.remove();
86
95
  }
87
- async play(options = {}) {
96
+ async play(params = { fade: this.options.crossfade }) {
88
97
  if (!this.playerElement.src)
89
98
  throw new Error("No file opened");
90
- this.log("Called play");
91
- await this.playerElement?.play();
92
- if (options.fadeIn !== undefined ? options.fadeIn : this.options.fadeIn)
93
- await this.fadeIn();
94
- }
95
- async fadeIn() {
96
- this.clearFade();
97
- return new Promise((resolve) => {
99
+ if (this.status.value === PlayerStatus.playing)
100
+ return;
101
+ this.log("Called play", params.fade ? "with fade" : "");
102
+ if (params.fade) {
98
103
  this.playerElement.volume = 0;
99
- this.fadePromiseResolve = resolve;
100
- this.fadeIntervalSubscription = setInterval(() => {
101
- if (this.playerElement.volume >= 1) {
102
- clearInterval(this.fadeIntervalSubscription);
103
- return resolve();
104
- }
105
- this.playerElement.volume = Math.min(this.playerElement.volume + (1 / this.options.fadeInterval) * 100, 1);
106
- }, 100);
107
- });
108
- }
109
- async fadeOut() {
110
- this.clearFade();
111
- return new Promise((resolve) => {
112
- this.fadePromiseResolve = resolve;
113
- this.fadeIntervalSubscription = setInterval(() => {
114
- if (this.playerElement.volume <= 0) {
115
- clearInterval(this.fadeIntervalSubscription);
116
- this.playerElement.pause();
117
- return;
118
- }
119
- this.playerElement.volume = Math.max(this.playerElement.volume - (1 / this.options.fadeInterval) * 100, 0);
120
- }, 100);
121
- });
122
- }
123
- clearFade() {
124
- clearInterval(this.fadeIntervalSubscription);
125
- this.fadePromiseResolve?.();
104
+ }
105
+ await this.playerElement?.play();
106
+ if (params.fade) {
107
+ await this.fadeToVolume(this.volume);
108
+ }
126
109
  }
127
- async pause(options = {}) {
110
+ async pause() {
128
111
  if (!this.playerElement.src)
129
112
  throw new Error("No file opened");
113
+ if (this.status.value === PlayerStatus.ended)
114
+ return;
130
115
  this.log("Called pause");
131
116
  this.playerElement?.pause();
132
- if (options.fadeIn !== undefined ? options.fadeIn : this.options.fadeIn)
133
- await this.fadeOut();
117
+ }
118
+ async stop(params = { fade: this.options.crossfade }) {
119
+ this.log("Called stop", params.fade ? "with fade" : "");
120
+ if (this.status.value !== PlayerStatus.ended) {
121
+ this.onStop.next();
122
+ this.status.next(PlayerStatus.ended);
123
+ }
124
+ if (params.fade) {
125
+ await this.fadeToVolume(0);
126
+ }
127
+ this.playerElement.pause();
134
128
  }
135
129
  seekTo(seconds) {
136
130
  if (!this.playerElement.src)
137
131
  throw new Error("No file opened");
132
+ if (this.status.value === PlayerStatus.ended)
133
+ return;
138
134
  this.log("Called seekTo");
139
135
  this.playerElement.currentTime = seconds;
140
136
  }
137
+ setVolume(volume, params = {}) {
138
+ this.log("Called set volume", volume, params.fade ? "with fade" : "");
139
+ this.volume = volume;
140
+ if (params.fade) {
141
+ this.fadeToVolume(volume);
142
+ }
143
+ else {
144
+ this.playerElement.volume = Math.max(Math.min(volume, 1), 0);
145
+ }
146
+ }
147
+ async fadeToVolume(volume) {
148
+ return new Promise((resolve, reject) => {
149
+ this.fadeCancelEvent.next();
150
+ this.volume = volume;
151
+ const fadeOutInterval = 100;
152
+ const fadeOutStep = (this.volume - this.playerElement.volume) / (this.options.crossfadeTime / fadeOutInterval);
153
+ if (fadeOutStep === 0)
154
+ return resolve();
155
+ interval(fadeOutInterval)
156
+ .pipe(takeUntil(this.fadeCancelEvent))
157
+ .pipe(takeUntil(this.destroyEvent))
158
+ .pipe(takeWhile(() => Math.abs(this.playerElement.volume - this.volume) > Math.abs(fadeOutStep)))
159
+ .subscribe({
160
+ next: () => {
161
+ this.playerElement.volume += fadeOutStep;
162
+ },
163
+ error: (error) => reject(error),
164
+ complete: () => {
165
+ this.playerElement.volume = this.volume;
166
+ resolve();
167
+ },
168
+ });
169
+ });
170
+ }
141
171
  back(seconds = 10) {
142
- if (!this.playerElement.src)
143
- throw new Error("No file opened");
144
172
  const position = this.playerElement.currentTime;
145
173
  this.seekTo(Math.max(position - seconds, 0));
146
174
  }
147
175
  forward(seconds = 10) {
148
- if (!this.playerElement.src)
149
- throw new Error("No file opened");
150
176
  const position = this.playerElement.currentTime;
151
177
  const duration = this.playerElement.duration;
152
178
  this.seekTo(duration && duration > 0 ? Math.min(position + seconds, duration) : position + seconds);
@@ -159,7 +185,7 @@ export class PlayerController {
159
185
  log(message, ...args) {
160
186
  const time = this.playerElement?.currentTime ? Math.round(this.playerElement?.currentTime) : null;
161
187
  if (time)
162
- message += ` @${time}s`;
188
+ args.push(`@${time}s`);
163
189
  console.log(`[PlayerController] ${message}`, ...args);
164
190
  }
165
191
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@audiowalk/sdk",
3
- "version": "1.5.2",
3
+ "version": "1.5.4",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/audiowalk-cz/components.git"