tiny-essentials 1.28.6 → 1.28.8
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/changelog/1/28/6.md +3 -0
- package/changelog/1/28/7.md +7 -0
- package/changelog/1/28/8.md +17 -0
- package/dist/v1/TinyBasicsEs.min.js +1 -1
- package/dist/v1/TinyDebugger.min.js +1 -0
- package/dist/v1/TinyEssentials.min.js +1 -1
- package/dist/v1/TinyMediaPlayer.min.js +1 -1
- package/dist/v1/TinyRadioFm.min.js +1 -1
- package/dist/v1/TinyUploadClicker.min.js +1 -1
- package/dist/v1/basics/browserDetector.cjs +228 -0
- package/dist/v1/basics/browserDetector.d.mts +66 -0
- package/dist/v1/basics/browserDetector.mjs +207 -0
- package/dist/v1/basics/index.cjs +6 -0
- package/dist/v1/basics/index.d.mts +2 -1
- package/dist/v1/basics/index.mjs +2 -1
- package/dist/v1/build/TinyDebugger.cjs +7 -0
- package/dist/v1/build/TinyDebugger.d.mts +3 -0
- package/dist/v1/build/TinyDebugger.mjs +2 -0
- package/dist/v1/index.cjs +8 -0
- package/dist/v1/index.d.mts +3 -1
- package/dist/v1/index.mjs +4 -2
- package/dist/v1/libs/TinyDebugger.cjs +380 -0
- package/dist/v1/libs/TinyDebugger.d.mts +132 -0
- package/dist/v1/libs/TinyDebugger.mjs +351 -0
- package/dist/v1/libs/TinyMediaPlayer/HtmlAudio.cjs +66 -3
- package/dist/v1/libs/TinyMediaPlayer/HtmlAudio.d.mts +25 -0
- package/dist/v1/libs/TinyMediaPlayer/HtmlAudio.mjs +59 -3
- package/dist/v1/libs/TinyMediaPlayer/HtmlVideo.cjs +66 -3
- package/dist/v1/libs/TinyMediaPlayer/HtmlVideo.d.mts +25 -0
- package/dist/v1/libs/TinyMediaPlayer/HtmlVideo.mjs +59 -3
- package/dist/v1/libs/TinyMediaPlayer/Mock.cjs +333 -0
- package/dist/v1/libs/TinyMediaPlayer/Mock.d.mts +154 -0
- package/dist/v1/libs/TinyMediaPlayer/Mock.mjs +296 -0
- package/dist/v1/libs/TinyMediaPlayer/SoundCloud.cjs +85 -1
- package/dist/v1/libs/TinyMediaPlayer/SoundCloud.d.mts +25 -0
- package/dist/v1/libs/TinyMediaPlayer/SoundCloud.mjs +75 -1
- package/dist/v1/libs/TinyMediaPlayer/Youtube.cjs +66 -3
- package/dist/v1/libs/TinyMediaPlayer/Youtube.d.mts +25 -0
- package/dist/v1/libs/TinyMediaPlayer/Youtube.mjs +56 -3
- package/dist/v1/libs/TinyMediaPlayer/index.cjs +45 -0
- package/dist/v1/libs/TinyMediaPlayer/index.d.mts +25 -0
- package/dist/v1/libs/TinyMediaPlayer/index.mjs +40 -0
- package/dist/v1/libs/TinyMediaPlayer.cjs +172 -10
- package/dist/v1/libs/TinyMediaPlayer.d.mts +47 -2
- package/dist/v1/libs/TinyMediaPlayer.mjs +160 -10
- package/dist/v1/libs/TinyPromiseQueue.cjs +6 -4
- package/dist/v1/libs/TinyPromiseQueue.d.mts +8 -6
- package/dist/v1/libs/TinyPromiseQueue.mjs +6 -4
- package/dist/v1/libs/TinyRadioFm.cjs +271 -138
- package/dist/v1/libs/TinyRadioFm.d.mts +67 -3
- package/dist/v1/libs/TinyRadioFm.mjs +239 -123
- package/docs/v1/libs/TinyMediaPlayer.md +52 -54
- package/docs/v1/libs/TinyRadioFm.md +25 -3
- package/package.json +13 -1
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { createCheckDestroyed } from '../utils.mjs';
|
|
2
|
+
import { BaseMediaAdapter } from './index.mjs';
|
|
3
|
+
import { isValidObj } from '../../basics/objChecker.mjs';
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import('../../basics/mediaContent.mjs').PictureDataType} PictureDataType
|
|
6
|
+
* @typedef {import('../../basics/mediaContent.mjs').MediaContent<PictureDataType>} MediaContent
|
|
7
|
+
* @typedef {import('./index.mjs').ContentData} ContentData
|
|
8
|
+
* @typedef {import('./index.mjs').ContentTimeData} ContentTimeData
|
|
9
|
+
*/
|
|
10
|
+
const checkDestroy = createCheckDestroyed('MockMediaAdapter');
|
|
11
|
+
const createdAt = new Date().toISOString();
|
|
12
|
+
/**
|
|
13
|
+
* MOCK VERSION of BaseMediaAdapter
|
|
14
|
+
*
|
|
15
|
+
* This file simulates the behavior of the Media Adapter for testing purposes.
|
|
16
|
+
*/
|
|
17
|
+
class MockMediaAdapter extends BaseMediaAdapter {
|
|
18
|
+
get id() {
|
|
19
|
+
return 'mock';
|
|
20
|
+
}
|
|
21
|
+
#isReady = true;
|
|
22
|
+
#currentVolume = 1.0;
|
|
23
|
+
/** @type {string|null} */
|
|
24
|
+
#currentContentId = null;
|
|
25
|
+
#destroyed = false;
|
|
26
|
+
#isPlaying = false;
|
|
27
|
+
#isPaused = false;
|
|
28
|
+
#isEnded = false;
|
|
29
|
+
#isMuted = false;
|
|
30
|
+
#currentTime = 0; // in milliseconds
|
|
31
|
+
#totalDuration = 60000; // 60 seconds in milliseconds
|
|
32
|
+
/**
|
|
33
|
+
* @returns {boolean}
|
|
34
|
+
*/
|
|
35
|
+
get destroyed() {
|
|
36
|
+
return this.#destroyed;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* @returns {boolean}
|
|
40
|
+
*/
|
|
41
|
+
get isPlaying() {
|
|
42
|
+
checkDestroy(this.#destroyed);
|
|
43
|
+
return this.#isPlaying;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* @returns {string|null}
|
|
47
|
+
*/
|
|
48
|
+
get currentContentId() {
|
|
49
|
+
checkDestroy(this.#destroyed);
|
|
50
|
+
return this.#currentContentId;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* @returns {number}
|
|
54
|
+
*/
|
|
55
|
+
get volume() {
|
|
56
|
+
checkDestroy(this.#destroyed);
|
|
57
|
+
return this.#currentVolume;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* @param {number} value - O volume de 0.0 a 1.0.
|
|
61
|
+
* @throws {RangeError} Se o valor estiver fora do intervalo.
|
|
62
|
+
*/
|
|
63
|
+
set volume(value) {
|
|
64
|
+
checkDestroy(this.#destroyed);
|
|
65
|
+
if (typeof value !== 'number' || value < 0 || value > 1) {
|
|
66
|
+
throw new RangeError('Volume must be a number between 0.0 and 1.0.');
|
|
67
|
+
}
|
|
68
|
+
this.#currentVolume = value;
|
|
69
|
+
this.emit('volumeChange', value);
|
|
70
|
+
}
|
|
71
|
+
constructor() {
|
|
72
|
+
super();
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Checks whether the media adapter is fully initialized and ready.
|
|
76
|
+
* @returns {boolean} True if the adapter is ready, false otherwise.
|
|
77
|
+
*/
|
|
78
|
+
isReady() {
|
|
79
|
+
checkDestroy(this.#destroyed);
|
|
80
|
+
return this.#isReady;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Checks whether the media adapter is paused.
|
|
84
|
+
* @returns {boolean} True if the adapter is ready, false otherwise.
|
|
85
|
+
*/
|
|
86
|
+
isPaused() {
|
|
87
|
+
checkDestroy(this.#destroyed);
|
|
88
|
+
return this.#isPaused;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Checks whether the media adapter is ended.
|
|
92
|
+
* @returns {boolean} True if the adapter is ready, false otherwise.
|
|
93
|
+
*/
|
|
94
|
+
isEnded() {
|
|
95
|
+
checkDestroy(this.#destroyed);
|
|
96
|
+
return this.#isEnded;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Asynchronously waits until the content media adapter is fully initialized and ready.
|
|
100
|
+
* @returns {Promise<void>} A promise that resolves once the adapter is ready.
|
|
101
|
+
*/
|
|
102
|
+
async waitIsReady() {
|
|
103
|
+
checkDestroy(this.#destroyed);
|
|
104
|
+
return Promise.resolve();
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Retrieves a consolidated object containing all time-related metrics for the current media content.
|
|
108
|
+
* @returns {ContentTimeData} An object containing total, current, remaining time, and playback percentage.
|
|
109
|
+
*/
|
|
110
|
+
getTimeData() {
|
|
111
|
+
checkDestroy(this.#destroyed);
|
|
112
|
+
const total = this.getTotalDuration();
|
|
113
|
+
const current = this.getCurrentTime();
|
|
114
|
+
const remaining = total > 0 ? total - current : 0;
|
|
115
|
+
const playbackPercentage = total > 0 ? (current / total) * 100 : 0;
|
|
116
|
+
return { total, current, remaining, playbackPercentage };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Determines if this adapter can play the provided content.
|
|
120
|
+
* @param {MediaContent} content - The media content to evaluate.
|
|
121
|
+
* @returns {boolean} True if the adapter can handle the content, false otherwise.
|
|
122
|
+
*/
|
|
123
|
+
static canHandle(content) {
|
|
124
|
+
return content && typeof content.url === 'string';
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Determines if this adapter can play the provided content.
|
|
128
|
+
* @param {MediaContent} content - The media content to evaluate.
|
|
129
|
+
* @returns {boolean} True if the adapter can handle the content, false otherwise.
|
|
130
|
+
*/
|
|
131
|
+
canHandle(content) {
|
|
132
|
+
checkDestroy(this.#destroyed);
|
|
133
|
+
return MockMediaAdapter.canHandle(content);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Starts or resumes playback of the provided content.
|
|
137
|
+
* @param {MediaContent} content - The media content to play.
|
|
138
|
+
* @returns {Promise<void>}
|
|
139
|
+
*/
|
|
140
|
+
async play(content) {
|
|
141
|
+
checkDestroy(this.#destroyed);
|
|
142
|
+
if (!isValidObj(content)) {
|
|
143
|
+
throw new TypeError('Invalid media content provided to play().');
|
|
144
|
+
}
|
|
145
|
+
this.#currentContentId = typeof content.url === 'string' ? content.url : 'mock';
|
|
146
|
+
this.#isPaused = false;
|
|
147
|
+
this.#isEnded = false;
|
|
148
|
+
this.#isPlaying = true;
|
|
149
|
+
// Simulate a small delay for loading
|
|
150
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
151
|
+
this.emit('play');
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Pauses the current playback.
|
|
155
|
+
* @returns {Promise<void>}
|
|
156
|
+
*/
|
|
157
|
+
async pause() {
|
|
158
|
+
checkDestroy(this.#destroyed);
|
|
159
|
+
this.#isPaused = true;
|
|
160
|
+
this.#isEnded = false;
|
|
161
|
+
this.#isPlaying = false;
|
|
162
|
+
this.emit('pause');
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Stops the playback completely and resets the internal platform state.
|
|
166
|
+
* @returns {Promise<void>}
|
|
167
|
+
*/
|
|
168
|
+
async stop() {
|
|
169
|
+
checkDestroy(this.#destroyed);
|
|
170
|
+
this.#isPaused = false;
|
|
171
|
+
this.#isEnded = true;
|
|
172
|
+
this.#isPlaying = false;
|
|
173
|
+
this.#currentTime = 0;
|
|
174
|
+
this.emit('ended');
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Seeks to a specific time in the media timeline.
|
|
178
|
+
* @param {number} timeMs - The target time in milliseconds.
|
|
179
|
+
* @returns {Promise<void>}
|
|
180
|
+
*/
|
|
181
|
+
async seek(timeMs) {
|
|
182
|
+
checkDestroy(this.#destroyed);
|
|
183
|
+
if (typeof timeMs !== 'number') {
|
|
184
|
+
throw new TypeError('Seek time must be a number.');
|
|
185
|
+
}
|
|
186
|
+
this.#currentTime = timeMs;
|
|
187
|
+
this.emit('seek', this.#currentTime);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Checks whether the player is currently muted.
|
|
191
|
+
* @returns {boolean|null} True if muted, false otherwise.
|
|
192
|
+
*/
|
|
193
|
+
isMuted() {
|
|
194
|
+
checkDestroy(this.#destroyed);
|
|
195
|
+
return this.#isMuted;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Mutes the current playback.
|
|
199
|
+
* @returns {Promise<void>}
|
|
200
|
+
*/
|
|
201
|
+
async mute() {
|
|
202
|
+
checkDestroy(this.#destroyed);
|
|
203
|
+
this.#isMuted = true;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Unmutes the current playback.
|
|
207
|
+
* @returns {Promise<void>}
|
|
208
|
+
*/
|
|
209
|
+
async unMute() {
|
|
210
|
+
checkDestroy(this.#destroyed);
|
|
211
|
+
this.#isMuted = false;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Retrieves the current playback time from the underlying API.
|
|
215
|
+
* @returns {number} The current time in milliseconds.
|
|
216
|
+
*/
|
|
217
|
+
getCurrentTime() {
|
|
218
|
+
checkDestroy(this.#destroyed);
|
|
219
|
+
return this.#currentTime;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Gets the total duration of the content.
|
|
223
|
+
* @returns {number} The total duration in milliseconds.
|
|
224
|
+
*/
|
|
225
|
+
getTotalDuration() {
|
|
226
|
+
checkDestroy(this.#destroyed);
|
|
227
|
+
return this.#totalDuration;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Gets the remaining time until the content ends.
|
|
231
|
+
* @returns {number} The remaining time in milliseconds.
|
|
232
|
+
*/
|
|
233
|
+
getRemainingTime() {
|
|
234
|
+
checkDestroy(this.#destroyed);
|
|
235
|
+
const total = this.getTotalDuration();
|
|
236
|
+
const current = this.getCurrentTime();
|
|
237
|
+
return total > 0 ? total - current : 0;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Gets the percentage of the content that has been played.
|
|
241
|
+
* @returns {number} The percentage from 0 to 100.
|
|
242
|
+
*/
|
|
243
|
+
getPlaybackPercentage() {
|
|
244
|
+
checkDestroy(this.#destroyed);
|
|
245
|
+
const total = this.getTotalDuration();
|
|
246
|
+
const current = this.getCurrentTime();
|
|
247
|
+
return total > 0 ? (current / total) * 100 : 0;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Sets the playback volume for the underlying API.
|
|
251
|
+
* @param {number} volume - The volume level from 0.0 to 1.0.
|
|
252
|
+
* @returns {void}
|
|
253
|
+
*/
|
|
254
|
+
setVolume(volume) {
|
|
255
|
+
checkDestroy(this.#destroyed);
|
|
256
|
+
this.volume = volume;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Gets the playback volume for the underlying API.
|
|
260
|
+
* @returns {number} - The volume level from 0.0 to 1.0.
|
|
261
|
+
*/
|
|
262
|
+
getVolume() {
|
|
263
|
+
checkDestroy(this.#destroyed);
|
|
264
|
+
return this.volume;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Retrieves the metadata of the currently loaded content, returning a structured
|
|
268
|
+
* object containing details such as the content ID, title, author, and duration.
|
|
269
|
+
* @returns {Promise<ContentData>}
|
|
270
|
+
*/
|
|
271
|
+
async getContentData() {
|
|
272
|
+
checkDestroy(this.#destroyed);
|
|
273
|
+
return {
|
|
274
|
+
id: this.#currentContentId ?? 'mock',
|
|
275
|
+
createdAt,
|
|
276
|
+
artistId: 'mock-artist',
|
|
277
|
+
artistName: 'Mock Artist',
|
|
278
|
+
description: 'Mock description',
|
|
279
|
+
title: 'Mock Title',
|
|
280
|
+
duration: this.#totalDuration,
|
|
281
|
+
avatar: '',
|
|
282
|
+
url: '',
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Cleans up the instance.
|
|
287
|
+
* @returns {void}
|
|
288
|
+
*/
|
|
289
|
+
destroy() {
|
|
290
|
+
if (this.#destroyed)
|
|
291
|
+
return;
|
|
292
|
+
this.#destroyed = true;
|
|
293
|
+
super.destroy();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
export { MockMediaAdapter };
|
|
@@ -284,6 +284,18 @@ class SoundCloudMediaAdapter extends index.BaseMediaAdapter {
|
|
|
284
284
|
/** @type {number} */
|
|
285
285
|
#cachedPosition = 0;
|
|
286
286
|
|
|
287
|
+
/** @type {boolean} */
|
|
288
|
+
#isMuted = false;
|
|
289
|
+
|
|
290
|
+
/** @type {number} */
|
|
291
|
+
#preMuteVolume = 1.0;
|
|
292
|
+
|
|
293
|
+
/** @type {boolean} */
|
|
294
|
+
#isPaused = false;
|
|
295
|
+
|
|
296
|
+
/** @type {boolean} */
|
|
297
|
+
#isEnded = false;
|
|
298
|
+
|
|
287
299
|
/**
|
|
288
300
|
* Gets whether the adapter has been destroyed.
|
|
289
301
|
* @returns {boolean}
|
|
@@ -363,6 +375,24 @@ class SoundCloudMediaAdapter extends index.BaseMediaAdapter {
|
|
|
363
375
|
return this.#isReady;
|
|
364
376
|
}
|
|
365
377
|
|
|
378
|
+
/**
|
|
379
|
+
* Checks whether the media adapter is paused.
|
|
380
|
+
* @returns {boolean} True if the adapter is ready, false otherwise.
|
|
381
|
+
*/
|
|
382
|
+
isPaused() {
|
|
383
|
+
checkDestroy(this.#destroyed);
|
|
384
|
+
return this.#isPaused;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Checks whether the media adapter is ended.
|
|
389
|
+
* @returns {boolean} True if the adapter is ready, false otherwise.
|
|
390
|
+
*/
|
|
391
|
+
isEnded() {
|
|
392
|
+
checkDestroy(this.#destroyed);
|
|
393
|
+
return this.#isEnded;
|
|
394
|
+
}
|
|
395
|
+
|
|
366
396
|
/**
|
|
367
397
|
* Asynchronously waits until the SoundCloud media adapter is fully initialized and ready.
|
|
368
398
|
* @returns {Promise<void>} A promise that resolves once the adapter is ready.
|
|
@@ -449,7 +479,7 @@ class SoundCloudMediaAdapter extends index.BaseMediaAdapter {
|
|
|
449
479
|
|
|
450
480
|
// Command to load the new song
|
|
451
481
|
this.#widget?.load(`https://api.soundcloud.com/tracks/${targetId}`);
|
|
452
|
-
await promiseUtils.waitForTrue(() => true,
|
|
482
|
+
await promiseUtils.waitForTrue(() => true, 700);
|
|
453
483
|
/** @type {Partial<SoundObject>} */
|
|
454
484
|
let sound = {};
|
|
455
485
|
|
|
@@ -508,6 +538,13 @@ class SoundCloudMediaAdapter extends index.BaseMediaAdapter {
|
|
|
508
538
|
|
|
509
539
|
const eventSync = (/** @type {any} */ event, /** @type {string} */ eventName) => {
|
|
510
540
|
if (!event) return;
|
|
541
|
+
if (this.#isMuted)
|
|
542
|
+
this.#widget?.getVolume((volume) => {
|
|
543
|
+
if (this.#isMuted && volume > 0) {
|
|
544
|
+
this.#isMuted = false;
|
|
545
|
+
this.volume = this.#preMuteVolume;
|
|
546
|
+
}
|
|
547
|
+
});
|
|
511
548
|
if (typeof event.currentPosition === 'number') this.#cachedPosition = event.currentPosition;
|
|
512
549
|
if (typeof event.loadedProgress === 'number') this.#loadedProgress = event.loadedProgress;
|
|
513
550
|
if (typeof event.soundId === 'number') this.#currentContentId = String(event.soundId);
|
|
@@ -525,16 +562,22 @@ class SoundCloudMediaAdapter extends index.BaseMediaAdapter {
|
|
|
525
562
|
|
|
526
563
|
this.#widget.bind(SoundCloudMediaAdapter.Events.PLAY, (/** @type {any} */ event) => {
|
|
527
564
|
eventSync(event);
|
|
565
|
+
this.#isPaused = false;
|
|
566
|
+
this.#isEnded = false;
|
|
528
567
|
this.#masterEmitter?.emit('play');
|
|
529
568
|
});
|
|
530
569
|
|
|
531
570
|
this.#widget.bind(SoundCloudMediaAdapter.Events.PAUSE, (/** @type {any} */ event) => {
|
|
532
571
|
eventSync(event);
|
|
572
|
+
this.#isPaused = true;
|
|
573
|
+
this.#isEnded = false;
|
|
533
574
|
this.#masterEmitter?.emit('pause');
|
|
534
575
|
});
|
|
535
576
|
|
|
536
577
|
this.#widget.bind(SoundCloudMediaAdapter.Events.FINISH, (/** @type {any} */ event) => {
|
|
537
578
|
eventSync(event);
|
|
579
|
+
this.#isPaused = false;
|
|
580
|
+
this.#isEnded = true;
|
|
538
581
|
this.#masterEmitter?.emit('ended');
|
|
539
582
|
});
|
|
540
583
|
|
|
@@ -640,6 +683,47 @@ class SoundCloudMediaAdapter extends index.BaseMediaAdapter {
|
|
|
640
683
|
this.#widget?.seekTo(timeMs);
|
|
641
684
|
}
|
|
642
685
|
|
|
686
|
+
/**
|
|
687
|
+
* Checks whether the player is currently muted.
|
|
688
|
+
* @returns {boolean} True if muted, false otherwise.
|
|
689
|
+
*/
|
|
690
|
+
isMuted() {
|
|
691
|
+
checkDestroy(this.#destroyed);
|
|
692
|
+
return this.#isMuted;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* Mutes the current playback.
|
|
697
|
+
* @returns {Promise<void>}
|
|
698
|
+
*/
|
|
699
|
+
async mute() {
|
|
700
|
+
checkDestroy(this.#destroyed);
|
|
701
|
+
if (this.#isMuted) return;
|
|
702
|
+
|
|
703
|
+
this.#isMuted = true;
|
|
704
|
+
this.#preMuteVolume = this.#currentVolume;
|
|
705
|
+
|
|
706
|
+
// We update the state directly to avoid the setter's logic
|
|
707
|
+
// overwriting preMuteVolume during the transition.
|
|
708
|
+
this.#currentVolume = 0;
|
|
709
|
+
if (this.#widget && typeof this.#widget.setVolume === 'function') {
|
|
710
|
+
this.#widget.setVolume(0);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Unmutes the current playback.
|
|
716
|
+
* @returns {Promise<void>}
|
|
717
|
+
*/
|
|
718
|
+
async unMute() {
|
|
719
|
+
checkDestroy(this.#destroyed);
|
|
720
|
+
if (!this.#isMuted) return;
|
|
721
|
+
|
|
722
|
+
this.#isMuted = false;
|
|
723
|
+
// This will trigger the 'set volume' logic which applies the restored volume
|
|
724
|
+
this.volume = this.#preMuteVolume;
|
|
725
|
+
}
|
|
726
|
+
|
|
643
727
|
/**
|
|
644
728
|
* Retrieves the current playback time.
|
|
645
729
|
* @returns {number} The current time in milliseconds.
|
|
@@ -187,6 +187,16 @@ declare class SoundCloudMediaAdapter extends BaseMediaAdapter {
|
|
|
187
187
|
* @returns {boolean} True if the adapter is ready, false otherwise.
|
|
188
188
|
*/
|
|
189
189
|
isReady(): boolean;
|
|
190
|
+
/**
|
|
191
|
+
* Checks whether the media adapter is paused.
|
|
192
|
+
* @returns {boolean} True if the adapter is ready, false otherwise.
|
|
193
|
+
*/
|
|
194
|
+
isPaused(): boolean;
|
|
195
|
+
/**
|
|
196
|
+
* Checks whether the media adapter is ended.
|
|
197
|
+
* @returns {boolean} True if the adapter is ready, false otherwise.
|
|
198
|
+
*/
|
|
199
|
+
isEnded(): boolean;
|
|
190
200
|
/**
|
|
191
201
|
* Asynchronously waits until the SoundCloud media adapter is fully initialized and ready.
|
|
192
202
|
* @returns {Promise<void>} A promise that resolves once the adapter is ready.
|
|
@@ -237,6 +247,21 @@ declare class SoundCloudMediaAdapter extends BaseMediaAdapter {
|
|
|
237
247
|
* @throws {TypeError} If timeMs is not a number.
|
|
238
248
|
*/
|
|
239
249
|
seek(timeMs: number): Promise<void>;
|
|
250
|
+
/**
|
|
251
|
+
* Checks whether the player is currently muted.
|
|
252
|
+
* @returns {boolean} True if muted, false otherwise.
|
|
253
|
+
*/
|
|
254
|
+
isMuted(): boolean;
|
|
255
|
+
/**
|
|
256
|
+
* Mutes the current playback.
|
|
257
|
+
* @returns {Promise<void>}
|
|
258
|
+
*/
|
|
259
|
+
mute(): Promise<void>;
|
|
260
|
+
/**
|
|
261
|
+
* Unmutes the current playback.
|
|
262
|
+
* @returns {Promise<void>}
|
|
263
|
+
*/
|
|
264
|
+
unMute(): Promise<void>;
|
|
240
265
|
/**
|
|
241
266
|
* Retrieves the current playback time.
|
|
242
267
|
* @returns {number} The current time in milliseconds.
|
|
@@ -249,6 +249,14 @@ class SoundCloudMediaAdapter extends BaseMediaAdapter {
|
|
|
249
249
|
#cachedDuration = 0;
|
|
250
250
|
/** @type {number} */
|
|
251
251
|
#cachedPosition = 0;
|
|
252
|
+
/** @type {boolean} */
|
|
253
|
+
#isMuted = false;
|
|
254
|
+
/** @type {number} */
|
|
255
|
+
#preMuteVolume = 1.0;
|
|
256
|
+
/** @type {boolean} */
|
|
257
|
+
#isPaused = false;
|
|
258
|
+
/** @type {boolean} */
|
|
259
|
+
#isEnded = false;
|
|
252
260
|
/**
|
|
253
261
|
* Gets whether the adapter has been destroyed.
|
|
254
262
|
* @returns {boolean}
|
|
@@ -314,6 +322,22 @@ class SoundCloudMediaAdapter extends BaseMediaAdapter {
|
|
|
314
322
|
checkDestroy(this.#destroyed);
|
|
315
323
|
return this.#isReady;
|
|
316
324
|
}
|
|
325
|
+
/**
|
|
326
|
+
* Checks whether the media adapter is paused.
|
|
327
|
+
* @returns {boolean} True if the adapter is ready, false otherwise.
|
|
328
|
+
*/
|
|
329
|
+
isPaused() {
|
|
330
|
+
checkDestroy(this.#destroyed);
|
|
331
|
+
return this.#isPaused;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Checks whether the media adapter is ended.
|
|
335
|
+
* @returns {boolean} True if the adapter is ready, false otherwise.
|
|
336
|
+
*/
|
|
337
|
+
isEnded() {
|
|
338
|
+
checkDestroy(this.#destroyed);
|
|
339
|
+
return this.#isEnded;
|
|
340
|
+
}
|
|
317
341
|
/**
|
|
318
342
|
* Asynchronously waits until the SoundCloud media adapter is fully initialized and ready.
|
|
319
343
|
* @returns {Promise<void>} A promise that resolves once the adapter is ready.
|
|
@@ -391,7 +415,7 @@ class SoundCloudMediaAdapter extends BaseMediaAdapter {
|
|
|
391
415
|
}, 10000);
|
|
392
416
|
// Command to load the new song
|
|
393
417
|
this.#widget?.load(`https://api.soundcloud.com/tracks/${targetId}`);
|
|
394
|
-
await waitForTrue(() => true,
|
|
418
|
+
await waitForTrue(() => true, 700);
|
|
395
419
|
/** @type {Partial<SoundObject>} */
|
|
396
420
|
let sound = {};
|
|
397
421
|
// We registered the listener for the READY event before calling the load
|
|
@@ -444,6 +468,13 @@ class SoundCloudMediaAdapter extends BaseMediaAdapter {
|
|
|
444
468
|
const eventSync = (/** @type {any} */ event, /** @type {string} */ eventName) => {
|
|
445
469
|
if (!event)
|
|
446
470
|
return;
|
|
471
|
+
if (this.#isMuted)
|
|
472
|
+
this.#widget?.getVolume((volume) => {
|
|
473
|
+
if (this.#isMuted && volume > 0) {
|
|
474
|
+
this.#isMuted = false;
|
|
475
|
+
this.volume = this.#preMuteVolume;
|
|
476
|
+
}
|
|
477
|
+
});
|
|
447
478
|
if (typeof event.currentPosition === 'number')
|
|
448
479
|
this.#cachedPosition = event.currentPosition;
|
|
449
480
|
if (typeof event.loadedProgress === 'number')
|
|
@@ -463,14 +494,20 @@ class SoundCloudMediaAdapter extends BaseMediaAdapter {
|
|
|
463
494
|
});
|
|
464
495
|
this.#widget.bind(SoundCloudMediaAdapter.Events.PLAY, (/** @type {any} */ event) => {
|
|
465
496
|
eventSync(event, 'PLAY');
|
|
497
|
+
this.#isPaused = false;
|
|
498
|
+
this.#isEnded = false;
|
|
466
499
|
this.#masterEmitter?.emit('play');
|
|
467
500
|
});
|
|
468
501
|
this.#widget.bind(SoundCloudMediaAdapter.Events.PAUSE, (/** @type {any} */ event) => {
|
|
469
502
|
eventSync(event, 'PAUSE');
|
|
503
|
+
this.#isPaused = true;
|
|
504
|
+
this.#isEnded = false;
|
|
470
505
|
this.#masterEmitter?.emit('pause');
|
|
471
506
|
});
|
|
472
507
|
this.#widget.bind(SoundCloudMediaAdapter.Events.FINISH, (/** @type {any} */ event) => {
|
|
473
508
|
eventSync(event, 'FINISH');
|
|
509
|
+
this.#isPaused = false;
|
|
510
|
+
this.#isEnded = true;
|
|
474
511
|
this.#masterEmitter?.emit('ended');
|
|
475
512
|
});
|
|
476
513
|
this.#widget.bind(SoundCloudMediaAdapter.Events.LOAD_PROGRESS, (/** @type {any} */ event) => {
|
|
@@ -561,6 +598,43 @@ class SoundCloudMediaAdapter extends BaseMediaAdapter {
|
|
|
561
598
|
throw new TypeError('Time must be a number.');
|
|
562
599
|
this.#widget?.seekTo(timeMs);
|
|
563
600
|
}
|
|
601
|
+
/**
|
|
602
|
+
* Checks whether the player is currently muted.
|
|
603
|
+
* @returns {boolean} True if muted, false otherwise.
|
|
604
|
+
*/
|
|
605
|
+
isMuted() {
|
|
606
|
+
checkDestroy(this.#destroyed);
|
|
607
|
+
return this.#isMuted;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Mutes the current playback.
|
|
611
|
+
* @returns {Promise<void>}
|
|
612
|
+
*/
|
|
613
|
+
async mute() {
|
|
614
|
+
checkDestroy(this.#destroyed);
|
|
615
|
+
if (this.#isMuted)
|
|
616
|
+
return;
|
|
617
|
+
this.#isMuted = true;
|
|
618
|
+
this.#preMuteVolume = this.#currentVolume;
|
|
619
|
+
// We update the state directly to avoid the setter's logic
|
|
620
|
+
// overwriting preMuteVolume during the transition.
|
|
621
|
+
this.#currentVolume = 0;
|
|
622
|
+
if (this.#widget && typeof this.#widget.setVolume === 'function') {
|
|
623
|
+
this.#widget.setVolume(0);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Unmutes the current playback.
|
|
628
|
+
* @returns {Promise<void>}
|
|
629
|
+
*/
|
|
630
|
+
async unMute() {
|
|
631
|
+
checkDestroy(this.#destroyed);
|
|
632
|
+
if (!this.#isMuted)
|
|
633
|
+
return;
|
|
634
|
+
this.#isMuted = false;
|
|
635
|
+
// This will trigger the 'set volume' logic which applies the restored volume
|
|
636
|
+
this.volume = this.#preMuteVolume;
|
|
637
|
+
}
|
|
564
638
|
/**
|
|
565
639
|
* Retrieves the current playback time.
|
|
566
640
|
* @returns {number} The current time in milliseconds.
|
|
@@ -300,6 +300,12 @@ class YoutubeMediaAdapter extends index.BaseMediaAdapter {
|
|
|
300
300
|
/** @type {number} */
|
|
301
301
|
#pollingTimeUpdateValue = 250;
|
|
302
302
|
|
|
303
|
+
/** @type {boolean} */
|
|
304
|
+
#isPaused = false;
|
|
305
|
+
|
|
306
|
+
/** @type {boolean} */
|
|
307
|
+
#isEnded = false;
|
|
308
|
+
|
|
303
309
|
get pollingTimeUpdateValue() {
|
|
304
310
|
checkDestroy(this.#destroyed);
|
|
305
311
|
return this.#pollingTimeUpdateValue;
|
|
@@ -388,6 +394,24 @@ class YoutubeMediaAdapter extends index.BaseMediaAdapter {
|
|
|
388
394
|
return this.#isReady;
|
|
389
395
|
}
|
|
390
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Checks whether the media adapter is paused.
|
|
399
|
+
* @returns {boolean} True if the adapter is ready, false otherwise.
|
|
400
|
+
*/
|
|
401
|
+
isPaused() {
|
|
402
|
+
checkDestroy(this.#destroyed);
|
|
403
|
+
return this.#isPaused;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Checks whether the media adapter is ended.
|
|
408
|
+
* @returns {boolean} True if the adapter is ready, false otherwise.
|
|
409
|
+
*/
|
|
410
|
+
isEnded() {
|
|
411
|
+
checkDestroy(this.#destroyed);
|
|
412
|
+
return this.#isEnded;
|
|
413
|
+
}
|
|
414
|
+
|
|
391
415
|
/**
|
|
392
416
|
* Asynchronously waits until the YouTube media adapter is fully initialized and ready.
|
|
393
417
|
* @returns {Promise<void>} A promise that resolves once the adapter is ready.
|
|
@@ -507,9 +531,21 @@ class YoutubeMediaAdapter extends index.BaseMediaAdapter {
|
|
|
507
531
|
if (ytEvent === 'onStateChange') {
|
|
508
532
|
const state = args[0];
|
|
509
533
|
if (objChecker.isValidObj(state)) {
|
|
510
|
-
if (state.data === YoutubeMediaAdapter.PlayerState.PLAYING)
|
|
511
|
-
|
|
512
|
-
|
|
534
|
+
if (state.data === YoutubeMediaAdapter.PlayerState.PLAYING) {
|
|
535
|
+
this.#isPaused = false;
|
|
536
|
+
this.#isEnded = false;
|
|
537
|
+
masterEmitter.emit('play');
|
|
538
|
+
}
|
|
539
|
+
if (state.data === YoutubeMediaAdapter.PlayerState.PAUSED) {
|
|
540
|
+
this.#isPaused = true;
|
|
541
|
+
this.#isEnded = false;
|
|
542
|
+
masterEmitter.emit('pause');
|
|
543
|
+
}
|
|
544
|
+
if (state.data === YoutubeMediaAdapter.PlayerState.ENDED) {
|
|
545
|
+
this.#isPaused = false;
|
|
546
|
+
this.#isEnded = true;
|
|
547
|
+
masterEmitter.emit('ended');
|
|
548
|
+
}
|
|
513
549
|
}
|
|
514
550
|
}
|
|
515
551
|
return result;
|
|
@@ -633,6 +669,33 @@ class YoutubeMediaAdapter extends index.BaseMediaAdapter {
|
|
|
633
669
|
this.#player?.seekTo(targetTimeInSeconds, true);
|
|
634
670
|
}
|
|
635
671
|
|
|
672
|
+
/**
|
|
673
|
+
* Checks whether the player is currently muted.
|
|
674
|
+
* @returns {boolean|null} True if muted, false otherwise.
|
|
675
|
+
*/
|
|
676
|
+
isMuted() {
|
|
677
|
+
checkDestroy(this.#destroyed);
|
|
678
|
+
return this.#player?.isMuted() ?? null;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Mutes the current playback.
|
|
683
|
+
* @returns {Promise<void>}
|
|
684
|
+
*/
|
|
685
|
+
async mute() {
|
|
686
|
+
checkDestroy(this.#destroyed);
|
|
687
|
+
this.#player?.mute();
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Unmutes the current playback.
|
|
692
|
+
* @returns {Promise<void>}
|
|
693
|
+
*/
|
|
694
|
+
async unMute() {
|
|
695
|
+
checkDestroy(this.#destroyed);
|
|
696
|
+
this.#player?.unMute();
|
|
697
|
+
}
|
|
698
|
+
|
|
636
699
|
/**
|
|
637
700
|
* Retrieves the current playback time.
|
|
638
701
|
* @returns {number} The current time in milliseconds.
|