@fluxerjs/voice 1.3.0 → 1.3.1

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/index.d.mts CHANGED
@@ -42,12 +42,17 @@ declare class VoiceConnection extends EventEmitter {
42
42
  private setupUDP;
43
43
  /**
44
44
  * Play a stream of raw Opus packets
45
- * Uses the same queue and 20ms pacing as play(). Use this for local files (MP3 → PCM → Opus) or other Opus sources.
45
+ * Uses the same queue and 20ms pacing as play(). Use this for raw Opus packet streams (e.g. MP3 → PCM → Opus pipelines) when you are not using WebM/Opus.
46
46
  */
47
47
  playOpus(stream: NodeJS.ReadableStream): void;
48
48
  /**
49
- * Play a direct WebM/Opus URL or stream. Fetches the URL (if string), demuxes with prism-media WebmDemuxer,
50
- * and sends Opus packets to the voice connection. No FFmpeg or encoding; input must be WebM with Opus.
49
+ * Play a direct WebM/Opus source. When `urlOrStream` is a string:
50
+ * - `http://` / `https://` fetched over the network
51
+ * - `file://` — read from the local filesystem
52
+ * - any other string — treated as a filesystem path (e.g. `./music/track.webm`)
53
+ *
54
+ * You can also pass a Node.js Readable stream. Demuxes with prism-media WebmDemuxer; no FFmpeg;
55
+ * input must be WebM with Opus.
51
56
  */
52
57
  play(urlOrStream: string | NodeJS.ReadableStream): Promise<void>;
53
58
  private sendAudioFrame;
@@ -222,10 +227,13 @@ declare class LiveKitRtcConnection extends EventEmitter {
222
227
  */
223
228
  private playVideoFFmpeg;
224
229
  /**
225
- * Play audio from a WebM/Opus URL or readable stream. Publishes to the LiveKit room as an audio track.
230
+ * Play audio from a WebM/Opus source. Publishes to the LiveKit room as an audio track.
231
+ *
232
+ * When `urlOrStream` is a string: `http(s)://` is fetched; `file://` or any other string is read
233
+ * from the local filesystem (path). Otherwise pass a Node.js ReadableStream.
226
234
  *
227
- * @param urlOrStream - Audio source: HTTP(S) URL to a WebM/Opus file, or a Node.js ReadableStream
228
- * @emits error - On fetch failure or decode errors
235
+ * @param urlOrStream - WebM/Opus HTTP(S) URL, `file://` URL, filesystem path, or ReadableStream
236
+ * @emits error - On fetch/read failure or decode errors
229
237
  */
230
238
  play(urlOrStream: string | NodeJS.ReadableStream): Promise<void>;
231
239
  /**
package/dist/index.d.ts CHANGED
@@ -42,12 +42,17 @@ declare class VoiceConnection extends EventEmitter {
42
42
  private setupUDP;
43
43
  /**
44
44
  * Play a stream of raw Opus packets
45
- * Uses the same queue and 20ms pacing as play(). Use this for local files (MP3 → PCM → Opus) or other Opus sources.
45
+ * Uses the same queue and 20ms pacing as play(). Use this for raw Opus packet streams (e.g. MP3 → PCM → Opus pipelines) when you are not using WebM/Opus.
46
46
  */
47
47
  playOpus(stream: NodeJS.ReadableStream): void;
48
48
  /**
49
- * Play a direct WebM/Opus URL or stream. Fetches the URL (if string), demuxes with prism-media WebmDemuxer,
50
- * and sends Opus packets to the voice connection. No FFmpeg or encoding; input must be WebM with Opus.
49
+ * Play a direct WebM/Opus source. When `urlOrStream` is a string:
50
+ * - `http://` / `https://` fetched over the network
51
+ * - `file://` — read from the local filesystem
52
+ * - any other string — treated as a filesystem path (e.g. `./music/track.webm`)
53
+ *
54
+ * You can also pass a Node.js Readable stream. Demuxes with prism-media WebmDemuxer; no FFmpeg;
55
+ * input must be WebM with Opus.
51
56
  */
52
57
  play(urlOrStream: string | NodeJS.ReadableStream): Promise<void>;
53
58
  private sendAudioFrame;
@@ -222,10 +227,13 @@ declare class LiveKitRtcConnection extends EventEmitter {
222
227
  */
223
228
  private playVideoFFmpeg;
224
229
  /**
225
- * Play audio from a WebM/Opus URL or readable stream. Publishes to the LiveKit room as an audio track.
230
+ * Play audio from a WebM/Opus source. Publishes to the LiveKit room as an audio track.
231
+ *
232
+ * When `urlOrStream` is a string: `http(s)://` is fetched; `file://` or any other string is read
233
+ * from the local filesystem (path). Otherwise pass a Node.js ReadableStream.
226
234
  *
227
- * @param urlOrStream - Audio source: HTTP(S) URL to a WebM/Opus file, or a Node.js ReadableStream
228
- * @emits error - On fetch failure or decode errors
235
+ * @param urlOrStream - WebM/Opus HTTP(S) URL, `file://` URL, filesystem path, or ReadableStream
236
+ * @emits error - On fetch/read failure or decode errors
229
237
  */
230
238
  play(urlOrStream: string | NodeJS.ReadableStream): Promise<void>;
231
239
  /**
package/dist/index.js CHANGED
@@ -53,7 +53,9 @@ var import_util = require("@fluxerjs/util");
53
53
  var nacl = __toESM(require("tweetnacl"));
54
54
  var dgram = __toESM(require("dgram"));
55
55
  var ws = __toESM(require("ws"));
56
+ var import_node_fs = require("fs");
56
57
  var import_node_stream = require("stream");
58
+ var import_node_url = require("url");
57
59
  var import_prism_media = require("prism-media");
58
60
  var VOICE_WS_OPCODES = {
59
61
  Identify: 0,
@@ -257,7 +259,7 @@ var VoiceConnection = class extends import_events.EventEmitter {
257
259
  }
258
260
  /**
259
261
  * Play a stream of raw Opus packets
260
- * Uses the same queue and 20ms pacing as play(). Use this for local files (MP3 → PCM → Opus) or other Opus sources.
262
+ * Uses the same queue and 20ms pacing as play(). Use this for raw Opus packet streams (e.g. MP3 → PCM → Opus pipelines) when you are not using WebM/Opus.
261
263
  */
262
264
  playOpus(stream) {
263
265
  this.stop();
@@ -293,22 +295,35 @@ var VoiceConnection = class extends import_events.EventEmitter {
293
295
  });
294
296
  }
295
297
  /**
296
- * Play a direct WebM/Opus URL or stream. Fetches the URL (if string), demuxes with prism-media WebmDemuxer,
297
- * and sends Opus packets to the voice connection. No FFmpeg or encoding; input must be WebM with Opus.
298
+ * Play a direct WebM/Opus source. When `urlOrStream` is a string:
299
+ * - `http://` / `https://` fetched over the network
300
+ * - `file://` — read from the local filesystem
301
+ * - any other string — treated as a filesystem path (e.g. `./music/track.webm`)
302
+ *
303
+ * You can also pass a Node.js Readable stream. Demuxes with prism-media WebmDemuxer; no FFmpeg;
304
+ * input must be WebM with Opus.
298
305
  */
299
306
  async play(urlOrStream) {
300
307
  this.stop();
301
308
  let inputStream;
302
309
  if (typeof urlOrStream === "string") {
303
310
  try {
304
- const response = await fetch(urlOrStream);
305
- if (!response.ok) {
306
- throw new import_util.FluxerError(`HTTP ${response.status}`, { code: import_util.ErrorCodes.VoiceHttpError });
307
- }
308
- if (!response.body) {
309
- throw new import_util.FluxerError("No response body", { code: import_util.ErrorCodes.VoiceNoResponseBody });
311
+ const spec = urlOrStream.trim();
312
+ const lower = spec.toLowerCase();
313
+ if (lower.startsWith("http://") || lower.startsWith("https://")) {
314
+ const response = await fetch(spec);
315
+ if (!response.ok) {
316
+ throw new import_util.FluxerError(`HTTP ${response.status}`, { code: import_util.ErrorCodes.VoiceHttpError });
317
+ }
318
+ if (!response.body) {
319
+ throw new import_util.FluxerError("No response body", { code: import_util.ErrorCodes.VoiceNoResponseBody });
320
+ }
321
+ inputStream = import_node_stream.Readable.fromWeb(response.body);
322
+ } else if (lower.startsWith("file:")) {
323
+ inputStream = (0, import_node_fs.createReadStream)((0, import_node_url.fileURLToPath)(new URL(spec)));
324
+ } else {
325
+ inputStream = (0, import_node_fs.createReadStream)(spec);
310
326
  }
311
- inputStream = import_node_stream.Readable.fromWeb(response.body);
312
327
  } catch (e) {
313
328
  const err = e instanceof import_util.FluxerError ? e : new import_util.FluxerError(e instanceof Error ? e.message : String(e), {
314
329
  code: import_util.ErrorCodes.FileFetchFailed,
@@ -504,7 +519,9 @@ function concatUint8Arrays(a, b) {
504
519
  }
505
520
 
506
521
  // src/LiveKitRtcConnection.ts
522
+ var import_node_fs2 = require("fs");
507
523
  var import_node_stream2 = require("stream");
524
+ var import_node_url2 = require("url");
508
525
  var import_opus_decoder = require("opus-decoder");
509
526
  var import_prism_media2 = require("prism-media");
510
527
  var import_node_util = require("util");
@@ -1698,10 +1715,13 @@ var LiveKitRtcConnection = class extends import_events2.EventEmitter {
1698
1715
  runFFmpeg().catch((e) => this.audioDebug("ffmpeg error", { error: String(e) }));
1699
1716
  }
1700
1717
  /**
1701
- * Play audio from a WebM/Opus URL or readable stream. Publishes to the LiveKit room as an audio track.
1718
+ * Play audio from a WebM/Opus source. Publishes to the LiveKit room as an audio track.
1719
+ *
1720
+ * When `urlOrStream` is a string: `http(s)://` is fetched; `file://` or any other string is read
1721
+ * from the local filesystem (path). Otherwise pass a Node.js ReadableStream.
1702
1722
  *
1703
- * @param urlOrStream - Audio source: HTTP(S) URL to a WebM/Opus file, or a Node.js ReadableStream
1704
- * @emits error - On fetch failure or decode errors
1723
+ * @param urlOrStream - WebM/Opus HTTP(S) URL, `file://` URL, filesystem path, or ReadableStream
1724
+ * @emits error - On fetch/read failure or decode errors
1705
1725
  */
1706
1726
  async play(urlOrStream) {
1707
1727
  this.stop();
@@ -1712,14 +1732,22 @@ var LiveKitRtcConnection = class extends import_events2.EventEmitter {
1712
1732
  let inputStream;
1713
1733
  if (typeof urlOrStream === "string") {
1714
1734
  try {
1715
- const response = await fetch(urlOrStream);
1716
- if (!response.ok) {
1717
- throw new import_util2.FluxerError(`HTTP ${response.status}`, { code: import_util2.ErrorCodes.VoiceHttpError });
1718
- }
1719
- if (!response.body) {
1720
- throw new import_util2.FluxerError("No response body", { code: import_util2.ErrorCodes.VoiceNoResponseBody });
1735
+ const spec = urlOrStream.trim();
1736
+ const lower = spec.toLowerCase();
1737
+ if (lower.startsWith("http://") || lower.startsWith("https://")) {
1738
+ const response = await fetch(spec);
1739
+ if (!response.ok) {
1740
+ throw new import_util2.FluxerError(`HTTP ${response.status}`, { code: import_util2.ErrorCodes.VoiceHttpError });
1741
+ }
1742
+ if (!response.body) {
1743
+ throw new import_util2.FluxerError("No response body", { code: import_util2.ErrorCodes.VoiceNoResponseBody });
1744
+ }
1745
+ inputStream = import_node_stream2.Readable.fromWeb(response.body);
1746
+ } else if (lower.startsWith("file:")) {
1747
+ inputStream = (0, import_node_fs2.createReadStream)((0, import_node_url2.fileURLToPath)(new URL(spec)));
1748
+ } else {
1749
+ inputStream = (0, import_node_fs2.createReadStream)(spec);
1721
1750
  }
1722
- inputStream = import_node_stream2.Readable.fromWeb(response.body);
1723
1751
  } catch (e) {
1724
1752
  const err = e instanceof import_util2.FluxerError ? e : new import_util2.FluxerError(e instanceof Error ? e.message : String(e), {
1725
1753
  code: import_util2.ErrorCodes.FileFetchFailed,
package/dist/index.mjs CHANGED
@@ -13,7 +13,9 @@ import { ErrorCodes, FluxerError } from "@fluxerjs/util";
13
13
  import * as nacl from "tweetnacl";
14
14
  import * as dgram from "dgram";
15
15
  import * as ws from "ws";
16
+ import { createReadStream } from "fs";
16
17
  import { Readable } from "stream";
18
+ import { fileURLToPath } from "url";
17
19
  import { opus } from "prism-media";
18
20
  var VOICE_WS_OPCODES = {
19
21
  Identify: 0,
@@ -217,7 +219,7 @@ var VoiceConnection = class extends EventEmitter {
217
219
  }
218
220
  /**
219
221
  * Play a stream of raw Opus packets
220
- * Uses the same queue and 20ms pacing as play(). Use this for local files (MP3 → PCM → Opus) or other Opus sources.
222
+ * Uses the same queue and 20ms pacing as play(). Use this for raw Opus packet streams (e.g. MP3 → PCM → Opus pipelines) when you are not using WebM/Opus.
221
223
  */
222
224
  playOpus(stream) {
223
225
  this.stop();
@@ -253,22 +255,35 @@ var VoiceConnection = class extends EventEmitter {
253
255
  });
254
256
  }
255
257
  /**
256
- * Play a direct WebM/Opus URL or stream. Fetches the URL (if string), demuxes with prism-media WebmDemuxer,
257
- * and sends Opus packets to the voice connection. No FFmpeg or encoding; input must be WebM with Opus.
258
+ * Play a direct WebM/Opus source. When `urlOrStream` is a string:
259
+ * - `http://` / `https://` fetched over the network
260
+ * - `file://` — read from the local filesystem
261
+ * - any other string — treated as a filesystem path (e.g. `./music/track.webm`)
262
+ *
263
+ * You can also pass a Node.js Readable stream. Demuxes with prism-media WebmDemuxer; no FFmpeg;
264
+ * input must be WebM with Opus.
258
265
  */
259
266
  async play(urlOrStream) {
260
267
  this.stop();
261
268
  let inputStream;
262
269
  if (typeof urlOrStream === "string") {
263
270
  try {
264
- const response = await fetch(urlOrStream);
265
- if (!response.ok) {
266
- throw new FluxerError(`HTTP ${response.status}`, { code: ErrorCodes.VoiceHttpError });
267
- }
268
- if (!response.body) {
269
- throw new FluxerError("No response body", { code: ErrorCodes.VoiceNoResponseBody });
271
+ const spec = urlOrStream.trim();
272
+ const lower = spec.toLowerCase();
273
+ if (lower.startsWith("http://") || lower.startsWith("https://")) {
274
+ const response = await fetch(spec);
275
+ if (!response.ok) {
276
+ throw new FluxerError(`HTTP ${response.status}`, { code: ErrorCodes.VoiceHttpError });
277
+ }
278
+ if (!response.body) {
279
+ throw new FluxerError("No response body", { code: ErrorCodes.VoiceNoResponseBody });
280
+ }
281
+ inputStream = Readable.fromWeb(response.body);
282
+ } else if (lower.startsWith("file:")) {
283
+ inputStream = createReadStream(fileURLToPath(new URL(spec)));
284
+ } else {
285
+ inputStream = createReadStream(spec);
270
286
  }
271
- inputStream = Readable.fromWeb(response.body);
272
287
  } catch (e) {
273
288
  const err = e instanceof FluxerError ? e : new FluxerError(e instanceof Error ? e.message : String(e), {
274
289
  code: ErrorCodes.FileFetchFailed,
@@ -478,7 +493,9 @@ function concatUint8Arrays(a, b) {
478
493
  }
479
494
 
480
495
  // src/LiveKitRtcConnection.ts
496
+ import { createReadStream as createReadStream2 } from "fs";
481
497
  import { Readable as Readable2 } from "stream";
498
+ import { fileURLToPath as fileURLToPath2 } from "url";
482
499
  import { OpusDecoder } from "opus-decoder";
483
500
  import { opus as opus2 } from "prism-media";
484
501
  import { promisify } from "util";
@@ -1672,10 +1689,13 @@ var LiveKitRtcConnection = class extends EventEmitter2 {
1672
1689
  runFFmpeg().catch((e) => this.audioDebug("ffmpeg error", { error: String(e) }));
1673
1690
  }
1674
1691
  /**
1675
- * Play audio from a WebM/Opus URL or readable stream. Publishes to the LiveKit room as an audio track.
1692
+ * Play audio from a WebM/Opus source. Publishes to the LiveKit room as an audio track.
1693
+ *
1694
+ * When `urlOrStream` is a string: `http(s)://` is fetched; `file://` or any other string is read
1695
+ * from the local filesystem (path). Otherwise pass a Node.js ReadableStream.
1676
1696
  *
1677
- * @param urlOrStream - Audio source: HTTP(S) URL to a WebM/Opus file, or a Node.js ReadableStream
1678
- * @emits error - On fetch failure or decode errors
1697
+ * @param urlOrStream - WebM/Opus HTTP(S) URL, `file://` URL, filesystem path, or ReadableStream
1698
+ * @emits error - On fetch/read failure or decode errors
1679
1699
  */
1680
1700
  async play(urlOrStream) {
1681
1701
  this.stop();
@@ -1686,14 +1706,22 @@ var LiveKitRtcConnection = class extends EventEmitter2 {
1686
1706
  let inputStream;
1687
1707
  if (typeof urlOrStream === "string") {
1688
1708
  try {
1689
- const response = await fetch(urlOrStream);
1690
- if (!response.ok) {
1691
- throw new FluxerError2(`HTTP ${response.status}`, { code: ErrorCodes2.VoiceHttpError });
1692
- }
1693
- if (!response.body) {
1694
- throw new FluxerError2("No response body", { code: ErrorCodes2.VoiceNoResponseBody });
1709
+ const spec = urlOrStream.trim();
1710
+ const lower = spec.toLowerCase();
1711
+ if (lower.startsWith("http://") || lower.startsWith("https://")) {
1712
+ const response = await fetch(spec);
1713
+ if (!response.ok) {
1714
+ throw new FluxerError2(`HTTP ${response.status}`, { code: ErrorCodes2.VoiceHttpError });
1715
+ }
1716
+ if (!response.body) {
1717
+ throw new FluxerError2("No response body", { code: ErrorCodes2.VoiceNoResponseBody });
1718
+ }
1719
+ inputStream = Readable2.fromWeb(response.body);
1720
+ } else if (lower.startsWith("file:")) {
1721
+ inputStream = createReadStream2(fileURLToPath2(new URL(spec)));
1722
+ } else {
1723
+ inputStream = createReadStream2(spec);
1695
1724
  }
1696
- inputStream = Readable2.fromWeb(response.body);
1697
1725
  } catch (e) {
1698
1726
  const err = e instanceof FluxerError2 ? e : new FluxerError2(e instanceof Error ? e.message : String(e), {
1699
1727
  code: ErrorCodes2.FileFetchFailed,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.3.0",
6
+ "version": "1.3.1",
7
7
  "description": "Voice support for Fluxer bots",
8
8
  "repository": {
9
9
  "type": "git",
@@ -39,10 +39,10 @@
39
39
  "prism-media": "^1.3.5",
40
40
  "tweetnacl": "^1.0.3",
41
41
  "ws": "^8.18.0",
42
- "@fluxerjs/collection": "1.3.0",
43
- "@fluxerjs/core": "1.3.0",
44
- "@fluxerjs/util": "1.3.0",
45
- "@fluxerjs/types": "1.3.0"
42
+ "@fluxerjs/collection": "1.3.1",
43
+ "@fluxerjs/core": "1.3.1",
44
+ "@fluxerjs/types": "1.3.1",
45
+ "@fluxerjs/util": "1.3.1"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/node": "^20.0.0",