streamify-audio 2.1.12 → 2.1.13

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.
@@ -27,6 +27,7 @@ await player.play(result.tracks[0]);
27
27
  - `startPosition` - Start playback at a specific position in milliseconds
28
28
  - `volume` - Set volume before playing (0-200)
29
29
  - `filters` - Apply filters before playing
30
+ - `replace` - Replace current track without adding to queue/history
30
31
 
31
32
  ```javascript
32
33
  // Start playing at 30 seconds
@@ -38,11 +39,15 @@ await player.play(track, { volume: 50 });
38
39
  // Start with filters applied
39
40
  await player.play(track, { filters: { bass: 10, nightcore: true } });
40
41
 
41
- // Combine options (useful for session restoration)
42
+ // Replace current track without queueing
43
+ await player.play(track, { replace: true });
44
+
45
+ // Combine options
42
46
  await player.play(track, {
43
47
  startPosition: savedPositionMs,
44
48
  volume: savedVolume,
45
- filters: savedFilters
49
+ filters: savedFilters,
50
+ replace: true
46
51
  });
47
52
  ```
48
53
 
@@ -120,6 +125,57 @@ console.log(player.filters); // { bass: 10, nightcore: true }
120
125
 
121
126
  See [Filters](../filters.md) for all available filters.
122
127
 
128
+ ## Effect Presets
129
+
130
+ Effect presets are pre-configured filter combinations that stack by default.
131
+
132
+ ```javascript
133
+ // Apply a single preset
134
+ await player.setEffectPresets(['bassboost']);
135
+
136
+ // Apply multiple presets (they stack)
137
+ await player.setEffectPresets(['nightcore', 'bassboost']);
138
+
139
+ // Apply with custom intensity (0.1 - 1.0)
140
+ await player.setEffectPresets([
141
+ { name: 'nightcore', intensity: 0.8 },
142
+ { name: 'bassboost', intensity: 0.5 }
143
+ ]);
144
+
145
+ // Replace all presets instead of stacking
146
+ await player.setEffectPresets(['8d'], { replace: true });
147
+
148
+ // Get active presets
149
+ const active = player.getActiveEffectPresets();
150
+ // [{ name: 'nightcore', intensity: 0.8 }, { name: 'bassboost', intensity: 0.5 }]
151
+
152
+ // Clear all effect presets
153
+ await player.clearEffectPresets();
154
+
155
+ // List available presets
156
+ const presets = player.getEffectPresets();
157
+ // ['bassboost', 'nightcore', 'vaporwave', '8d', 'karaoke', ...]
158
+ ```
159
+
160
+ **Available Presets:**
161
+ - `bassboost` - Boost bass frequencies
162
+ - `nightcore` - Speed up with higher pitch
163
+ - `vaporwave` - Slow down with lower pitch
164
+ - `8d` - 8D rotating audio effect
165
+ - `karaoke` - Reduce vocals
166
+ - `trebleboost` - Boost treble frequencies
167
+ - `deep` - Deep bass with lower pitch
168
+ - `lofi` - Lo-fi aesthetic
169
+ - `radio` - Radio/telephone effect
170
+ - `telephone` - Old telephone effect
171
+ - `soft` - Softer, quieter sound
172
+ - `loud` - Louder, compressed sound
173
+ - `chipmunk` - High-pitched voice
174
+ - `darth` - Deep Darth Vader voice
175
+ - `echo` - Echo/reverb effect
176
+ - `vibrato` - Vibrato effect
177
+ - `tremolo` - Tremolo effect
178
+
123
179
  ## Loop Modes
124
180
 
