audio-channel-queue 1.4.0 → 1.6.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 ADDED
@@ -0,0 +1,273 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Core queue management functions for the audio-channel-queue package
4
+ */
5
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
6
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
7
+ return new (P || (P = Promise))(function (resolve, reject) {
8
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
9
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
10
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
11
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
12
+ });
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.stopAllAudio = exports.stopAllAudioInChannel = exports.stopCurrentAudioInChannel = exports.playAudioQueue = exports.queueAudioPriority = exports.queueAudio = void 0;
16
+ const info_1 = require("./info");
17
+ const utils_1 = require("./utils");
18
+ const events_1 = require("./events");
19
+ const volume_1 = require("./volume");
20
+ /**
21
+ * Queues an audio file to a specific channel and starts playing if it's the first in queue
22
+ * @param audioUrl - The URL of the audio file to queue
23
+ * @param channelNumber - The channel number to queue the audio to (defaults to 0)
24
+ * @param options - Optional configuration for the audio file
25
+ * @returns Promise that resolves when the audio is queued and starts playing (if first in queue)
26
+ * @example
27
+ * ```typescript
28
+ * await queueAudio('https://example.com/song.mp3', 0);
29
+ * await queueAudio('./sounds/notification.wav'); // Uses default channel 0
30
+ * await queueAudio('./music/loop.mp3', 1, { loop: true }); // Loop the audio
31
+ * await queueAudio('./urgent.wav', 0, { addToFront: true }); // Add to front of queue
32
+ * ```
33
+ */
34
+ const queueAudio = (audioUrl_1, ...args_1) => __awaiter(void 0, [audioUrl_1, ...args_1], void 0, function* (audioUrl, channelNumber = 0, options) {
35
+ if (!info_1.audioChannels[channelNumber]) {
36
+ info_1.audioChannels[channelNumber] = {
37
+ audioCompleteCallbacks: new Set(),
38
+ audioPauseCallbacks: new Set(),
39
+ audioResumeCallbacks: new Set(),
40
+ audioStartCallbacks: new Set(),
41
+ isPaused: false,
42
+ progressCallbacks: new Map(),
43
+ queue: [],
44
+ queueChangeCallbacks: new Set(),
45
+ volume: 1.0
46
+ };
47
+ }
48
+ const audio = new Audio(audioUrl);
49
+ // Apply audio configuration from options
50
+ if (options === null || options === void 0 ? void 0 : options.loop) {
51
+ audio.loop = true;
52
+ }
53
+ if ((options === null || options === void 0 ? void 0 : options.volume) !== undefined) {
54
+ const clampedVolume = Math.max(0, Math.min(1, options.volume));
55
+ // Handle NaN case - default to channel volume or 1.0
56
+ const safeVolume = isNaN(clampedVolume) ? (info_1.audioChannels[channelNumber].volume || 1.0) : clampedVolume;
57
+ audio.volume = safeVolume;
58
+ // Also update the channel volume
59
+ info_1.audioChannels[channelNumber].volume = safeVolume;
60
+ }
61
+ else {
62
+ // Use channel volume if no specific volume is set
63
+ const channelVolume = info_1.audioChannels[channelNumber].volume || 1.0;
64
+ audio.volume = channelVolume;
65
+ }
66
+ // Add to front or back of queue based on options
67
+ if (((options === null || options === void 0 ? void 0 : options.addToFront) || (options === null || options === void 0 ? void 0 : options.priority)) && info_1.audioChannels[channelNumber].queue.length > 0) {
68
+ // Insert after the currently playing audio (index 1)
69
+ info_1.audioChannels[channelNumber].queue.splice(1, 0, audio);
70
+ }
71
+ else if (((options === null || options === void 0 ? void 0 : options.addToFront) || (options === null || options === void 0 ? void 0 : options.priority)) && info_1.audioChannels[channelNumber].queue.length === 0) {
72
+ // If queue is empty, just add normally
73
+ info_1.audioChannels[channelNumber].queue.push(audio);
74
+ }
75
+ else {
76
+ // Default behavior - add to back of queue
77
+ info_1.audioChannels[channelNumber].queue.push(audio);
78
+ }
79
+ (0, events_1.emitQueueChange)(channelNumber, info_1.audioChannels);
80
+ if (info_1.audioChannels[channelNumber].queue.length === 1) {
81
+ // Don't await - let playback happen asynchronously
82
+ (0, exports.playAudioQueue)(channelNumber).catch(console.error);
83
+ }
84
+ });
85
+ exports.queueAudio = queueAudio;
86
+ /**
87
+ * Adds an audio file to the front of the queue in a specific channel
88
+ * This is a convenience function that places the audio right after the currently playing track
89
+ * @param audioUrl - The URL of the audio file to queue
90
+ * @param channelNumber - The channel number to queue the audio to (defaults to 0)
91
+ * @param options - Optional configuration for the audio file
92
+ * @returns Promise that resolves when the audio is queued
93
+ * @example
94
+ * ```typescript
95
+ * await queueAudioPriority('./urgent-announcement.wav', 0);
96
+ * await queueAudioPriority('./priority-sound.mp3', 1, { loop: true });
97
+ * ```
98
+ */
99
+ const queueAudioPriority = (audioUrl_1, ...args_1) => __awaiter(void 0, [audioUrl_1, ...args_1], void 0, function* (audioUrl, channelNumber = 0, options) {
100
+ const priorityOptions = Object.assign(Object.assign({}, options), { addToFront: true });
101
+ return (0, exports.queueAudio)(audioUrl, channelNumber, priorityOptions);
102
+ });
103
+ exports.queueAudioPriority = queueAudioPriority;
104
+ /**
105
+ * Plays the audio queue for a specific channel
106
+ * @param channelNumber - The channel number to play
107
+ * @returns Promise that resolves when the current audio finishes playing
108
+ * @example
109
+ * ```typescript
110
+ * await playAudioQueue(0); // Play queue for channel 0
111
+ * ```
112
+ */
113
+ const playAudioQueue = (channelNumber) => __awaiter(void 0, void 0, void 0, function* () {
114
+ const channel = info_1.audioChannels[channelNumber];
115
+ if (!channel || channel.queue.length === 0)
116
+ return;
117
+ const currentAudio = channel.queue[0];
118
+ // Apply channel volume if not already set
119
+ if (currentAudio.volume === 1.0 && channel.volume !== undefined) {
120
+ currentAudio.volume = channel.volume;
121
+ }
122
+ (0, events_1.setupProgressTracking)(currentAudio, channelNumber, info_1.audioChannels);
123
+ // Apply volume ducking when audio starts
124
+ yield (0, volume_1.applyVolumeDucking)(channelNumber);
125
+ return new Promise((resolve) => {
126
+ let hasStarted = false;
127
+ let metadataLoaded = false;
128
+ let playStarted = false;
129
+ // Check if we should fire onAudioStart (both conditions met)
130
+ const tryFireAudioStart = () => {
131
+ if (!hasStarted && metadataLoaded && playStarted) {
132
+ hasStarted = true;
133
+ (0, events_1.emitAudioStart)(channelNumber, {
134
+ channelNumber,
135
+ duration: currentAudio.duration * 1000, // Now guaranteed to have valid duration
136
+ fileName: (0, utils_1.extractFileName)(currentAudio.src),
137
+ src: currentAudio.src
138
+ }, info_1.audioChannels);
139
+ }
140
+ };
141
+ // Event handler for when metadata loads (duration becomes available)
142
+ const handleLoadedMetadata = () => {
143
+ metadataLoaded = true;
144
+ tryFireAudioStart();
145
+ };
146
+ // Event handler for when audio actually starts playing
147
+ const handlePlay = () => {
148
+ playStarted = true;
149
+ tryFireAudioStart();
150
+ };
151
+ // Event handler for when audio ends
152
+ const handleEnded = () => __awaiter(void 0, void 0, void 0, function* () {
153
+ (0, events_1.emitAudioComplete)(channelNumber, {
154
+ channelNumber,
155
+ fileName: (0, utils_1.extractFileName)(currentAudio.src),
156
+ remainingInQueue: channel.queue.length - 1,
157
+ src: currentAudio.src
158
+ }, info_1.audioChannels);
159
+ // Restore volume levels when priority channel stops
160
+ yield (0, volume_1.restoreVolumeLevels)(channelNumber);
161
+ // Clean up event listeners
162
+ currentAudio.removeEventListener('loadedmetadata', handleLoadedMetadata);
163
+ currentAudio.removeEventListener('play', handlePlay);
164
+ currentAudio.removeEventListener('ended', handleEnded);
165
+ (0, events_1.cleanupProgressTracking)(currentAudio, channelNumber, info_1.audioChannels);
166
+ // Handle looping vs non-looping audio
167
+ if (currentAudio.loop) {
168
+ // For looping audio, reset current time and continue playing
169
+ currentAudio.currentTime = 0;
170
+ yield currentAudio.play();
171
+ // Don't remove from queue, but resolve the promise so tests don't hang
172
+ resolve();
173
+ }
174
+ else {
175
+ // For non-looping audio, remove from queue and play next
176
+ channel.queue.shift();
177
+ // Emit queue change after completion
178
+ setTimeout(() => (0, events_1.emitQueueChange)(channelNumber, info_1.audioChannels), 10);
179
+ yield (0, exports.playAudioQueue)(channelNumber);
180
+ resolve();
181
+ }
182
+ });
183
+ // Add event listeners
184
+ currentAudio.addEventListener('loadedmetadata', handleLoadedMetadata);
185
+ currentAudio.addEventListener('play', handlePlay);
186
+ currentAudio.addEventListener('ended', handleEnded);
187
+ // Check if metadata is already loaded (in case it loads before we add the listener)
188
+ if (currentAudio.readyState >= 1) { // HAVE_METADATA or higher
189
+ metadataLoaded = true;
190
+ }
191
+ currentAudio.play();
192
+ });
193
+ });
194
+ exports.playAudioQueue = playAudioQueue;
195
+ /**
196
+ * Stops the currently playing audio in a specific channel and plays the next audio in queue
197
+ * @param channelNumber - The channel number (defaults to 0)
198
+ * @example
199
+ * ```typescript
200
+ * await stopCurrentAudioInChannel(); // Stop current audio in default channel (0)
201
+ * await stopCurrentAudioInChannel(1); // Stop current audio in channel 1
202
+ * ```
203
+ */
204
+ const stopCurrentAudioInChannel = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (channelNumber = 0) {
205
+ const channel = info_1.audioChannels[channelNumber];
206
+ if (channel && channel.queue.length > 0) {
207
+ const currentAudio = channel.queue[0];
208
+ (0, events_1.emitAudioComplete)(channelNumber, {
209
+ channelNumber,
210
+ fileName: (0, utils_1.extractFileName)(currentAudio.src),
211
+ remainingInQueue: channel.queue.length - 1,
212
+ src: currentAudio.src
213
+ }, info_1.audioChannels);
214
+ // Restore volume levels when stopping
215
+ yield (0, volume_1.restoreVolumeLevels)(channelNumber);
216
+ currentAudio.pause();
217
+ (0, events_1.cleanupProgressTracking)(currentAudio, channelNumber, info_1.audioChannels);
218
+ channel.queue.shift();
219
+ channel.isPaused = false; // Reset pause state
220
+ (0, events_1.emitQueueChange)(channelNumber, info_1.audioChannels);
221
+ // Start next audio without waiting for it to complete
222
+ (0, exports.playAudioQueue)(channelNumber).catch(console.error);
223
+ }
224
+ });
225
+ exports.stopCurrentAudioInChannel = stopCurrentAudioInChannel;
226
+ /**
227
+ * Stops all audio in a specific channel and clears the entire queue
228
+ * @param channelNumber - The channel number (defaults to 0)
229
+ * @example
230
+ * ```typescript
231
+ * await stopAllAudioInChannel(); // Clear all audio in default channel (0)
232
+ * await stopAllAudioInChannel(1); // Clear all audio in channel 1
233
+ * ```
234
+ */
235
+ const stopAllAudioInChannel = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (channelNumber = 0) {
236
+ const channel = info_1.audioChannels[channelNumber];
237
+ if (channel) {
238
+ if (channel.queue.length > 0) {
239
+ const currentAudio = channel.queue[0];
240
+ (0, events_1.emitAudioComplete)(channelNumber, {
241
+ channelNumber,
242
+ fileName: (0, utils_1.extractFileName)(currentAudio.src),
243
+ remainingInQueue: 0, // Will be 0 since we're clearing the queue
244
+ src: currentAudio.src
245
+ }, info_1.audioChannels);
246
+ // Restore volume levels when stopping
247
+ yield (0, volume_1.restoreVolumeLevels)(channelNumber);
248
+ currentAudio.pause();
249
+ (0, events_1.cleanupProgressTracking)(currentAudio, channelNumber, info_1.audioChannels);
250
+ }
251
+ // Clean up all progress tracking for this channel
252
+ channel.queue.forEach(audio => (0, events_1.cleanupProgressTracking)(audio, channelNumber, info_1.audioChannels));
253
+ channel.queue = [];
254
+ channel.isPaused = false; // Reset pause state
255
+ (0, events_1.emitQueueChange)(channelNumber, info_1.audioChannels);
256
+ }
257
+ });
258
+ exports.stopAllAudioInChannel = stopAllAudioInChannel;
259
+ /**
260
+ * Stops all audio across all channels and clears all queues
261
+ * @example
262
+ * ```typescript
263
+ * await stopAllAudio(); // Emergency stop - clears everything
264
+ * ```
265
+ */
266
+ const stopAllAudio = () => __awaiter(void 0, void 0, void 0, function* () {
267
+ const stopPromises = [];
268
+ info_1.audioChannels.forEach((_channel, index) => {
269
+ stopPromises.push((0, exports.stopAllAudioInChannel)(index));
270
+ });
271
+ yield Promise.all(stopPromises);
272
+ });
273
+ exports.stopAllAudio = stopAllAudio;
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @fileoverview Event handling and emission for the audio-channel-queue package
3
+ */
4
+ import { AudioStartInfo, AudioCompleteInfo, ExtendedAudioQueueChannel, AudioInfo } from './types';
5
+ /**
6
+ * Emits a queue change event to all registered listeners for a specific channel
7
+ * @param channelNumber - The channel number that experienced a queue change
8
+ * @param audioChannels - Array of audio channels
9
+ * @example
10
+ * ```typescript
11
+ * emitQueueChange(0, audioChannels); // Notifies all queue change listeners
12
+ * ```
13
+ */
14
+ export declare const emitQueueChange: (channelNumber: number, audioChannels: ExtendedAudioQueueChannel[]) => void;
15
+ /**
16
+ * Emits an audio start event to all registered listeners for a specific channel
17
+ * @param channelNumber - The channel number where audio started
18
+ * @param audioInfo - Information about the audio that started
19
+ * @param audioChannels - Array of audio channels
20
+ * @example
21
+ * ```typescript
22
+ * emitAudioStart(0, { src: 'song.mp3', fileName: 'song.mp3', duration: 180000, channelNumber: 0 }, audioChannels);
23
+ * ```
24
+ */
25
+ export declare const emitAudioStart: (channelNumber: number, audioInfo: AudioStartInfo, audioChannels: ExtendedAudioQueueChannel[]) => void;
26
+ /**
27
+ * Emits an audio complete event to all registered listeners for a specific channel
28
+ * @param channelNumber - The channel number where audio completed
29
+ * @param audioInfo - Information about the audio that completed
30
+ * @param audioChannels - Array of audio channels
31
+ * @example
32
+ * ```typescript
33
+ * emitAudioComplete(0, { src: 'song.mp3', fileName: 'song.mp3', channelNumber: 0, remainingInQueue: 2 }, audioChannels);
34
+ * ```
35
+ */
36
+ export declare const emitAudioComplete: (channelNumber: number, audioInfo: AudioCompleteInfo, audioChannels: ExtendedAudioQueueChannel[]) => void;
37
+ /**
38
+ * Emits an audio pause event to all registered listeners for a specific channel
39
+ * @param channelNumber - The channel number where audio was paused
40
+ * @param audioInfo - Information about the audio that was paused
41
+ * @param audioChannels - Array of audio channels
42
+ * @example
43
+ * ```typescript
44
+ * emitAudioPause(0, audioInfo, audioChannels);
45
+ * ```
46
+ */
47
+ export declare const emitAudioPause: (channelNumber: number, audioInfo: AudioInfo, audioChannels: ExtendedAudioQueueChannel[]) => void;
48
+ /**
49
+ * Emits an audio resume event to all registered listeners for a specific channel
50
+ * @param channelNumber - The channel number where audio was resumed
51
+ * @param audioInfo - Information about the audio that was resumed
52
+ * @param audioChannels - Array of audio channels
53
+ * @example
54
+ * ```typescript
55
+ * emitAudioResume(0, audioInfo, audioChannels);
56
+ * ```
57
+ */
58
+ export declare const emitAudioResume: (channelNumber: number, audioInfo: AudioInfo, audioChannels: ExtendedAudioQueueChannel[]) => void;
59
+ /**
60
+ * Sets up comprehensive progress tracking for an audio element
61
+ * @param audio - The HTML audio element to track
62
+ * @param channelNumber - The channel number this audio belongs to
63
+ * @param audioChannels - Array of audio channels
64
+ * @example
65
+ * ```typescript
66
+ * const audioElement = new Audio('song.mp3');
67
+ * setupProgressTracking(audioElement, 0, audioChannels);
68
+ * ```
69
+ */
70
+ export declare const setupProgressTracking: (audio: HTMLAudioElement, channelNumber: number, audioChannels: ExtendedAudioQueueChannel[]) => void;
71
+ /**
72
+ * Cleans up progress tracking for an audio element to prevent memory leaks
73
+ * @param audio - The HTML audio element to clean up
74
+ * @param channelNumber - The channel number this audio belongs to
75
+ * @param audioChannels - Array of audio channels
76
+ * @example
77
+ * ```typescript
78
+ * cleanupProgressTracking(audioElement, 0, audioChannels);
79
+ * ```
80
+ */
81
+ export declare const cleanupProgressTracking: (audio: HTMLAudioElement, channelNumber: number, audioChannels: ExtendedAudioQueueChannel[]) => void;
package/dist/events.js ADDED
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Event handling and emission for the audio-channel-queue package
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.cleanupProgressTracking = exports.setupProgressTracking = exports.emitAudioResume = exports.emitAudioPause = exports.emitAudioComplete = exports.emitAudioStart = exports.emitQueueChange = void 0;
7
+ const utils_1 = require("./utils");
8
+ /**
9
+ * Emits a queue change event to all registered listeners for a specific channel
10
+ * @param channelNumber - The channel number that experienced a queue change
11
+ * @param audioChannels - Array of audio channels
12
+ * @example
13
+ * ```typescript
14
+ * emitQueueChange(0, audioChannels); // Notifies all queue change listeners
15
+ * ```
16
+ */
17
+ const emitQueueChange = (channelNumber, audioChannels) => {
18
+ const channel = audioChannels[channelNumber];
19
+ if (!channel || !channel.queueChangeCallbacks)
20
+ return;
21
+ const snapshot = (0, utils_1.createQueueSnapshot)(channelNumber, audioChannels);
22
+ if (!snapshot)
23
+ return;
24
+ channel.queueChangeCallbacks.forEach(callback => {
25
+ try {
26
+ callback(snapshot);
27
+ }
28
+ catch (error) {
29
+ console.error('Error in queue change callback:', error);
30
+ }
31
+ });
32
+ };
33
+ exports.emitQueueChange = emitQueueChange;
34
+ /**
35
+ * Emits an audio start event to all registered listeners for a specific channel
36
+ * @param channelNumber - The channel number where audio started
37
+ * @param audioInfo - Information about the audio that started
38
+ * @param audioChannels - Array of audio channels
39
+ * @example
40
+ * ```typescript
41
+ * emitAudioStart(0, { src: 'song.mp3', fileName: 'song.mp3', duration: 180000, channelNumber: 0 }, audioChannels);
42
+ * ```
43
+ */
44
+ const emitAudioStart = (channelNumber, audioInfo, audioChannels) => {
45
+ const channel = audioChannels[channelNumber];
46
+ if (!channel || !channel.audioStartCallbacks)
47
+ return;
48
+ channel.audioStartCallbacks.forEach(callback => {
49
+ try {
50
+ callback(audioInfo);
51
+ }
52
+ catch (error) {
53
+ console.error('Error in audio start callback:', error);
54
+ }
55
+ });
56
+ };
57
+ exports.emitAudioStart = emitAudioStart;
58
+ /**
59
+ * Emits an audio complete event to all registered listeners for a specific channel
60
+ * @param channelNumber - The channel number where audio completed
61
+ * @param audioInfo - Information about the audio that completed
62
+ * @param audioChannels - Array of audio channels
63
+ * @example
64
+ * ```typescript
65
+ * emitAudioComplete(0, { src: 'song.mp3', fileName: 'song.mp3', channelNumber: 0, remainingInQueue: 2 }, audioChannels);
66
+ * ```
67
+ */
68
+ const emitAudioComplete = (channelNumber, audioInfo, audioChannels) => {
69
+ const channel = audioChannels[channelNumber];
70
+ if (!channel || !channel.audioCompleteCallbacks)
71
+ return;
72
+ channel.audioCompleteCallbacks.forEach(callback => {
73
+ try {
74
+ callback(audioInfo);
75
+ }
76
+ catch (error) {
77
+ console.error('Error in audio complete callback:', error);
78
+ }
79
+ });
80
+ };
81
+ exports.emitAudioComplete = emitAudioComplete;
82
+ /**
83
+ * Emits an audio pause event to all registered listeners for a specific channel
84
+ * @param channelNumber - The channel number where audio was paused
85
+ * @param audioInfo - Information about the audio that was paused
86
+ * @param audioChannels - Array of audio channels
87
+ * @example
88
+ * ```typescript
89
+ * emitAudioPause(0, audioInfo, audioChannels);
90
+ * ```
91
+ */
92
+ const emitAudioPause = (channelNumber, audioInfo, audioChannels) => {
93
+ const channel = audioChannels[channelNumber];
94
+ if (!channel || !channel.audioPauseCallbacks)
95
+ return;
96
+ channel.audioPauseCallbacks.forEach(callback => {
97
+ try {
98
+ callback(channelNumber, audioInfo);
99
+ }
100
+ catch (error) {
101
+ console.error('Error in audio pause callback:', error);
102
+ }
103
+ });
104
+ };
105
+ exports.emitAudioPause = emitAudioPause;
106
+ /**
107
+ * Emits an audio resume event to all registered listeners for a specific channel
108
+ * @param channelNumber - The channel number where audio was resumed
109
+ * @param audioInfo - Information about the audio that was resumed
110
+ * @param audioChannels - Array of audio channels
111
+ * @example
112
+ * ```typescript
113
+ * emitAudioResume(0, audioInfo, audioChannels);
114
+ * ```
115
+ */
116
+ const emitAudioResume = (channelNumber, audioInfo, audioChannels) => {
117
+ const channel = audioChannels[channelNumber];
118
+ if (!channel || !channel.audioResumeCallbacks)
119
+ return;
120
+ channel.audioResumeCallbacks.forEach(callback => {
121
+ try {
122
+ callback(channelNumber, audioInfo);
123
+ }
124
+ catch (error) {
125
+ console.error('Error in audio resume callback:', error);
126
+ }
127
+ });
128
+ };
129
+ exports.emitAudioResume = emitAudioResume;
130
+ // Store listener functions for cleanup
131
+ const progressListeners = new WeakMap();
132
+ /**
133
+ * Sets up comprehensive progress tracking for an audio element
134
+ * @param audio - The HTML audio element to track
135
+ * @param channelNumber - The channel number this audio belongs to
136
+ * @param audioChannels - Array of audio channels
137
+ * @example
138
+ * ```typescript
139
+ * const audioElement = new Audio('song.mp3');
140
+ * setupProgressTracking(audioElement, 0, audioChannels);
141
+ * ```
142
+ */
143
+ const setupProgressTracking = (audio, channelNumber, audioChannels) => {
144
+ const channel = audioChannels[channelNumber];
145
+ if (!channel)
146
+ return;
147
+ if (!channel.progressCallbacks) {
148
+ channel.progressCallbacks = new Map();
149
+ }
150
+ // Don't set up tracking if already exists
151
+ if (progressListeners.has(audio))
152
+ return;
153
+ const updateProgress = () => {
154
+ var _a, _b;
155
+ // Get callbacks for this specific audio element AND the channel-wide callbacks
156
+ const audioCallbacks = ((_a = channel.progressCallbacks) === null || _a === void 0 ? void 0 : _a.get(audio)) || new Set();
157
+ const channelCallbacks = ((_b = channel.progressCallbacks) === null || _b === void 0 ? void 0 : _b.get(null)) || new Set();
158
+ // Combine both sets of callbacks
159
+ const allCallbacks = new Set([...audioCallbacks, ...channelCallbacks]);
160
+ if (allCallbacks.size === 0)
161
+ return;
162
+ const info = (0, utils_1.getAudioInfoFromElement)(audio, channelNumber, audioChannels);
163
+ if (info) {
164
+ allCallbacks.forEach((callback) => {
165
+ try {
166
+ callback(info);
167
+ }
168
+ catch (error) {
169
+ console.error('Error in progress callback:', error);
170
+ }
171
+ });
172
+ }
173
+ };
174
+ // Store the listener function for cleanup
175
+ progressListeners.set(audio, updateProgress);
176
+ // Set up comprehensive event listeners for progress tracking
177
+ audio.addEventListener('timeupdate', updateProgress);
178
+ audio.addEventListener('loadedmetadata', updateProgress);
179
+ audio.addEventListener('play', updateProgress);
180
+ audio.addEventListener('pause', updateProgress);
181
+ audio.addEventListener('ended', updateProgress);
182
+ };
183
+ exports.setupProgressTracking = setupProgressTracking;
184
+ /**
185
+ * Cleans up progress tracking for an audio element to prevent memory leaks
186
+ * @param audio - The HTML audio element to clean up
187
+ * @param channelNumber - The channel number this audio belongs to
188
+ * @param audioChannels - Array of audio channels
189
+ * @example
190
+ * ```typescript
191
+ * cleanupProgressTracking(audioElement, 0, audioChannels);
192
+ * ```
193
+ */
194
+ const cleanupProgressTracking = (audio, channelNumber, audioChannels) => {
195
+ const channel = audioChannels[channelNumber];
196
+ if (!channel || !channel.progressCallbacks)
197
+ return;
198
+ // Remove event listeners
199
+ const updateProgress = progressListeners.get(audio);
200
+ if (updateProgress) {
201
+ audio.removeEventListener('timeupdate', updateProgress);
202
+ audio.removeEventListener('loadedmetadata', updateProgress);
203
+ audio.removeEventListener('play', updateProgress);
204
+ audio.removeEventListener('pause', updateProgress);
205
+ audio.removeEventListener('ended', updateProgress);
206
+ progressListeners.delete(audio);
207
+ }
208
+ channel.progressCallbacks.delete(audio);
209
+ };
210
+ exports.cleanupProgressTracking = cleanupProgressTracking;
@@ -0,0 +1,29 @@
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, 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
+ * ```
23
+ */
24
+ export type { AudioCompleteCallback, AudioCompleteInfo, AudioInfo, AudioPauseCallback, AudioQueue, AudioQueueChannel, AudioQueueOptions, AudioResumeCallback, AudioStartCallback, AudioStartInfo, ExtendedAudioQueueChannel, ProgressCallback, QueueChangeCallback, QueueItem, QueueSnapshot, VolumeConfig } from './types';
25
+ export { playAudioQueue, queueAudio, queueAudioPriority, stopAllAudio, stopAllAudioInChannel, stopCurrentAudioInChannel } from './core';
26
+ export { getAllChannelsPauseState, isChannelPaused, pauseAllChannels, pauseChannel, resumeAllChannels, resumeChannel, togglePauseAllChannels, togglePauseChannel } from './pause';
27
+ export { applyVolumeDucking, clearVolumeDucking, getAllChannelsVolume, getChannelVolume, restoreVolumeLevels, setAllChannelsVolume, setChannelVolume, setVolumeDucking, transitionVolume } from './volume';
28
+ export { audioChannels, getAllChannelsInfo, getCurrentAudioInfo, getQueueSnapshot, offAudioPause, offAudioProgress, offAudioResume, offQueueChange, onAudioComplete, onAudioPause, onAudioProgress, onAudioResume, onAudioStart, onQueueChange } from './info';
29
+ export { cleanWebpackFilename, createQueueSnapshot, extractFileName, getAudioInfoFromElement } from './utils';
package/dist/index.js ADDED
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Main entry point for the audio-channel-queue package
4
+ *
5
+ * A comprehensive audio queue management system with real-time progress tracking,
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
+ * ```
24
+ */
25
+ 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.audioChannels = exports.transitionVolume = exports.setVolumeDucking = exports.setChannelVolume = exports.setAllChannelsVolume = exports.restoreVolumeLevels = exports.getChannelVolume = exports.getAllChannelsVolume = exports.clearVolumeDucking = exports.applyVolumeDucking = exports.togglePauseChannel = exports.togglePauseAllChannels = exports.resumeChannel = exports.resumeAllChannels = exports.pauseChannel = exports.pauseAllChannels = exports.isChannelPaused = exports.getAllChannelsPauseState = exports.stopCurrentAudioInChannel = exports.stopAllAudioInChannel = exports.stopAllAudio = exports.queueAudioPriority = exports.queueAudio = exports.playAudioQueue = void 0;
27
+ // Export core queue management functions
28
+ var core_1 = require("./core");
29
+ Object.defineProperty(exports, "playAudioQueue", { enumerable: true, get: function () { return core_1.playAudioQueue; } });
30
+ Object.defineProperty(exports, "queueAudio", { enumerable: true, get: function () { return core_1.queueAudio; } });
31
+ 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
+ Object.defineProperty(exports, "stopCurrentAudioInChannel", { enumerable: true, get: function () { return core_1.stopCurrentAudioInChannel; } });
35
+ // Export pause and resume functionality
36
+ var pause_1 = require("./pause");
37
+ Object.defineProperty(exports, "getAllChannelsPauseState", { enumerable: true, get: function () { return pause_1.getAllChannelsPauseState; } });
38
+ Object.defineProperty(exports, "isChannelPaused", { enumerable: true, get: function () { return pause_1.isChannelPaused; } });
39
+ Object.defineProperty(exports, "pauseAllChannels", { enumerable: true, get: function () { return pause_1.pauseAllChannels; } });
40
+ Object.defineProperty(exports, "pauseChannel", { enumerable: true, get: function () { return pause_1.pauseChannel; } });
41
+ Object.defineProperty(exports, "resumeAllChannels", { enumerable: true, get: function () { return pause_1.resumeAllChannels; } });
42
+ Object.defineProperty(exports, "resumeChannel", { enumerable: true, get: function () { return pause_1.resumeChannel; } });
43
+ Object.defineProperty(exports, "togglePauseAllChannels", { enumerable: true, get: function () { return pause_1.togglePauseAllChannels; } });
44
+ Object.defineProperty(exports, "togglePauseChannel", { enumerable: true, get: function () { return pause_1.togglePauseChannel; } });
45
+ // Export volume control functions
46
+ var volume_1 = require("./volume");
47
+ Object.defineProperty(exports, "applyVolumeDucking", { enumerable: true, get: function () { return volume_1.applyVolumeDucking; } });
48
+ Object.defineProperty(exports, "clearVolumeDucking", { enumerable: true, get: function () { return volume_1.clearVolumeDucking; } });
49
+ Object.defineProperty(exports, "getAllChannelsVolume", { enumerable: true, get: function () { return volume_1.getAllChannelsVolume; } });
50
+ 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
+ Object.defineProperty(exports, "setAllChannelsVolume", { enumerable: true, get: function () { return volume_1.setAllChannelsVolume; } });
53
+ Object.defineProperty(exports, "setChannelVolume", { enumerable: true, get: function () { return volume_1.setChannelVolume; } });
54
+ Object.defineProperty(exports, "setVolumeDucking", { enumerable: true, get: function () { return volume_1.setVolumeDucking; } });
55
+ Object.defineProperty(exports, "transitionVolume", { enumerable: true, get: function () { return volume_1.transitionVolume; } });
56
+ // Export audio information and progress tracking functions
57
+ var info_1 = require("./info");
58
+ Object.defineProperty(exports, "audioChannels", { enumerable: true, get: function () { return info_1.audioChannels; } });
59
+ Object.defineProperty(exports, "getAllChannelsInfo", { enumerable: true, get: function () { return info_1.getAllChannelsInfo; } });
60
+ Object.defineProperty(exports, "getCurrentAudioInfo", { enumerable: true, get: function () { return info_1.getCurrentAudioInfo; } });
61
+ Object.defineProperty(exports, "getQueueSnapshot", { enumerable: true, get: function () { return info_1.getQueueSnapshot; } });
62
+ Object.defineProperty(exports, "offAudioPause", { enumerable: true, get: function () { return info_1.offAudioPause; } });
63
+ Object.defineProperty(exports, "offAudioProgress", { enumerable: true, get: function () { return info_1.offAudioProgress; } });
64
+ Object.defineProperty(exports, "offAudioResume", { enumerable: true, get: function () { return info_1.offAudioResume; } });
65
+ Object.defineProperty(exports, "offQueueChange", { enumerable: true, get: function () { return info_1.offQueueChange; } });
66
+ Object.defineProperty(exports, "onAudioComplete", { enumerable: true, get: function () { return info_1.onAudioComplete; } });
67
+ Object.defineProperty(exports, "onAudioPause", { enumerable: true, get: function () { return info_1.onAudioPause; } });
68
+ Object.defineProperty(exports, "onAudioProgress", { enumerable: true, get: function () { return info_1.onAudioProgress; } });
69
+ Object.defineProperty(exports, "onAudioResume", { enumerable: true, get: function () { return info_1.onAudioResume; } });
70
+ Object.defineProperty(exports, "onAudioStart", { enumerable: true, get: function () { return info_1.onAudioStart; } });
71
+ Object.defineProperty(exports, "onQueueChange", { enumerable: true, get: function () { return info_1.onQueueChange; } });
72
+ // Export utility functions (for advanced usage)
73
+ var utils_1 = require("./utils");
74
+ Object.defineProperty(exports, "cleanWebpackFilename", { enumerable: true, get: function () { return utils_1.cleanWebpackFilename; } });
75
+ Object.defineProperty(exports, "createQueueSnapshot", { enumerable: true, get: function () { return utils_1.createQueueSnapshot; } });
76
+ Object.defineProperty(exports, "extractFileName", { enumerable: true, get: function () { return utils_1.extractFileName; } });
77
+ Object.defineProperty(exports, "getAudioInfoFromElement", { enumerable: true, get: function () { return utils_1.getAudioInfoFromElement; } });