@lox-audioserver/node-airplay-sender 0.4.8 → 0.4.10

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.
@@ -7,6 +7,7 @@ type RTSPClient = {
7
7
  once: (event: string, cb: (...args: any[]) => void) => void;
8
8
  startHandshake: (udpServers: any, host: string, port: number) => void;
9
9
  teardown: () => void;
10
+ flush: () => void;
10
11
  setVolume: (volume: number, callback?: (err?: unknown) => void) => void;
11
12
  setTrackInfo: (name: string, artist?: string, album?: string, callback?: (err?: unknown) => void) => void;
12
13
  setProgress: (progress: number, duration: number, callback?: (err?: unknown) => void) => void;
@@ -438,6 +438,11 @@ AirTunesDevice.prototype.setVolume = function (volume, callback) {
438
438
  return;
439
439
  this.rtsp.setVolume(volume, callback);
440
440
  };
441
+ AirTunesDevice.prototype.flush = function () {
442
+ if (!this.rtsp)
443
+ return;
444
+ this.rtsp.flush();
445
+ };
441
446
  AirTunesDevice.prototype.setTrackInfo = function (name, artist, album, callback) {
442
447
  if (!this.rtsp)
443
448
  return;
@@ -28,6 +28,8 @@ export default class Devices extends EventEmitter implements DevicesEmitter {
28
28
  * @param txt TXT records advertised by the device.
29
29
  */
30
30
  add(type: string, host: string | null, options: any, mode?: number, txt?: string[] | string): AirTunesDeviceInstance;
31
+ /** Flush buffered audio on every device, re-anchoring playback at the current RTP position. */
32
+ flush(): void;
31
33
  /** Adjust volume on one device. */
32
34
  setVolume(key: string, volume: number, callback?: (err?: unknown) => void): void;
33
35
  /** Push playback position to one or all devices. */
@@ -70,6 +70,17 @@ class Devices extends node_events_1.EventEmitter {
70
70
  this.checkAirTunesDevices();
71
71
  return dev;
72
72
  }
73
+ /** Flush buffered audio on every device, re-anchoring playback at the current RTP position. */
74
+ flush() {
75
+ this.forEach((dev) => {
76
+ try {
77
+ dev.flush?.();
78
+ }
79
+ catch {
80
+ // ignore
81
+ }
82
+ });
83
+ }
73
84
  /** Adjust volume on one device. */
74
85
  setVolume(key, volume, callback) {
75
86
  const dev = this.devices[key];
@@ -35,7 +35,11 @@ declare class AirTunes extends Duplex {
35
35
  * Update track title/artist/album on a device.
36
36
  */
37
37
  setTrackInfo(deviceKey: string, name: string, artist?: string, album?: string, callback?: (err?: unknown) => void): void;
38
- /** Reset the circular buffer state. */
38
+ /**
39
+ * Flush buffered audio for a track switch: clear our circular buffer and tell
40
+ * every receiver to drop its buffered audio and re-anchor at the current RTP
41
+ * position. Keeps the RTP timeline continuous so playback does not desync.
42
+ */
39
43
  reset(): void;
40
44
  /** Send artwork to a device. */
41
45
  setArtwork(deviceKey: string, art: Buffer, contentType?: string, callback?: (err?: unknown) => void): void;
@@ -111,9 +111,14 @@ class AirTunes extends node_stream_1.Duplex {
111
111
  setTrackInfo(deviceKey, name, artist, album, callback) {
112
112
  this.devices.setTrackInfo(deviceKey, name, artist, album, callback);
113
113
  }
114
- /** Reset the circular buffer state. */
114
+ /**
115
+ * Flush buffered audio for a track switch: clear our circular buffer and tell
116
+ * every receiver to drop its buffered audio and re-anchor at the current RTP
117
+ * position. Keeps the RTP timeline continuous so playback does not desync.
118
+ */
115
119
  reset() {
116
120
  this.circularBuffer.reset();
121
+ this.devices.flush();
117
122
  }
118
123
  /** Send artwork to a device. */
119
124
  setArtwork(deviceKey, art, contentType, callback) {
package/dist/core/rtsp.js CHANGED
@@ -280,6 +280,15 @@ Client.prototype.setVolume = function (volume, callback) {
280
280
  this.status = SETVOLUME;
281
281
  this.sendNextRequest();
282
282
  };
283
+ // Drop the receiver's buffered audio and re-anchor playback at the current RTP
284
+ // position (RTP-Info: seq=lastSeq+1). Used on track switch/scrub so the new
285
+ // track starts cleanly instead of replaying stale buffered frames.
286
+ Client.prototype.flush = function () {
287
+ if (this.status !== PLAYING)
288
+ return;
289
+ this.status = FLUSH;
290
+ this.sendNextRequest();
291
+ };
283
292
  Client.prototype.setProgress = function (progress, duration, callback) {
284
293
  if (this.status !== PLAYING)
285
294
  return;
@@ -438,6 +438,11 @@ AirTunesDevice.prototype.setVolume = function (volume, callback) {
438
438
  return;
439
439
  this.rtsp.setVolume(volume, callback);
440
440
  };
441
+ AirTunesDevice.prototype.flush = function () {
442
+ if (!this.rtsp)
443
+ return;
444
+ this.rtsp.flush();
445
+ };
441
446
  AirTunesDevice.prototype.setTrackInfo = function (name, artist, album, callback) {
442
447
  if (!this.rtsp)
443
448
  return;
@@ -70,6 +70,17 @@ class Devices extends node_events_1.EventEmitter {
70
70
  this.checkAirTunesDevices();
71
71
  return dev;
72
72
  }
73
+ /** Flush buffered audio on every device, re-anchoring playback at the current RTP position. */
74
+ flush() {
75
+ this.forEach((dev) => {
76
+ try {
77
+ dev.flush?.();
78
+ }
79
+ catch {
80
+ // ignore
81
+ }
82
+ });
83
+ }
73
84
  /** Adjust volume on one device. */
74
85
  setVolume(key, volume, callback) {
75
86
  const dev = this.devices[key];
@@ -111,9 +111,14 @@ class AirTunes extends node_stream_1.Duplex {
111
111
  setTrackInfo(deviceKey, name, artist, album, callback) {
112
112
  this.devices.setTrackInfo(deviceKey, name, artist, album, callback);
113
113
  }
114
- /** Reset the circular buffer state. */
114
+ /**
115
+ * Flush buffered audio for a track switch: clear our circular buffer and tell
116
+ * every receiver to drop its buffered audio and re-anchor at the current RTP
117
+ * position. Keeps the RTP timeline continuous so playback does not desync.
118
+ */
115
119
  reset() {
116
120
  this.circularBuffer.reset();
121
+ this.devices.flush();
117
122
  }
118
123
  /** Send artwork to a device. */
119
124
  setArtwork(deviceKey, art, contentType, callback) {
@@ -280,6 +280,15 @@ Client.prototype.setVolume = function (volume, callback) {
280
280
  this.status = SETVOLUME;
281
281
  this.sendNextRequest();
282
282
  };
283
+ // Drop the receiver's buffered audio and re-anchor playback at the current RTP
284
+ // position (RTP-Info: seq=lastSeq+1). Used on track switch/scrub so the new
285
+ // track starts cleanly instead of replaying stale buffered frames.
286
+ Client.prototype.flush = function () {
287
+ if (this.status !== PLAYING)
288
+ return;
289
+ this.status = FLUSH;
290
+ this.sendNextRequest();
291
+ };
283
292
  Client.prototype.setProgress = function (progress, duration, callback) {
284
293
  if (this.status !== PLAYING)
285
294
  return;
package/dist/esm/index.js CHANGED
@@ -116,6 +116,15 @@ class LoxAirplaySender extends node_events_1.EventEmitter {
116
116
  return;
117
117
  this.airtunes.write(chunk);
118
118
  }
119
+ /**
120
+ * Clear the internal circular buffer without tearing down the RTSP session.
121
+ * Use on track switches to drop buffered PCM in place of a full restart.
122
+ */
123
+ reset() {
124
+ if (!this.airtunes)
125
+ return;
126
+ this.airtunes.reset();
127
+ }
119
128
  /**
120
129
  * Pipe a readable stream into the sender; auto-stops on end/error.
121
130
  */
package/dist/index.d.ts CHANGED
@@ -78,6 +78,11 @@ export declare class LoxAirplaySender extends EventEmitter {
78
78
  * Push raw PCM or ALAC frames into the stream.
79
79
  */
80
80
  sendPcm(chunk: Buffer): void;
81
+ /**
82
+ * Clear the internal circular buffer without tearing down the RTSP session.
83
+ * Use on track switches to drop buffered PCM in place of a full restart.
84
+ */
85
+ reset(): void;
81
86
  /**
82
87
  * Pipe a readable stream into the sender; auto-stops on end/error.
83
88
  */
package/dist/index.js CHANGED
@@ -116,6 +116,15 @@ class LoxAirplaySender extends node_events_1.EventEmitter {
116
116
  return;
117
117
  this.airtunes.write(chunk);
118
118
  }
119
+ /**
120
+ * Clear the internal circular buffer without tearing down the RTSP session.
121
+ * Use on track switches to drop buffered PCM in place of a full restart.
122
+ */
123
+ reset() {
124
+ if (!this.airtunes)
125
+ return;
126
+ this.airtunes.reset();
127
+ }
119
128
  /**
120
129
  * Pipe a readable stream into the sender; auto-stops on end/error.
121
130
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lox-audioserver/node-airplay-sender",
3
- "version": "0.4.8",
3
+ "version": "0.4.10",
4
4
  "description": "AirPlay sender (RAOP/AirPlay 1; AirPlay 2 control/auth, best-effort audio)",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",