@students-dev/audify-js 1.0.0 → 1.0.2

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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +93 -310
  3. package/dist/AudioEngine.js +232 -0
  4. package/dist/cjs/index.js +1497 -1392
  5. package/dist/cjs/index.js.map +1 -1
  6. package/dist/constants/index.js +35 -0
  7. package/dist/engine/Filters.js +137 -0
  8. package/dist/engine/MockAudioContext.js +53 -0
  9. package/dist/engine/Player.js +209 -0
  10. package/dist/esm/index.js +1490 -1389
  11. package/dist/esm/index.js.map +1 -1
  12. package/dist/events/EventBus.js +61 -0
  13. package/dist/index.js +18 -0
  14. package/dist/interfaces/index.js +1 -0
  15. package/dist/plugins/Plugin.js +27 -0
  16. package/dist/plugins/PluginManager.js +106 -0
  17. package/dist/providers/LavalinkProvider.js +81 -0
  18. package/dist/providers/LocalProvider.js +70 -0
  19. package/dist/providers/ProviderRegistry.js +20 -0
  20. package/dist/providers/SpotifyProvider.js +59 -0
  21. package/dist/providers/YouTubeProvider.js +48 -0
  22. package/dist/queue/Queue.js +186 -0
  23. package/dist/queue/Track.js +54 -0
  24. package/dist/types/AudioEngine.d.ts +107 -0
  25. package/dist/types/constants/index.d.ts +39 -0
  26. package/dist/types/engine/AudioEngine.d.ts +44 -1
  27. package/dist/types/engine/Filters.d.ts +25 -24
  28. package/dist/types/engine/MockAudioContext.d.ts +43 -0
  29. package/dist/types/engine/Player.d.ts +25 -21
  30. package/dist/types/events/EventBus.d.ts +17 -15
  31. package/dist/types/index.d.ts +17 -13
  32. package/dist/types/interfaces/index.d.ts +31 -0
  33. package/dist/types/plugins/Plugin.d.ts +11 -43
  34. package/dist/types/plugins/PluginManager.d.ts +19 -19
  35. package/dist/types/providers/LavalinkProvider.d.ts +17 -0
  36. package/dist/types/providers/LocalProvider.d.ts +11 -22
  37. package/dist/types/providers/ProviderRegistry.d.ts +10 -0
  38. package/dist/types/providers/SpotifyProvider.d.ts +14 -0
  39. package/dist/types/providers/YouTubeProvider.d.ts +11 -28
  40. package/dist/types/queue/Queue.d.ts +28 -22
  41. package/dist/types/queue/Track.d.ts +18 -16
  42. package/dist/types/utils/Logger.d.ts +12 -16
  43. package/dist/types/utils/Metadata.d.ts +16 -15
  44. package/dist/types/utils/Probe.d.ts +7 -7
  45. package/dist/types/utils/Time.d.ts +9 -9
  46. package/dist/utils/Logger.js +59 -0
  47. package/dist/utils/Metadata.js +90 -0
  48. package/dist/utils/Probe.js +59 -0
  49. package/dist/utils/Time.js +54 -0
  50. package/package.json +19 -9
