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/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
- if (typeof channelProp === 'string' &&
39
- !['queue', 'volume', 'isPaused', 'isLocked', 'volumeConfig'].includes(channelProp)) {
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(0); // Stop receiving queue change notifications for channel 0
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(0); // Stop receiving pause notifications for channel 0
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(0); // Stop receiving resume notifications for channel 0
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
- /** Volume level for all other channels when priority channel is active (0-1) */
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
- * Information about an audio error that occurred
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
- src: string;
200
- fileName: string;
210
+ /** The actual error object that was thrown */
201
211
  error: Error;
202
- errorType: 'network' | 'decode' | 'unsupported' | 'permission' | 'abort' | 'timeout' | 'unknown';
203
- timestamp: number;
204
- retryAttempt?: number;
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
- enabled: boolean;
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
- timeoutMs: number;
216
- fallbackUrls?: string[];
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
- showUserFeedback: boolean;
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
- fallbackToNextTrack: boolean;
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 callback support
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
- * Fade type for pause/resume operations with integrated volume transitions
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
- * Easing function types for volume transitions
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
- * Fade type for pause/resume operations with integrated volume transitions
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.fadeVolume = exports.applyVolumeDucking = exports.clearVolumeDucking = exports.setVolumeDucking = exports.setAllChannelsVolume = exports.getAllChannelsVolume = exports.getChannelVolume = exports.setChannelVolume = exports.transitionVolume = exports.getFadeConfig = void 0;
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 : 500;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "audio-channel-queue",
3
- "version": "1.10.0",
3
+ "version": "1.12.0",
4
4
  "description": "Allows you to queue audio files to different playback channels.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
package/src/core.ts CHANGED
@@ -300,10 +300,10 @@ export const queueAudio = async (
300
300
  }
301
301
  }
302
302
 
303
- // Handle priority option (same as addToFront for backward compatibility)
304
- const shouldAddToFront = options?.addToFront || options?.priority;
303
+ // Handle addToFront option
304
+ const shouldAddToFront = options?.addToFront;
305
305
 
306
- // Add to queue based on priority/addToFront option
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
- // Use setTimeout to ensure the queue change event is emitted first
324
- setTimeout(() => {
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 current audio finishes playing
353
+ * @returns Promise that resolves when the audio starts playing (setup complete)
358
354
  * @example
359
355
  * ```typescript
360
- * await playAudioQueue(0); // Play queue for channel 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
- resolve(); // Resolve to prevent hanging
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
- * showUserFeedback: true,
148
+ * fallbackToNextTrack: true,
147
149
  * logErrorsToAnalytics: true,
148
150
  * preserveQueueOnError: true,
149
- * fallbackToNextTrack: true
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 'network';
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 'unsupported';
265
+ return AudioErrorType.Unsupported;
267
266
  }
268
267
 
269
268
  if (errorMessage.includes('decode') || errorMessage.includes('format')) {
270
- return 'decode';
269
+ return AudioErrorType.Decode;
271
270
  }
272
271
 
273
272
  if (errorMessage.includes('permission') || errorMessage.includes('blocked')) {
274
- return 'permission';
273
+ return AudioErrorType.Permission;
275
274
  }
276
275
 
277
276
  if (errorMessage.includes('abort')) {
278
- return 'abort';
277
+ return AudioErrorType.Abort;
279
278
  }
280
279
 
281
280
  if (errorMessage.includes('timeout')) {
282
- return 'timeout';
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 'network';
286
+ return AudioErrorType.Network;
288
287
  }
289
288
 
290
289
  if (audio.networkState === HTMLMediaElement.NETWORK_LOADING) {
291
- return 'timeout';
290
+ return AudioErrorType.Timeout;
292
291
  }
293
292
 
294
- return 'unknown';
293
+ return AudioErrorType.Unknown;
295
294
  };
296
295
 
297
296
  /**