audio-channel-queue 1.10.0 → 1.11.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 +12 -0
- package/dist/core.d.ts +2 -2
- package/dist/core.js +14 -11
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -1
- package/dist/info.d.ts +41 -0
- package/dist/info.js +94 -3
- package/package.json +1 -1
- package/src/core.ts +15 -11
- package/src/index.ts +2 -0
- package/src/info.ts +101 -4
package/README.md
CHANGED
|
@@ -362,12 +362,24 @@ onAudioStart(channelNumber, callback);
|
|
|
362
362
|
onAudioStart(0, (info) => console.log(`Started: ${info.fileName}`)); // Log audio starts
|
|
363
363
|
```
|
|
364
364
|
|
|
365
|
+
```typescript
|
|
366
|
+
// Unsubscribe from audio start events (removes ALL start callbacks for the channel)
|
|
367
|
+
offAudioStart(channelNumber);
|
|
368
|
+
offAudioStart(0); // Stop receiving all start notifications for channel 0
|
|
369
|
+
```
|
|
370
|
+
|
|
365
371
|
```typescript
|
|
366
372
|
// Subscribe to audio completion events.
|
|
367
373
|
onAudioComplete(channelNumber, callback);
|
|
368
374
|
onAudioComplete(0, (info) => logPlayHistory(info)); // Track completed audio
|
|
369
375
|
```
|
|
370
376
|
|
|
377
|
+
```typescript
|
|
378
|
+
// Unsubscribe from audio completion events (removes ALL complete callbacks for the channel)
|
|
379
|
+
offAudioComplete(channelNumber);
|
|
380
|
+
offAudioComplete(0); // Stop receiving all completion notifications for channel 0
|
|
381
|
+
```
|
|
382
|
+
|
|
371
383
|
```typescript
|
|
372
384
|
// Subscribe to audio pause events.
|
|
373
385
|
onAudioPause(channelNumber, callback);
|
package/dist/core.d.ts
CHANGED
|
@@ -73,10 +73,10 @@ export declare const queueAudioPriority: (audioUrl: string, channelNumber?: numb
|
|
|
73
73
|
/**
|
|
74
74
|
* Plays the audio queue for a specific channel
|
|
75
75
|
* @param channelNumber - The channel number to play
|
|
76
|
-
* @returns Promise that resolves when the
|
|
76
|
+
* @returns Promise that resolves when the audio starts playing (setup complete)
|
|
77
77
|
* @example
|
|
78
78
|
* ```typescript
|
|
79
|
-
* await playAudioQueue(0); //
|
|
79
|
+
* await playAudioQueue(0); // Start playing queue for channel 0
|
|
80
80
|
* ```
|
|
81
81
|
*/
|
|
82
82
|
export declare const playAudioQueue: (channelNumber: number) => Promise<void>;
|
package/dist/core.js
CHANGED
|
@@ -279,12 +279,8 @@ const queueAudio = (audioUrl_1, ...args_1) => __awaiter(void 0, [audioUrl_1, ...
|
|
|
279
279
|
(0, events_1.emitQueueChange)(channelNumber, info_1.audioChannels);
|
|
280
280
|
// Start playing if this is the first item and channel isn't paused
|
|
281
281
|
if (channel.queue.length === 1 && !channel.isPaused) {
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
(0, exports.playAudioQueue)(channelNumber).catch((error) => {
|
|
285
|
-
(0, errors_1.handleAudioError)(audio, channelNumber, validatedUrl, error);
|
|
286
|
-
});
|
|
287
|
-
}, 0);
|
|
282
|
+
// Await the audio setup to complete before resolving queueAudio
|
|
283
|
+
yield (0, exports.playAudioQueue)(channelNumber);
|
|
288
284
|
}
|
|
289
285
|
});
|
|
290
286
|
exports.queueAudio = queueAudio;
|
|
@@ -309,10 +305,10 @@ exports.queueAudioPriority = queueAudioPriority;
|
|
|
309
305
|
/**
|
|
310
306
|
* Plays the audio queue for a specific channel
|
|
311
307
|
* @param channelNumber - The channel number to play
|
|
312
|
-
* @returns Promise that resolves when the
|
|
308
|
+
* @returns Promise that resolves when the audio starts playing (setup complete)
|
|
313
309
|
* @example
|
|
314
310
|
* ```typescript
|
|
315
|
-
* await playAudioQueue(0); //
|
|
311
|
+
* await playAudioQueue(0); // Start playing queue for channel 0
|
|
316
312
|
* ```
|
|
317
313
|
*/
|
|
318
314
|
const playAudioQueue = (channelNumber) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -331,6 +327,7 @@ const playAudioQueue = (channelNumber) => __awaiter(void 0, void 0, void 0, func
|
|
|
331
327
|
let hasStarted = false;
|
|
332
328
|
let metadataLoaded = false;
|
|
333
329
|
let playStarted = false;
|
|
330
|
+
let setupComplete = false;
|
|
334
331
|
// Check if we should fire onAudioStart (both conditions met)
|
|
335
332
|
const tryFireAudioStart = () => {
|
|
336
333
|
if (!hasStarted && metadataLoaded && playStarted) {
|
|
@@ -341,6 +338,11 @@ const playAudioQueue = (channelNumber) => __awaiter(void 0, void 0, void 0, func
|
|
|
341
338
|
fileName: (0, utils_1.extractFileName)(currentAudio.src),
|
|
342
339
|
src: currentAudio.src
|
|
343
340
|
}, info_1.audioChannels);
|
|
341
|
+
// Resolve setup promise when audio start event is fired
|
|
342
|
+
if (!setupComplete) {
|
|
343
|
+
setupComplete = true;
|
|
344
|
+
resolve();
|
|
345
|
+
}
|
|
344
346
|
}
|
|
345
347
|
};
|
|
346
348
|
// Event handler for when metadata loads (duration becomes available)
|
|
@@ -371,7 +373,6 @@ const playAudioQueue = (channelNumber) => __awaiter(void 0, void 0, void 0, func
|
|
|
371
373
|
catch (error) {
|
|
372
374
|
yield (0, errors_1.handleAudioError)(currentAudio, channelNumber, currentAudio.src, error);
|
|
373
375
|
}
|
|
374
|
-
resolve();
|
|
375
376
|
}
|
|
376
377
|
else {
|
|
377
378
|
// For non-looping audio, remove from queue and play next
|
|
@@ -384,7 +385,6 @@ const playAudioQueue = (channelNumber) => __awaiter(void 0, void 0, void 0, func
|
|
|
384
385
|
(0, events_1.emitQueueChange)(channelNumber, info_1.audioChannels);
|
|
385
386
|
// Play next audio immediately if there's more in queue
|
|
386
387
|
yield (0, exports.playAudioQueue)(channelNumber);
|
|
387
|
-
resolve();
|
|
388
388
|
}
|
|
389
389
|
});
|
|
390
390
|
// Add event listeners
|
|
@@ -398,7 +398,10 @@ const playAudioQueue = (channelNumber) => __awaiter(void 0, void 0, void 0, func
|
|
|
398
398
|
// Enhanced play with error handling
|
|
399
399
|
currentAudio.play().catch((error) => __awaiter(void 0, void 0, void 0, function* () {
|
|
400
400
|
yield (0, errors_1.handleAudioError)(currentAudio, channelNumber, currentAudio.src, error);
|
|
401
|
-
|
|
401
|
+
if (!setupComplete) {
|
|
402
|
+
setupComplete = true;
|
|
403
|
+
resolve(); // Resolve gracefully instead of rejecting
|
|
404
|
+
}
|
|
402
405
|
}));
|
|
403
406
|
});
|
|
404
407
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export { clearQueueAfterCurrent, getQueueItemInfo, getQueueLength, removeQueuedI
|
|
|
8
8
|
export { getErrorRecovery, getRetryConfig, offAudioError, onAudioError, retryFailedAudio, setErrorRecovery, setRetryConfig } from './errors';
|
|
9
9
|
export { getAllChannelsPauseState, isChannelPaused, pauseAllChannels, pauseAllWithFade, pauseChannel, pauseWithFade, resumeAllChannels, resumeAllWithFade, resumeChannel, resumeWithFade, togglePauseAllChannels, togglePauseAllWithFade, togglePauseChannel, togglePauseWithFade } from './pause';
|
|
10
10
|
export { clearVolumeDucking, fadeVolume, getAllChannelsVolume, getChannelVolume, getFadeConfig, setAllChannelsVolume, setChannelVolume, setVolumeDucking, transitionVolume, cancelVolumeTransition, cancelAllVolumeTransitions } from './volume';
|
|
11
|
-
export { getAllChannelsInfo, getCurrentAudioInfo, getQueueSnapshot, offAudioPause, offAudioProgress, offAudioResume, offQueueChange, onAudioComplete, onAudioPause, onAudioProgress, onAudioResume, onAudioStart, onQueueChange } from './info';
|
|
11
|
+
export { getAllChannelsInfo, getCurrentAudioInfo, getQueueSnapshot, offAudioComplete, offAudioPause, offAudioProgress, offAudioResume, offAudioStart, offQueueChange, onAudioComplete, onAudioPause, onAudioProgress, onAudioResume, onAudioStart, onQueueChange } from './info';
|
|
12
12
|
export { audioChannels } from './info';
|
|
13
13
|
export { cleanWebpackFilename, createQueueSnapshot, extractFileName, getAudioInfoFromElement, sanitizeForDisplay, validateAudioUrl } from './utils';
|
|
14
14
|
export type { AudioCompleteCallback, AudioCompleteInfo, AudioErrorCallback, AudioErrorInfo, AudioInfo, AudioPauseCallback, AudioQueueOptions, AudioResumeCallback, AudioStartCallback, AudioStartInfo, ChannelFadeState, ErrorRecoveryOptions, ExtendedAudioQueueChannel, FadeConfig, ProgressCallback, QueueChangeCallback, QueueItem, QueueManipulationResult, QueueSnapshot, RetryConfig, VolumeConfig, QueueConfig } from './types';
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
exports.getAllChannelsInfo = exports.cancelAllVolumeTransitions = exports.cancelVolumeTransition = exports.transitionVolume = exports.setVolumeDucking = exports.setChannelVolume = exports.setAllChannelsVolume = exports.getFadeConfig = exports.getChannelVolume = exports.getAllChannelsVolume = exports.fadeVolume = exports.clearVolumeDucking = exports.togglePauseWithFade = exports.togglePauseChannel = exports.togglePauseAllWithFade = exports.togglePauseAllChannels = exports.resumeWithFade = exports.resumeChannel = exports.resumeAllWithFade = exports.resumeAllChannels = exports.pauseWithFade = exports.pauseChannel = exports.pauseAllWithFade = exports.pauseAllChannels = exports.isChannelPaused = exports.getAllChannelsPauseState = exports.setRetryConfig = exports.setErrorRecovery = exports.retryFailedAudio = exports.onAudioError = exports.offAudioError = exports.getRetryConfig = exports.getErrorRecovery = exports.swapQueueItems = exports.reorderQueue = exports.removeQueuedItem = exports.getQueueLength = exports.getQueueItemInfo = exports.clearQueueAfterCurrent = exports.setChannelQueueLimit = exports.getQueueConfig = exports.setQueueConfig = exports.destroyAllChannels = exports.destroyChannel = exports.playAudioQueue = exports.stopAllAudio = exports.stopAllAudioInChannel = exports.stopCurrentAudioInChannel = exports.queueAudioPriority = exports.queueAudio = void 0;
|
|
9
|
-
exports.GLOBAL_PROGRESS_KEY = exports.TimerType = exports.MAX_CHANNELS = exports.FadeType = exports.EasingType = exports.validateAudioUrl = exports.sanitizeForDisplay = 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 = void 0;
|
|
9
|
+
exports.GLOBAL_PROGRESS_KEY = exports.TimerType = exports.MAX_CHANNELS = exports.FadeType = exports.EasingType = exports.validateAudioUrl = exports.sanitizeForDisplay = 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.offAudioStart = exports.offAudioResume = exports.offAudioProgress = exports.offAudioPause = exports.offAudioComplete = exports.getQueueSnapshot = exports.getCurrentAudioInfo = void 0;
|
|
10
10
|
// Core queue management functions
|
|
11
11
|
var core_1 = require("./core");
|
|
12
12
|
Object.defineProperty(exports, "queueAudio", { enumerable: true, get: function () { return core_1.queueAudio; } });
|
|
@@ -71,9 +71,11 @@ var info_1 = require("./info");
|
|
|
71
71
|
Object.defineProperty(exports, "getAllChannelsInfo", { enumerable: true, get: function () { return info_1.getAllChannelsInfo; } });
|
|
72
72
|
Object.defineProperty(exports, "getCurrentAudioInfo", { enumerable: true, get: function () { return info_1.getCurrentAudioInfo; } });
|
|
73
73
|
Object.defineProperty(exports, "getQueueSnapshot", { enumerable: true, get: function () { return info_1.getQueueSnapshot; } });
|
|
74
|
+
Object.defineProperty(exports, "offAudioComplete", { enumerable: true, get: function () { return info_1.offAudioComplete; } });
|
|
74
75
|
Object.defineProperty(exports, "offAudioPause", { enumerable: true, get: function () { return info_1.offAudioPause; } });
|
|
75
76
|
Object.defineProperty(exports, "offAudioProgress", { enumerable: true, get: function () { return info_1.offAudioProgress; } });
|
|
76
77
|
Object.defineProperty(exports, "offAudioResume", { enumerable: true, get: function () { return info_1.offAudioResume; } });
|
|
78
|
+
Object.defineProperty(exports, "offAudioStart", { enumerable: true, get: function () { return info_1.offAudioStart; } });
|
|
77
79
|
Object.defineProperty(exports, "offQueueChange", { enumerable: true, get: function () { return info_1.offQueueChange; } });
|
|
78
80
|
Object.defineProperty(exports, "onAudioComplete", { enumerable: true, get: function () { return info_1.onAudioComplete; } });
|
|
79
81
|
Object.defineProperty(exports, "onAudioPause", { enumerable: true, get: function () { return info_1.onAudioPause; } });
|
package/dist/info.d.ts
CHANGED
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
* @fileoverview Audio information and progress tracking functions for the audio-channel-queue package
|
|
3
3
|
*/
|
|
4
4
|
import { AudioInfo, QueueSnapshot, ProgressCallback, QueueChangeCallback, AudioStartCallback, AudioCompleteCallback, AudioPauseCallback, AudioResumeCallback, ExtendedAudioQueueChannel } from './types';
|
|
5
|
+
/**
|
|
6
|
+
* Gets the current list of whitelisted channel properties
|
|
7
|
+
* This is automatically derived from the ExtendedAudioQueueChannel interface
|
|
8
|
+
* @returns Array of whitelisted property names
|
|
9
|
+
* @internal
|
|
10
|
+
*/
|
|
11
|
+
export declare const getWhitelistedChannelProperties: () => string[];
|
|
12
|
+
/**
|
|
13
|
+
* Returns the list of non-whitelisted properties found on a specific channel
|
|
14
|
+
* These are properties that will trigger warnings when modified directly
|
|
15
|
+
* @param channelNumber - The channel number to inspect (defaults to 0)
|
|
16
|
+
* @returns Array of property names that are not in the whitelist, or empty array if channel doesn't exist
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* // Add some custom property to a channel
|
|
20
|
+
* (audioChannels[0] as any).customProperty = 'test';
|
|
21
|
+
*
|
|
22
|
+
* const nonWhitelisted = getNonWhitelistedChannelProperties(0);
|
|
23
|
+
* console.log(nonWhitelisted); // ['customProperty']
|
|
24
|
+
* ```
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
export declare const getNonWhitelistedChannelProperties: (channelNumber?: number) => string[];
|
|
5
28
|
/**
|
|
6
29
|
* Global array to store audio channels with their queues and callback management
|
|
7
30
|
* Each channel maintains its own audio queue and event callback sets
|
|
@@ -176,3 +199,21 @@ export declare const offAudioPause: (channelNumber: number) => void;
|
|
|
176
199
|
* ```
|
|
177
200
|
*/
|
|
178
201
|
export declare const offAudioResume: (channelNumber: number) => void;
|
|
202
|
+
/**
|
|
203
|
+
* Removes audio start event listeners for a specific channel
|
|
204
|
+
* @param channelNumber - The channel number
|
|
205
|
+
* @example
|
|
206
|
+
* ```typescript
|
|
207
|
+
* offAudioStart(0); // Stop receiving start notifications for channel 0
|
|
208
|
+
* ```
|
|
209
|
+
*/
|
|
210
|
+
export declare const offAudioStart: (channelNumber: number) => void;
|
|
211
|
+
/**
|
|
212
|
+
* Removes audio complete event listeners for a specific channel
|
|
213
|
+
* @param channelNumber - The channel number
|
|
214
|
+
* @example
|
|
215
|
+
* ```typescript
|
|
216
|
+
* offAudioComplete(0); // Stop receiving completion notifications for channel 0
|
|
217
|
+
* ```
|
|
218
|
+
*/
|
|
219
|
+
export declare const offAudioComplete: (channelNumber: number) => void;
|
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.');
|
|
@@ -429,3 +490,33 @@ const offAudioResume = (channelNumber) => {
|
|
|
429
490
|
channel.audioResumeCallbacks.clear();
|
|
430
491
|
};
|
|
431
492
|
exports.offAudioResume = offAudioResume;
|
|
493
|
+
/**
|
|
494
|
+
* Removes audio start event listeners for a specific channel
|
|
495
|
+
* @param channelNumber - The channel number
|
|
496
|
+
* @example
|
|
497
|
+
* ```typescript
|
|
498
|
+
* offAudioStart(0); // Stop receiving start notifications for channel 0
|
|
499
|
+
* ```
|
|
500
|
+
*/
|
|
501
|
+
const offAudioStart = (channelNumber) => {
|
|
502
|
+
const channel = exports.audioChannels[channelNumber];
|
|
503
|
+
if (!(channel === null || channel === void 0 ? void 0 : channel.audioStartCallbacks))
|
|
504
|
+
return;
|
|
505
|
+
channel.audioStartCallbacks.clear();
|
|
506
|
+
};
|
|
507
|
+
exports.offAudioStart = offAudioStart;
|
|
508
|
+
/**
|
|
509
|
+
* Removes audio complete event listeners for a specific channel
|
|
510
|
+
* @param channelNumber - The channel number
|
|
511
|
+
* @example
|
|
512
|
+
* ```typescript
|
|
513
|
+
* offAudioComplete(0); // Stop receiving completion notifications for channel 0
|
|
514
|
+
* ```
|
|
515
|
+
*/
|
|
516
|
+
const offAudioComplete = (channelNumber) => {
|
|
517
|
+
const channel = exports.audioChannels[channelNumber];
|
|
518
|
+
if (!(channel === null || channel === void 0 ? void 0 : channel.audioCompleteCallbacks))
|
|
519
|
+
return;
|
|
520
|
+
channel.audioCompleteCallbacks.clear();
|
|
521
|
+
};
|
|
522
|
+
exports.offAudioComplete = offAudioComplete;
|
package/package.json
CHANGED
package/src/core.ts
CHANGED
|
@@ -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/index.ts
CHANGED
package/src/info.ts
CHANGED
|
@@ -18,6 +18,73 @@ import {
|
|
|
18
18
|
import { getAudioInfoFromElement, createQueueSnapshot } from './utils';
|
|
19
19
|
import { setupProgressTracking, cleanupProgressTracking } from './events';
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Gets the current list of whitelisted channel properties
|
|
23
|
+
* This is automatically derived from the ExtendedAudioQueueChannel interface
|
|
24
|
+
* @returns Array of whitelisted property names
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
export const getWhitelistedChannelProperties = (): string[] => {
|
|
28
|
+
// Create a sample channel object to extract property names
|
|
29
|
+
const sampleChannel: ExtendedAudioQueueChannel = {
|
|
30
|
+
audioCompleteCallbacks: new Set(),
|
|
31
|
+
audioErrorCallbacks: new Set(),
|
|
32
|
+
audioPauseCallbacks: new Set(),
|
|
33
|
+
audioResumeCallbacks: new Set(),
|
|
34
|
+
audioStartCallbacks: new Set(),
|
|
35
|
+
isPaused: false,
|
|
36
|
+
progressCallbacks: new Map(),
|
|
37
|
+
queue: [],
|
|
38
|
+
queueChangeCallbacks: new Set(),
|
|
39
|
+
volume: 1.0
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Get all property names from the interface (including optional ones)
|
|
43
|
+
const propertyNames = [
|
|
44
|
+
...Object.getOwnPropertyNames(sampleChannel),
|
|
45
|
+
// Add optional properties that might not be present on the sample
|
|
46
|
+
'fadeState',
|
|
47
|
+
'isLocked',
|
|
48
|
+
'maxQueueSize',
|
|
49
|
+
'retryConfig',
|
|
50
|
+
'volumeConfig' // Legacy property that might still be used
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
return [...new Set(propertyNames)]; // Remove duplicates
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Returns the list of non-whitelisted properties found on a specific channel
|
|
58
|
+
* These are properties that will trigger warnings when modified directly
|
|
59
|
+
* @param channelNumber - The channel number to inspect (defaults to 0)
|
|
60
|
+
* @returns Array of property names that are not in the whitelist, or empty array if channel doesn't exist
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* // Add some custom property to a channel
|
|
64
|
+
* (audioChannels[0] as any).customProperty = 'test';
|
|
65
|
+
*
|
|
66
|
+
* const nonWhitelisted = getNonWhitelistedChannelProperties(0);
|
|
67
|
+
* console.log(nonWhitelisted); // ['customProperty']
|
|
68
|
+
* ```
|
|
69
|
+
* @internal
|
|
70
|
+
*/
|
|
71
|
+
export const getNonWhitelistedChannelProperties = (channelNumber: number = 0): string[] => {
|
|
72
|
+
const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
|
|
73
|
+
if (!channel) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const whitelistedProperties: string[] = getWhitelistedChannelProperties();
|
|
78
|
+
const allChannelProperties: string[] = Object.getOwnPropertyNames(channel);
|
|
79
|
+
|
|
80
|
+
// Filter out properties that are in the whitelist
|
|
81
|
+
const nonWhitelistedProperties: string[] = allChannelProperties.filter(
|
|
82
|
+
(property: string) => !whitelistedProperties.includes(property)
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
return nonWhitelistedProperties;
|
|
86
|
+
};
|
|
87
|
+
|
|
21
88
|
/**
|
|
22
89
|
* Global array to store audio channels with their queues and callback management
|
|
23
90
|
* Each channel maintains its own audio queue and event callback sets
|
|
@@ -56,10 +123,10 @@ export const audioChannels: ExtendedAudioQueueChannel[] = new Proxy(
|
|
|
56
123
|
channelValue: unknown
|
|
57
124
|
): boolean {
|
|
58
125
|
// Allow internal modifications but warn about direct property changes
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
) {
|
|
126
|
+
// Use the automatically-derived whitelist from the interface
|
|
127
|
+
const whitelistedProperties = getWhitelistedChannelProperties();
|
|
128
|
+
|
|
129
|
+
if (typeof channelProp === 'string' && !whitelistedProperties.includes(channelProp)) {
|
|
63
130
|
// eslint-disable-next-line no-console
|
|
64
131
|
console.warn(
|
|
65
132
|
`Warning: Direct modification of channel.${channelProp} detected. ` +
|
|
@@ -484,3 +551,33 @@ export const offAudioResume = (channelNumber: number): void => {
|
|
|
484
551
|
|
|
485
552
|
channel.audioResumeCallbacks.clear();
|
|
486
553
|
};
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Removes audio start event listeners for a specific channel
|
|
557
|
+
* @param channelNumber - The channel number
|
|
558
|
+
* @example
|
|
559
|
+
* ```typescript
|
|
560
|
+
* offAudioStart(0); // Stop receiving start notifications for channel 0
|
|
561
|
+
* ```
|
|
562
|
+
*/
|
|
563
|
+
export const offAudioStart = (channelNumber: number): void => {
|
|
564
|
+
const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
|
|
565
|
+
if (!channel?.audioStartCallbacks) return;
|
|
566
|
+
|
|
567
|
+
channel.audioStartCallbacks.clear();
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Removes audio complete event listeners for a specific channel
|
|
572
|
+
* @param channelNumber - The channel number
|
|
573
|
+
* @example
|
|
574
|
+
* ```typescript
|
|
575
|
+
* offAudioComplete(0); // Stop receiving completion notifications for channel 0
|
|
576
|
+
* ```
|
|
577
|
+
*/
|
|
578
|
+
export const offAudioComplete = (channelNumber: number): void => {
|
|
579
|
+
const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
|
|
580
|
+
if (!channel?.audioCompleteCallbacks) return;
|
|
581
|
+
|
|
582
|
+
channel.audioCompleteCallbacks.clear();
|
|
583
|
+
};
|