@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.
- package/dist/helpers/objects.d.ts +2 -0
- package/dist/helpers/objects.d.ts.map +1 -0
- package/dist/helpers/objects.js +1 -0
- package/dist/player/media-controls.js +1 -1
- package/dist/player/player-controller.d.ts +19 -16
- package/dist/player/player-controller.d.ts.map +1 -1
- package/dist/player/player-controller.js +86 -60
- package/package.json +1 -1
|
@@ -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:
|
|
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
|
-
|
|
7
|
-
|
|
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
|
|
31
|
-
private
|
|
32
|
-
constructor(trackId: string, trackUrl: string, options?:
|
|
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(
|
|
36
|
-
|
|
35
|
+
destroy(now?: boolean): Promise<void>;
|
|
36
|
+
play(params?: {
|
|
37
|
+
fade?: boolean;
|
|
37
38
|
}): Promise<void>;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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,
|
|
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,
|
|
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(
|
|
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
|
-
|
|
23
|
+
crossfadeTime: 2000,
|
|
24
24
|
};
|
|
25
25
|
destroyEvent = new Subject();
|
|
26
|
-
|
|
27
|
-
|
|
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.
|
|
41
|
-
|
|
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.
|
|
60
|
+
if (this.options.crossfade && !this.options.loop) {
|
|
58
61
|
combineLatest([this.currentTime, this.totalTime])
|
|
59
62
|
.pipe(takeUntil(this.destroyEvent))
|
|
60
|
-
.pipe(
|
|
61
|
-
.
|
|
62
|
-
|
|
63
|
-
this.
|
|
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",
|
|
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(
|
|
96
|
+
async play(params = { fade: this.options.crossfade }) {
|
|
88
97
|
if (!this.playerElement.src)
|
|
89
98
|
throw new Error("No file opened");
|
|
90
|
-
this.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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(
|
|
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
|
-
|
|
133
|
-
|
|
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
|
-
|
|
188
|
+
args.push(`@${time}s`);
|
|
163
189
|
console.log(`[PlayerController] ${message}`, ...args);
|
|
164
190
|
}
|
|
165
191
|
}
|