@tidal-music/player-web-components 0.1.0

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,21 @@
1
+ import { CredentialsProvider } from '@tidal-music/common';
2
+ import { events } from '@tidal-music/player';
3
+ import { setCredentialsProvider } from '@tidal-music/player';
4
+
5
+ export { CredentialsProvider }
6
+
7
+ export { events }
8
+
9
+ export { setCredentialsProvider }
10
+
11
+ export declare const TidalCurrentTime = "tidal-current-time";
12
+
13
+ export declare const TidalDurationTime = "tidal-duration-time";
14
+
15
+ export declare const TidalPlayButton = "tidal-play-trigger";
16
+
17
+ export declare const TidalProgressBar = "tidal-progress-bar";
18
+
19
+ export declare const TidalVideoView = "tidal-video-view";
20
+
21
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ import { e as i, f as s, m as r, p as d, q as t, y as l, X as o } from "./index-tM9JvbA8.js";
2
+ export {
3
+ i as TidalCurrentTime,
4
+ s as TidalDurationTime,
5
+ r as TidalPlayButton,
6
+ d as TidalProgressBar,
7
+ t as TidalVideoView,
8
+ l as events,
9
+ o as setCredentialsProvider
10
+ };
@@ -0,0 +1,362 @@
1
+ import { b as m, y as s, H as u, n as k, P as p, k as o, l as g, s as P, t as S, i as b } from "./index-tM9JvbA8.js";
2
+ import { Y as f, V as y, O as L } from "./basePlayer-a-avZASH-Ux-6s9Ex.js";
3
+ const E = "active-device-disconnected";
4
+ function w() {
5
+ return new CustomEvent(E);
6
+ }
7
+ const D = {
8
+ file_checksum_mismatch: "NPO02",
9
+ no_such_file: "NPO01",
10
+ unreadable_file: "NPO03"
11
+ }, N = {
12
+ devicedisconnected: "NPD01",
13
+ deviceexclusivemodenotallowed: "NPD02",
14
+ deviceformatnotsupported: "NPD03",
15
+ devicelocked: "NPD04",
16
+ devicenotfound: "NPD05",
17
+ deviceunknownerror: "NPD00"
18
+ };
19
+ let i;
20
+ class A extends f {
21
+ #s = "default";
22
+ /**
23
+ * A Boolean which is true if the media contained in the element has finished playing.
24
+ *
25
+ * (Native player sends multiple "complete" events.
26
+ * This variable should be set to true on the first call
27
+ * to be able to ignore subsequent onces; until reset
28
+ * for a new media product.)
29
+ *
30
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ended
31
+ */
32
+ #i;
33
+ #r = !0;
34
+ #e;
35
+ #a;
36
+ name = "nativePlayer";
37
+ playbackEngineHandlerAttached = !1;
38
+ constructor() {
39
+ super(), m("outputDevicesEnabled") && (async () => (i = (await import("./output-devices-NXuo75MQ-_vYYuqUm.js")).outputDevices, this.#e.listDevices()))(), this.#e = // eslint-disable-next-line @typescript-eslint/ban-ts-comment
40
+ // @ts-ignore
41
+ window.NativePlayerComponent.Player(), this.playbackState = "IDLE", this.registerEventListeners(), this.#e.setVolume(100);
42
+ }
43
+ // eslint-disable-next-line class-methods-use-this
44
+ #t(e) {
45
+ s.dispatchError(
46
+ new u("EUnexpected", N[e])
47
+ );
48
+ }
49
+ #d(e) {
50
+ this.debugLog("handleMediaError", e.target);
51
+ const t = e.target, a = D[t.errorCode];
52
+ this.currentStreamingSessionId && k({
53
+ errorCode: a,
54
+ errorMessage: JSON.stringify(e.target),
55
+ streamingSessionId: this.currentStreamingSessionId
56
+ }), s.dispatchError(new u("EUnexpected", a));
57
+ }
58
+ #n(e) {
59
+ switch (this.debugLog("handleNativePlayerStateChange", e), e) {
60
+ case "paused":
61
+ case "ready":
62
+ this.playbackState = "NOT_PLAYING";
63
+ break;
64
+ case "active":
65
+ this.playbackState = "PLAYING";
66
+ break;
67
+ case "seeking":
68
+ case "idle":
69
+ this.playbackState = "STALLED";
70
+ break;
71
+ case "uninitialized":
72
+ this.playbackState = "IDLE";
73
+ break;
74
+ case "stopped":
75
+ this.playbackState = "NOT_PLAYING";
76
+ break;
77
+ default:
78
+ this.debugLog("No handling for state", e);
79
+ break;
80
+ }
81
+ }
82
+ async #o() {
83
+ this.debugLog("handleNetworkError");
84
+ const e = p.timestamp(
85
+ "streaming_metrics:playback_statistics:actualStartTimestamp"
86
+ );
87
+ if ((e !== void 0 ? Math.abs(p.now() - e) : 0) >= 36e5) {
88
+ const t = structuredClone(this.currentMediaProduct), a = this.currentTime;
89
+ this.finishCurrentMediaProduct("error"), t && (await this.hardReload(t, a), await this.play());
90
+ return;
91
+ }
92
+ await Promise.race([
93
+ this.mediaStateChange("idle"),
94
+ new Promise((t) => {
95
+ window.addEventListener("online", () => t("online"));
96
+ })
97
+ ]) === "idle" && s.dispatchError(new u("PENetwork", "NPN01"));
98
+ }
99
+ /**
100
+ * Clean up native player before leaving for another player.
101
+ */
102
+ abandon() {
103
+ i && i.deviceMode === "exclusive" && this.#e.selectSystemDevice();
104
+ }
105
+ getPosition() {
106
+ return this.currentTime;
107
+ }
108
+ /**
109
+ * We cannot run multiple instances of native player so this function is
110
+ * for catching duration for a preloaded item in native player.
111
+ *
112
+ * I.e. wait for player to load it and emit mediaduration event, then we
113
+ * can gather the duration data and send a media product transition.
114
+ */
115
+ async handleAutomaticTransitionToPreloadedMediaProduct() {
116
+ await this.nativeEvent("mediaduration"), this.#a = void 0;
117
+ const e = o.getMediaProductTransition(
118
+ this.preloadedStreamingSessionId
119
+ );
120
+ if (!e) {
121
+ console.warn(
122
+ "No media product transition saved for next item. Stopping playback."
123
+ ), this.playbackState = "NOT_PLAYING";
124
+ return;
125
+ }
126
+ const { mediaProduct: t, playbackContext: a } = e, r = {
127
+ ...a,
128
+ actualDuration: this.#i
129
+ };
130
+ this.preloadedStreamingSessionId && o.saveMediaProductTransition(
131
+ this.preloadedStreamingSessionId,
132
+ {
133
+ mediaProduct: t,
134
+ playbackContext: r
135
+ }
136
+ ), await this.mediaStateChange("active"), s.dispatchEvent(
137
+ y(t, r)
138
+ ), this.currentStreamingSessionId = this.preloadedStreamingSessionId, this.mediaProductStarted(this.currentStreamingSessionId);
139
+ }
140
+ async load(e, t) {
141
+ this.debugLog("load", e), this.currentTime = e.assetPosition, this.startAssetPosition = e.assetPosition, await this.reset(), this.#r = !1;
142
+ const { assetPosition: a, mediaProduct: r, playbackInfo: h, streamInfo: d } = e, { securityToken: c, streamFormat: n, streamUrl: l } = d;
143
+ this.currentStreamingSessionId = d.streamingSessionId, t === "explicit" && (this.playbackState = "NOT_PLAYING");
144
+ const I = this.nativeEvent("mediaduration");
145
+ if (n)
146
+ this.#e.load(l, n, c);
147
+ else
148
+ throw new Error("Stream format is undefined.");
149
+ if (await I, this.currentStreamingSessionId !== d.streamingSessionId)
150
+ return;
151
+ this.debugLog("load() duration is", this.#i), a !== 0 && a < this.#i ? (async () => {
152
+ await this.mediaStateChange("active"), await this.seek(a), this.currentTime = a;
153
+ })().catch(console.error) : this.currentTime = 0;
154
+ const v = L({
155
+ assetPosition: a,
156
+ duration: this.#i,
157
+ playbackInfo: h,
158
+ streamInfo: d
159
+ });
160
+ o.saveMediaProductTransition(
161
+ d.streamingSessionId,
162
+ { mediaProduct: r, playbackContext: v }
163
+ ), this.debugLog("load() mediaProductTransition"), s.dispatchEvent(
164
+ y(r, v)
165
+ ), this.debugLog("load() pb NOT_PLAYING"), this.debugLog("load() done");
166
+ }
167
+ mediaStateChange(e) {
168
+ return new Promise((t) => {
169
+ this.#e.addEventListener(
170
+ "mediastate",
171
+ (a) => {
172
+ a.target === e && t(a.target);
173
+ }
174
+ );
175
+ });
176
+ }
177
+ nativeEvent(e) {
178
+ return new Promise((t) => {
179
+ this.#e.addEventListener(
180
+ e,
181
+ (a) => t(a)
182
+ );
183
+ });
184
+ }
185
+ // eslint-disable-next-line @typescript-eslint/require-await
186
+ async next(e) {
187
+ this.debugLog("next", e), this.hasNextItem() && await this.unloadPreloadedMediaProduct();
188
+ const { mediaProduct: t, playbackInfo: a, streamInfo: r } = e, { securityToken: h, streamFormat: d, streamUrl: c, streamingSessionId: n } = r;
189
+ this.preloadedStreamingSessionId = n, this.debugLog("preloading", c, "for", n), d ? this.#e.preload(c, d, h) : console.error("Stream format undefined for preload."), this.debugLog("preloading done");
190
+ const l = L({
191
+ assetPosition: 0,
192
+ duration: 0,
193
+ // TODO: Cannot get duration here, try to solve in some other way...
194
+ playbackInfo: a,
195
+ streamInfo: r
196
+ });
197
+ o.saveMediaProductTransition(n, {
198
+ mediaProduct: t,
199
+ playbackContext: l
200
+ }), this.#a = e;
201
+ }
202
+ pause() {
203
+ this.#e.pause();
204
+ }
205
+ async play() {
206
+ if (this.debugLog("play"), await this.maybeHardReload(), this.playbackState === "IDLE") {
207
+ this.debugLog("play()", this.playbackState, "returning early");
208
+ return;
209
+ }
210
+ this.setStateToXIfNotYInZMs(1e3, "PLAYING", "STALLED"), await this.updateOutputDevice(), this.mediaProductStarted(this.currentStreamingSessionId), this.debugLog("nativePlayer", "play()"), this.#e.play();
211
+ }
212
+ async playbackEngineEndedHandler(e) {
213
+ if (this.isActivePlayer) {
214
+ const { reason: t } = e.detail;
215
+ t === "completed" && (this.hasNextItem() ? await this.handleAutomaticTransitionToPreloadedMediaProduct() : (g.preloadedStreamingSessionId ? this.debugLog(
216
+ `Switching player from ${this.name} to ${g.preloadPlayer?.name}`
217
+ ) : this.debugLog("No next item queued."), this.playbackState = "NOT_PLAYING"));
218
+ }
219
+ }
220
+ registerEventListeners() {
221
+ this.debugLog("registerEventListeners"), this.#e.addEventListener("mediacurrenttime", (e) => {
222
+ this.currentTime = Number(e.target);
223
+ }), this.#e.addEventListener(
224
+ "mediastate",
225
+ (e) => {
226
+ e.target === "completed" ? this.finishCurrentMediaProduct("completed") : this.#n(e.target);
227
+ }
228
+ ), this.#e.addEventListener(
229
+ "devices",
230
+ (e) => {
231
+ i ? i.addNativeDevices(e.target) : console.error("Output devices not loaded.");
232
+ }
233
+ ), this.#e.addEventListener("devicedisconnected", () => {
234
+ s.dispatchEvent(w()), this.#t("devicedisconnected");
235
+ }), this.#e.addEventListener(
236
+ "deviceexclusivemodenotallowed",
237
+ () => this.#t("deviceexclusivemodenotallowed")
238
+ ), this.#e.addEventListener(
239
+ "deviceformatnotsupported",
240
+ () => this.#t("deviceformatnotsupported")
241
+ ), this.#e.addEventListener(
242
+ "devicelocked",
243
+ () => this.#t("devicelocked")
244
+ ), this.#e.addEventListener(
245
+ "devicenotfound",
246
+ () => this.#t("devicenotfound")
247
+ ), this.#e.addEventListener(
248
+ "deviceunknownerror",
249
+ () => this.#t("deviceunknownerror")
250
+ ), this.#e.addEventListener(
251
+ "mediaduration",
252
+ (e) => {
253
+ this.#i = Number(e.target);
254
+ }
255
+ ), this.#e.addEventListener(
256
+ "mediaerror",
257
+ (e) => this.#d(e)
258
+ ), this.#e.addEventListener("mediamaxconnectionsreached", () => {
259
+ this.#o().catch(console.error);
260
+ });
261
+ }
262
+ // eslint-disable-next-line @typescript-eslint/require-await
263
+ async reset({ keepPreload: e } = { keepPreload: !1 }) {
264
+ this.#r || (this.debugLog("reset"), e || await this.unloadPreloadedMediaProduct(), this.#e.stop(), this.playbackState !== "IDLE" && this.hasStarted() && this.finishCurrentMediaProduct("skip"), this.detachPlaybackEngineEndedHandler(), this.currentStreamingSessionId = void 0, e || (this.preloadedStreamingSessionId = void 0), this.playbackState = "IDLE", this.#r = !0);
265
+ }
266
+ async seek(e) {
267
+ this.hasStarted() || await this.mediaStateChange("active"), this.seekStart(), this.currentTime = e, this.#e.seek(e), this.seekEnd();
268
+ }
269
+ // Handles track "skip next" and progressions between shaka and native player
270
+ async skipToPreloadedMediaProduct() {
271
+ this.debugLog(
272
+ "skipToPreloadedMediaProduct",
273
+ this.preloadedStreamingSessionId
274
+ );
275
+ const e = this.currentStreamingSessionId === void 0;
276
+ if (this.preloadedStreamingSessionId && this.#a) {
277
+ const t = o.getMediaProductTransition(
278
+ this.preloadedStreamingSessionId
279
+ );
280
+ t && (this.#a.mediaProduct = t.mediaProduct), await this.load(this.#a, "implicit"), await this.updateOutputDevice(), e && (this.playbackState = "PLAYING"), this.playbackState === "IDLE" && (this.playbackState = "NOT_PLAYING");
281
+ return;
282
+ }
283
+ console.warn("No preloaded item in native player.");
284
+ }
285
+ // eslint-disable-next-line @typescript-eslint/require-await
286
+ async unloadPreloadedMediaProduct() {
287
+ this.debugLog(
288
+ "unloadPreloadedMediaProduct",
289
+ this.preloadedStreamingSessionId
290
+ ), this.hasNextItem() && (this.cleanUpStoredPreloadInfo(), "cancelPreload" in this.#e ? this.#e.cancelPreload() : console.warn("cancelPreload not available. Update native player."));
291
+ }
292
+ updateDeviceMode() {
293
+ this.updateOutputDevice()?.catch(console.error), i && s.dispatchEvent(
294
+ P(i.deviceMode)
295
+ );
296
+ }
297
+ updateOutputDevice() {
298
+ if (!i || (this.debugLog("updateOutputDevice", i.activeDevice), !i.activeDevice))
299
+ return Promise.resolve();
300
+ const { nativeDeviceId: e } = i.activeDevice;
301
+ if (this.outputDeviceType = i.activeDevice.type, e === "default")
302
+ this.#s !== "default" && (this.#e.selectSystemDevice(), s.dispatchEvent(S("default")), this.#s = "default");
303
+ else if (e) {
304
+ const t = i.getNativeDevice(e);
305
+ t && (this.#e.selectDevice(t, i.deviceMode), s.dispatchEvent(
306
+ S(i.activeDevice.id)
307
+ ), s.dispatchEvent(
308
+ P(i.deviceMode)
309
+ ), this.#s = e);
310
+ } else
311
+ console.warn(`Device with sinkId ${e} not found.`);
312
+ return Promise.resolve();
313
+ }
314
+ /**
315
+ * WIMPWC-7901
316
+ * This is a temporary fix as there is currently no way to enable
317
+ * the MQA decoder in Native Player after disabling it.
318
+ *
319
+ * Switching devices forces the Native Player to use the new device settings.
320
+ * By default `device.passThrough` is `undefined`, that's why we check explicitly for `false`.
321
+ *
322
+ * TODO: Refactor when `this._player.enableMQADecoder();` works....
323
+ */
324
+ updatePassThrough() {
325
+ if (!i)
326
+ return;
327
+ const { activeDevice: e } = i;
328
+ if (i.passThrough === !0) {
329
+ this.#e.disableMQADecoder(), s.dispatchEvent(b(!0));
330
+ return;
331
+ }
332
+ if (i.passThrough === !1)
333
+ if (this.#e.selectSystemDevice(), e.nativeDeviceId) {
334
+ const t = i.getNativeDevice(
335
+ e.nativeDeviceId
336
+ );
337
+ t && this.#e.selectDevice(
338
+ t,
339
+ i.deviceMode
340
+ );
341
+ } else
342
+ console.error(
343
+ "Passthrough could not be properly disabled since the nativeDeviceId is missing for",
344
+ e
345
+ );
346
+ this.#e.enableMQADecoder(), s.dispatchEvent(b(!1));
347
+ }
348
+ // eslint-disable-next-line class-methods-use-this
349
+ get ready() {
350
+ return Promise.resolve();
351
+ }
352
+ // eslint-disable-next-line class-methods-use-this
353
+ get volume() {
354
+ return m("desiredVolumeLevel");
355
+ }
356
+ set volume(e) {
357
+ this.debugLog("Setting volume to", e), this.#e.setVolume(e * 100);
358
+ }
359
+ }
360
+ export {
361
+ A as default
362
+ };
@@ -0,0 +1,236 @@
1
+ import { h as V, y as S, l as b, g as T } from "./index-tM9JvbA8.js";
2
+ import { l as W } from "./_commonjsHelpers-f3sTPFkQ-YKQV-mq1.js";
3
+ var q = function() {
4
+ function s(e, t, n, i, a) {
5
+ return e < t || n < t ? e > n ? n + 1 : e + 1 : i === a ? t : t + 1;
6
+ }
7
+ return function(e, t) {
8
+ if (e === t)
9
+ return 0;
10
+ if (e.length > t.length) {
11
+ var n = e;
12
+ e = t, t = n;
13
+ }
14
+ for (var i = e.length, a = t.length; i > 0 && e.charCodeAt(i - 1) === t.charCodeAt(a - 1); )
15
+ i--, a--;
16
+ for (var c = 0; c < i && e.charCodeAt(c) === t.charCodeAt(c); )
17
+ c++;
18
+ if (i -= c, a -= c, i === 0 || a < 3)
19
+ return a;
20
+ var o = 0, r, d, h, l, f, v, m, p, D, C, E, A, u = [];
21
+ for (r = 0; r < i; r++)
22
+ u.push(r + 1), u.push(e.charCodeAt(c + r));
23
+ for (var P = u.length - 1; o < a - 3; )
24
+ for (D = t.charCodeAt(c + (d = o)), C = t.charCodeAt(c + (h = o + 1)), E = t.charCodeAt(c + (l = o + 2)), A = t.charCodeAt(c + (f = o + 3)), v = o += 4, r = 0; r < P; r += 2)
25
+ m = u[r], p = u[r + 1], d = s(m, d, h, D, p), h = s(d, h, l, C, p), l = s(h, l, f, E, p), v = s(l, f, v, A, p), u[r] = v, f = l, l = h, h = d, d = m;
26
+ for (; o < a; )
27
+ for (D = t.charCodeAt(c + (d = o)), v = ++o, r = 0; r < P; r += 2)
28
+ m = u[r], u[r] = v = s(m, d, v, D, u[r + 1]), d = m;
29
+ return v;
30
+ };
31
+ }();
32
+ const O = /* @__PURE__ */ W(q);
33
+ function M(s) {
34
+ return new CustomEvent("device-change", {
35
+ detail: {
36
+ devices: s
37
+ }
38
+ });
39
+ }
40
+ const g = V.parse(navigator.userAgent);
41
+ class w {
42
+ controllableVolume;
43
+ id;
44
+ name;
45
+ nativeDeviceId;
46
+ type;
47
+ webDeviceId;
48
+ constructor({
49
+ controllableVolume: e,
50
+ name: t,
51
+ nativeDeviceId: n,
52
+ type: i,
53
+ webDeviceId: a
54
+ }) {
55
+ this.name = t, this.id = n === "default" && a === "default" ? "default" : T(), this.nativeDeviceId = n, this.webDeviceId = a, this.type = i, this.controllableVolume = e !== !1;
56
+ }
57
+ }
58
+ function y(s) {
59
+ if (B(s))
60
+ return "windowsCommunication";
61
+ if ("id" in s && s.id === "BuiltInSpeakerDevice")
62
+ return "builtIn";
63
+ if ("type" in s) {
64
+ if (s.type === "airplay")
65
+ return "airplay";
66
+ if (s.type === "mqa")
67
+ return "mqa";
68
+ }
69
+ if ("label" in s) {
70
+ const e = s.label.toLowerCase();
71
+ if (e.includes("bluetooth"))
72
+ return "bluetooth";
73
+ if (e.includes("displayport"))
74
+ return "displayPort";
75
+ if (e.includes("hdmi"))
76
+ return "hdmi";
77
+ if (e.includes("usb"))
78
+ return "usb";
79
+ if (e.includes("built-in"))
80
+ return "builtIn";
81
+ if (e.includes("airplay"))
82
+ return "airplay";
83
+ }
84
+ }
85
+ function I(s, e) {
86
+ const t = e.toLowerCase();
87
+ let n = s;
88
+ return t.includes("mac") && (n = s.split("(")[0].trim()), n;
89
+ }
90
+ function B(s) {
91
+ let e;
92
+ return "label" in s && (e = s.label), "name" in s && (e = s.name), e !== void 0 && e.startsWith("Communications");
93
+ }
94
+ function L(s, e) {
95
+ if (e = I(e, g.os.name || ""), [...s].length === 0 || e === "")
96
+ return;
97
+ const t = [...s].filter((i) => i.name === e)[0];
98
+ if (t)
99
+ return t;
100
+ const n = [...s].filter((i) => e.includes(i.name) || i.name.includes(e)).map((i) => ({
101
+ device: i,
102
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
103
+ distance: O(i.name, e)
104
+ })).sort((i, a) => i.distance - a.distance).reverse();
105
+ if (n.length > 0) {
106
+ const i = n.pop();
107
+ if (i && i.distance <= 16)
108
+ return i.device;
109
+ }
110
+ }
111
+ class N {
112
+ #i;
113
+ #s;
114
+ #n = "shared";
115
+ #e;
116
+ #t;
117
+ #a = void 0;
118
+ #r;
119
+ outputDevices;
120
+ constructor() {
121
+ this.#t = /* @__PURE__ */ new Set(), this.#r = /* @__PURE__ */ new Set(), this.#e = new EventTarget(), this.#s = new w({
122
+ name: "System Default",
123
+ nativeDeviceId: "default",
124
+ type: "systemDefault",
125
+ webDeviceId: "default"
126
+ }), this.#i = this.#s, this.outputDevices = /* @__PURE__ */ new Set([this.#s]), this.hydrateWebDevices().then().catch(console.error), navigator.mediaDevices.addEventListener("devicechange", () => {
127
+ this.hydrateWebDevices().then().catch(console.error);
128
+ }), this.#e.addEventListener("native-devices", (e) => {
129
+ this.#t = new Set(e.detail), this.queueUpdate().then().catch(console.error);
130
+ }), this.#e.addEventListener("web-devices", (e) => {
131
+ this.#r = new Set(e.detail), this.queueUpdate().then().catch(console.error);
132
+ });
133
+ }
134
+ addNativeDevices(e) {
135
+ this.#e.dispatchEvent(
136
+ new CustomEvent("native-devices", {
137
+ detail: e
138
+ })
139
+ );
140
+ }
141
+ addWebDevices(e) {
142
+ e = e.filter((t) => t.deviceId !== "default"), this.#e.dispatchEvent(
143
+ new CustomEvent("web-devices", {
144
+ detail: e
145
+ })
146
+ );
147
+ }
148
+ emitDeviceChange() {
149
+ S.dispatchEvent(M([...this.outputDevices]));
150
+ }
151
+ getNativeDevice(e) {
152
+ return [...this.#t].find((t) => t.id === e);
153
+ }
154
+ async hydrateWebDevices() {
155
+ const e = (await navigator.mediaDevices.enumerateDevices()).filter((t) => t.kind === "audiooutput");
156
+ this.addWebDevices(e);
157
+ }
158
+ mergeDevices() {
159
+ [...this.outputDevices].filter((e) => e.id !== "default").forEach((e) => {
160
+ e.nativeDeviceId = void 0, e.webDeviceId = void 0;
161
+ }), this.#t.forEach((e) => {
162
+ const t = L(this.outputDevices, e.name);
163
+ t ? (t.nativeDeviceId = e.id, t.controllableVolume = e.controllableVolume, t.type = y(e) || t.type) : this.outputDevices.add(
164
+ new w({
165
+ controllableVolume: e.controllableVolume,
166
+ name: I(e.name, g.os.name || ""),
167
+ nativeDeviceId: e.id,
168
+ type: y(e)
169
+ })
170
+ );
171
+ }), this.#r.forEach((e) => {
172
+ const t = L(this.outputDevices, e.label);
173
+ t ? (t.webDeviceId = e.deviceId, t.type = y(e) || t.type) : this.outputDevices.add(
174
+ new w({
175
+ name: I(e.label, g.os.name || ""),
176
+ type: y(e),
177
+ webDeviceId: e.deviceId
178
+ })
179
+ );
180
+ }), [...this.outputDevices].filter(
181
+ (e) => e.webDeviceId === void 0 && e.nativeDeviceId === void 0 || e.type === "airplay" || e.type === "windowsCommunication"
182
+ ).forEach((e) => this.outputDevices.delete(e));
183
+ }
184
+ async queueUpdate() {
185
+ const e = new Promise(
186
+ (i) => this.#e.addEventListener(
187
+ "native-devices",
188
+ (a) => i(a.detail),
189
+ { once: !0 }
190
+ )
191
+ ), t = new Promise(
192
+ (i) => this.#e.addEventListener(
193
+ "web-devices",
194
+ (a) => i(a.detail),
195
+ { once: !0 }
196
+ )
197
+ ), n = (i) => new Promise((a) => setTimeout(() => a(), i));
198
+ await Promise.any([e, t, n(1e3)]), this.mergeDevices(), this.emitDeviceChange();
199
+ }
200
+ set activeDevice(e) {
201
+ this.#i = e, this.#a = !1, this.#n = "shared", b.activePlayer?.updateOutputDevice()?.catch(console.error);
202
+ }
203
+ get activeDevice() {
204
+ return this.#i;
205
+ }
206
+ /**
207
+ * Set the current device mode for the output device.
208
+ */
209
+ set deviceMode(e) {
210
+ const { activeDevice: t } = this, { activePlayer: n } = b;
211
+ t && n && n.name === "nativePlayer" && this.deviceMode !== e && (this.#n = e, n.updateDeviceMode());
212
+ }
213
+ get deviceMode() {
214
+ return this.#n;
215
+ }
216
+ /**
217
+ * Set to true to disable software MQA decoder.
218
+ */
219
+ set passThrough(e) {
220
+ const { activeDevice: t } = this, { activePlayer: n } = b;
221
+ t && n && n.name === "nativePlayer" && this.passThrough !== e && (this.#a = e, n.updatePassThrough());
222
+ }
223
+ get passThrough() {
224
+ return !!this.#a;
225
+ }
226
+ }
227
+ const $ = new N();
228
+ export {
229
+ w as OutputDevice,
230
+ N as OutputDevices,
231
+ y as findOutputType,
232
+ L as getOutputDeviceByName,
233
+ B as isWindowsCommunicationsDevice,
234
+ I as marshalLabel,
235
+ $ as outputDevices
236
+ };