125
181
  ```javascript
package/index.d.ts CHANGED
@@ -163,6 +163,8 @@ declare module 'streamify-audio' {
163
163
  static Manager: typeof Manager;
164
164
  static Player: typeof Player;
165
165
  static Queue: typeof Queue;
166
+ static getEffectPresetsInfo(): EffectPresetInfo[];
167
+ static EFFECT_PRESETS: Record<string, { filters: Filters; description: string }>;
166
168
  }
167
169
 
168
170
  // ========================================================================
@@ -242,6 +244,22 @@ declare module 'streamify-audio' {
242
244
  seek?: number;
243
245
  volume?: number;
244
246
  filters?: Filters;
247
+ replace?: boolean;
248
+ }
249
+
250
+ export interface EffectPreset {
251
+ name: string;
252
+ intensity?: number;
253
+ }
254
+
255
+ export interface EffectPresetInfo {
256
+ name: string;
257
+ description: string;
258
+ filters: string[];
259
+ }
260
+
261
+ export interface SetEffectPresetsOptions {
262
+ replace?: boolean;
245
263
  }
246
264
 
247
265
  export class Player extends EventEmitter {
@@ -288,6 +306,11 @@ declare module 'streamify-audio' {
288
306
  clearEQ(): Promise<boolean>;
289
307
  getPresets(): string[];
290
308
 
309
+ setEffectPresets(presets: (string | EffectPreset)[], options?: SetEffectPresetsOptions): Promise<boolean>;
310
+ getActiveEffectPresets(): EffectPreset[];
311
+ clearEffectPresets(): Promise<boolean>;
312
+ getEffectPresets(): string[];
313
+
291
314
  toJSON(): any;
292
315
 
293
316
  on(event: 'trackStart', listener: (track: Track) => void): this;
package/index.js CHANGED
@@ -249,4 +249,8 @@ Streamify.Manager = Manager;
249
249
  Streamify.Player = Manager ? require('./src/discord/Player') : null;
250
250
  Streamify.Queue = Manager ? require('./src/discord/Queue') : null;
251
251
 
252
+ const { getEffectPresetsInfo, EFFECT_PRESETS } = require('./src/filters/ffmpeg');
253
+ Streamify.getEffectPresetsInfo = getEffectPresetsInfo;
254
+ Streamify.EFFECT_PRESETS = EFFECT_PRESETS;
255
+
252
256
  module.exports = Streamify;
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "streamify-audio",
3
- "version": "2.1.12",
3
+ "version": "2.1.13",
4
4
  "description": "Dual-mode audio library: HTTP streaming proxy + Discord player (Lavalink alternative). Supports YouTube, Spotify, SoundCloud with audio filters.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
7
7
  "bin": {
8
- "streamify": "./bin/streamify.js"
8
+ "streamify": "bin/streamify.js"
9
9
  },
10
10
  "scripts": {
11
11
  "start": "node index.js",
@@ -48,7 +48,7 @@
48
48
  },
49
49
  "repository": {
50
50
  "type": "git",
51
- "url": "https://github.com/LucasCzechia/streamify.git"
51
+ "url": "git+https://github.com/LucasCzechia/streamify.git"
52
52
  },
53
53
  "homepage": "https://github.com/LucasCzechia/streamify#readme",
54
54
  "bugs": {
@@ -1,6 +1,7 @@
1
1
  const { EventEmitter } = require('events');
2
2
  const Queue = require('./Queue');
3
3
  const { createStream } = require('./Stream');
4
+ const { applyEffectPreset, EFFECT_PRESETS } = require('../filters/ffmpeg');
4
5
  const log = require('../utils/logger');
5
6
 
6
7
  let voiceModule;
@@ -35,6 +36,7 @@ class Player extends EventEmitter {
35
36
 
36
37
  this._volume = options.volume || manager.config.defaultVolume || 80;
37
38
  this._filters = {};
39
+ this._effectPresets = [];
38
40
  this._playing = false;
39
41
  this._paused = false;
40
42
  this._positionTimestamp = 0;
@@ -264,7 +266,8 @@ class Player extends EventEmitter {
264
266
  const playOptions = {
265
267
  startPosition: options.startPosition || options.seek || 0,
266
268
  volume: options.volume,
267
- filters: options.filters
269
+ filters: options.filters,
270
+ replace: options.replace || false
268
271
  };
269
272
 
270
273
  if (options.volume !== undefined) {
@@ -275,11 +278,16 @@ class Player extends EventEmitter {
275
278
  this._filters = { ...this._filters, ...options.filters };
276
279
  }
277
280
 
278
- if (this.queue.current) {
281
+ if (this.queue.current && !playOptions.replace) {
279
282
  this.queue.add(track, 0);
280
283
  return this.skip();
281
284
  }
282
285
 
286
+ if (playOptions.replace && this.stream) {
287
+ this.stream.destroy();
288
+ this.stream = null;
289
+ }
290
+
283
291
  this.queue.setCurrent(track);
284
292
  return this._playTrack(track, playOptions.startPosition);
285
293
  }
@@ -616,6 +624,70 @@ class Player extends EventEmitter {
616
624
  return Object.keys(PRESETS);
617
625
  }
618
626
 
627
+ async setEffectPresets(presets, options = {}) {
628
+ if (!Array.isArray(presets)) {
629
+ presets = [presets];
630
+ }
631
+
632
+ const replace = options.replace ?? false;
633
+ const appliedFilters = {};
634
+ const newPresets = [];
635
+
636
+ for (const preset of presets) {
637
+ const name = typeof preset === 'string' ? preset : preset.name;
638
+ const intensity = typeof preset === 'object' ? (preset.intensity ?? 1.0) : 1.0;
639
+
640
+ if (!EFFECT_PRESETS[name]) {
641
+ log.warn('PLAYER', `Unknown effect preset: ${name}`);
642
+ continue;
643
+ }
644
+
645
+ const filters = applyEffectPreset(name, intensity);
646
+ if (filters) {
647
+ Object.assign(appliedFilters, filters);
648
+ newPresets.push({ name, intensity });
649
+ }
650
+ }
651
+
652
+ if (replace) {
653
+ this._effectPresets = newPresets;
654
+ this._filters = appliedFilters;
655
+ } else {
656
+ const existingNames = this._effectPresets.map(p => p.name);
657
+ for (const preset of newPresets) {
658
+ const idx = existingNames.indexOf(preset.name);
659
+ if (idx >= 0) {
660
+ this._effectPresets[idx] = preset;
661
+ } else {
662
+ this._effectPresets.push(preset);
663
+ }
664
+ }
665
+ this._filters = { ...this._filters, ...appliedFilters };
666
+ }
667
+
668
+ if (this._playing && this.queue.current) {
669
+ return this.setFilter('_trigger', null);
670
+ }
671
+ return true;
672
+ }
673
+
674
+ getActiveEffectPresets() {
675
+ return [...this._effectPresets];
676
+ }
677
+
678
+ async clearEffectPresets() {
679
+ this._effectPresets = [];
680
+ this._filters = {};
681
+ if (this._playing && this.queue.current) {
682
+ return this.setFilter('_trigger', null);
683
+ }
684
+ return true;
685
+ }
686
+
687
+ getEffectPresets() {
688
+ return Object.keys(EFFECT_PRESETS);
689
+ }
690
+
619
691
  disconnect() {
620
692
  if (this.connection) {
621
693
  this.connection.destroy();
@@ -18,6 +18,26 @@ const PRESETS = {
18
18
  treble_heavy: [0, 0, 0, 0, 0, 0, 0, 0, 0.1, 0.2, 0.3, 0.4, 0.45, 0.5, 0.5]
19
19
  };
20
20
 
21
+ const EFFECT_PRESETS = {
22
+ bassboost: { filters: { bass: 10 }, description: 'Boost bass frequencies' },
23
+ nightcore: { filters: { speed: 1.25, pitch: 1.25 }, description: 'Speed up with higher pitch' },
24
+ vaporwave: { filters: { speed: 0.8, pitch: 0.8 }, description: 'Slow down with lower pitch' },
25
+ '8d': { filters: { rotation: { speed: 0.2 } }, description: '8D rotating audio effect' },
26
+ karaoke: { filters: { karaoke: true }, description: 'Reduce vocals' },
27
+ trebleboost: { filters: { treble: 10 }, description: 'Boost treble frequencies' },
28
+ deep: { filters: { bass: 15, pitch: 0.9 }, description: 'Deep bass with lower pitch' },
29
+ lofi: { filters: { lowpass: 3000, bass: 5 }, description: 'Lo-fi aesthetic' },
30
+ radio: { filters: { highpass: 300, lowpass: 5000 }, description: 'Radio/telephone effect' },
31
+ telephone: { filters: { highpass: 500, lowpass: 3500 }, description: 'Old telephone effect' },
32
+ soft: { filters: { bass: -5, treble: -3, volume: 70 }, description: 'Softer, quieter sound' },
33
+ loud: { filters: { bass: 5, treble: 3, compressor: true }, description: 'Louder, compressed sound' },
34
+ chipmunk: { filters: { pitch: 1.5 }, description: 'High-pitched chipmunk voice' },
35
+ darth: { filters: { pitch: 0.7 }, description: 'Deep Darth Vader voice' },
36
+ echo: { filters: { echo: true }, description: 'Echo/reverb effect' },
37
+ vibrato: { filters: { vibrato: { frequency: 5, depth: 0.5 } }, description: 'Vibrato effect' },
38
+ tremolo: { filters: { tremolo: { frequency: 5, depth: 0.5 } }, description: 'Tremolo effect' }
39
+ };
40
+
21
41
  function buildEqualizer(bands) {
22
42
  if (!bands || !Array.isArray(bands)) return null;
23
43
 
@@ -267,4 +287,49 @@ function getEQBands() {
267
287
  return EQ_BANDS;
268
288
  }
269
289
 
270
- module.exports = { buildFfmpegArgs, getAvailableFilters, getPresets, getEQBands, PRESETS, EQ_BANDS };
290
+ function getEffectPresets() {
291
+ return EFFECT_PRESETS;
292
+ }
293
+
294
+ function getEffectPresetsInfo() {
295
+ return Object.entries(EFFECT_PRESETS).map(([name, data]) => ({
296
+ name,
297
+ description: data.description,
298
+ filters: Object.keys(data.filters)
299
+ }));
300
+ }
301
+
302
+ function applyEffectPreset(name, intensity = 1.0) {
303
+ const preset = EFFECT_PRESETS[name];
304
+ if (!preset) return null;
305
+
306
+ const filters = {};
307
+ for (const [key, value] of Object.entries(preset.filters)) {
308
+ if (typeof value === 'number') {
309
+ filters[key] = value * intensity;
310
+ } else if (typeof value === 'boolean') {
311
+ filters[key] = value;
312
+ } else if (typeof value === 'object') {
313
+ filters[key] = { ...value };
314
+ for (const [k, v] of Object.entries(filters[key])) {
315
+ if (typeof v === 'number') {
316
+ filters[key][k] = v * intensity;
317
+ }
318
+ }
319
+ }
320
+ }
321
+ return filters;
322
+ }
323
+
324
+ module.exports = {
325
+ buildFfmpegArgs,
326
+ getAvailableFilters,
327
+ getPresets,
328
+ getEQBands,
329
+ getEffectPresets,
330
+ getEffectPresetsInfo,
331
+ applyEffectPreset,
332
+ PRESETS,
333
+ EQ_BANDS,
334
+ EFFECT_PRESETS
335
+ };