audio-channel-queue 1.4.0 → 1.5.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/src/events.ts ADDED
@@ -0,0 +1,189 @@
1
+ /**
2
+ * @fileoverview Event handling and emission for the audio-channel-queue package
3
+ */
4
+
5
+ import {
6
+ AudioStartInfo,
7
+ AudioCompleteInfo,
8
+ ExtendedAudioQueueChannel,
9
+ QueueSnapshot,
10
+ ProgressCallback,
11
+ AudioInfo,
12
+ } from './types';
13
+ import { createQueueSnapshot, getAudioInfoFromElement } from './utils';
14
+
15
+ /**
16
+ * Emits a queue change event to all registered listeners for a specific channel
17
+ * @param channelNumber - The channel number that experienced a queue change
18
+ * @param audioChannels - Array of audio channels
19
+ * @example
20
+ * ```typescript
21
+ * emitQueueChange(0, audioChannels); // Notifies all queue change listeners
22
+ * ```
23
+ */
24
+ export const emitQueueChange = (
25
+ channelNumber: number,
26
+ audioChannels: ExtendedAudioQueueChannel[]
27
+ ): void => {
28
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
29
+ if (!channel || !channel.queueChangeCallbacks) return;
30
+
31
+ const snapshot: QueueSnapshot | null = createQueueSnapshot(channelNumber, audioChannels);
32
+ if (!snapshot) return;
33
+
34
+ channel.queueChangeCallbacks.forEach(callback => {
35
+ try {
36
+ callback(snapshot);
37
+ } catch (error) {
38
+ console.error('Error in queue change callback:', error);
39
+ }
40
+ });
41
+ };
42
+
43
+ /**
44
+ * Emits an audio start event to all registered listeners for a specific channel
45
+ * @param channelNumber - The channel number where audio started
46
+ * @param audioInfo - Information about the audio that started
47
+ * @param audioChannels - Array of audio channels
48
+ * @example
49
+ * ```typescript
50
+ * emitAudioStart(0, { src: 'song.mp3', fileName: 'song.mp3', duration: 180000, channelNumber: 0 }, audioChannels);
51
+ * ```
52
+ */
53
+ export const emitAudioStart = (
54
+ channelNumber: number,
55
+ audioInfo: AudioStartInfo,
56
+ audioChannels: ExtendedAudioQueueChannel[]
57
+ ): void => {
58
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
59
+ if (!channel || !channel.audioStartCallbacks) return;
60
+
61
+ channel.audioStartCallbacks.forEach(callback => {
62
+ try {
63
+ callback(audioInfo);
64
+ } catch (error) {
65
+ console.error('Error in audio start callback:', error);
66
+ }
67
+ });
68
+ };
69
+
70
+ /**
71
+ * Emits an audio complete event to all registered listeners for a specific channel
72
+ * @param channelNumber - The channel number where audio completed
73
+ * @param audioInfo - Information about the audio that completed
74
+ * @param audioChannels - Array of audio channels
75
+ * @example
76
+ * ```typescript
77
+ * emitAudioComplete(0, { src: 'song.mp3', fileName: 'song.mp3', channelNumber: 0, remainingInQueue: 2 }, audioChannels);
78
+ * ```
79
+ */
80
+ export const emitAudioComplete = (
81
+ channelNumber: number,
82
+ audioInfo: AudioCompleteInfo,
83
+ audioChannels: ExtendedAudioQueueChannel[]
84
+ ): void => {
85
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
86
+ if (!channel || !channel.audioCompleteCallbacks) return;
87
+
88
+ channel.audioCompleteCallbacks.forEach(callback => {
89
+ try {
90
+ callback(audioInfo);
91
+ } catch (error) {
92
+ console.error('Error in audio complete callback:', error);
93
+ }
94
+ });
95
+ };
96
+
97
+ // Store listener functions for cleanup
98
+ const progressListeners = new WeakMap<HTMLAudioElement, () => void>();
99
+
100
+ /**
101
+ * Sets up comprehensive progress tracking for an audio element
102
+ * @param audio - The HTML audio element to track
103
+ * @param channelNumber - The channel number this audio belongs to
104
+ * @param audioChannels - Array of audio channels
105
+ * @example
106
+ * ```typescript
107
+ * const audioElement = new Audio('song.mp3');
108
+ * setupProgressTracking(audioElement, 0, audioChannels);
109
+ * ```
110
+ */
111
+ export const setupProgressTracking = (
112
+ audio: HTMLAudioElement,
113
+ channelNumber: number,
114
+ audioChannels: ExtendedAudioQueueChannel[]
115
+ ): void => {
116
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
117
+ if (!channel) return;
118
+
119
+ if (!channel.progressCallbacks) {
120
+ channel.progressCallbacks = new Map();
121
+ }
122
+
123
+ // Don't set up tracking if already exists
124
+ if (progressListeners.has(audio)) return;
125
+
126
+ const updateProgress = (): void => {
127
+ // Get callbacks for this specific audio element AND the channel-wide callbacks
128
+ const audioCallbacks: Set<ProgressCallback> = channel.progressCallbacks?.get(audio) || new Set();
129
+ const channelCallbacks: Set<ProgressCallback> = channel.progressCallbacks?.get(null as any) || new Set();
130
+
131
+ // Combine both sets of callbacks
132
+ const allCallbacks: Set<ProgressCallback> = new Set([...audioCallbacks, ...channelCallbacks]);
133
+
134
+ if (allCallbacks.size === 0) return;
135
+
136
+ const info: AudioInfo | null = getAudioInfoFromElement(audio);
137
+ if (info) {
138
+ allCallbacks.forEach((callback: ProgressCallback) => {
139
+ try {
140
+ callback(info);
141
+ } catch (error) {
142
+ console.error('Error in progress callback:', error);
143
+ }
144
+ });
145
+ }
146
+ };
147
+
148
+ // Store the listener function for cleanup
149
+ progressListeners.set(audio, updateProgress);
150
+
151
+ // Set up comprehensive event listeners for progress tracking
152
+ audio.addEventListener('timeupdate', updateProgress);
153
+ audio.addEventListener('loadedmetadata', updateProgress);
154
+ audio.addEventListener('play', updateProgress);
155
+ audio.addEventListener('pause', updateProgress);
156
+ audio.addEventListener('ended', updateProgress);
157
+ };
158
+
159
+ /**
160
+ * Cleans up progress tracking for an audio element to prevent memory leaks
161
+ * @param audio - The HTML audio element to clean up
162
+ * @param channelNumber - The channel number this audio belongs to
163
+ * @param audioChannels - Array of audio channels
164
+ * @example
165
+ * ```typescript
166
+ * cleanupProgressTracking(audioElement, 0, audioChannels);
167
+ * ```
168
+ */
169
+ export const cleanupProgressTracking = (
170
+ audio: HTMLAudioElement,
171
+ channelNumber: number,
172
+ audioChannels: ExtendedAudioQueueChannel[]
173
+ ): void => {
174
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
175
+ if (!channel || !channel.progressCallbacks) return;
176
+
177
+ // Remove event listeners
178
+ const updateProgress: (() => void) | undefined = progressListeners.get(audio);
179
+ if (updateProgress) {
180
+ audio.removeEventListener('timeupdate', updateProgress);
181
+ audio.removeEventListener('loadedmetadata', updateProgress);
182
+ audio.removeEventListener('play', updateProgress);
183
+ audio.removeEventListener('pause', updateProgress);
184
+ audio.removeEventListener('ended', updateProgress);
185
+ progressListeners.delete(audio);
186
+ }
187
+
188
+ channel.progressCallbacks.delete(audio);
189
+ };
package/src/index.ts ADDED
@@ -0,0 +1,66 @@
1
+ /**
2
+ * @fileoverview Main entry point for the audio-channel-queue package
3
+ *
4
+ * A comprehensive audio queue management system with real-time progress tracking,
5
+ * multi-channel support, and extensive event handling capabilities.
6
+ *
7
+ * @example Basic Usage
8
+ * ```typescript
9
+ * import { queueAudio, onAudioProgress } from 'audio-channel-queue';
10
+ *
11
+ * // Queue an audio file
12
+ * await queueAudio('song.mp3');
13
+ *
14
+ * // Track progress
15
+ * onAudioProgress(0, (info) => {
16
+ * console.log(`Progress: ${info.progress * 100}%`);
17
+ * });
18
+ * ```
19
+ */
20
+
21
+ // Export all type definitions
22
+ export type {
23
+ AudioCompleteCallback,
24
+ AudioCompleteInfo,
25
+ AudioInfo,
26
+ AudioQueue,
27
+ AudioQueueChannel,
28
+ AudioStartCallback,
29
+ AudioStartInfo,
30
+ ExtendedAudioQueueChannel,
31
+ ProgressCallback,
32
+ QueueChangeCallback,
33
+ QueueItem,
34
+ QueueSnapshot
35
+ } from './types';
36
+
37
+ // Export core queue management functions
38
+ export {
39
+ playAudioQueue,
40
+ queueAudio,
41
+ stopAllAudio,
42
+ stopAllAudioInChannel,
43
+ stopCurrentAudioInChannel
44
+ } from './core';
45
+
46
+ // Export audio information and progress tracking functions
47
+ export {
48
+ audioChannels,
49
+ getAllChannelsInfo,
50
+ getCurrentAudioInfo,
51
+ getQueueSnapshot,
52
+ offAudioProgress,
53
+ onAudioComplete,
54
+ onAudioProgress,
55
+ onAudioStart,
56
+ onQueueChange,
57
+ offQueueChange
58
+ } from './info';
59
+
60
+ // Export utility functions (for advanced usage)
61
+ export {
62
+ cleanWebpackFilename,
63
+ createQueueSnapshot,
64
+ extractFileName,
65
+ getAudioInfoFromElement
66
+ } from './utils';
package/src/info.ts ADDED
@@ -0,0 +1,262 @@
1
+ /**
2
+ * @fileoverview Audio information and progress tracking functions for the audio-channel-queue package
3
+ */
4
+
5
+ import {
6
+ AudioInfo,
7
+ QueueSnapshot,
8
+ ProgressCallback,
9
+ QueueChangeCallback,
10
+ AudioStartCallback,
11
+ AudioCompleteCallback,
12
+ ExtendedAudioQueueChannel
13
+ } from './types';
14
+ import { getAudioInfoFromElement, createQueueSnapshot } from './utils';
15
+ import { setupProgressTracking, cleanupProgressTracking } from './events';
16
+
17
+ /**
18
+ * Global array of extended audio queue channels
19
+ */
20
+ export const audioChannels: ExtendedAudioQueueChannel[] = [];
21
+
22
+ /**
23
+ * Gets current audio information for a specific channel
24
+ * @param channelNumber - The channel number (defaults to 0)
25
+ * @returns AudioInfo object or null if no audio is playing
26
+ * @example
27
+ * ```typescript
28
+ * const info = getCurrentAudioInfo(0);
29
+ * if (info) {
30
+ * console.log(`Currently playing: ${info.fileName}`);
31
+ * console.log(`Progress: ${(info.progress * 100).toFixed(1)}%`);
32
+ * }
33
+ * ```
34
+ */
35
+ export const getCurrentAudioInfo = (channelNumber: number = 0): AudioInfo | null => {
36
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
37
+ if (!channel || channel.queue.length === 0) {
38
+ return null;
39
+ }
40
+
41
+ const currentAudio: HTMLAudioElement = channel.queue[0];
42
+ return getAudioInfoFromElement(currentAudio);
43
+ };
44
+
45
+ /**
46
+ * Gets audio information for all channels
47
+ * @returns Array of AudioInfo objects (null for channels with no audio)
48
+ * @example
49
+ * ```typescript
50
+ * const allInfo = getAllChannelsInfo();
51
+ * allInfo.forEach((info, channel) => {
52
+ * if (info) {
53
+ * console.log(`Channel ${channel}: ${info.fileName}`);
54
+ * }
55
+ * });
56
+ * ```
57
+ */
58
+ export const getAllChannelsInfo = (): (AudioInfo | null)[] => {
59
+ const allChannelsInfo: (AudioInfo | null)[] = [];
60
+
61
+ for (let i = 0; i < audioChannels.length; i++) {
62
+ allChannelsInfo.push(getCurrentAudioInfo(i));
63
+ }
64
+
65
+ return allChannelsInfo;
66
+ };
67
+
68
+ /**
69
+ * Gets a complete snapshot of the queue state for a specific channel
70
+ * @param channelNumber - The channel number
71
+ * @returns QueueSnapshot object or null if channel doesn't exist
72
+ * @example
73
+ * ```typescript
74
+ * const snapshot = getQueueSnapshot(0);
75
+ * if (snapshot) {
76
+ * console.log(`Queue has ${snapshot.totalItems} items`);
77
+ * console.log(`Currently playing: ${snapshot.items[0]?.fileName}`);
78
+ * }
79
+ * ```
80
+ */
81
+ export const getQueueSnapshot = (channelNumber: number): QueueSnapshot | null => {
82
+ return createQueueSnapshot(channelNumber, audioChannels);
83
+ };
84
+
85
+ /**
86
+ * Subscribes to real-time progress updates for a specific channel
87
+ * @param channelNumber - The channel number
88
+ * @param callback - Function to call with audio info updates
89
+ * @example
90
+ * ```typescript
91
+ * onAudioProgress(0, (info) => {
92
+ * updateProgressBar(info.progress);
93
+ * updateTimeDisplay(info.currentTime, info.duration);
94
+ * });
95
+ * ```
96
+ */
97
+ export const onAudioProgress = (channelNumber: number, callback: ProgressCallback): void => {
98
+ if (!audioChannels[channelNumber]) {
99
+ audioChannels[channelNumber] = {
100
+ audioCompleteCallbacks: new Set(),
101
+ audioStartCallbacks: new Set(),
102
+ progressCallbacks: new Map(),
103
+ queue: [],
104
+ queueChangeCallbacks: new Set()
105
+ };
106
+ }
107
+
108
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
109
+ if (!channel.progressCallbacks) {
110
+ channel.progressCallbacks = new Map();
111
+ }
112
+
113
+ // Add callback for current audio if exists
114
+ if (channel.queue.length > 0) {
115
+ const currentAudio: HTMLAudioElement = channel.queue[0];
116
+ if (!channel.progressCallbacks.has(currentAudio)) {
117
+ channel.progressCallbacks.set(currentAudio, new Set());
118
+ }
119
+ channel.progressCallbacks.get(currentAudio)!.add(callback);
120
+
121
+ // Set up tracking if not already done
122
+ setupProgressTracking(currentAudio, channelNumber, audioChannels);
123
+ }
124
+
125
+ // Store callback for future audio elements in this channel
126
+ if (!channel.progressCallbacks.has(null as any)) {
127
+ channel.progressCallbacks.set(null as any, new Set());
128
+ }
129
+ channel.progressCallbacks.get(null as any)!.add(callback);
130
+ };
131
+
132
+ /**
133
+ * Removes progress listeners for a specific channel
134
+ * @param channelNumber - The channel number
135
+ * @example
136
+ * ```typescript
137
+ * offAudioProgress(0); // Stop receiving progress updates for channel 0
138
+ * ```
139
+ */
140
+ export const offAudioProgress = (channelNumber: number): void => {
141
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
142
+ if (!channel || !channel.progressCallbacks) return;
143
+
144
+ // Clean up event listeners for current audio if exists
145
+ if (channel.queue.length > 0) {
146
+ const currentAudio: HTMLAudioElement = channel.queue[0];
147
+ cleanupProgressTracking(currentAudio, channelNumber, audioChannels);
148
+ }
149
+
150
+ // Clear all callbacks for this channel
151
+ channel.progressCallbacks.clear();
152
+ };
153
+
154
+ /**
155
+ * Subscribes to queue change events for a specific channel
156
+ * @param channelNumber - The channel number to monitor
157
+ * @param callback - Function to call when queue changes
158
+ * @example
159
+ * ```typescript
160
+ * onQueueChange(0, (snapshot) => {
161
+ * updateQueueDisplay(snapshot.items);
162
+ * updateQueueCount(snapshot.totalItems);
163
+ * });
164
+ * ```
165
+ */
166
+ export const onQueueChange = (channelNumber: number, callback: QueueChangeCallback): void => {
167
+ if (!audioChannels[channelNumber]) {
168
+ audioChannels[channelNumber] = {
169
+ audioCompleteCallbacks: new Set(),
170
+ audioStartCallbacks: new Set(),
171
+ progressCallbacks: new Map(),
172
+ queue: [],
173
+ queueChangeCallbacks: new Set()
174
+ };
175
+ }
176
+
177
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
178
+ if (!channel.queueChangeCallbacks) {
179
+ channel.queueChangeCallbacks = new Set();
180
+ }
181
+
182
+ channel.queueChangeCallbacks.add(callback);
183
+ };
184
+
185
+ /**
186
+ * Removes queue change listeners for a specific channel
187
+ * @param channelNumber - The channel number
188
+ * @example
189
+ * ```typescript
190
+ * offQueueChange(0); // Stop receiving queue change notifications for channel 0
191
+ * ```
192
+ */
193
+ export const offQueueChange = (channelNumber: number): void => {
194
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
195
+ if (!channel || !channel.queueChangeCallbacks) return;
196
+
197
+ channel.queueChangeCallbacks.clear();
198
+ };
199
+
200
+ /**
201
+ * Subscribes to audio start events for a specific channel
202
+ * @param channelNumber - The channel number to monitor
203
+ * @param callback - Function to call when audio starts playing
204
+ * @example
205
+ * ```typescript
206
+ * onAudioStart(0, (info) => {
207
+ * showNowPlaying(info.fileName);
208
+ * setTotalDuration(info.duration);
209
+ * });
210
+ * ```
211
+ */
212
+ export const onAudioStart = (channelNumber: number, callback: AudioStartCallback): void => {
213
+ if (!audioChannels[channelNumber]) {
214
+ audioChannels[channelNumber] = {
215
+ audioCompleteCallbacks: new Set(),
216
+ audioStartCallbacks: new Set(),
217
+ progressCallbacks: new Map(),
218
+ queue: [],
219
+ queueChangeCallbacks: new Set()
220
+ };
221
+ }
222
+
223
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
224
+ if (!channel.audioStartCallbacks) {
225
+ channel.audioStartCallbacks = new Set();
226
+ }
227
+
228
+ channel.audioStartCallbacks.add(callback);
229
+ };
230
+
231
+ /**
232
+ * Subscribes to audio complete events for a specific channel
233
+ * @param channelNumber - The channel number to monitor
234
+ * @param callback - Function to call when audio completes
235
+ * @example
236
+ * ```typescript
237
+ * onAudioComplete(0, (info) => {
238
+ * logPlaybackComplete(info.fileName);
239
+ * if (info.remainingInQueue === 0) {
240
+ * showQueueComplete();
241
+ * }
242
+ * });
243
+ * ```
244
+ */
245
+ export const onAudioComplete = (channelNumber: number, callback: AudioCompleteCallback): void => {
246
+ if (!audioChannels[channelNumber]) {
247
+ audioChannels[channelNumber] = {
248
+ audioCompleteCallbacks: new Set(),
249
+ audioStartCallbacks: new Set(),
250
+ progressCallbacks: new Map(),
251
+ queue: [],
252
+ queueChangeCallbacks: new Set()
253
+ };
254
+ }
255
+
256
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
257
+ if (!channel.audioCompleteCallbacks) {
258
+ channel.audioCompleteCallbacks = new Set();
259
+ }
260
+
261
+ channel.audioCompleteCallbacks.add(callback);
262
+ };
package/src/types.ts ADDED
@@ -0,0 +1,127 @@
1
+ /**
2
+ * @fileoverview Type definitions for the audio-channel-queue package
3
+ */
4
+
5
+ /**
6
+ * Array of HTMLAudioElement objects representing an audio queue
7
+ */
8
+ export type AudioQueue = HTMLAudioElement[];
9
+
10
+ /**
11
+ * Basic audio queue channel structure
12
+ */
13
+ export type AudioQueueChannel = {
14
+ queue: AudioQueue;
15
+ }
16
+
17
+ /**
18
+ * Comprehensive audio information interface providing metadata about currently playing audio
19
+ */
20
+ export interface AudioInfo {
21
+ /** Current playback position in milliseconds */
22
+ currentTime: number;
23
+ /** Total audio duration in milliseconds */
24
+ duration: number;
25
+ /** Extracted filename from the source URL */
26
+ fileName: string;
27
+ /** Whether the audio is currently playing */
28
+ isPlaying: boolean;
29
+ /** Playback progress as a decimal (0-1) */
30
+ progress: number;
31
+ /** Audio file source URL */
32
+ src: string;
33
+ }
34
+
35
+ /**
36
+ * Information provided when an audio file completes playback
37
+ */
38
+ export interface AudioCompleteInfo {
39
+ /** Channel number where the audio completed */
40
+ channelNumber: number;
41
+ /** Extracted filename from the source URL */
42
+ fileName: string;
43
+ /** Number of audio files remaining in the queue after completion */
44
+ remainingInQueue: number;
45
+ /** Audio file source URL */
46
+ src: string;
47
+ }
48
+
49
+ /**
50
+ * Information provided when an audio file starts playing
51
+ */
52
+ export interface AudioStartInfo {
53
+ /** Channel number where the audio is starting */
54
+ channelNumber: number;
55
+ /** Total audio duration in milliseconds */
56
+ duration: number;
57
+ /** Extracted filename from the source URL */
58
+ fileName: string;
59
+ /** Audio file source URL */
60
+ src: string;
61
+ }
62
+
63
+ /**
64
+ * Information about a single item in an audio queue
65
+ */
66
+ export interface QueueItem {
67
+ /** Total audio duration in milliseconds */
68
+ duration: number;
69
+ /** Extracted filename from the source URL */
70
+ fileName: string;
71
+ /** Whether this item is currently playing */
72
+ isCurrentlyPlaying: boolean;
73
+ /** Audio file source URL */
74
+ src: string;
75
+ }
76
+
77
+ /**
78
+ * Complete snapshot of a queue's current state
79
+ */
80
+ export interface QueueSnapshot {
81
+ /** Channel number this snapshot represents */
82
+ channelNumber: number;
83
+ /** Zero-based index of the currently playing item */
84
+ currentIndex: number;
85
+ /** Array of audio items in the queue with their metadata */
86
+ items: QueueItem[];
87
+ /** Total number of items in the queue */
88
+ totalItems: number;
89
+ }
90
+
91
+ /**
92
+ * Callback function type for audio progress updates
93
+ * @param info Current audio information
94
+ */
95
+ export type ProgressCallback = (info: AudioInfo) => void;
96
+
97
+ /**
98
+ * Callback function type for queue change notifications
99
+ * @param queueSnapshot Current state of the queue
100
+ */
101
+ export type QueueChangeCallback = (queueSnapshot: QueueSnapshot) => void;
102
+
103
+ /**
104
+ * Callback function type for audio start notifications
105
+ * @param audioInfo Information about the audio that started
106
+ */
107
+ export type AudioStartCallback = (audioInfo: AudioStartInfo) => void;
108
+
109
+ /**
110
+ * Callback function type for audio complete notifications
111
+ * @param audioInfo Information about the audio that completed
112
+ */
113
+ export type AudioCompleteCallback = (audioInfo: AudioCompleteInfo) => void;
114
+
115
+ /**
116
+ * Extended audio queue channel with event callback management
117
+ */
118
+ export type ExtendedAudioQueueChannel = AudioQueueChannel & {
119
+ /** Set of callbacks for audio completion events */
120
+ audioCompleteCallbacks?: Set<AudioCompleteCallback>;
121
+ /** Set of callbacks for audio start events */
122
+ audioStartCallbacks?: Set<AudioStartCallback>;
123
+ /** Map of audio elements to their progress callback sets */
124
+ progressCallbacks?: Map<HTMLAudioElement, Set<ProgressCallback>>;
125
+ /** Set of callbacks for queue change events */
126
+ queueChangeCallbacks?: Set<QueueChangeCallback>;
127
+ }