@@ -0,0 +1,209 @@
1
+ import { LOOP_MODES, EVENTS } from '../constants';
2
+ import { EventBus } from '../events/EventBus';
3
+ import { MockAudioContext } from './MockAudioContext';
4
+ /**
5
+ * Audio player with playback controls
6
+ */
7
+ export class Player {
8
+ constructor(audioEngine) {
9
+ this.audioEngine = audioEngine;
10
+ let AudioContextClass;
11
+ if (typeof window !== 'undefined') {
12
+ AudioContextClass = window.AudioContext || window.webkitAudioContext;
13
+ }
14
+ else {
15
+ AudioContextClass = global.AudioContext;
16
+ }
17
+ if (AudioContextClass) {
18
+ this.audioContext = new AudioContextClass();
19
+ }
20
+ else {
21
+ this.audioContext = new MockAudioContext();
22
+ }
23
+ this.source = null;
24
+ this.isPlaying = false;
25
+ this.currentTime = 0;
26
+ this.duration = 0;
27
+ this.volume = 1;
28
+ this.loopMode = LOOP_MODES.OFF;
29
+ this.eventBus = new EventBus();
30
+ }
31
+ /**
32
+ * Play audio track
33
+ * @param track - Track to play
34
+ */
35
+ async play(track) {
36
+ if (!track)
37
+ return;
38
+ // Reset state
39
+ this.stop();
40
+ try {
41
+ this.eventBus.emit(EVENTS.PLAY, track);
42
+ // Check providers via registry
43
+ const provider = this.audioEngine.getProvider(track.source || 'local');
44
+ if (provider) {
45
+ await provider.play(track);
46
+ }
47
+ else {
48
+ // Fallback to direct URL playback if no specific provider found
49
+ await this.playStream(track);
50
+ }
51
+ this.eventBus.emit(EVENTS.TRACK_START, track);
52
+ }
53
+ catch (error) {
54
+ console.error(error);
55
+ this.eventBus.emit(EVENTS.ERROR, error);
56
+ }
57
+ }
58
+ /**
59
+ * Play audio from URL/Stream directly
60
+ * This is called by Providers or as fallback
61
+ * @param track - Track object with URL
62
+ */
63
+ async playStream(track) {
64
+ if (!this.audioContext)
65
+ throw new Error('AudioContext not available');
66
+ // If already playing, stop
67
+ if (this.source) {
68
+ this.source.stop();
69
+ }
70
+ try {
71
+ // Fetch audio data
72
+ // For Node.js (Mock), we might fail to fetch if it's a real URL
73
+ // If MockAudioContext is used, we probably want to skip fetch?
74
+ // Or Mock fetch?
75
+ // In Node environment, fetch is global in recent versions (v18+)
76
+ // But if we are mocking, we can't really "decode" the buffer from a remote stream easily without logic.
77
+ // My MockAudioContext.decodeAudioData returns a mock buffer.
78
+ let audioBuffer;
79
+ // Check if real fetch is feasible
80
+ if (this.audioContext instanceof MockAudioContext) {
81
+ // Mock fetch behavior if needed or just create dummy buffer
82
+ audioBuffer = await this.audioContext.decodeAudioData(new ArrayBuffer(0));
83
+ }
84
+ else {
85
+ const response = await fetch(track.url);
86
+ const arrayBuffer = await response.arrayBuffer();
87
+ audioBuffer = await this.audioContext.decodeAudioData(arrayBuffer);
88
+ }
89
+ this.source = this.audioContext.createBufferSource();
90
+ this.source.buffer = audioBuffer;
91
+ this.duration = audioBuffer.duration;
92
+ // Connect through filters
93
+ this.audioEngine.filters.connect(this.source, this.audioContext.destination);
94
+ // Handle end of track
95
+ this.source.onended = () => {
96
+ this.isPlaying = false;
97
+ this.eventBus.emit(EVENTS.TRACK_END, track);
98
+ this.handleTrackEnd();
99
+ };
100
+ this.source.start(0);
101
+ this.isPlaying = true;
102
+ }
103
+ catch (error) {
104
+ throw new Error(`Failed to play stream: ${error}`);
105
+ }
106
+ }
107
+ /**
108
+ * Pause playback
109
+ */
110
+ pause() {
111
+ if (this.audioContext.state === 'running') {
112
+ this.audioContext.suspend();
113
+ this.isPlaying = false;
114
+ this.eventBus.emit(EVENTS.PAUSE);
115
+ }
116
+ }
117
+ /**
118
+ * Resume playback
119
+ */
120
+ resume() {
121
+ if (this.audioContext.state === 'suspended') {
122
+ this.audioContext.resume();
123
+ this.isPlaying = true;
124
+ this.eventBus.emit(EVENTS.PLAY);
125
+ }
126
+ }
127
+ /**
128
+ * Stop playback
129
+ */
130
+ stop() {
131
+ if (this.source) {
132
+ try {
133
+ this.source.stop();
134
+ }
135
+ catch (e) {
136
+ // Ignore if already stopped
137
+ }
138
+ this.source = null;
139
+ }
140
+ this.isPlaying = false;
141
+ this.currentTime = 0;
142
+ this.eventBus.emit(EVENTS.STOP);
143
+ }
144
+ /**
145
+ * Seek to position
146
+ * @param time - Time in seconds
147
+ */
148
+ seek(time) {
149
+ if (this.source && this.isPlaying) {
150
+ // TODO: Implement proper seek
151
+ console.warn('Seek not fully implemented for Web Audio BufferSource');
152
+ }
153
+ this.currentTime = Math.max(0, Math.min(time, this.duration));
154
+ }
155
+ /**
156
+ * Set volume
157
+ * @param volume - Volume level (0-1)
158
+ */
159
+ setVolume(volume) {
160
+ this.volume = Math.max(0, Math.min(1, volume));
161
+ }
162
+ /**
163
+ * Set loop mode
164
+ * @param mode - Loop mode
165
+ */
166
+ setLoopMode(mode) {
167
+ this.loopMode = mode;
168
+ }
169
+ /**
170
+ * Handle track end based on loop mode
171
+ */
172
+ handleTrackEnd() {
173
+ if (this.loopMode === LOOP_MODES.TRACK) {
174
+ // Replay current track
175
+ const current = this.audioEngine.queue.getCurrent();
176
+ if (current)
177
+ this.play(current);
178
+ }
179
+ else if (this.loopMode === LOOP_MODES.QUEUE) {
180
+ // Play next in queue
181
+ const next = this.audioEngine.queue.next(true);
182
+ if (next)
183
+ this.play(next);
184
+ }
185
+ else {
186
+ // Loop OFF: Play next or stop
187
+ const next = this.audioEngine.queue.next(false);
188
+ if (next) {
189
+ this.play(next);
190
+ }
191
+ else {
192
+ this.stop();
193
+ }
194
+ }
195
+ }
196
+ /**
197
+ * Get current playback state
198
+ * @returns State object
199
+ */
200
+ getState() {
201
+ return {
202
+ isPlaying: this.isPlaying,
203
+ currentTime: this.audioContext.currentTime,
204
+ duration: this.duration,
205
+ volume: this.volume,
206
+ loopMode: this.loopMode
207
+ };
208
+ }
209
+ }