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/errors.js
ADDED
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Error handling, retry logic, and recovery mechanisms for the audio-channel-queue package
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
22
|
+
if (mod && mod.__esModule) return mod;
|
|
23
|
+
var result = {};
|
|
24
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
25
|
+
__setModuleDefault(result, mod);
|
|
26
|
+
return result;
|
|
27
|
+
};
|
|
28
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
29
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
30
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
31
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
32
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
33
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
34
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.createProtectedAudioElement = exports.handleAudioError = exports.setupAudioErrorHandling = exports.categorizeError = exports.emitAudioError = exports.retryFailedAudio = exports.getErrorRecovery = exports.setErrorRecovery = exports.getRetryConfig = exports.setRetryConfig = exports.offAudioError = exports.onAudioError = void 0;
|
|
39
|
+
const info_1 = require("./info");
|
|
40
|
+
const utils_1 = require("./utils");
|
|
41
|
+
let globalRetryConfig = {
|
|
42
|
+
enabled: true,
|
|
43
|
+
maxRetries: 3,
|
|
44
|
+
baseDelay: 1000,
|
|
45
|
+
exponentialBackoff: true,
|
|
46
|
+
timeoutMs: 10000,
|
|
47
|
+
skipOnFailure: false
|
|
48
|
+
};
|
|
49
|
+
let globalErrorRecovery = {
|
|
50
|
+
autoRetry: true,
|
|
51
|
+
showUserFeedback: false,
|
|
52
|
+
logErrorsToAnalytics: false,
|
|
53
|
+
preserveQueueOnError: true,
|
|
54
|
+
fallbackToNextTrack: true
|
|
55
|
+
};
|
|
56
|
+
const retryAttempts = new WeakMap();
|
|
57
|
+
const loadTimeouts = new WeakMap();
|
|
58
|
+
/**
|
|
59
|
+
* Subscribes to audio error events for a specific channel
|
|
60
|
+
* @param channelNumber - The channel number to listen to (defaults to 0)
|
|
61
|
+
* @param callback - Function to call when an audio error occurs
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* onAudioError(0, (errorInfo) => {
|
|
65
|
+
* console.log(`Audio error: ${errorInfo.error.message}`);
|
|
66
|
+
* console.log(`Error type: ${errorInfo.errorType}`);
|
|
67
|
+
* });
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
const onAudioError = (channelNumber = 0, callback) => {
|
|
71
|
+
// Ensure channel exists
|
|
72
|
+
while (info_1.audioChannels.length <= channelNumber) {
|
|
73
|
+
info_1.audioChannels.push({
|
|
74
|
+
audioCompleteCallbacks: new Set(),
|
|
75
|
+
audioErrorCallbacks: new Set(),
|
|
76
|
+
audioPauseCallbacks: new Set(),
|
|
77
|
+
audioResumeCallbacks: new Set(),
|
|
78
|
+
audioStartCallbacks: new Set(),
|
|
79
|
+
isPaused: false,
|
|
80
|
+
progressCallbacks: new Map(),
|
|
81
|
+
queue: [],
|
|
82
|
+
queueChangeCallbacks: new Set(),
|
|
83
|
+
volume: 1.0
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
87
|
+
if (!channel.audioErrorCallbacks) {
|
|
88
|
+
channel.audioErrorCallbacks = new Set();
|
|
89
|
+
}
|
|
90
|
+
channel.audioErrorCallbacks.add(callback);
|
|
91
|
+
};
|
|
92
|
+
exports.onAudioError = onAudioError;
|
|
93
|
+
/**
|
|
94
|
+
* Unsubscribes from audio error events for a specific channel
|
|
95
|
+
* @param channelNumber - The channel number to stop listening to (defaults to 0)
|
|
96
|
+
* @param callback - The specific callback to remove (optional - if not provided, removes all)
|
|
97
|
+
* @example
|
|
98
|
+
* ```typescript
|
|
99
|
+
* offAudioError(0); // Remove all error callbacks for channel 0
|
|
100
|
+
* offAudioError(0, specificCallback); // Remove specific callback
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
const offAudioError = (channelNumber = 0, callback) => {
|
|
104
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
105
|
+
if (!(channel === null || channel === void 0 ? void 0 : channel.audioErrorCallbacks))
|
|
106
|
+
return;
|
|
107
|
+
if (callback) {
|
|
108
|
+
channel.audioErrorCallbacks.delete(callback);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
channel.audioErrorCallbacks.clear();
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
exports.offAudioError = offAudioError;
|
|
115
|
+
/**
|
|
116
|
+
* Sets the global retry configuration for audio loading failures
|
|
117
|
+
* @param config - Retry configuration options
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* setRetryConfig({
|
|
121
|
+
* enabled: true,
|
|
122
|
+
* maxRetries: 5,
|
|
123
|
+
* baseDelay: 1000,
|
|
124
|
+
* exponentialBackoff: true,
|
|
125
|
+
* timeoutMs: 15000,
|
|
126
|
+
* fallbackUrls: ['https://cdn.backup.com/audio/'],
|
|
127
|
+
* skipOnFailure: true
|
|
128
|
+
* });
|
|
129
|
+
* ```
|
|
130
|
+
*/
|
|
131
|
+
const setRetryConfig = (config) => {
|
|
132
|
+
globalRetryConfig = Object.assign(Object.assign({}, globalRetryConfig), config);
|
|
133
|
+
};
|
|
134
|
+
exports.setRetryConfig = setRetryConfig;
|
|
135
|
+
/**
|
|
136
|
+
* Gets the current global retry configuration
|
|
137
|
+
* @returns Current retry configuration
|
|
138
|
+
* @example
|
|
139
|
+
* ```typescript
|
|
140
|
+
* const config = getRetryConfig();
|
|
141
|
+
* console.log(`Max retries: ${config.maxRetries}`);
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
const getRetryConfig = () => {
|
|
145
|
+
return Object.assign({}, globalRetryConfig);
|
|
146
|
+
};
|
|
147
|
+
exports.getRetryConfig = getRetryConfig;
|
|
148
|
+
/**
|
|
149
|
+
* Sets the global error recovery configuration
|
|
150
|
+
* @param options - Error recovery options
|
|
151
|
+
* @example
|
|
152
|
+
* ```typescript
|
|
153
|
+
* setErrorRecovery({
|
|
154
|
+
* autoRetry: true,
|
|
155
|
+
* showUserFeedback: true,
|
|
156
|
+
* logErrorsToAnalytics: true,
|
|
157
|
+
* preserveQueueOnError: true,
|
|
158
|
+
* fallbackToNextTrack: true
|
|
159
|
+
* });
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
const setErrorRecovery = (options) => {
|
|
163
|
+
globalErrorRecovery = Object.assign(Object.assign({}, globalErrorRecovery), options);
|
|
164
|
+
};
|
|
165
|
+
exports.setErrorRecovery = setErrorRecovery;
|
|
166
|
+
/**
|
|
167
|
+
* Gets the current global error recovery configuration
|
|
168
|
+
* @returns Current error recovery configuration
|
|
169
|
+
* @example
|
|
170
|
+
* ```typescript
|
|
171
|
+
* const recovery = getErrorRecovery();
|
|
172
|
+
* console.log(`Auto retry enabled: ${recovery.autoRetry}`);
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
const getErrorRecovery = () => {
|
|
176
|
+
return Object.assign({}, globalErrorRecovery);
|
|
177
|
+
};
|
|
178
|
+
exports.getErrorRecovery = getErrorRecovery;
|
|
179
|
+
/**
|
|
180
|
+
* Manually retries loading failed audio for a specific channel
|
|
181
|
+
* @param channelNumber - The channel number to retry (defaults to 0)
|
|
182
|
+
* @returns Promise that resolves to true if retry was successful, false otherwise
|
|
183
|
+
* @example
|
|
184
|
+
* ```typescript
|
|
185
|
+
* const success = await retryFailedAudio(0);
|
|
186
|
+
* if (success) {
|
|
187
|
+
* console.log('Audio retry successful');
|
|
188
|
+
* } else {
|
|
189
|
+
* console.log('Audio retry failed');
|
|
190
|
+
* }
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
const retryFailedAudio = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (channelNumber = 0) {
|
|
194
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
195
|
+
if (!channel || channel.queue.length === 0)
|
|
196
|
+
return false;
|
|
197
|
+
const currentAudio = channel.queue[0];
|
|
198
|
+
const currentAttempts = retryAttempts.get(currentAudio) || 0;
|
|
199
|
+
if (currentAttempts >= globalRetryConfig.maxRetries) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
// Reset the audio element
|
|
204
|
+
currentAudio.currentTime = 0;
|
|
205
|
+
yield currentAudio.play();
|
|
206
|
+
// Reset retry counter on successful play
|
|
207
|
+
retryAttempts.delete(currentAudio);
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
catch (error) {
|
|
211
|
+
// Increment retry counter
|
|
212
|
+
retryAttempts.set(currentAudio, currentAttempts + 1);
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
exports.retryFailedAudio = retryFailedAudio;
|
|
217
|
+
/**
|
|
218
|
+
* Emits an audio error event to all registered listeners for a specific channel
|
|
219
|
+
* @param channelNumber - The channel number where the error occurred
|
|
220
|
+
* @param errorInfo - Information about the error
|
|
221
|
+
* @param audioChannels - Array of audio channels
|
|
222
|
+
* @internal
|
|
223
|
+
*/
|
|
224
|
+
const emitAudioError = (channelNumber, errorInfo, audioChannels) => {
|
|
225
|
+
const channel = audioChannels[channelNumber];
|
|
226
|
+
if (!(channel === null || channel === void 0 ? void 0 : channel.audioErrorCallbacks))
|
|
227
|
+
return;
|
|
228
|
+
// Log to analytics if enabled
|
|
229
|
+
if (globalErrorRecovery.logErrorsToAnalytics) {
|
|
230
|
+
console.warn('Audio Error Analytics:', errorInfo);
|
|
231
|
+
}
|
|
232
|
+
channel.audioErrorCallbacks.forEach(callback => {
|
|
233
|
+
try {
|
|
234
|
+
callback(errorInfo);
|
|
235
|
+
}
|
|
236
|
+
catch (error) {
|
|
237
|
+
console.error('Error in audio error callback:', error);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
};
|
|
241
|
+
exports.emitAudioError = emitAudioError;
|
|
242
|
+
/**
|
|
243
|
+
* Determines the error type based on the error object and context
|
|
244
|
+
* @param error - The error that occurred
|
|
245
|
+
* @param audio - The audio element that failed
|
|
246
|
+
* @returns The categorized error type
|
|
247
|
+
* @internal
|
|
248
|
+
*/
|
|
249
|
+
const categorizeError = (error, audio) => {
|
|
250
|
+
const errorMessage = error.message.toLowerCase();
|
|
251
|
+
if (errorMessage.includes('network') || errorMessage.includes('fetch')) {
|
|
252
|
+
return 'network';
|
|
253
|
+
}
|
|
254
|
+
// Check for unsupported format first (more specific than decode)
|
|
255
|
+
if (errorMessage.includes('not supported') || errorMessage.includes('unsupported') ||
|
|
256
|
+
errorMessage.includes('format not supported')) {
|
|
257
|
+
return 'unsupported';
|
|
258
|
+
}
|
|
259
|
+
if (errorMessage.includes('decode') || errorMessage.includes('format')) {
|
|
260
|
+
return 'decode';
|
|
261
|
+
}
|
|
262
|
+
if (errorMessage.includes('permission') || errorMessage.includes('blocked')) {
|
|
263
|
+
return 'permission';
|
|
264
|
+
}
|
|
265
|
+
if (errorMessage.includes('abort')) {
|
|
266
|
+
return 'abort';
|
|
267
|
+
}
|
|
268
|
+
if (errorMessage.includes('timeout')) {
|
|
269
|
+
return 'timeout';
|
|
270
|
+
}
|
|
271
|
+
// Check audio element network state for more context
|
|
272
|
+
if (audio.networkState === HTMLMediaElement.NETWORK_NO_SOURCE) {
|
|
273
|
+
return 'network';
|
|
274
|
+
}
|
|
275
|
+
if (audio.networkState === HTMLMediaElement.NETWORK_LOADING) {
|
|
276
|
+
return 'timeout';
|
|
277
|
+
}
|
|
278
|
+
return 'unknown';
|
|
279
|
+
};
|
|
280
|
+
exports.categorizeError = categorizeError;
|
|
281
|
+
/**
|
|
282
|
+
* Sets up comprehensive error handling for an audio element
|
|
283
|
+
* @param audio - The audio element to set up error handling for
|
|
284
|
+
* @param channelNumber - The channel number this audio belongs to
|
|
285
|
+
* @param originalUrl - The original URL that was requested
|
|
286
|
+
* @param onError - Callback for when an error occurs
|
|
287
|
+
* @internal
|
|
288
|
+
*/
|
|
289
|
+
const setupAudioErrorHandling = (audio, channelNumber, originalUrl, onError) => {
|
|
290
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
291
|
+
if (!channel)
|
|
292
|
+
return;
|
|
293
|
+
// Set up loading timeout with test environment compatibility
|
|
294
|
+
let timeoutId;
|
|
295
|
+
if (typeof setTimeout !== 'undefined') {
|
|
296
|
+
timeoutId = setTimeout(() => {
|
|
297
|
+
if (audio.networkState === HTMLMediaElement.NETWORK_LOADING) {
|
|
298
|
+
const timeoutError = new Error(`Audio loading timeout after ${globalRetryConfig.timeoutMs}ms`);
|
|
299
|
+
(0, exports.handleAudioError)(audio, channelNumber, originalUrl, timeoutError);
|
|
300
|
+
}
|
|
301
|
+
}, globalRetryConfig.timeoutMs);
|
|
302
|
+
loadTimeouts.set(audio, timeoutId);
|
|
303
|
+
}
|
|
304
|
+
// Clear timeout when metadata loads successfully
|
|
305
|
+
const handleLoadSuccess = () => {
|
|
306
|
+
if (typeof setTimeout !== 'undefined') {
|
|
307
|
+
const timeoutId = loadTimeouts.get(audio);
|
|
308
|
+
if (timeoutId) {
|
|
309
|
+
clearTimeout(timeoutId);
|
|
310
|
+
loadTimeouts.delete(audio);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
// Handle various error events
|
|
315
|
+
const handleError = (event) => {
|
|
316
|
+
var _a;
|
|
317
|
+
if (typeof setTimeout !== 'undefined') {
|
|
318
|
+
const timeoutId = loadTimeouts.get(audio);
|
|
319
|
+
if (timeoutId) {
|
|
320
|
+
clearTimeout(timeoutId);
|
|
321
|
+
loadTimeouts.delete(audio);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const error = new Error(`Audio loading failed: ${((_a = audio.error) === null || _a === void 0 ? void 0 : _a.message) || 'Unknown error'}`);
|
|
325
|
+
(0, exports.handleAudioError)(audio, channelNumber, originalUrl, error);
|
|
326
|
+
};
|
|
327
|
+
const handleAbort = () => {
|
|
328
|
+
const error = new Error('Audio loading was aborted');
|
|
329
|
+
(0, exports.handleAudioError)(audio, channelNumber, originalUrl, error);
|
|
330
|
+
};
|
|
331
|
+
const handleStall = () => {
|
|
332
|
+
const error = new Error('Audio loading stalled');
|
|
333
|
+
(0, exports.handleAudioError)(audio, channelNumber, originalUrl, error);
|
|
334
|
+
};
|
|
335
|
+
// Add event listeners
|
|
336
|
+
audio.addEventListener('error', handleError);
|
|
337
|
+
audio.addEventListener('abort', handleAbort);
|
|
338
|
+
audio.addEventListener('stalled', handleStall);
|
|
339
|
+
audio.addEventListener('loadedmetadata', handleLoadSuccess);
|
|
340
|
+
audio.addEventListener('canplay', handleLoadSuccess);
|
|
341
|
+
// Custom play error handling
|
|
342
|
+
if (onError) {
|
|
343
|
+
const originalPlay = audio.play.bind(audio);
|
|
344
|
+
const wrappedPlay = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
345
|
+
try {
|
|
346
|
+
yield originalPlay();
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
yield onError(error);
|
|
350
|
+
throw error;
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
audio.play = wrappedPlay;
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
exports.setupAudioErrorHandling = setupAudioErrorHandling;
|
|
357
|
+
/**
|
|
358
|
+
* Handles audio errors with retry logic and recovery mechanisms
|
|
359
|
+
* @param audio - The audio element that failed
|
|
360
|
+
* @param channelNumber - The channel number
|
|
361
|
+
* @param originalUrl - The original URL that was requested
|
|
362
|
+
* @param error - The error that occurred
|
|
363
|
+
* @internal
|
|
364
|
+
*/
|
|
365
|
+
const handleAudioError = (audio, channelNumber, originalUrl, error) => __awaiter(void 0, void 0, void 0, function* () {
|
|
366
|
+
const channel = info_1.audioChannels[channelNumber];
|
|
367
|
+
if (!channel)
|
|
368
|
+
return;
|
|
369
|
+
const currentAttempts = retryAttempts.get(audio) || 0;
|
|
370
|
+
const retryConfig = channel.retryConfig || globalRetryConfig;
|
|
371
|
+
const errorInfo = {
|
|
372
|
+
channelNumber,
|
|
373
|
+
src: originalUrl,
|
|
374
|
+
fileName: (0, utils_1.extractFileName)(originalUrl),
|
|
375
|
+
error,
|
|
376
|
+
errorType: (0, exports.categorizeError)(error, audio),
|
|
377
|
+
timestamp: Date.now(),
|
|
378
|
+
retryAttempt: currentAttempts,
|
|
379
|
+
remainingInQueue: channel.queue.length - 1
|
|
380
|
+
};
|
|
381
|
+
// Emit error event
|
|
382
|
+
(0, exports.emitAudioError)(channelNumber, errorInfo, info_1.audioChannels);
|
|
383
|
+
// Attempt retry if enabled and within limits
|
|
384
|
+
if (retryConfig.enabled && currentAttempts < retryConfig.maxRetries && globalErrorRecovery.autoRetry) {
|
|
385
|
+
const delay = retryConfig.exponentialBackoff
|
|
386
|
+
? retryConfig.baseDelay * Math.pow(2, currentAttempts)
|
|
387
|
+
: retryConfig.baseDelay;
|
|
388
|
+
retryAttempts.set(audio, currentAttempts + 1);
|
|
389
|
+
const retryFunction = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
390
|
+
try {
|
|
391
|
+
// Try fallback URLs if available
|
|
392
|
+
if (retryConfig.fallbackUrls && retryConfig.fallbackUrls.length > 0) {
|
|
393
|
+
const fallbackIndex = currentAttempts % retryConfig.fallbackUrls.length;
|
|
394
|
+
const fallbackUrl = retryConfig.fallbackUrls[fallbackIndex] + (0, utils_1.extractFileName)(originalUrl);
|
|
395
|
+
audio.src = fallbackUrl;
|
|
396
|
+
}
|
|
397
|
+
yield audio.load();
|
|
398
|
+
yield audio.play();
|
|
399
|
+
// Reset retry counter on success
|
|
400
|
+
retryAttempts.delete(audio);
|
|
401
|
+
}
|
|
402
|
+
catch (retryError) {
|
|
403
|
+
yield (0, exports.handleAudioError)(audio, channelNumber, originalUrl, retryError);
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
setTimeout(retryFunction, delay);
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
// Max retries reached or retry disabled
|
|
410
|
+
if (retryConfig.skipOnFailure || globalErrorRecovery.fallbackToNextTrack) {
|
|
411
|
+
// Skip to next track in queue
|
|
412
|
+
channel.queue.shift();
|
|
413
|
+
// Import and use playAudioQueue to continue with next track
|
|
414
|
+
const { playAudioQueue } = yield Promise.resolve().then(() => __importStar(require('./core')));
|
|
415
|
+
playAudioQueue(channelNumber).catch(console.error);
|
|
416
|
+
}
|
|
417
|
+
else if (!globalErrorRecovery.preserveQueueOnError) {
|
|
418
|
+
// Clear the entire queue on failure
|
|
419
|
+
channel.queue = [];
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
exports.handleAudioError = handleAudioError;
|
|
424
|
+
/**
|
|
425
|
+
* Creates a timeout-protected audio element with comprehensive error handling
|
|
426
|
+
* @param url - The audio URL to load
|
|
427
|
+
* @param channelNumber - The channel number this audio belongs to
|
|
428
|
+
* @returns Promise that resolves to the configured audio element
|
|
429
|
+
* @internal
|
|
430
|
+
*/
|
|
431
|
+
const createProtectedAudioElement = (url, channelNumber) => __awaiter(void 0, void 0, void 0, function* () {
|
|
432
|
+
const audio = new Audio();
|
|
433
|
+
return new Promise((resolve, reject) => {
|
|
434
|
+
const cleanup = () => {
|
|
435
|
+
const timeoutId = loadTimeouts.get(audio);
|
|
436
|
+
if (timeoutId) {
|
|
437
|
+
clearTimeout(timeoutId);
|
|
438
|
+
loadTimeouts.delete(audio);
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
const handleSuccess = () => {
|
|
442
|
+
cleanup();
|
|
443
|
+
resolve(audio);
|
|
444
|
+
};
|
|
445
|
+
const handleError = (error) => {
|
|
446
|
+
cleanup();
|
|
447
|
+
reject(error);
|
|
448
|
+
};
|
|
449
|
+
// Set up error handling
|
|
450
|
+
(0, exports.setupAudioErrorHandling)(audio, channelNumber, url, (error) => __awaiter(void 0, void 0, void 0, function* () {
|
|
451
|
+
handleError(error);
|
|
452
|
+
}));
|
|
453
|
+
// Set up success handlers
|
|
454
|
+
audio.addEventListener('canplay', handleSuccess, { once: true });
|
|
455
|
+
audio.addEventListener('loadedmetadata', handleSuccess, { once: true });
|
|
456
|
+
// Start loading
|
|
457
|
+
audio.src = url;
|
|
458
|
+
audio.load();
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
exports.createProtectedAudioElement = createProtectedAudioElement;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,29 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Main entry point for the audio-channel-queue package
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* multi-channel support, pause/resume functionality, volume control with ducking,
|
|
6
|
-
* looping capabilities, and extensive event handling.
|
|
7
|
-
*
|
|
8
|
-
* @example Basic Usage
|
|
9
|
-
* ```typescript
|
|
10
|
-
* import { queueAudio, onAudioProgress, pauseChannel } from 'audio-channel-queue';
|
|
11
|
-
*
|
|
12
|
-
* // Queue an audio file
|
|
13
|
-
* await queueAudio('song.mp3');
|
|
14
|
-
*
|
|
15
|
-
* // Track progress
|
|
16
|
-
* onAudioProgress(0, (info) => {
|
|
17
|
-
* console.log(`Progress: ${info.progress * 100}%`);
|
|
18
|
-
* });
|
|
19
|
-
*
|
|
20
|
-
* // Pause playback
|
|
21
|
-
* await pauseChannel(0);
|
|
22
|
-
* ```
|
|
3
|
+
* Exports all public functions and types for audio queue management, pause/resume controls,
|
|
4
|
+
* volume management with ducking, progress tracking, and comprehensive event system
|
|
23
5
|
*/
|
|
24
|
-
export
|
|
25
|
-
export {
|
|
26
|
-
export { getAllChannelsPauseState, isChannelPaused, pauseAllChannels, pauseChannel, resumeAllChannels, resumeChannel, togglePauseAllChannels, togglePauseChannel } from './pause';
|
|
27
|
-
export {
|
|
28
|
-
export {
|
|
6
|
+
export { queueAudio, queueAudioPriority, stopCurrentAudioInChannel, stopAllAudioInChannel, stopAllAudio, playAudioQueue } from './core';
|
|
7
|
+
export { getErrorRecovery, getRetryConfig, offAudioError, onAudioError, retryFailedAudio, setErrorRecovery, setRetryConfig, } from './errors';
|
|
8
|
+
export { getAllChannelsPauseState, isChannelPaused, pauseAllChannels, pauseChannel, resumeAllChannels, resumeChannel, togglePauseAllChannels, togglePauseChannel, } from './pause';
|
|
9
|
+
export { clearVolumeDucking, getAllChannelsVolume, getChannelVolume, setAllChannelsVolume, setChannelVolume, setVolumeDucking, } from './volume';
|
|
10
|
+
export { getAllChannelsInfo, getCurrentAudioInfo, getQueueSnapshot, offAudioPause, offAudioProgress, offAudioResume, offQueueChange, onAudioComplete, onAudioPause, onAudioProgress, onAudioResume, onAudioStart, onQueueChange, } from './info';
|
|
11
|
+
export { audioChannels } from './info';
|
|
29
12
|
export { cleanWebpackFilename, createQueueSnapshot, extractFileName, getAudioInfoFromElement } from './utils';
|
|
13
|
+
export type { AudioCompleteCallback, AudioCompleteInfo, AudioErrorCallback, AudioErrorInfo, AudioInfo, AudioPauseCallback, AudioQueueOptions, AudioResumeCallback, AudioStartCallback, AudioStartInfo, ErrorRecoveryOptions, ExtendedAudioQueueChannel, ProgressCallback, QueueChangeCallback, QueueItem, QueueSnapshot, RetryConfig, VolumeConfig } from './types';
|
package/dist/index.js
CHANGED
|
@@ -1,38 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* @fileoverview Main entry point for the audio-channel-queue package
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* multi-channel support, pause/resume functionality, volume control with ducking,
|
|
7
|
-
* looping capabilities, and extensive event handling.
|
|
8
|
-
*
|
|
9
|
-
* @example Basic Usage
|
|
10
|
-
* ```typescript
|
|
11
|
-
* import { queueAudio, onAudioProgress, pauseChannel } from 'audio-channel-queue';
|
|
12
|
-
*
|
|
13
|
-
* // Queue an audio file
|
|
14
|
-
* await queueAudio('song.mp3');
|
|
15
|
-
*
|
|
16
|
-
* // Track progress
|
|
17
|
-
* onAudioProgress(0, (info) => {
|
|
18
|
-
* console.log(`Progress: ${info.progress * 100}%`);
|
|
19
|
-
* });
|
|
20
|
-
*
|
|
21
|
-
* // Pause playback
|
|
22
|
-
* await pauseChannel(0);
|
|
23
|
-
* ```
|
|
4
|
+
* Exports all public functions and types for audio queue management, pause/resume controls,
|
|
5
|
+
* volume management with ducking, progress tracking, and comprehensive event system
|
|
24
6
|
*/
|
|
25
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.getAudioInfoFromElement = exports.extractFileName = exports.createQueueSnapshot = exports.cleanWebpackFilename = exports.onQueueChange = exports.onAudioStart = exports.onAudioResume = exports.onAudioProgress = exports.onAudioPause = exports.onAudioComplete = exports.offQueueChange = exports.offAudioResume = exports.offAudioProgress = exports.offAudioPause = exports.getQueueSnapshot = exports.getCurrentAudioInfo = exports.getAllChannelsInfo = exports.
|
|
27
|
-
//
|
|
8
|
+
exports.getAudioInfoFromElement = exports.extractFileName = exports.createQueueSnapshot = exports.cleanWebpackFilename = exports.audioChannels = exports.onQueueChange = exports.onAudioStart = exports.onAudioResume = exports.onAudioProgress = exports.onAudioPause = exports.onAudioComplete = exports.offQueueChange = exports.offAudioResume = exports.offAudioProgress = exports.offAudioPause = exports.getQueueSnapshot = exports.getCurrentAudioInfo = exports.getAllChannelsInfo = exports.setVolumeDucking = exports.setChannelVolume = exports.setAllChannelsVolume = exports.getChannelVolume = exports.getAllChannelsVolume = exports.clearVolumeDucking = exports.togglePauseChannel = exports.togglePauseAllChannels = exports.resumeChannel = exports.resumeAllChannels = exports.pauseChannel = exports.pauseAllChannels = exports.isChannelPaused = exports.getAllChannelsPauseState = exports.setRetryConfig = exports.setErrorRecovery = exports.retryFailedAudio = exports.onAudioError = exports.offAudioError = exports.getRetryConfig = exports.getErrorRecovery = exports.playAudioQueue = exports.stopAllAudio = exports.stopAllAudioInChannel = exports.stopCurrentAudioInChannel = exports.queueAudioPriority = exports.queueAudio = void 0;
|
|
9
|
+
// Core queue management functions
|
|
28
10
|
var core_1 = require("./core");
|
|
29
|
-
Object.defineProperty(exports, "playAudioQueue", { enumerable: true, get: function () { return core_1.playAudioQueue; } });
|
|
30
11
|
Object.defineProperty(exports, "queueAudio", { enumerable: true, get: function () { return core_1.queueAudio; } });
|
|
31
12
|
Object.defineProperty(exports, "queueAudioPriority", { enumerable: true, get: function () { return core_1.queueAudioPriority; } });
|
|
32
|
-
Object.defineProperty(exports, "stopAllAudio", { enumerable: true, get: function () { return core_1.stopAllAudio; } });
|
|
33
|
-
Object.defineProperty(exports, "stopAllAudioInChannel", { enumerable: true, get: function () { return core_1.stopAllAudioInChannel; } });
|
|
34
13
|
Object.defineProperty(exports, "stopCurrentAudioInChannel", { enumerable: true, get: function () { return core_1.stopCurrentAudioInChannel; } });
|
|
35
|
-
|
|
14
|
+
Object.defineProperty(exports, "stopAllAudioInChannel", { enumerable: true, get: function () { return core_1.stopAllAudioInChannel; } });
|
|
15
|
+
Object.defineProperty(exports, "stopAllAudio", { enumerable: true, get: function () { return core_1.stopAllAudio; } });
|
|
16
|
+
Object.defineProperty(exports, "playAudioQueue", { enumerable: true, get: function () { return core_1.playAudioQueue; } });
|
|
17
|
+
// Error handling and recovery functions
|
|
18
|
+
var errors_1 = require("./errors");
|
|
19
|
+
Object.defineProperty(exports, "getErrorRecovery", { enumerable: true, get: function () { return errors_1.getErrorRecovery; } });
|
|
20
|
+
Object.defineProperty(exports, "getRetryConfig", { enumerable: true, get: function () { return errors_1.getRetryConfig; } });
|
|
21
|
+
Object.defineProperty(exports, "offAudioError", { enumerable: true, get: function () { return errors_1.offAudioError; } });
|
|
22
|
+
Object.defineProperty(exports, "onAudioError", { enumerable: true, get: function () { return errors_1.onAudioError; } });
|
|
23
|
+
Object.defineProperty(exports, "retryFailedAudio", { enumerable: true, get: function () { return errors_1.retryFailedAudio; } });
|
|
24
|
+
Object.defineProperty(exports, "setErrorRecovery", { enumerable: true, get: function () { return errors_1.setErrorRecovery; } });
|
|
25
|
+
Object.defineProperty(exports, "setRetryConfig", { enumerable: true, get: function () { return errors_1.setRetryConfig; } });
|
|
26
|
+
// Pause and resume management functions
|
|
36
27
|
var pause_1 = require("./pause");
|
|
37
28
|
Object.defineProperty(exports, "getAllChannelsPauseState", { enumerable: true, get: function () { return pause_1.getAllChannelsPauseState; } });
|
|
38
29
|
Object.defineProperty(exports, "isChannelPaused", { enumerable: true, get: function () { return pause_1.isChannelPaused; } });
|
|
@@ -42,20 +33,16 @@ Object.defineProperty(exports, "resumeAllChannels", { enumerable: true, get: fun
|
|
|
42
33
|
Object.defineProperty(exports, "resumeChannel", { enumerable: true, get: function () { return pause_1.resumeChannel; } });
|
|
43
34
|
Object.defineProperty(exports, "togglePauseAllChannels", { enumerable: true, get: function () { return pause_1.togglePauseAllChannels; } });
|
|
44
35
|
Object.defineProperty(exports, "togglePauseChannel", { enumerable: true, get: function () { return pause_1.togglePauseChannel; } });
|
|
45
|
-
//
|
|
36
|
+
// Volume control and ducking functions
|
|
46
37
|
var volume_1 = require("./volume");
|
|
47
|
-
Object.defineProperty(exports, "applyVolumeDucking", { enumerable: true, get: function () { return volume_1.applyVolumeDucking; } });
|
|
48
38
|
Object.defineProperty(exports, "clearVolumeDucking", { enumerable: true, get: function () { return volume_1.clearVolumeDucking; } });
|
|
49
39
|
Object.defineProperty(exports, "getAllChannelsVolume", { enumerable: true, get: function () { return volume_1.getAllChannelsVolume; } });
|
|
50
40
|
Object.defineProperty(exports, "getChannelVolume", { enumerable: true, get: function () { return volume_1.getChannelVolume; } });
|
|
51
|
-
Object.defineProperty(exports, "restoreVolumeLevels", { enumerable: true, get: function () { return volume_1.restoreVolumeLevels; } });
|
|
52
41
|
Object.defineProperty(exports, "setAllChannelsVolume", { enumerable: true, get: function () { return volume_1.setAllChannelsVolume; } });
|
|
53
42
|
Object.defineProperty(exports, "setChannelVolume", { enumerable: true, get: function () { return volume_1.setChannelVolume; } });
|
|
54
43
|
Object.defineProperty(exports, "setVolumeDucking", { enumerable: true, get: function () { return volume_1.setVolumeDucking; } });
|
|
55
|
-
|
|
56
|
-
// Export audio information and progress tracking functions
|
|
44
|
+
// Audio information and progress tracking functions
|
|
57
45
|
var info_1 = require("./info");
|
|
58
|
-
Object.defineProperty(exports, "audioChannels", { enumerable: true, get: function () { return info_1.audioChannels; } });
|
|
59
46
|
Object.defineProperty(exports, "getAllChannelsInfo", { enumerable: true, get: function () { return info_1.getAllChannelsInfo; } });
|
|
60
47
|
Object.defineProperty(exports, "getCurrentAudioInfo", { enumerable: true, get: function () { return info_1.getCurrentAudioInfo; } });
|
|
61
48
|
Object.defineProperty(exports, "getQueueSnapshot", { enumerable: true, get: function () { return info_1.getQueueSnapshot; } });
|
|
@@ -69,7 +56,10 @@ Object.defineProperty(exports, "onAudioProgress", { enumerable: true, get: funct
|
|
|
69
56
|
Object.defineProperty(exports, "onAudioResume", { enumerable: true, get: function () { return info_1.onAudioResume; } });
|
|
70
57
|
Object.defineProperty(exports, "onAudioStart", { enumerable: true, get: function () { return info_1.onAudioStart; } });
|
|
71
58
|
Object.defineProperty(exports, "onQueueChange", { enumerable: true, get: function () { return info_1.onQueueChange; } });
|
|
72
|
-
//
|
|
59
|
+
// Core data access for legacy compatibility
|
|
60
|
+
var info_2 = require("./info");
|
|
61
|
+
Object.defineProperty(exports, "audioChannels", { enumerable: true, get: function () { return info_2.audioChannels; } });
|
|
62
|
+
// Utility helper functions
|
|
73
63
|
var utils_1 = require("./utils");
|
|
74
64
|
Object.defineProperty(exports, "cleanWebpackFilename", { enumerable: true, get: function () { return utils_1.cleanWebpackFilename; } });
|
|
75
65
|
Object.defineProperty(exports, "createQueueSnapshot", { enumerable: true, get: function () { return utils_1.createQueueSnapshot; } });
|
package/dist/info.d.ts
CHANGED
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { AudioInfo, QueueSnapshot, ProgressCallback, QueueChangeCallback, AudioStartCallback, AudioCompleteCallback, AudioPauseCallback, AudioResumeCallback, ExtendedAudioQueueChannel } from './types';
|
|
5
5
|
/**
|
|
6
|
-
* Global array
|
|
6
|
+
* Global array to store audio channels with their queues and callback management
|
|
7
|
+
* Each channel maintains its own audio queue and event callback sets
|
|
7
8
|
*/
|
|
8
9
|
export declare const audioChannels: ExtendedAudioQueueChannel[];
|
|
9
10
|
/**
|
package/dist/info.js
CHANGED
|
@@ -7,7 +7,8 @@ exports.offAudioResume = exports.offAudioPause = exports.onAudioResume = exports
|
|
|
7
7
|
const utils_1 = require("./utils");
|
|
8
8
|
const events_1 = require("./events");
|
|
9
9
|
/**
|
|
10
|
-
* Global array
|
|
10
|
+
* Global array to store audio channels with their queues and callback management
|
|
11
|
+
* Each channel maintains its own audio queue and event callback sets
|
|
11
12
|
*/
|
|
12
13
|
exports.audioChannels = [];
|
|
13
14
|
/**
|
|
@@ -86,6 +87,7 @@ const onAudioProgress = (channelNumber, callback) => {
|
|
|
86
87
|
if (!exports.audioChannels[channelNumber]) {
|
|
87
88
|
exports.audioChannels[channelNumber] = {
|
|
88
89
|
audioCompleteCallbacks: new Set(),
|
|
90
|
+
audioErrorCallbacks: new Set(),
|
|
89
91
|
audioPauseCallbacks: new Set(),
|
|
90
92
|
audioResumeCallbacks: new Set(),
|
|
91
93
|
audioStartCallbacks: new Set(),
|
|
@@ -154,6 +156,7 @@ const onQueueChange = (channelNumber, callback) => {
|
|
|
154
156
|
if (!exports.audioChannels[channelNumber]) {
|
|
155
157
|
exports.audioChannels[channelNumber] = {
|
|
156
158
|
audioCompleteCallbacks: new Set(),
|
|
159
|
+
audioErrorCallbacks: new Set(),
|
|
157
160
|
audioPauseCallbacks: new Set(),
|
|
158
161
|
audioResumeCallbacks: new Set(),
|
|
159
162
|
audioStartCallbacks: new Set(),
|
|
@@ -202,6 +205,7 @@ const onAudioStart = (channelNumber, callback) => {
|
|
|
202
205
|
if (!exports.audioChannels[channelNumber]) {
|
|
203
206
|
exports.audioChannels[channelNumber] = {
|
|
204
207
|
audioCompleteCallbacks: new Set(),
|
|
208
|
+
audioErrorCallbacks: new Set(),
|
|
205
209
|
audioPauseCallbacks: new Set(),
|
|
206
210
|
audioResumeCallbacks: new Set(),
|
|
207
211
|
audioStartCallbacks: new Set(),
|
|
@@ -237,6 +241,7 @@ const onAudioComplete = (channelNumber, callback) => {
|
|
|
237
241
|
if (!exports.audioChannels[channelNumber]) {
|
|
238
242
|
exports.audioChannels[channelNumber] = {
|
|
239
243
|
audioCompleteCallbacks: new Set(),
|
|
244
|
+
audioErrorCallbacks: new Set(),
|
|
240
245
|
audioPauseCallbacks: new Set(),
|
|
241
246
|
audioResumeCallbacks: new Set(),
|
|
242
247
|
audioStartCallbacks: new Set(),
|
|
@@ -270,6 +275,7 @@ const onAudioPause = (channelNumber, callback) => {
|
|
|
270
275
|
if (!exports.audioChannels[channelNumber]) {
|
|
271
276
|
exports.audioChannels[channelNumber] = {
|
|
272
277
|
audioCompleteCallbacks: new Set(),
|
|
278
|
+
audioErrorCallbacks: new Set(),
|
|
273
279
|
audioPauseCallbacks: new Set(),
|
|
274
280
|
audioResumeCallbacks: new Set(),
|
|
275
281
|
audioStartCallbacks: new Set(),
|
|
@@ -303,6 +309,7 @@ const onAudioResume = (channelNumber, callback) => {
|
|
|
303
309
|
if (!exports.audioChannels[channelNumber]) {
|
|
304
310
|
exports.audioChannels[channelNumber] = {
|
|
305
311
|
audioCompleteCallbacks: new Set(),
|
|
312
|
+
audioErrorCallbacks: new Set(),
|
|
306
313
|
audioPauseCallbacks: new Set(),
|
|
307
314
|
audioResumeCallbacks: new Set(),
|
|
308
315
|
audioStartCallbacks: new Set(),
|
package/dist/pause.js
CHANGED
|
@@ -55,7 +55,7 @@ const resumeChannel = (...args_1) => __awaiter(void 0, [...args_1], void 0, func
|
|
|
55
55
|
const channel = info_1.audioChannels[channelNumber];
|
|
56
56
|
if (channel && channel.queue.length > 0) {
|
|
57
57
|
const currentAudio = channel.queue[0];
|
|
58
|
-
// Only resume if both the channel is marked as paused AND the audio element is actually paused
|
|
58
|
+
// Only resume if both the channel is marked as paused AND the audio element is actually paused AND not ended
|
|
59
59
|
if (channel.isPaused && currentAudio.paused && !currentAudio.ended) {
|
|
60
60
|
yield currentAudio.play();
|
|
61
61
|
channel.isPaused = false;
|