Skip to content

Commit

Permalink
improve dbus & mpris api (wip)
Browse files Browse the repository at this point in the history
  • Loading branch information
LabyStudio committed Jan 8, 2024
1 parent 2c09b4a commit 699150c
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
* Thanks for LabyStudio for many code snippets.
*/
public class LinuxSpotifyApi extends AbstractTickSpotifyAPI {

private boolean connected = false;

private Track currentTrack;
Expand All @@ -24,11 +25,11 @@ public class LinuxSpotifyApi extends AbstractTickSpotifyAPI {

private long lastTimePositionUpdated;

private MPRISCommunicator MPRISCommunicator = new MPRISCommunicator();
private final MPRISCommunicator mediaPlayer = new MPRISCommunicator();

@Override
protected void onTick() {
String trackId = MPRISCommunicator.getTrackId();
protected void onTick() throws Exception {
String trackId = this.mediaPlayer.getTrackId();

// Handle on connect
if (!this.connected && !trackId.isEmpty()) {
Expand All @@ -38,9 +39,9 @@ protected void onTick() {

// Handle track changes
if (!Objects.equals(trackId, this.currentTrack == null ? null : this.currentTrack.getId())) {
String trackName = MPRISCommunicator.getTrackName();
String trackArtist = MPRISCommunicator.getArtist();
int trackLength = MPRISCommunicator.getTrackLength();
String trackName = this.mediaPlayer.getTrackName();
String trackArtist = this.mediaPlayer.getArtist();
int trackLength = this.mediaPlayer.getTrackLength();

boolean isFirstTrack = !this.hasTrack();

Expand All @@ -57,7 +58,7 @@ protected void onTick() {
}

// Handle is playing changes
boolean isPlaying = MPRISCommunicator.isPlaying();
boolean isPlaying = this.mediaPlayer.isPlaying();
if (isPlaying != this.isPlaying) {
this.isPlaying = isPlaying;

Expand All @@ -66,7 +67,7 @@ protected void onTick() {
}


this.updatePosition(MPRISCommunicator.getPosition());
this.updatePosition(this.mediaPlayer.getPosition());

// Fire keep alive
this.listeners.forEach(SpotifyListener::onSync);
Expand Down Expand Up @@ -96,13 +97,13 @@ public void pressMediaKey(MediaKey mediaKey) {
try {
switch (mediaKey) {
case PLAY_PAUSE:
MPRISCommunicator.playPause();
this.mediaPlayer.playPause();
break;
case NEXT:
MPRISCommunicator.next();
this.mediaPlayer.next();
break;
case PREV:
MPRISCommunicator.previous();
this.mediaPlayer.previous();
break;
}
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,76 +5,80 @@
import java.util.List;
import java.util.Map;

public class MetadataParser {
public class DBusResponse {

private static final String splitRegex = "\\s+(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)";

public static Map<String, Object> parse(String input) {
Map<String, Object> result = new HashMap<>();
private final Map<String, Object> result = new HashMap<>();

String[] lines = input.split("\n");
public DBusResponse(String raw) {
String[] lines = raw.split("\n");

int arrayStartPos = 0;
String key = "";
Object value = new Object();
for (int i = 1; i < lines.length-1; i++) {
for (int i = 1; i < lines.length - 1; i++) {
String line = lines[i].trim();

if(arrayStartPos != 0){
if(line.contains("]")){
if (arrayStartPos != 0) {
if (line.contains("]")) {
StringBuilder arrayString = new StringBuilder();
int j = arrayStartPos;
for (; j < i+1; j++) {
for (; j < i + 1; j++) {
arrayString.append(lines[j]).append("\n");
}
i = j - 1;
arrayStartPos = 0;

value = parseList(arrayString.toString());
}else continue;
}else if (line.startsWith("variant ")) {
value = this.parseList(arrayString.toString());
}
} else if (line.startsWith("variant ")) {
// Should be value of the dict entry

String[] words = line.replaceFirst("variant {16}", "").split(splitRegex);

if (words[0].equals("array")) {
arrayStartPos = i;
} else {
value = parseValue(words[0], words[1]);
value = this.parseValue(words[0], words[1]);
}
}else if (line.startsWith("dict entry(")) {
} else if (line.startsWith("dict entry(")) {
// start of dict entry
key = "";
value = null;
} else if (line.endsWith(")")) {
// end of dict entry
result.put(key, value);
this.result.put(key, value);
} else {
// Should be key of the dict entry

String[] words = line.split(splitRegex);
key = (String) parseValue(words[0], words[1]);
key = (String) this.parseValue(words[0], words[1]);
}
}
}

return result;
public boolean has(String key) {
return this.result.containsKey(key);
}

private static List<Object> parseList(String arrayString) {
@SuppressWarnings("unchecked")
public <T> T get(String key) {
return (T) this.result.get(key);
}

private List<Object> parseList(String arrayString) {
List<Object> result = new ArrayList<>();

String[] lines = arrayString.split("\n");

for (int i = 1; i < lines.length-1; i++) {
for (int i = 1; i < lines.length - 1; i++) {
String[] words = lines[i].trim().split(splitRegex);

result.add(parseValue(words[0], words[1]));
result.add(this.parseValue(words[0], words[1]));
}

return result;
}

public static Object parseValue(String type, String value) {
private Object parseValue(String type, String value) {
switch (type) {
case "string":
return value.split("\"")[1];
Expand All @@ -89,9 +93,8 @@ public static Object parseValue(String type, String value) {
}
}

public static Object parseValueFromString(String string) {
private Object parseValueFromString(String string) {
String[] words = string.split(splitRegex);

return parseValue(words[0], words[1]);
return this.parseValue(words[0], words[1]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package de.labystudio.spotifyapi.platform.linux.api;

import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
* Java wrapper for the dbus-send application
* <p>
* The dbus-send command is used to send a message to a D-Bus message bus.
* There are two well-known message buses:
* - the systemwide message bus (installed on many systems as the "messagebus" service)
* - the per-user-login-session message bus (started each time a user logs in).
* <p>
* The "system" parameter and "session" parameter options direct dbus-send to send messages to the system or session buses respectively.
* If neither is specified, dbus-send sends to the session bus.
* <p>
* Nearly all uses of dbus-send must provide the "dest" parameter which is the name of
* a connection on the bus to send the message to. If the "dest" parameter is omitted, no destination is set.
* <p>
* The object path and the name of the message to send must always be specified.
* Following arguments, if any, are the message contents (message arguments).
* These are given as type-specified values and may include containers (arrays, dicts, and variants).
*
* @author LabyStudio
*/
public class DBusSend {

private static final Parameter PARAM_PRINT_REPLY = new Parameter("print-reply");
private static final InterfaceMember INTERFACE_GET = new InterfaceMember("org.freedesktop.DBus.Properties.Get");

private final Parameter[] parameters;
private final String objectPath;
private final Runtime runtime;

/**
* Creates a new DBusSend API for a specific application
*
* @param parameters The parameters to use
* @param objectPath The object path to use
*/
public DBusSend(Parameter[] parameters, String objectPath) {
this.parameters = parameters;
this.objectPath = objectPath;
this.runtime = Runtime.getRuntime();
}

/**
* Request an information from the application
*
* @param keys The requested type of information
* @return The requested information
* @throws Exception If the request failed
*/
public DBusResponse get(String... keys) throws Exception {
String[] contents = new String[keys.length];
for (int i = 0; i < keys.length; i++) {
contents[i] = "string:'" + keys[i] + "'";
}
return this.send(INTERFACE_GET, contents);
}

/**
* Execute an DBusSend command.
*
* @param interfaceMember The interface member to execute
* @param contents The contents to send
* @return The result of the command
* @throws Exception If the command failed
*/
public DBusResponse send(InterfaceMember interfaceMember, String... contents) throws Exception {
// Build arguments
String[] arguments = new String[2 + this.parameters.length + 2 + contents.length];
arguments[0] = "dbus-send";
arguments[1] = PARAM_PRINT_REPLY.toString();
for (int i = 0; i < this.parameters.length; i++) {
arguments[2 + i] = this.parameters[i].toString();
}
arguments[2 + this.parameters.length] = this.objectPath;
arguments[2 + this.parameters.length + 1] = interfaceMember.toString();
for (int i = 0; i < contents.length; i++) {
arguments[2 + this.parameters.length + 2 + i] = contents[i];
}

// Execute dbus-send process
Process process = this.runtime.exec(arguments);
int exitCode = process.waitFor();
if (exitCode == 0) {
// Read response
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder builder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
}
return new DBusResponse(builder.toString());
} else {
// Handle error message
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line;
StringBuilder builder = new StringBuilder();
while ((line = reader.readLine()) != null) {
builder.append(line);
}
throw new Exception("dbus-send execution \"" + String.join(" ", arguments) + "\" failed with exit code " + exitCode + ": " + builder);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package de.labystudio.spotifyapi.platform.linux.api;

/**
* Interface member wrapper for the DBusSend class.
*
* @author LabyStudio
*/
public class InterfaceMember {

private final String path;

public InterfaceMember(String path) {
this.path = path;
}

public String toString() {
return this.path;
}

}

0 comments on commit 699150c

Please sign in to comment.