Skip to content

Commit 6783975

Browse files
committed
implement media keys api, version 1.0.7
1 parent 7613670 commit 6783975

File tree

7 files changed

+91
-1
lines changed

7 files changed

+91
-1
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ because the API reads the information directly from the application itself.
77
#### Supported operating systems:
88
- Windows
99

10+
## Feature Overview
11+
- Track id
12+
- Track title & artist
13+
- Track progress & length
14+
- Playing state (Playing, paused)
15+
- Track cover
16+
- Media keys (Previous song, play/pause & next song)
17+
1018
## Gradle Setup
1119
```groovy
1220
repositories {
@@ -96,3 +104,11 @@ OpenSpotifyAPI openSpotifyAPI = new OpenSpotifyAPI();
96104
// Download the cover art of the current song
97105
BufferedImage imageTrackCover = openSpotifyAPI.requestImage(track);
98106
```
107+
108+
You can also skip the current song using the Media Key API:
109+
```java
110+
SpotifyAPI api = SpotifyAPIFactory.createInitialized();
111+
112+
// Send media key to the operating system
113+
api.pressMediaKey(MediaKey.NEXT);
114+
```

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ plugins {
44
}
55

66
group 'de.labystudio'
7-
version '1.0.6'
7+
version '1.0.7'
88

99
compileJava {
1010
sourceCompatibility = '1.8'

src/main/java/de/labystudio/spotifyapi/SpotifyAPI.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package de.labystudio.spotifyapi;
22

3+
import de.labystudio.spotifyapi.model.MediaKey;
34
import de.labystudio.spotifyapi.model.Track;
45

56
import java.util.concurrent.CompletableFuture;
@@ -71,6 +72,19 @@ default boolean hasTrack() {
7172
*/
7273
boolean isPlaying();
7374

75+
/**
76+
* Send a key pres of a media key to the system.<br>
77+
* The key can be one of the following:
78+
* <ul>
79+
* <li>{@link MediaKey#PLAY_PAUSE}</li>
80+
* <li>{@link MediaKey#NEXT}</li>
81+
* <li>{@link MediaKey#PREV}</li>
82+
* </ul>
83+
*
84+
* @param mediaKey the key to send
85+
*/
86+
void pressMediaKey(MediaKey mediaKey);
87+
7488
/**
7589
* Returns true if the api is connected to the Spotify application.
7690
*
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package de.labystudio.spotifyapi.model;
2+
3+
public enum MediaKey {
4+
PLAY_PAUSE,
5+
NEXT,
6+
PREV
7+
}

src/main/java/de/labystudio/spotifyapi/platform/osx/OSXSpotifyApi.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package de.labystudio.spotifyapi.platform.osx;
22

33
import de.labystudio.spotifyapi.SpotifyAPI;
4+
import de.labystudio.spotifyapi.model.MediaKey;
45
import de.labystudio.spotifyapi.model.Track;
56
import de.labystudio.spotifyapi.platform.AbstractSpotifyAPI;
67

@@ -40,6 +41,11 @@ public boolean hasPosition() {
4041
return false; // TODO Implement OSX SpotifyAPI
4142
}
4243

44+
@Override
45+
public void pressMediaKey(MediaKey mediaKey) {
46+
47+
}
48+
4349
@Override
4450
public boolean isPlaying() {
4551
return false; // TODO Implement OSX SpotifyAPI

src/main/java/de/labystudio/spotifyapi/platform/windows/WinSpotifyAPI.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package de.labystudio.spotifyapi.platform.windows;
22

33
import de.labystudio.spotifyapi.SpotifyAPI;
4+
import de.labystudio.spotifyapi.model.MediaKey;
5+
import de.labystudio.spotifyapi.platform.windows.api.WinApi;
46
import de.labystudio.spotifyapi.platform.windows.api.playback.PlaybackAccessor;
57
import de.labystudio.spotifyapi.platform.windows.api.spotify.SpotifyTitle;
68
import de.labystudio.spotifyapi.SpotifyListener;
@@ -148,6 +150,21 @@ public boolean hasPosition() {
148150
return this.positionKnown;
149151
}
150152

153+
@Override
154+
public void pressMediaKey(MediaKey mediaKey) {
155+
switch (mediaKey) {
156+
case NEXT:
157+
this.process.pressKey(WinApi.VK_MEDIA_NEXT_TRACK);
158+
break;
159+
case PREV:
160+
this.process.pressKey(WinApi.VK_MEDIA_PREV_TRACK);
161+
break;
162+
case PLAY_PAUSE:
163+
this.process.pressKey(WinApi.VK_MEDIA_PLAY_PAUSE);
164+
break;
165+
}
166+
}
167+
151168
@Override
152169
public boolean isPlaying() {
153170
return this.isPlaying;

src/main/java/de/labystudio/spotifyapi/platform/windows/api/WinApi.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
import com.sun.jna.Native;
44
import com.sun.jna.Pointer;
5+
import com.sun.jna.platform.win32.BaseTSD;
56
import com.sun.jna.platform.win32.Kernel32;
67
import com.sun.jna.platform.win32.Tlhelp32;
78
import com.sun.jna.platform.win32.User32;
89
import com.sun.jna.platform.win32.WinDef;
910
import com.sun.jna.platform.win32.WinNT;
11+
import com.sun.jna.platform.win32.WinUser;
1012
import com.sun.jna.ptr.IntByReference;
1113

1214
import java.util.ArrayList;
@@ -27,6 +29,14 @@ public interface WinApi {
2729
int PROCESS_VM_WRITE = 0x0020;
2830
int PROCESS_VM_OPERATION = 0x0008;
2931

32+
int VK_VOLUME_MUTE = 0xAD;
33+
int VK_VOLUME_DOWN = 0xAE;
34+
int VK_VOLUME_UP = 0xAF;
35+
int VK_MEDIA_NEXT_TRACK = 0xB0;
36+
int VK_MEDIA_PREV_TRACK = 0xB1;
37+
int VK_MEDIA_STOP = 0xB2;
38+
int VK_MEDIA_PLAY_PAUSE = 0xB3;
39+
3040
default int getProcessIdByName(String exeName) {
3141
Kernel32 kernel = Kernel32.INSTANCE;
3242

@@ -137,4 +147,24 @@ default long getModuleAddress(long pid, String moduleName) {
137147
Kernel32.INSTANCE.CloseHandle(snapshot);
138148
return -1;
139149
}
150+
151+
default void pressKey(int keyCode) {
152+
WinUser.INPUT input = new WinUser.INPUT();
153+
154+
input.type = new WinDef.DWORD(WinUser.INPUT.INPUT_KEYBOARD);
155+
input.input.setType("ki");
156+
input.input.ki.wVk = new WinDef.WORD(keyCode); // Key code
157+
input.input.ki.wScan = new WinDef.WORD(0); // Hardware scan code
158+
input.input.ki.time = new WinDef.DWORD(0); // Timestamp (System default)
159+
input.input.ki.dwExtraInfo = new BaseTSD.ULONG_PTR(0);
160+
161+
// Press the key
162+
input.input.ki.dwFlags = new WinDef.DWORD(0); // Key down
163+
User32.INSTANCE.SendInput(new WinDef.DWORD(1), new WinUser.INPUT[]{input}, input.size());
164+
165+
// Release the key
166+
input.input.ki.dwFlags = new WinDef.DWORD(2); // Key up
167+
User32.INSTANCE.SendInput(new WinDef.DWORD(1), new WinUser.INPUT[]{input}, input.size());
168+
169+
}
140170
}

0 commit comments

Comments
 (0)