@corewire/airplay 1.0.0-next.1 → 1.0.0-next.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.
package/README.md CHANGED
@@ -7,7 +7,7 @@ Part of [CoreWire](https://corewire.dev). To control a device without handling s
7
7
  ## Install
8
8
 
9
9
  ```sh
10
- npm install @corewire/airplay
10
+ npm install @corewire/airplay@next
11
11
  ```
12
12
 
13
13
  Requires Node.js 18.17 or later (20.3 or later on the 20 line).
package/dist/index.mjs CHANGED
@@ -2064,6 +2064,7 @@ async function transientPairing(control, context) {
2064
2064
  * @throws {PairingError} When the receiver refuses to start pairing.
2065
2065
  */
2066
2066
  async function pair(target, options = {}) {
2067
+ if (options.signal?.aborted) throw new ConnectionClosedError("Pairing was interrupted.");
2067
2068
  const context = options.context ?? new Context(target.id ?? target.address, options.identity);
2068
2069
  const control = new ControlChannel(context, target.address, target.port ?? 7e3);
2069
2070
  const interrupt = () => control.destroy();
package/dist/mrp.mjs CHANGED
@@ -627,6 +627,11 @@ function getExtension(message, extension) {
627
627
 
628
628
  //#endregion
629
629
  //#region src/mrp/commands.ts
630
+ /**
631
+ * Milliseconds a command waits for its result. The Music app on tvOS answers Play after about
632
+ * 9 s while playback starts within a second, and the receiver handles commands one at a time.
633
+ */
634
+ const COMMAND_TIMEOUT = 15e3;
630
635
  /** The receiver refused a SendCommand. */
631
636
  var SendCommandError = class extends CommandError {
632
637
  sendError;
@@ -737,7 +742,7 @@ var Commands = class {
737
742
  await this.#exchange(sendCommandWithSleepTimer(seconds, stopMode));
738
743
  }
739
744
  async #exchange(message) {
740
- const response = await this.#channel.exchange(message);
745
+ const response = await this.#channel.exchange(message, COMMAND_TIMEOUT);
741
746
  let result;
742
747
  try {
743
748
  result = getExtension(response, sendCommandResultMessage);
@@ -1667,29 +1672,29 @@ var MediaRemoteState = class extends EventEmitter {
1667
1672
  }
1668
1673
  this.#emitNowPlayingChangedIfNeeded();
1669
1674
  }
1670
- /** One SET_STATE can carry playback state, now-playing info, commands and queue at once; only the active client emits. */
1675
+ /** One SET_STATE can carry playback state, now-playing info, commands and queue at once; only the active player of the active client emits. */
1671
1676
  #onSetState(message) {
1672
1677
  const bundleIdentifier = message.playerPath?.client?.bundleIdentifier;
1673
1678
  if (!bundleIdentifier) return;
1674
1679
  const client = this.#client(bundleIdentifier, message.displayName);
1675
1680
  const playerIdentifier = message.playerPath?.player?.identifier || "MediaRemote-DefaultPlayer";
1676
1681
  const player = client.getOrCreatePlayer(playerIdentifier, message.playerPath?.player?.displayName);
1677
- const isActiveClient = bundleIdentifier === this.#nowPlayingClientBundleIdentifier;
1682
+ const isActive = bundleIdentifier === this.#nowPlayingClientBundleIdentifier && player === client.activePlayer;
1678
1683
  if (message.playbackState) {
1679
1684
  const oldState = player.playbackState;
1680
1685
  player.setPlaybackState(message.playbackState, message.playbackStateTimestamp);
1681
- if (isActiveClient && oldState !== player.playbackState) this.emit("playbackStateChanged", client, player, oldState, player.playbackState);
1686
+ if (isActive && oldState !== player.playbackState) this.emit("playbackStateChanged", client, player, oldState, player.playbackState);
1682
1687
  }
1683
1688
  if (message.nowPlayingInfo) player.setNowPlayingInfo(message.nowPlayingInfo);
1684
1689
  if (message.supportedCommands) {
1685
1690
  player.setSupportedCommands(message.supportedCommands.supportedCommands);
1686
- if (isActiveClient) this.emit("supportedCommandsChanged", client, player, player.supportedCommands);
1691
+ if (isActive) this.emit("supportedCommandsChanged", client, player, player.supportedCommands);
1687
1692
  }
1688
1693
  if (message.playbackQueue) {
1689
1694
  player.setPlaybackQueue(message.playbackQueue);
1690
- if (isActiveClient) this.emit("playbackQueueChanged", client, player);
1695
+ if (isActive) this.emit("playbackQueueChanged", client, player);
1691
1696
  }
1692
- if (isActiveClient) this.#emitNowPlayingChangedIfNeeded();
1697
+ if (isActive) this.#emitNowPlayingChangedIfNeeded();
1693
1698
  }
1694
1699
  #onUpdateContentItem(message) {
1695
1700
  const bundleIdentifier = message.playerPath?.client?.bundleIdentifier;
@@ -1786,12 +1791,15 @@ var MediaRemoteState = class extends EventEmitter {
1786
1791
  #createNowPlayingSnapshot() {
1787
1792
  const client = this.nowPlayingClient;
1788
1793
  const player = client?.activePlayer ?? null;
1794
+ const metadata = player?.currentItemMetadata;
1789
1795
  return {
1790
1796
  bundleIdentifier: client?.bundleIdentifier ?? null,
1797
+ displayName: client?.displayName ?? null,
1791
1798
  playerIdentifier: player?.identifier ?? null,
1792
1799
  playbackState: player?.playbackState ?? 0,
1793
1800
  title: player?.title ?? "",
1794
1801
  artist: player?.artist ?? "",
1802
+ trackArtist: metadata?.trackArtistName ?? "",
1795
1803
  album: player?.album ?? "",
1796
1804
  genre: player?.genre ?? "",
1797
1805
  duration: player?.duration ?? 0,
@@ -1805,6 +1813,8 @@ var MediaRemoteState = class extends EventEmitter {
1805
1813
  episodeTitle: player?.episodeTitle ?? "",
1806
1814
  contentRating: player?.contentRating ?? "",
1807
1815
  contentIdentifier: player?.contentIdentifier ?? "",
1816
+ storeAlbumId: String(metadata?.iTunesStoreAlbumIdentifier ?? ""),
1817
+ lyricsId: String(metadata?.lyricsAdamID ?? ""),
1808
1818
  artworkId: player?.artworkId ?? null,
1809
1819
  hasArtworkUrl: player?.artworkUrl() != null,
1810
1820
  hasArtworkData: player?.currentItemArtwork != null,
@@ -1825,7 +1835,7 @@ var MediaRemoteState = class extends EventEmitter {
1825
1835
  this.emit("nowPlayingChanged", client, client?.activePlayer ?? null);
1826
1836
  }
1827
1837
  #snapshotsEqual(a, b) {
1828
- return a.bundleIdentifier === b.bundleIdentifier && a.playerIdentifier === b.playerIdentifier && a.playbackState === b.playbackState && a.title === b.title && a.artist === b.artist && a.album === b.album && a.genre === b.genre && a.duration === b.duration && a.playbackRate === b.playbackRate && a.shuffleMode === b.shuffleMode && a.repeatMode === b.repeatMode && a.mediaType === b.mediaType && a.seriesName === b.seriesName && a.seasonNumber === b.seasonNumber && a.episodeNumber === b.episodeNumber && a.episodeTitle === b.episodeTitle && a.contentRating === b.contentRating && a.contentIdentifier === b.contentIdentifier && a.artworkId === b.artworkId && a.hasArtworkUrl === b.hasArtworkUrl && a.hasArtworkData === b.hasArtworkData && a.isAlwaysLive === b.isAlwaysLive && a.isAdvertisement === b.isAdvertisement;
1838
+ return a.bundleIdentifier === b.bundleIdentifier && a.displayName === b.displayName && a.playerIdentifier === b.playerIdentifier && a.playbackState === b.playbackState && a.title === b.title && a.artist === b.artist && a.trackArtist === b.trackArtist && a.album === b.album && a.genre === b.genre && a.duration === b.duration && a.playbackRate === b.playbackRate && a.shuffleMode === b.shuffleMode && a.repeatMode === b.repeatMode && a.mediaType === b.mediaType && a.seriesName === b.seriesName && a.seasonNumber === b.seasonNumber && a.episodeNumber === b.episodeNumber && a.episodeTitle === b.episodeTitle && a.contentRating === b.contentRating && a.contentIdentifier === b.contentIdentifier && a.storeAlbumId === b.storeAlbumId && a.lyricsId === b.lyricsId && a.artworkId === b.artworkId && a.hasArtworkUrl === b.hasArtworkUrl && a.hasArtworkData === b.hasArtworkData && a.isAlwaysLive === b.isAlwaysLive && a.isAdvertisement === b.isAdvertisement;
1829
1839
  }
1830
1840
  };
1831
1841
 
package/dist/raop.mjs CHANGED
@@ -855,7 +855,7 @@ var StreamClient = class extends EventEmitter {
855
855
  } finally {
856
856
  this.#packetBacklog.clear();
857
857
  if (transport) {
858
- await this.#rtsp.teardown(this.#streamContext.rtspSession);
858
+ await this.#rtsp.teardown(this.#streamContext.rtspSession).catch((err) => this.#context.logger.warn("TEARDOWN failed.", err));
859
859
  transport.close();
860
860
  }
861
861
  this.#protocol.teardown();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@corewire/airplay",
3
3
  "description": "AirPlay 2 client for Node.js, with MRP remote control, URL playback, audio streaming and RAOP.",
4
- "version": "1.0.0-next.1",
4
+ "version": "1.0.0-next.2",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": {
@@ -11,9 +11,13 @@
11
11
  },
12
12
  "repository": {
13
13
  "type": "git",
14
- "url": "https://github.com/basmilius/apple-protocols",
14
+ "url": "https://github.com/basmilius/corewire",
15
15
  "directory": "packages/airplay"
16
16
  },
17
+ "homepage": "https://corewire.dev",
18
+ "bugs": {
19
+ "url": "https://github.com/basmilius/corewire/issues"
20
+ },
17
21
  "keywords": [
18
22
  "apple",
19
23
  "airplay",
@@ -61,7 +65,7 @@
61
65
  }
62
66
  },
63
67
  "dependencies": {
64
- "@corewire/core": "1.0.0-next.1"
68
+ "@corewire/core": "1.0.0-next.2"
65
69
  },
66
70
  "devDependencies": {
67
71
  "@bufbuild/buf": "^1.70.0",