audio-channel-queue 1.10.0 → 1.12.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.
- package/README.md +56 -21
- package/dist/core.d.ts +2 -2
- package/dist/core.js +17 -14
- package/dist/errors.d.ts +4 -4
- package/dist/errors.js +12 -11
- package/dist/index.d.ts +3 -3
- package/dist/index.js +7 -5
- package/dist/info.d.ts +55 -9
- package/dist/info.js +108 -12
- package/dist/types.d.ts +66 -24
- package/dist/types.js +17 -4
- package/dist/volume.d.ts +0 -14
- package/dist/volume.js +2 -19
- package/package.json +1 -1
- package/src/core.ts +18 -14
- package/src/errors.ts +14 -15
- package/src/index.ts +13 -5
- package/src/info.ts +115 -13
- package/src/types.ts +67 -24
- package/src/volume.ts +1 -23
package/dist/info.js
CHANGED
|
@@ -3,11 +3,71 @@
|
|
|
3
3
|
* @fileoverview Audio information and progress tracking functions for the audio-channel-queue package
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.offAudioResume = exports.offAudioPause = exports.onAudioResume = exports.onAudioPause = exports.onAudioComplete = exports.onAudioStart = exports.offQueueChange = exports.onQueueChange = exports.onAudioProgress = exports.getQueueSnapshot = exports.getAllChannelsInfo = exports.getCurrentAudioInfo = exports.audioChannels = void 0;
|
|
6
|
+
exports.offAudioComplete = exports.offAudioStart = exports.offAudioResume = exports.offAudioPause = exports.onAudioResume = exports.onAudioPause = exports.onAudioComplete = exports.onAudioStart = exports.offQueueChange = exports.onQueueChange = exports.onAudioProgress = exports.getQueueSnapshot = exports.getAllChannelsInfo = exports.getCurrentAudioInfo = exports.audioChannels = exports.getNonWhitelistedChannelProperties = exports.getWhitelistedChannelProperties = void 0;
|
|
7
7
|
exports.offAudioProgress = offAudioProgress;
|
|
8
8
|
const types_1 = require("./types");
|
|
9
9
|
const utils_1 = require("./utils");
|
|
10
10
|
const events_1 = require("./events");
|
|
11
|
+
/**
|
|
12
|
+
* Gets the current list of whitelisted channel properties
|
|
13
|
+
* This is automatically derived from the ExtendedAudioQueueChannel interface
|
|
14
|
+
* @returns Array of whitelisted property names
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
const getWhitelistedChannelProperties = () => {
|
|
18
|
+
// Create a sample channel object to extract property names
|
|
19
|
+
const sampleChannel = {
|
|
20
|
+
audioCompleteCallbacks: new Set(),
|
|
21
|
+
audioErrorCallbacks: new Set(),
|
|
22
|
+
audioPauseCallbacks: new Set(),
|
|
23
|
+
audioResumeCallbacks: new Set(),
|
|
24
|
+
audioStartCallbacks: new Set(),
|
|
25
|
+
isPaused: false,
|
|
26
|
+
progressCallbacks: new Map(),
|
|
27
|
+
queue: [],
|
|
28
|
+
queueChangeCallbacks: new Set(),
|
|
29
|
+
volume: 1.0
|
|
30
|
+
};
|
|
31
|
+
// Get all property names from the interface (including optional ones)
|
|
32
|
+
const propertyNames = [
|
|
33
|
+
...Object.getOwnPropertyNames(sampleChannel),
|
|
34
|
+
// Add optional properties that might not be present on the sample
|
|
35
|
+
'fadeState',
|
|
36
|
+
'isLocked',
|
|
37
|
+
'maxQueueSize',
|
|
38
|
+
'retryConfig',
|
|
39
|
+
'volumeConfig' // Legacy property that might still be used
|
|
40
|
+
];
|
|
41
|
+
return [...new Set(propertyNames)]; // Remove duplicates
|
|
42
|
+
};
|
|
43
|
+
exports.getWhitelistedChannelProperties = getWhitelistedChannelProperties;
|
|
44
|
+
/**
|
|
45
|
+
* Returns the list of non-whitelisted properties found on a specific channel
|
|
46
|
+
* These are properties that will trigger warnings when modified directly
|
|
47
|
+
* @param channelNumber - The channel number to inspect (defaults to 0)
|
|
48
|
+
* @returns Array of property names that are not in the whitelist, or empty array if channel doesn't exist
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* // Add some custom property to a channel
|
|
52
|
+
* (audioChannels[0] as any).customProperty = 'test';
|
|
53
|
+
*
|
|
54
|
+
* const nonWhitelisted = getNonWhitelistedChannelProperties(0);
|
|
55
|
+
* console.log(nonWhitelisted); // ['customProperty']
|
|
56
|
+
* ```
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
59
|
+
const getNonWhitelistedChannelProperties = (channelNumber = 0) => {
|
|
60
|
+
const channel = exports.audioChannels[channelNumber];
|
|
61
|
+
if (!channel) {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
const whitelistedProperties = (0, exports.getWhitelistedChannelProperties)();
|
|
65
|
+
const allChannelProperties = Object.getOwnPropertyNames(channel);
|
|
66
|
+
// Filter out properties that are in the whitelist
|
|
67
|
+
const nonWhitelistedProperties = allChannelProperties.filter((property) => !whitelistedProperties.includes(property));
|
|
68
|
+
return nonWhitelistedProperties;
|
|
69
|
+
};
|
|
70
|
+
exports.getNonWhitelistedChannelProperties = getNonWhitelistedChannelProperties;
|
|
11
71
|
/**
|
|
12
72
|
* Global array to store audio channels with their queues and callback management
|
|
13
73
|
* Each channel maintains its own audio queue and event callback sets
|
|
@@ -35,8 +95,9 @@ exports.audioChannels = new Proxy([], {
|
|
|
35
95
|
return new Proxy(value, {
|
|
36
96
|
set(channelTarget, channelProp, channelValue) {
|
|
37
97
|
// Allow internal modifications but warn about direct property changes
|
|
38
|
-
|
|
39
|
-
|
|
98
|
+
// Use the automatically-derived whitelist from the interface
|
|
99
|
+
const whitelistedProperties = (0, exports.getWhitelistedChannelProperties)();
|
|
100
|
+
if (typeof channelProp === 'string' && !whitelistedProperties.includes(channelProp)) {
|
|
40
101
|
// eslint-disable-next-line no-console
|
|
41
102
|
console.warn(`Warning: Direct modification of channel.${channelProp} detected. ` +
|
|
42
103
|
'Use API functions for safer channel management.');
|
|
@@ -240,13 +301,14 @@ const onQueueChange = (channelNumber, callback) => {
|
|
|
240
301
|
exports.onQueueChange = onQueueChange;
|
|
241
302
|
/**
|
|
242
303
|
* Removes queue change listeners for a specific channel
|
|
243
|
-
* @param channelNumber - The channel number
|
|
304
|
+
* @param channelNumber - The channel number (defaults to 0)
|
|
244
305
|
* @example
|
|
245
306
|
* ```typescript
|
|
246
|
-
* offQueueChange(
|
|
307
|
+
* offQueueChange(); // Stop receiving queue change notifications for default channel (0)
|
|
308
|
+
* offQueueChange(1); // Stop receiving queue change notifications for channel 1
|
|
247
309
|
* ```
|
|
248
310
|
*/
|
|
249
|
-
const offQueueChange = (channelNumber) => {
|
|
311
|
+
const offQueueChange = (channelNumber = 0) => {
|
|
250
312
|
const channel = exports.audioChannels[channelNumber];
|
|
251
313
|
if (!(channel === null || channel === void 0 ? void 0 : channel.queueChangeCallbacks))
|
|
252
314
|
return;
|
|
@@ -401,13 +463,14 @@ const onAudioResume = (channelNumber, callback) => {
|
|
|
401
463
|
exports.onAudioResume = onAudioResume;
|
|
402
464
|
/**
|
|
403
465
|
* Removes pause event listeners for a specific channel
|
|
404
|
-
* @param channelNumber - The channel number
|
|
466
|
+
* @param channelNumber - The channel number (defaults to 0)
|
|
405
467
|
* @example
|
|
406
468
|
* ```typescript
|
|
407
|
-
* offAudioPause(
|
|
469
|
+
* offAudioPause(); // Stop receiving pause notifications for default channel (0)
|
|
470
|
+
* offAudioPause(1); // Stop receiving pause notifications for channel 1
|
|
408
471
|
* ```
|
|
409
472
|
*/
|
|
410
|
-
const offAudioPause = (channelNumber) => {
|
|
473
|
+
const offAudioPause = (channelNumber = 0) => {
|
|
411
474
|
const channel = exports.audioChannels[channelNumber];
|
|
412
475
|
if (!(channel === null || channel === void 0 ? void 0 : channel.audioPauseCallbacks))
|
|
413
476
|
return;
|
|
@@ -416,16 +479,49 @@ const offAudioPause = (channelNumber) => {
|
|
|
416
479
|
exports.offAudioPause = offAudioPause;
|
|
417
480
|
/**
|
|
418
481
|
* Removes resume event listeners for a specific channel
|
|
419
|
-
* @param channelNumber - The channel number
|
|
482
|
+
* @param channelNumber - The channel number (defaults to 0)
|
|
420
483
|
* @example
|
|
421
484
|
* ```typescript
|
|
422
|
-
* offAudioResume(
|
|
485
|
+
* offAudioResume(); // Stop receiving resume notifications for default channel (0)
|
|
486
|
+
* offAudioResume(1); // Stop receiving resume notifications for channel 1
|
|
423
487
|
* ```
|
|
424
488
|
*/
|
|
425
|
-
const offAudioResume = (channelNumber) => {
|
|
489
|
+
const offAudioResume = (channelNumber = 0) => {
|
|
426
490
|
const channel = exports.audioChannels[channelNumber];
|
|
427
491
|
if (!(channel === null || channel === void 0 ? void 0 : channel.audioResumeCallbacks))
|
|
428
492
|
return;
|
|
429
493
|
channel.audioResumeCallbacks.clear();
|
|
430
494
|
};
|
|
431
495
|
exports.offAudioResume = offAudioResume;
|
|
496
|
+
/**
|
|
497
|
+
* Removes audio start event listeners for a specific channel
|
|
498
|
+
* @param channelNumber - The channel number (defaults to 0)
|
|
499
|
+
* @example
|
|
500
|
+
* ```typescript
|
|
501
|
+
* offAudioStart(); // Stop receiving start notifications for default channel (0)
|
|
502
|
+
* offAudioStart(1); // Stop receiving start notifications for channel 1
|
|
503
|
+
* ```
|
|
504
|
+
*/
|
|
505
|
+
const offAudioStart = (channelNumber = 0) => {
|
|
506
|
+
const channel = exports.audioChannels[channelNumber];
|
|
507
|
+
if (!(channel === null || channel === void 0 ? void 0 : channel.audioStartCallbacks))
|
|
508
|
+
return;
|
|
509
|
+
channel.audioStartCallbacks.clear();
|
|
510
|
+
};
|
|
511
|
+
exports.offAudioStart = offAudioStart;
|
|
512
|
+
/**
|
|
513
|
+
* Removes audio complete event listeners for a specific channel
|
|
514
|
+
* @param channelNumber - The channel number (defaults to 0)
|
|
515
|
+
* @example
|
|
516
|
+
* ```typescript
|
|
517
|
+
* offAudioComplete(); // Stop receiving completion notifications for default channel (0)
|
|
518
|
+
* offAudioComplete(1); // Stop receiving completion notifications for channel 1
|
|
519
|
+
* ```
|
|
520
|
+
*/
|
|
521
|
+
const offAudioComplete = (channelNumber = 0) => {
|
|
522
|
+
const channel = exports.audioChannels[channelNumber];
|
|
523
|
+
if (!(channel === null || channel === void 0 ? void 0 : channel.audioCompleteCallbacks))
|
|
524
|
+
return;
|
|
525
|
+
channel.audioCompleteCallbacks.clear();
|
|
526
|
+
};
|
|
527
|
+
exports.offAudioComplete = offAudioComplete;
|
package/dist/types.d.ts
CHANGED
|
@@ -24,15 +24,15 @@ export interface AudioQueueChannel {
|
|
|
24
24
|
* Volume ducking configuration for channels
|
|
25
25
|
*/
|
|
26
26
|
export interface VolumeConfig {
|
|
27
|
+
/** Duration in milliseconds for volume duck transition (defaults to 250ms) */
|
|
28
|
+
duckTransitionDuration?: number;
|
|
29
|
+
/** Volume level for all other channels when priority channel is active (0-1) */
|
|
30
|
+
duckingVolume: number;
|
|
27
31
|
/** The channel number that should have priority */
|
|
28
32
|
priorityChannel: number;
|
|
29
33
|
/** Volume level for the priority channel (0-1) */
|
|
30
34
|
priorityVolume: number;
|
|
31
|
-
/**
|
|
32
|
-
duckingVolume: number;
|
|
33
|
-
/** Duration in milliseconds for volume duck transition (defaults to 250ms) */
|
|
34
|
-
duckTransitionDuration?: number;
|
|
35
|
-
/** Duration in milliseconds for volume restore transition (defaults to 500ms) */
|
|
35
|
+
/** Duration in milliseconds for volume restore transition (defaults to 250ms) */
|
|
36
36
|
restoreTransitionDuration?: number;
|
|
37
37
|
/** Easing function for volume transitions (defaults to 'ease-out') */
|
|
38
38
|
transitionEasing?: EasingType;
|
|
@@ -47,8 +47,6 @@ export interface AudioQueueOptions {
|
|
|
47
47
|
loop?: boolean;
|
|
48
48
|
/** Maximum number of items allowed in the queue (defaults to unlimited) */
|
|
49
49
|
maxQueueSize?: number;
|
|
50
|
-
/** @deprecated Use addToFront instead. Legacy support for priority queuing */
|
|
51
|
-
priority?: boolean;
|
|
52
50
|
/** Volume level for this specific audio (0-1) */
|
|
53
51
|
volume?: number;
|
|
54
52
|
}
|
|
@@ -192,67 +190,111 @@ export type AudioPauseCallback = (channelNumber: number, audioInfo: AudioInfo) =
|
|
|
192
190
|
*/
|
|
193
191
|
export type AudioResumeCallback = (channelNumber: number, audioInfo: AudioInfo) => void;
|
|
194
192
|
/**
|
|
195
|
-
*
|
|
193
|
+
* Types of audio errors that can occur during playback
|
|
194
|
+
*/
|
|
195
|
+
export declare enum AudioErrorType {
|
|
196
|
+
Abort = "abort",
|
|
197
|
+
Decode = "decode",
|
|
198
|
+
Network = "network",
|
|
199
|
+
Permission = "permission",
|
|
200
|
+
Timeout = "timeout",
|
|
201
|
+
Unknown = "unknown",
|
|
202
|
+
Unsupported = "unsupported"
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Information about an audio error that occurred during playback or loading
|
|
196
206
|
*/
|
|
197
207
|
export interface AudioErrorInfo {
|
|
208
|
+
/** Channel number where the error occurred */
|
|
198
209
|
channelNumber: number;
|
|
199
|
-
|
|
200
|
-
fileName: string;
|
|
210
|
+
/** The actual error object that was thrown */
|
|
201
211
|
error: Error;
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
212
|
+
/** Categorized type of error for handling different scenarios */
|
|
213
|
+
errorType: AudioErrorType;
|
|
214
|
+
/** Extracted filename from the source URL */
|
|
215
|
+
fileName: string;
|
|
216
|
+
/** Number of audio files remaining in the queue after this error */
|
|
205
217
|
remainingInQueue: number;
|
|
218
|
+
/** Current retry attempt number (if retrying is enabled) */
|
|
219
|
+
retryAttempt?: number;
|
|
220
|
+
/** Audio file source URL that failed */
|
|
221
|
+
src: string;
|
|
222
|
+
/** Unix timestamp when the error occurred */
|
|
223
|
+
timestamp: number;
|
|
206
224
|
}
|
|
207
225
|
/**
|
|
208
226
|
* Configuration for automatic retry behavior when audio fails to load or play
|
|
209
227
|
*/
|
|
210
228
|
export interface RetryConfig {
|
|
211
|
-
|
|
212
|
-
maxRetries: number;
|
|
229
|
+
/** Initial delay in milliseconds before first retry attempt */
|
|
213
230
|
baseDelay: number;
|
|
231
|
+
/** Whether automatic retries are enabled for this channel */
|
|
232
|
+
enabled: boolean;
|
|
233
|
+
/** Whether to use exponential backoff (doubling delay each retry) */
|
|
214
234
|
exponentialBackoff: boolean;
|
|
215
|
-
|
|
216
|
-
fallbackUrls
|
|
235
|
+
/** Alternative URLs to try if the primary source fails */
|
|
236
|
+
fallbackUrls: string[];
|
|
237
|
+
/** Maximum number of retry attempts before giving up */
|
|
238
|
+
maxRetries: number;
|
|
239
|
+
/** Whether to skip to next track in queue if all retries fail */
|
|
217
240
|
skipOnFailure: boolean;
|
|
241
|
+
/** Timeout in milliseconds for each individual retry attempt */
|
|
242
|
+
timeoutMs: number;
|
|
218
243
|
}
|
|
219
244
|
/**
|
|
220
|
-
* Configuration options for error recovery mechanisms
|
|
245
|
+
* Configuration options for error recovery mechanisms across the audio system
|
|
221
246
|
*/
|
|
222
247
|
export interface ErrorRecoveryOptions {
|
|
248
|
+
/** Whether to automatically retry failed audio loads/plays */
|
|
223
249
|
autoRetry: boolean;
|
|
224
|
-
|
|
250
|
+
/** Whether to automatically skip to next track when current fails */
|
|
251
|
+
fallbackToNextTrack: boolean;
|
|
252
|
+
/** Whether to send error data to analytics systems */
|
|
225
253
|
logErrorsToAnalytics: boolean;
|
|
254
|
+
/** Whether to maintain queue integrity when errors occur */
|
|
226
255
|
preserveQueueOnError: boolean;
|
|
227
|
-
|
|
256
|
+
/** Whether to display user-visible error feedback */
|
|
257
|
+
showUserFeedback: boolean;
|
|
228
258
|
}
|
|
229
259
|
/**
|
|
230
260
|
* Callback function type for audio error events
|
|
231
261
|
*/
|
|
232
262
|
export type AudioErrorCallback = (errorInfo: AudioErrorInfo) => void;
|
|
233
263
|
/**
|
|
234
|
-
* Extended audio channel with queue management and
|
|
264
|
+
* Extended audio channel with comprehensive queue management, callback support, and state tracking
|
|
235
265
|
*/
|
|
236
266
|
export interface ExtendedAudioQueueChannel {
|
|
267
|
+
/** Set of callbacks triggered when audio completes playback */
|
|
237
268
|
audioCompleteCallbacks: Set<AudioCompleteCallback>;
|
|
269
|
+
/** Set of callbacks triggered when audio errors occur */
|
|
238
270
|
audioErrorCallbacks: Set<AudioErrorCallback>;
|
|
271
|
+
/** Set of callbacks triggered when audio is paused */
|
|
239
272
|
audioPauseCallbacks: Set<AudioPauseCallback>;
|
|
273
|
+
/** Set of callbacks triggered when audio is resumed */
|
|
240
274
|
audioResumeCallbacks: Set<AudioResumeCallback>;
|
|
275
|
+
/** Set of callbacks triggered when audio starts playing */
|
|
241
276
|
audioStartCallbacks: Set<AudioStartCallback>;
|
|
277
|
+
/** Current fade state if pause/resume with fade is active */
|
|
242
278
|
fadeState?: ChannelFadeState;
|
|
279
|
+
/** Whether the channel is currently paused */
|
|
243
280
|
isPaused: boolean;
|
|
244
281
|
/** Active operation lock to prevent race conditions */
|
|
245
282
|
isLocked?: boolean;
|
|
246
283
|
/** Maximum allowed queue size for this channel */
|
|
247
284
|
maxQueueSize?: number;
|
|
285
|
+
/** Map of progress callbacks keyed by audio element or global symbol */
|
|
248
286
|
progressCallbacks: Map<HTMLAudioElement | typeof GLOBAL_PROGRESS_KEY, Set<ProgressCallback>>;
|
|
287
|
+
/** Array of HTMLAudioElement objects in the queue */
|
|
249
288
|
queue: HTMLAudioElement[];
|
|
289
|
+
/** Set of callbacks triggered when the queue changes */
|
|
250
290
|
queueChangeCallbacks: Set<QueueChangeCallback>;
|
|
291
|
+
/** Retry configuration for failed audio loads/plays */
|
|
251
292
|
retryConfig?: RetryConfig;
|
|
293
|
+
/** Current volume level for the channel (0-1) */
|
|
252
294
|
volume: number;
|
|
253
295
|
}
|
|
254
296
|
/**
|
|
255
|
-
* Easing function types for volume transitions
|
|
297
|
+
* Easing function types for smooth volume transitions and animations
|
|
256
298
|
*/
|
|
257
299
|
export declare enum EasingType {
|
|
258
300
|
Linear = "linear",
|
|
@@ -261,7 +303,7 @@ export declare enum EasingType {
|
|
|
261
303
|
EaseInOut = "ease-in-out"
|
|
262
304
|
}
|
|
263
305
|
/**
|
|
264
|
-
*
|
|
306
|
+
* Predefined fade types for pause/resume operations with different transition characteristics
|
|
265
307
|
*/
|
|
266
308
|
export declare enum FadeType {
|
|
267
309
|
Linear = "linear",
|
|
@@ -269,7 +311,7 @@ export declare enum FadeType {
|
|
|
269
311
|
Dramatic = "dramatic"
|
|
270
312
|
}
|
|
271
313
|
/**
|
|
272
|
-
* Timer types for volume transitions to ensure proper cleanup
|
|
314
|
+
* Timer implementation types used for volume transitions to ensure proper cleanup
|
|
273
315
|
*/
|
|
274
316
|
export declare enum TimerType {
|
|
275
317
|
RequestAnimationFrame = "raf",
|
package/dist/types.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @fileoverview Type definitions for the audio-channel-queue package
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.TimerType = exports.FadeType = exports.EasingType = exports.GLOBAL_PROGRESS_KEY = exports.MAX_CHANNELS = void 0;
|
|
6
|
+
exports.TimerType = exports.FadeType = exports.EasingType = exports.AudioErrorType = exports.GLOBAL_PROGRESS_KEY = exports.MAX_CHANNELS = void 0;
|
|
7
7
|
/**
|
|
8
8
|
* Maximum number of audio channels allowed to prevent memory exhaustion
|
|
9
9
|
*/
|
|
@@ -14,7 +14,20 @@ exports.MAX_CHANNELS = 64;
|
|
|
14
14
|
*/
|
|
15
15
|
exports.GLOBAL_PROGRESS_KEY = Symbol('global-progress-callbacks');
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
17
|
+
* Types of audio errors that can occur during playback
|
|
18
|
+
*/
|
|
19
|
+
var AudioErrorType;
|
|
20
|
+
(function (AudioErrorType) {
|
|
21
|
+
AudioErrorType["Abort"] = "abort";
|
|
22
|
+
AudioErrorType["Decode"] = "decode";
|
|
23
|
+
AudioErrorType["Network"] = "network";
|
|
24
|
+
AudioErrorType["Permission"] = "permission";
|
|
25
|
+
AudioErrorType["Timeout"] = "timeout";
|
|
26
|
+
AudioErrorType["Unknown"] = "unknown";
|
|
27
|
+
AudioErrorType["Unsupported"] = "unsupported";
|
|
28
|
+
})(AudioErrorType || (exports.AudioErrorType = AudioErrorType = {}));
|
|
29
|
+
/**
|
|
30
|
+
* Easing function types for smooth volume transitions and animations
|
|
18
31
|
*/
|
|
19
32
|
var EasingType;
|
|
20
33
|
(function (EasingType) {
|
|
@@ -24,7 +37,7 @@ var EasingType;
|
|
|
24
37
|
EasingType["EaseInOut"] = "ease-in-out";
|
|
25
38
|
})(EasingType || (exports.EasingType = EasingType = {}));
|
|
26
39
|
/**
|
|
27
|
-
*
|
|
40
|
+
* Predefined fade types for pause/resume operations with different transition characteristics
|
|
28
41
|
*/
|
|
29
42
|
var FadeType;
|
|
30
43
|
(function (FadeType) {
|
|
@@ -33,7 +46,7 @@ var FadeType;
|
|
|
33
46
|
FadeType["Dramatic"] = "dramatic";
|
|
34
47
|
})(FadeType || (exports.FadeType = FadeType = {}));
|
|
35
48
|
/**
|
|
36
|
-
* Timer types for volume transitions to ensure proper cleanup
|
|
49
|
+
* Timer implementation types used for volume transitions to ensure proper cleanup
|
|
37
50
|
*/
|
|
38
51
|
var TimerType;
|
|
39
52
|
(function (TimerType) {
|
package/dist/volume.d.ts
CHANGED
|
@@ -103,20 +103,6 @@ export declare const clearVolumeDucking: () => void;
|
|
|
103
103
|
* @internal
|
|
104
104
|
*/
|
|
105
105
|
export declare const applyVolumeDucking: (activeChannelNumber: number) => Promise<void>;
|
|
106
|
-
/**
|
|
107
|
-
* Fades the volume for a specific channel over time (alias for transitionVolume with improved naming)
|
|
108
|
-
* @param channelNumber - The channel number to fade
|
|
109
|
-
* @param targetVolume - Target volume level (0-1)
|
|
110
|
-
* @param duration - Fade duration in milliseconds (defaults to 250)
|
|
111
|
-
* @param easing - Easing function type (defaults to 'ease-out')
|
|
112
|
-
* @returns Promise that resolves when fade completes
|
|
113
|
-
* @example
|
|
114
|
-
* ```typescript
|
|
115
|
-
* await fadeVolume(0, 0, 800, 'ease-in'); // Fade out over 800ms
|
|
116
|
-
* await fadeVolume(0, 1, 600, 'ease-out'); // Fade in over 600ms
|
|
117
|
-
* ```
|
|
118
|
-
*/
|
|
119
|
-
export declare const fadeVolume: (channelNumber: number, targetVolume: number, duration?: number, easing?: EasingType) => Promise<void>;
|
|
120
106
|
/**
|
|
121
107
|
* Restores normal volume levels when priority channel queue becomes empty
|
|
122
108
|
* @param stoppedChannelNumber - The channel that just stopped playing
|
package/dist/volume.js
CHANGED
|
@@ -12,7 +12,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
12
12
|
});
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.cancelAllVolumeTransitions = exports.cancelVolumeTransition = exports.restoreVolumeLevels = exports.
|
|
15
|
+
exports.cancelAllVolumeTransitions = exports.cancelVolumeTransition = exports.restoreVolumeLevels = exports.applyVolumeDucking = exports.clearVolumeDucking = exports.setVolumeDucking = exports.setAllChannelsVolume = exports.getAllChannelsVolume = exports.getChannelVolume = exports.setChannelVolume = exports.transitionVolume = exports.getFadeConfig = void 0;
|
|
16
16
|
const types_1 = require("./types");
|
|
17
17
|
const info_1 = require("./info");
|
|
18
18
|
// Store active volume transitions to handle interruptions
|
|
@@ -346,23 +346,6 @@ const applyVolumeDucking = (activeChannelNumber) => __awaiter(void 0, void 0, vo
|
|
|
346
346
|
yield Promise.all(transitionPromises);
|
|
347
347
|
});
|
|
348
348
|
exports.applyVolumeDucking = applyVolumeDucking;
|
|
349
|
-
/**
|
|
350
|
-
* Fades the volume for a specific channel over time (alias for transitionVolume with improved naming)
|
|
351
|
-
* @param channelNumber - The channel number to fade
|
|
352
|
-
* @param targetVolume - Target volume level (0-1)
|
|
353
|
-
* @param duration - Fade duration in milliseconds (defaults to 250)
|
|
354
|
-
* @param easing - Easing function type (defaults to 'ease-out')
|
|
355
|
-
* @returns Promise that resolves when fade completes
|
|
356
|
-
* @example
|
|
357
|
-
* ```typescript
|
|
358
|
-
* await fadeVolume(0, 0, 800, 'ease-in'); // Fade out over 800ms
|
|
359
|
-
* await fadeVolume(0, 1, 600, 'ease-out'); // Fade in over 600ms
|
|
360
|
-
* ```
|
|
361
|
-
*/
|
|
362
|
-
const fadeVolume = (channelNumber_1, targetVolume_1, ...args_1) => __awaiter(void 0, [channelNumber_1, targetVolume_1, ...args_1], void 0, function* (channelNumber, targetVolume, duration = 250, easing = types_1.EasingType.EaseOut) {
|
|
363
|
-
return (0, exports.transitionVolume)(channelNumber, targetVolume, duration, easing);
|
|
364
|
-
});
|
|
365
|
-
exports.fadeVolume = fadeVolume;
|
|
366
349
|
/**
|
|
367
350
|
* Restores normal volume levels when priority channel queue becomes empty
|
|
368
351
|
* @param stoppedChannelNumber - The channel that just stopped playing
|
|
@@ -388,7 +371,7 @@ const restoreVolumeLevels = (stoppedChannelNumber) => __awaiter(void 0, void 0,
|
|
|
388
371
|
return;
|
|
389
372
|
}
|
|
390
373
|
// Restore this channel to its desired volume
|
|
391
|
-
const duration = (_a = config.restoreTransitionDuration) !== null && _a !== void 0 ? _a :
|
|
374
|
+
const duration = (_a = config.restoreTransitionDuration) !== null && _a !== void 0 ? _a : 250;
|
|
392
375
|
const easing = (_b = config.transitionEasing) !== null && _b !== void 0 ? _b : types_1.EasingType.EaseOut;
|
|
393
376
|
const targetVolume = (_c = channel.volume) !== null && _c !== void 0 ? _c : 1.0;
|
|
394
377
|
// Only transition the audio element volume, keep channel.volume as the desired volume
|
package/package.json
CHANGED
package/src/core.ts
CHANGED
|
@@ -300,10 +300,10 @@ export const queueAudio = async (
|
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
-
// Handle
|
|
304
|
-
const shouldAddToFront = options?.addToFront
|
|
303
|
+
// Handle addToFront option
|
|
304
|
+
const shouldAddToFront = options?.addToFront;
|
|
305
305
|
|
|
306
|
-
// Add to queue based on
|
|
306
|
+
// Add to queue based on addToFront option
|
|
307
307
|
if (shouldAddToFront && channel.queue.length > 0) {
|
|
308
308
|
// Insert after currently playing track (at index 1)
|
|
309
309
|
channel.queue.splice(1, 0, audio);
|
|
@@ -320,12 +320,8 @@ export const queueAudio = async (
|
|
|
320
320
|
|
|
321
321
|
// Start playing if this is the first item and channel isn't paused
|
|
322
322
|
if (channel.queue.length === 1 && !channel.isPaused) {
|
|
323
|
-
//
|
|
324
|
-
|
|
325
|
-
playAudioQueue(channelNumber).catch((error: Error) => {
|
|
326
|
-
handleAudioError(audio, channelNumber, validatedUrl, error);
|
|
327
|
-
});
|
|
328
|
-
}, 0);
|
|
323
|
+
// Await the audio setup to complete before resolving queueAudio
|
|
324
|
+
await playAudioQueue(channelNumber);
|
|
329
325
|
}
|
|
330
326
|
};
|
|
331
327
|
|
|
@@ -354,10 +350,10 @@ export const queueAudioPriority = async (
|
|
|
354
350
|
/**
|
|
355
351
|
* Plays the audio queue for a specific channel
|
|
356
352
|
* @param channelNumber - The channel number to play
|
|
357
|
-
* @returns Promise that resolves when the
|
|
353
|
+
* @returns Promise that resolves when the audio starts playing (setup complete)
|
|
358
354
|
* @example
|
|
359
355
|
* ```typescript
|
|
360
|
-
* await playAudioQueue(0); //
|
|
356
|
+
* await playAudioQueue(0); // Start playing queue for channel 0
|
|
361
357
|
* ```
|
|
362
358
|
*/
|
|
363
359
|
export const playAudioQueue = async (channelNumber: number): Promise<void> => {
|
|
@@ -381,6 +377,7 @@ export const playAudioQueue = async (channelNumber: number): Promise<void> => {
|
|
|
381
377
|
let hasStarted: boolean = false;
|
|
382
378
|
let metadataLoaded: boolean = false;
|
|
383
379
|
let playStarted: boolean = false;
|
|
380
|
+
let setupComplete: boolean = false;
|
|
384
381
|
|
|
385
382
|
// Check if we should fire onAudioStart (both conditions met)
|
|
386
383
|
const tryFireAudioStart = (): void => {
|
|
@@ -396,6 +393,12 @@ export const playAudioQueue = async (channelNumber: number): Promise<void> => {
|
|
|
396
393
|
},
|
|
397
394
|
audioChannels
|
|
398
395
|
);
|
|
396
|
+
|
|
397
|
+
// Resolve setup promise when audio start event is fired
|
|
398
|
+
if (!setupComplete) {
|
|
399
|
+
setupComplete = true;
|
|
400
|
+
resolve();
|
|
401
|
+
}
|
|
399
402
|
}
|
|
400
403
|
};
|
|
401
404
|
|
|
@@ -433,7 +436,6 @@ export const playAudioQueue = async (channelNumber: number): Promise<void> => {
|
|
|
433
436
|
} catch (error) {
|
|
434
437
|
await handleAudioError(currentAudio, channelNumber, currentAudio.src, error as Error);
|
|
435
438
|
}
|
|
436
|
-
resolve();
|
|
437
439
|
} else {
|
|
438
440
|
// For non-looping audio, remove from queue and play next
|
|
439
441
|
currentAudio.pause();
|
|
@@ -448,7 +450,6 @@ export const playAudioQueue = async (channelNumber: number): Promise<void> => {
|
|
|
448
450
|
|
|
449
451
|
// Play next audio immediately if there's more in queue
|
|
450
452
|
await playAudioQueue(channelNumber);
|
|
451
|
-
resolve();
|
|
452
453
|
}
|
|
453
454
|
};
|
|
454
455
|
|
|
@@ -465,7 +466,10 @@ export const playAudioQueue = async (channelNumber: number): Promise<void> => {
|
|
|
465
466
|
// Enhanced play with error handling
|
|
466
467
|
currentAudio.play().catch(async (error: Error) => {
|
|
467
468
|
await handleAudioError(currentAudio, channelNumber, currentAudio.src, error);
|
|
468
|
-
|
|
469
|
+
if (!setupComplete) {
|
|
470
|
+
setupComplete = true;
|
|
471
|
+
resolve(); // Resolve gracefully instead of rejecting
|
|
472
|
+
}
|
|
469
473
|
});
|
|
470
474
|
});
|
|
471
475
|
};
|
package/src/errors.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import {
|
|
6
6
|
AudioErrorInfo,
|
|
7
7
|
AudioErrorCallback,
|
|
8
|
+
AudioErrorType,
|
|
8
9
|
RetryConfig,
|
|
9
10
|
ErrorRecoveryOptions,
|
|
10
11
|
ExtendedAudioQueueChannel,
|
|
@@ -17,6 +18,7 @@ let globalRetryConfig: RetryConfig = {
|
|
|
17
18
|
baseDelay: 1000,
|
|
18
19
|
enabled: true,
|
|
19
20
|
exponentialBackoff: true,
|
|
21
|
+
fallbackUrls: [],
|
|
20
22
|
maxRetries: 3,
|
|
21
23
|
skipOnFailure: false,
|
|
22
24
|
timeoutMs: 10000
|
|
@@ -143,10 +145,10 @@ export const getRetryConfig = (): RetryConfig => {
|
|
|
143
145
|
* ```typescript
|
|
144
146
|
* setErrorRecovery({
|
|
145
147
|
* autoRetry: true,
|
|
146
|
-
*
|
|
148
|
+
* fallbackToNextTrack: true,
|
|
147
149
|
* logErrorsToAnalytics: true,
|
|
148
150
|
* preserveQueueOnError: true,
|
|
149
|
-
*
|
|
151
|
+
* showUserFeedback: true
|
|
150
152
|
* });
|
|
151
153
|
* ```
|
|
152
154
|
*/
|
|
@@ -247,14 +249,11 @@ export const emitAudioError = (
|
|
|
247
249
|
* @returns The categorized error type
|
|
248
250
|
* @internal
|
|
249
251
|
*/
|
|
250
|
-
export const categorizeError = (
|
|
251
|
-
error: Error,
|
|
252
|
-
audio: HTMLAudioElement
|
|
253
|
-
): AudioErrorInfo['errorType'] => {
|
|
252
|
+
export const categorizeError = (error: Error, audio: HTMLAudioElement): AudioErrorType => {
|
|
254
253
|
const errorMessage = error.message.toLowerCase();
|
|
255
254
|
|
|
256
255
|
if (errorMessage.includes('network') || errorMessage.includes('fetch')) {
|
|
257
|
-
return
|
|
256
|
+
return AudioErrorType.Network;
|
|
258
257
|
}
|
|
259
258
|
|
|
260
259
|
// Check for unsupported format first (more specific than decode)
|
|
@@ -263,35 +262,35 @@ export const categorizeError = (
|
|
|
263
262
|
errorMessage.includes('unsupported') ||
|
|
264
263
|
errorMessage.includes('format not supported')
|
|
265
264
|
) {
|
|
266
|
-
return
|
|
265
|
+
return AudioErrorType.Unsupported;
|
|
267
266
|
}
|
|
268
267
|
|
|
269
268
|
if (errorMessage.includes('decode') || errorMessage.includes('format')) {
|
|
270
|
-
return
|
|
269
|
+
return AudioErrorType.Decode;
|
|
271
270
|
}
|
|
272
271
|
|
|
273
272
|
if (errorMessage.includes('permission') || errorMessage.includes('blocked')) {
|
|
274
|
-
return
|
|
273
|
+
return AudioErrorType.Permission;
|
|
275
274
|
}
|
|
276
275
|
|
|
277
276
|
if (errorMessage.includes('abort')) {
|
|
278
|
-
return
|
|
277
|
+
return AudioErrorType.Abort;
|
|
279
278
|
}
|
|
280
279
|
|
|
281
280
|
if (errorMessage.includes('timeout')) {
|
|
282
|
-
return
|
|
281
|
+
return AudioErrorType.Timeout;
|
|
283
282
|
}
|
|
284
283
|
|
|
285
284
|
// Check audio element network state for more context
|
|
286
285
|
if (audio.networkState === HTMLMediaElement.NETWORK_NO_SOURCE) {
|
|
287
|
-
return
|
|
286
|
+
return AudioErrorType.Network;
|
|
288
287
|
}
|
|
289
288
|
|
|
290
289
|
if (audio.networkState === HTMLMediaElement.NETWORK_LOADING) {
|
|
291
|
-
return
|
|
290
|
+
return AudioErrorType.Timeout;
|
|
292
291
|
}
|
|
293
292
|
|
|
294
|
-
return
|
|
293
|
+
return AudioErrorType.Unknown;
|
|
295
294
|
};
|
|
296
295
|
|
|
297
296
|
/**
|