@basmilius/apple-devices 0.9.15 → 0.9.16

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
@@ -14,6 +14,7 @@ declare class Player {
14
14
  get nowPlayingInfo(): Proto.NowPlayingInfo | null;
15
15
  get playbackQueue(): Proto.PlaybackQueue | null;
16
16
  get playbackState(): Proto.PlaybackState_Enum;
17
+ get rawPlaybackState(): Proto.PlaybackState_Enum;
17
18
  get playbackStateTimestamp(): number;
18
19
  get supportedCommands(): Proto.CommandInfo[];
19
20
  get title(): string;
@@ -33,10 +34,13 @@ declare class Player {
33
34
  get elapsedTime(): number;
34
35
  get currentItem(): Proto.ContentItem | null;
35
36
  get currentItemMetadata(): Proto.ContentItemMetadata | null;
37
+ get artworkId(): string | null;
38
+ artworkUrl(width?: number, height?: number): string | null;
36
39
  get currentItemArtwork(): Uint8Array | null;
37
40
  get currentItemArtworkUrl(): string | null;
38
41
  get currentItemLyrics(): Proto.LyricsItem | null;
39
42
  constructor(identifier: string, displayName: string);
43
+ findCommand(command: Proto.Command): Proto.CommandInfo | null;
40
44
  isCommandSupported(command: Proto.Command): boolean;
41
45
  setNowPlayingInfo(nowPlayingInfo: Proto.NowPlayingInfo): void;
42
46
  setPlaybackQueue(playbackQueue: Proto.PlaybackQueue): void;
@@ -72,6 +76,8 @@ declare class Client {
72
76
  get shuffleMode(): Proto.ShuffleMode_Enum;
73
77
  get repeatMode(): Proto.RepeatMode_Enum;
74
78
  get elapsedTime(): number;
79
+ get artworkId(): string | null;
80
+ artworkUrl(width?: number, height?: number): string | null;
75
81
  get currentItem(): Proto.ContentItem | null;
76
82
  get currentItemMetadata(): Proto.ContentItemMetadata | null;
77
83
  get currentItemArtwork(): Uint8Array | null;
@@ -82,7 +88,9 @@ declare class Client {
82
88
  setActivePlayer(identifier: string): void;
83
89
  removePlayer(identifier: string): void;
84
90
  setDefaultSupportedCommands(supportedCommands: Proto.CommandInfo[]): void;
91
+ findCommand(command: Proto.Command): Proto.CommandInfo | null;
85
92
  isCommandSupported(command: Proto.Command): boolean;
93
+ updateDisplayName(displayName: string): void;
86
94
  }
87
95
  //#endregion
88
96
  //#region src/airplay/const.d.ts
@@ -91,6 +99,11 @@ declare const STATE_SUBSCRIBE_SYMBOL: unique symbol;
91
99
  declare const STATE_UNSUBSCRIBE_SYMBOL: unique symbol;
92
100
  //#endregion
93
101
  //#region src/airplay/remote.d.ts
102
+ declare class SendCommandError extends Error {
103
+ readonly sendError: Proto.SendError_Enum;
104
+ readonly handlerReturnStatus: Proto.HandlerReturnStatus_Enum;
105
+ constructor(sendError: Proto.SendError_Enum, handlerReturnStatus: Proto.HandlerReturnStatus_Enum);
106
+ }
94
107
  declare class export_default$1 {
95
108
  #private;
96
109
  constructor(device: export_default);
@@ -123,8 +136,18 @@ declare class export_default$1 {
123
136
  commandSetShuffleMode(mode: Proto.ShuffleMode_Enum): Promise<void>;
124
137
  commandSetRepeatMode(mode: Proto.RepeatMode_Enum): Promise<void>;
125
138
  commandChangePlaybackRate(rate: number): Promise<void>;
139
+ commandAdvanceShuffleMode(): Promise<void>;
140
+ commandAdvanceRepeatMode(): Promise<void>;
141
+ commandBeginFastForward(): Promise<void>;
142
+ commandEndFastForward(): Promise<void>;
143
+ commandBeginRewind(): Promise<void>;
144
+ commandEndRewind(): Promise<void>;
126
145
  commandNextChapter(): Promise<void>;
127
146
  commandPreviousChapter(): Promise<void>;
147
+ commandLikeTrack(): Promise<void>;
148
+ commandDislikeTrack(): Promise<void>;
149
+ commandBookmarkTrack(): Promise<void>;
150
+ commandAddNowPlayingItemToLibrary(): Promise<void>;
128
151
  tap(x: number, y: number, finger?: number): Promise<void>;
129
152
  swipeUp(duration?: number): Promise<void>;
130
153
  swipeDown(duration?: number): Promise<void>;
@@ -363,4 +386,4 @@ declare class export_default$6 extends export_default$8 {}
363
386
  //#region src/model/homepod-mini.d.ts
364
387
  declare class export_default$7 extends export_default$8 {}
365
388
  //#endregion
366
- export { PROTOCOL as AIRPLAY, Client as AirPlayClient, export_default as AirPlayDevice, Player as AirPlayPlayer, export_default$1 as AirPlayRemote, export_default$2 as AirPlayState, export_default$3 as AirPlayVolume, export_default$4 as AppleTV, PROTOCOL$1 as COMPANION_LINK, export_default$5 as CompanionLinkDevice, export_default$6 as HomePod, export_default$7 as HomePodMini };
389
+ export { PROTOCOL as AIRPLAY, Client as AirPlayClient, export_default as AirPlayDevice, Player as AirPlayPlayer, export_default$1 as AirPlayRemote, export_default$2 as AirPlayState, export_default$3 as AirPlayVolume, export_default$4 as AppleTV, PROTOCOL$1 as COMPANION_LINK, export_default$5 as CompanionLinkDevice, export_default$6 as HomePod, export_default$7 as HomePodMini, SendCommandError };
package/dist/index.mjs CHANGED
@@ -30,6 +30,10 @@ var Player = class {
30
30
  return this.#playbackQueue;
31
31
  }
32
32
  get playbackState() {
33
+ if (this.#playbackState === Proto.PlaybackState_Enum.Playing && this.playbackRate === 0) return Proto.PlaybackState_Enum.Paused;
34
+ return this.#playbackState;
35
+ }
36
+ get rawPlaybackState() {
33
37
  return this.#playbackState;
34
38
  }
35
39
  get playbackStateTimestamp() {
@@ -100,6 +104,25 @@ var Player = class {
100
104
  get currentItemMetadata() {
101
105
  return this.currentItem?.metadata ?? null;
102
106
  }
107
+ get artworkId() {
108
+ const metadata = this.currentItemMetadata;
109
+ if (!metadata) return null;
110
+ if (!metadata.artworkAvailable && !metadata.artworkURL && !metadata.artworkIdentifier) return null;
111
+ if (metadata.artworkIdentifier) return metadata.artworkIdentifier;
112
+ if (metadata.contentIdentifier) return metadata.contentIdentifier;
113
+ return this.currentItem?.identifier ?? null;
114
+ }
115
+ artworkUrl(width = 600, height = -1) {
116
+ const metadata = this.currentItemMetadata;
117
+ if (metadata?.artworkURL) return metadata.artworkURL;
118
+ const item = this.currentItem;
119
+ if (item?.remoteArtworks.length > 0 && item.remoteArtworks[0].artworkURLString) return item.remoteArtworks[0].artworkURLString;
120
+ if (metadata?.artworkIdentifier) try {
121
+ const url = metadata.artworkIdentifier.replace("{w}", String(width < 1 ? 999999 : width)).replace("{h}", String(height < 1 ? 999999 : height)).replace("{c}", "bb").replace("{f}", "png");
122
+ if (url.startsWith("http://") || url.startsWith("https://")) return url;
123
+ } catch {}
124
+ return null;
125
+ }
103
126
  get currentItemArtwork() {
104
127
  const item = this.currentItem;
105
128
  if (!item) return null;
@@ -129,8 +152,12 @@ var Player = class {
129
152
  this.#displayName = displayName;
130
153
  this.#playbackState = Proto.PlaybackState_Enum.Unknown;
131
154
  }
155
+ findCommand(command) {
156
+ return this.#supportedCommands.find((c) => c.command === command) ?? null;
157
+ }
132
158
  isCommandSupported(command) {
133
- return this.#supportedCommands.some((c) => c.command === command);
159
+ const info = this.findCommand(command);
160
+ return info != null && info.enabled !== false;
134
161
  }
135
162
  setNowPlayingInfo(nowPlayingInfo) {
136
163
  this.#nowPlayingInfo = nowPlayingInfo;
@@ -188,7 +215,13 @@ var Client = class {
188
215
  return this.activePlayer?.playbackStateTimestamp ?? -1;
189
216
  }
190
217
  get supportedCommands() {
191
- return this.activePlayer?.supportedCommands ?? this.#defaultSupportedCommands;
218
+ const playerCommands = this.activePlayer?.supportedCommands ?? [];
219
+ if (playerCommands.length === 0) return this.#defaultSupportedCommands;
220
+ if (this.#defaultSupportedCommands.length === 0) return playerCommands;
221
+ const playerCommandSet = new Set(playerCommands.map((c) => c.command));
222
+ const merged = [...playerCommands];
223
+ for (const cmd of this.#defaultSupportedCommands) if (!playerCommandSet.has(cmd.command)) merged.push(cmd);
224
+ return merged;
192
225
  }
193
226
  get title() {
194
227
  return this.activePlayer?.title ?? "";
@@ -235,6 +268,12 @@ var Client = class {
235
268
  get elapsedTime() {
236
269
  return this.activePlayer?.elapsedTime ?? 0;
237
270
  }
271
+ get artworkId() {
272
+ return this.activePlayer?.artworkId ?? null;
273
+ }
274
+ artworkUrl(width = 600, height = -1) {
275
+ return this.activePlayer?.artworkUrl(width, height) ?? null;
276
+ }
238
277
  get currentItem() {
239
278
  return this.activePlayer?.currentItem ?? null;
240
279
  }
@@ -277,8 +316,17 @@ var Client = class {
277
316
  setDefaultSupportedCommands(supportedCommands) {
278
317
  this.#defaultSupportedCommands = supportedCommands;
279
318
  }
319
+ findCommand(command) {
320
+ const playerCmd = this.activePlayer?.findCommand(command) ?? null;
321
+ if (playerCmd) return playerCmd;
322
+ return this.#defaultSupportedCommands.find((c) => c.command === command) ?? null;
323
+ }
280
324
  isCommandSupported(command) {
281
- return this.activePlayer?.isCommandSupported(command) ?? false;
325
+ const info = this.findCommand(command);
326
+ return info != null && info.enabled !== false;
327
+ }
328
+ updateDisplayName(displayName) {
329
+ this.#displayName = displayName;
282
330
  }
283
331
  };
284
332
 
@@ -291,6 +339,16 @@ const STATE_UNSUBSCRIBE_SYMBOL = Symbol("com.basmilius.airplay:unsubscribe");
291
339
 
292
340
  //#endregion
293
341
  //#region src/airplay/remote.ts
342
+ var SendCommandError = class extends Error {
343
+ sendError;
344
+ handlerReturnStatus;
345
+ constructor(sendError, handlerReturnStatus) {
346
+ super(`SendCommand failed: sendError=${Proto.SendError_Enum[sendError]}, handlerReturnStatus=${Proto.HandlerReturnStatus_Enum[handlerReturnStatus]}`);
347
+ this.name = "SendCommandError";
348
+ this.sendError = sendError;
349
+ this.handlerReturnStatus = handlerReturnStatus;
350
+ }
351
+ };
294
352
  var remote_default = class {
295
353
  get #dataStream() {
296
354
  return this.#protocol.dataStream;
@@ -373,22 +431,40 @@ var remote_default = class {
373
431
  await this.#sendCommand(Proto.Command.PreviousTrack);
374
432
  }
375
433
  async commandSkipForward(interval = 15) {
376
- await this.#dataStream.exchange(DataStreamMessage.sendCommandWithSkipInterval(Proto.Command.SkipForward, interval));
434
+ await this.#sendCommandRaw(DataStreamMessage.sendCommandWithSkipInterval(Proto.Command.SkipForward, interval));
377
435
  }
378
436
  async commandSkipBackward(interval = 15) {
379
- await this.#dataStream.exchange(DataStreamMessage.sendCommandWithSkipInterval(Proto.Command.SkipBackward, interval));
437
+ await this.#sendCommandRaw(DataStreamMessage.sendCommandWithSkipInterval(Proto.Command.SkipBackward, interval));
380
438
  }
381
439
  async commandSeekToPosition(position) {
382
- await this.#dataStream.exchange(DataStreamMessage.sendCommandWithPlaybackPosition(Proto.Command.SeekToPlaybackPosition, position));
440
+ await this.#sendCommandRaw(DataStreamMessage.sendCommandWithPlaybackPosition(Proto.Command.SeekToPlaybackPosition, position));
383
441
  }
384
442
  async commandSetShuffleMode(mode) {
385
- await this.#dataStream.exchange(DataStreamMessage.sendCommandWithShuffleMode(Proto.Command.ChangeShuffleMode, mode));
443
+ await this.#sendCommandRaw(DataStreamMessage.sendCommandWithShuffleMode(Proto.Command.ChangeShuffleMode, mode));
386
444
  }
387
445
  async commandSetRepeatMode(mode) {
388
- await this.#dataStream.exchange(DataStreamMessage.sendCommandWithRepeatMode(Proto.Command.ChangeRepeatMode, mode));
446
+ await this.#sendCommandRaw(DataStreamMessage.sendCommandWithRepeatMode(Proto.Command.ChangeRepeatMode, mode));
389
447
  }
390
448
  async commandChangePlaybackRate(rate) {
391
- await this.#dataStream.exchange(DataStreamMessage.sendCommandWithPlaybackRate(Proto.Command.ChangePlaybackRate, rate));
449
+ await this.#sendCommandRaw(DataStreamMessage.sendCommandWithPlaybackRate(Proto.Command.ChangePlaybackRate, rate));
450
+ }
451
+ async commandAdvanceShuffleMode() {
452
+ await this.#sendCommand(Proto.Command.AdvanceShuffleMode);
453
+ }
454
+ async commandAdvanceRepeatMode() {
455
+ await this.#sendCommand(Proto.Command.AdvanceRepeatMode);
456
+ }
457
+ async commandBeginFastForward() {
458
+ await this.#sendCommand(Proto.Command.BeginFastForward);
459
+ }
460
+ async commandEndFastForward() {
461
+ await this.#sendCommand(Proto.Command.EndFastForward);
462
+ }
463
+ async commandBeginRewind() {
464
+ await this.#sendCommand(Proto.Command.BeginRewind);
465
+ }
466
+ async commandEndRewind() {
467
+ await this.#sendCommand(Proto.Command.EndRewind);
392
468
  }
393
469
  async commandNextChapter() {
394
470
  await this.#sendCommand(Proto.Command.NextChapter);
@@ -396,6 +472,18 @@ var remote_default = class {
396
472
  async commandPreviousChapter() {
397
473
  await this.#sendCommand(Proto.Command.PreviousChapter);
398
474
  }
475
+ async commandLikeTrack() {
476
+ await this.#sendCommand(Proto.Command.LikeTrack);
477
+ }
478
+ async commandDislikeTrack() {
479
+ await this.#sendCommand(Proto.Command.DislikeTrack);
480
+ }
481
+ async commandBookmarkTrack() {
482
+ await this.#sendCommand(Proto.Command.BookmarkTrack);
483
+ }
484
+ async commandAddNowPlayingItemToLibrary() {
485
+ await this.#sendCommand(Proto.Command.AddNowPlayingItemToLibrary);
486
+ }
399
487
  async tap(x, y, finger = 1) {
400
488
  await this.#sendTouch(x, y, 1, finger);
401
489
  await waitFor(50);
@@ -429,7 +517,17 @@ var remote_default = class {
429
517
  await this.#dataStream.exchange(DataStreamMessage.sendHIDEvent(usePage, usage, false));
430
518
  }
431
519
  async #sendCommand(command, options) {
432
- await this.#dataStream.exchange(DataStreamMessage.sendCommand(command, options));
520
+ const response = await this.#dataStream.exchange(DataStreamMessage.sendCommand(command, options));
521
+ return this.#checkCommandResult(response);
522
+ }
523
+ async #sendCommandRaw(message) {
524
+ const response = await this.#dataStream.exchange(message);
525
+ return this.#checkCommandResult(response);
526
+ }
527
+ #checkCommandResult(response) {
528
+ const result = DataStreamMessage.getExtension(response, Proto.sendCommandResultMessage);
529
+ if (result.sendError !== Proto.SendError_Enum.NoError) throw new SendCommandError(result.sendError, result.handlerReturnStatus);
530
+ return result;
433
531
  }
434
532
  async #sendTouch(x, y, phase, finger) {
435
533
  await this.#dataStream.exchange(DataStreamMessage.sendVirtualTouchEvent(x, y, phase, finger));
@@ -608,6 +706,7 @@ var state_default = class extends EventEmitter {
608
706
  }
609
707
  onSetNowPlayingClient(message) {
610
708
  this.#nowPlayingClientBundleIdentifier = message.client?.bundleIdentifier ?? null;
709
+ if (message.client?.bundleIdentifier && message.client?.displayName) this.#client(message.client.bundleIdentifier, message.client.displayName);
611
710
  this.emit("setNowPlayingClient", message);
612
711
  this.#emitNowPlayingChangedIfNeeded();
613
712
  }
@@ -679,8 +778,11 @@ var state_default = class extends EventEmitter {
679
778
  this.emit("volumeDidChange", message.volume);
680
779
  }
681
780
  #client(bundleIdentifier, displayName) {
682
- if (bundleIdentifier in this.#clients) return this.#clients[bundleIdentifier];
683
- else {
781
+ if (bundleIdentifier in this.#clients) {
782
+ const client = this.#clients[bundleIdentifier];
783
+ if (displayName) client.updateDisplayName(displayName);
784
+ return client;
785
+ } else {
684
786
  const client = new Client(bundleIdentifier, displayName);
685
787
  this.#clients[bundleIdentifier] = client;
686
788
  this.emit("clients", this.#clients);
@@ -705,7 +807,10 @@ var state_default = class extends EventEmitter {
705
807
  seriesName: player?.seriesName ?? "",
706
808
  seasonNumber: player?.seasonNumber ?? 0,
707
809
  episodeNumber: player?.episodeNumber ?? 0,
708
- contentIdentifier: player?.contentIdentifier ?? ""
810
+ contentIdentifier: player?.contentIdentifier ?? "",
811
+ artworkId: player?.artworkId ?? null,
812
+ hasArtworkUrl: player?.artworkUrl() != null,
813
+ hasArtworkData: player?.currentItemArtwork != null
709
814
  };
710
815
  }
711
816
  #emitNowPlayingChangedIfNeeded() {
@@ -717,7 +822,7 @@ var state_default = class extends EventEmitter {
717
822
  this.emit("nowPlayingChanged", client, client?.activePlayer ?? null);
718
823
  }
719
824
  #snapshotsEqual(a, b) {
720
- 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.shuffleMode === b.shuffleMode && a.repeatMode === b.repeatMode && a.mediaType === b.mediaType && a.seriesName === b.seriesName && a.seasonNumber === b.seasonNumber && a.episodeNumber === b.episodeNumber && a.contentIdentifier === b.contentIdentifier;
825
+ 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.shuffleMode === b.shuffleMode && a.repeatMode === b.repeatMode && a.mediaType === b.mediaType && a.seriesName === b.seriesName && a.seasonNumber === b.seasonNumber && a.episodeNumber === b.episodeNumber && a.contentIdentifier === b.contentIdentifier && a.artworkId === b.artworkId && a.hasArtworkUrl === b.hasArtworkUrl && a.hasArtworkData === b.hasArtworkData;
721
826
  }
722
827
  };
723
828
 
@@ -1354,4 +1459,4 @@ var homepod_default = class extends homepod_base_default {};
1354
1459
  var homepod_mini_default = class extends homepod_base_default {};
1355
1460
 
1356
1461
  //#endregion
1357
- export { PROTOCOL as AIRPLAY, Client as AirPlayClient, device_default as AirPlayDevice, Player as AirPlayPlayer, remote_default as AirPlayRemote, state_default as AirPlayState, volume_default as AirPlayVolume, apple_tv_default as AppleTV, PROTOCOL$1 as COMPANION_LINK, device_default$1 as CompanionLinkDevice, homepod_default as HomePod, homepod_mini_default as HomePodMini };
1462
+ export { PROTOCOL as AIRPLAY, Client as AirPlayClient, device_default as AirPlayDevice, Player as AirPlayPlayer, remote_default as AirPlayRemote, state_default as AirPlayState, volume_default as AirPlayVolume, apple_tv_default as AppleTV, PROTOCOL$1 as COMPANION_LINK, device_default$1 as CompanionLinkDevice, homepod_default as HomePod, homepod_mini_default as HomePodMini, SendCommandError };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@basmilius/apple-devices",
3
3
  "description": "Exposes various Apple devices to connect with either AirPlay or Companion Link.",
4
- "version": "0.9.15",
4
+ "version": "0.9.16",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": {
@@ -49,10 +49,10 @@
49
49
  }
50
50
  },
51
51
  "dependencies": {
52
- "@basmilius/apple-airplay": "0.9.15",
53
- "@basmilius/apple-common": "0.9.15",
54
- "@basmilius/apple-companion-link": "0.9.15",
55
- "@basmilius/apple-encoding": "0.9.15"
52
+ "@basmilius/apple-airplay": "0.9.16",
53
+ "@basmilius/apple-common": "0.9.16",
54
+ "@basmilius/apple-companion-link": "0.9.16",
55
+ "@basmilius/apple-encoding": "0.9.16"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@types/bun": "^1.3.11",