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/src/pause.ts CHANGED
@@ -1,190 +1,190 @@
1
- /**
2
- * @fileoverview Pause and resume management functions for the audio-channel-queue package
3
- */
4
-
5
- import { ExtendedAudioQueueChannel, AudioInfo } from './types';
6
- import { audioChannels } from './info';
7
- import { getAudioInfoFromElement } from './utils';
8
- import { emitAudioPause, emitAudioResume } from './events';
9
-
10
- /**
11
- * Pauses the currently playing audio in a specific channel
12
- * @param channelNumber - The channel number to pause (defaults to 0)
13
- * @returns Promise that resolves when the audio is paused
14
- * @example
15
- * ```typescript
16
- * await pauseChannel(0); // Pause audio in channel 0
17
- * await pauseChannel(); // Pause audio in default channel
18
- * ```
19
- */
20
- export const pauseChannel = async (channelNumber: number = 0): Promise<void> => {
21
- const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
22
-
23
- if (channel && channel.queue.length > 0) {
24
- const currentAudio: HTMLAudioElement = channel.queue[0];
25
-
26
- if (!currentAudio.paused && !currentAudio.ended) {
27
- currentAudio.pause();
28
- channel.isPaused = true;
29
-
30
- const audioInfo: AudioInfo | null = getAudioInfoFromElement(currentAudio, channelNumber, audioChannels);
31
- if (audioInfo) {
32
- emitAudioPause(channelNumber, audioInfo, audioChannels);
33
- }
34
- }
35
- }
36
- };
37
-
38
- /**
39
- * Resumes the currently paused audio in a specific channel
40
- * @param channelNumber - The channel number to resume (defaults to 0)
41
- * @returns Promise that resolves when the audio starts playing
42
- * @example
43
- * ```typescript
44
- * await resumeChannel(0); // Resume audio in channel 0
45
- * await resumeChannel(); // Resume audio in default channel
46
- * ```
47
- */
48
- export const resumeChannel = async (channelNumber: number = 0): Promise<void> => {
49
- const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
50
-
51
- if (channel && channel.queue.length > 0) {
52
- const currentAudio: HTMLAudioElement = channel.queue[0];
53
-
54
- // Only resume if both the channel is marked as paused AND the audio element is actually paused
55
- if (channel.isPaused && currentAudio.paused && !currentAudio.ended) {
56
- await currentAudio.play();
57
- channel.isPaused = false;
58
-
59
- const audioInfo: AudioInfo | null = getAudioInfoFromElement(currentAudio, channelNumber, audioChannels);
60
- if (audioInfo) {
61
- emitAudioResume(channelNumber, audioInfo, audioChannels);
62
- }
63
- }
64
- }
65
- };
66
-
67
- /**
68
- * Toggles pause/resume state for a specific channel
69
- * @param channelNumber - The channel number to toggle (defaults to 0)
70
- * @returns Promise that resolves when the toggle is complete
71
- * @example
72
- * ```typescript
73
- * await togglePauseChannel(0); // Toggle pause state for channel 0
74
- * ```
75
- */
76
- export const togglePauseChannel = async (channelNumber: number = 0): Promise<void> => {
77
- const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
78
-
79
- if (channel && channel.queue.length > 0) {
80
- const currentAudio: HTMLAudioElement = channel.queue[0];
81
-
82
- if (currentAudio.paused) {
83
- await resumeChannel(channelNumber);
84
- } else {
85
- await pauseChannel(channelNumber);
86
- }
87
- }
88
- };
89
-
90
- /**
91
- * Pauses all currently playing audio across all channels
92
- * @returns Promise that resolves when all audio is paused
93
- * @example
94
- * ```typescript
95
- * await pauseAllChannels(); // Pause everything
96
- * ```
97
- */
98
- export const pauseAllChannels = async (): Promise<void> => {
99
- const pausePromises: Promise<void>[] = [];
100
-
101
- audioChannels.forEach((_channel: ExtendedAudioQueueChannel, index: number) => {
102
- pausePromises.push(pauseChannel(index));
103
- });
104
-
105
- await Promise.all(pausePromises);
106
- };
107
-
108
- /**
109
- * Resumes all currently paused audio across all channels
110
- * @returns Promise that resolves when all audio is resumed
111
- * @example
112
- * ```typescript
113
- * await resumeAllChannels(); // Resume everything that was paused
114
- * ```
115
- */
116
- export const resumeAllChannels = async (): Promise<void> => {
117
- const resumePromises: Promise<void>[] = [];
118
-
119
- audioChannels.forEach((_channel: ExtendedAudioQueueChannel, index: number) => {
120
- resumePromises.push(resumeChannel(index));
121
- });
122
-
123
- await Promise.all(resumePromises);
124
- };
125
-
126
- /**
127
- * Checks if a specific channel is currently paused
128
- * @param channelNumber - The channel number to check (defaults to 0)
129
- * @returns True if the channel is paused, false otherwise
130
- * @example
131
- * ```typescript
132
- * const isPaused = isChannelPaused(0);
133
- * console.log(`Channel 0 is ${isPaused ? 'paused' : 'playing'}`);
134
- * ```
135
- */
136
- export const isChannelPaused = (channelNumber: number = 0): boolean => {
137
- const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
138
- return channel?.isPaused || false;
139
- };
140
-
141
- /**
142
- * Gets the pause state of all channels
143
- * @returns Array of boolean values indicating pause state for each channel
144
- * @example
145
- * ```typescript
146
- * const pauseStates = getAllChannelsPauseState();
147
- * pauseStates.forEach((isPaused, index) => {
148
- * console.log(`Channel ${index}: ${isPaused ? 'paused' : 'playing'}`);
149
- * });
150
- * ```
151
- */
152
- export const getAllChannelsPauseState = (): boolean[] => {
153
- return audioChannels.map((channel: ExtendedAudioQueueChannel) =>
154
- channel?.isPaused || false
155
- );
156
- };
157
-
158
- /**
159
- * Toggles pause/resume state for all channels globally
160
- * If any channels are currently playing, all channels will be paused
161
- * If all channels are paused, all channels will be resumed
162
- * @returns Promise that resolves when the toggle is complete
163
- * @example
164
- * ```typescript
165
- * await togglePauseAllChannels(); // Pause all if any are playing, resume all if all are paused
166
- * ```
167
- */
168
- export const togglePauseAllChannels = async (): Promise<void> => {
169
- let hasPlayingChannel: boolean = false;
170
-
171
- // Check if any channel is currently playing
172
- for (let i = 0; i < audioChannels.length; i++) {
173
- const channel: ExtendedAudioQueueChannel = audioChannels[i];
174
- if (channel && channel.queue.length > 0) {
175
- const currentAudio: HTMLAudioElement = channel.queue[0];
176
- if (!currentAudio.paused && !currentAudio.ended) {
177
- hasPlayingChannel = true;
178
- break;
179
- }
180
- }
181
- }
182
-
183
- // If any channel is playing, pause all channels
184
- // If no channels are playing, resume all channels
185
- if (hasPlayingChannel) {
186
- await pauseAllChannels();
187
- } else {
188
- await resumeAllChannels();
189
- }
1
+ /**
2
+ * @fileoverview Pause and resume management functions for the audio-channel-queue package
3
+ */
4
+
5
+ import { ExtendedAudioQueueChannel, AudioInfo } from './types';
6
+ import { audioChannels } from './info';
7
+ import { getAudioInfoFromElement } from './utils';
8
+ import { emitAudioPause, emitAudioResume } from './events';
9
+
10
+ /**
11
+ * Pauses the currently playing audio in a specific channel
12
+ * @param channelNumber - The channel number to pause (defaults to 0)
13
+ * @returns Promise that resolves when the audio is paused
14
+ * @example
15
+ * ```typescript
16
+ * await pauseChannel(0); // Pause audio in channel 0
17
+ * await pauseChannel(); // Pause audio in default channel
18
+ * ```
19
+ */
20
+ export const pauseChannel = async (channelNumber: number = 0): Promise<void> => {
21
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
22
+
23
+ if (channel && channel.queue.length > 0) {
24
+ const currentAudio: HTMLAudioElement = channel.queue[0];
25
+
26
+ if (!currentAudio.paused && !currentAudio.ended) {
27
+ currentAudio.pause();
28
+ channel.isPaused = true;
29
+
30
+ const audioInfo: AudioInfo | null = getAudioInfoFromElement(currentAudio, channelNumber, audioChannels);
31
+ if (audioInfo) {
32
+ emitAudioPause(channelNumber, audioInfo, audioChannels);
33
+ }
34
+ }
35
+ }
36
+ };
37
+
38
+ /**
39
+ * Resumes the currently paused audio in a specific channel
40
+ * @param channelNumber - The channel number to resume (defaults to 0)
41
+ * @returns Promise that resolves when the audio starts playing
42
+ * @example
43
+ * ```typescript
44
+ * await resumeChannel(0); // Resume audio in channel 0
45
+ * await resumeChannel(); // Resume audio in default channel
46
+ * ```
47
+ */
48
+ export const resumeChannel = async (channelNumber: number = 0): Promise<void> => {
49
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
50
+
51
+ if (channel && channel.queue.length > 0) {
52
+ const currentAudio: HTMLAudioElement = channel.queue[0];
53
+
54
+ // Only resume if both the channel is marked as paused AND the audio element is actually paused AND not ended
55
+ if (channel.isPaused && currentAudio.paused && !currentAudio.ended) {
56
+ await currentAudio.play();
57
+ channel.isPaused = false;
58
+
59
+ const audioInfo: AudioInfo | null = getAudioInfoFromElement(currentAudio, channelNumber, audioChannels);
60
+ if (audioInfo) {
61
+ emitAudioResume(channelNumber, audioInfo, audioChannels);
62
+ }
63
+ }
64
+ }
65
+ };
66
+
67
+ /**
68
+ * Toggles pause/resume state for a specific channel
69
+ * @param channelNumber - The channel number to toggle (defaults to 0)
70
+ * @returns Promise that resolves when the toggle is complete
71
+ * @example
72
+ * ```typescript
73
+ * await togglePauseChannel(0); // Toggle pause state for channel 0
74
+ * ```
75
+ */
76
+ export const togglePauseChannel = async (channelNumber: number = 0): Promise<void> => {
77
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
78
+
79
+ if (channel && channel.queue.length > 0) {
80
+ const currentAudio: HTMLAudioElement = channel.queue[0];
81
+
82
+ if (currentAudio.paused) {
83
+ await resumeChannel(channelNumber);
84
+ } else {
85
+ await pauseChannel(channelNumber);
86
+ }
87
+ }
88
+ };
89
+
90
+ /**
91
+ * Pauses all currently playing audio across all channels
92
+ * @returns Promise that resolves when all audio is paused
93
+ * @example
94
+ * ```typescript
95
+ * await pauseAllChannels(); // Pause everything
96
+ * ```
97
+ */
98
+ export const pauseAllChannels = async (): Promise<void> => {
99
+ const pausePromises: Promise<void>[] = [];
100
+
101
+ audioChannels.forEach((_channel: ExtendedAudioQueueChannel, index: number) => {
102
+ pausePromises.push(pauseChannel(index));
103
+ });
104
+
105
+ await Promise.all(pausePromises);
106
+ };
107
+
108
+ /**
109
+ * Resumes all currently paused audio across all channels
110
+ * @returns Promise that resolves when all audio is resumed
111
+ * @example
112
+ * ```typescript
113
+ * await resumeAllChannels(); // Resume everything that was paused
114
+ * ```
115
+ */
116
+ export const resumeAllChannels = async (): Promise<void> => {
117
+ const resumePromises: Promise<void>[] = [];
118
+
119
+ audioChannels.forEach((_channel: ExtendedAudioQueueChannel, index: number) => {
120
+ resumePromises.push(resumeChannel(index));
121
+ });
122
+
123
+ await Promise.all(resumePromises);
124
+ };
125
+
126
+ /**
127
+ * Checks if a specific channel is currently paused
128
+ * @param channelNumber - The channel number to check (defaults to 0)
129
+ * @returns True if the channel is paused, false otherwise
130
+ * @example
131
+ * ```typescript
132
+ * const isPaused = isChannelPaused(0);
133
+ * console.log(`Channel 0 is ${isPaused ? 'paused' : 'playing'}`);
134
+ * ```
135
+ */
136
+ export const isChannelPaused = (channelNumber: number = 0): boolean => {
137
+ const channel: ExtendedAudioQueueChannel = audioChannels[channelNumber];
138
+ return channel?.isPaused || false;
139
+ };
140
+
141
+ /**
142
+ * Gets the pause state of all channels
143
+ * @returns Array of boolean values indicating pause state for each channel
144
+ * @example
145
+ * ```typescript
146
+ * const pauseStates = getAllChannelsPauseState();
147
+ * pauseStates.forEach((isPaused, index) => {
148
+ * console.log(`Channel ${index}: ${isPaused ? 'paused' : 'playing'}`);
149
+ * });
150
+ * ```
151
+ */
152
+ export const getAllChannelsPauseState = (): boolean[] => {
153
+ return audioChannels.map((channel: ExtendedAudioQueueChannel) =>
154
+ channel?.isPaused || false
155
+ );
156
+ };
157
+
158
+ /**
159
+ * Toggles pause/resume state for all channels globally
160
+ * If any channels are currently playing, all channels will be paused
161
+ * If all channels are paused, all channels will be resumed
162
+ * @returns Promise that resolves when the toggle is complete
163
+ * @example
164
+ * ```typescript
165
+ * await togglePauseAllChannels(); // Pause all if any are playing, resume all if all are paused
166
+ * ```
167
+ */
168
+ export const togglePauseAllChannels = async (): Promise<void> => {
169
+ let hasPlayingChannel: boolean = false;
170
+
171
+ // Check if any channel is currently playing
172
+ for (let i = 0; i < audioChannels.length; i++) {
173
+ const channel: ExtendedAudioQueueChannel = audioChannels[i];
174
+ if (channel && channel.queue.length > 0) {
175
+ const currentAudio: HTMLAudioElement = channel.queue[0];
176
+ if (!currentAudio.paused && !currentAudio.ended) {
177
+ hasPlayingChannel = true;
178
+ break;
179
+ }
180
+ }
181
+ }
182
+
183
+ // If any channel is playing, pause all channels
184
+ // If no channels are playing, resume all channels
185
+ if (hasPlayingChannel) {
186
+ await pauseAllChannels();
187
+ } else {
188
+ await resumeAllChannels();
189
+ }
190
190
  };
package/src/types.ts CHANGED
@@ -175,25 +175,62 @@ export type AudioPauseCallback = (channelNumber: number, audioInfo: AudioInfo) =
175
175
  export type AudioResumeCallback = (channelNumber: number, audioInfo: AudioInfo) => void;
176
176
 
177
177
  /**
178
- * Extended audio queue channel with event callback management and additional features
179
- */
180
- export type ExtendedAudioQueueChannel = AudioQueueChannel & {
181
- /** Set of callbacks for audio completion events */
182
- audioCompleteCallbacks?: Set<AudioCompleteCallback>;
183
- /** Set of callbacks for audio pause events */
184
- audioPauseCallbacks?: Set<AudioPauseCallback>;
185
- /** Set of callbacks for audio resume events */
186
- audioResumeCallbacks?: Set<AudioResumeCallback>;
187
- /** Set of callbacks for audio start events */
188
- audioStartCallbacks?: Set<AudioStartCallback>;
189
- /** Whether the current audio in this channel is paused */
178
+ * Information about an audio error that occurred
179
+ */
180
+ export interface AudioErrorInfo {
181
+ channelNumber: number;
182
+ src: string;
183
+ fileName: string;
184
+ error: Error;
185
+ errorType: 'network' | 'decode' | 'unsupported' | 'permission' | 'abort' | 'timeout' | 'unknown';
186
+ timestamp: number;
187
+ retryAttempt?: number;
188
+ remainingInQueue: number;
189
+ }
190
+
191
+ /**
192
+ * Configuration for automatic retry behavior when audio fails to load or play
193
+ */
194
+ export interface RetryConfig {
195
+ enabled: boolean;
196
+ maxRetries: number;
197
+ baseDelay: number;
198
+ exponentialBackoff: boolean;
199
+ timeoutMs: number;
200
+ fallbackUrls?: string[];
201
+ skipOnFailure: boolean;
202
+ }
203
+
204
+ /**
205
+ * Configuration options for error recovery mechanisms
206
+ */
207
+ export interface ErrorRecoveryOptions {
208
+ autoRetry: boolean;
209
+ showUserFeedback: boolean;
210
+ logErrorsToAnalytics: boolean;
211
+ preserveQueueOnError: boolean;
212
+ fallbackToNextTrack: boolean;
213
+ }
214
+
215
+ /**
216
+ * Callback function type for audio error events
217
+ */
218
+ export type AudioErrorCallback = (errorInfo: AudioErrorInfo) => void;
219
+
220
+ /**
221
+ * Extended audio queue channel with error handling capabilities
222
+ */
223
+ export interface ExtendedAudioQueueChannel {
224
+ audioCompleteCallbacks: Set<AudioCompleteCallback>;
225
+ audioErrorCallbacks: Set<AudioErrorCallback>;
226
+ audioPauseCallbacks: Set<AudioPauseCallback>;
227
+ audioResumeCallbacks: Set<AudioResumeCallback>;
228
+ audioStartCallbacks: Set<AudioStartCallback>;
190
229
  isPaused?: boolean;
191
- /** Map of audio elements to their progress callback sets */
192
- progressCallbacks?: Map<HTMLAudioElement, Set<ProgressCallback>>;
193
- /** Set of callbacks for queue change events */
194
- queueChangeCallbacks?: Set<QueueChangeCallback>;
195
- /** Current volume level for this channel (0-1) */
230
+ progressCallbacks: Map<HTMLAudioElement | null, Set<ProgressCallback>>;
231
+ queue: HTMLAudioElement[];
232
+ queueChangeCallbacks: Set<QueueChangeCallback>;
233
+ retryConfig?: RetryConfig;
196
234
  volume?: number;
197
- /** Volume ducking configuration for this channel */
198
235
  volumeConfig?: VolumeConfig;
199
236
  }