audio-channel-queue 1.6.0 → 1.7.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/core.js +53 -34
- package/dist/errors.d.ts +137 -0
- package/dist/errors.js +461 -0
- package/dist/index.d.ts +9 -25
- package/dist/index.js +23 -33
- package/dist/info.d.ts +2 -1
- package/dist/info.js +8 -1
- package/dist/pause.js +1 -1
- package/dist/types.d.ts +52 -19
- package/dist/volume.js +4 -1
- package/package.json +1 -1
- package/src/core.ts +55 -35
- package/src/errors.ts +480 -0
- package/src/index.ts +71 -84
- package/src/info.ts +8 -1
- package/src/pause.ts +189 -189
- package/src/types.ts +55 -18
- package/src/volume.ts +330 -327
package/dist/types.d.ts
CHANGED
|
@@ -159,25 +159,58 @@ export type AudioPauseCallback = (channelNumber: number, audioInfo: AudioInfo) =
|
|
|
159
159
|
*/
|
|
160
160
|
export type AudioResumeCallback = (channelNumber: number, audioInfo: AudioInfo) => void;
|
|
161
161
|
/**
|
|
162
|
-
*
|
|
163
|
-
*/
|
|
164
|
-
export
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
162
|
+
* Information about an audio error that occurred
|
|
163
|
+
*/
|
|
164
|
+
export interface AudioErrorInfo {
|
|
165
|
+
channelNumber: number;
|
|
166
|
+
src: string;
|
|
167
|
+
fileName: string;
|
|
168
|
+
error: Error;
|
|
169
|
+
errorType: 'network' | 'decode' | 'unsupported' | 'permission' | 'abort' | 'timeout' | 'unknown';
|
|
170
|
+
timestamp: number;
|
|
171
|
+
retryAttempt?: number;
|
|
172
|
+
remainingInQueue: number;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Configuration for automatic retry behavior when audio fails to load or play
|
|
176
|
+
*/
|
|
177
|
+
export interface RetryConfig {
|
|
178
|
+
enabled: boolean;
|
|
179
|
+
maxRetries: number;
|
|
180
|
+
baseDelay: number;
|
|
181
|
+
exponentialBackoff: boolean;
|
|
182
|
+
timeoutMs: number;
|
|
183
|
+
fallbackUrls?: string[];
|
|
184
|
+
skipOnFailure: boolean;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Configuration options for error recovery mechanisms
|
|
188
|
+
*/
|
|
189
|
+
export interface ErrorRecoveryOptions {
|
|
190
|
+
autoRetry: boolean;
|
|
191
|
+
showUserFeedback: boolean;
|
|
192
|
+
logErrorsToAnalytics: boolean;
|
|
193
|
+
preserveQueueOnError: boolean;
|
|
194
|
+
fallbackToNextTrack: boolean;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Callback function type for audio error events
|
|
198
|
+
*/
|
|
199
|
+
export type AudioErrorCallback = (errorInfo: AudioErrorInfo) => void;
|
|
200
|
+
/**
|
|
201
|
+
* Extended audio queue channel with error handling capabilities
|
|
202
|
+
*/
|
|
203
|
+
export interface ExtendedAudioQueueChannel {
|
|
204
|
+
audioCompleteCallbacks: Set<AudioCompleteCallback>;
|
|
205
|
+
audioErrorCallbacks: Set<AudioErrorCallback>;
|
|
206
|
+
audioPauseCallbacks: Set<AudioPauseCallback>;
|
|
207
|
+
audioResumeCallbacks: Set<AudioResumeCallback>;
|
|
208
|
+
audioStartCallbacks: Set<AudioStartCallback>;
|
|
174
209
|
isPaused?: boolean;
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
/** Current volume level for this channel (0-1) */
|
|
210
|
+
progressCallbacks: Map<HTMLAudioElement | null, Set<ProgressCallback>>;
|
|
211
|
+
queue: HTMLAudioElement[];
|
|
212
|
+
queueChangeCallbacks: Set<QueueChangeCallback>;
|
|
213
|
+
retryConfig?: RetryConfig;
|
|
180
214
|
volume?: number;
|
|
181
|
-
/** Volume ducking configuration for this channel */
|
|
182
215
|
volumeConfig?: VolumeConfig;
|
|
183
|
-
}
|
|
216
|
+
}
|
package/dist/volume.js
CHANGED
|
@@ -55,7 +55,7 @@ const transitionVolume = (channelNumber_1, targetVolume_1, ...args_1) => __await
|
|
|
55
55
|
return Promise.resolve();
|
|
56
56
|
}
|
|
57
57
|
// Handle zero duration - instant change
|
|
58
|
-
if (duration
|
|
58
|
+
if (duration === 0) {
|
|
59
59
|
channel.volume = targetVolume;
|
|
60
60
|
if (channel.queue.length > 0) {
|
|
61
61
|
channel.queue[0].volume = targetVolume;
|
|
@@ -115,6 +115,7 @@ const setChannelVolume = (channelNumber, volume, transitionDuration, easing) =>
|
|
|
115
115
|
if (!info_1.audioChannels[channelNumber]) {
|
|
116
116
|
info_1.audioChannels[channelNumber] = {
|
|
117
117
|
audioCompleteCallbacks: new Set(),
|
|
118
|
+
audioErrorCallbacks: new Set(),
|
|
118
119
|
audioPauseCallbacks: new Set(),
|
|
119
120
|
audioResumeCallbacks: new Set(),
|
|
120
121
|
audioStartCallbacks: new Set(),
|
|
@@ -207,6 +208,7 @@ const setVolumeDucking = (config) => {
|
|
|
207
208
|
while (info_1.audioChannels.length <= config.priorityChannel) {
|
|
208
209
|
info_1.audioChannels.push({
|
|
209
210
|
audioCompleteCallbacks: new Set(),
|
|
211
|
+
audioErrorCallbacks: new Set(),
|
|
210
212
|
audioPauseCallbacks: new Set(),
|
|
211
213
|
audioResumeCallbacks: new Set(),
|
|
212
214
|
audioStartCallbacks: new Set(),
|
|
@@ -222,6 +224,7 @@ const setVolumeDucking = (config) => {
|
|
|
222
224
|
if (!info_1.audioChannels[index]) {
|
|
223
225
|
info_1.audioChannels[index] = {
|
|
224
226
|
audioCompleteCallbacks: new Set(),
|
|
227
|
+
audioErrorCallbacks: new Set(),
|
|
225
228
|
audioPauseCallbacks: new Set(),
|
|
226
229
|
audioResumeCallbacks: new Set(),
|
|
227
230
|
audioStartCallbacks: new Set(),
|
package/package.json
CHANGED
package/src/core.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
cleanupProgressTracking
|
|
14
14
|
} from './events';
|
|
15
15
|
import { applyVolumeDucking, restoreVolumeLevels } from './volume';
|
|
16
|
+
import { setupAudioErrorHandling, handleAudioError } from './errors';
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Queues an audio file to a specific channel and starts playing if it's the first in queue
|
|
@@ -33,9 +34,11 @@ export const queueAudio = async (
|
|
|
33
34
|
channelNumber: number = 0,
|
|
34
35
|
options?: AudioQueueOptions
|
|
35
36
|
): Promise<void> => {
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
// Ensure the channel exists
|
|
38
|
+
while (audioChannels.length <= channelNumber) {
|
|
39
|
+
audioChannels.push({
|
|
38
40
|
audioCompleteCallbacks: new Set(),
|
|
41
|
+
audioErrorCallbacks: new Set(),
|
|
39
42
|
audioPauseCallbacks: new Set(),
|
|
40
43
|
audioResumeCallbacks: new Set(),
|
|
41
44
|
audioStartCallbacks: new Set(),
|
|
@@ -44,46 +47,56 @@ export const queueAudio = async (
|
|
|
44
47
|
queue: [],
|
|
45
48
|
queueChangeCallbacks: new Set(),
|
|
46
49
|
volume: 1.0
|
|
47
|
-
};
|
|
50
|
+
});
|
|
48
51
|
}
|
|
49
52
|
|
|
53
|
+
const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
|
|
50
54
|
const audio: HTMLAudioElement = new Audio(audioUrl);
|
|
51
|
-
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
audio
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
55
|
+
|
|
56
|
+
// Set up comprehensive error handling
|
|
57
|
+
setupAudioErrorHandling(audio, channelNumber, audioUrl, async (error: Error) => {
|
|
58
|
+
await handleAudioError(audio, channelNumber, audioUrl, error);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Apply options if provided
|
|
62
|
+
if (options) {
|
|
63
|
+
if (typeof options.loop === 'boolean') {
|
|
64
|
+
audio.loop = options.loop;
|
|
65
|
+
}
|
|
66
|
+
if (typeof options.volume === 'number' && !isNaN(options.volume)) {
|
|
67
|
+
const clampedVolume = Math.max(0, Math.min(1, options.volume));
|
|
68
|
+
audio.volume = clampedVolume;
|
|
69
|
+
// Set channel volume to match the audio volume
|
|
70
|
+
channel.volume = clampedVolume;
|
|
71
|
+
}
|
|
68
72
|
}
|
|
69
73
|
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
//
|
|
76
|
-
|
|
74
|
+
// Handle priority option (same as addToFront for backward compatibility)
|
|
75
|
+
const shouldAddToFront = options?.addToFront || options?.priority;
|
|
76
|
+
|
|
77
|
+
// Add to queue based on priority/addToFront option
|
|
78
|
+
if (shouldAddToFront && channel.queue.length > 0) {
|
|
79
|
+
// Insert after currently playing track (at index 1)
|
|
80
|
+
channel.queue.splice(1, 0, audio);
|
|
81
|
+
} else if (shouldAddToFront) {
|
|
82
|
+
// If queue is empty, add to front
|
|
83
|
+
channel.queue.unshift(audio);
|
|
77
84
|
} else {
|
|
78
|
-
//
|
|
79
|
-
|
|
85
|
+
// Add to back of queue
|
|
86
|
+
channel.queue.push(audio);
|
|
80
87
|
}
|
|
81
88
|
|
|
89
|
+
// Emit queue change event
|
|
82
90
|
emitQueueChange(channelNumber, audioChannels);
|
|
83
91
|
|
|
84
|
-
if
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
// Start playing if this is the first item and channel isn't paused
|
|
93
|
+
if (channel.queue.length === 1 && !channel.isPaused) {
|
|
94
|
+
// Use setTimeout to ensure the queue change event is emitted first
|
|
95
|
+
setTimeout(() => {
|
|
96
|
+
playAudioQueue(channelNumber).catch((error: Error) => {
|
|
97
|
+
handleAudioError(audio, channelNumber, audioUrl, error);
|
|
98
|
+
});
|
|
99
|
+
}, 0);
|
|
87
100
|
}
|
|
88
101
|
};
|
|
89
102
|
|
|
@@ -188,8 +201,11 @@ export const playAudioQueue = async (channelNumber: number): Promise<void> => {
|
|
|
188
201
|
if (currentAudio.loop) {
|
|
189
202
|
// For looping audio, reset current time and continue playing
|
|
190
203
|
currentAudio.currentTime = 0;
|
|
191
|
-
|
|
192
|
-
|
|
204
|
+
try {
|
|
205
|
+
await currentAudio.play();
|
|
206
|
+
} catch (error) {
|
|
207
|
+
await handleAudioError(currentAudio, channelNumber, currentAudio.src, error as Error);
|
|
208
|
+
}
|
|
193
209
|
resolve();
|
|
194
210
|
} else {
|
|
195
211
|
// For non-looping audio, remove from queue and play next
|
|
@@ -213,7 +229,11 @@ export const playAudioQueue = async (channelNumber: number): Promise<void> => {
|
|
|
213
229
|
metadataLoaded = true;
|
|
214
230
|
}
|
|
215
231
|
|
|
216
|
-
|
|
232
|
+
// Enhanced play with error handling
|
|
233
|
+
currentAudio.play().catch(async (error: Error) => {
|
|
234
|
+
await handleAudioError(currentAudio, channelNumber, currentAudio.src, error);
|
|
235
|
+
resolve(); // Resolve to prevent hanging
|
|
236
|
+
});
|
|
217
237
|
});
|
|
218
238
|
};
|
|
219
239
|
|