|
1 | 1 | package de.labystudio.spotifyapi.platform.osx; |
2 | 2 |
|
3 | | -import de.labystudio.spotifyapi.SpotifyAPI; |
| 3 | +import de.labystudio.spotifyapi.SpotifyListener; |
4 | 4 | import de.labystudio.spotifyapi.model.MediaKey; |
5 | 5 | import de.labystudio.spotifyapi.model.Track; |
6 | | -import de.labystudio.spotifyapi.platform.AbstractSpotifyAPI; |
| 6 | +import de.labystudio.spotifyapi.platform.AbstractTickSpotifyAPI; |
| 7 | +import de.labystudio.spotifyapi.platform.osx.api.spotify.SpotifyAppleScript; |
| 8 | + |
| 9 | +import java.util.Objects; |
7 | 10 |
|
8 | 11 | /** |
9 | 12 | * OSX implementation of the SpotifyAPI. |
| 13 | + * It uses the AppleScript API to access the Spotify application. |
10 | 14 | * |
11 | 15 | * @author LabyStudio |
12 | 16 | */ |
13 | | -public class OSXSpotifyApi extends AbstractSpotifyAPI { |
| 17 | +public class OSXSpotifyApi extends AbstractTickSpotifyAPI { |
14 | 18 |
|
15 | | - public OSXSpotifyApi() { |
16 | | - super(); |
17 | | - } |
| 19 | + private final SpotifyAppleScript appleScript = new SpotifyAppleScript(); |
| 20 | + |
| 21 | + private boolean connected = false; |
| 22 | + |
| 23 | + private Track currentTrack; |
| 24 | + private int currentPosition = -1; |
| 25 | + private boolean isPlaying; |
| 26 | + |
| 27 | + private long lastTimePositionUpdated; |
18 | 28 |
|
19 | 29 | @Override |
20 | | - public SpotifyAPI initialize() { |
21 | | - return this; |
| 30 | + protected void onTick() { |
| 31 | + try { |
| 32 | + String trackId = this.appleScript.getTrackId(); |
| 33 | + |
| 34 | + // Handle on connect |
| 35 | + if (!this.connected && !trackId.isEmpty()) { |
| 36 | + this.connected = true; |
| 37 | + this.listeners.forEach(SpotifyListener::onConnect); |
| 38 | + } |
| 39 | + |
| 40 | + // Handle track changes |
| 41 | + if (!Objects.equals(trackId, this.currentTrack == null ? null : this.currentTrack.getId())) { |
| 42 | + String trackName = this.appleScript.getTrackName(); |
| 43 | + String trackArtist = this.appleScript.getTrackArtist(); |
| 44 | + int trackLength = this.appleScript.getTrackLength(); |
| 45 | + |
| 46 | + boolean isFirstTrack = !this.hasTrack(); |
| 47 | + |
| 48 | + Track track = new Track(trackId, trackName, trackArtist, trackLength); |
| 49 | + this.currentTrack = track; |
| 50 | + |
| 51 | + // Fire on track changed |
| 52 | + this.listeners.forEach(listener -> listener.onTrackChanged(track)); |
| 53 | + |
| 54 | + // Reset position on song change |
| 55 | + if (!isFirstTrack) { |
| 56 | + this.updatePosition(0); |
| 57 | + } |
| 58 | + } |
| 59 | + |
| 60 | + // Handle is playing changes |
| 61 | + boolean isPlaying = this.appleScript.getPlayerState(); |
| 62 | + if (isPlaying != this.isPlaying) { |
| 63 | + this.isPlaying = isPlaying; |
| 64 | + |
| 65 | + // Fire on play back changed |
| 66 | + this.listeners.forEach(listener -> listener.onPlayBackChanged(isPlaying)); |
| 67 | + } |
| 68 | + |
| 69 | + // Handle position changes |
| 70 | + int position = this.appleScript.getPlayerPosition(); |
| 71 | + if (!this.hasPosition() || Math.abs(position - this.getPosition()) > 1000) { |
| 72 | + this.updatePosition(position); |
| 73 | + } |
| 74 | + |
| 75 | + // Fire keep alive |
| 76 | + this.listeners.forEach(SpotifyListener::onSync); |
| 77 | + } catch (Exception e) { |
| 78 | + this.listeners.forEach(listener -> listener.onDisconnect(e)); |
| 79 | + this.connected = false; |
| 80 | + } |
22 | 81 | } |
23 | 82 |
|
24 | | - @Override |
25 | | - public boolean isInitialized() { |
26 | | - return false; |
| 83 | + private void updatePosition(int position) { |
| 84 | + if (position == this.currentPosition) { |
| 85 | + return; |
| 86 | + } |
| 87 | + |
| 88 | + // Update position known state |
| 89 | + this.currentPosition = position; |
| 90 | + this.lastTimePositionUpdated = System.currentTimeMillis(); |
| 91 | + |
| 92 | + // Fire on position changed |
| 93 | + this.listeners.forEach(listener -> listener.onPositionChanged(position)); |
27 | 94 | } |
28 | 95 |
|
29 | 96 | @Override |
30 | | - public Track getTrack() { |
31 | | - return null; // TODO Implement OSX SpotifyAPI |
| 97 | + public void pressMediaKey(MediaKey mediaKey) { |
| 98 | + try { |
| 99 | + switch (mediaKey) { |
| 100 | + case PLAY_PAUSE: |
| 101 | + this.appleScript.playPause(); |
| 102 | + break; |
| 103 | + case NEXT: |
| 104 | + this.appleScript.nextTrack(); |
| 105 | + break; |
| 106 | + case PREV: |
| 107 | + this.appleScript.previousTrack(); |
| 108 | + break; |
| 109 | + } |
| 110 | + } catch (Exception e) { |
| 111 | + this.listeners.forEach(listener -> listener.onDisconnect(e)); |
| 112 | + this.connected = false; |
| 113 | + } |
32 | 114 | } |
33 | 115 |
|
34 | 116 | @Override |
35 | 117 | public int getPosition() { |
36 | | - return 0; // TODO Implement OSX SpotifyAPI |
37 | | - } |
| 118 | + if (!this.hasPosition()) { |
| 119 | + throw new IllegalStateException("Position is not known yet"); |
| 120 | + } |
38 | 121 |
|
39 | | - @Override |
40 | | - public boolean hasPosition() { |
41 | | - return false; // TODO Implement OSX SpotifyAPI |
| 122 | + if (this.isPlaying) { |
| 123 | + // Interpolate position |
| 124 | + long timePassed = System.currentTimeMillis() - this.lastTimePositionUpdated; |
| 125 | + return this.currentPosition + (int) timePassed; |
| 126 | + } else { |
| 127 | + return this.currentPosition; |
| 128 | + } |
42 | 129 | } |
43 | 130 |
|
44 | 131 | @Override |
45 | | - public void pressMediaKey(MediaKey mediaKey) { |
46 | | - |
| 132 | + public Track getTrack() { |
| 133 | + return this.currentTrack; |
47 | 134 | } |
48 | 135 |
|
49 | 136 | @Override |
50 | 137 | public boolean isPlaying() { |
51 | | - return false; // TODO Implement OSX SpotifyAPI |
| 138 | + return this.isPlaying; |
52 | 139 | } |
53 | 140 |
|
54 | 141 | @Override |
55 | 142 | public boolean isConnected() { |
56 | | - return false; // TODO Implement OSX SpotifyAPI |
| 143 | + return this.connected; |
57 | 144 | } |
58 | 145 |
|
59 | 146 | @Override |
60 | | - public void stop() { |
61 | | - |
| 147 | + public boolean hasPosition() { |
| 148 | + return this.currentPosition != -1; |
62 | 149 | } |
63 | 150 |
|
64 | 151 | } |
0 commit comments